@beignet/cli 0.0.41 → 0.0.42

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 (58) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +77 -21
  3. package/dist/choices.d.ts +18 -0
  4. package/dist/choices.d.ts.map +1 -1
  5. package/dist/choices.js +35 -0
  6. package/dist/choices.js.map +1 -1
  7. package/dist/db.d.ts +18 -7
  8. package/dist/db.d.ts.map +1 -1
  9. package/dist/db.js +20 -7
  10. package/dist/db.js.map +1 -1
  11. package/dist/doctor-fixes.d.ts +64 -0
  12. package/dist/doctor-fixes.d.ts.map +1 -0
  13. package/dist/doctor-fixes.js +142 -0
  14. package/dist/doctor-fixes.js.map +1 -0
  15. package/dist/explain.d.ts +3 -1
  16. package/dist/explain.d.ts.map +1 -1
  17. package/dist/explain.js +136 -42
  18. package/dist/explain.js.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +92 -25
  21. package/dist/index.js.map +1 -1
  22. package/dist/inspect.d.ts +33 -9
  23. package/dist/inspect.d.ts.map +1 -1
  24. package/dist/inspect.js +353 -116
  25. package/dist/inspect.js.map +1 -1
  26. package/dist/lib.d.ts +6 -2
  27. package/dist/lib.d.ts.map +1 -1
  28. package/dist/lib.js +3 -2
  29. package/dist/lib.js.map +1 -1
  30. package/dist/make/shared.js +3 -3
  31. package/dist/make/shared.js.map +1 -1
  32. package/dist/mcp.d.ts.map +1 -1
  33. package/dist/mcp.js +121 -13
  34. package/dist/mcp.js.map +1 -1
  35. package/dist/templates/agents.d.ts.map +1 -1
  36. package/dist/templates/agents.js +26 -10
  37. package/dist/templates/agents.js.map +1 -1
  38. package/dist/templates/base.d.ts.map +1 -1
  39. package/dist/templates/base.js +3 -3
  40. package/dist/templates/base.js.map +1 -1
  41. package/dist/templates/shared.d.ts +2 -1
  42. package/dist/templates/shared.d.ts.map +1 -1
  43. package/dist/templates/shared.js +7 -4
  44. package/dist/templates/shared.js.map +1 -1
  45. package/package.json +3 -2
  46. package/skills/app-structure/SKILL.md +34 -10
  47. package/src/choices.ts +57 -0
  48. package/src/db.ts +45 -15
  49. package/src/doctor-fixes.ts +252 -0
  50. package/src/explain.ts +151 -43
  51. package/src/index.ts +130 -35
  52. package/src/inspect.ts +497 -145
  53. package/src/lib.ts +28 -1
  54. package/src/make/shared.ts +3 -3
  55. package/src/mcp.ts +187 -13
  56. package/src/templates/agents.ts +26 -10
  57. package/src/templates/base.ts +3 -2
  58. package/src/templates/shared.ts +14 -6
package/src/explain.ts CHANGED
@@ -8,14 +8,10 @@ import {
8
8
  mapApp,
9
9
  projectAppMap,
10
10
  } from "./app-map.js";
11
+ import { appMapNodeKinds } from "./app-map-schema.js";
11
12
 
12
13
  /** Targets supported by `beignet explain` and the MCP `explain` tool. */
13
- export const explainTargetKinds = [
14
- "feature",
15
- "route",
16
- "provider",
17
- "diagnostic",
18
- ] as const;
14
+ export const explainTargetKinds = [...appMapNodeKinds, "diagnostic"] as const;
19
15
 
20
16
  /** One supported explanation target kind. */
21
17
  export type ExplainTargetKind = (typeof explainTargetKinds)[number];
@@ -32,6 +28,8 @@ export type ExplainTarget = {
32
28
  kind: ExplainTargetKind;
33
29
  id: string;
34
30
  name: string;
31
+ runtimeName?: string;
32
+ feature?: string;
35
33
  source?: AppMapSource;
36
34
  status?: AppMapNodeStatus;
37
35
  details?: Record<string, unknown>;
@@ -318,8 +316,13 @@ function resolveTargetNode(
318
316
  const nodeKind: AppMapNodeKind = kind;
319
317
  const candidates = appMap.nodes.filter((node) => node.kind === nodeKind);
320
318
  const query = normalized(target);
319
+ const idMatches = candidates.filter((node) => normalized(node.id) === query);
320
+ if (idMatches.length === 1) return idMatches[0];
321
+
321
322
  let matches = candidates.filter((node) =>
322
- aliasesForNode(node).some((alias) => normalized(alias) === query),
323
+ aliasesForNode(node).some(
324
+ (alias) => normalized(alias) === query && normalized(node.id) !== query,
325
+ ),
323
326
  );
324
327
 
325
328
  if (kind === "provider" && matches.length === 0) {
@@ -353,18 +356,27 @@ function resolveTargetNode(
353
356
  );
354
357
  }
355
358
 
356
- const available = candidates
357
- .map((node) => node.name)
359
+ const available = [...new Set(candidates.map(canonicalNodeSelector))]
358
360
  .sort()
359
361
  .slice(0, 20);
360
362
  throw new Error(
361
- `Could not explain ${kind} "${target}". ${available.length > 0 ? `Available ${kind}s: ${available.join(", ")}.` : `This app has no mapped ${kind}s.`}`,
363
+ `Could not explain ${kind} "${target}". ${available.length > 0 ? `Available ${kind} targets: ${available.join(", ")}.` : `This app has no mapped ${kind} targets.`}`,
362
364
  );
363
365
  }
364
366
 
367
+ function canonicalNodeSelector(node: AppMapNode): string {
368
+ return node.runtimeName ?? node.name;
369
+ }
370
+
365
371
  function aliasesForNode(node: AppMapNode): string[] {
366
- const aliases = [node.id, node.name];
372
+ const aliases = [node.id, node.name, node.source.file];
367
373
  if (node.runtimeName) aliases.push(node.runtimeName);
374
+ if (node.source.exportName) {
375
+ aliases.push(
376
+ node.source.exportName,
377
+ `${node.source.file}#${node.source.exportName}`,
378
+ );
379
+ }
368
380
  if (node.id.includes("#"))
369
381
  aliases.push(node.id.slice(node.id.lastIndexOf("#") + 1));
370
382
  if (node.kind === "route" && typeof node.details?.path === "string") {
@@ -412,6 +424,8 @@ function nodeTarget(
412
424
  kind,
413
425
  id: node.id,
414
426
  name: node.name,
427
+ ...(node.runtimeName ? { runtimeName: node.runtimeName } : {}),
428
+ ...(node.feature ? { feature: node.feature } : {}),
415
429
  source: node.source,
416
430
  ...(node.status ? { status: node.status } : {}),
417
431
  ...(node.details ? { details: node.details } : {}),
@@ -654,16 +668,7 @@ function suggestedActions(
654
668
  findings: ExplainFinding[],
655
669
  targetDir: string,
656
670
  ): ExplainSuggestedAction[] {
657
- const inspectCommand =
658
- target.kind === "feature"
659
- ? `beignet map --feature ${target.name} --json`
660
- : target.kind === "route"
661
- ? "beignet routes --json"
662
- : target.kind === "provider"
663
- ? "beignet provider audit --json"
664
- : findings.some((finding) => finding.source === "lint")
665
- ? "beignet lint --json"
666
- : "beignet doctor --strict --json";
671
+ const inspectCommand = inspectCommandForTarget(target, findings);
667
672
  const actions: ExplainSuggestedAction[] = [
668
673
  {
669
674
  kind: "inspect",
@@ -688,6 +693,35 @@ function suggestedActions(
688
693
  return actions;
689
694
  }
690
695
 
696
+ function inspectCommandForTarget(
697
+ target: ExplainTarget,
698
+ findings: ExplainFinding[],
699
+ ): string {
700
+ if (target.kind === "feature") {
701
+ return `beignet map --feature ${target.name} --json`;
702
+ }
703
+ if (
704
+ target.kind === "contract" ||
705
+ target.kind === "route" ||
706
+ target.kind === "route-group" ||
707
+ target.kind === "openapi"
708
+ ) {
709
+ return "beignet routes --json";
710
+ }
711
+ if (target.kind === "port" || target.kind === "provider") {
712
+ return "beignet provider audit --json";
713
+ }
714
+ if (target.kind === "diagnostic") {
715
+ return findings.every((finding) => finding.source === "lint")
716
+ ? "beignet lint --json"
717
+ : "beignet doctor --strict --json";
718
+ }
719
+ if (target.feature) {
720
+ return `beignet map --feature ${target.feature} --json`;
721
+ }
722
+ return `beignet map --kind ${target.kind} --json`;
723
+ }
724
+
691
725
  function providerRegistrationSources(target: ExplainTarget): AppMapSource[] {
692
726
  if (target.kind !== "provider") return [];
693
727
  const sources = target.details?.registrationSources;
@@ -722,29 +756,103 @@ function explainSummary(
722
756
  return `${target.name} is ${target.status ?? "mapped"} with ${countLabel(edges.length, "relationship")}, ${countLabel(fileCount, "source")}, and ${countLabel(findings.length, "relevant finding")}.`;
723
757
  }
724
758
 
759
+ const conventionsByTargetKind = {
760
+ feature: [
761
+ "Feature-owned contracts, use cases, policies, ports, workflows, clients, components, and tests stay under features/<feature>/.",
762
+ "Feature route groups and workflow artifacts register through the central server entrypoints.",
763
+ ],
764
+ contract: [
765
+ "Contracts own HTTP method, path, schemas, responses, metadata, and route errors.",
766
+ "Contracts stay transport-focused and bind to application behavior through feature routes.",
767
+ ],
768
+ route: [
769
+ "Routes bind contracts to use cases and policies without importing concrete infrastructure.",
770
+ "Feature route groups compose centrally in server/routes.ts and runtime route adapters stay thin.",
771
+ ],
772
+ "route-group": [
773
+ "Feature route groups are declared in features/<feature>/routes.ts and composed centrally in server/routes.ts.",
774
+ "Route groups bind contracts to use cases while preserving the app's dependency direction.",
775
+ ],
776
+ "use-case": [
777
+ "Use cases own validated application workflows and may depend on ports, policies, and domain code, not concrete infrastructure or UI.",
778
+ "Use cases remain callable outside HTTP so routes, tasks, jobs, and agent capabilities can reuse the same behavior.",
779
+ ],
780
+ policy: [
781
+ "Feature policies own business authorization for resources owned by that feature.",
782
+ "Authorization remains enforced in application workflows even when contract metadata also declares an ability.",
783
+ ],
784
+ ability: [
785
+ "An ability belongs to the policy of the feature that owns the resource being authorized.",
786
+ "Contracts may name authorization abilities, while policies and use cases enforce the business rule.",
787
+ ],
788
+ event: [
789
+ "Events represent facts that happened and belong to the feature that owns the fact.",
790
+ "Durable event publication registers through the app's workflow and outbox boundaries.",
791
+ ],
792
+ listener: [
793
+ "Listeners react to registered events and delegate application work through ports, jobs, notifications, or use cases.",
794
+ "Feature listeners register through the central listener registry used by runtime wiring and integrity checks.",
795
+ ],
796
+ job: [
797
+ "Jobs represent background work and remain explicit at provider-backed execution boundaries.",
798
+ "Feature jobs register through central runtime or outbox registries before workers can dispatch them.",
799
+ ],
800
+ schedule: [
801
+ "Schedules represent time-based triggers and delegate work to application workflows.",
802
+ "Feature schedules register centrally so runtime wiring, doctor, and beignet schedule run inspect the same surface.",
803
+ ],
804
+ task: [
805
+ "Tasks are explicit operational entrypoints for maintenance, backfills, and other bounded application work.",
806
+ "Feature tasks register centrally so beignet task run and runtime integrity checks use the same definitions.",
807
+ ],
808
+ notification: [
809
+ "Notifications represent user-facing communication intent, separate from provider-specific delivery.",
810
+ "Queued notification definitions register with the central notification and job runtime.",
811
+ ],
812
+ upload: [
813
+ "Uploads define feature-owned validation and storage workflows while storage remains behind an app port.",
814
+ "Upload route adapters stay thin and reuse the registered feature upload definition.",
815
+ ],
816
+ "agent-capability": [
817
+ "Agent capabilities expose curated, validated inputs and outputs by adapting existing application use cases.",
818
+ "Delegated context and authorization resolve centrally before an agent capability executes.",
819
+ ],
820
+ registry: [
821
+ "Registries explicitly compose feature declarations into the runtime surface inspected by doctor and app-map.",
822
+ "Central registries remain the source of truth for runtime wiring and integrity checks.",
823
+ ],
824
+ port: [
825
+ "Ports define app-facing dependency interfaces; infrastructure adapters provide their concrete implementations.",
826
+ "App ports wire through infra/port-wiring.ts and remain available to use cases through application context.",
827
+ ],
828
+ provider: [
829
+ "Ports define app-facing interfaces, providers adapt external systems, and app infrastructure wires them together.",
830
+ "Lifecycle providers register in server/providers.ts and provider-backed app ports wire through infra/port-wiring.ts.",
831
+ ],
832
+ table: [
833
+ "Database tables remain infrastructure-owned persistence details rather than domain or use-case dependencies.",
834
+ "Configured schema sources and migrations must describe the same deployed database surface.",
835
+ ],
836
+ openapi: [
837
+ "OpenAPI documents derive from the same registered contract surface served by the application.",
838
+ "Documented methods, paths, schemas, errors, and operation IDs stay aligned with their contracts.",
839
+ ],
840
+ entrypoint: [
841
+ "Runtime entrypoints compose the server or expose thin framework adapters over registered routes.",
842
+ "Entrypoints reuse central context, routes, providers, and workflow registries instead of duplicating application behavior.",
843
+ ],
844
+ test: [
845
+ "Feature behavior tests stay under features/<feature>/tests and exercise use cases, routes, policies, workflows, and UI.",
846
+ "Infrastructure and server integration tests may remain adjacent to the modules they exercise.",
847
+ ],
848
+ diagnostic: [
849
+ "Doctor findings describe framework convention drift; lint findings describe dependency-direction violations.",
850
+ "Fix the app structure or wiring named by the finding, then run the complete validation loop.",
851
+ ],
852
+ } as const satisfies Record<ExplainTargetKind, readonly string[]>;
853
+
725
854
  function conventionsFor(kind: ExplainTargetKind): string[] {
726
- switch (kind) {
727
- case "feature":
728
- return [
729
- "Feature-owned contracts, use cases, policies, ports, workflows, clients, components, and tests stay under features/<feature>/.",
730
- "Feature route groups and workflow artifacts register through the central server entrypoints.",
731
- ];
732
- case "route":
733
- return [
734
- "Contracts own HTTP method, path, schemas, responses, metadata, and route errors; routes bind contracts to use cases.",
735
- "Feature route groups compose centrally in server/routes.ts and runtime route adapters stay thin.",
736
- ];
737
- case "provider":
738
- return [
739
- "Ports define app-facing interfaces, providers adapt external systems, and app infrastructure wires them together.",
740
- "Lifecycle providers register in server/providers.ts and provider-backed app ports wire through infra/port-wiring.ts.",
741
- ];
742
- case "diagnostic":
743
- return [
744
- "Doctor findings describe framework convention drift; lint findings describe dependency-direction violations.",
745
- "Fix the app structure or wiring named by the finding, then run the complete validation loop.",
746
- ];
747
- }
855
+ return [...conventionsByTargetKind[kind]];
748
856
  }
749
857
 
750
858
  function scopeFiles(nodes: AppMapNode[], edges: AppMapEdge[]): Set<string> {
package/src/index.ts CHANGED
@@ -19,9 +19,16 @@ import {
19
19
  type CreateProviderName,
20
20
  completionShellChoices,
21
21
  createProviderChoices,
22
+ type DatabaseCommand,
22
23
  type DatabaseName,
24
+ type DatabaseSchemaDialect,
25
+ type DatabaseSchemaTable,
26
+ type DoctorFixOperationId,
23
27
  databaseChoices,
28
+ databaseSchemaDialectChoices,
29
+ databaseSchemaTableChoices,
24
30
  databaseStartCommand,
31
+ doctorFixOperationIds,
25
32
  type MakeFeatureAddon,
26
33
  type MakeFeatureRecipe,
27
34
  makeFeatureAddonChoices,
@@ -143,21 +150,6 @@ type DbFlags = {
143
150
  cwd?: string;
144
151
  };
145
152
 
146
- type DatabaseSchemaDialect = "sqlite" | "postgres" | "mysql";
147
- type DatabaseSchemaTable = "audit" | "idempotency" | "outbox";
148
-
149
- const databaseSchemaDialectChoices = [
150
- "sqlite",
151
- "postgres",
152
- "mysql",
153
- ] as const satisfies readonly DatabaseSchemaDialect[];
154
-
155
- const databaseSchemaTableChoices = [
156
- "audit",
157
- "idempotency",
158
- "outbox",
159
- ] as const satisfies readonly DatabaseSchemaTable[];
160
-
161
153
  type DbSchemaSyncFlags = DbFlags & {
162
154
  dialect?: DatabaseSchemaDialect;
163
155
  tables?: readonly DatabaseSchemaTable[];
@@ -258,6 +250,9 @@ type DoctorFlags = {
258
250
  json?: boolean;
259
251
  strict?: boolean;
260
252
  fix?: boolean;
253
+ dryRun?: boolean;
254
+ plan?: string;
255
+ only?: readonly DoctorFixOperationId[];
261
256
  cwd?: string;
262
257
  format?: OutputFormat;
263
258
  };
@@ -925,16 +920,37 @@ function explainCommand(kind: ExplainTargetKind) {
925
920
  });
926
921
  }
927
922
 
923
+ const explainRouteCommands = {
924
+ ability: explainCommand("ability"),
925
+ "agent-capability": explainCommand("agent-capability"),
926
+ contract: explainCommand("contract"),
927
+ diagnostic: explainCommand("diagnostic"),
928
+ entrypoint: explainCommand("entrypoint"),
929
+ event: explainCommand("event"),
930
+ feature: explainCommand("feature"),
931
+ job: explainCommand("job"),
932
+ listener: explainCommand("listener"),
933
+ notification: explainCommand("notification"),
934
+ openapi: explainCommand("openapi"),
935
+ policy: explainCommand("policy"),
936
+ port: explainCommand("port"),
937
+ provider: explainCommand("provider"),
938
+ registry: explainCommand("registry"),
939
+ route: explainCommand("route"),
940
+ "route-group": explainCommand("route-group"),
941
+ schedule: explainCommand("schedule"),
942
+ table: explainCommand("table"),
943
+ task: explainCommand("task"),
944
+ test: explainCommand("test"),
945
+ upload: explainCommand("upload"),
946
+ "use-case": explainCommand("use-case"),
947
+ } satisfies Record<ExplainTargetKind, ReturnType<typeof explainCommand>>;
948
+
928
949
  const explainRoutes = buildRouteMap({
929
950
  docs: {
930
951
  brief: "Explain mapped Beignet app concepts with source evidence.",
931
952
  },
932
- routes: {
933
- diagnostic: explainCommand("diagnostic"),
934
- feature: explainCommand("feature"),
935
- provider: explainCommand("provider"),
936
- route: explainCommand("route"),
937
- },
953
+ routes: explainRouteCommands,
938
954
  });
939
955
 
940
956
  const doctorCommand = buildCommand<DoctorFlags, [], CliContext>({
@@ -956,27 +972,103 @@ const doctorCommand = buildCommand<DoctorFlags, [], CliContext>({
956
972
  withNegated: false,
957
973
  brief: "Apply low-risk fixes before reporting.",
958
974
  },
975
+ dryRun: {
976
+ ...dryRunFlag,
977
+ brief: "Preview an exact doctor fix plan without writing files.",
978
+ },
979
+ plan: parsedStringFlag(
980
+ "Apply only when the current repair plan matches this plan ID.",
981
+ ),
982
+ only: {
983
+ kind: "enum",
984
+ values: doctorFixOperationIds,
985
+ optional: true,
986
+ variadic: ",",
987
+ brief: "Apply only these repair operation IDs. Requires --plan.",
988
+ },
959
989
  cwd: cwdFlag,
960
990
  format: formatFlag,
961
- },
991
+ } satisfies FlagParametersForType<DoctorFlags, CliContext>,
962
992
  },
963
993
  loader: async () => {
964
- const { applyDoctorFixes, formatDoctor, formatDoctorGithub, inspectApp } =
965
- await import("./inspect.js");
994
+ const {
995
+ applyDoctorFixPlan,
996
+ applyDoctorFixesWithResult,
997
+ createDoctorFixInspectionResult,
998
+ formatDoctor,
999
+ formatDoctorFixPlan,
1000
+ formatDoctorFixPlanGithub,
1001
+ formatDoctorGithub,
1002
+ inspectApp,
1003
+ planDoctorFixes,
1004
+ } = await import("./inspect.js");
966
1005
 
967
1006
  return async function runDoctor(this: CliContext, flags: DoctorFlags) {
968
1007
  const format = resolveOutputFormat(flags);
969
- const fixes = flags.fix
970
- ? await applyDoctorFixes({
1008
+ const only = flags.only ?? [];
1009
+ if (flags.dryRun && !flags.fix) {
1010
+ throw new Error("doctor --dry-run requires --fix.");
1011
+ }
1012
+ if (flags.plan && !flags.fix) {
1013
+ throw new Error("doctor --plan requires --fix.");
1014
+ }
1015
+ if (flags.dryRun && flags.plan) {
1016
+ throw new Error("doctor --dry-run cannot be combined with --plan.");
1017
+ }
1018
+ if (flags.dryRun && only.length > 0) {
1019
+ throw new Error(
1020
+ "doctor --dry-run previews the complete plan; use --only when applying it.",
1021
+ );
1022
+ }
1023
+ if (only.length > 0 && !flags.plan) {
1024
+ throw new Error("doctor --only requires --plan.");
1025
+ }
1026
+
1027
+ if (flags.dryRun) {
1028
+ const plan = await planDoctorFixes({
1029
+ cwd: flags.cwd,
1030
+ strict: Boolean(flags.strict),
1031
+ });
1032
+ writeOutput(
1033
+ this,
1034
+ format === "json"
1035
+ ? JSON.stringify(plan, null, 2)
1036
+ : format === "github"
1037
+ ? formatDoctorFixPlanGithub(plan)
1038
+ : formatDoctorFixPlan(plan, { color: useColor() }),
1039
+ );
1040
+ if (
1041
+ plan.diagnostics.some(
1042
+ (diagnostic) =>
1043
+ diagnostic.severity === "error" ||
1044
+ (flags.strict && diagnostic.severity === "warning"),
1045
+ )
1046
+ ) {
1047
+ this.process.exitCode = 1;
1048
+ }
1049
+ return;
1050
+ }
1051
+
1052
+ const applied = flags.plan
1053
+ ? await applyDoctorFixPlan({
971
1054
  cwd: flags.cwd,
972
1055
  strict: Boolean(flags.strict),
1056
+ planId: flags.plan,
1057
+ ...(only.length > 0 ? { fixIds: only } : {}),
973
1058
  })
974
- : [];
975
- const result = await inspectApp({
1059
+ : flags.fix
1060
+ ? await applyDoctorFixesWithResult({
1061
+ cwd: flags.cwd,
1062
+ strict: Boolean(flags.strict),
1063
+ })
1064
+ : undefined;
1065
+ const inspected = await inspectApp({
976
1066
  cwd: flags.cwd,
977
1067
  strict: Boolean(flags.strict),
978
1068
  });
979
- result.fixes = fixes;
1069
+ const result = applied
1070
+ ? createDoctorFixInspectionResult(inspected, applied)
1071
+ : inspected;
980
1072
  writeOutput(
981
1073
  this,
982
1074
  format === "json"
@@ -1241,9 +1333,7 @@ const providerRoutes = buildRouteMap({
1241
1333
  },
1242
1334
  });
1243
1335
 
1244
- type DatabaseCommandName = "generate" | "migrate" | "reset" | "seed";
1245
-
1246
- function databaseCommand(command: DatabaseCommandName) {
1336
+ function databaseCommand(command: DatabaseCommand) {
1247
1337
  return buildCommand<DbFlags, [], CliContext>({
1248
1338
  docs: {
1249
1339
  brief: `Run the app's db:${command} script.`,
@@ -1252,13 +1342,18 @@ function databaseCommand(command: DatabaseCommandName) {
1252
1342
  flags: dbFlagParameters,
1253
1343
  },
1254
1344
  loader: async () => {
1255
- const { runDatabaseCommand } = await import("./db.js");
1345
+ const { defaultDatabaseCommandMaxOutputBytes, runDatabaseCommand } =
1346
+ await import("./db.js");
1256
1347
 
1257
1348
  return async function runDb(this: CliContext, flags: DbFlags) {
1349
+ const captureOutput = Boolean(flags.json);
1258
1350
  const result = await runDatabaseCommand({
1259
1351
  command,
1260
1352
  cwd: flags.cwd,
1261
- captureOutput: Boolean(flags.json),
1353
+ captureOutput,
1354
+ maxOutputBytes: captureOutput
1355
+ ? defaultDatabaseCommandMaxOutputBytes
1356
+ : undefined,
1262
1357
  dryRun: Boolean(flags.dryRun),
1263
1358
  });
1264
1359
 
@@ -1329,7 +1424,7 @@ const dbRoutes = buildRouteMap({
1329
1424
  reset: databaseCommand("reset"),
1330
1425
  schema: dbSchemaRoutes,
1331
1426
  seed: databaseCommand("seed"),
1332
- },
1427
+ } satisfies Record<DatabaseCommand | "schema", unknown>,
1333
1428
  });
1334
1429
 
1335
1430
  const taskRunCommand = buildCommand<TaskRunFlags, [string], CliContext>({