wizards 0.2.19 → 0.2.21

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 (79) hide show
  1. package/apps/runtime/dist/app.js +2 -1
  2. package/apps/runtime/dist/authoring/guide.js +32 -4
  3. package/apps/runtime/dist/connectors/external.js +4 -1
  4. package/apps/runtime/dist/engine/prepare.js +37 -2
  5. package/apps/runtime/dist/engine/requirements.js +22 -1
  6. package/apps/runtime/dist/engine/steps.js +8 -1
  7. package/apps/runtime/dist/engine/template.js +8 -0
  8. package/apps/runtime/dist/files/storage.js +8 -1
  9. package/apps/runtime/dist/film/project.js +195 -0
  10. package/apps/runtime/dist/film/skills.js +91 -0
  11. package/apps/runtime/dist/film/sounds.js +122 -0
  12. package/apps/runtime/dist/film/step.js +286 -0
  13. package/apps/runtime/dist/film/tools.js +99 -0
  14. package/apps/runtime/dist/manage.js +4 -0
  15. package/apps/runtime/dist/mcp/auth.js +4 -0
  16. package/apps/runtime/dist/mcp/handler.js +2 -1
  17. package/apps/runtime/dist/media/transcribe.js +150 -0
  18. package/apps/runtime/dist/models.js +24 -0
  19. package/apps/runtime/dist/render/downloads.js +1 -1
  20. package/apps/runtime/dist/routes/api.js +12 -9
  21. package/apps/runtime/dist/routes/internal.js +17 -1
  22. package/apps/runtime/dist/routes/studio.js +55 -7
  23. package/apps/runtime/dist/services/access.js +25 -0
  24. package/apps/runtime/dist/services/errors.js +1 -0
  25. package/apps/runtime/dist/services/package.js +2 -0
  26. package/apps/runtime/dist/services/projects.js +15 -2
  27. package/apps/runtime/dist/services/wizards.js +4 -1
  28. package/apps/runtime/dist/widgets/render.js +20 -3
  29. package/apps/runtime/dist/widgets/runtime.js +2 -1
  30. package/apps/runtime/dist/widgets/step.js +16 -0
  31. package/apps/web/dist/assets/{Account-O6OTAY88.js → Account-BxpaLgg6.js} +1 -1
  32. package/apps/web/dist/assets/{ChangelogDialog-DLrXuyLL.js → ChangelogDialog-Ds9XgxMP.js} +1 -1
  33. package/apps/web/dist/assets/{ChatPanel-BPD-iQK0.js → ChatPanel-CGL-SaYv.js} +1 -1
  34. package/apps/web/dist/assets/{CreditsDialog-Ckema8Ss.js → CreditsDialog-DZPOim-g.js} +1 -1
  35. package/apps/web/dist/assets/EditorPage-DJDTvELx.js +2 -0
  36. package/apps/web/dist/assets/{FlowPreview-DB-86F8z.js → FlowPreview-DObLaWK0.js} +1 -1
  37. package/apps/web/dist/assets/{Harness-D-DSa4ip.js → Harness-BEIJfBYu.js} +1 -1
  38. package/apps/web/dist/assets/HomePage-BxTQiJog.js +1 -0
  39. package/apps/web/dist/assets/{ImportWizard-DtZH-PgS.js → ImportWizard-fp4Q1iZ6.js} +1 -1
  40. package/apps/web/dist/assets/{LocalRuntime-o1OAvp7c.js → LocalRuntime-C1izy1g5.js} +1 -1
  41. package/apps/web/dist/assets/NewWizardPage-BDOoKp6y.js +2 -0
  42. package/apps/web/dist/assets/ProjectPage-Cs86dGVd.js +5 -0
  43. package/apps/web/dist/assets/{PublicRunner-CYo2F8H4.js → PublicRunner-B53p-hWG.js} +1 -1
  44. package/apps/web/dist/assets/{RunnerView-Bqp0paLi.js → RunnerView-CrgDUD9U.js} +2 -2
  45. package/apps/web/dist/assets/SettingsPage-07vGl-P6.js +1 -0
  46. package/apps/web/dist/assets/{SetupPage-CdBYFmh1.js → SetupPage-Ba0WirOM.js} +1 -1
  47. package/apps/web/dist/assets/{SharePage-B7ON0pe1.js → SharePage-J7eD3paN.js} +1 -1
  48. package/apps/web/dist/assets/{api-CVG-xDEn.js → api-BkWIPEWf.js} +1 -1
  49. package/apps/web/dist/assets/changelog-D4wD3iMl.js +1 -0
  50. package/apps/web/dist/assets/definition-BznNdlhf.js +55 -0
  51. package/apps/web/dist/assets/{esm-DutMJAod.js → esm-Bk4BRENl.js} +1 -1
  52. package/apps/web/dist/assets/image-CC_2mJd6.js +1 -0
  53. package/apps/web/dist/assets/index-CJuJLvrl.js +10 -0
  54. package/apps/web/dist/assets/meta-DMOYx_c4.js +1 -0
  55. package/apps/web/dist/assets/{settings-menu-BXpT5jOB.js → settings-menu-BI5cH97k.js} +1 -1
  56. package/apps/web/dist/assets/ui-CiYJabAi.js +1 -0
  57. package/apps/web/dist/assets/volume-2-DE67_rue.js +1 -0
  58. package/apps/web/dist/index.html +3 -3
  59. package/apps/web/dist/mcp/flow.html +53 -53
  60. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +146 -0
  61. package/node_modules/@engenty-wizards/shared/dist/definition.js +101 -0
  62. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +3 -0
  63. package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +6 -1
  64. package/node_modules/@engenty-wizards/shared/dist/marketplace.js +12 -0
  65. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +30 -0
  66. package/node_modules/@engenty-wizards/shared/dist/run.js +18 -0
  67. package/package.json +1 -1
  68. package/apps/web/dist/assets/EditorPage-Dtu9j41h.js +0 -2
  69. package/apps/web/dist/assets/HomePage-ZrCxIiJT.js +0 -1
  70. package/apps/web/dist/assets/NewWizardPage-Bbt-w8tE.js +0 -2
  71. package/apps/web/dist/assets/ProjectPage-MHxFqA98.js +0 -5
  72. package/apps/web/dist/assets/SettingsPage-XyRrZeMr.js +0 -1
  73. package/apps/web/dist/assets/changelog-B2U80Wtl.js +0 -1
  74. package/apps/web/dist/assets/definition-Dp0DR2D6.js +0 -55
  75. package/apps/web/dist/assets/image-BShbErBm.js +0 -1
  76. package/apps/web/dist/assets/index-hpNaR92X.js +0 -10
  77. package/apps/web/dist/assets/meta--kpiTOS-.js +0 -1
  78. package/apps/web/dist/assets/ui-BPY2pIh0.js +0 -1
  79. package/apps/web/dist/assets/volume-2-Bp2tyaKE.js +0 -1
@@ -117,6 +117,7 @@ export declare const fieldSchema: z.ZodObject<{
117
117
  max: z.ZodOptional<z.ZodNumber>;
118
118
  camera: z.ZodOptional<z.ZodBoolean>;
119
119
  video: z.ZodOptional<z.ZodBoolean>;
120
+ listen: z.ZodOptional<z.ZodBoolean>;
120
121
  scan: z.ZodOptional<z.ZodBoolean>;
121
122
  connection: z.ZodOptional<z.ZodString>;
122
123
  list: z.ZodOptional<z.ZodString>;
@@ -201,6 +202,7 @@ export declare const pageStepSchema: z.ZodObject<{
201
202
  max: z.ZodOptional<z.ZodNumber>;
202
203
  camera: z.ZodOptional<z.ZodBoolean>;
203
204
  video: z.ZodOptional<z.ZodBoolean>;
205
+ listen: z.ZodOptional<z.ZodBoolean>;
204
206
  scan: z.ZodOptional<z.ZodBoolean>;
205
207
  connection: z.ZodOptional<z.ZodString>;
206
208
  list: z.ZodOptional<z.ZodString>;
@@ -385,6 +387,60 @@ export declare const widgetStepSchema: z.ZodObject<{
385
387
  goto: z.ZodString;
386
388
  }, z.core.$strip>>>;
387
389
  }, z.core.$strip>;
390
+ /** Formats a film step makes; the composition is drawn for this frame. */
391
+ export declare const FILM_FORMATS: readonly ["16:9", "9:16", "1:1"];
392
+ export type FilmFormat = (typeof FILM_FORMATS)[number];
393
+ export declare const FILM_SIZES: Record<FilmFormat, {
394
+ width: number;
395
+ height: number;
396
+ }>;
397
+ /**
398
+ * A film the AI client installed on this machine makes in a folder of its own: it writes the
399
+ * composition as code (HTML, CSS, JS) with the skills it is given, looks at its frames and fixes
400
+ * them; the runtime renders the MP4. It runs on the person's subscription, never on credits.
401
+ */
402
+ export declare const filmStepSchema: z.ZodObject<{
403
+ type: z.ZodLiteral<"film">;
404
+ brief: z.ZodString;
405
+ skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
406
+ kit: z.ZodOptional<z.ZodString>;
407
+ inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
408
+ format: z.ZodDefault<z.ZodEnum<{
409
+ "1:1": "1:1";
410
+ "16:9": "16:9";
411
+ "9:16": "9:16";
412
+ }>>;
413
+ seconds: z.ZodOptional<z.ZodNumber>;
414
+ model: z.ZodOptional<z.ZodEnum<{
415
+ classifier: "classifier";
416
+ standard: "standard";
417
+ high: "high";
418
+ highest: "highest";
419
+ }>>;
420
+ effort: z.ZodOptional<z.ZodEnum<{
421
+ medium: "medium";
422
+ high: "high";
423
+ low: "low";
424
+ }>>;
425
+ working: z.ZodOptional<z.ZodString>;
426
+ id: z.ZodString;
427
+ title: z.ZodString;
428
+ description: z.ZodOptional<z.ZodString>;
429
+ next: z.ZodOptional<z.ZodArray<z.ZodObject<{
430
+ when: z.ZodObject<{
431
+ field: z.ZodString;
432
+ op: z.ZodEnum<{
433
+ in: "in";
434
+ equals: "equals";
435
+ notEquals: "notEquals";
436
+ notEmpty: "notEmpty";
437
+ empty: "empty";
438
+ }>;
439
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>;
440
+ }, z.core.$strip>;
441
+ goto: z.ZodString;
442
+ }, z.core.$strip>>>;
443
+ }, z.core.$strip>;
388
444
  export declare const reviewStepSchema: z.ZodObject<{
389
445
  type: z.ZodLiteral<"review">;
390
446
  show: z.ZodArray<z.ZodString>;
@@ -514,6 +570,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
514
570
  max: z.ZodOptional<z.ZodNumber>;
515
571
  camera: z.ZodOptional<z.ZodBoolean>;
516
572
  video: z.ZodOptional<z.ZodBoolean>;
573
+ listen: z.ZodOptional<z.ZodBoolean>;
517
574
  scan: z.ZodOptional<z.ZodBoolean>;
518
575
  connection: z.ZodOptional<z.ZodString>;
519
576
  list: z.ZodOptional<z.ZodString>;
@@ -694,6 +751,47 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
694
751
  }, z.core.$strip>;
695
752
  goto: z.ZodString;
696
753
  }, z.core.$strip>>>;
754
+ }, z.core.$strip>, z.ZodObject<{
755
+ type: z.ZodLiteral<"film">;
756
+ brief: z.ZodString;
757
+ skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
758
+ kit: z.ZodOptional<z.ZodString>;
759
+ inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
760
+ format: z.ZodDefault<z.ZodEnum<{
761
+ "1:1": "1:1";
762
+ "16:9": "16:9";
763
+ "9:16": "9:16";
764
+ }>>;
765
+ seconds: z.ZodOptional<z.ZodNumber>;
766
+ model: z.ZodOptional<z.ZodEnum<{
767
+ classifier: "classifier";
768
+ standard: "standard";
769
+ high: "high";
770
+ highest: "highest";
771
+ }>>;
772
+ effort: z.ZodOptional<z.ZodEnum<{
773
+ medium: "medium";
774
+ high: "high";
775
+ low: "low";
776
+ }>>;
777
+ working: z.ZodOptional<z.ZodString>;
778
+ id: z.ZodString;
779
+ title: z.ZodString;
780
+ description: z.ZodOptional<z.ZodString>;
781
+ next: z.ZodOptional<z.ZodArray<z.ZodObject<{
782
+ when: z.ZodObject<{
783
+ field: z.ZodString;
784
+ op: z.ZodEnum<{
785
+ in: "in";
786
+ equals: "equals";
787
+ notEquals: "notEquals";
788
+ notEmpty: "notEmpty";
789
+ empty: "empty";
790
+ }>;
791
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>;
792
+ }, z.core.$strip>;
793
+ goto: z.ZodString;
794
+ }, z.core.$strip>>>;
697
795
  }, z.core.$strip>, z.ZodObject<{
698
796
  type: z.ZodLiteral<"review">;
699
797
  show: z.ZodArray<z.ZodString>;
@@ -766,6 +864,7 @@ export type AgentStep = z.infer<typeof agentStepSchema>;
766
864
  export declare function isDecisionStep(step: AgentStep): boolean;
767
865
  export type GenerateStep = z.infer<typeof generateStepSchema>;
768
866
  export type WidgetStep = z.infer<typeof widgetStepSchema>;
867
+ export type FilmStep = z.infer<typeof filmStepSchema>;
769
868
  export type ReviewStep = z.infer<typeof reviewStepSchema>;
770
869
  export type ResultStep = z.infer<typeof resultStepSchema>;
771
870
  export type StepType = Step["type"];
@@ -1129,6 +1228,7 @@ export declare const wizardSchema: z.ZodObject<{
1129
1228
  max: z.ZodOptional<z.ZodNumber>;
1130
1229
  camera: z.ZodOptional<z.ZodBoolean>;
1131
1230
  video: z.ZodOptional<z.ZodBoolean>;
1231
+ listen: z.ZodOptional<z.ZodBoolean>;
1132
1232
  scan: z.ZodOptional<z.ZodBoolean>;
1133
1233
  connection: z.ZodOptional<z.ZodString>;
1134
1234
  list: z.ZodOptional<z.ZodString>;
@@ -1309,6 +1409,47 @@ export declare const wizardSchema: z.ZodObject<{
1309
1409
  }, z.core.$strip>;
1310
1410
  goto: z.ZodString;
1311
1411
  }, z.core.$strip>>>;
1412
+ }, z.core.$strip>, z.ZodObject<{
1413
+ type: z.ZodLiteral<"film">;
1414
+ brief: z.ZodString;
1415
+ skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
1416
+ kit: z.ZodOptional<z.ZodString>;
1417
+ inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1418
+ format: z.ZodDefault<z.ZodEnum<{
1419
+ "1:1": "1:1";
1420
+ "16:9": "16:9";
1421
+ "9:16": "9:16";
1422
+ }>>;
1423
+ seconds: z.ZodOptional<z.ZodNumber>;
1424
+ model: z.ZodOptional<z.ZodEnum<{
1425
+ classifier: "classifier";
1426
+ standard: "standard";
1427
+ high: "high";
1428
+ highest: "highest";
1429
+ }>>;
1430
+ effort: z.ZodOptional<z.ZodEnum<{
1431
+ medium: "medium";
1432
+ high: "high";
1433
+ low: "low";
1434
+ }>>;
1435
+ working: z.ZodOptional<z.ZodString>;
1436
+ id: z.ZodString;
1437
+ title: z.ZodString;
1438
+ description: z.ZodOptional<z.ZodString>;
1439
+ next: z.ZodOptional<z.ZodArray<z.ZodObject<{
1440
+ when: z.ZodObject<{
1441
+ field: z.ZodString;
1442
+ op: z.ZodEnum<{
1443
+ in: "in";
1444
+ equals: "equals";
1445
+ notEquals: "notEquals";
1446
+ notEmpty: "notEmpty";
1447
+ empty: "empty";
1448
+ }>;
1449
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>;
1450
+ }, z.core.$strip>;
1451
+ goto: z.ZodString;
1452
+ }, z.core.$strip>>>;
1312
1453
  }, z.core.$strip>, z.ZodObject<{
1313
1454
  type: z.ZodLiteral<"review">;
1314
1455
  show: z.ZodArray<z.ZodString>;
@@ -1373,6 +1514,11 @@ export declare const wizardSchema: z.ZodObject<{
1373
1514
  }, z.core.$strip>;
1374
1515
  export type WizardDefinition = z.infer<typeof wizardSchema>;
1375
1516
  export declare const INTERACTIVE: ReadonlySet<StepType>;
1517
+ /**
1518
+ * A wizard with a film step runs only where an AI client is installed and signed in, on the
1519
+ * person's own subscription: one film takes a client many minutes and many tokens.
1520
+ */
1521
+ export declare function needsInstalledClient(def: WizardDefinition): boolean;
1376
1522
  export declare function isInteractive(step: Step): boolean;
1377
1523
  /** Which formats each producing step can be downloaded in. */
1378
1524
  export declare function formatsFor(step: Step): Format[];
@@ -141,6 +141,11 @@ export const fieldSchema = z.object({
141
141
  camera: z.boolean().optional(),
142
142
  /** file: the camera also records short video clips. */
143
143
  video: z.boolean().optional(),
144
+ /**
145
+ * file: a recording (video or audio) is listened to before the next step runs — what is said,
146
+ * piece by piece between pauses, with exact times. Read as {{field.speech}}.
147
+ */
148
+ listen: z.boolean().optional(),
144
149
  /** text: also fill it by scanning a QR code or barcode with the camera. */
145
150
  scan: z.boolean().optional(),
146
151
  /** connection: id of the wizard connection the person connects here. */
@@ -274,6 +279,46 @@ export const widgetStepSchema = z.object({
274
279
  video: z.boolean().optional(),
275
280
  working: z.string().optional(),
276
281
  });
282
+ /** Formats a film step makes; the composition is drawn for this frame. */
283
+ export const FILM_FORMATS = ["16:9", "9:16", "1:1"];
284
+ export const FILM_SIZES = {
285
+ "16:9": { width: 1920, height: 1080 },
286
+ "9:16": { width: 1080, height: 1920 },
287
+ "1:1": { width: 1080, height: 1080 },
288
+ };
289
+ /**
290
+ * A film the AI client installed on this machine makes in a folder of its own: it writes the
291
+ * composition as code (HTML, CSS, JS) with the skills it is given, looks at its frames and fixes
292
+ * them; the runtime renders the MP4. It runs on the person's subscription, never on credits.
293
+ */
294
+ export const filmStepSchema = z.object({
295
+ ...stepBase,
296
+ type: z.literal("film"),
297
+ /** What the film is: story, look, what to use. A template. */
298
+ brief: z.string().min(1),
299
+ /**
300
+ * Skill packages the client reads: platform packages ("hyperframes") or a folder of the
301
+ * workspace with a SKILL.md ("skills/<name>").
302
+ */
303
+ skills: z.array(z.string().min(1)).max(12).default(["hyperframes"]),
304
+ /**
305
+ * A style kit in the workspace — a showreel made once with its rules, helpers and stills, as
306
+ * a .zip — unpacked as `showcase/`: the bar every film of this wizard is made to.
307
+ */
308
+ kit: z.string().optional(),
309
+ /**
310
+ * What the film is made from: name → a field id or "steps.<id>". Files (uploads, voice lines,
311
+ * pictures) are copied into `input/<name>/`, text is written to `input/<name>.md`.
312
+ */
313
+ inputs: z.record(z.string(), z.string()).default({}),
314
+ format: z.enum(FILM_FORMATS).default("16:9"),
315
+ /** About how long the film runs, in seconds. */
316
+ seconds: z.number().int().min(3).max(600).optional(),
317
+ /** The kind of model; default `standard` — the kit carries the craft. */
318
+ model: z.enum(TEXT_CLASSES).optional(),
319
+ effort: z.enum(EFFORTS).optional(),
320
+ working: z.string().optional(),
321
+ });
277
322
  export const reviewStepSchema = z.object({
278
323
  ...stepBase,
279
324
  type: z.literal("review"),
@@ -298,6 +343,7 @@ export const stepSchema = z.discriminatedUnion("type", [
298
343
  agentStepSchema,
299
344
  generateStepSchema,
300
345
  widgetStepSchema,
346
+ filmStepSchema,
301
347
  reviewStepSchema,
302
348
  resultStepSchema,
303
349
  ]);
@@ -366,6 +412,13 @@ export const wizardSchema = z.object({
366
412
  steps: z.array(stepSchema).min(1),
367
413
  });
368
414
  export const INTERACTIVE = new Set(["page", "review", "result"]);
415
+ /**
416
+ * A wizard with a film step runs only where an AI client is installed and signed in, on the
417
+ * person's own subscription: one film takes a client many minutes and many tokens.
418
+ */
419
+ export function needsInstalledClient(def) {
420
+ return def.steps.some((s) => s.type === "film");
421
+ }
369
422
  export function isInteractive(step) {
370
423
  return INTERACTIVE.has(step.type);
371
424
  }
@@ -385,6 +438,9 @@ export function formatsFor(step) {
385
438
  return ["html", "pdf", "png"];
386
439
  }
387
440
  }
441
+ if (step.type === "film") {
442
+ return ["mp4"];
443
+ }
388
444
  if (step.type === "widget") {
389
445
  return step.video ? ["mp4", "png"] : ["html", "png", "pdf", "mp4", "json"];
390
446
  }
@@ -524,6 +580,12 @@ export function validateWizard(def, files) {
524
580
  message: `Field "${field.id}": "scan" fills a text field; kind is "${field.kind}".`,
525
581
  });
526
582
  }
583
+ if (field.listen && field.kind !== "file") {
584
+ issues.push({
585
+ stepId: step.id,
586
+ message: `Field "${field.id}": "listen" belongs to a file field; kind is "${field.kind}".`,
587
+ });
588
+ }
527
589
  if (field.video && field.kind !== "file") {
528
590
  issues.push({
529
591
  stepId: step.id,
@@ -601,6 +663,7 @@ export function validateWizard(def, files) {
601
663
  break;
602
664
  case "generate":
603
665
  checkTemplate(step, step.prompt);
666
+ checkTemplate(step, step.options?.style ?? "");
604
667
  if (step.referenceImage &&
605
668
  !seenFields.has(step.referenceImage) &&
606
669
  !seenSteps.has(step.referenceImage)) {
@@ -648,6 +711,44 @@ export function validateWizard(def, files) {
648
711
  });
649
712
  }
650
713
  break;
714
+ case "film":
715
+ checkTemplate(step, step.brief);
716
+ for (const [name, ref] of Object.entries(step.inputs)) {
717
+ if (!/^[a-z][a-z0-9-]{0,40}$/.test(name)) {
718
+ issues.push({
719
+ stepId: step.id,
720
+ message: `Film input "${name}" is not a folder name (lowercase letters, digits, -).`,
721
+ });
722
+ }
723
+ const r = dataRef(ref);
724
+ if (r.startsWith("steps.")) {
725
+ checkTemplate(step, `{{${r}}}`);
726
+ }
727
+ else if (!seenFields.has(r)) {
728
+ issues.push({
729
+ stepId: step.id,
730
+ message: `Film input "${name}": "${ref}" is not an earlier field or "steps.<id>".`,
731
+ });
732
+ }
733
+ }
734
+ if (step.kit && !step.kit.endsWith(".zip")) {
735
+ issues.push({ stepId: step.id, message: `Film kit "${step.kit}" is a .zip.` });
736
+ }
737
+ else if (files && step.kit && !files.includes(step.kit)) {
738
+ issues.push({
739
+ stepId: step.id,
740
+ message: `Film kit "${step.kit}" is not in the workspace.`,
741
+ });
742
+ }
743
+ for (const skill of step.skills) {
744
+ if (skill.startsWith("skills/") && files && !files.includes(`${skill}/SKILL.md`)) {
745
+ issues.push({
746
+ stepId: step.id,
747
+ message: `Skill "${skill}" has no SKILL.md in the workspace.`,
748
+ });
749
+ }
750
+ }
751
+ break;
651
752
  case "review":
652
753
  for (const ref of step.show) {
653
754
  const list = listRef(ref);
@@ -53,6 +53,9 @@ function resultKind(definition, from) {
53
53
  if (step?.type === "widget") {
54
54
  return step.video ? "video" : "dashboard";
55
55
  }
56
+ if (step?.type === "film") {
57
+ return "video";
58
+ }
56
59
  return step?.type === "agent" && step.output.format === "json" ? "table" : "text";
57
60
  }
58
61
  export function resultsOf(definition) {
@@ -174,6 +174,11 @@ export declare const CAPABILITIES: {
174
174
  de: string;
175
175
  en: string;
176
176
  };
177
+ /** A film the installed client makes: only on a computer with Claude Code, on its subscription. */
178
+ film: {
179
+ de: string;
180
+ en: string;
181
+ };
177
182
  };
178
183
  export type Capability = keyof typeof CAPABILITIES;
179
184
  export declare const COST_TIERS: {
@@ -194,7 +199,7 @@ export type CostTier = keyof typeof COST_TIERS;
194
199
  export declare const ITEM_FORMAT_IDS: ("text" | "image" | "audio" | "video" | "document" | "dashboard" | "table")[];
195
200
  export declare const INDUSTRY_IDS: ("any" | "trades" | "gastronomy" | "hospitality" | "realEstate" | "retail" | "health" | "professional" | "agency" | "events" | "publicSector")[];
196
201
  export declare const USE_CASE_IDS: ("marketing" | "sales" | "accounting" | "service" | "research" | "hr" | "operations" | "website" | "visuals" | "inquiries" | "advice")[];
197
- export declare const CAPABILITY_IDS: ("text" | "code" | "image" | "browser" | "video" | "speech" | "web" | "documents" | "listening" | "connectors")[];
202
+ export declare const CAPABILITY_IDS: ("text" | "code" | "image" | "browser" | "video" | "speech" | "film" | "web" | "documents" | "listening" | "connectors")[];
198
203
  /** The formats a run hands over, read from what the result step offers. */
199
204
  export declare function formatsOf(def: WizardDefinition): ItemFormat[];
200
205
  /** The capabilities a definition needs, read from its steps. */
@@ -52,6 +52,11 @@ export const CAPABILITIES = {
52
52
  listening: { de: "Sprachnotizen verstehen", en: "Understands voice notes" },
53
53
  code: { de: "Code ausführen", en: "Runs code" },
54
54
  connectors: { de: "Verbundene Konten & Systeme", en: "Connected accounts & systems" },
55
+ /** A film the installed client makes: only on a computer with Claude Code, on its subscription. */
56
+ film: {
57
+ de: "Filme mit Claude Code (auf deinem Abo)",
58
+ en: "Films with Claude Code (on your subscription)",
59
+ },
55
60
  };
56
61
  export const COST_TIERS = {
57
62
  low: { de: "günstig", en: "low cost" },
@@ -150,6 +155,9 @@ export function capabilitiesOf(def) {
150
155
  found.add("text");
151
156
  }
152
157
  }
158
+ else if (step.type === "film") {
159
+ found.add("film");
160
+ }
153
161
  }
154
162
  if (def.connections?.length) {
155
163
  found.add("connectors");
@@ -195,6 +203,10 @@ export function effortOf(def) {
195
203
  else if (step.type === "widget" && step.video) {
196
204
  seconds += 60;
197
205
  }
206
+ else if (step.type === "film") {
207
+ // A client writing a film from a style kit: ten minutes to half an hour (2026-10 tests).
208
+ seconds += 25 * 60;
209
+ }
198
210
  }
199
211
  return { fields, required, reviews, minutes: Math.max(1, Math.round(seconds / 60)) };
200
212
  }
@@ -34,6 +34,12 @@ export interface RunState {
34
34
  notes: Record<string, string>;
35
35
  /** Steps that make several results: the entries (from 0) to make again; the others stay. */
36
36
  redo?: Record<string, number[]>;
37
+ /** File fields with `listen`: what is said in the recording, piece by piece with its times. */
38
+ heard?: Record<string, {
39
+ start: number;
40
+ end: number;
41
+ text: string;
42
+ }[]>;
37
43
  }
38
44
  interface AskBase {
39
45
  id: string;
@@ -249,6 +255,30 @@ export declare const RUN_NOTES: {
249
255
  readonly de: "Legt {count} Dateien ab";
250
256
  readonly en: "Filing {count} files";
251
257
  };
258
+ readonly filmSetup: {
259
+ readonly de: "Richtet den Arbeitsordner für den Film ein";
260
+ readonly en: "Setting up the folder for the film";
261
+ };
262
+ readonly filmClient: {
263
+ readonly de: "{client} arbeitet am Film – das dauert meist 10–40 Minuten.";
264
+ readonly en: "{client} is working on the film – this usually takes 10–40 minutes.";
265
+ };
266
+ readonly filmTask: {
267
+ readonly de: "Am Film: {task}";
268
+ readonly en: "On the film: {task}";
269
+ };
270
+ readonly filmLooks: {
271
+ readonly de: "Sieht sich Bilder des Films an";
272
+ readonly en: "Looking at frames of the film";
273
+ };
274
+ readonly filmFix: {
275
+ readonly de: "Der Film ließ sich nicht rendern – Claude Code bessert nach";
276
+ readonly en: "The film did not render – Claude Code is fixing it";
277
+ };
278
+ readonly filmResume: {
279
+ readonly de: "Macht dort weiter, wo der Film stehen geblieben ist";
280
+ readonly en: "Picking the film up where it stopped";
281
+ };
252
282
  };
253
283
  export type RunNoteCode = keyof typeof RUN_NOTES;
254
284
  /** A line of `RUN_NOTES` with what goes into its gaps. */
@@ -66,6 +66,24 @@ export const RUN_NOTES = {
66
66
  },
67
67
  remembers: { de: "Merkt sich {title}", en: "Remembering {title}" },
68
68
  files: { de: "Legt {count} Dateien ab", en: "Filing {count} files" },
69
+ filmSetup: {
70
+ de: "Richtet den Arbeitsordner für den Film ein",
71
+ en: "Setting up the folder for the film",
72
+ },
73
+ filmClient: {
74
+ de: "{client} arbeitet am Film – das dauert meist 10–40 Minuten.",
75
+ en: "{client} is working on the film – this usually takes 10–40 minutes.",
76
+ },
77
+ filmTask: { de: "Am Film: {task}", en: "On the film: {task}" },
78
+ filmLooks: { de: "Sieht sich Bilder des Films an", en: "Looking at frames of the film" },
79
+ filmFix: {
80
+ de: "Der Film ließ sich nicht rendern – Claude Code bessert nach",
81
+ en: "The film did not render – Claude Code is fixing it",
82
+ },
83
+ filmResume: {
84
+ de: "Macht dort weiter, wo der Film stehen geblieben ist",
85
+ en: "Picking the film up where it stopped",
86
+ },
69
87
  };
70
88
  /** A note in a language, its gaps filled. */
71
89
  export function noteText(note, lang) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wizards",
3
- "version": "0.2.19",
3
+ "version": "0.2.21",
4
4
  "description": "Make a wish. Get your wizard. It guides you step by step. Let AI do the work. Runs on your machine, on the AI subscription you already have.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "homepage": "https://engenty.ai",