@smallpen/cli 0.1.0-alpha.1 → 0.1.0-alpha.12

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 CHANGED
@@ -1,216 +1,274 @@
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"],
1
+ import {
2
+ COMMAND_CONTRACTS,
3
+ commandContract,
4
+ commandHelp,
5
+ } from "./command-contract.mjs";
6
+ import { commandOverview, printBriefHelp } from "./help-topics.mjs";
7
+ import { printText } from "./result-files.mjs";
8
+ import { COMMAND_GROUPS, publicArgv } from "./command-tree.mjs";
9
+
10
+ const COMMANDS = Object.entries(COMMAND_CONTRACTS).map(
11
+ ([name, { purpose }]) => [name, purpose],
12
+ );
13
+ // What help lists: every grouped action and the shared commands.
14
+ const PUBLIC_PATHS = [
15
+ ...Object.entries(COMMAND_GROUPS).flatMap(([group, { actions }]) =>
16
+ Object.entries(actions).map(([action, { purpose }]) => [`${group} ${action}`, purpose]),
17
+ ),
18
+ ...["view", "changes", "export", "validate", "help", "schema", "version"].map((name) => [name, COMMAND_CONTRACTS[name].purpose]),
37
19
  ];
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`;
20
+ // Help texts are kept per engine; they print with the grouped path a caller
21
+ // types ("smallpen apply" reads "smallpen advanced apply").
22
+ const publicName = (name) => publicArgv([name]).join(" ");
23
+ const publicText = (text) =>
24
+ text.replace(/\b(smallpen|SmallPen) ([a-z][a-z-]*)/g, (match, word, name) =>
25
+ Object.hasOwn(COMMAND_CONTRACTS, name) ? `${word} ${publicName(name)}` : match);
26
+
27
+ const WRITE_REPLY = `Reply size:
28
+ Default: batchId, revision, changed/alreadyApplied/dryRun, counts, warnings,
29
+ and summary. changeReportPath records exact before/after values. reverseEdit
30
+ describes an explicit new write at the confirmed revision; inverseBatchPath
31
+ locates its JSON. This does not rewind history. --inverse-out FILE chooses a
32
+ durable caller-owned file (only when the batch commits). --compact is default.
33
+ --full includes inverseBatch, guidance, and exact before/after diff values.
34
+ After later edits, read current values and restore intended fields in a new
35
+ scoped edit. Never force an old snapshot by replacing its baseRevision.
36
+ For safe retries choose batchId before the first call and reuse it with the
37
+ same input. The local ledger retains at most 500 batch identities.
38
+ smallpen apply PACKAGE --batch FILE --inverse-out FILE.reverse.json`;
48
39
 
49
40
  const HELP = {
41
+ assets: `Usage:
42
+ smallpen advanced style list PACKAGE [--kind colors|typographies]
43
+ [--limit N] [--offset N] [--json]
44
+
45
+ Read the App's stored color and typography assets. Use token for design values
46
+ and token theme for their options and defaults. File assets are managed through
47
+ asset media and asset font. Write styles in their owning package.`,
48
+ help: `Usage:
49
+ smallpen help [OBJECT [GROUP] [ACTION]|rules [TOPIC]] [--json]
50
+
51
+ Use COMMAND --help for options, or schema for JSON input shapes. Use help rules for shared operation rules; schema command OBJECT ACTION
52
+ describes one command at a time.`,
53
+ themes: `Usage:
54
+ smallpen token theme list PACKAGE [--theme GROUP/NAME]... [--json]
55
+
56
+ Read project-defined groups, options (named Group/Option), defaults and this
57
+ call's selection. This is a lightweight settings query, with no Token inventory.
58
+ selection and option active flags mean this command's parameters and defaults,
59
+ never a previous CLI call. The stored App selection is unrelated to this query
60
+ and omitted by default; --full includes appSelection for explicit inspection.
61
+ It never supplies the CLI defaults.
62
+ No --theme uses project defaults; App current selection is independent.
63
+ --theme overrides only named groups. No data or App state is written.
64
+ Write: token theme add|rename|default|delete --theme Group/Option.`,
65
+ view: `Usage:
66
+ smallpen view PACKAGE [what] [--as text|spec|wireframe|png|issues] [--theme G/O]...
67
+
68
+ Look at anything by name; no IDs. What:
69
+ (nothing) the whole package: themes, Tokens, components, pages
70
+ --tokens | --token color every Token, or those under a name
71
+ --components every component
72
+ --component Button one component: every variant
73
+ [--variant "Style=secondary"] [--element Label] one variant, one part of it
74
+ --pages [--platform mobile] every page
75
+ --page Board one page ("Board" or "Tasks / Board")
76
+ [--platform mobile] [--element "Top bar"] one version, one part of it
77
+ Three design outputs: spec for implementation and provenance, wireframe for
78
+ spatial orientation, PNG for high-fidelity appearance. --as spec reports actual
79
+ bounds separately from fixed/fill/hug constraints, Token/component sources and
80
+ node diagnostics. --element expands details. Compare layout.hash and selection
81
+ across outputs. ?/partial means unresolved; never implement from seed geometry.
82
+ --as text (default) is a compact navigation outline; issues lists layout, text
83
+ and contrast problems. Tokens have text, wireframe and png. Specifications
84
+ target one page/component; choose a target before requesting --as spec.
85
+ --theme Group/Option shows values under that option for this call only.
86
+ Large results go to a file. Unknown names fail with the names that exist;
87
+ "Name [2]" picks one of a name stored twice.
88
+ Next: changes PACKAGE --since REVISION after someone edits the design.`,
89
+ "token-export": `Usage:
90
+ smallpen token export PACKAGE [--type TYPE] [--token PREFIX] [--by GROUP]
91
+ [--format json|flat] [--theme G/O]... [--output DIR] [--json]
92
+
93
+ Export resolved Token values as JSON. --by GROUP writes one file
94
+ per option of that theme group, named after the option (--type string
95
+ --by Language with options English and Chinese -> English.json, Chinese.json);
96
+ without it one file, tokens.json, holds the values of the selected themes
97
+ (project defaults, or --theme). json nests by name
98
+ (text.task.new -> {"text":{"task":{"new":...}}}); flat keeps one key per name.
99
+ Other groups use the project defaults or --theme.`,
100
+ changes: `Usage:
101
+ smallpen changes PACKAGE [--since REVISION] [--limit N] [--full] [--json]
102
+
103
+ What changed since a revision you saw, by name: themes, Tokens (with values),
104
+ components (per variant and element) and pages (per platform and element),
105
+ including edits a person made in the App. Every read and write reports a
106
+ revision; keep the latest one and pass it as --since to align code with the
107
+ design. Without --since: the most recent edits, who made them (app or cli) and
108
+ the revision before each. --full adds the structured list. Nothing is stored
109
+ for the caller.`,
110
+ export: `Usage:
111
+ smallpen export PACKAGE [--page P [--platform X] [--element E]]
112
+ [--component C [--variant V] [--element E]] [--design-system]
113
+ [--format text|spec|wireframe|png] [--output FILE] [--scale N] [--json]
114
+
115
+ Save a specification (implementation), wireframe (structure, default) or PNG
116
+ (appearance), named the way view names it. Nothing named is the main page.
117
+ Read --format spec before implementation; inspect PNG only when the caller
118
+ can see images. Text files contain LF newlines and alignment spaces.
119
+ Missing --output uses a temporary file. Returns path, hash, bytes, target,
120
+ themes and revisions. --design-system exports the generated Token/component
121
+ page; do not draw a copy. --format png --evidence writes PNG, .spec.txt,
122
+ .wireframe.txt and JSON evidence from one native layout snapshot. Read skipped
123
+ coverage; untested sizes/states and undeclared business rules remain unknown.`,
50
124
  version: `Usage:
51
- smallpen --version
52
125
  smallpen version [--json]
53
126
 
54
127
  Output:
55
- The installed CLI package version, or {name,version} with --json.
128
+ JSON {name,version} for the installed CLI package.
56
129
  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]
130
+ schema: `Usage:
131
+ smallpen schema [TOPIC] [--json]
132
+ smallpen schema operation TYPE [--json]
60
133
 
61
134
  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.
135
+ Print the exact JSON that apply, token, page, flow, and init accept: fields,
136
+ types, allowed values, a valid example, and the inverse each operation
137
+ returns. No package is opened. Field lists come from the validators, and every
138
+ example is tested against a workspace made by smallpen init.
139
+
140
+ Topics:
141
+ command NAME one command: parameters, types, defaults and constraints
142
+ commands command names and purposes
143
+ operations every operation type, its purpose, Package, required fields
144
+ operation TYPE one operation in full
145
+ batch batch contract, Foundation vs Product, worked example
146
+ node node fields, textStyle, tokenBindings
147
+ node-types what each node type requires
148
+ token Tokens in token sets, DTCG files, bindings
149
+ theme token sets and themes, --theme, Foundation + Product themes
150
+ token-types the value each Token type accepts
151
+ component-set Component Set, Axis roles (configuration, state), variants
152
+ instance INSTANCE nodes and overrides
153
+ presentation Presentation fields, size, and resizing
154
+ screen Screen fields
155
+ scenario Scenario fields and viewport
156
+ context Context Axes (viewport, density, locale, ...)
157
+ init blank package paths, layouts and creation behavior
158
+
159
+ Examples:
160
+ smallpen schema operations
161
+ smallpen schema operation update-component-node
162
+ smallpen schema component-set`,
163
+ init: `Usage:
164
+ smallpen project init PATH [--name NAME] [--layout single|foundation-product] [--json]
65
165
 
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\").
166
+ Create a blank package immediately. PATH ending in .smallpen creates that
167
+ package; a directory creates a new workspace containing a package. The default
168
+ is one self-contained package with one canvas and Theme/Default, no Tokens,
169
+ components, business flows or platform copies. Existing targets are rejected.
170
+ --layout foundation-product creates two blank packages with a local dependency.
171
+ No business questionnaire, saved selection or initialization state is required.
70
172
 
71
173
  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
174
+ smallpen project init ./acme.smallpen --name Acme --json
175
+ smallpen project init ./acme --layout foundation-product --json
176
+
177
+ Next: smallpen schema command project init --json`,
75
178
 
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
179
  validate: `Usage:
89
- smallpen validate <product.smallpen> [--json]
180
+ smallpen validate PACKAGE [--page P [--platform X] [--element E]]
181
+ [--component C [--variant V] [--element E]] [--design-system]
182
+ [--theme G/O]... [--limit N] [--offset N] [--json]
90
183
 
91
184
  Purpose:
92
- Validate Canonical JSON, IDs, references, Product/Foundation dependency, all Context
93
- combinations used by projections, and strict Scenario/component selections.
185
+ Validate the stored data, references and the Product/Foundation dependency,
186
+ and check text fit, bounds, overlaps and contrast for the target. A page
187
+ checks every version unless --platform names one; a component every variant
188
+ unless --variant names one. Nothing named checks the main page and lists
189
+ names stored twice.
94
190
 
95
191
  Output:
96
- status valid with Product and Foundation revisions; dependency conflicts return
97
- repair_required with exact choices.
192
+ status/dataStatus valid means the data and references are valid. Visual
193
+ issues are separate: read visualStatus, issueCount, issues and
194
+ coverage.skipped. Valid data is not visual approval. Issue pagination does
195
+ not shrink coverage. Dependency conflicts return repair_required with exact
196
+ choices. view --as issues gives the same issues by element name.
98
197
 
99
198
  Next:
100
- smallpen inspect <package> --json
101
- smallpen repair <product> --json`,
199
+ smallpen view <package> --as issues --json
200
+ smallpen project repair <product> --json`,
102
201
  inspect: `Usage:
103
202
  smallpen inspect <package.smallpen> [--json]
104
203
 
105
204
  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.
205
+ Return Package identity, revision, and domain counts. --full includes all
206
+ domain summaries and format capabilities.
109
207
 
110
208
  Next:
111
- smallpen list <package> --kind screens --json
112
- smallpen read-view <package> --json`,
209
+ smallpen view <package> --json`,
113
210
  list: `Usage:
114
211
  smallpen list <package.smallpen> [--kind KIND] [--offset N] [--limit N] [--json]
115
212
 
116
213
  Values:
117
214
  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.
215
+ Defaults: kind=all, offset=0, limit=20. Return identities and counts, without
216
+ node trees or variants. page includes total/hasMore; --full includes definitions.
217
+ Default item fields follow the public allowlist in schema command list.
218
+ Descriptions and editor root IDs belong to target reads, not this directory.
219
+ Omit visibility=public and deprecated=false. Only report private visibility,
220
+ actual deprecation and a configured replacement when they affect reuse.
221
+ JSON is compact in all modes; --full adds data, not pretty-printing.
155
222
 
156
223
  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.`,
224
+ Look at one by name: smallpen view <package> --page NAME | --component NAME.`,
167
225
  catalog: `Usage:
168
- smallpen catalog <product.smallpen> [--context AXIS=VALUE]... [--json]
226
+ smallpen catalog <product.smallpen> [--context AXIS=VALUE]... [--theme GROUP/NAME]...
227
+ [--offset N] [--limit N] [--full] [--json]
169
228
 
170
229
  Purpose:
171
- Derive legal Component variants, inherited assets, Effective Tokens, Contexts,
172
- Scenarios, Screens, and requirement coverage from the same validated workspace.`,
230
+ Return Component identities (limit=20) and domain counts. Use component for one
231
+ candidate's legal selections, tokens/search-tokens for values, or --full for
232
+ the complete Catalog. Token definitions, effective values and inventory are
233
+ not repeated in the default response. Default items use the same public
234
+ discovery fields as list; descriptions require a target read.`,
173
235
  "search-components": `Usage:
174
236
  smallpen search-components <product.smallpen> --query TEXT [--limit N] [--json]
175
237
 
176
238
  Purpose:
177
239
  Find reusable Product, public Foundation, and linked Library components before
178
240
  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
241
+ owning Package, source layer, variant count, and an executable
180
242
  owner-scoped component command (argv plus a POSIX-shell command).
243
+ Default candidates use the public discovery fields; read component for
244
+ descriptions and legal choices. Private reuse restrictions remain explicit.
181
245
 
182
246
  State:
183
247
  Every query is explicit and self-contained. It never creates a current component
184
248
  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
249
  tokens: `Usage:
206
- smallpen tokens <product.smallpen> [--context AXIS=VALUE]... [--json]
250
+ smallpen tokens <package.smallpen> [--theme GROUP/NAME]... [--context AXIS=VALUE]...
251
+ [--offset N] [--limit N] [--locale LOCALE] [--full] [--json]
252
+ smallpen tokens <package.smallpen> --definitions [--group GROUP]
253
+ [--offset N] [--limit N] [--full] [--json]
207
254
 
208
255
  Purpose:
209
- List Effective Tokens using Product first, Context specificity within a layer,
210
- then Foundation fallback.`,
256
+ List Effective Tokens: the active token sets of the selected themes (a later
257
+ set overrides an earlier one by name; a Product's own sets sit on top of its
258
+ Foundation's), then Context specificity for legacy contextValues. themes[]
259
+ lists every theme with active, owner (package|foundation) and its sets.
260
+ --theme selects themes for this read only (one per group); unknown groups or
261
+ names exit unknown_token_theme with validThemes. labels follow --locale or the
262
+ environment. Default: 20 items with target, Token id/path/type and resolved
263
+ value, plus page.total/hasMore. --full includes every definition and source chain.
264
+ --definitions reads exact stored Tokens in every option Set, with owning
265
+ packageId, setId, group and option. Optional --group filters the App Set group.
266
+ Missing cells have no item; they do not inherit Default. This mode cannot take
267
+ --theme or --context. Use the ordinary tokens read to resolve a combination.`,
211
268
  "search-tokens": `Usage:
212
269
  smallpen search-tokens <product.smallpen> (--color COLOR | --value JSON | --query TEXT | --type TYPE)
213
- [--context AXIS=VALUE]... [--limit N] [--json]
270
+ [--context AXIS=VALUE]... [--theme GROUP/NAME]... [--all-themes]
271
+ [--limit N] [--json]
214
272
 
215
273
  Purpose:
216
274
  Find reusable Product and Foundation Design Tokens before writing a hard-coded
@@ -218,6 +276,11 @@ Purpose:
218
276
  values. Without --context, every finite Web/Desktop/theme/etc. Context is searched
219
277
  and each resolved value reports its matching Contexts. Repeating --context restricts
220
278
  the search explicitly. Name queries match Token path, description, and type.
279
+ Token themes: by default project defaults are searched; --theme overrides only
280
+ named groups, as every resolved read does. --all-themes searches every
281
+ selection of one theme per group (at most 64) and gives each item themes[],
282
+ the selections where it has that value. themeScope {mode active|explicit|all,
283
+ selections} says which themes were searched.
221
284
 
222
285
  Examples:
223
286
  smallpen search-tokens product.smallpen --color '#6750a4' --json
@@ -228,143 +291,47 @@ State:
228
291
  Every search is self-contained. It never reads or writes a current selection,
229
292
  previous result, session, or Web UI state.`,
230
293
  "effective-token": `Usage:
231
- smallpen effective-token <product.smallpen> --token-id TOK [--package-id PKG]
232
- [--context AXIS=VALUE]... [--json]
294
+ smallpen token show <package.smallpen> --path TOKEN.NAME
295
+ [--theme GROUP/NAME]... [--json]
233
296
 
234
297
  Defaults:
235
- package-id defaults to the Product Package ID; Context axes use defaults.`,
298
+ Themes use project defaults, independently of the App's active selection.
299
+ --theme overrides only named groups.`,
236
300
  "explain-token": `Usage:
237
- smallpen explain-token <product.smallpen> --token-id TOK [--package-id PKG]
238
- [--context AXIS=VALUE]... [--json]
301
+ smallpen token explain <package.smallpen> --path TOKEN.NAME
302
+ [--theme GROUP/NAME]... [--json]
239
303
 
240
304
  Output:
241
305
  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
306
  "import-media": `Usage:
336
- smallpen import-media <package.smallpen> --file MEDIA_FILE [--media-id media_...]
307
+ smallpen asset media import <package.smallpen> --file MEDIA_FILE
337
308
  [--media-path PATH] [--name NAME] [--json]
338
309
 
339
310
  Import is the public binary Media entry for an existing Product: PNG, JPEG, GIF,
340
311
  WebP, and SVG are detected from bytes, dimensions are validated, and the blob is
341
312
  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.
313
+ still forbids blob writes; asset media import is a file entry, not a batch operation.
314
+ asset media delete --media NAME deletes the descriptor; the content-addressed blob
315
+ itself is immutable and shared.
345
316
 
346
317
  Errors and recovery:
347
318
  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
319
 
354
- Removes one Media descriptor from the Asset Library atomically; the inverse batch
355
- restores it. Unknown ids exit missing_media.
320
+ Output:
321
+ descriptor and revision, plus batchId and the revision-guarded reverse edit (inverseBatch).
356
322
 
357
323
  `,
358
324
  "import-font": `Usage:
359
- smallpen import-font <package.smallpen> --file FONT_FILE --family NAME
360
- [--font-id font_...] [--variant-id fvar_...]
325
+ smallpen asset font import <package.smallpen> --file FONT_FILE --family NAME
361
326
  [--weight 400] [--style normal] [--name NAME] [--json]
362
327
 
363
328
  Public Font entry for an existing Product. TTF and OTF are validated by SFNT
364
329
  signature and converted to WOFF (the renderer loads ttf/otf/woff). WOFF imports
365
330
  as-is after table validation. A valid WOFF2 reaches an explicit
366
331
  unsupported_font_conversion boundary (no WOFF2 decoder is bundled); corrupt
367
- fonts fail invalid_font_blob. Either way nothing is written.
332
+ fonts fail invalid_font_blob. Either way nothing is written. Success returns
333
+ the family, files, revision, batchId, and the exact inverseBatch. Use the
334
+ family by name: "fontFamily": "Inter", or a font-family Token.
368
335
 
369
336
  `,
370
337
  "library-refresh": `Usage:
@@ -376,6 +343,13 @@ mismatch exits library_id_mismatch with expected/actual identities and leaves th
376
343
  previous verified snapshot readable offline. Success reports before/after
377
344
  revisions; failures keep the previous revision.
378
345
 
346
+ When a fetch fails and a verified cache exists, the command exits 0 with
347
+ after.cache "stale", the cached revision, and after.warning naming the cause.
348
+ With no cache the Library cannot resolve: the command exits repair_required
349
+ with a library_unavailable conflict whose message names the cause. Requests
350
+ follow at most five redirects, only within the Library's origin; each file
351
+ is capped at 50 MB.
352
+
379
353
  `,
380
354
  "import-draft": `Usage:
381
355
  smallpen import-draft <new-draft.smallpen> --kind figma|svg|png --input FILE
@@ -396,13 +370,15 @@ Next:
396
370
  "import-tokens": `Usage:
397
371
  smallpen import-tokens <package.smallpen> --input TOKENS.json [--set-name NAME]
398
372
  [--select SET/NAME]... [--apply] [--dry-run]
399
- [--batch-id ID] [--json]
373
+ [--batch-id ID] [--compact] [--inverse-out FILE] [--json]
400
374
 
401
375
  Purpose:
402
376
  Import a DTCG token file (Penpot or Tokens Studio export, single-set, multi-set
403
377
  with $themes/$metadata, or legacy value/type) into the package's Penpot-shaped
404
378
  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.
379
+ tokens, sets, and themes that would be added, changed, or removed (counts and
380
+ up to 20 identities per action). --full includes the replacement library and
381
+ exact before/after values.
406
382
  --dry-run keeps the operation in no-write review mode even with --apply;
407
383
  that combination also validates the prepared Operation Batch without committing.
408
384
 
@@ -439,64 +415,47 @@ Selections:
439
415
  Output:
440
416
  A typed Operation Batch whose baseRevision is the current Canonical revision.
441
417
  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
418
  impact: `Usage:
481
- smallpen impact <product.smallpen> (--token-id TOK | --path TOKEN.PATH) [--package-id PKG] [--json]
419
+ smallpen token impact <package.smallpen> --path TOKEN.NAME [--json]
482
420
 
483
421
  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.`,
422
+ Find every element whose bindings use the Token, by page, component and
423
+ element, so an Agent can see what a change touches before making it.
424
+ Foundation Tokens are resolved through the Product dependency.`,
489
425
  apply: `Usage:
490
- smallpen apply <package.smallpen> --batch BATCH.json [--dry-run]
491
- [--warning-detail compact|full] [--confirm-unmatched] [--json]
426
+ smallpen apply <package.smallpen> --batch BATCH.json [--dry-run] [--explain] [--diff]
427
+ [--warning-detail compact|full] [--confirm-unmatched]
428
+ [--compact] [--inverse-out FILE] [--json]
492
429
 
493
430
  Batch contract:
494
431
  {"baseRevision":"...","batchId":"unique-id","operations":[...]}
432
+ baseRevision is package.revision from smallpen inspect PACKAGE --json.
495
433
  The whole candidate validates and commits atomically. Success returns revision,
496
- affected stable IDs/files, and an exact inverseBatch for Undo/Redo.
434
+ affected stable IDs/files, a revision-guarded reverse edit, and changed
435
+ (false, with noChange, when the batch leaves the Package as it was).
497
436
  Revisions are content hashes, not increasing counters: restoring the exact
498
437
  Canonical content restores its previous revision.
499
438
 
439
+ Operations:
440
+ smallpen schema operations every type, purpose, Package, required fields
441
+ smallpen schema operation TYPE fields, allowed values, example, inverse
442
+ Unknown operation types and unknown fields are rejected, with a suggestion.
443
+ In the default layout everything goes to the one Package. In the
444
+ foundation-product layout Tokens, themes, Context Axes, and shared Component
445
+ Sets go to the Foundation; Screens, nodes, Instances, and Scenarios go to the
446
+ Product.
447
+ Example: {"baseRevision":"<revision>","batchId":"resize-home-1","operations":[
448
+ {"type":"update-presentation-node","screenId":"scr_home",
449
+ "presentationId":"pres_home_mobile","nodeId":"node_home_root",
450
+ "changes":{"width":360,"height":640}}]}
451
+
452
+ Component instance overrides:
453
+ set-instance-override {screenId, presentationId?, nodeId, overridePath, value}
454
+ and clear-instance-override {screenId, presentationId?, nodeId, overridePath}
455
+ change one field of one Instance node without touching the shared component.
456
+ overridePath is "<sourceNodeId>:<field>", or "<nestedInstanceId>__<sourceNodeId>:<field>"
457
+ inside a nested instance. Fields: fills, name, opacity, text, visible.
458
+
500
459
  Token references on nodes:
501
460
  tokenBindings uses package-qualified references, for example
502
461
  {"fill":{"packageId":"pkg_foundation","assetId":"tok_brand"}}.
@@ -506,7 +465,10 @@ Token references on nodes:
506
465
 
507
466
  Raw-value confirmation policy:
508
467
  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,
468
+ warning; the write itself is not gated. Warnings are design advice, not
469
+ validity: validate does not repeat them, and warningSummary.note says so. Raw
470
+ width/height values are listed only when a Token resolves to them or lies within
471
+ 10% (at least 2); the rest are counted in warningSummary.suppressed. --confirm-unmatched is an explicit,
510
472
  machine-readable acknowledgment: the response marks those warning groups with
511
473
  "confirmed": true. Omitting the flag changes nothing about the write.
512
474
 
@@ -518,32 +480,75 @@ Warning output:
518
480
  and summary contextScope, then sort by warningIndex to recover the original list.
519
481
  Select --warning-detail full before executing to emit the original flat list.
520
482
  Do not replay an already applied batch just to expand warnings: compact output
521
- already contains every occurrence, and stale-revision checks still apply.
483
+ already contains every listed occurrence, and stale-revision checks still apply.
484
+
485
+ ${WRITE_REPLY}
522
486
 
523
487
  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.
488
+ Committed batch identities are recorded beside the Package. Retrying the same
489
+ batchId with the same baseRevision and operations, from any process, returns the
490
+ recorded confirmation with alreadyApplied:true and writes nothing, as long as
491
+ the Package is still at the recorded revision. If the batch's effect was undone
492
+ (the Package is back at its baseRevision) the retry applies it again as a new
493
+ write. If later writes moved the Package elsewhere the retry exits
494
+ batch_superseded with committedRevision and currentRevision. The same batchId
495
+ with a different baseRevision or operations exits batch_id_conflict.
496
+ The name-based writes (page, component, flow, token, theme, asset, canvas)
497
+ and import-tokens rebuild the batch from the current
498
+ revision; rerunning one with the same --batch-id and input after success
499
+ returns alreadyApplied:true under the same rules.
527
500
  stale_revision returns actual/base revisions. Inspect current content first:
528
501
  the intended change may already be committed. Rebuild only the remaining intent
529
502
  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.
503
+ --dry-run validates and returns the result/inverse without writing. --explain
504
+ adds per-operation targets and --diff a before/after field diff of every
505
+ changed entry; both are previews that never write, with or without --dry-run.
531
506
  Invalid batches never write.`,
532
507
  watch: `Usage:
533
508
  smallpen watch <package.smallpen> [--interval MS] [--max-events N] [--json]
534
509
 
535
510
  Watch is the public live mode: it polls the Canonical Package and emits one NDJSON
536
511
  {event:"revision", packageId, revision} line whenever the on-disk revision
537
- changes, and {event:"invalid"} lines when the package is temporarily unreadable.
512
+ changes. An unreadable package emits one {event:"invalid", code, message} line
513
+ (again only when the error changes); recovery emits its revision line again.
538
514
  The initial revision is emitted immediately. --max-events bounds the run for
539
515
  scripted use; Ctrl-C exits 0. Watch never writes.
540
516
 
541
517
  `,
518
+ "migrate-themes": `Usage:
519
+ smallpen migrate-themes <package.smallpen> --output NEW_PATH [--json]
520
+
521
+ Purpose:
522
+ Upgrade by copy (the original stays untouched): write a new Package whose
523
+ theme-kind Context Axes and Token contextValues are Penpot token sets and
524
+ themes. For a Product with a Foundation, both are copied: --output names the new
525
+ workspace directory, which receives the Foundation and the Product under their
526
+ current directory names, still linked. Otherwise --output is the new
527
+ <name>.smallpen path.
528
+
529
+ Conversion:
530
+ Each theme-kind axis becomes a theme group named after the axis. The set base
531
+ holds every Token's default value (Token ids, names and bindings stay as they
532
+ are); one set per non-default value holds that value's overrides as Tokens of
533
+ the same name (ids tok_<base id>__<value>); one theme per value activates
534
+ [base, its set]; the default value's theme is active. DTCG token files become
535
+ the token library. A Product stores the active Foundation theme on its
536
+ dependency. Scenario contexts on theme axes become Scenario themes. Legacy
537
+ {x, y} shadows become {offsetX, offsetY}.
538
+
539
+ Warnings:
540
+ contextValues whose when names a non-theme axis, or more than one axis, cannot
541
+ be a theme set: they are kept as they were and listed in warnings with their
542
+ Tokens. Product Token overrides (overrideOf) stay DTCG Tokens.
543
+
544
+ Next:
545
+ smallpen view NEW_PATH --theme Theme/Dark --json`,
542
546
  repair: `Usage:
543
547
  smallpen repair <product.smallpen> [--conflict N] [--action ACTION]
544
548
  [action arguments] [--batch-id ID] [--json]
545
549
 
546
550
  Actions:
551
+ select-instance-variant [--selection JSON]; default: the closest variant
547
552
  retarget-reference --replacement-package-id PKG --replacement-asset-id ID
548
553
  remove-dependent-usage no additional arguments
549
554
  choose-foundation --foundation PATH
@@ -551,70 +556,179 @@ Actions:
551
556
 
552
557
  Without --action, returns all typed conflicts and choices. Each action is checked
553
558
  against the selected conflict and committed through one atomic Operation Batch.
554
- After each step the workspace is resolved again; remaining conflicts stay explicit.`,
559
+ After each step the workspace is resolved again; remaining conflicts stay explicit.
560
+
561
+ Instances whose variant is gone:
562
+ An Instance selects variant values. When its Foundation renames those values
563
+ while the Product is open in the same Desktop or Web session, the Product's
564
+ Instances follow the variant in a batch of their own (with its own undo).
565
+ When the variant is deleted, or the rename happened elsewhere, the Product
566
+ enters Repair: the conflict is missing_variant with degraded:true, the
567
+ Instance still renders with the closest variant (a stale_instance_variant
568
+ diagnostic says so), and the choices are select-instance-variant (the closest
569
+ variant; --selection picks one of validSelections), retarget-reference or
570
+ remove-dependent-usage (delete the Instance).`,
555
571
  };
556
572
 
557
- export function printHelp(command) {
558
- if (command && HELP[command]) {
559
- process.stdout.write(`SmallPen ${command}\n\n${HELP[command]}
573
+ export function printHelp(command, { full = false, route } = {}) {
574
+ const say = (text) => printText(publicText(text));
575
+ const name = route?.name ?? command;
576
+ if (Object.hasOwn(COMMAND_GROUPS, command) || !command) {
577
+ printBriefHelp(command, undefined, { full });
578
+ say(
579
+ "\nOptions: -h, --help; --version\nUse smallpen help OBJECT ACTION for options. Common rules: smallpen help rules\n",
580
+ );
581
+ return;
582
+ }
583
+ if (!full) {
584
+ if (!command) {
585
+ printBriefHelp();
586
+ say(
587
+ "\nOptions: -h, --help; --version\nUse smallpen COMMAND --help for options. Common rules: smallpen help rules\n",
588
+ );
589
+ } else
590
+ say(
591
+ `SmallPen ${command}\n\n${commandContract(command).purpose}\n\nUsage: smallpen ${command} ${commandContract(
592
+ command,
593
+ )
594
+ .arguments.map((a) => (a.required ? `<${a.name}>` : `[${a.name}]`))
595
+ .join(
596
+ " ",
597
+ )} [options]\n\n${commandHelp(command)}\n\nInput shapes: smallpen schema command ${command} --json\n`,
598
+ );
599
+ if (command && commandOverview(command).notes)
600
+ say(commandOverview(command).notes.join("\n") + "\n");
601
+ return;
602
+ }
603
+ const guidance = commandGuidance(command, route);
604
+ if (command && guidance) {
605
+ say(`SmallPen ${name}\n\n${guidance}
606
+
607
+ ${commandHelp(name)}
560
608
 
561
609
  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
610
+ Default JSON is compact and bounded to 8 KiB. Large complete results are saved
611
+ to resultFile.path with bytes/SHA-256; stdout retains status and write receipts.
612
+ Search/read the JSON with jq or your file tools, and text with rg/sed.
613
+ Each call uses random filenames of a fixed format in one system temporary
614
+ folder: smallpen-TIMESTAMP-UUID-KIND.ext. Every invocation cleans CLI files
615
+ whose mtime is older than 30 minutes; active calls are protected.
616
+ --output chooses a retained file. Lists default to 20 items; --full expands data
617
+ but still uses files for large results. --stdout opts into large stdout.
618
+ --base64 explicitly requests inline image bytes; PNG paths are the default.
619
+ --json emits stable English keys; localized labels remain additional data and
620
+ follow --locale where offered, else LC_ALL, LC_MESSAGES, or LANG (zh* selects
621
+ Chinese, anything else English). Success
622
+ exits 0. Errors exit 1 as {error:{code,message,details,writeState}} and include exact
564
623
  nextOperations when recovery requires refresh, replay, Repair, or confirmation.
565
- Unknown options are not part of the command contract shown above.
624
+ Unknown options are not part of the command contract shown above. Only options
625
+ shown with ..., repeated, or described as repeatable may appear more than once;
626
+ any other option given twice exits duplicate_option.
566
627
 
567
628
  Help / next:
568
629
  smallpen --help
569
- smallpen ${command} --help
630
+ smallpen ${name} --help
570
631
  `);
571
632
  return;
572
633
  }
573
- process.stdout.write(`SmallPen Canonical Package CLI
634
+ say(`SmallPen Canonical Package CLI
574
635
 
575
636
  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
637
+ One .smallpen directory is one Canonical Package. init creates one
638
+ self-contained Package (<name>.smallpen) by default: token sets and themes,
639
+ Context Axes, Component Sets, Screens, Presentations, Scenarios. Write
640
+ everything to it and pass it to view, validate, export, token list and
641
+ search. With init --layout foundation-product it creates a Foundation
642
+ (<name>-foundation.smallpen: token sets and themes, Context Axes, shared
643
+ Component Sets) and a Product (<name>.smallpen: Screens, Presentations,
644
+ Scenarios) instead: write Tokens and components to the Foundation, Screens and
645
+ nodes to the Product, and pass the Product to the reads.
646
+ Themes are Penpot token sets + themes. token theme list reads groups/options/defaults;
647
+ token theme add|rename|default|delete write them by Group/Option name. Resolved reads take
648
+ --theme GROUP/NAME for this call only: project defaults, then an explicit
649
+ Scenario, then explicit overrides of named groups. App active selection is
650
+ independent. set-active-token-themes explicitly stores the App selection.
651
+ A Product references exactly one same-workspace Foundation by permanent Package
652
+ ID and relative path. Every CLI call
578
653
  receives its Package locator and complete selectors explicitly; there is no current
579
654
  selection, current page, or cross-call session state. Packages are never merged into
580
655
  a synthetic multi-root file. CLI, Desktop, renderer, and Agents share one resolver
581
656
  and one typed Operation Batch contract. No PostgreSQL, collaboration server, MCP,
582
657
  or built-in AI Chat is required.
583
658
 
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.
659
+ CLI entry point:
660
+ smallpen help
661
+ smallpen help OBJECT ACTION
662
+ smallpen schema command OBJECT ACTION --json
663
+ smallpen help rules
664
+ CLI usage is complete without a Skill. Scene Skills supply task-specific rules.
665
+
666
+ Separate actions:
667
+ settings token/component/page/canvas/asset/flow actions save changes by
668
+ name; advanced apply runs an exact batch, such as an undo batch.
669
+ definitions token theme list is a cheap settings query; search finds reusable Tokens
670
+ and components.
671
+ view Navigate with text; implement with spec; orient with wireframe;
672
+ inspect appearance with PNG. Choose one page/component/region.
673
+ validate Problems and coverage; data validity is separate from visual QA.
674
+ export Specification, wireframe or PNG files with layout.hash/selection.
675
+ Three design outputs: spec for implementation and provenance; wireframe for
676
+ structure; PNG for appearance. --format png --evidence writes all three from
677
+ one native solve. Match layout.hash and selection; read unknowns and coverage.
678
+ export defaults to a text wireframe; PNG requires --format png. The CLI does
679
+ not detect model image capability. Text-only models may export PNG deliverables
680
+ but must review specifications/issues and must not claim visual inspection.
593
681
 
594
682
  Usage:
595
683
  smallpen <command> [arguments] [--json]
596
684
  smallpen <command> --help
597
- smallpen --version
685
+ smallpen version --json
598
686
 
599
687
  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
688
+ ${PUBLIC_PATHS.map(([name, summary]) => ` ${name.padEnd(24)} ${summary}`).join("\n")}
689
+
690
+ Query examples:
691
+ smallpen init ./acme --json
692
+ smallpen themes ./acme/acme.smallpen --json
693
+ smallpen help rules --json
694
+ smallpen validate ./acme/acme.smallpen --json
695
+ smallpen search-tokens ./acme/acme.smallpen --color '#6750a4' --json
696
+ smallpen search-components ./acme/acme.smallpen --query button --json
697
+ smallpen catalog ./acme/acme.smallpen --json
698
+ smallpen view ./acme/acme.smallpen --json
699
+ smallpen apply ./acme/acme.smallpen --batch batch.json --json
700
+ smallpen export ./acme/acme.smallpen --format text --json
701
+ smallpen export ./acme/acme.smallpen --format png --json
702
+ smallpen export ./acme/acme.smallpen --design-system --json
703
+ smallpen import-draft ./acme/import-1.smallpen --kind figma --input clipboard.html --json
704
+ smallpen repair ./acme/acme.smallpen --json
614
705
 
706
+ Output:
707
+ Defaults are summaries, paginated identities, and artifact paths (8 KiB budget).
708
+ JSON is compact. Directory fields are a public CLI contract, not a dump of
709
+ internal objects. Reuse restrictions appear only when they affect a decision.
710
+ Read one target before editing. --full requests complete data; --base64 requests
711
+ image bytes. Operation schemas are available individually: smallpen schema operation TYPE.
712
+ smallpen schema returns cliVersion/contractRevision for stable contract caching.
713
+ Package reads return revision; workspace views also return dependency revisions.
714
+ Cache a view by those revisions plus its selectors, theme, context and locale.
715
+
716
+ Worked example:
717
+ smallpen schema batch --full
718
+
719
+ Every write input has a schema and a tested example: smallpen schema
720
+ (operations, node, token, theme, component-set, instance, presentation, batch, init).
615
721
  Run smallpen <command> --help for syntax, defaults, values, examples, outputs,
616
722
  errors, and relevant next commands.
617
723
  `);
618
724
  }
619
725
 
620
726
  export const COMMAND_NAMES = Object.freeze(COMMANDS.map(([name]) => name));
727
+ export function commandGuidance(command, route) {
728
+ if (route && (command === "assets" || route.fixed || !HELP[command])) {
729
+ const contract = commandContract(route.name);
730
+ const args = contract.arguments.map(({ name, required }) => required ? `<${name}>` : `[${name}]`).join(" ");
731
+ return `Usage:\n smallpen ${route.name} ${args} [options]\n\n${route.purpose}`;
732
+ }
733
+ return HELP[command] ? publicText(HELP[command]) : undefined;
734
+ }