@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 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
+ });