@firenet-designs/fnd-cli 2.6.0 → 2.7.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.
Files changed (54) hide show
  1. package/README.md +103 -63
  2. package/bin/dev.js +1 -1
  3. package/dist/commands/alt-text.d.ts +64 -15
  4. package/dist/commands/alt-text.js +277 -65
  5. package/dist/commands/backfill-project.js +1 -1
  6. package/dist/commands/create-project.js +1 -1
  7. package/dist/commands/workspace/index.d.ts +3 -2
  8. package/dist/commands/workspace/index.js +96 -49
  9. package/dist/lib/alt-text.d.ts +33 -2
  10. package/dist/lib/alt-text.js +56 -4
  11. package/dist/lib/mcp/bracket-args.d.ts +37 -0
  12. package/dist/lib/mcp/bracket-args.js +65 -0
  13. package/dist/lib/mcp/define-tool.d.ts +52 -0
  14. package/dist/lib/mcp/define-tool.js +2 -0
  15. package/dist/lib/mcp/registry.d.ts +38 -0
  16. package/dist/lib/mcp/registry.js +98 -0
  17. package/dist/lib/mcp/server.d.ts +66 -0
  18. package/dist/lib/mcp/server.js +176 -0
  19. package/dist/lib/mcp/tools/shopify-common.d.ts +139 -0
  20. package/dist/lib/mcp/tools/shopify-common.js +167 -0
  21. package/dist/lib/mcp/tools/shopify-execute.d.ts +2 -0
  22. package/dist/lib/mcp/tools/shopify-execute.js +105 -0
  23. package/dist/lib/mcp/tools/shopify-file-delete.d.ts +2 -0
  24. package/dist/lib/mcp/tools/shopify-file-delete.js +49 -0
  25. package/dist/lib/mcp/tools/shopify-file-replace.d.ts +2 -0
  26. package/dist/lib/mcp/tools/shopify-file-replace.js +79 -0
  27. package/dist/lib/mcp/tools/shopify-file-search.d.ts +2 -0
  28. package/dist/lib/mcp/tools/shopify-file-search.js +199 -0
  29. package/dist/lib/mcp/tools/shopify-file-upload.d.ts +2 -0
  30. package/dist/lib/mcp/tools/shopify-file-upload.js +76 -0
  31. package/dist/lib/shopify/graphql/AccessScopes.graphql +7 -0
  32. package/dist/lib/shopify/graphql/CurrentBulkOperation.graphql +8 -0
  33. package/dist/lib/shopify/graphql/FileCreate.graphql +25 -0
  34. package/dist/lib/shopify/graphql/FileDelete.graphql +11 -0
  35. package/dist/lib/shopify/graphql/FileReplace.graphql +26 -0
  36. package/dist/lib/shopify/graphql/FileStatus.graphql +19 -0
  37. package/dist/lib/shopify/graphql/FilesBulkQuery.graphql +27 -0
  38. package/dist/lib/shopify/graphql/ProductsBulkQuery.graphql +27 -0
  39. package/dist/lib/shopify/graphql/SearchFiles.graphql +36 -0
  40. package/dist/lib/shopify/graphql/StagedUploadsCreate.graphql +20 -0
  41. package/dist/lib/shopify/graphql/StartBulkQuery.graphql +16 -0
  42. package/dist/lib/shopify/graphql/UpdateFileAlt.graphql +9 -0
  43. package/dist/lib/shopify/shopify.d.ts +228 -0
  44. package/dist/lib/shopify/shopify.js +662 -0
  45. package/dist/lib/workspace.d.ts +19 -8
  46. package/dist/lib/workspace.js +13 -13
  47. package/oclif.manifest.json +48 -46
  48. package/package.json +17 -10
  49. package/dist/hooks/init/check-for-updates.d.ts +0 -3
  50. package/dist/hooks/init/check-for-updates.js +0 -15
  51. package/dist/lib/kv-flag.d.ts +0 -15
  52. package/dist/lib/kv-flag.js +0 -75
  53. package/dist/lib/rpc.d.ts +0 -69
  54. package/dist/lib/rpc.js +0 -313
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.6.0 linux-x64 node-v24.18.0
23
+ @firenet-designs/fnd-cli/2.7.0 node-v24.18.0
24
24
  $ fnd --help [COMMAND]
25
25
  USAGE
26
26
  $ fnd COMMAND
@@ -44,14 +44,20 @@ Generate alt text for a site's images with a local Ollama vision model and write
44
44
 
45
45
  ```
46
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]
47
+ $ fnd alt-text [--api-key <value>] [--dry] [--filter <value>] [--limit <value>] [--ollama-host <value>]
48
+ [--ollama-model <value>] [--only <value>... --cms] [--output-stats <value>] [--platform webflow|shopify] [--select ]
49
+ [--site-id <value>] [--skip]
50
50
 
51
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.
52
+ --api-key=<value> Webflow API token (site-scoped). Webflow only Shopify auth comes from the Shopify CLI.
53
+ Prompted for if omitted.
54
+ --cms also add alt text to the images in CMS collection items. Webflow only. Off by default — only
55
+ the site asset library is walked.
56
+ --dry report how many images the run would caption without describing or writing anything. Every
57
+ image is still downloaded and run through --filter so the count is accurate, but nothing
58
+ reaches the model. The --ollama-host/--ollama-model and --output-stats flags are ignored in
59
+ this mode, so --dry can be added to (and removed from) a full command without changing the
60
+ rest.
55
61
  --filter=<value> a JavaScript expression deciding which images are worth describing, e.g.
56
62
  "fileSize>=sizes.KB(100) && width>=100 && height>=100". Available: fileSize (bytes), width,
57
63
  height (pixels), url, type ("webp", "png", "jpeg", "svg", …), and sizes.KB/MB/GB helpers.
@@ -65,43 +71,48 @@ FLAGS
65
71
  slug. Repeat the flag for more than one.
66
72
  --output-stats=<value> also write the run stats to this file as Markdown: totals, a row per image (url, description,
67
73
  tokens, time, size), and the failures. Parent directories are created.
74
+ --platform=<option> which platform to run against. Prompted for if omitted.
75
+ <options: webflow|shopify>
68
76
  --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.
77
+ --site-id=<value> Webflow site ID, or the Shopify store (mystore or mystore.myshopify.com). Prompted for if
78
+ omitted.
71
79
  --skip leave images that already have alt text alone instead of overwriting them
72
- --webflow run against a Webflow site
73
80
 
74
81
  DESCRIPTION
75
82
  Generate alt text for a site's images with a local Ollama vision model and write it back.
76
83
 
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.
84
+ Pick a platform with --platform (webflow or shopify). For Webflow, walks the site asset library and (with --cms) the
85
+ image fields of CMS collection items and PATCHes each description back through the Data API. For Shopify, walks the
86
+ store's file library and writes alt text back through the Shopify CLI (which owns the auth — no --api-key). Images are
87
+ fetched and described one at a time; nothing but the platform API calls leaves your network.
81
88
 
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.
89
+ Webflow CMS writes go to STAGING, so publish the site in Webflow to make them live. Any required flag you omit is
90
+ prompted for.
84
91
 
85
92
  ALIASES
86
93
  $ fnd caption
87
94
 
88
95
  EXAMPLES
89
- $ fnd alt-text --webflow --api-key <key> --site-id <id> --ollama-host http://localhost:11434 --ollama-model qwen3-vl:8b
96
+ $ fnd alt-text --platform webflow --api-key <key> --site-id <id> --ollama-host http://localhost:11434 --ollama-model qwen3-vl:8b
90
97
 
91
- $ fnd alt-text --webflow --skip --limit 20
98
+ $ fnd alt-text --platform shopify --site-id mystore
92
99
 
93
- $ fnd alt-text --webflow --cms --select
100
+ $ fnd alt-text --platform shopify --site-id mystore --dry
94
101
 
95
- $ fnd alt-text --webflow --cms --only products --only sku
102
+ $ fnd alt-text --platform webflow --skip --limit 20
96
103
 
97
- $ fnd alt-text --webflow --output-stats ./alt-text-run.md
104
+ $ fnd alt-text --platform webflow --cms --select
98
105
 
99
- $ fnd alt-text --webflow --filter "fileSize>=sizes.KB(100) && width>=100 && height>=100"
106
+ $ fnd alt-text --platform webflow --cms --only products --only sku
100
107
 
101
- $ fnd alt-text --webflow --filter "type !== 'svg' && !url.includes('/icons/')"
108
+ $ fnd alt-text --platform webflow --output-stats ./alt-text-run.md
109
+
110
+ $ fnd alt-text --platform webflow --filter "fileSize>=sizes.KB(100) && width>=100 && height>=100"
111
+
112
+ $ fnd alt-text --platform webflow --filter "type !== 'svg' && !url.includes('/icons/')"
102
113
  ```
103
114
 
104
- _See code: [src/commands/alt-text.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/alt-text.ts)_
115
+ _See code: [src/commands/alt-text.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.7.0/src/commands/alt-text.ts)_
105
116
 
106
117
  ## `fnd backfill-project [SHOP]`
107
118
 
@@ -128,7 +139,7 @@ EXAMPLES
128
139
  $ fnd backfill-project my-store "extra hints"
129
140
  ```
130
141
 
131
- _See code: [src/commands/backfill-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/backfill-project.ts)_
142
+ _See code: [src/commands/backfill-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.7.0/src/commands/backfill-project.ts)_
132
143
 
133
144
  ## `fnd caption`
134
145
 
@@ -136,14 +147,20 @@ Generate alt text for a site's images with a local Ollama vision model and write
136
147
 
137
148
  ```
138
149
  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]
150
+ $ fnd caption [--api-key <value>] [--dry] [--filter <value>] [--limit <value>] [--ollama-host <value>]
151
+ [--ollama-model <value>] [--only <value>... --cms] [--output-stats <value>] [--platform webflow|shopify] [--select ]
152
+ [--site-id <value>] [--skip]
142
153
 
143
154
  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.
155
+ --api-key=<value> Webflow API token (site-scoped). Webflow only Shopify auth comes from the Shopify CLI.
156
+ Prompted for if omitted.
157
+ --cms also add alt text to the images in CMS collection items. Webflow only. Off by default — only
158
+ the site asset library is walked.
159
+ --dry report how many images the run would caption without describing or writing anything. Every
160
+ image is still downloaded and run through --filter so the count is accurate, but nothing
161
+ reaches the model. The --ollama-host/--ollama-model and --output-stats flags are ignored in
162
+ this mode, so --dry can be added to (and removed from) a full command without changing the
163
+ rest.
147
164
  --filter=<value> a JavaScript expression deciding which images are worth describing, e.g.
148
165
  "fileSize>=sizes.KB(100) && width>=100 && height>=100". Available: fileSize (bytes), width,
149
166
  height (pixels), url, type ("webp", "png", "jpeg", "svg", …), and sizes.KB/MB/GB helpers.
@@ -157,40 +174,45 @@ FLAGS
157
174
  slug. Repeat the flag for more than one.
158
175
  --output-stats=<value> also write the run stats to this file as Markdown: totals, a row per image (url, description,
159
176
  tokens, time, size), and the failures. Parent directories are created.
177
+ --platform=<option> which platform to run against. Prompted for if omitted.
178
+ <options: webflow|shopify>
160
179
  --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.
180
+ --site-id=<value> Webflow site ID, or the Shopify store (mystore or mystore.myshopify.com). Prompted for if
181
+ omitted.
163
182
  --skip leave images that already have alt text alone instead of overwriting them
164
- --webflow run against a Webflow site
165
183
 
166
184
  DESCRIPTION
167
185
  Generate alt text for a site's images with a local Ollama vision model and write it back.
168
186
 
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.
187
+ Pick a platform with --platform (webflow or shopify). For Webflow, walks the site asset library and (with --cms) the
188
+ image fields of CMS collection items and PATCHes each description back through the Data API. For Shopify, walks the
189
+ store's file library and writes alt text back through the Shopify CLI (which owns the auth — no --api-key). Images are
190
+ fetched and described one at a time; nothing but the platform API calls leaves your network.
173
191
 
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.
192
+ Webflow CMS writes go to STAGING, so publish the site in Webflow to make them live. Any required flag you omit is
193
+ prompted for.
176
194
 
177
195
  ALIASES
178
196
  $ fnd caption
179
197
 
180
198
  EXAMPLES
181
- $ fnd caption --webflow --api-key <key> --site-id <id> --ollama-host http://localhost:11434 --ollama-model qwen3-vl:8b
199
+ $ fnd caption --platform webflow --api-key <key> --site-id <id> --ollama-host http://localhost:11434 --ollama-model qwen3-vl:8b
200
+
201
+ $ fnd caption --platform shopify --site-id mystore
202
+
203
+ $ fnd caption --platform shopify --site-id mystore --dry
182
204
 
183
- $ fnd caption --webflow --skip --limit 20
205
+ $ fnd caption --platform webflow --skip --limit 20
184
206
 
185
- $ fnd caption --webflow --cms --select
207
+ $ fnd caption --platform webflow --cms --select
186
208
 
187
- $ fnd caption --webflow --cms --only products --only sku
209
+ $ fnd caption --platform webflow --cms --only products --only sku
188
210
 
189
- $ fnd caption --webflow --output-stats ./alt-text-run.md
211
+ $ fnd caption --platform webflow --output-stats ./alt-text-run.md
190
212
 
191
- $ fnd caption --webflow --filter "fileSize>=sizes.KB(100) && width>=100 && height>=100"
213
+ $ fnd caption --platform webflow --filter "fileSize>=sizes.KB(100) && width>=100 && height>=100"
192
214
 
193
- $ fnd caption --webflow --filter "type !== 'svg' && !url.includes('/icons/')"
215
+ $ fnd caption --platform webflow --filter "type !== 'svg' && !url.includes('/icons/')"
194
216
  ```
195
217
 
196
218
  ## `fnd create-app`
@@ -212,7 +234,7 @@ EXAMPLES
212
234
  $ fnd create-app
213
235
  ```
214
236
 
215
- _See code: [src/commands/create-app.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/create-app.ts)_
237
+ _See code: [src/commands/create-app.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.7.0/src/commands/create-app.ts)_
216
238
 
217
239
  ## `fnd create-project [SHOP]`
218
240
 
@@ -242,7 +264,7 @@ EXAMPLES
242
264
  $ fnd create-project my-store "Acme storefront, Klaviyo"
243
265
  ```
244
266
 
245
- _See code: [src/commands/create-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/create-project.ts)_
267
+ _See code: [src/commands/create-project.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.7.0/src/commands/create-project.ts)_
246
268
 
247
269
  ## `fnd help [COMMAND]`
248
270
 
@@ -294,7 +316,7 @@ EXAMPLES
294
316
  $ fnd token -s mystore -i <id> -c <secret> --write ./.mcp.json
295
317
  ```
296
318
 
297
- _See code: [src/commands/token.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/token.ts)_
319
+ _See code: [src/commands/token.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.7.0/src/commands/token.ts)_
298
320
 
299
321
  ## `fnd workspace`
300
322
 
@@ -303,12 +325,12 @@ Open a remote workspace: two-way sync the current directory to a remote Linux bo
303
325
  ```
304
326
  USAGE
305
327
  $ fnd workspace --ssh <value> [--cleanup] [--delete-remote-dir] [--devtools <value>] [--ignore-vcs]
306
- [--remote-base <value>] [--rpc <value>] [--source remote|local]
328
+ [--remote-base <value>] [--site-id <value>] [--source remote|local] [--with-tool <value>...]
307
329
 
308
330
  FLAGS
309
331
  --cleanup
310
332
  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
333
+ and (using whatever other flags are set) strip the remote --devtools/--with-tool MCP entries and, with
312
334
  --delete-remote-dir, remove the remote dir. Re-run your original command with --cleanup appended after a session
313
335
  that dropped without cleaning up.
314
336
 
@@ -328,13 +350,10 @@ FLAGS
328
350
  --remote-base=<value>
329
351
  [default: /home/fnd] base dir on the remote; the workspace lands at <base>/<local-user>/<dir-name>
330
352
 
331
- --rpc=<value>
332
- expose a run_local_command MCP tool to Claude on the remote that executes commands back on THIS machine (the one
333
- running fnd workspace). Value is
334
- port=<port|remote:local>[,profile=<true|1|false|0>][,shell=<bash|batch|powershell|sh|zsh>] port opens a reverse
335
- tunnel (ssh -R <remote>:localhost:<local>) to a command server started here; shell defaults to the shell fnd
336
- workspace was called from; profile (default true) controls whether the shell loads its startup files — with it on,
337
- POSIX shells run interactively (-i) so rc files like ~/.bashrc or ~/.zshrc are sourced and tools such as nvm work.
353
+ --site-id=<value>
354
+ the Shopify store (mystore or mystore.myshopify.com) the Shopify --with-tool tools operate on. Required when any
355
+ Shopify tool (shopify-file-* or shopify-execute) is selected; fixing the store here is a safety boundary — the
356
+ remote AI cannot target another store.
338
357
 
339
358
  --source=<option>
340
359
  which side wins on conflict: "remote" = this server (where the workspace shell runs), "local" = the machine you ran
@@ -344,6 +363,21 @@ FLAGS
344
363
  --ssh=<value>
345
364
  (required) remote to connect to, as user@host
346
365
 
366
+ --with-tool=<value>...
367
+ expose an fnd tool to Claude on the remote via a loopback MCP server on THIS machine (the one running fnd
368
+ workspace), reached through a reverse tunnel. Repeatable. Value is <name> or <name>[options]; available:
369
+ shopify-execute[<scopes=all|<list>[,ask]>], shopify-file-delete[[<ask>]], shopify-file-replace[[<ask>]],
370
+ shopify-file-search[[<ask>]], shopify-file-upload[[<ask>]]. e.g. shopify-file-upload (with --site-id mystore) lets
371
+ the remote AI upload files from the workspace into that Shopify store using your local Shopify CLI;
372
+ shopify-file-replace and shopify-file-delete add in-place replace and delete; shopify-file-search looks up files by
373
+ name/size/type/url (metadata only, no image bytes) to dedupe before upload or find oversized images; shopify-execute
374
+ runs arbitrary Admin GraphQL and REQUIRES a scopes option — shopify-execute[scopes=all] for broad access or
375
+ shopify-execute[scopes=read_products+write_orders] for an explicit set (which the store is then trimmed to, revoking
376
+ anything extra, so the AI is held to least privilege). Append ,ask (e.g. shopify-file-delete[ask],
377
+ shopify-execute[ask,scopes=all]) to make that tool prompt for confirmation before EVERY call, even under the
378
+ remote's auto-accept/bypass permissions — use it to gate the powerful/destructive tools. Prerequisites (auth,
379
+ scopes) are checked and set up before connecting.
380
+
347
381
  DESCRIPTION
348
382
  Open a remote workspace: two-way sync the current directory to a remote Linux box with Mutagen and drop into a shell
349
383
  there, tearing the sync down on exit.
@@ -367,14 +401,20 @@ EXAMPLES
367
401
 
368
402
  $ fnd workspace --ssh user@host --devtools 9333:9222
369
403
 
370
- $ fnd workspace --ssh user@host --rpc port=7777:7700
404
+ $ fnd workspace --ssh user@host --with-tool shopify-file-upload --site-id mystore
405
+
406
+ $ fnd workspace --ssh user@host --with-tool shopify-file-upload --with-tool shopify-file-search --with-tool "shopify-file-delete[ask]" --site-id mystore
407
+
408
+ $ fnd workspace --ssh user@host --with-tool shopify-file-upload --with-tool shopify-file-replace --with-tool shopify-file-delete --site-id mystore.myshopify.com
409
+
410
+ $ fnd workspace --ssh user@host --with-tool "shopify-execute[scopes=read_products+read_orders]" --site-id mystore
371
411
 
372
- $ fnd workspace --ssh user@host --rpc port=7777:7700,profile=false,shell=zsh
412
+ $ fnd workspace --ssh user@host --with-tool "shopify-execute[ask,scopes=all]" --site-id mystore
373
413
 
374
414
  $ fnd workspace --ssh user@host --delete-remote-dir
375
415
 
376
- $ fnd workspace --ssh user@host --rpc 7700 --cleanup
416
+ $ fnd workspace --ssh user@host --with-tool shopify-file-upload --site-id mystore --cleanup
377
417
  ```
378
418
 
379
- _See code: [src/commands/workspace/index.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.6.0/src/commands/workspace/index.ts)_
419
+ _See code: [src/commands/workspace/index.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v2.7.0/src/commands/workspace/index.ts)_
380
420
  <!-- commandsstop -->
package/bin/dev.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S node --loader ts-node/esm --disable-warning=ExperimentalWarning
1
+ #!/usr/bin/env -S node --loader ts-node/esm --conditions=development --disable-warning=ExperimentalWarning
2
2
 
3
3
  import {execute} from '@oclif/core'
4
4
 
@@ -1,30 +1,47 @@
1
- import { Command } from '@oclif/core';
1
+ import { Command, Interfaces } from '@oclif/core';
2
2
  export default class AltText extends Command {
3
3
  static aliases: string[];
4
4
  static description: string;
5
5
  static examples: string[];
6
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>;
7
+ 'api-key': Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
8
+ cms: Interfaces.BooleanFlag<boolean>;
9
+ dry: Interfaces.BooleanFlag<boolean>;
10
+ filter: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
11
+ limit: Interfaces.OptionFlag<number | undefined, Interfaces.CustomOptions>;
12
+ 'ollama-host': Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
13
+ 'ollama-model': Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
14
+ only: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
15
+ 'output-stats': Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
16
+ platform: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
17
+ select: Interfaces.BooleanFlag<boolean>;
18
+ 'site-id': Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
19
+ skip: Interfaces.BooleanFlag<boolean>;
20
20
  };
21
21
  run(): Promise<void>;
22
22
  /**
23
23
  * Describe one image and account for it. Returns undefined when the model or
24
24
  * the download failed — one bad image must not abort a run that may have
25
25
  * hundreds of good ones behind it.
26
+ *
27
+ * Also the fallback for a --dry run: when an image's metadata wasn't known up
28
+ * front (so the caller couldn't filter it cheaply), the describer downloads it
29
+ * just far enough to measure it, and this counts it via recordDry.
26
30
  */
27
31
  private describe;
32
+ /**
33
+ * Guarantee the store's app installation has the scopes this run needs before
34
+ * any captioning. `required` is the minimal set for the run — the full
35
+ * REQUIRED_SCOPES for a real run, just DRY_RUN_SCOPES (read_files) for a --dry
36
+ * one, which never writes or maps product context. A non-zero exit from the
37
+ * scope probe is the CLI's way of saying the store isn't authenticated at all;
38
+ * a clean exit with a scope missing means it's authenticated but under-scoped.
39
+ * Either way `shopify store auth` with `required` fixes it — it adds only
40
+ * what's missing, and a write scope already covers its read counterpart.
41
+ */
42
+ private ensureShopifyScopes;
43
+ /** The `[n/limit] label — file` progress line, shared by real and dry counting. */
44
+ private logImageLine;
28
45
  /** Which CMS collections this run should walk. */
29
46
  private pickCollections;
30
47
  /**
@@ -43,10 +60,42 @@ export default class AltText extends Command {
43
60
  * changed fields is never PATCHed.
44
61
  */
45
62
  private processItem;
63
+ /**
64
+ * The Shopify path: make sure the CLI is present and the store is authenticated
65
+ * with the file scopes, then caption every image in the file library. Always
66
+ * returns false — there's no separate staging step to warn about.
67
+ */
68
+ private processShopify;
69
+ /**
70
+ * The Webflow path: the site asset library, then (with --cms) whichever CMS
71
+ * collections the flags select. Returns whether any CMS work was done, so the
72
+ * summary can remind the user to publish staging.
73
+ */
74
+ private processWebflow;
75
+ /**
76
+ * Count one image toward a --dry total from metadata already in hand — no
77
+ * download, no model. Applies --filter to decide caption-vs-skip; the caller
78
+ * has already logged the image line.
79
+ */
80
+ private recordDry;
46
81
  /** Totals and averages only — the per-image rows live in the --output-stats file. */
47
82
  private report;
48
- /** --webflow / --shopify, or ask when neither was given. */
83
+ /**
84
+ * The --dry summary: how many images a real run would caption, the ones the
85
+ * --filter would skip, and any that couldn't even be downloaded. No tokens,
86
+ * time, or descriptions to report — nothing was described.
87
+ */
88
+ private reportDry;
89
+ /** The --platform value, or ask when it was omitted. */
49
90
  private resolvePlatform;
91
+ /**
92
+ * Stop a spinner and undo ora's parting cursor move. On stop, ora's clear()
93
+ * runs cursorTo(indent) on its stream (stderr) — and since stdout shares the
94
+ * terminal's cursor, the very next this.log would start at that column and be
95
+ * pushed right by `indent` spaces. Resetting to column 0 keeps the following
96
+ * line at exactly the indent it asks for.
97
+ */
98
+ private stopSpinner;
50
99
  /**
51
100
  * Write the Markdown report. This runs after everything else, so a run that
52
101
  * gets this far has already written its alt text back to Webflow — failing to