@beignet/cli 0.0.37 → 0.0.39

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 (113) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +138 -101
  3. package/dist/choices.d.ts +17 -21
  4. package/dist/choices.d.ts.map +1 -1
  5. package/dist/choices.js +21 -67
  6. package/dist/choices.js.map +1 -1
  7. package/dist/config.d.ts +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +1 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/create-prompts.d.ts +5 -6
  12. package/dist/create-prompts.d.ts.map +1 -1
  13. package/dist/create-prompts.js +15 -15
  14. package/dist/create-prompts.js.map +1 -1
  15. package/dist/create.d.ts +11 -2
  16. package/dist/create.d.ts.map +1 -1
  17. package/dist/create.js +38 -28
  18. package/dist/create.js.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +149 -47
  21. package/dist/index.js.map +1 -1
  22. package/dist/inspect.js +69 -16
  23. package/dist/inspect.js.map +1 -1
  24. package/dist/lib.d.ts +1 -1
  25. package/dist/lib.d.ts.map +1 -1
  26. package/dist/lint.d.ts.map +1 -1
  27. package/dist/lint.js +27 -6
  28. package/dist/lint.js.map +1 -1
  29. package/dist/make/inbox.js +10 -10
  30. package/dist/make/inbox.js.map +1 -1
  31. package/dist/make/payments.d.ts +2 -1
  32. package/dist/make/payments.d.ts.map +1 -1
  33. package/dist/make/payments.js +18 -14
  34. package/dist/make/payments.js.map +1 -1
  35. package/dist/make/shared.d.ts +11 -4
  36. package/dist/make/shared.d.ts.map +1 -1
  37. package/dist/make/shared.js +56 -45
  38. package/dist/make/shared.js.map +1 -1
  39. package/dist/make/tenancy.js +8 -8
  40. package/dist/make/tenancy.js.map +1 -1
  41. package/dist/make.d.ts +3 -2
  42. package/dist/make.d.ts.map +1 -1
  43. package/dist/make.js +481 -200
  44. package/dist/make.js.map +1 -1
  45. package/dist/mcp.d.ts.map +1 -1
  46. package/dist/mcp.js +15 -11
  47. package/dist/mcp.js.map +1 -1
  48. package/dist/operational-error-reporting.d.ts +15 -0
  49. package/dist/operational-error-reporting.d.ts.map +1 -0
  50. package/dist/operational-error-reporting.js +42 -0
  51. package/dist/operational-error-reporting.js.map +1 -0
  52. package/dist/outbox.d.ts.map +1 -1
  53. package/dist/outbox.js +73 -0
  54. package/dist/outbox.js.map +1 -1
  55. package/dist/provider-add.d.ts +21 -3
  56. package/dist/provider-add.d.ts.map +1 -1
  57. package/dist/provider-add.js +117 -81
  58. package/dist/provider-add.js.map +1 -1
  59. package/dist/provider-audit.js +3 -3
  60. package/dist/provider-audit.js.map +1 -1
  61. package/dist/schedule.d.ts.map +1 -1
  62. package/dist/schedule.js +28 -1
  63. package/dist/schedule.js.map +1 -1
  64. package/dist/task.d.ts.map +1 -1
  65. package/dist/task.js +38 -5
  66. package/dist/task.js.map +1 -1
  67. package/dist/templates/base.d.ts +1 -1
  68. package/dist/templates/base.d.ts.map +1 -1
  69. package/dist/templates/base.js +15 -15
  70. package/dist/templates/base.js.map +1 -1
  71. package/dist/templates/index.d.ts +2 -2
  72. package/dist/templates/index.d.ts.map +1 -1
  73. package/dist/templates/index.js +7 -7
  74. package/dist/templates/index.js.map +1 -1
  75. package/dist/templates/server.d.ts +1 -1
  76. package/dist/templates/server.d.ts.map +1 -1
  77. package/dist/templates/server.js +22 -18
  78. package/dist/templates/server.js.map +1 -1
  79. package/dist/templates/shared.d.ts +4 -4
  80. package/dist/templates/shared.d.ts.map +1 -1
  81. package/dist/templates/shared.js +6 -6
  82. package/dist/templates/shared.js.map +1 -1
  83. package/dist/templates/testing.js +1 -1
  84. package/dist/templates/todos.js +6 -6
  85. package/package.json +4 -4
  86. package/skills/app-structure/SKILL.md +13 -8
  87. package/src/choices.ts +38 -88
  88. package/src/config.ts +2 -2
  89. package/src/create-prompts.ts +20 -21
  90. package/src/create.ts +54 -36
  91. package/src/index.ts +193 -65
  92. package/src/inspect.ts +116 -18
  93. package/src/lib.ts +1 -1
  94. package/src/lint.ts +35 -4
  95. package/src/make/inbox.ts +10 -10
  96. package/src/make/payments.ts +26 -18
  97. package/src/make/shared.ts +72 -50
  98. package/src/make/tenancy.ts +8 -8
  99. package/src/make.ts +607 -191
  100. package/src/mcp.ts +20 -13
  101. package/src/operational-error-reporting.ts +60 -0
  102. package/src/outbox.ts +77 -0
  103. package/src/provider-add.ts +179 -95
  104. package/src/provider-audit.ts +3 -3
  105. package/src/schedule.ts +32 -1
  106. package/src/task.ts +42 -5
  107. package/src/templates/base.ts +16 -16
  108. package/src/templates/index.ts +8 -8
  109. package/src/templates/server.ts +22 -18
  110. package/src/templates/shared.ts +10 -10
  111. package/src/templates/testing.ts +1 -1
  112. package/src/templates/todos.ts +6 -6
  113. package/src/test-helpers/generated-app.ts +1 -0
package/src/index.ts CHANGED
@@ -15,13 +15,12 @@ import {
15
15
  } from "@stricli/core";
16
16
  import {
17
17
  type CompletionShell,
18
- type CreateIntegrationName,
18
+ type CreateProviderName,
19
19
  completionShellChoices,
20
- createIntegrationChoices,
20
+ createProviderChoices,
21
21
  type DatabaseName,
22
22
  databaseChoices,
23
23
  databaseStartCommand,
24
- type IntegrationName,
25
24
  type MakeFeatureAddon,
26
25
  type MakeFeatureRecipe,
27
26
  makeFeatureAddonChoices,
@@ -30,7 +29,8 @@ import {
30
29
  type ProviderPresetName,
31
30
  packageManagerChoices,
32
31
  providerPresetChoices,
33
- splitCreateIntegrations,
32
+ type StarterProviderName,
33
+ splitCreateProviders,
34
34
  type TemplateName,
35
35
  templateChoices,
36
36
  } from "./choices.js";
@@ -56,7 +56,7 @@ type CreateFlags = {
56
56
  api?: boolean;
57
57
  db?: DatabaseName;
58
58
  packageManager?: PackageManager;
59
- integrations?: readonly CreateIntegrationName[];
59
+ providers?: readonly CreateProviderName[];
60
60
  yes?: boolean;
61
61
  force?: boolean;
62
62
  dryRun?: boolean;
@@ -72,11 +72,12 @@ type MakeFlags = {
72
72
  force?: boolean;
73
73
  dryRun?: boolean;
74
74
  json?: boolean;
75
+ cwd?: string;
75
76
  };
76
77
 
77
78
  type MakeResourceFlags = MakeFlags & {
78
- auth?: boolean;
79
- tenant?: boolean;
79
+ authorization?: boolean;
80
+ tenantScoped?: boolean;
80
81
  events?: boolean;
81
82
  softDelete?: boolean;
82
83
  };
@@ -96,6 +97,10 @@ type MakeScheduleFlags = MakeFlags & {
96
97
  route?: boolean;
97
98
  };
98
99
 
100
+ type MakeUploadFlags = MakeFlags & {
101
+ ui?: boolean;
102
+ };
103
+
99
104
  type RoutesFlags = {
100
105
  json?: boolean;
101
106
  cwd?: string;
@@ -126,6 +131,7 @@ type PreflightFlags = {
126
131
  type DbFlags = {
127
132
  json?: boolean;
128
133
  dryRun?: boolean;
134
+ cwd?: string;
129
135
  };
130
136
 
131
137
  type DatabaseSchemaDialect = "sqlite" | "postgres" | "mysql";
@@ -154,12 +160,14 @@ type TaskRunFlags = {
154
160
  input?: string;
155
161
  tenant?: string;
156
162
  module?: string;
163
+ cwd?: string;
157
164
  };
158
165
 
159
166
  type OutboxDrainFlags = {
160
167
  json?: boolean;
161
168
  module?: string;
162
169
  batchSize?: number;
170
+ cwd?: string;
163
171
  };
164
172
 
165
173
  type OutboxMessageStatus = "pending" | "claimed" | "delivered" | "deadLettered";
@@ -185,11 +193,13 @@ type OutboxListFlags = {
185
193
  kind?: OutboxMessageKind;
186
194
  name?: string;
187
195
  limit?: number;
196
+ cwd?: string;
188
197
  };
189
198
 
190
199
  type OutboxShowFlags = {
191
200
  json?: boolean;
192
201
  module?: string;
202
+ cwd?: string;
193
203
  };
194
204
 
195
205
  type OutboxRequeueFlags = {
@@ -197,6 +207,7 @@ type OutboxRequeueFlags = {
197
207
  module?: string;
198
208
  availableAt?: Date;
199
209
  resetAttempts?: boolean;
210
+ cwd?: string;
200
211
  };
201
212
 
202
213
  type OutboxPurgeFlags = {
@@ -206,6 +217,7 @@ type OutboxPurgeFlags = {
206
217
  all?: boolean;
207
218
  limit?: number;
208
219
  dryRun?: boolean;
220
+ cwd?: string;
209
221
  };
210
222
 
211
223
  type OutboxPruneFlags = {
@@ -214,17 +226,19 @@ type OutboxPruneFlags = {
214
226
  before?: Date;
215
227
  limit?: number;
216
228
  dryRun?: boolean;
229
+ cwd?: string;
217
230
  };
218
231
 
219
232
  type ScheduleRunFlags = {
220
233
  json?: boolean;
221
234
  module?: string;
222
235
  payload?: string;
223
- id?: string;
236
+ runId?: string;
224
237
  attempt?: number;
225
238
  scheduledAt?: string;
226
239
  triggeredAt?: string;
227
240
  source?: string;
241
+ cwd?: string;
228
242
  };
229
243
 
230
244
  type McpFlags = {
@@ -277,11 +291,18 @@ const parseDate = (input: string): Date => {
277
291
  return value;
278
292
  };
279
293
 
280
- const forceFlag = {
294
+ const createForceFlag = {
295
+ kind: "boolean",
296
+ optional: true,
297
+ withNegated: false,
298
+ brief: "Create the app in a non-empty directory and overwrite conflicts.",
299
+ } as const;
300
+
301
+ const makeForceFlag = {
281
302
  kind: "boolean",
282
303
  optional: true,
283
304
  withNegated: false,
284
- brief: "Overwrite conflicting files or write into a non-empty directory.",
305
+ brief: "Overwrite conflicting generated files.",
285
306
  } as const;
286
307
 
287
308
  const dryRunFlag = {
@@ -322,7 +343,7 @@ const parsedDateFlag = (brief: string) =>
322
343
  }) as const;
323
344
 
324
345
  const cwdFlag = parsedStringFlag(
325
- "App directory to inspect. Defaults to the current working directory.",
346
+ "App directory. Defaults to the current working directory.",
326
347
  );
327
348
 
328
349
  const formatFlag = {
@@ -334,9 +355,10 @@ const formatFlag = {
334
355
  } as const;
335
356
 
336
357
  const makeFlagParameters = {
337
- force: forceFlag,
358
+ force: makeForceFlag,
338
359
  dryRun: dryRunFlag,
339
360
  json: jsonFlag,
361
+ cwd: cwdFlag,
340
362
  } satisfies FlagParametersForType<MakeFlags, CliContext>;
341
363
 
342
364
  const routesFlagParameters = {
@@ -364,6 +386,7 @@ const providerAddFlagParameters = {
364
386
  const dbFlagParameters = {
365
387
  json: jsonFlag,
366
388
  dryRun: dryRunFlag,
389
+ cwd: cwdFlag,
367
390
  } satisfies FlagParametersForType<DbFlags, CliContext>;
368
391
 
369
392
  const dbSchemaSyncFlagParameters = {
@@ -397,6 +420,7 @@ const taskRunFlagParameters = {
397
420
  module: parsedStringFlag(
398
421
  "Task registry module. Defaults to server/tasks.ts.",
399
422
  ),
423
+ cwd: cwdFlag,
400
424
  } satisfies FlagParametersForType<TaskRunFlags, CliContext>;
401
425
 
402
426
  const outboxDrainFlagParameters = {
@@ -410,6 +434,7 @@ const outboxDrainFlagParameters = {
410
434
  optional: true,
411
435
  brief: "Maximum messages to claim in one drain pass.",
412
436
  },
437
+ cwd: cwdFlag,
413
438
  } satisfies FlagParametersForType<OutboxDrainFlags, CliContext>;
414
439
 
415
440
  const outboxListFlagParameters = {
@@ -436,6 +461,7 @@ const outboxListFlagParameters = {
436
461
  optional: true,
437
462
  brief: "Maximum messages to return. Defaults to 50.",
438
463
  },
464
+ cwd: cwdFlag,
439
465
  } satisfies FlagParametersForType<OutboxListFlags, CliContext>;
440
466
 
441
467
  const outboxShowFlagParameters = {
@@ -443,6 +469,7 @@ const outboxShowFlagParameters = {
443
469
  module: parsedStringFlag(
444
470
  "Outbox registry module. Defaults to server/outbox.ts.",
445
471
  ),
472
+ cwd: cwdFlag,
446
473
  } satisfies FlagParametersForType<OutboxShowFlags, CliContext>;
447
474
 
448
475
  const outboxRequeueFlagParameters = {
@@ -459,6 +486,7 @@ const outboxRequeueFlagParameters = {
459
486
  withNegated: false,
460
487
  brief: "Reset attempts to zero before requeueing.",
461
488
  },
489
+ cwd: cwdFlag,
462
490
  } satisfies FlagParametersForType<OutboxRequeueFlags, CliContext>;
463
491
 
464
492
  const outboxPurgeFlagParameters = {
@@ -482,6 +510,7 @@ const outboxPurgeFlagParameters = {
482
510
  brief: "Maximum messages to purge.",
483
511
  },
484
512
  dryRun: operationDryRunFlag,
513
+ cwd: cwdFlag,
485
514
  } satisfies FlagParametersForType<OutboxPurgeFlags, CliContext>;
486
515
 
487
516
  const outboxPruneFlagParameters = {
@@ -499,6 +528,7 @@ const outboxPruneFlagParameters = {
499
528
  brief: "Maximum messages to prune.",
500
529
  },
501
530
  dryRun: operationDryRunFlag,
531
+ cwd: cwdFlag,
502
532
  } satisfies FlagParametersForType<OutboxPruneFlags, CliContext>;
503
533
 
504
534
  const scheduleRunFlagParameters = {
@@ -507,7 +537,7 @@ const scheduleRunFlagParameters = {
507
537
  "Schedule registry module. Defaults to server/schedules.ts.",
508
538
  ),
509
539
  payload: parsedStringFlag("JSON payload passed to the schedule schema."),
510
- id: parsedStringFlag("Provider or app schedule run ID."),
540
+ runId: parsedStringFlag("Provider or app schedule run ID."),
511
541
  attempt: {
512
542
  kind: "parsed",
513
543
  parse: parsePositiveInteger,
@@ -517,6 +547,7 @@ const scheduleRunFlagParameters = {
517
547
  scheduledAt: parsedStringFlag("Provider scheduled timestamp."),
518
548
  triggeredAt: parsedStringFlag("Schedule trigger timestamp."),
519
549
  source: parsedStringFlag("Provider or app source label."),
550
+ cwd: cwdFlag,
520
551
  } satisfies FlagParametersForType<ScheduleRunFlags, CliContext>;
521
552
 
522
553
  const namePositional = {
@@ -588,11 +619,11 @@ and a shadcn UI shell. Pass --api for an API-only scaffold without the
588
619
  UI shell, and --db to choose the database.
589
620
 
590
621
  Available databases: ${databaseChoices.join(", ")}
591
- Available integrations: ${createIntegrationChoices.join(", ")}
622
+ Available providers: ${createProviderChoices.join(", ")}
592
623
 
593
- --integrations accepts the starter template integrations plus the full
594
- beignet providers add preset catalog; presets are applied to the scaffold
595
- with the same machinery as beignet providers add.
624
+ --providers accepts the full beignet provider add preset catalog. Providers
625
+ with starter-native setup are rendered directly; the rest use the same
626
+ machinery as beignet provider add.
596
627
 
597
628
  Running create on an interactive terminal without selection flags opens a
598
629
  prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
@@ -623,13 +654,12 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
623
654
  optional: true,
624
655
  brief: "Package manager to use in next-step commands.",
625
656
  },
626
- integrations: {
657
+ providers: {
627
658
  kind: "enum",
628
- values: createIntegrationChoices,
659
+ values: createProviderChoices,
629
660
  optional: true,
630
661
  variadic: ",",
631
- brief:
632
- "Add first-party integrations or provider presets as a comma-separated list.",
662
+ brief: "Add provider presets as a comma-separated list.",
633
663
  },
634
664
  yes: {
635
665
  kind: "boolean",
@@ -637,7 +667,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
637
667
  withNegated: false,
638
668
  brief: "Skip interactive prompts and use the defaults.",
639
669
  },
640
- force: forceFlag,
670
+ force: createForceFlag,
641
671
  dryRun: dryRunFlag,
642
672
  json: jsonFlag,
643
673
  },
@@ -654,7 +684,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
654
684
  },
655
685
  },
656
686
  loader: async () => {
657
- const { createProject } = await import("./create.js");
687
+ const { createProject, planCreateProject } = await import("./create.js");
658
688
  const { promptCreateSelections, shouldPromptInteractively } = await import(
659
689
  "./create-prompts.js"
660
690
  );
@@ -668,7 +698,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
668
698
  directory,
669
699
  api: flags.api,
670
700
  db: flags.db,
671
- integrations: flags.integrations,
701
+ providers: flags.providers,
672
702
  packageManager: flags.packageManager,
673
703
  };
674
704
 
@@ -697,43 +727,79 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
697
727
 
698
728
  const api = selections.api ?? false;
699
729
  const database = selections.db ?? "sqlite";
700
- const { integrations, presets } = splitCreateIntegrations(
701
- uniqueValues([...(selections.integrations ?? [])]),
730
+ const { starterProviders, presets } = splitCreateProviders(
731
+ uniqueValues([...(selections.providers ?? [])]),
702
732
  );
703
733
 
704
734
  let presetsNeedEnv = false;
705
735
  if (presets.length > 0) {
706
736
  const { assertCompatibleCreatePresets, providerPresetNeedsEnv } =
707
737
  await import("./provider-add.js");
708
- assertCompatibleCreatePresets(presets, integrations);
738
+ assertCompatibleCreatePresets(presets, starterProviders);
709
739
  presetsNeedEnv = presets.some(providerPresetNeedsEnv);
710
740
  }
711
741
 
712
- const result = await createProject({
742
+ const createOptions = {
713
743
  name: selections.directory,
714
744
  template: flags.template,
715
745
  api,
716
746
  packageManager: selections.packageManager,
717
- integrations,
747
+ providers: starterProviders,
718
748
  db: database,
719
749
  force: Boolean(flags.force),
720
750
  dryRun: Boolean(flags.dryRun),
721
- });
751
+ } as const;
752
+
753
+ let result: Awaited<ReturnType<typeof createProject>>;
754
+ if (flags.dryRun) {
755
+ const plan = await planCreateProject(createOptions);
756
+ if (presets.length === 0) {
757
+ result = plan.result;
758
+ } else {
759
+ const { planProviderPresetsForCreate } = await import(
760
+ "./provider-add.js"
761
+ );
762
+ const providerPlan = await planProviderPresetsForCreate({
763
+ targetDir: plan.result.targetDir,
764
+ files: plan.files,
765
+ names: presets,
766
+ });
767
+ result = {
768
+ ...plan.result,
769
+ files: providerPlan.files.map((file) => file.path),
770
+ };
771
+ }
772
+ } else {
773
+ result = await createProject(createOptions);
774
+ }
722
775
 
723
776
  if (!flags.dryRun && presets.length > 0) {
724
777
  const { addProviderPreset } = await import("./provider-add.js");
778
+ const presetFiles: string[] = [];
725
779
  for (const preset of presets) {
726
- await addProviderPreset({ name: preset, cwd: result.targetDir });
780
+ const presetResult = await addProviderPreset({
781
+ name: preset,
782
+ cwd: result.targetDir,
783
+ });
784
+ presetFiles.push(...presetResult.files);
727
785
  }
786
+ result = {
787
+ ...result,
788
+ files: uniqueValues([...result.files, ...presetFiles]),
789
+ };
728
790
  }
729
791
 
730
792
  writeOutput(
731
793
  this,
732
794
  flags.json
733
- ? JSON.stringify({ ...result, providerPresets: presets }, null, 2)
795
+ ? JSON.stringify(
796
+ { ...result, providers: selections.providers ?? [] },
797
+ null,
798
+ 2,
799
+ )
734
800
  : nextSteps(result, {
735
801
  api,
736
- integrations,
802
+ providers: starterProviders,
737
803
  database,
738
804
  presets,
739
805
  presetsNeedEnv,
@@ -1083,6 +1149,7 @@ function databaseCommand(command: DatabaseCommandName) {
1083
1149
  return async function runDb(this: CliContext, flags: DbFlags) {
1084
1150
  const result = await runDatabaseCommand({
1085
1151
  command,
1152
+ cwd: flags.cwd,
1086
1153
  captureOutput: Boolean(flags.json),
1087
1154
  dryRun: Boolean(flags.dryRun),
1088
1155
  });
@@ -1118,6 +1185,7 @@ const dbSchemaSyncCommand = buildCommand<DbSchemaSyncFlags, [], CliContext>({
1118
1185
  flags: DbSchemaSyncFlags,
1119
1186
  ) {
1120
1187
  const result = await syncDatabaseSchema({
1188
+ cwd: flags.cwd,
1121
1189
  dialect: flags.dialect,
1122
1190
  tables: flags.tables,
1123
1191
  output: flags.output,
@@ -1174,6 +1242,7 @@ const taskRunCommand = buildCommand<TaskRunFlags, [string], CliContext>({
1174
1242
  ) {
1175
1243
  const result = await runAppTask({
1176
1244
  name,
1245
+ cwd: flags.cwd,
1177
1246
  input: flags.input,
1178
1247
  tenant: flags.tenant,
1179
1248
  modulePath: flags.module,
@@ -1210,6 +1279,7 @@ const outboxDrainCommand = buildCommand<OutboxDrainFlags, [], CliContext>({
1210
1279
 
1211
1280
  return async function runDrain(this: CliContext, flags: OutboxDrainFlags) {
1212
1281
  const result = await runOutboxDrain({
1282
+ cwd: flags.cwd,
1213
1283
  modulePath: flags.module,
1214
1284
  batchSize: flags.batchSize,
1215
1285
  });
@@ -1236,6 +1306,7 @@ const outboxListCommand = buildCommand<OutboxListFlags, [], CliContext>({
1236
1306
 
1237
1307
  return async function runList(this: CliContext, flags: OutboxListFlags) {
1238
1308
  const result = await runOutboxList({
1309
+ cwd: flags.cwd,
1239
1310
  modulePath: flags.module,
1240
1311
  status: flags.status,
1241
1312
  kind: flags.kind,
@@ -1270,6 +1341,7 @@ const outboxShowCommand = buildCommand<OutboxShowFlags, [string], CliContext>({
1270
1341
  id: string,
1271
1342
  ) {
1272
1343
  const result = await runOutboxShow({
1344
+ cwd: flags.cwd,
1273
1345
  modulePath: flags.module,
1274
1346
  id,
1275
1347
  });
@@ -1305,6 +1377,7 @@ const outboxRequeueCommand = buildCommand<
1305
1377
  id: string,
1306
1378
  ) {
1307
1379
  const result = await runOutboxRequeue({
1380
+ cwd: flags.cwd,
1308
1381
  modulePath: flags.module,
1309
1382
  id,
1310
1383
  availableAt: flags.availableAt,
@@ -1333,6 +1406,7 @@ const outboxPurgeCommand = buildCommand<OutboxPurgeFlags, [], CliContext>({
1333
1406
 
1334
1407
  return async function runPurge(this: CliContext, flags: OutboxPurgeFlags) {
1335
1408
  const result = await runOutboxPurge({
1409
+ cwd: flags.cwd,
1336
1410
  modulePath: flags.module,
1337
1411
  before: flags.before,
1338
1412
  all: Boolean(flags.all),
@@ -1369,6 +1443,7 @@ const outboxPruneCommand = buildCommand<OutboxPruneFlags, [], CliContext>({
1369
1443
  }
1370
1444
 
1371
1445
  const result = await runOutboxPrune({
1446
+ cwd: flags.cwd,
1372
1447
  modulePath: flags.module,
1373
1448
  before: flags.before,
1374
1449
  limit: flags.limit,
@@ -1418,9 +1493,10 @@ const scheduleRunCommand = buildCommand<ScheduleRunFlags, [string], CliContext>(
1418
1493
  ) {
1419
1494
  const result = await runAppSchedule({
1420
1495
  name,
1496
+ cwd: flags.cwd,
1421
1497
  modulePath: flags.module,
1422
1498
  payload: flags.payload,
1423
- id: flags.id,
1499
+ id: flags.runId,
1424
1500
  attempt: flags.attempt,
1425
1501
  scheduledAt: flags.scheduledAt,
1426
1502
  triggeredAt: flags.triggeredAt,
@@ -1533,14 +1609,14 @@ const makeResourceCommand = buildCommand<
1533
1609
  parameters: {
1534
1610
  flags: {
1535
1611
  ...makeFlagParameters,
1536
- auth: {
1612
+ authorization: {
1537
1613
  kind: "boolean",
1538
1614
  optional: true,
1539
1615
  withNegated: false,
1540
1616
  brief:
1541
1617
  "Generate policy metadata and use-case authorization checks for mutating routes.",
1542
1618
  },
1543
- tenant: {
1619
+ tenantScoped: {
1544
1620
  kind: "boolean",
1545
1621
  optional: true,
1546
1622
  withNegated: false,
@@ -1573,10 +1649,11 @@ const makeResourceCommand = buildCommand<
1573
1649
  ) {
1574
1650
  const result = await makeResource({
1575
1651
  name,
1652
+ cwd: flags.cwd,
1576
1653
  force: Boolean(flags.force),
1577
1654
  dryRun: Boolean(flags.dryRun),
1578
- auth: Boolean(flags.auth),
1579
- tenant: Boolean(flags.tenant),
1655
+ authorization: Boolean(flags.authorization),
1656
+ tenantScoped: Boolean(flags.tenantScoped),
1580
1657
  events: Boolean(flags.events),
1581
1658
  softDelete: Boolean(flags.softDelete),
1582
1659
  });
@@ -1604,7 +1681,7 @@ const makeFeatureCommand = buildCommand<MakeFeatureFlags, [string], CliContext>(
1604
1681
  optional: true,
1605
1682
  variadic: ",",
1606
1683
  brief:
1607
- "Add feature-owned artifacts. Supports policy, factory/factories, seed/seeds, task/tasks, event/events, listener/listeners, job/jobs, notification/notifications, schedule/schedules, ui, and upload/uploads.",
1684
+ "Add feature-owned artifacts. Supports policy, factory, seed, task, event, listener, job, notification, schedule, ui, and upload.",
1608
1685
  },
1609
1686
  recipe: {
1610
1687
  kind: "enum",
@@ -1626,6 +1703,7 @@ const makeFeatureCommand = buildCommand<MakeFeatureFlags, [string], CliContext>(
1626
1703
  ) {
1627
1704
  const result = await makeFeature({
1628
1705
  name,
1706
+ cwd: flags.cwd,
1629
1707
  with: flags.with,
1630
1708
  recipe: flags.recipe,
1631
1709
  force: Boolean(flags.force),
@@ -1654,7 +1732,6 @@ type MakeGeneratorName =
1654
1732
  | "makeSeed"
1655
1733
  | "makeTask"
1656
1734
  | "makeTest"
1657
- | "makeUpload"
1658
1735
  | "makeUseCase";
1659
1736
 
1660
1737
  function simpleMakeCommand(
@@ -1680,6 +1757,7 @@ function simpleMakeCommand(
1680
1757
  ) {
1681
1758
  const result = await generators[generator]({
1682
1759
  name,
1760
+ cwd: flags.cwd,
1683
1761
  force: Boolean(flags.force),
1684
1762
  dryRun: Boolean(flags.dryRun),
1685
1763
  });
@@ -1694,6 +1772,48 @@ function simpleMakeCommand(
1694
1772
  });
1695
1773
  }
1696
1774
 
1775
+ const makeUploadCommand = buildCommand<MakeUploadFlags, [string], CliContext>({
1776
+ docs: {
1777
+ brief: "Generate a feature upload.",
1778
+ },
1779
+ parameters: {
1780
+ flags: {
1781
+ ...makeFlagParameters,
1782
+ ui: {
1783
+ kind: "boolean",
1784
+ optional: true,
1785
+ withNegated: false,
1786
+ brief:
1787
+ "Generate a typed React upload client, uploader component, and component test.",
1788
+ },
1789
+ } satisfies FlagParametersForType<MakeUploadFlags, CliContext>,
1790
+ positional: namePositional,
1791
+ },
1792
+ loader: async () => {
1793
+ const { makeUpload } = await import("./make.js");
1794
+
1795
+ return async function runMakeUpload(
1796
+ this: CliContext,
1797
+ flags: MakeUploadFlags,
1798
+ name: string,
1799
+ ) {
1800
+ const result = await makeUpload({
1801
+ name,
1802
+ cwd: flags.cwd,
1803
+ force: Boolean(flags.force),
1804
+ dryRun: Boolean(flags.dryRun),
1805
+ ui: Boolean(flags.ui),
1806
+ });
1807
+ writeOutput(
1808
+ this,
1809
+ flags.json
1810
+ ? JSON.stringify(result, null, 2)
1811
+ : makeUploadNextSteps(result),
1812
+ );
1813
+ };
1814
+ },
1815
+ });
1816
+
1697
1817
  const makeListenerCommand = buildCommand<
1698
1818
  MakeListenerFlags,
1699
1819
  [string],
@@ -1706,7 +1826,7 @@ const makeListenerCommand = buildCommand<
1706
1826
  flags: {
1707
1827
  ...makeFlagParameters,
1708
1828
  event: parsedStringFlag(
1709
- "Event to listen to, for example posts/published.",
1829
+ "Event to listen to, for example posts.published.",
1710
1830
  ),
1711
1831
  } satisfies FlagParametersForType<MakeListenerFlags, CliContext>,
1712
1832
  positional: namePositional,
@@ -1721,12 +1841,13 @@ const makeListenerCommand = buildCommand<
1721
1841
  ) {
1722
1842
  if (!flags.event) {
1723
1843
  throw new Error(
1724
- "beignet make listener requires --event feature/name, for example --event posts/published.",
1844
+ "beignet make listener requires --event feature.name, for example --event posts.published.",
1725
1845
  );
1726
1846
  }
1727
1847
 
1728
1848
  const result = await makeListener({
1729
1849
  name,
1850
+ cwd: flags.cwd,
1730
1851
  event: flags.event,
1731
1852
  force: Boolean(flags.force),
1732
1853
  dryRun: Boolean(flags.dryRun),
@@ -1773,6 +1894,7 @@ const makeScheduleCommand = buildCommand<
1773
1894
  ) {
1774
1895
  const result = await makeSchedule({
1775
1896
  name,
1897
+ cwd: flags.cwd,
1776
1898
  cron: flags.cron,
1777
1899
  timezone: flags.timezone,
1778
1900
  route: Boolean(flags.route),
@@ -1803,6 +1925,7 @@ const makePaymentsCommand = buildCommand<MakeFlags, [], CliContext>({
1803
1925
 
1804
1926
  return async function runMakePayments(this: CliContext, flags: MakeFlags) {
1805
1927
  const result = await makePayments({
1928
+ cwd: flags.cwd,
1806
1929
  force: Boolean(flags.force),
1807
1930
  dryRun: Boolean(flags.dryRun),
1808
1931
  });
@@ -1832,6 +1955,7 @@ const makeTenancyCommand = buildCommand<MakeFlags, [], CliContext>({
1832
1955
 
1833
1956
  return async function runMakeTenancy(this: CliContext, flags: MakeFlags) {
1834
1957
  const result = await makeTenancy({
1958
+ cwd: flags.cwd,
1835
1959
  force: Boolean(flags.force),
1836
1960
  dryRun: Boolean(flags.dryRun),
1837
1961
  });
@@ -1861,6 +1985,7 @@ const makeInboxCommand = buildCommand<MakeFlags, [], CliContext>({
1861
1985
 
1862
1986
  return async function runMakeInbox(this: CliContext, flags: MakeFlags) {
1863
1987
  const result = await makeInbox({
1988
+ cwd: flags.cwd,
1864
1989
  force: Boolean(flags.force),
1865
1990
  dryRun: Boolean(flags.dryRun),
1866
1991
  });
@@ -1890,6 +2015,7 @@ const makeOutboxCommand = buildCommand<MakeFlags, [], CliContext>({
1890
2015
 
1891
2016
  return async function runMakeOutbox(this: CliContext, flags: MakeFlags) {
1892
2017
  const result = await makeOutbox({
2018
+ cwd: flags.cwd,
1893
2019
  force: Boolean(flags.force),
1894
2020
  dryRun: Boolean(flags.dryRun),
1895
2021
  });
@@ -1971,11 +2097,7 @@ const makeRoutes = buildRouteMap({
1971
2097
  "Generate a use case test.",
1972
2098
  makeTestNextSteps,
1973
2099
  ),
1974
- upload: simpleMakeCommand(
1975
- "makeUpload",
1976
- "Generate a feature upload.",
1977
- makeUploadNextSteps,
1978
- ),
2100
+ upload: makeUploadCommand,
1979
2101
  useCase: simpleMakeCommand(
1980
2102
  "makeUseCase",
1981
2103
  "Generate a use case.",
@@ -2002,15 +2124,11 @@ Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
2002
2124
  mcp: mcpCommand,
2003
2125
  outbox: outboxRoutes,
2004
2126
  preflight: preflightCommand,
2005
- providers: providerRoutes,
2127
+ provider: providerRoutes,
2006
2128
  routes: routesCommand,
2007
2129
  schedule: scheduleRoutes,
2008
2130
  task: taskRoutes,
2009
2131
  },
2010
- aliases: {
2011
- // The singular form is a common near-miss for the providers commands.
2012
- provider: "providers",
2013
- },
2014
2132
  });
2015
2133
 
2016
2134
  const cli = buildApplication(rootRoutes, {
@@ -2058,6 +2176,7 @@ type MakeNextStepsResult = {
2058
2176
  name: string;
2059
2177
  targetDir: string;
2060
2178
  dryRun: boolean;
2179
+ files: string[];
2061
2180
  createdFiles: string[];
2062
2181
  updatedFiles: string[];
2063
2182
  skippedFiles: string[];
@@ -2245,7 +2364,7 @@ function nextSteps(
2245
2364
  result: CreateNextStepsResult,
2246
2365
  options: {
2247
2366
  api?: boolean;
2248
- integrations?: readonly IntegrationName[];
2367
+ providers?: readonly StarterProviderName[];
2249
2368
  database?: DatabaseName;
2250
2369
  presets?: readonly ProviderPresetName[];
2251
2370
  presetsNeedEnv?: boolean;
@@ -2255,7 +2374,7 @@ function nextSteps(
2255
2374
  const plannedFiles = result.files.map((file) => ` ${file}`).join("\n");
2256
2375
  const plannedPresets =
2257
2376
  options.presets && options.presets.length > 0
2258
- ? `\n\nProvider presets applied after scaffold (skipped in dry run):\n${options.presets
2377
+ ? `\n\nProvider presets included in this plan:\n${options.presets
2259
2378
  .map((preset) => ` ${preset}`)
2260
2379
  .join("\n")}`
2261
2380
  : "";
@@ -2269,11 +2388,11 @@ ${plannedFiles}${plannedPresets}`;
2269
2388
  const pm = result.packageManager;
2270
2389
  const run = pm === "npm" ? "npm run" : `${pm} run`;
2271
2390
  const cli = packageRunnerFromPackageManager(pm);
2272
- const envRequiredIntegrations =
2273
- options.integrations?.filter(isEnvRequiredProviderIntegration) ?? [];
2391
+ const envRequiredProviders =
2392
+ options.providers?.filter(isEnvRequiredStarterProvider) ?? [];
2274
2393
  const envStep =
2275
- envRequiredIntegrations.length > 0 || options.presetsNeedEnv
2276
- ? " Fill .env.local for selected provider integrations before starting the app.\n"
2394
+ envRequiredProviders.length > 0 || options.presetsNeedEnv
2395
+ ? " Fill .env.local for selected providers before starting the app.\n"
2277
2396
  : "";
2278
2397
  const presetSection =
2279
2398
  options.presets && options.presets.length > 0
@@ -2281,7 +2400,7 @@ ${plannedFiles}${plannedPresets}`;
2281
2400
  .map((preset) => ` ${preset}`)
2282
2401
  .join(
2283
2402
  "\n",
2284
- )}\n See docs/integrations.md for setup notes and follow-up steps.\n`
2403
+ )}\n See docs/providers.md for setup notes and follow-up steps.\n`
2285
2404
  : "";
2286
2405
  const openStep = options.api
2287
2406
  ? " open http://localhost:3000 for the API landing page"
@@ -2334,13 +2453,11 @@ function packageRunnerFromPackageManager(pm: PackageManager): string {
2334
2453
  }
2335
2454
  }
2336
2455
 
2337
- function isEnvRequiredProviderIntegration(
2338
- integration: IntegrationName,
2339
- ): boolean {
2456
+ function isEnvRequiredStarterProvider(provider: StarterProviderName): boolean {
2340
2457
  return (
2341
- integration === "inngest" ||
2342
- integration === "resend" ||
2343
- integration === "upstash-rate-limit"
2458
+ provider === "jobs-inngest" ||
2459
+ provider === "mail-resend" ||
2460
+ provider === "rate-limit-upstash"
2344
2461
  );
2345
2462
  }
2346
2463
 
@@ -2558,8 +2675,19 @@ function makeScheduleNextSteps(
2558
2675
  }
2559
2676
 
2560
2677
  function makeUploadNextSteps(result: MakeNextStepsResult): string {
2678
+ const includesUi = result.files.some((file) =>
2679
+ file.endsWith("-uploader.tsx"),
2680
+ );
2681
+
2561
2682
  return makeNextSteps(result, "upload", [
2562
2683
  "Replace the starter metadata, constraints, key, authorization, and onComplete behavior.",
2684
+ ...(includesUi
2685
+ ? [
2686
+ "Render the generated uploader with its required resourceId and connect onComplete to refresh the owning view.",
2687
+ ]
2688
+ : [
2689
+ "Add --ui in a full-stack app when you want a typed upload client, component, and component test.",
2690
+ ]),
2563
2691
  "Use the generated upload route for local development, then swap the local storage provider for S3-compatible storage before production.",
2564
2692
  ]);
2565
2693
  }