@mccaskie-labs/workbench-config 0.2.2 → 0.2.4

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.
@@ -1,6 +1,6 @@
1
1
  // Generated by `npm run capabilities:build` from capabilities/*.json. Do not edit.
2
2
  // check:capabilities fails in Checks when this file is stale.
3
- export const MANIFEST_VERSION = "cfe1c836b884";
3
+ export const MANIFEST_VERSION = "15786d683e1e";
4
4
  export const capabilities = {
5
5
  "ai": {
6
6
  "$schema": "./schema.json",
@@ -294,10 +294,190 @@ export const capabilities = {
294
294
  "cost_hint_usd": 0.008
295
295
  }
296
296
  ],
297
+ "offer": {
298
+ "pattern": "feature_or_content",
299
+ "when": "The client wants the app to write, summarise, sort, answer questions or pull details out of text with AI, as a feature visitors use, or wants wording for the site written once.",
300
+ "say": "I'll do this with Workbench AI, which uses Claude through Workbench, so the app holds no AI key and spending has a monthly cap.",
301
+ "answers_file": "workbench/ai/<name>.json",
302
+ "per": "feature or piece of wording",
303
+ "answers_format": "For a feature: `{ \"format\": 1, \"route\": \"feature\", \"name\", \"about\" (the client's words), \"who\", \"cap_usd\", \"keep\", \"job\" }`, where `job` is the `X-Workbench-Job` the feature's calls send (`<name>` with dots, such as `quotes.estimate`), so its spending shows on its own line in Control. Write it, then build the feature with the package. For wording made once: `npx workbench-ai pick <name> <round>-<draft>` writes it from the draft the client picked, with the round's request ids and cost; never write one from nothing. Then put the wording into the strings catalogue (the workbench-strings skill), keyed for where it is shown. Drafts and their previews stay in `workbench/ai/drafts/`, which isn't committed.",
304
+ "questions": [
305
+ {
306
+ "id": "ai.route",
307
+ "sets": "route",
308
+ "ask": "Is this something visitors use on the site, or something made once for the site?",
309
+ "style": "cards",
310
+ "answers": [
311
+ {
312
+ "label": "Visitors use it",
313
+ "value": "feature",
314
+ "means": "A feature on the site that calls the AI each time someone uses it, such as a question box or a summary of what they paste in."
315
+ },
316
+ {
317
+ "label": "Made once for the site",
318
+ "value": "content",
319
+ "means": "Wording for a page, written once and kept: an About page, a welcome, product descriptions."
320
+ }
321
+ ],
322
+ "must_ask": true,
323
+ "note": "Ask it even when the client's words seem to say, as a quick check: the two routes are built and paid for differently."
324
+ },
325
+ {
326
+ "id": "ai.who",
327
+ "sets": "feature.who",
328
+ "ask": "Who may use it?",
329
+ "style": "cards",
330
+ "answers": [
331
+ {
332
+ "label": "People who are signed in",
333
+ "value": "signed_in",
334
+ "means": "Anyone with an account. Each use is tied to a person, so misuse can be traced and stopped."
335
+ },
336
+ {
337
+ "label": "Admins only",
338
+ "value": "admins",
339
+ "means": "Only the business's own admins, for example a tool for writing their own posts."
340
+ },
341
+ {
342
+ "label": "Anyone visiting",
343
+ "value": "anyone",
344
+ "means": "No sign-in. The page checks for bots (Turnstile) and limits how often one visitor can use it."
345
+ }
346
+ ],
347
+ "when": {
348
+ "id": "ai.route",
349
+ "is": [
350
+ "feature"
351
+ ]
352
+ },
353
+ "note": "Signed in means `requireUser` and admins means `app_admins`, as the workbench-sign-in skill says. For anyone, check Turnstile on every use and keep a per-visitor limit; say plainly that strangers can run up the cost. Pass `subject` on every call so the Gateway can tell people apart: `user:<id>` for a signed-in person, and for anyone a random id kept in a cookie (`visitor:<id>`), never an email address or name."
354
+ },
355
+ {
356
+ "id": "ai.cap",
357
+ "sets": "feature.cap_usd",
358
+ "ask": "How much may it spend in a month, at most? When it's reached, the feature stops until next month.",
359
+ "style": "cards",
360
+ "answers": [
361
+ {
362
+ "label": "US$5 a month",
363
+ "value": 5,
364
+ "means": "A small trial, or light use."
365
+ },
366
+ {
367
+ "label": "US$20 a month",
368
+ "value": 20,
369
+ "means": "Steady use by a small business's visitors."
370
+ },
371
+ {
372
+ "label": "US$50 a month",
373
+ "value": 50,
374
+ "means": "Heavy use. It can be raised later."
375
+ }
376
+ ],
377
+ "other": {
378
+ "kind": "money",
379
+ "min": 1,
380
+ "max": 1000,
381
+ "description": "US dollars a month, whole or with cents."
382
+ },
383
+ "must_ask": true,
384
+ "when": {
385
+ "id": "ai.route",
386
+ "is": [
387
+ "feature"
388
+ ]
389
+ },
390
+ "note": "Say roughly what one use costs (usually a cent or two with claude-default) so the owner can judge. The cap is the project's production cap for the `ai` tool in the Gateway, shared by every feature that uses it: set it with Workbench Control's `gw_budget_set` (env production, tool ai, monthly_usd). Raising a cap waits for the owner's approval on Control's approval page. When it is reached, calls fail with `budget_exceeded`: the page says the feature is resting until next month, never an error."
391
+ },
392
+ {
393
+ "id": "ai.sensitive",
394
+ "sets": "feature.keep",
395
+ "ask": "Will people type in sensitive personal details, such as their health, religion, money troubles or someone else's private information?",
396
+ "style": "cards",
397
+ "answers": [
398
+ {
399
+ "label": "No",
400
+ "value": "no",
401
+ "means": "Workbench keeps what each call sent and got back, so a problem can be traced."
402
+ },
403
+ {
404
+ "label": "Yes",
405
+ "value": "yes",
406
+ "means": "This feature's calls are marked not to be kept: Workbench records only that each call happened and what it cost."
407
+ }
408
+ ],
409
+ "must_ask": true,
410
+ "when": {
411
+ "id": "ai.route",
412
+ "is": [
413
+ "feature"
414
+ ]
415
+ },
416
+ "note": "With yes, pass `keep: false` on this feature's calls. If most of the app handles such details, suggest switching keeping off for the whole project instead (Workbench Control's `gw_keep_set`, env production, on false). Record it as `keep` (true or false) in the answers file."
417
+ },
418
+ {
419
+ "id": "ai.what",
420
+ "sets": [
421
+ "content.name",
422
+ "content.about"
423
+ ],
424
+ "ask": "What wording do you need?",
425
+ "style": "cards",
426
+ "answers": [
427
+ {
428
+ "label": "A page's main text",
429
+ "value": "page_text",
430
+ "means": "Such as an About page or a services page."
431
+ },
432
+ {
433
+ "label": "A short blurb",
434
+ "value": "blurb",
435
+ "means": "A line or two for the top of a page or a card."
436
+ },
437
+ {
438
+ "label": "Descriptions",
439
+ "value": "descriptions",
440
+ "means": "For products or services."
441
+ }
442
+ ],
443
+ "other": {
444
+ "kind": "text",
445
+ "min": 3,
446
+ "max": 200,
447
+ "description": "What the wording is, in a few words."
448
+ },
449
+ "when": {
450
+ "id": "ai.route",
451
+ "is": [
452
+ "content"
453
+ ]
454
+ },
455
+ "note": "Name the piece after where it goes (about_us, home_blurb): lower case and underscores. The client's own words, with any facts they give (when they started, where they are), go in --about, because the drafts never invent facts and leave [gaps] for the owner instead."
456
+ }
457
+ ],
458
+ "not_asked": [
459
+ {
460
+ "sets": "model",
461
+ "why": "Features use claude-default unless the work needs more (claude-best) or speed matters more than depth (claude-fast). Choose from what the feature does; the client can't judge models."
462
+ },
463
+ {
464
+ "sets": "voice",
465
+ "why": "The tone comes from the project's voice and spelling, set once for the whole site."
466
+ }
467
+ ],
468
+ "drafts": {
469
+ "of": "wording",
470
+ "surface": "ai",
471
+ "alias": "claude-default",
472
+ "count": 3,
473
+ "max_rounds": 3,
474
+ "job": "ai.drafts",
475
+ "command": "npx workbench-ai drafts text <name> --about \"<what it is for, with the client's facts>\""
476
+ }
477
+ },
297
478
  "delivery": {
298
479
  "choice": "built_in",
299
- "intro": "Writing, summarising and answering questions with Claude, with a monthly spending cap.",
300
- "when": "The client wants the app to write, summarise, sort, answer questions or pull details out of text with AI, as a feature visitors use or as something made once for the site."
480
+ "intro": "Writing, summarising and answering questions with Claude, with a monthly spending cap."
301
481
  }
302
482
  },
303
483
  "analytics": {
@@ -2218,10 +2398,161 @@ export const capabilities = {
2218
2398
  "cost_hint_usd": 0.001
2219
2399
  }
2220
2400
  ],
2401
+ "offer": {
2402
+ "pattern": "feature_or_content",
2403
+ "when": "The client wants to find existing pictures on the web, for the site itself or as a search visitors use.",
2404
+ "say": "I'll do this with Workbench picture search, which searches the web through Workbench, so the app holds no search key and spending has a monthly cap.",
2405
+ "answers_file": "workbench/image-search/<name>.json",
2406
+ "per": "feature or picture",
2407
+ "answers_format": "For a feature: `{ \"format\": 1, \"route\": \"feature\", \"name\", \"about\" (the client's words), \"who\", \"cap_usd\", \"job\" }`, where `job` is the `X-Workbench-Job` the feature's calls send (`<name>` with dots, such as `quotes.estimate`), so its spending shows on its own line in Control. Write it, then build the feature with the package. For a picture found once: `npx workbench-ai pick <name> <round>-<draft> --licence \"<the owner's words>\"` downloads it to `public/images/<name>.jpg` (or .png) and writes the answers file with where it came from and the licence; never write one from nothing, and never pick one without the licence answer. Show the credit the licence asks for beside the picture. Drafts and their previews stay in `workbench/image-search/drafts/`, which isn't committed.",
2408
+ "questions": [
2409
+ {
2410
+ "id": "image-search.route",
2411
+ "sets": "route",
2412
+ "ask": "Is this something visitors use on the site, or something made once for the site?",
2413
+ "style": "cards",
2414
+ "answers": [
2415
+ {
2416
+ "label": "Visitors use it",
2417
+ "value": "feature",
2418
+ "means": "A search on the site that finds pictures for whoever uses it."
2419
+ },
2420
+ {
2421
+ "label": "Made once for the site",
2422
+ "value": "content",
2423
+ "means": "A picture for the site, found once and kept."
2424
+ }
2425
+ ],
2426
+ "must_ask": true,
2427
+ "note": "Ask it even when the client's words seem to say, as a quick check: the two routes are built and paid for differently."
2428
+ },
2429
+ {
2430
+ "id": "image-search.who",
2431
+ "sets": "feature.who",
2432
+ "ask": "Who may use it?",
2433
+ "style": "cards",
2434
+ "answers": [
2435
+ {
2436
+ "label": "People who are signed in",
2437
+ "value": "signed_in",
2438
+ "means": "Anyone with an account. Each use is tied to a person, so misuse can be traced and stopped."
2439
+ },
2440
+ {
2441
+ "label": "Admins only",
2442
+ "value": "admins",
2443
+ "means": "Only the business's own admins, for example a tool for writing their own posts."
2444
+ },
2445
+ {
2446
+ "label": "Anyone visiting",
2447
+ "value": "anyone",
2448
+ "means": "No sign-in. The page checks for bots (Turnstile) and limits how often one visitor can use it."
2449
+ }
2450
+ ],
2451
+ "when": {
2452
+ "id": "image-search.route",
2453
+ "is": [
2454
+ "feature"
2455
+ ]
2456
+ },
2457
+ "note": "Signed in means `requireUser` and admins means `app_admins`, as the workbench-sign-in skill says. For anyone, check Turnstile on every use and keep a per-visitor limit; say plainly that strangers can run up the cost. Pass `subject` on every call so the Gateway can tell people apart: `user:<id>` for a signed-in person, and for anyone a random id kept in a cookie (`visitor:<id>`), never an email address or name."
2458
+ },
2459
+ {
2460
+ "id": "image-search.cap",
2461
+ "sets": "feature.cap_usd",
2462
+ "ask": "How much may it spend in a month, at most? When it's reached, the feature stops until next month.",
2463
+ "style": "cards",
2464
+ "answers": [
2465
+ {
2466
+ "label": "US$5 a month",
2467
+ "value": 5,
2468
+ "means": "A small trial, or light use."
2469
+ },
2470
+ {
2471
+ "label": "US$20 a month",
2472
+ "value": 20,
2473
+ "means": "Steady use by a small business's visitors."
2474
+ },
2475
+ {
2476
+ "label": "US$50 a month",
2477
+ "value": 50,
2478
+ "means": "Heavy use. It can be raised later."
2479
+ }
2480
+ ],
2481
+ "other": {
2482
+ "kind": "money",
2483
+ "min": 1,
2484
+ "max": 1000,
2485
+ "description": "US dollars a month, whole or with cents."
2486
+ },
2487
+ "must_ask": true,
2488
+ "when": {
2489
+ "id": "image-search.route",
2490
+ "is": [
2491
+ "feature"
2492
+ ]
2493
+ },
2494
+ "note": "Say roughly what one use costs (about a tenth of a cent a search) so the owner can judge. The cap is the project's production cap for the `search` tool in the Gateway, shared by every feature that uses it: set it with Workbench Control's `gw_budget_set` (env production, tool search, monthly_usd). Raising a cap waits for the owner's approval on Control's approval page. When it is reached, calls fail with `budget_exceeded`: the page says the feature is resting until next month, never an error."
2495
+ },
2496
+ {
2497
+ "id": "image-search.licence",
2498
+ "sets": "content.licence",
2499
+ "ask": "Does the site have the right to use this picture?",
2500
+ "style": "cards",
2501
+ "answers": [
2502
+ {
2503
+ "label": "Yes, it's ours",
2504
+ "value": "own",
2505
+ "means": "The business took it or owns it."
2506
+ },
2507
+ {
2508
+ "label": "Yes, it's free to use",
2509
+ "value": "free",
2510
+ "means": "Its page says so, with a licence such as CC BY. Say which."
2511
+ },
2512
+ {
2513
+ "label": "No, find another",
2514
+ "value": "none",
2515
+ "means": "Pick another result, or make one with AI pictures instead."
2516
+ }
2517
+ ],
2518
+ "other": {
2519
+ "kind": "text",
2520
+ "min": 3,
2521
+ "max": 200,
2522
+ "description": "What gives the site the right to use it."
2523
+ },
2524
+ "must_ask": true,
2525
+ "when": {
2526
+ "id": "image-search.route",
2527
+ "is": [
2528
+ "content"
2529
+ ]
2530
+ },
2531
+ "note": "Ask after the client picks a result, showing its page link. A search result is a lead, not permission. Pass the client's words as --licence; with no, don't pick it."
2532
+ }
2533
+ ],
2534
+ "not_asked": [
2535
+ {
2536
+ "sets": "query",
2537
+ "why": "The client's description is the search: pass it as --about, shortened to the words that matter."
2538
+ },
2539
+ {
2540
+ "sets": "filters",
2541
+ "why": "Results narrower than 600 pixels are dropped; anything finer (shape, site) is for a feature to do in its own code."
2542
+ }
2543
+ ],
2544
+ "drafts": {
2545
+ "of": "found pictures",
2546
+ "surface": "image-search",
2547
+ "count": 4,
2548
+ "max_rounds": 3,
2549
+ "job": "image-search.drafts",
2550
+ "command": "npx workbench-ai drafts search <name> --about \"<what to search for>\""
2551
+ }
2552
+ },
2221
2553
  "delivery": {
2222
2554
  "choice": "built_in",
2223
- "intro": "Finding existing pictures on the web.",
2224
- "when": "The client wants to find existing pictures on the web, for the site itself or as a search visitors use."
2555
+ "intro": "Finding existing pictures on the web."
2225
2556
  }
2226
2557
  },
2227
2558
  "images": {
@@ -2431,10 +2762,199 @@ export const capabilities = {
2431
2762
  "cost_hint_usd": 0
2432
2763
  }
2433
2764
  ],
2765
+ "offer": {
2766
+ "pattern": "feature_or_content",
2767
+ "when": "The client wants a picture made by AI, such as an illustration, a hero image or a product mock-up, as a feature visitors use or once for the site.",
2768
+ "say": "I'll make this with Workbench pictures, made by AI through Workbench, so the app holds no image key and spending has a monthly cap.",
2769
+ "answers_file": "workbench/images/<name>.json",
2770
+ "per": "feature or picture",
2771
+ "answers_format": "For a feature: `{ \"format\": 1, \"route\": \"feature\", \"name\", \"about\" (the client's words), \"who\", \"cap_usd\", \"job\" }`, where `job` is the `X-Workbench-Job` the feature's calls send (`<name>` with dots, such as `quotes.estimate`), so its spending shows on its own line in Control. Write it, then build the feature with the package. For a picture made once: `npx workbench-ai pick <name> <round>-<draft>` copies the picked picture to `public/images/<name>.png` (or .jpg) and writes the answers file with its prompt, model, request id and cost; never write one from nothing. Show it on the page from `/images/<name>.png`, with alt text that says what it shows. Drafts and their previews stay in `workbench/images/drafts/`, which isn't committed.",
2772
+ "questions": [
2773
+ {
2774
+ "id": "images.route",
2775
+ "sets": "route",
2776
+ "ask": "Is this something visitors use on the site, or something made once for the site?",
2777
+ "style": "cards",
2778
+ "answers": [
2779
+ {
2780
+ "label": "Visitors use it",
2781
+ "value": "feature",
2782
+ "means": "A feature that makes a new picture each time someone uses it, such as a mock-up of their own design."
2783
+ },
2784
+ {
2785
+ "label": "Made once for the site",
2786
+ "value": "content",
2787
+ "means": "A picture for the site, made once and kept: a hero image, an illustration for a page."
2788
+ }
2789
+ ],
2790
+ "must_ask": true,
2791
+ "note": "Ask it even when the client's words seem to say, as a quick check: the two routes are built and paid for differently."
2792
+ },
2793
+ {
2794
+ "id": "images.who",
2795
+ "sets": "feature.who",
2796
+ "ask": "Who may use it?",
2797
+ "style": "cards",
2798
+ "answers": [
2799
+ {
2800
+ "label": "People who are signed in",
2801
+ "value": "signed_in",
2802
+ "means": "Anyone with an account. Each use is tied to a person, so misuse can be traced and stopped."
2803
+ },
2804
+ {
2805
+ "label": "Admins only",
2806
+ "value": "admins",
2807
+ "means": "Only the business's own admins, for example a tool for writing their own posts."
2808
+ },
2809
+ {
2810
+ "label": "Anyone visiting",
2811
+ "value": "anyone",
2812
+ "means": "No sign-in. The page checks for bots (Turnstile) and limits how often one visitor can use it."
2813
+ }
2814
+ ],
2815
+ "when": {
2816
+ "id": "images.route",
2817
+ "is": [
2818
+ "feature"
2819
+ ]
2820
+ },
2821
+ "note": "Signed in means `requireUser` and admins means `app_admins`, as the workbench-sign-in skill says. For anyone, check Turnstile on every use and keep a per-visitor limit; say plainly that strangers can run up the cost. Pass `subject` on every call so the Gateway can tell people apart: `user:<id>` for a signed-in person, and for anyone a random id kept in a cookie (`visitor:<id>`), never an email address or name."
2822
+ },
2823
+ {
2824
+ "id": "images.cap",
2825
+ "sets": "feature.cap_usd",
2826
+ "ask": "How much may it spend in a month, at most? When it's reached, the feature stops until next month.",
2827
+ "style": "cards",
2828
+ "answers": [
2829
+ {
2830
+ "label": "US$5 a month",
2831
+ "value": 5,
2832
+ "means": "A small trial, or light use."
2833
+ },
2834
+ {
2835
+ "label": "US$20 a month",
2836
+ "value": 20,
2837
+ "means": "Steady use by a small business's visitors."
2838
+ },
2839
+ {
2840
+ "label": "US$50 a month",
2841
+ "value": 50,
2842
+ "means": "Heavy use. It can be raised later."
2843
+ }
2844
+ ],
2845
+ "other": {
2846
+ "kind": "money",
2847
+ "min": 1,
2848
+ "max": 1000,
2849
+ "description": "US dollars a month, whole or with cents."
2850
+ },
2851
+ "must_ask": true,
2852
+ "when": {
2853
+ "id": "images.route",
2854
+ "is": [
2855
+ "feature"
2856
+ ]
2857
+ },
2858
+ "note": "Say roughly what one use costs (about US$0.07 a picture with image-default) so the owner can judge. The cap is the project's production cap for the `images` tool in the Gateway, shared by every feature that uses it: set it with Workbench Control's `gw_budget_set` (env production, tool images, monthly_usd). Raising a cap waits for the owner's approval on Control's approval page. When it is reached, calls fail with `budget_exceeded`: the page says the feature is resting until next month, never an error."
2859
+ },
2860
+ {
2861
+ "id": "images.what",
2862
+ "sets": [
2863
+ "content.name",
2864
+ "content.about"
2865
+ ],
2866
+ "ask": "What picture do you need?",
2867
+ "style": "cards",
2868
+ "answers": [
2869
+ {
2870
+ "label": "A hero image",
2871
+ "value": "hero",
2872
+ "means": "The large picture across the top of a page."
2873
+ },
2874
+ {
2875
+ "label": "An illustration",
2876
+ "value": "illustration",
2877
+ "means": "A drawing for a page or a section."
2878
+ },
2879
+ {
2880
+ "label": "A product mock-up",
2881
+ "value": "mockup",
2882
+ "means": "A product shown in use or in a setting."
2883
+ }
2884
+ ],
2885
+ "other": {
2886
+ "kind": "text",
2887
+ "min": 3,
2888
+ "max": 200,
2889
+ "description": "What the picture is, in a few words."
2890
+ },
2891
+ "when": {
2892
+ "id": "images.route",
2893
+ "is": [
2894
+ "content"
2895
+ ]
2896
+ },
2897
+ "note": "Name the picture after where it goes (home_hero): lower case and underscores. The client's own words about what it shows and the feel they want go in --about."
2898
+ },
2899
+ {
2900
+ "id": "images.shape",
2901
+ "sets": "content.aspect",
2902
+ "ask": "What shape should it be?",
2903
+ "style": "cards",
2904
+ "answers": [
2905
+ {
2906
+ "label": "Landscape (3:2)",
2907
+ "value": "3:2",
2908
+ "means": "Wider than tall, the usual shape for a page."
2909
+ },
2910
+ {
2911
+ "label": "Wide banner (16:9)",
2912
+ "value": "16:9",
2913
+ "means": "For a picture across the top of a page."
2914
+ },
2915
+ {
2916
+ "label": "Square (1:1)",
2917
+ "value": "1:1",
2918
+ "means": "For cards and social posts."
2919
+ },
2920
+ {
2921
+ "label": "Portrait (2:3)",
2922
+ "value": "2:3",
2923
+ "means": "Taller than wide."
2924
+ }
2925
+ ],
2926
+ "when": {
2927
+ "id": "images.route",
2928
+ "is": [
2929
+ "content"
2930
+ ]
2931
+ },
2932
+ "note": "Recommend the banner for a hero image. Pass it to the drafts command as --aspect."
2933
+ }
2934
+ ],
2935
+ "not_asked": [
2936
+ {
2937
+ "sets": "model",
2938
+ "why": "Drafts and features use image-default. image-pro costs about twice as much for finer detail and image-openai is cheapest; suggest one only when the client asks about quality or cost."
2939
+ },
2940
+ {
2941
+ "sets": "references",
2942
+ "why": "A reference picture (to make something like it) is only for a feature that needs one, and works with Google aliases only; offer it when the client mentions one."
2943
+ }
2944
+ ],
2945
+ "drafts": {
2946
+ "of": "pictures",
2947
+ "surface": "images",
2948
+ "alias": "image-default",
2949
+ "count": 3,
2950
+ "max_rounds": 3,
2951
+ "job": "images.drafts",
2952
+ "command": "npx workbench-ai drafts image <name> --about \"<what it shows, in the client's words>\" --aspect <shape>"
2953
+ }
2954
+ },
2434
2955
  "delivery": {
2435
2956
  "choice": "built_in",
2436
- "intro": "Pictures made by AI from a description.",
2437
- "when": "The client wants a picture made by AI, such as an illustration, a hero image or a product mock-up, as a feature visitors use or once for the site."
2957
+ "intro": "Pictures made by AI from a description."
2438
2958
  }
2439
2959
  },
2440
2960
  "pdf": {
@@ -2447,7 +2967,9 @@ export const capabilities = {
2447
2967
  "surfaces": {
2448
2968
  "package": [
2449
2969
  "createPdfMaker",
2450
- "WorkbenchPdfError"
2970
+ "WorkbenchPdfError",
2971
+ "checkLayout",
2972
+ "fillLayout"
2451
2973
  ]
2452
2974
  },
2453
2975
  "limits": [
@@ -2529,13 +3051,95 @@ export const capabilities = {
2529
3051
  "note": "Prints the app's own HTML holding an image from a host not in allowOrigins; the PDF is made and the browser reports that image's request as blocked"
2530
3052
  }
2531
3053
  ],
3054
+ "offer": {
3055
+ "pattern": "drafts",
3056
+ "when": "The client wants a PDF: an invoice, receipt, quote, certificate, report or anything else to download or print.",
3057
+ "say": "I'll make this with Workbench PDFs, printed with your logo and colours. I'll show you a few layouts to pick from. The one you pick is saved, so every one looks the same from then on.",
3058
+ "answers_file": "workbench/pdf/<name>.json",
3059
+ "per": "document",
3060
+ "generator": "npx workbench-pdf generate",
3061
+ "answers_format": "the saved layout. `npx workbench-pdf pick <name> <round>-<draft>` writes it from the draft the client picked, with the draft's request id, model and cost; never write one from nothing. It holds the document's `fields` (each value is text the app formats first, money and dates through the country pack), its `blocks` with `{field}` placeholders, and made-up `sample` values. To change a saved layout in the client's words (move a block, rename a heading, add a field), edit the file and run the generator, which refuses a layout that doesn't check. In the app, print with `pdfMakerFor(env).document(PDF_LAYOUTS.<name>.document(data), { size: PDF_LAYOUTS.<name>.size })`. Drafts and their previews stay in `workbench/pdf/drafts/`, which isn't committed.",
3062
+ "questions": [
3063
+ {
3064
+ "id": "pdf.what",
3065
+ "sets": [
3066
+ "layout.name",
3067
+ "layout.about"
3068
+ ],
3069
+ "ask": "What is this document, and what is it for?",
3070
+ "style": "cards",
3071
+ "answers": [
3072
+ {
3073
+ "label": "An invoice",
3074
+ "value": "invoice",
3075
+ "means": "What a customer owes, with the items and the total."
3076
+ },
3077
+ {
3078
+ "label": "A receipt",
3079
+ "value": "receipt",
3080
+ "means": "Proof a customer paid."
3081
+ },
3082
+ {
3083
+ "label": "A quote",
3084
+ "value": "quote",
3085
+ "means": "What a job would cost, before the customer agrees."
3086
+ }
3087
+ ],
3088
+ "other": {
3089
+ "kind": "text",
3090
+ "min": 3,
3091
+ "max": 200,
3092
+ "description": "What the document is, in a few words: a certificate, a run sheet, a booking confirmation."
3093
+ },
3094
+ "must_ask": true,
3095
+ "note": "The answer's value is the layout's name (lower case and underscores: run_sheet). The client's own words, with anything they add about who reads it, go in --about."
3096
+ },
3097
+ {
3098
+ "id": "pdf.size",
3099
+ "sets": "layout.size",
3100
+ "ask": "Which paper size?",
3101
+ "style": "cards",
3102
+ "answers": [
3103
+ {
3104
+ "label": "A4",
3105
+ "value": "A4",
3106
+ "means": "Used almost everywhere outside North America."
3107
+ },
3108
+ {
3109
+ "label": "US Letter",
3110
+ "value": "Letter",
3111
+ "means": "Used in the United States and Canada."
3112
+ }
3113
+ ],
3114
+ "note": "Recommend US Letter only when the readers are in the United States or Canada. Pass it to the drafts command as --size."
3115
+ }
3116
+ ],
3117
+ "not_asked": [
3118
+ {
3119
+ "sets": "layout.colours",
3120
+ "why": "The logo, colours and fonts come from the brand (brand.json and the admins' brand page), so every document matches the rest of the app. To restyle a part of the layout, use project.css (see Restyling)."
3121
+ },
3122
+ {
3123
+ "sets": "layout.contents",
3124
+ "why": "The drafts propose what each layout shows. The client changes a picked layout in plain words, and you edit the saved file."
3125
+ }
3126
+ ],
3127
+ "drafts": {
3128
+ "of": "layouts for this document",
3129
+ "surface": "ai",
3130
+ "alias": "claude-default",
3131
+ "count": 3,
3132
+ "max_rounds": 3,
3133
+ "job": "pdf.drafts",
3134
+ "command": "npx workbench-pdf drafts <name> --about \"<what it is for, in the client's words>\" --size <A4 or Letter>"
3135
+ }
3136
+ },
2532
3137
  "styles": {
2533
3138
  "prefix": "wb-pdf-"
2534
3139
  },
2535
3140
  "delivery": {
2536
3141
  "choice": "optional",
2537
- "intro": "PDFs such as invoices and receipts, printed with the project's logo and colours.",
2538
- "when": "The client wants a PDF: an invoice, receipt, quote, certificate, report or anything else to download or print."
3142
+ "intro": "PDFs such as invoices and receipts, printed with the project's logo and colours."
2539
3143
  }
2540
3144
  },
2541
3145
  "sign-in": {