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