@smallpen/cli 0.1.0-alpha.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/bin/help.mjs +620 -0
- package/bin/smallpen-check.cjs +30 -0
- package/bin/smallpen.mjs +2509 -0
- package/bin/warning-output.mjs +64 -0
- package/package.json +26 -0
package/bin/help.mjs
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
const COMMANDS = [
|
|
2
|
+
["version", "Print the CLI package version without opening a workspace"],
|
|
3
|
+
["init", "Persist a guided Brief/Proposal and create Foundation + Product"],
|
|
4
|
+
["validate", "Validate a package and its same-workspace Foundation"],
|
|
5
|
+
["inspect", "Inspect package identity, capabilities, and domain summary"],
|
|
6
|
+
["list", "List stable domain objects by kind"],
|
|
7
|
+
["read", "Read the complete validated Canonical Snapshot"],
|
|
8
|
+
["read-view", "Read the deterministic default or selected Design View"],
|
|
9
|
+
["discover", "Discover exact selectors and executable next reads"],
|
|
10
|
+
["catalog", "Derive the Design System Catalog and requirement coverage"],
|
|
11
|
+
["search-components", "Find reusable Product and Foundation components"],
|
|
12
|
+
["component", "Inspect a Component Set and its legal variants"],
|
|
13
|
+
["compare", "Compare two to four explicitly selected Design Views"],
|
|
14
|
+
["tokens", "List Effective Tokens for a Context"],
|
|
15
|
+
["search-tokens", "Find reusable Tokens by name, type, or design value"],
|
|
16
|
+
["effective-token", "Resolve one Effective Token"],
|
|
17
|
+
["explain-token", "Explain one Effective Token resolution decision"],
|
|
18
|
+
["render", "Render the selected Design View to deterministic PNG"],
|
|
19
|
+
["inspect-view", "Return wireframe, semanticTree, optional image, and supporting context"],
|
|
20
|
+
["render-matrix", "Render multiple design contexts for AI comparison"],
|
|
21
|
+
["evidence", "Return PNG plus deterministic review evidence; --output exports files"],
|
|
22
|
+
["import-draft", "Import Figma clipboard, SVG, or PNG as a separate Draft file"],
|
|
23
|
+
["import-media", "Import binary Media (PNG/JPEG/GIF/WebP/SVG) into a Product Asset Library"],
|
|
24
|
+
["remove-media", "Remove an imported Media descriptor from the Asset Library"],
|
|
25
|
+
["import-font", "Import a TTF/OTF/WOFF font and convert SFNT to WOFF"],
|
|
26
|
+
["import-tokens", "Review and import a DTCG / Tokens Studio token file into the Token Library"],
|
|
27
|
+
["library-refresh", "Re-fetch a declared URL Library into its verified cache"],
|
|
28
|
+
["draft-diff", "Compare two independent Draft files without changing Canonical"],
|
|
29
|
+
["draft-compile", "Compile selected Draft fields into a current-revision batch"],
|
|
30
|
+
["flow", "Create flowchart nodes from a declarative intent"],
|
|
31
|
+
["page", "Create page nodes from a declarative intent"],
|
|
32
|
+
["token", "Apply declarative Token operations atomically"],
|
|
33
|
+
["impact", "Find every node and component using a Token"],
|
|
34
|
+
["apply", "Atomically apply one typed Operation Batch"],
|
|
35
|
+
["watch", "Emit NDJSON revision events for local external changes"],
|
|
36
|
+
["repair", "Inspect or execute one explicit Repair choice"],
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const DESIGN_SELECTORS = `Design selectors:
|
|
40
|
+
--screen ID Screen; default: manifest defaultScreenId
|
|
41
|
+
--presentation ID Presentation; default: Screen Base Presentation
|
|
42
|
+
--scenario ID Reproducible Scenario; default: canonical initial state
|
|
43
|
+
--context-profile ID Named Context profile
|
|
44
|
+
--context AXIS=VALUE Repeatable finite Context selection`;
|
|
45
|
+
|
|
46
|
+
const VIEW_SELECTORS = `${DESIGN_SELECTORS}
|
|
47
|
+
--format FORMAT structure|semantic|wireframe|screenshot`;
|
|
48
|
+
|
|
49
|
+
const HELP = {
|
|
50
|
+
version: `Usage:
|
|
51
|
+
smallpen --version
|
|
52
|
+
smallpen version [--json]
|
|
53
|
+
|
|
54
|
+
Output:
|
|
55
|
+
The installed CLI package version, or {name,version} with --json.
|
|
56
|
+
No package path is required; design files are unchanged and no services start.`,
|
|
57
|
+
init: `Usage:
|
|
58
|
+
smallpen init <workspace-directory> [--state FILE] [--answers FILE]
|
|
59
|
+
[--answer QUESTION_ID=JSON]... [--confirm] [--locale LOCALE] [--json]
|
|
60
|
+
|
|
61
|
+
Purpose:
|
|
62
|
+
Persist a progressively completed Initialization Brief and validated Proposal.
|
|
63
|
+
No hidden conversational state is used. --confirm atomically creates one local
|
|
64
|
+
Foundation and one Product; an existing target is never overwritten.
|
|
65
|
+
|
|
66
|
+
Defaults and values:
|
|
67
|
+
--state defaults to <workspace-directory>.smallpen-init.json.
|
|
68
|
+
Project kinds: application|motion|custom. Foundation choice: create-new.
|
|
69
|
+
--answer values are JSON (for example projectKind=\"application\").
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
smallpen init ./quincy --json
|
|
73
|
+
smallpen init ./quincy --state ./quincy-init.json --answer projectKind=\"application\" --json
|
|
74
|
+
smallpen init ./quincy --answers ./answers.json --confirm --json
|
|
75
|
+
|
|
76
|
+
Output:
|
|
77
|
+
status needs_input returns a stable question id, localized label, schema,
|
|
78
|
+
optional recommendation, and continuation args with an answer placeholder. Replace
|
|
79
|
+
<JSON> with your schema-valid answer; if no recommendation is present, supply the
|
|
80
|
+
project's own purpose, audience, or deliverable. status proposal returns the
|
|
81
|
+
persisted Proposal and confirmation args. Only --confirm creates the packages;
|
|
82
|
+
status initialized returns both package paths.
|
|
83
|
+
|
|
84
|
+
Errors and next commands:
|
|
85
|
+
Malformed --answer JSON identifies the question; schema-invalid values also return
|
|
86
|
+
its schema. Correct the answer and retry with the same --state. After confirming:
|
|
87
|
+
smallpen validate <product.smallpen> --json`,
|
|
88
|
+
validate: `Usage:
|
|
89
|
+
smallpen validate <product.smallpen> [--json]
|
|
90
|
+
|
|
91
|
+
Purpose:
|
|
92
|
+
Validate Canonical JSON, IDs, references, Product/Foundation dependency, all Context
|
|
93
|
+
combinations used by projections, and strict Scenario/component selections.
|
|
94
|
+
|
|
95
|
+
Output:
|
|
96
|
+
status valid with Product and Foundation revisions; dependency conflicts return
|
|
97
|
+
repair_required with exact choices.
|
|
98
|
+
|
|
99
|
+
Next:
|
|
100
|
+
smallpen inspect <package> --json
|
|
101
|
+
smallpen repair <product> --json`,
|
|
102
|
+
inspect: `Usage:
|
|
103
|
+
smallpen inspect <package.smallpen> [--json]
|
|
104
|
+
|
|
105
|
+
Purpose:
|
|
106
|
+
Return package identity/revision, format capabilities, Contexts, Screens,
|
|
107
|
+
Presentations, Tokens, Component Sets, Scenarios, requirements, Flows, and
|
|
108
|
+
machine-readable Design System preflight operations.
|
|
109
|
+
|
|
110
|
+
Next:
|
|
111
|
+
smallpen list <package> --kind screens --json
|
|
112
|
+
smallpen read-view <package> --json`,
|
|
113
|
+
list: `Usage:
|
|
114
|
+
smallpen list <package.smallpen> [--kind KIND] [--offset N] [--limit N] [--json]
|
|
115
|
+
|
|
116
|
+
Values:
|
|
117
|
+
KIND: all|screens|presentations|contexts|tokens|components|scenarios|requirements|flows
|
|
118
|
+
Defaults: kind=all, offset=0, limit=100. Lists include total/range pagination.
|
|
119
|
+
|
|
120
|
+
Next:
|
|
121
|
+
Use returned stable IDs with read-view, effective-token, or compare.`,
|
|
122
|
+
read: `Usage:
|
|
123
|
+
smallpen read <package.smallpen> [--json]
|
|
124
|
+
|
|
125
|
+
Purpose:
|
|
126
|
+
Return the complete validated Canonical Snapshot used by adapters. Binary blobs are
|
|
127
|
+
not embedded in JSON. Prefer inspect/list/read-view for bounded Agent reads.
|
|
128
|
+
|
|
129
|
+
Next:
|
|
130
|
+
smallpen inspect <package> --json`,
|
|
131
|
+
"read-view": `Usage:
|
|
132
|
+
smallpen read-view <product.smallpen> [selectors] [--offset N] [--limit N]
|
|
133
|
+
[--locale LOCALE] [--json]
|
|
134
|
+
|
|
135
|
+
${VIEW_SELECTORS}
|
|
136
|
+
|
|
137
|
+
Defaults:
|
|
138
|
+
Base Presentation + default Context values + canonical initial state + structure.
|
|
139
|
+
All formats resolve the same selection.
|
|
140
|
+
|
|
141
|
+
Choose a format:
|
|
142
|
+
wireframe A scaled 2D ASCII canvas for fast spatial reasoning, followed by a
|
|
143
|
+
compact marker key with hierarchy, layer type, name, and stable ID.
|
|
144
|
+
semantic Exact machine-readable hierarchy, bounds, text, alignment, resolved
|
|
145
|
+
typography, component references, variants, and Token bindings.
|
|
146
|
+
screenshot Rendered PNG metadata only; use render/evidence for inline pixels,
|
|
147
|
+
or add --output to explicitly write PNG files.
|
|
148
|
+
The PNG image itself is raster pixels only: it has no layer IDs,
|
|
149
|
+
hierarchy, bounds, component references, or Token bindings.
|
|
150
|
+
|
|
151
|
+
Agent guidance:
|
|
152
|
+
Use wireframe or semantic to understand and edit the design. Use PNG to inspect
|
|
153
|
+
high-fidelity color, typography, spacing, effects, and rendering. Use inspect-view
|
|
154
|
+
--include-image when both structural reasoning and visual QA are required.
|
|
155
|
+
|
|
156
|
+
Next:
|
|
157
|
+
Execute any Discovery Guide command without inventing arguments.`,
|
|
158
|
+
discover: `Usage:
|
|
159
|
+
smallpen discover <product.smallpen> [selectors] [--offset N] [--limit N]
|
|
160
|
+
[--locale LOCALE] [--json]
|
|
161
|
+
|
|
162
|
+
${VIEW_SELECTORS}
|
|
163
|
+
|
|
164
|
+
Output:
|
|
165
|
+
Exact selector parameters, defaults, finite valid values, localized summaries,
|
|
166
|
+
machine operation/args, copyable commands, and pagination metadata.`,
|
|
167
|
+
catalog: `Usage:
|
|
168
|
+
smallpen catalog <product.smallpen> [--context AXIS=VALUE]... [--json]
|
|
169
|
+
|
|
170
|
+
Purpose:
|
|
171
|
+
Derive legal Component variants, inherited assets, Effective Tokens, Contexts,
|
|
172
|
+
Scenarios, Screens, and requirement coverage from the same validated workspace.`,
|
|
173
|
+
"search-components": `Usage:
|
|
174
|
+
smallpen search-components <product.smallpen> --query TEXT [--limit N] [--json]
|
|
175
|
+
|
|
176
|
+
Purpose:
|
|
177
|
+
Find reusable Product, public Foundation, and linked Library components before
|
|
178
|
+
drawing a custom replacement. Matches stable ID, name, category, and description and returns the
|
|
179
|
+
owning Package, source layer, legal variant selections, and an executable
|
|
180
|
+
owner-scoped component command (argv plus a POSIX-shell command).
|
|
181
|
+
|
|
182
|
+
State:
|
|
183
|
+
Every query is explicit and self-contained. It never creates a current component
|
|
184
|
+
or affects a later command.`,
|
|
185
|
+
component: `Usage:
|
|
186
|
+
smallpen component <package.smallpen> --component-id CMP [--json]
|
|
187
|
+
|
|
188
|
+
Purpose:
|
|
189
|
+
Return one exact Component Set, its legal variant selections, replacement metadata,
|
|
190
|
+
and component-targeted Scenarios. This is read-only and uses stable Canonical IDs.
|
|
191
|
+
The package path must identify the owning Package. For inherited components,
|
|
192
|
+
execute the owner-scoped command returned by search-components.
|
|
193
|
+
An HTTP(S) Library URL is also accepted and uses the normal Library cache.
|
|
194
|
+
|
|
195
|
+
Next:
|
|
196
|
+
Use a returned selection with read-view or inspect-view to review the design.`,
|
|
197
|
+
compare: `Usage:
|
|
198
|
+
smallpen compare <product.smallpen> --selector JSON --selector JSON
|
|
199
|
+
[--selector JSON] [--selector JSON] [--json]
|
|
200
|
+
|
|
201
|
+
Values:
|
|
202
|
+
Exactly two to four selector objects. Example:
|
|
203
|
+
smallpen compare product.smallpen --selector '{"presentationId":"pres_desktop"}' \\
|
|
204
|
+
--selector '{"presentationId":"pres_mobile"}' --json`,
|
|
205
|
+
tokens: `Usage:
|
|
206
|
+
smallpen tokens <product.smallpen> [--context AXIS=VALUE]... [--json]
|
|
207
|
+
|
|
208
|
+
Purpose:
|
|
209
|
+
List Effective Tokens using Product first, Context specificity within a layer,
|
|
210
|
+
then Foundation fallback.`,
|
|
211
|
+
"search-tokens": `Usage:
|
|
212
|
+
smallpen search-tokens <product.smallpen> (--color COLOR | --value JSON | --query TEXT | --type TYPE)
|
|
213
|
+
[--context AXIS=VALUE]... [--limit N] [--json]
|
|
214
|
+
|
|
215
|
+
Purpose:
|
|
216
|
+
Find reusable Product and Foundation Design Tokens before writing a hard-coded
|
|
217
|
+
design value. Color and numeric searches rank exact matches first, then nearest
|
|
218
|
+
values. Without --context, every finite Web/Desktop/theme/etc. Context is searched
|
|
219
|
+
and each resolved value reports its matching Contexts. Repeating --context restricts
|
|
220
|
+
the search explicitly. Name queries match Token path, description, and type.
|
|
221
|
+
|
|
222
|
+
Examples:
|
|
223
|
+
smallpen search-tokens product.smallpen --color '#6750a4' --json
|
|
224
|
+
smallpen search-tokens product.smallpen --type spacing --value 16 --json
|
|
225
|
+
smallpen search-tokens product.smallpen --query surface --json
|
|
226
|
+
|
|
227
|
+
State:
|
|
228
|
+
Every search is self-contained. It never reads or writes a current selection,
|
|
229
|
+
previous result, session, or Web UI state.`,
|
|
230
|
+
"effective-token": `Usage:
|
|
231
|
+
smallpen effective-token <product.smallpen> --token-id TOK [--package-id PKG]
|
|
232
|
+
[--context AXIS=VALUE]... [--json]
|
|
233
|
+
|
|
234
|
+
Defaults:
|
|
235
|
+
package-id defaults to the Product Package ID; Context axes use defaults.`,
|
|
236
|
+
"explain-token": `Usage:
|
|
237
|
+
smallpen explain-token <product.smallpen> --token-id TOK [--package-id PKG]
|
|
238
|
+
[--context AXIS=VALUE]... [--json]
|
|
239
|
+
|
|
240
|
+
Output:
|
|
241
|
+
Chosen source/value plus all candidates, specificity, rejection reasons, and layer.`,
|
|
242
|
+
render: `Usage:
|
|
243
|
+
smallpen render <product.smallpen> [selectors] [--scale N] [--output FILE] [--json]
|
|
244
|
+
|
|
245
|
+
${DESIGN_SELECTORS}
|
|
246
|
+
|
|
247
|
+
Defaults and limits:
|
|
248
|
+
No --output: return a PNG as bare base64 in one JSON result; no image file or
|
|
249
|
+
temporary image is created. This also applies without --json.
|
|
250
|
+
Explicit --output FILE: write that PNG and return its absolute output path
|
|
251
|
+
instead of base64. Existing FILE is replaced only after a successful write.
|
|
252
|
+
scale=1, scale range (0,8], maximum dimension 8192,
|
|
253
|
+
maximum 32M pixels. Output is deterministic PNG with revision/render hash. The
|
|
254
|
+
PNG image itself is raster pixels only and contains no layer IDs, hierarchy,
|
|
255
|
+
bounds, component references, or Token bindings. Use it for high-fidelity visual
|
|
256
|
+
QA of color, typography, spacing, effects, alignment, and rendering.
|
|
257
|
+
Decode the current response in an image-capable client; base64 text alone is
|
|
258
|
+
not visual QA. Verify decoded bytes against renderHash (SHA-256), and use the
|
|
259
|
+
accompanying revision and selection. On failure, never open a previous file.
|
|
260
|
+
|
|
261
|
+
Wireframe:
|
|
262
|
+
Use smallpen read-view <product.smallpen> --format wireframe --json.
|
|
263
|
+
It returns a scaled 2D ASCII canvas and compact marker key. Use semanticTree
|
|
264
|
+
from inspect-view when exact bounds, text, component, and Token data is needed.`,
|
|
265
|
+
"inspect-view": `Usage:
|
|
266
|
+
smallpen inspect-view <product.smallpen> [selectors] [--include-image]
|
|
267
|
+
[--output FILE] [--base64] [--json]
|
|
268
|
+
|
|
269
|
+
${DESIGN_SELECTORS}
|
|
270
|
+
--scale N Image scale; default: 1 (requires --include-image to render)
|
|
271
|
+
--locale LOCALE Localized view labels; default: zh-TW
|
|
272
|
+
|
|
273
|
+
Purpose:
|
|
274
|
+
Return one AI-readable view bundle.
|
|
275
|
+
|
|
276
|
+
Three complementary view parts:
|
|
277
|
+
wireframe Scaled 2D ASCII canvas plus a compact layer marker key.
|
|
278
|
+
semanticTree Exact hierarchy, bounds, text, alignment, typography, component,
|
|
279
|
+
variant, and Token data.
|
|
280
|
+
image Optional high-fidelity PNG and metadata; add --include-image to render it.
|
|
281
|
+
|
|
282
|
+
Supporting context:
|
|
283
|
+
Effective Tokens, component catalog, selected Context, and revision metadata.
|
|
284
|
+
Without --output, image.base64 contains bare PNG base64 in this one JSON result
|
|
285
|
+
(also without --json). No image file or temporary image is created.
|
|
286
|
+
Explicit --output FILE writes the image and returns image.output instead of
|
|
287
|
+
base64; add --base64 only when both file and inline bytes are wanted.
|
|
288
|
+
--output and --base64 require --include-image.
|
|
289
|
+
|
|
290
|
+
Agent guidance:
|
|
291
|
+
Use this when the Agent needs both structure and visual QA. Start with wireframe
|
|
292
|
+
for spatial relationships, query semanticTree for exact data, and inspect image
|
|
293
|
+
only when pixel-level visual QA is needed. The PNG itself contains no layer or
|
|
294
|
+
Token metadata. Decode this response in an image-capable client; base64 text
|
|
295
|
+
alone is not visual QA. image.revision/selection identify the rendered snapshot;
|
|
296
|
+
image.renderHash is the SHA-256 of the decoded PNG. Never reuse an old image
|
|
297
|
+
after an error, or infer freshness from a filename.`,
|
|
298
|
+
"render-matrix": `Usage:
|
|
299
|
+
smallpen render-matrix <product.smallpen> --contexts CONTEXTS.json
|
|
300
|
+
[--output DIRECTORY] [--scale N] [--json]
|
|
301
|
+
|
|
302
|
+
Purpose:
|
|
303
|
+
Render a deterministic PNG for every explicit design selector in CONTEXTS.json.
|
|
304
|
+
Each selector may choose context axes, screen, presentation, and scenario.
|
|
305
|
+
Without --output, one JSON result contains ordered images with bare base64,
|
|
306
|
+
dimensions, exact selectors, per-image revisions, and PNG SHA-256 renderHash.
|
|
307
|
+
No image files or temporary images are created, including if a later selector
|
|
308
|
+
fails. This also applies without --json.
|
|
309
|
+
Explicit --output DIRECTORY writes PNG files and returns each image.output
|
|
310
|
+
instead of base64. Different render hashes retain separate files; there is no
|
|
311
|
+
automatic cleanup or all-or-nothing multi-file export. Only consume images
|
|
312
|
+
from a successful current response; never fall back to earlier matrix files.`,
|
|
313
|
+
evidence: `Usage:
|
|
314
|
+
smallpen evidence <product.smallpen> [selectors] [--scale N] [--output PREFIX] [--json]
|
|
315
|
+
|
|
316
|
+
${DESIGN_SELECTORS}
|
|
317
|
+
|
|
318
|
+
Output:
|
|
319
|
+
Without --output, return one JSON evidence bundle with image.base64 (bare PNG
|
|
320
|
+
base64); no PNG, evidence JSON file, or temporary image is created. This also
|
|
321
|
+
applies without --json. image includes the current revision/selection and
|
|
322
|
+
renderHash (SHA-256 of decoded PNG bytes), not a cache locator.
|
|
323
|
+
Explicit --output PREFIX writes PREFIX.png and PREFIX.json instead of base64.
|
|
324
|
+
Evidence contains revision/package hash, resolved selectors,
|
|
325
|
+
render hash, node-to-image regions, Semantic Tree, viewport, and diagnostics.
|
|
326
|
+
PREFIX.png is an unannotated high-fidelity raster image. Semantic details live in
|
|
327
|
+
PREFIX.json and are not embedded visibly in the PNG.
|
|
328
|
+
Decode the current inline response in an image-capable client for visual QA.
|
|
329
|
+
Failed exports do not authorize reading an old file; multi-file export is not
|
|
330
|
+
an all-or-nothing transaction and existing files are not automatically cleaned.
|
|
331
|
+
|
|
332
|
+
Wireframe:
|
|
333
|
+
Use smallpen read-view <product.smallpen> --format wireframe --json.
|
|
334
|
+
It returns a scaled 2D ASCII canvas with one [NN] marker per layer.`,
|
|
335
|
+
"import-media": `Usage:
|
|
336
|
+
smallpen import-media <package.smallpen> --file MEDIA_FILE [--media-id media_...]
|
|
337
|
+
[--media-path PATH] [--name NAME] [--json]
|
|
338
|
+
|
|
339
|
+
Import is the public binary Media entry for an existing Product: PNG, JPEG, GIF,
|
|
340
|
+
WebP, and SVG are detected from bytes, dimensions are validated, and the blob is
|
|
341
|
+
content-addressed (identical bytes in one package share one blob). JSON apply
|
|
342
|
+
still forbids blob writes; media import is a file entry, not a batch operation.
|
|
343
|
+
remove-media deletes the descriptor (metadata lifecycle is reversible); the
|
|
344
|
+
content-addressed blob itself is immutable and shared.
|
|
345
|
+
|
|
346
|
+
Errors and recovery:
|
|
347
|
+
Corrupt or unsupported bytes exit 1 invalid_media_file and write nothing.
|
|
348
|
+
Reusing --media-id exits duplicate_media_id with the existing descriptor.
|
|
349
|
+
|
|
350
|
+
`,
|
|
351
|
+
"remove-media": `Usage:
|
|
352
|
+
smallpen remove-media <package.smallpen> --media-id MEDIA [--json]
|
|
353
|
+
|
|
354
|
+
Removes one Media descriptor from the Asset Library atomically; the inverse batch
|
|
355
|
+
restores it. Unknown ids exit missing_media.
|
|
356
|
+
|
|
357
|
+
`,
|
|
358
|
+
"import-font": `Usage:
|
|
359
|
+
smallpen import-font <package.smallpen> --file FONT_FILE --family NAME
|
|
360
|
+
[--font-id font_...] [--variant-id fvar_...]
|
|
361
|
+
[--weight 400] [--style normal] [--name NAME] [--json]
|
|
362
|
+
|
|
363
|
+
Public Font entry for an existing Product. TTF and OTF are validated by SFNT
|
|
364
|
+
signature and converted to WOFF (the renderer loads ttf/otf/woff). WOFF imports
|
|
365
|
+
as-is after table validation. A valid WOFF2 reaches an explicit
|
|
366
|
+
unsupported_font_conversion boundary (no WOFF2 decoder is bundled); corrupt
|
|
367
|
+
fonts fail invalid_font_blob. Either way nothing is written.
|
|
368
|
+
|
|
369
|
+
`,
|
|
370
|
+
"library-refresh": `Usage:
|
|
371
|
+
smallpen library-refresh <package.smallpen> [--library PACKAGE_ID|URL] [--json]
|
|
372
|
+
|
|
373
|
+
Explicitly re-fetches one declared URL Library from its origin into the same
|
|
374
|
+
verified cache. The refreshed snapshot must match the declared Package ID; a
|
|
375
|
+
mismatch exits library_id_mismatch with expected/actual identities and leaves the
|
|
376
|
+
previous verified snapshot readable offline. Success reports before/after
|
|
377
|
+
revisions; failures keep the previous revision.
|
|
378
|
+
|
|
379
|
+
`,
|
|
380
|
+
"import-draft": `Usage:
|
|
381
|
+
smallpen import-draft <new-draft.smallpen> --kind figma|svg|png --input FILE
|
|
382
|
+
[--package-id PKG] [--json]
|
|
383
|
+
|
|
384
|
+
Purpose:
|
|
385
|
+
Create one new, independent .smallpen Draft file. Figma structured clipboard HTML
|
|
386
|
+
is decoded first; SVG and PNG preserve the source bytes as flat IMAGE fallbacks.
|
|
387
|
+
Provenance and a Loss Report are canonical Draft metadata.
|
|
388
|
+
|
|
389
|
+
Safety:
|
|
390
|
+
The output must not already exist. Import never opens or modifies a Product or
|
|
391
|
+
Foundation file and reimport always creates another Draft file.
|
|
392
|
+
|
|
393
|
+
Next:
|
|
394
|
+
smallpen draft-diff earlier.smallpen --after reimport.smallpen --json
|
|
395
|
+
smallpen draft-compile product.smallpen --draft reimport.smallpen --selections selections.json --json`,
|
|
396
|
+
"import-tokens": `Usage:
|
|
397
|
+
smallpen import-tokens <package.smallpen> --input TOKENS.json [--set-name NAME]
|
|
398
|
+
[--select SET/NAME]... [--apply] [--dry-run]
|
|
399
|
+
[--batch-id ID] [--json]
|
|
400
|
+
|
|
401
|
+
Purpose:
|
|
402
|
+
Import a DTCG token file (Penpot or Tokens Studio export, single-set, multi-set
|
|
403
|
+
with $themes/$metadata, or legacy value/type) into the package's Penpot-shaped
|
|
404
|
+
Token Library. Without --apply the command only reviews: it prints the diff of
|
|
405
|
+
tokens, sets, and themes that would be added, changed, or removed, with values.
|
|
406
|
+
--dry-run keeps the operation in no-write review mode even with --apply;
|
|
407
|
+
that combination also validates the prepared Operation Batch without committing.
|
|
408
|
+
|
|
409
|
+
Identity:
|
|
410
|
+
Sets match by name, tokens by set name plus token name, themes by group/name.
|
|
411
|
+
Matching entries keep their existing ids; everything else gets a fresh id.
|
|
412
|
+
--select limits the applied token rows to the listed "set/name" keys; an
|
|
413
|
+
unselected change keeps its current value, an unselected addition is dropped,
|
|
414
|
+
an unselected removal is kept. Sets and themes always follow the file.
|
|
415
|
+
|
|
416
|
+
Types:
|
|
417
|
+
DTCG $type names (borderRadius, fontFamilies, borderWidth, ...) are mapped to
|
|
418
|
+
SmallPen types. Tokens with unsupported types are skipped and reported in
|
|
419
|
+
warnings. --set-name names the single set of a file without $themes/$metadata.
|
|
420
|
+
|
|
421
|
+
Output:
|
|
422
|
+
Review: {diff, warnings, library, dryRun:true}. With --apply: the atomic apply
|
|
423
|
+
result plus the same diff and warnings. With --apply --dry-run: the prepared
|
|
424
|
+
batch result, diff, warnings, and dryRun:true (not the review library field).`,
|
|
425
|
+
"draft-diff": `Usage:
|
|
426
|
+
smallpen draft-diff <before-draft.smallpen> --after AFTER.smallpen [--json]
|
|
427
|
+
|
|
428
|
+
Purpose:
|
|
429
|
+
Compare semantic projections and Loss Reports from two independent Draft files.
|
|
430
|
+
Neither Draft nor any Canonical Product is modified.`,
|
|
431
|
+
"draft-compile": `Usage:
|
|
432
|
+
smallpen draft-compile <canonical.smallpen> --draft DRAFT.smallpen
|
|
433
|
+
--selections FILE [--batch-id ID] [--json]
|
|
434
|
+
|
|
435
|
+
Selections:
|
|
436
|
+
JSON array of explicit matching nodes and fields, for example:
|
|
437
|
+
[{"screenId":"scr_home","nodeId":"node_title","fields":["text","x"]}]
|
|
438
|
+
|
|
439
|
+
Output:
|
|
440
|
+
A typed Operation Batch whose baseRevision is the current Canonical revision.
|
|
441
|
+
This command does not apply the batch. Review it, then use smallpen apply.`,
|
|
442
|
+
flow: `Usage:
|
|
443
|
+
smallpen flow <product.smallpen> --intent INTENT.json [--batch-id ID] [--dry-run]
|
|
444
|
+
[--warning-detail compact|full] [--json]
|
|
445
|
+
|
|
446
|
+
Purpose:
|
|
447
|
+
Compile and atomically apply a declarative flowchart intent. The intent contains
|
|
448
|
+
screenId, optional presentationId/parentId, and nodes. Each node becomes a
|
|
449
|
+
canonical add-presentation-node operation and receives an exact inverse batch.
|
|
450
|
+
|
|
451
|
+
Intent example:
|
|
452
|
+
{"screenId":"scr_home","nodes":[{"id":"node_start","type":"ELLIPSE",
|
|
453
|
+
"name":"Start","x":80,"y":80,"width":120,"height":56,"children":[]}]}
|
|
454
|
+
|
|
455
|
+
Node types:
|
|
456
|
+
FRAME|ELLIPSE|GROUP|PATH|RECTANGLE|TEXT. PATH nodes may carry pathData, points,
|
|
457
|
+
strokes, and tokenBindings; arrowheads use stroke cap fields.
|
|
458
|
+
|
|
459
|
+
Output:
|
|
460
|
+
The normal atomic apply result. Follow with read-view or render to inspect it.`,
|
|
461
|
+
page: `Usage:
|
|
462
|
+
smallpen page <product.smallpen> --intent INTENT.json [--batch-id ID] [--dry-run]
|
|
463
|
+
[--warning-detail compact|full] [--json]
|
|
464
|
+
|
|
465
|
+
Purpose:
|
|
466
|
+
Compile and atomically apply declarative page nodes. The intent uses the same
|
|
467
|
+
screenId, optional presentationId/parentId, and nodes schema as flow. Use page
|
|
468
|
+
for general UI nodes and flow for flowchart-oriented PATH/ELLIPSE nodes.
|
|
469
|
+
Warning output follows apply; see smallpen apply --help for the compact schema.`,
|
|
470
|
+
token: `Usage:
|
|
471
|
+
smallpen token <product.smallpen> --intent INTENT.json [--batch-id ID] [--dry-run]
|
|
472
|
+
[--warning-detail compact|full] [--json]
|
|
473
|
+
|
|
474
|
+
Purpose:
|
|
475
|
+
Apply declarative Token operations through the canonical atomic batch contract.
|
|
476
|
+
Intent JSON contains an operations array using put-token, set-token-value,
|
|
477
|
+
set-active-token-themes, set-token-binding, clear-token-binding, remove-token,
|
|
478
|
+
or deprecate-token. Theme selection uses themePaths such as Product/Dark.
|
|
479
|
+
The current package revision is read immediately before applying the intent.`,
|
|
480
|
+
impact: `Usage:
|
|
481
|
+
smallpen impact <product.smallpen> (--token-id TOK | --path TOKEN.PATH) [--package-id PKG] [--json]
|
|
482
|
+
|
|
483
|
+
Purpose:
|
|
484
|
+
Find all Canonical nodes whose tokenBindings reference the selected Token. The
|
|
485
|
+
result includes screen, presentation, component, node, and field locations so
|
|
486
|
+
an Agent can preview the exact blast radius before changing a Token. Foundation
|
|
487
|
+
Tokens are resolved through the Product dependency; use --package-id to
|
|
488
|
+
disambiguate a Token ID or path that exists in both Packages.`,
|
|
489
|
+
apply: `Usage:
|
|
490
|
+
smallpen apply <package.smallpen> --batch BATCH.json [--dry-run]
|
|
491
|
+
[--warning-detail compact|full] [--confirm-unmatched] [--json]
|
|
492
|
+
|
|
493
|
+
Batch contract:
|
|
494
|
+
{"baseRevision":"...","batchId":"unique-id","operations":[...]}
|
|
495
|
+
The whole candidate validates and commits atomically. Success returns revision,
|
|
496
|
+
affected stable IDs/files, and an exact inverseBatch for Undo/Redo.
|
|
497
|
+
Revisions are content hashes, not increasing counters: restoring the exact
|
|
498
|
+
Canonical content restores its previous revision.
|
|
499
|
+
|
|
500
|
+
Token references on nodes:
|
|
501
|
+
tokenBindings uses package-qualified references, for example
|
|
502
|
+
{"fill":{"packageId":"pkg_foundation","assetId":"tok_brand"}}.
|
|
503
|
+
Use this field for Foundation and linked Library Tokens. appliedTokens is an
|
|
504
|
+
optional local-name compatibility field, not a cross-Package reference; do not
|
|
505
|
+
add it for an external binding or copy Tokens just to satisfy that field.
|
|
506
|
+
|
|
507
|
+
Raw-value confirmation policy:
|
|
508
|
+
Writes that resolve to no Token return a non-blocking design_token_value_unmatched
|
|
509
|
+
warning; the write itself is not gated. --confirm-unmatched is an explicit,
|
|
510
|
+
machine-readable acknowledgment: the response marks those warning groups with
|
|
511
|
+
"confirmed": true. Omitting the flag changes nothing about the write.
|
|
512
|
+
|
|
513
|
+
Warning output:
|
|
514
|
+
Default compact mode groups identical advice, puts exact matches first, and
|
|
515
|
+
shares contextScope in warningSummary. Each warning group retains all node
|
|
516
|
+
locations (warningIndex, operationIndex, nodeId, value); count is its occurrence
|
|
517
|
+
count. No warnings are dropped. Expand locations with the group's shared fields
|
|
518
|
+
and summary contextScope, then sort by warningIndex to recover the original list.
|
|
519
|
+
Select --warning-detail full before executing to emit the original flat list.
|
|
520
|
+
Do not replay an already applied batch just to expand warnings: compact output
|
|
521
|
+
already contains every occurrence, and stale-revision checks still apply.
|
|
522
|
+
|
|
523
|
+
Errors and recovery:
|
|
524
|
+
Each CLI call is independent; confirmed batch results are not retained across
|
|
525
|
+
calls. Retrying a committed batch against its old base returns stale_revision,
|
|
526
|
+
not a cached success. Batch IDs do not provide cross-process deduplication.
|
|
527
|
+
stale_revision returns actual/base revisions. Inspect current content first:
|
|
528
|
+
the intended change may already be committed. Rebuild only the remaining intent
|
|
529
|
+
against the actual revision and use a new unique batch ID; do not blindly replay.
|
|
530
|
+
--dry-run validates and returns the result/inverse without writing.
|
|
531
|
+
Invalid batches never write.`,
|
|
532
|
+
watch: `Usage:
|
|
533
|
+
smallpen watch <package.smallpen> [--interval MS] [--max-events N] [--json]
|
|
534
|
+
|
|
535
|
+
Watch is the public live mode: it polls the Canonical Package and emits one NDJSON
|
|
536
|
+
{event:"revision", packageId, revision} line whenever the on-disk revision
|
|
537
|
+
changes, and {event:"invalid"} lines when the package is temporarily unreadable.
|
|
538
|
+
The initial revision is emitted immediately. --max-events bounds the run for
|
|
539
|
+
scripted use; Ctrl-C exits 0. Watch never writes.
|
|
540
|
+
|
|
541
|
+
`,
|
|
542
|
+
repair: `Usage:
|
|
543
|
+
smallpen repair <product.smallpen> [--conflict N] [--action ACTION]
|
|
544
|
+
[action arguments] [--batch-id ID] [--json]
|
|
545
|
+
|
|
546
|
+
Actions:
|
|
547
|
+
retarget-reference --replacement-package-id PKG --replacement-asset-id ID
|
|
548
|
+
remove-dependent-usage no additional arguments
|
|
549
|
+
choose-foundation --foundation PATH
|
|
550
|
+
recreate-product-asset --asset-kind token|component --asset FILE
|
|
551
|
+
|
|
552
|
+
Without --action, returns all typed conflicts and choices. Each action is checked
|
|
553
|
+
against the selected conflict and committed through one atomic Operation Batch.
|
|
554
|
+
After each step the workspace is resolved again; remaining conflicts stay explicit.`,
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
export function printHelp(command) {
|
|
558
|
+
if (command && HELP[command]) {
|
|
559
|
+
process.stdout.write(`SmallPen ${command}\n\n${HELP[command]}
|
|
560
|
+
|
|
561
|
+
Common output and error contract:
|
|
562
|
+
--json emits stable English keys; localized labels remain additional data. Success
|
|
563
|
+
exits 0. Errors exit 1 as {error:{code,message,details}} and include exact
|
|
564
|
+
nextOperations when recovery requires refresh, replay, Repair, or confirmation.
|
|
565
|
+
Unknown options are not part of the command contract shown above.
|
|
566
|
+
|
|
567
|
+
Help / next:
|
|
568
|
+
smallpen --help
|
|
569
|
+
smallpen ${command} --help
|
|
570
|
+
`);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
process.stdout.write(`SmallPen Canonical Package CLI
|
|
574
|
+
|
|
575
|
+
Model:
|
|
576
|
+
One .smallpen directory is one Canonical Package. A Product references exactly one
|
|
577
|
+
same-workspace Foundation by permanent Package ID and relative path. Every CLI call
|
|
578
|
+
receives its Package locator and complete selectors explicitly; there is no current
|
|
579
|
+
selection, current page, or cross-call session state. Packages are never merged into
|
|
580
|
+
a synthetic multi-root file. CLI, Desktop, renderer, and Agents share one resolver
|
|
581
|
+
and one typed Operation Batch contract. No PostgreSQL, collaboration server, MCP,
|
|
582
|
+
or built-in AI Chat is required.
|
|
583
|
+
|
|
584
|
+
Choose an Agent view:
|
|
585
|
+
wireframe Fast 2D spatial reasoning from a scaled ASCII canvas plus a compact
|
|
586
|
+
[NN] key containing hierarchy, layer type, name, and stable ID.
|
|
587
|
+
semantic Exact hierarchy, bounds, text, alignment, typography, component,
|
|
588
|
+
variant, and Token properties.
|
|
589
|
+
PNG High-fidelity visual QA. The image is raster pixels only; it contains
|
|
590
|
+
no layer IDs, hierarchy, component references, or Token bindings.
|
|
591
|
+
both Use inspect-view --include-image, then reason from structure and verify
|
|
592
|
+
appearance from the PNG.
|
|
593
|
+
|
|
594
|
+
Usage:
|
|
595
|
+
smallpen <command> [arguments] [--json]
|
|
596
|
+
smallpen <command> --help
|
|
597
|
+
smallpen --version
|
|
598
|
+
|
|
599
|
+
Commands:
|
|
600
|
+
${COMMANDS.map(([name, summary]) => ` ${name.padEnd(18)} ${summary}`).join("\n")}
|
|
601
|
+
|
|
602
|
+
Common workflow:
|
|
603
|
+
smallpen init ./workspace --json
|
|
604
|
+
smallpen validate ./workspace/product.smallpen --json
|
|
605
|
+
smallpen search-tokens ./workspace/product.smallpen --color '#6750a4' --json
|
|
606
|
+
smallpen search-components ./workspace/product.smallpen --query button --json
|
|
607
|
+
smallpen catalog ./workspace/product.smallpen --json
|
|
608
|
+
smallpen read-view ./workspace/product.smallpen --json
|
|
609
|
+
smallpen discover ./workspace/product.smallpen --json
|
|
610
|
+
smallpen apply ./workspace/product.smallpen --batch batch.json --json
|
|
611
|
+
smallpen evidence ./workspace/product.smallpen --output review --json
|
|
612
|
+
smallpen import-draft ./workspace/import-1.smallpen --kind figma --input clipboard.html --json
|
|
613
|
+
smallpen repair ./workspace/product.smallpen --json
|
|
614
|
+
|
|
615
|
+
Run smallpen <command> --help for syntax, defaults, values, examples, outputs,
|
|
616
|
+
errors, and relevant next commands.
|
|
617
|
+
`);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export const COMMAND_NAMES = Object.freeze(COMMANDS.map(([name]) => name));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Launch gate (SP-050): the CLI's module graph uses Node 24+ APIs; fail with a
|
|
3
|
+
// clear message before ESM instantiation instead of an opaque engine error.
|
|
4
|
+
const [major] = process.versions.node.split(".").map(Number);
|
|
5
|
+
if (!Number.isFinite(major) || major < 24) {
|
|
6
|
+
process.stdout.write(
|
|
7
|
+
`${JSON.stringify({
|
|
8
|
+
error: {
|
|
9
|
+
code: "unsupported_node_runtime",
|
|
10
|
+
details: {
|
|
11
|
+
found: process.versions.node,
|
|
12
|
+
required: "Node.js 24 or newer on PATH",
|
|
13
|
+
},
|
|
14
|
+
message: `SmallPen CLI requires Node.js 24 or newer on PATH (found ${process.versions.node}). Upgrade Node and retry the same command.`,
|
|
15
|
+
},
|
|
16
|
+
}, null, 2)}\n`,
|
|
17
|
+
);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
import("./smallpen.mjs").catch((error) => {
|
|
21
|
+
process.stdout.write(
|
|
22
|
+
`${JSON.stringify({
|
|
23
|
+
error: {
|
|
24
|
+
code: error instanceof Error && error.name === "SmallPenError" ? "smallpen_error" : "internal_error",
|
|
25
|
+
message: error instanceof Error ? error.message : String(error),
|
|
26
|
+
},
|
|
27
|
+
}, null, 2)}\n`,
|
|
28
|
+
);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|