@beignet/cli 0.0.46 → 0.0.48

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 (97) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +50 -21
  3. package/dist/check.d.ts +8 -1
  4. package/dist/check.d.ts.map +1 -1
  5. package/dist/check.js +18 -6
  6. package/dist/check.js.map +1 -1
  7. package/dist/db.d.ts +38 -0
  8. package/dist/db.d.ts.map +1 -1
  9. package/dist/db.js +52 -0
  10. package/dist/db.js.map +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +73 -6
  13. package/dist/index.js.map +1 -1
  14. package/dist/inspect.d.ts.map +1 -1
  15. package/dist/inspect.js +39 -2
  16. package/dist/inspect.js.map +1 -1
  17. package/dist/lib.d.ts +2 -2
  18. package/dist/lib.d.ts.map +1 -1
  19. package/dist/lib.js +1 -1
  20. package/dist/lib.js.map +1 -1
  21. package/dist/make/inbox.js +1 -1
  22. package/dist/make/inbox.js.map +1 -1
  23. package/dist/make/payments.js +1 -1
  24. package/dist/make/payments.js.map +1 -1
  25. package/dist/make/tenancy.d.ts.map +1 -1
  26. package/dist/make/tenancy.js +3 -2
  27. package/dist/make/tenancy.js.map +1 -1
  28. package/dist/mcp.d.ts.map +1 -1
  29. package/dist/mcp.js +39 -2
  30. package/dist/mcp.js.map +1 -1
  31. package/dist/preflight.d.ts +11 -3
  32. package/dist/preflight.d.ts.map +1 -1
  33. package/dist/preflight.js +139 -16
  34. package/dist/preflight.js.map +1 -1
  35. package/dist/provider-add.d.ts.map +1 -1
  36. package/dist/provider-add.js +7 -2
  37. package/dist/provider-add.js.map +1 -1
  38. package/dist/provider-audit.d.ts +13 -0
  39. package/dist/provider-audit.d.ts.map +1 -1
  40. package/dist/provider-audit.js +122 -8
  41. package/dist/provider-audit.js.map +1 -1
  42. package/dist/templates/agents.d.ts.map +1 -1
  43. package/dist/templates/agents.js +14 -7
  44. package/dist/templates/agents.js.map +1 -1
  45. package/dist/templates/base.d.ts +1 -0
  46. package/dist/templates/base.d.ts.map +1 -1
  47. package/dist/templates/base.js +11 -5
  48. package/dist/templates/base.js.map +1 -1
  49. package/dist/templates/db/index.d.ts +1 -0
  50. package/dist/templates/db/index.d.ts.map +1 -1
  51. package/dist/templates/db/index.js.map +1 -1
  52. package/dist/templates/db/migration-status.d.ts +9 -0
  53. package/dist/templates/db/migration-status.d.ts.map +1 -0
  54. package/dist/templates/db/migration-status.js +117 -0
  55. package/dist/templates/db/migration-status.js.map +1 -0
  56. package/dist/templates/db/mysql.d.ts.map +1 -1
  57. package/dist/templates/db/mysql.js +23 -0
  58. package/dist/templates/db/mysql.js.map +1 -1
  59. package/dist/templates/db/postgres.d.ts.map +1 -1
  60. package/dist/templates/db/postgres.js +21 -0
  61. package/dist/templates/db/postgres.js.map +1 -1
  62. package/dist/templates/db/sqlite.d.ts.map +1 -1
  63. package/dist/templates/db/sqlite.js +53 -0
  64. package/dist/templates/db/sqlite.js.map +1 -1
  65. package/dist/templates/index.d.ts.map +1 -1
  66. package/dist/templates/index.js +5 -0
  67. package/dist/templates/index.js.map +1 -1
  68. package/dist/templates/server.d.ts.map +1 -1
  69. package/dist/templates/server.js +4 -1
  70. package/dist/templates/server.js.map +1 -1
  71. package/dist/templates/shared.d.ts.map +1 -1
  72. package/dist/templates/shared.js +1 -0
  73. package/dist/templates/shared.js.map +1 -1
  74. package/package.json +2 -2
  75. package/skills/app-structure/SKILL.md +11 -7
  76. package/src/check.ts +35 -7
  77. package/src/db.ts +97 -0
  78. package/src/index.ts +108 -7
  79. package/src/inspect.ts +76 -1
  80. package/src/lib.ts +9 -1
  81. package/src/make/inbox.ts +1 -1
  82. package/src/make/payments.ts +1 -1
  83. package/src/make/tenancy.ts +3 -2
  84. package/src/mcp.ts +56 -1
  85. package/src/preflight.ts +195 -16
  86. package/src/provider-add.ts +7 -2
  87. package/src/provider-audit.ts +210 -18
  88. package/src/templates/agents.ts +14 -7
  89. package/src/templates/base.ts +12 -3
  90. package/src/templates/db/index.ts +1 -0
  91. package/src/templates/db/migration-status.ts +126 -0
  92. package/src/templates/db/mysql.ts +23 -0
  93. package/src/templates/db/postgres.ts +21 -0
  94. package/src/templates/db/sqlite.ts +53 -0
  95. package/src/templates/index.ts +5 -0
  96. package/src/templates/server.ts +4 -1
  97. package/src/templates/shared.ts +1 -0
package/src/index.ts CHANGED
@@ -134,6 +134,8 @@ type CheckFlags = {
134
134
  json?: boolean;
135
135
  fix?: boolean;
136
136
  preflight?: boolean;
137
+ preflightConnect?: boolean;
138
+ connectTimeoutMs?: number;
137
139
  cwd?: string;
138
140
  };
139
141
 
@@ -142,6 +144,7 @@ type PreflightFlags = {
142
144
  envFile?: string;
143
145
  envModule?: string;
144
146
  connect?: boolean;
147
+ connectTimeoutMs?: number;
145
148
  serverModule?: string;
146
149
  cwd?: string;
147
150
  };
@@ -152,6 +155,11 @@ type DbFlags = {
152
155
  cwd?: string;
153
156
  };
154
157
 
158
+ type DbStatusFlags = {
159
+ json?: boolean;
160
+ cwd?: string;
161
+ };
162
+
155
163
  type DbSchemaSyncFlags = DbFlags & {
156
164
  dialect?: DatabaseSchemaDialect;
157
165
  tables?: readonly DatabaseSchemaTable[];
@@ -349,6 +357,14 @@ const parsedDateFlag = (brief: string) =>
349
357
  brief,
350
358
  }) as const;
351
359
 
360
+ const connectTimeoutMsFlag = {
361
+ kind: "parsed",
362
+ parse: parsePositiveInteger,
363
+ optional: true,
364
+ brief:
365
+ "Per migration-status or dependency health check timeout in milliseconds. Defaults to 5000.",
366
+ } as const;
367
+
352
368
  const cwdFlag = parsedStringFlag(
353
369
  "App directory. Defaults to the current working directory.",
354
370
  );
@@ -411,6 +427,11 @@ const dbFlagParameters = {
411
427
  cwd: cwdFlag,
412
428
  } satisfies FlagParametersForType<DbFlags, CliContext>;
413
429
 
430
+ const dbStatusFlagParameters = {
431
+ json: jsonFlag,
432
+ cwd: cwdFlag,
433
+ } satisfies FlagParametersForType<DbStatusFlags, CliContext>;
434
+
414
435
  const dbSchemaSyncFlagParameters = {
415
436
  ...dbFlagParameters,
416
437
  dialect: {
@@ -1097,7 +1118,7 @@ const checkCommand = buildCommand<CheckFlags, [], CliContext>({
1097
1118
  docs: {
1098
1119
  brief: "Run the full app validation loop as one command.",
1099
1120
  fullDescription:
1100
- "Runs beignet lint, beignet doctor --strict, and the app's lint, typecheck, and test package scripts through the detected package manager. Every step runs even when an earlier one fails, so one run reports everything. Missing package scripts are skipped, not failed.",
1121
+ "Runs beignet lint, beignet doctor --strict, optional disconnected or connected preflight, and the app's lint, typecheck, and test package scripts through the detected package manager. Every step runs even when an earlier one fails, so one run reports everything. Missing package scripts are skipped, not failed.",
1101
1122
  },
1102
1123
  parameters: {
1103
1124
  flags: {
@@ -1112,8 +1133,17 @@ const checkCommand = buildCommand<CheckFlags, [], CliContext>({
1112
1133
  kind: "boolean",
1113
1134
  optional: true,
1114
1135
  withNegated: false,
1115
- brief: "Append a beignet preflight step after doctor.",
1136
+ brief:
1137
+ "Append a disconnected beignet preflight environment step after doctor.",
1138
+ },
1139
+ preflightConnect: {
1140
+ kind: "boolean",
1141
+ optional: true,
1142
+ withNegated: false,
1143
+ brief:
1144
+ "Append beignet preflight --connect after doctor. Implies --preflight.",
1116
1145
  },
1146
+ connectTimeoutMs: connectTimeoutMsFlag,
1117
1147
  cwd: cwdFlag,
1118
1148
  } satisfies FlagParametersForType<CheckFlags, CliContext>,
1119
1149
  },
@@ -1128,6 +1158,8 @@ const checkCommand = buildCommand<CheckFlags, [], CliContext>({
1128
1158
  cwd: flags.cwd,
1129
1159
  fix: flags.fix,
1130
1160
  preflight: flags.preflight,
1161
+ preflightConnect: flags.preflightConnect,
1162
+ connectTimeoutMs: flags.connectTimeoutMs,
1131
1163
  color,
1132
1164
  ...(flags.json
1133
1165
  ? {}
@@ -1153,7 +1185,7 @@ const preflightCommand = buildCommand<PreflightFlags, [], CliContext>({
1153
1185
  docs: {
1154
1186
  brief: "Validate the deploy environment before shipping.",
1155
1187
  fullDescription:
1156
- "Runtime production gate, unlike the static doctor checks: reads the actual environment the process runs with, verifies every env var installed provider manifests mark as required, flags values still matching .env.example or common placeholders on secret-like keys, and validates the app env schema by importing lib/env.ts. Run it in the deploy pipeline where production configuration is present. Exits 1 on any error finding.",
1188
+ "Runtime production gate, unlike the static doctor checks: reads the actual environment the process runs with, verifies every env var installed provider manifests mark as required, flags values still matching .env.example or common placeholders on secret-like keys, and validates the app env schema by importing lib/env.ts. With --connect, it checks migration status before booting the server and running port health checks. Run it in the deploy pipeline where production configuration is present. Exits 1 on any error finding.",
1157
1189
  },
1158
1190
  parameters: {
1159
1191
  flags: {
@@ -1169,8 +1201,9 @@ const preflightCommand = buildCommand<PreflightFlags, [], CliContext>({
1169
1201
  optional: true,
1170
1202
  withNegated: false,
1171
1203
  brief:
1172
- "Boot the app server and run every port's checkHealth(). Needs network and real credentials.",
1204
+ "Check migration status, then boot the app server and run every port's checkHealth(). Needs network and real credentials.",
1173
1205
  },
1206
+ connectTimeoutMs: connectTimeoutMsFlag,
1174
1207
  serverModule: parsedStringFlag(
1175
1208
  "Module exporting getServer, used by --connect. Defaults to server/index.ts.",
1176
1209
  ),
@@ -1191,6 +1224,7 @@ const preflightCommand = buildCommand<PreflightFlags, [], CliContext>({
1191
1224
  envFile: flags.envFile,
1192
1225
  envModule: flags.envModule,
1193
1226
  connect: flags.connect,
1227
+ connectTimeoutMs: flags.connectTimeoutMs,
1194
1228
  serverModule: flags.serverModule,
1195
1229
  });
1196
1230
 
@@ -1374,6 +1408,42 @@ function databaseCommand(command: DatabaseCommand) {
1374
1408
  });
1375
1409
  }
1376
1410
 
1411
+ const databaseStatusCommand = buildCommand<DbStatusFlags, [], CliContext>({
1412
+ docs: {
1413
+ brief: "Inspect whether app-owned database migrations are current.",
1414
+ fullDescription:
1415
+ "Runs the app's read-only db:status script. Exit 0 means current, exit 2 means pending migrations, and any other non-zero exit means inspection failed.",
1416
+ },
1417
+ parameters: {
1418
+ flags: dbStatusFlagParameters,
1419
+ },
1420
+ loader: async () => {
1421
+ const { defaultDatabaseCommandMaxOutputBytes, runDatabaseStatus } =
1422
+ await import("./db.js");
1423
+
1424
+ return async function runDbStatus(this: CliContext, flags: DbStatusFlags) {
1425
+ const captureOutput = Boolean(flags.json);
1426
+ const result = await runDatabaseStatus({
1427
+ cwd: flags.cwd,
1428
+ captureOutput,
1429
+ maxOutputBytes: captureOutput
1430
+ ? defaultDatabaseCommandMaxOutputBytes
1431
+ : undefined,
1432
+ });
1433
+
1434
+ writeOutput(
1435
+ this,
1436
+ flags.json
1437
+ ? JSON.stringify(result, null, 2)
1438
+ : databaseStatusNextSteps(result),
1439
+ );
1440
+ if (result.exitCode !== 0) {
1441
+ this.process.exitCode = result.exitCode;
1442
+ }
1443
+ };
1444
+ },
1445
+ });
1446
+
1377
1447
  const dbSchemaSyncCommand = buildCommand<DbSchemaSyncFlags, [], CliContext>({
1378
1448
  docs: {
1379
1449
  brief: "Sync app-owned re-exports of Beignet provider tables.",
@@ -1427,7 +1497,8 @@ const dbRoutes = buildRouteMap({
1427
1497
  reset: databaseCommand("reset"),
1428
1498
  schema: dbSchemaRoutes,
1429
1499
  seed: databaseCommand("seed"),
1430
- } satisfies Record<DatabaseCommand | "schema", unknown>,
1500
+ status: databaseStatusCommand,
1501
+ } satisfies Record<DatabaseCommand | "schema" | "status", unknown>,
1431
1502
  });
1432
1503
 
1433
1504
  const taskRunCommand = buildCommand<TaskRunFlags, [string], CliContext>({
@@ -2398,6 +2469,14 @@ type DatabaseCommandNextStepsResult = {
2398
2469
  dryRun: boolean;
2399
2470
  };
2400
2471
 
2472
+ type DatabaseStatusNextStepsResult = {
2473
+ status: "current" | "pending" | "failed";
2474
+ cwd: string;
2475
+ script: string;
2476
+ runner: string;
2477
+ args: string[];
2478
+ };
2479
+
2401
2480
  type DatabaseSchemaSyncNextStepsResult = {
2402
2481
  cwd: string;
2403
2482
  dialect: DatabaseSchemaDialect;
@@ -2632,6 +2711,7 @@ Next steps:
2632
2711
  ${envStep}
2633
2712
  ${startDatabaseStep}Prepare the database:
2634
2713
  ${cli} db migrate
2714
+ ${cli} db status
2635
2715
 
2636
2716
  Start the app:
2637
2717
  ${run} dev
@@ -2646,6 +2726,7 @@ Generate a feature:
2646
2726
  ${cli} make feature projects
2647
2727
  ${cli} db generate
2648
2728
  ${cli} db migrate
2729
+ ${cli} db status
2649
2730
  ${cli} check`;
2650
2731
  }
2651
2732
 
@@ -2722,7 +2803,7 @@ function makeResourceNextSteps(result: MakeNextStepsResult): string {
2722
2803
  return makeNextSteps(result, "resource", [
2723
2804
  "Use make resource when the feature maps cleanly to a REST resource with repository-backed persistence.",
2724
2805
  "Review the generated schemas and repository fields.",
2725
- "If this app uses Drizzle, run beignet db generate and beignet db migrate so the new schema is ready.",
2806
+ "If this app uses Drizzle, run beignet db generate, beignet db migrate, and beignet db status so the new schema is ready and verified.",
2726
2807
  "Run your app's lint, test, and typecheck commands, then beignet lint and beignet doctor.",
2727
2808
  ]);
2728
2809
  }
@@ -2739,6 +2820,25 @@ Command:
2739
2820
  ${command}`;
2740
2821
  }
2741
2822
 
2823
+ function databaseStatusNextSteps(
2824
+ result: DatabaseStatusNextStepsResult,
2825
+ ): string {
2826
+ const command = [result.runner, ...result.args].join(" ");
2827
+ const summary =
2828
+ result.status === "current"
2829
+ ? "Database migrations are current."
2830
+ : result.status === "pending"
2831
+ ? "Database migrations are pending."
2832
+ : "Database migration status could not be determined.";
2833
+
2834
+ return `${summary}
2835
+
2836
+ Ran ${result.script} in ${result.cwd}
2837
+
2838
+ Command:
2839
+ ${command}`;
2840
+ }
2841
+
2742
2842
  function databaseSchemaSyncNextSteps(
2743
2843
  result: DatabaseSchemaSyncNextStepsResult,
2744
2844
  ): string {
@@ -2759,6 +2859,7 @@ ${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
2759
2859
  Next steps:
2760
2860
  Run beignet db generate to produce app-owned Drizzle migrations.
2761
2861
  Run beignet db migrate to apply the migration.
2862
+ Run beignet db status to verify the target database.
2762
2863
  Run beignet doctor --strict to check provider setup.`;
2763
2864
  }
2764
2865
 
@@ -2766,7 +2867,7 @@ function makeFeatureNextSteps(result: MakeNextStepsResult): string {
2766
2867
  return makeNextSteps(result, "feature slice", [
2767
2868
  "Use make feature for product capabilities and workflows. Use make resource when the concept is mostly CRUD-shaped.",
2768
2869
  "Treat the generated name field as a placeholder and shape the contracts, use cases, and repository around the feature's real workflow.",
2769
- "If this app uses Drizzle, run beignet db generate and beignet db migrate so the new schema is ready.",
2870
+ "If this app uses Drizzle, run beignet db generate, beignet db migrate, and beignet db status so the new schema is ready and verified.",
2770
2871
  "Run your app's lint, test, and typecheck commands, then beignet lint and beignet doctor.",
2771
2872
  ]);
2772
2873
  }
package/src/inspect.ts CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  import {
44
44
  detectedProviderVariants,
45
45
  diagnosticPackageJsonFile,
46
+ formatProviderEnvAlternatives,
46
47
  formatProviderMetadataIssues,
47
48
  installedPackageNames,
48
49
  providerDoctorRulesForInstalledPackages,
@@ -2619,6 +2620,27 @@ async function inspectProductionReadiness(
2619
2620
  provider.variants,
2620
2621
  providerEntries,
2621
2622
  )) {
2623
+ const envAlternatives = configuredProviderRequiredEnvAlternatives(
2624
+ variant.requiredEnvAlternatives,
2625
+ config,
2626
+ );
2627
+ if (
2628
+ envAlternatives.length > 0 &&
2629
+ !(await providerRequiredEnvAlternativeExists(
2630
+ targetDir,
2631
+ configFiles,
2632
+ envAlternatives,
2633
+ sourceCache,
2634
+ ))
2635
+ ) {
2636
+ diagnostics.push({
2637
+ severity: "warning",
2638
+ code: "BEIGNET_PROVIDER_ENV_MISSING",
2639
+ file: "package.json",
2640
+ message: `${provider.packageName} is installed and ${variant.displayName} is registered, but none of its required environment configurations are fully mentioned in app env/config files. Configure ${formatProviderEnvAlternatives(envAlternatives)}, or remove the provider registration.`,
2641
+ });
2642
+ }
2643
+
2622
2644
  for (const envVar of configuredProviderRequiredEnv(
2623
2645
  variant.requiredEnv,
2624
2646
  config,
@@ -2669,6 +2691,27 @@ async function inspectProductionReadiness(
2669
2691
  continue;
2670
2692
  }
2671
2693
 
2694
+ const envAlternatives = configuredProviderRequiredEnvAlternatives(
2695
+ provider.requiredEnvAlternatives ?? [],
2696
+ config,
2697
+ );
2698
+ if (
2699
+ envAlternatives.length > 0 &&
2700
+ !(await providerRequiredEnvAlternativeExists(
2701
+ targetDir,
2702
+ configFiles,
2703
+ envAlternatives,
2704
+ sourceCache,
2705
+ ))
2706
+ ) {
2707
+ diagnostics.push({
2708
+ severity: "warning",
2709
+ code: "BEIGNET_PROVIDER_ENV_MISSING",
2710
+ file: "package.json",
2711
+ message: `${provider.packageName} is installed, but none of its required environment configurations are fully mentioned in app env/config files. Configure ${formatProviderEnvAlternatives(envAlternatives)}, or remove the unused provider package.`,
2712
+ });
2713
+ }
2714
+
2672
2715
  for (const envVar of configuredProviderRequiredEnv(
2673
2716
  provider.requiredEnv ?? [],
2674
2717
  config,
@@ -5175,7 +5218,7 @@ async function inspectDatabaseLifecycleDrift(
5175
5218
  }
5176
5219
 
5177
5220
  if (hasDrizzleArtifacts) {
5178
- for (const script of ["db:generate", "db:migrate"] as const) {
5221
+ for (const script of ["db:generate", "db:migrate", "db:status"] as const) {
5179
5222
  if (packageScripts[script]) continue;
5180
5223
  diagnostics.push({
5181
5224
  severity: "warning",
@@ -5257,6 +5300,12 @@ async function inspectDatabaseLifecycleDrift(
5257
5300
  file: `${infrastructurePath}/db/reset.ts`,
5258
5301
  code: "BEIGNET_DB_RESET_ENTRYPOINT_MISSING",
5259
5302
  },
5303
+ {
5304
+ script: "db:status",
5305
+ command: "status",
5306
+ file: `${infrastructurePath}/db/migration-status.ts`,
5307
+ code: "BEIGNET_DB_STATUS_ENTRYPOINT_MISSING",
5308
+ },
5260
5309
  ] as const;
5261
5310
 
5262
5311
  for (const entrypoint of standardEntrypoints) {
@@ -6284,6 +6333,32 @@ function configuredProviderRequiredEnv(
6284
6333
  return envVars.filter((envVar) => !ignored.has(envVar));
6285
6334
  }
6286
6335
 
6336
+ function configuredProviderRequiredEnvAlternatives(
6337
+ alternatives: readonly (readonly string[])[],
6338
+ config: ResolvedBeignetConfig,
6339
+ ): string[][] {
6340
+ return alternatives.map((alternative) =>
6341
+ configuredProviderRequiredEnv(alternative, config),
6342
+ );
6343
+ }
6344
+
6345
+ async function providerRequiredEnvAlternativeExists(
6346
+ targetDir: string,
6347
+ configFiles: readonly string[],
6348
+ alternatives: readonly (readonly string[])[],
6349
+ sourceCache: Map<string, string>,
6350
+ ): Promise<boolean> {
6351
+ for (const alternative of alternatives) {
6352
+ const exists = await Promise.all(
6353
+ alternative.map((envVar) =>
6354
+ providerRequiredEnvExists(targetDir, configFiles, envVar, sourceCache),
6355
+ ),
6356
+ );
6357
+ if (exists.every(Boolean)) return true;
6358
+ }
6359
+ return false;
6360
+ }
6361
+
6287
6362
  function configuredProviderRequiredTable(
6288
6363
  tableName: string,
6289
6364
  config: ResolvedBeignetConfig,
package/src/lib.ts CHANGED
@@ -29,14 +29,22 @@ export type { CreateOptions } from "./create.js";
29
29
  export { createProject } from "./create.js";
30
30
  export type {
31
31
  DatabaseCommand,
32
+ DatabaseMigrationStatus,
32
33
  DatabaseSchemaDialect,
33
34
  DatabaseSchemaTable,
34
35
  RunDatabaseCommandOptions,
35
36
  RunDatabaseCommandResult,
37
+ RunDatabaseStatusOptions,
38
+ RunDatabaseStatusResult,
36
39
  SyncDatabaseSchemaOptions,
37
40
  SyncDatabaseSchemaResult,
38
41
  } from "./db.js";
39
- export { runDatabaseCommand, syncDatabaseSchema } from "./db.js";
42
+ export {
43
+ databaseMigrationsPendingExitCode,
44
+ runDatabaseCommand,
45
+ runDatabaseStatus,
46
+ syncDatabaseSchema,
47
+ } from "./db.js";
40
48
  export type {
41
49
  DoctorFixApplyResult,
42
50
  DoctorFixFileChange,
package/src/make/inbox.ts CHANGED
@@ -226,7 +226,7 @@ export async function makeInbox(
226
226
  */
227
227
  export function makeInboxNextStepLines(): string[] {
228
228
  return [
229
- "Run beignet db generate and beignet db migrate so inbox_notifications exists.",
229
+ "Run beignet db generate, beignet db migrate, and beignet db status so inbox_notifications exists and is verified.",
230
230
  'Add an in-app channel to any feature notification: import { defineInboxNotificationChannel } from "@/features/inbox/channel" and add inApp: defineInboxNotificationChannel(({ payload }) => ({ userId: payload.userId, type: "your-type", title: "Something happened" })) to its channels.',
231
231
  "Render the unread badge from features/inbox/components/unread-badge.tsx next to your Inbox nav link for a live unread count.",
232
232
  "Run your app's test, lint, and typecheck commands, then beignet lint and beignet doctor.",
@@ -209,7 +209,7 @@ export function makePaymentsNextStepLines(
209
209
  return [
210
210
  "The free plan works out of the box; billing accounts without an active subscription resolve to it automatically.",
211
211
  "Set BILLING_PRO_PRICE_ID in .env.local to the provider price ID for your pro plan.",
212
- "Run beignet db generate and beignet db migrate so billing_accounts exists before handling webhooks.",
212
+ "Run beignet db generate, beignet db migrate, and beignet db status so billing_accounts exists and is verified before handling webhooks.",
213
213
  'Gate paid actions once usage passes FREE_PLAN_LIMITS: await requireEntitlement(ctx, { entitlement: "todos.create", subject: { type: "tenant", id: tenantId } });',
214
214
  ...(hasPlanUi
215
215
  ? [
@@ -249,7 +249,7 @@ export async function makeTenancy(
249
249
  */
250
250
  export function makeTenancyNextStepLines(): string[] {
251
251
  return [
252
- "Run beignet db generate and beignet db migrate so the workspaces, workspace_members, and workspace_invites tables exist.",
252
+ "Run beignet db generate, beignet db migrate, and beignet db status so the workspaces, workspace_members, and workspace_invites tables exist and are verified.",
253
253
  "Requests resolve the active workspace from memberships and the beignet-workspace cookie; users without a workspace have no tenant, so point them at Settings > Workspace to create one.",
254
254
  "Run the db:seed script for a demo workspace with a sign-in-able demo admin (demo-admin@example.com).",
255
255
  "Invite emails send through the in-memory mailer in development; swap server/providers.ts to a real mail provider such as @beignet/provider-mail-resend for delivery.",
@@ -3857,7 +3857,7 @@ export type AppServiceContextInput =
3857
3857
  */
3858
3858
  export const appContext = defineServerContext<AppContext, AppRuntimePorts>()({
3859
3859
  \tgate: (ports) => ports.gate,
3860
- \trequest: async ({ req, ports, requestId, trace }) => {
3860
+ \trequest: async ({ req, requestInfo, ports, requestId, trace }) => {
3861
3861
  \t\tconst auth = await ports.auth.getSession(req);
3862
3862
  \t\tconst { tenant, membership } = await resolveRequestWorkspace({
3863
3863
  \t\t\tauth,
@@ -3876,6 +3876,7 @@ export const appContext = defineServerContext<AppContext, AppRuntimePorts>()({
3876
3876
  \t\t\t\t\t})
3877
3877
  \t\t\t\t: createAnonymousActor(),
3878
3878
  \t\t\tauth,
3879
+ \t\t\trequestInfo,
3879
3880
  \t\t\t...trace,
3880
3881
  \t\t\tports,
3881
3882
  \t\t\t...(tenant ? { tenant } : {}),
package/src/mcp.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  defaultDatabaseCommandMaxOutputBytes,
24
24
  defaultDatabaseCommandTimeoutMs,
25
25
  runDatabaseCommand,
26
+ runDatabaseStatus,
26
27
  syncDatabaseSchema,
27
28
  } from "./db.js";
28
29
  import { explainApp, explainTargetKinds } from "./explain.js";
@@ -639,7 +640,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
639
640
  "check",
640
641
  {
641
642
  description:
642
- "Run the full Beignet app validation loop: Beignet lint, strict doctor, and the app's lint, typecheck, and test scripts. Returns the same versioned JSON as beignet check --json, including every step, bounded failure output, durations, and overall ok status. Does not apply Beignet fixes, but app-owned scripts run with their normal side effects.",
643
+ "Run the full Beignet app validation loop: Beignet lint, strict doctor, optional disconnected or connected preflight, and the app's lint, typecheck, and test scripts. Returns the same versioned JSON as beignet check --json, including every step, bounded failure output, durations, and overall ok status. Does not apply Beignet fixes, but app-owned scripts run with their normal side effects.",
643
644
  inputSchema: {
644
645
  preflight: z
645
646
  .boolean()
@@ -647,6 +648,21 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
647
648
  .describe(
648
649
  "Include the environment preflight check before app package scripts. Defaults to false.",
649
650
  ),
651
+ preflightConnect: z
652
+ .boolean()
653
+ .optional()
654
+ .describe(
655
+ "Include connected preflight with migration status and dependency health checks. Implies preflight and defaults to false.",
656
+ ),
657
+ connectTimeoutMs: z
658
+ .number()
659
+ .int()
660
+ .min(1)
661
+ .max(60 * 60 * 1_000)
662
+ .optional()
663
+ .describe(
664
+ "Maximum duration for migration status and each dependency health check in milliseconds. Defaults to 5000.",
665
+ ),
650
666
  timeoutMs: z
651
667
  .number()
652
668
  .int()
@@ -670,6 +686,8 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
670
686
  await checkApp({
671
687
  cwd,
672
688
  preflight: input.preflight ?? false,
689
+ preflightConnect: input.preflightConnect ?? false,
690
+ connectTimeoutMs: input.connectTimeoutMs,
673
691
  scriptTimeoutMs: input.timeoutMs ?? defaultMcpCheckTimeoutMs,
674
692
  signal: extra.signal,
675
693
  }),
@@ -725,6 +743,43 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
725
743
  ),
726
744
  );
727
745
 
746
+ server.registerTool(
747
+ "db_status",
748
+ {
749
+ description:
750
+ "Inspect app-owned database migrations and return exactly the versioned beignet db status --json result. Exit state distinguishes current, pending, and failed inspection without mutating the database. Output is bounded, the complete child process tree is cancelled with the MCP request, and execution stops after the configured timeout.",
751
+ inputSchema: {
752
+ timeoutMs: z
753
+ .number()
754
+ .int()
755
+ .min(1_000)
756
+ .max(60 * 60 * 1_000)
757
+ .optional()
758
+ .describe(
759
+ "Maximum status duration in milliseconds. Defaults to 600000 (10 minutes).",
760
+ ),
761
+ },
762
+ annotations: {
763
+ readOnlyHint: true,
764
+ destructiveHint: false,
765
+ idempotentHint: true,
766
+ openWorldHint: true,
767
+ },
768
+ },
769
+ async (input, extra) =>
770
+ safeToolResult(async () =>
771
+ jsonResult(
772
+ await runDatabaseStatus({
773
+ cwd,
774
+ captureOutput: true,
775
+ maxOutputBytes: defaultDatabaseCommandMaxOutputBytes,
776
+ signal: extra.signal,
777
+ timeoutMs: input.timeoutMs ?? defaultDatabaseCommandTimeoutMs,
778
+ }),
779
+ ),
780
+ ),
781
+ );
782
+
728
783
  server.registerTool(
729
784
  "db_schema_sync",
730
785
  {