@firenet-designs/fnd-cli 2.4.0 → 2.6.0

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/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @firenet-designs/fnd-cli
20
20
  $ fnd COMMAND
21
21
  running command...
22
22
  $ fnd (--version)
23
- @firenet-designs/fnd-cli/2.4.0 linux-x64 node-v24.18.0
23
+ @firenet-designs/fnd-cli/2.6.0 linux-x64 node-v24.18.0
24
24
  $ fnd --help [COMMAND]
25
25
  USAGE
26
26
  $ fnd COMMAND
@@ -29,13 +29,79 @@ USAGE
29
29
  <!-- usagestop -->
30
30
  # Commands
31
31
  <!-- commands -->
32
+ * [`fnd alt-text`](#fnd-alt-text)
32
33
  * [`fnd backfill-project [SHOP]`](#fnd-backfill-project-shop)
34
+ * [`fnd caption`](#fnd-caption)
33
35
  * [`fnd create-app`](#fnd-create-app)
34
36
  * [`fnd create-project [SHOP]`](#fnd-create-project-shop)
35
37
  * [`fnd help [COMMAND]`](#fnd-help-command)
36
38
  * [`fnd token`](#fnd-token)
37
39
  * [`fnd workspace`](#fnd-workspace)
38
- * [`fnd workspace cleanup`](#fnd-workspace-cleanup)
40
+
41
+ ## `fnd alt-text`
42
+
43
+ Generate alt text for a site's images with a local Ollama vision model and write it back.
44
+
45
+ ```
46
+ USAGE
47
+ $ fnd alt-text [--api-key <value> --webflow] [--filter <value>] [--limit <value>] [--ollama-host <value>]
48
+ [--ollama-model <value>] [--only <value>... [--cms ]] [--output-stats <value>] [--select ] [--shopify | ] [--site-id
49
+ <value> ] [--skip]
50
+
51
+ FLAGS
52
+ --api-key=<value> Webflow API token (site-scoped). Prompted for if omitted.
53
+ --cms also add alt text to the images in CMS collection items. Off by default — only the site asset
54
+ library is walked.
55
+ --filter=<value> a JavaScript expression deciding which images are worth describing, e.g.
56
+ "fileSize>=sizes.KB(100) && width>=100 && height>=100". Available: fileSize (bytes), width,
57
+ height (pixels), url, type ("webp", "png", "jpeg", "svg", …), and sizes.KB/MB/GB helpers.
58
+ Images that fail it are skipped and reported separately. Evaluated after the download, since
59
+ dimensions can't be known before it.
60
+ --limit=<value> stop after this many images have been transcribed (counted across assets and CMS together)
61
+ --ollama-host=<value> base URL of the Ollama server. Prompted for if omitted.
62
+ --ollama-model=<value> the model used to describe the images. Omit it to pick from the vision-capable models pulled
63
+ on the host.
64
+ --only=<value>... only touch these CMS collections, matched case-insensitively against a collection's name or
65
+ slug. Repeat the flag for more than one.
66
+ --output-stats=<value> also write the run stats to this file as Markdown: totals, a row per image (url, description,
67
+ tokens, time, size), and the failures. Parent directories are created.
68
+ --select fetch the CMS collections and pick which ones to process interactively
69
+ --shopify run against a Shopify store (not implemented yet)
70
+ --site-id=<value> Webflow site ID. Prompted for if omitted.
71
+ --skip leave images that already have alt text alone instead of overwriting them
72
+ --webflow run against a Webflow site
73
+
74
+ DESCRIPTION
75
+ Generate alt text for a site's images with a local Ollama vision model and write it back.
76
+
77
+ Walks the Webflow site asset library and (with --cms) the image fields of CMS collection items, describes every image
78
+ that needs alt text, and PATCHes the description back. Images are fetched and described one at a time — a single local
79
+ model gains nothing from concurrency, and Webflow rate-limits. Nothing leaves your network except the Webflow API
80
+ calls.
81
+
82
+ CMS writes go to STAGING, so publish the site in Webflow to make them live. Any required flag you omit is prompted
83
+ for.
84
+
85
+ ALIASES
86
+ $ fnd caption
87
+
88
+ EXAMPLES
89
+ $ fnd alt-text --webflow --api-key <key> --site-id <id> --ollama-host http://localhost:11434 --ollama-model qwen3-vl:8b
90
+
91
+ $ fnd alt-text --webflow --skip --limit 20
92
+
93
+ $ fnd alt-text --webflow --cms --select
94
+
95
+ $ fnd alt-text --webflow --cms --only products --only sku
96
+
97
+ $ fnd alt-text --webflow --output-stats ./alt-text-run.md
98
+
99
+ $ fnd alt-text --webflow --filter "fileSize>=sizes.KB(100) && width>=100 && height>=100"
100
+
101
+ $ fnd alt-text --webflow --filter "type !== 'svg' && !url.includes('/icons/')"
102
+ ```
103
+
104
+ _See code: [src/commands/alt-text.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/alt-text.ts)_
39
105
 
40
106
  ## `fnd backfill-project [SHOP]`
41
107
 
@@ -62,7 +128,70 @@ EXAMPLES
62
128
  $ fnd backfill-project my-store "extra hints"
63
129
  ```
64
130
 
65
- _See code: [src/commands/backfill-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.4.0/src/commands/backfill-project.ts)_
131
+ _See code: [src/commands/backfill-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/backfill-project.ts)_
132
+
133
+ ## `fnd caption`
134
+
135
+ Generate alt text for a site's images with a local Ollama vision model and write it back.
136
+
137
+ ```
138
+ USAGE
139
+ $ fnd caption [--api-key <value> --webflow] [--filter <value>] [--limit <value>] [--ollama-host <value>]
140
+ [--ollama-model <value>] [--only <value>... [--cms ]] [--output-stats <value>] [--select ] [--shopify | ] [--site-id
141
+ <value> ] [--skip]
142
+
143
+ FLAGS
144
+ --api-key=<value> Webflow API token (site-scoped). Prompted for if omitted.
145
+ --cms also add alt text to the images in CMS collection items. Off by default — only the site asset
146
+ library is walked.
147
+ --filter=<value> a JavaScript expression deciding which images are worth describing, e.g.
148
+ "fileSize>=sizes.KB(100) && width>=100 && height>=100". Available: fileSize (bytes), width,
149
+ height (pixels), url, type ("webp", "png", "jpeg", "svg", …), and sizes.KB/MB/GB helpers.
150
+ Images that fail it are skipped and reported separately. Evaluated after the download, since
151
+ dimensions can't be known before it.
152
+ --limit=<value> stop after this many images have been transcribed (counted across assets and CMS together)
153
+ --ollama-host=<value> base URL of the Ollama server. Prompted for if omitted.
154
+ --ollama-model=<value> the model used to describe the images. Omit it to pick from the vision-capable models pulled
155
+ on the host.
156
+ --only=<value>... only touch these CMS collections, matched case-insensitively against a collection's name or
157
+ slug. Repeat the flag for more than one.
158
+ --output-stats=<value> also write the run stats to this file as Markdown: totals, a row per image (url, description,
159
+ tokens, time, size), and the failures. Parent directories are created.
160
+ --select fetch the CMS collections and pick which ones to process interactively
161
+ --shopify run against a Shopify store (not implemented yet)
162
+ --site-id=<value> Webflow site ID. Prompted for if omitted.
163
+ --skip leave images that already have alt text alone instead of overwriting them
164
+ --webflow run against a Webflow site
165
+
166
+ DESCRIPTION
167
+ Generate alt text for a site's images with a local Ollama vision model and write it back.
168
+
169
+ Walks the Webflow site asset library and (with --cms) the image fields of CMS collection items, describes every image
170
+ that needs alt text, and PATCHes the description back. Images are fetched and described one at a time — a single local
171
+ model gains nothing from concurrency, and Webflow rate-limits. Nothing leaves your network except the Webflow API
172
+ calls.
173
+
174
+ CMS writes go to STAGING, so publish the site in Webflow to make them live. Any required flag you omit is prompted
175
+ for.
176
+
177
+ ALIASES
178
+ $ fnd caption
179
+
180
+ EXAMPLES
181
+ $ fnd caption --webflow --api-key <key> --site-id <id> --ollama-host http://localhost:11434 --ollama-model qwen3-vl:8b
182
+
183
+ $ fnd caption --webflow --skip --limit 20
184
+
185
+ $ fnd caption --webflow --cms --select
186
+
187
+ $ fnd caption --webflow --cms --only products --only sku
188
+
189
+ $ fnd caption --webflow --output-stats ./alt-text-run.md
190
+
191
+ $ fnd caption --webflow --filter "fileSize>=sizes.KB(100) && width>=100 && height>=100"
192
+
193
+ $ fnd caption --webflow --filter "type !== 'svg' && !url.includes('/icons/')"
194
+ ```
66
195
 
67
196
  ## `fnd create-app`
68
197
 
@@ -83,11 +212,11 @@ EXAMPLES
83
212
  $ fnd create-app
84
213
  ```
85
214
 
86
- _See code: [src/commands/create-app.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.4.0/src/commands/create-app.ts)_
215
+ _See code: [src/commands/create-app.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/create-app.ts)_
87
216
 
88
217
  ## `fnd create-project [SHOP]`
89
218
 
90
- Scaffold a new client project: git on branch production, ignore files, Shopify theme pull, a Claude-generated CLAUDE.md, then a private GitHub repo under the FireNet-Designs org.
219
+ Scaffold a new client project: git on branch production, ignore files, Shopify theme pull, a Claude-generated CLAUDE.md, then a private GitHub repo under the FireNet-Designs org with production and staging branches pushed.
91
220
 
92
221
  ```
93
222
  USAGE
@@ -98,7 +227,7 @@ ARGUMENTS
98
227
 
99
228
  DESCRIPTION
100
229
  Scaffold a new client project: git on branch production, ignore files, Shopify theme pull, a Claude-generated
101
- CLAUDE.md, then a private GitHub repo under the FireNet-Designs org.
230
+ CLAUDE.md, then a private GitHub repo under the FireNet-Designs org with production and staging branches pushed.
102
231
 
103
232
  Requires the claude CLI (npm install -g @anthropic-ai/claude-code) for the CLAUDE.md step.
104
233
 
@@ -113,7 +242,7 @@ EXAMPLES
113
242
  $ fnd create-project my-store "Acme storefront, Klaviyo"
114
243
  ```
115
244
 
116
- _See code: [src/commands/create-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.4.0/src/commands/create-project.ts)_
245
+ _See code: [src/commands/create-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/create-project.ts)_
117
246
 
118
247
  ## `fnd help [COMMAND]`
119
248
 
@@ -165,7 +294,7 @@ EXAMPLES
165
294
  $ fnd token -s mystore -i <id> -c <secret> --write ./.mcp.json
166
295
  ```
167
296
 
168
- _See code: [src/commands/token.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.4.0/src/commands/token.ts)_
297
+ _See code: [src/commands/token.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/token.ts)_
169
298
 
170
299
  ## `fnd workspace`
171
300
 
@@ -173,10 +302,16 @@ Open a remote workspace: two-way sync the current directory to a remote Linux bo
173
302
 
174
303
  ```
175
304
  USAGE
176
- $ fnd workspace --ssh <value> [--delete-remote-dir] [--devtools <value>] [--ignore-vcs] [--remote-base
177
- <value>] [--rpc <value>] [--source remote|local]
305
+ $ fnd workspace --ssh <value> [--cleanup] [--delete-remote-dir] [--devtools <value>] [--ignore-vcs]
306
+ [--remote-base <value>] [--rpc <value>] [--source remote|local]
178
307
 
179
308
  FLAGS
309
+ --cleanup
310
+ tear down a leftover session instead of opening one — don't connect, just terminate this directory's Mutagen sync
311
+ and (using whatever other flags are set) strip the remote --devtools/--rpc MCP entries and, with
312
+ --delete-remote-dir, remove the remote dir. Re-run your original command with --cleanup appended after a session
313
+ that dropped without cleaning up.
314
+
180
315
  --delete-remote-dir
181
316
  on exit, delete the remote workspace directory instead of leaving the synced copy in place
182
317
 
@@ -237,47 +372,9 @@ EXAMPLES
237
372
  $ fnd workspace --ssh user@host --rpc port=7777:7700,profile=false,shell=zsh
238
373
 
239
374
  $ fnd workspace --ssh user@host --delete-remote-dir
240
- ```
241
-
242
- _See code: [src/commands/workspace/index.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.4.0/src/commands/workspace/index.ts)_
243
-
244
- ## `fnd workspace cleanup`
245
-
246
- Tear down a leftover workspace — use this if a `workspace` session dropped before it could clean up after itself.
247
-
248
- ```
249
- USAGE
250
- $ fnd workspace cleanup --ssh <value> [--delete-remote-dir] [--devtools] [--remote-base <value>] [--remote-dir
251
- <value>] [--rpc]
252
-
253
- FLAGS
254
- --delete-remote-dir also delete the remote workspace directory (by default the synced files are left in place)
255
- --devtools also strip the chrome-devtools MCP entry from the remote (only if the dropped session used
256
- --devtools)
257
- --remote-base=<value> [default: /home/fnd] base dir on the remote, used to derive the default remote directory path
258
- --remote-dir=<value> exact remote directory to target (defaults to the current dir mapping)
259
- --rpc also strip the local-shell MCP entry from the remote (only if the dropped session used --rpc)
260
- --ssh=<value> (required) remote to connect to, as user@host
261
-
262
- DESCRIPTION
263
- Tear down a leftover workspace — use this if a `workspace` session dropped before it could clean up after itself.
264
-
265
- Terminates any Mutagen sync sessions this machine started for the directory. Pass --devtools or --rpc to also strip
266
- the matching MCP entries from the remote (only do this if the dropped session used those flags). With no --remote-dir,
267
- it targets the same path `workspace` would use for the current directory. The synced files themselves are left in
268
- place unless you pass --delete-remote-dir.
269
-
270
- EXAMPLES
271
- $ fnd workspace cleanup --ssh user@host
272
-
273
- $ fnd workspace cleanup --ssh user@host --remote-dir /home/fnd/cole/fnd-cli
274
-
275
- $ fnd workspace cleanup --ssh user@host --devtools
276
-
277
- $ fnd workspace cleanup --ssh user@host --rpc
278
375
 
279
- $ fnd workspace cleanup --ssh user@host --delete-remote-dir
376
+ $ fnd workspace --ssh user@host --rpc 7700 --cleanup
280
377
  ```
281
378
 
282
- _See code: [src/commands/workspace/cleanup.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.4.0/src/commands/workspace/cleanup.ts)_
379
+ _See code: [src/commands/workspace/index.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/workspace/index.ts)_
283
380
  <!-- commandsstop -->
@@ -0,0 +1,56 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class AltText extends Command {
3
+ static aliases: string[];
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ cms: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ filter: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'ollama-host': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'ollama-model': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ only: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'output-stats': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ select: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ shopify: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ 'site-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ skip: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ webflow: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<void>;
22
+ /**
23
+ * Describe one image and account for it. Returns undefined when the model or
24
+ * the download failed — one bad image must not abort a run that may have
25
+ * hundreds of good ones behind it.
26
+ */
27
+ private describe;
28
+ /** Which CMS collections this run should walk. */
29
+ private pickCollections;
30
+ /**
31
+ * Ask which model to use, offering only the vision-capable models the host has
32
+ * already pulled — a text-only model would happily accept the request and
33
+ * describe nothing, and a model that isn't pulled would stall the run behind a
34
+ * silent download.
35
+ */
36
+ private pickModel;
37
+ /** The site asset library pass. Always runs; --cms only adds work on top of it. */
38
+ private processAssets;
39
+ private processCollection;
40
+ /**
41
+ * Fill in every image field on one item, then PATCH the item once. Fields that
42
+ * didn't change are left out of the payload entirely, and an item with no
43
+ * changed fields is never PATCHed.
44
+ */
45
+ private processItem;
46
+ /** Totals and averages only — the per-image rows live in the --output-stats file. */
47
+ private report;
48
+ /** --webflow / --shopify, or ask when neither was given. */
49
+ private resolvePlatform;
50
+ /**
51
+ * Write the Markdown report. This runs after everything else, so a run that
52
+ * gets this far has already written its alt text back to Webflow — failing to
53
+ * save the report loses the record, not the work, and the message says so.
54
+ */
55
+ private writeStats;
56
+ }