@beignet/cli 0.0.40 → 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 (111) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +139 -17
  3. package/dist/analysis/source-index.d.ts +38 -0
  4. package/dist/analysis/source-index.d.ts.map +1 -0
  5. package/dist/analysis/source-index.js +271 -0
  6. package/dist/analysis/source-index.js.map +1 -0
  7. package/dist/analysis/workspace.d.ts +16 -0
  8. package/dist/analysis/workspace.d.ts.map +1 -0
  9. package/dist/analysis/workspace.js +134 -0
  10. package/dist/analysis/workspace.js.map +1 -0
  11. package/dist/app-map-schema.d.ts +5 -0
  12. package/dist/app-map-schema.d.ts.map +1 -0
  13. package/dist/app-map-schema.js +26 -0
  14. package/dist/app-map-schema.js.map +1 -0
  15. package/dist/app-map.d.ts +96 -0
  16. package/dist/app-map.d.ts.map +1 -0
  17. package/dist/app-map.js +1141 -0
  18. package/dist/app-map.js.map +1 -0
  19. package/dist/check.d.ts +7 -0
  20. package/dist/check.d.ts.map +1 -1
  21. package/dist/check.js +20 -5
  22. package/dist/check.js.map +1 -1
  23. package/dist/choices.d.ts +18 -0
  24. package/dist/choices.d.ts.map +1 -1
  25. package/dist/choices.js +35 -0
  26. package/dist/choices.js.map +1 -1
  27. package/dist/db.d.ts +49 -13
  28. package/dist/db.d.ts.map +1 -1
  29. package/dist/db.js +202 -21
  30. package/dist/db.js.map +1 -1
  31. package/dist/doctor-fixes.d.ts +64 -0
  32. package/dist/doctor-fixes.d.ts.map +1 -0
  33. package/dist/doctor-fixes.js +142 -0
  34. package/dist/doctor-fixes.js.map +1 -0
  35. package/dist/explain.d.ts +100 -0
  36. package/dist/explain.d.ts.map +1 -0
  37. package/dist/explain.js +606 -0
  38. package/dist/explain.js.map +1 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +165 -21
  41. package/dist/index.js.map +1 -1
  42. package/dist/inspect.d.ts +33 -9
  43. package/dist/inspect.d.ts.map +1 -1
  44. package/dist/inspect.js +406 -119
  45. package/dist/inspect.js.map +1 -1
  46. package/dist/lib.d.ts +10 -2
  47. package/dist/lib.d.ts.map +1 -1
  48. package/dist/lib.js +5 -2
  49. package/dist/lib.js.map +1 -1
  50. package/dist/make/shared.js +3 -3
  51. package/dist/make/shared.js.map +1 -1
  52. package/dist/make.d.ts.map +1 -1
  53. package/dist/make.js +63 -2
  54. package/dist/make.js.map +1 -1
  55. package/dist/mcp.d.ts +1 -1
  56. package/dist/mcp.d.ts.map +1 -1
  57. package/dist/mcp.js +188 -12
  58. package/dist/mcp.js.map +1 -1
  59. package/dist/preflight.d.ts.map +1 -1
  60. package/dist/preflight.js +10 -0
  61. package/dist/preflight.js.map +1 -1
  62. package/dist/provider-add.d.ts.map +1 -1
  63. package/dist/provider-add.js +167 -13
  64. package/dist/provider-add.js.map +1 -1
  65. package/dist/provider-audit.d.ts +6 -0
  66. package/dist/provider-audit.d.ts.map +1 -1
  67. package/dist/provider-audit.js +63 -15
  68. package/dist/provider-audit.js.map +1 -1
  69. package/dist/templates/agents.d.ts.map +1 -1
  70. package/dist/templates/agents.js +35 -6
  71. package/dist/templates/agents.js.map +1 -1
  72. package/dist/templates/base.d.ts.map +1 -1
  73. package/dist/templates/base.js +13 -7
  74. package/dist/templates/base.js.map +1 -1
  75. package/dist/templates/index.d.ts +1 -1
  76. package/dist/templates/index.d.ts.map +1 -1
  77. package/dist/templates/index.js +8 -1
  78. package/dist/templates/index.js.map +1 -1
  79. package/dist/templates/server.d.ts +3 -0
  80. package/dist/templates/server.d.ts.map +1 -1
  81. package/dist/templates/server.js +55 -1
  82. package/dist/templates/server.js.map +1 -1
  83. package/dist/templates/shared.d.ts +2 -1
  84. package/dist/templates/shared.d.ts.map +1 -1
  85. package/dist/templates/shared.js +8 -5
  86. package/dist/templates/shared.js.map +1 -1
  87. package/package.json +3 -2
  88. package/skills/app-structure/SKILL.md +48 -6
  89. package/src/analysis/source-index.ts +395 -0
  90. package/src/analysis/workspace.ts +180 -0
  91. package/src/app-map-schema.ts +28 -0
  92. package/src/app-map.ts +1705 -0
  93. package/src/check.ts +27 -4
  94. package/src/choices.ts +57 -0
  95. package/src/db.ts +276 -28
  96. package/src/doctor-fixes.ts +252 -0
  97. package/src/explain.ts +894 -0
  98. package/src/index.ts +237 -31
  99. package/src/inspect.ts +567 -147
  100. package/src/lib.ts +64 -1
  101. package/src/make/shared.ts +3 -3
  102. package/src/make.ts +90 -2
  103. package/src/mcp.ts +292 -12
  104. package/src/preflight.ts +14 -0
  105. package/src/provider-add.ts +211 -12
  106. package/src/provider-audit.ts +127 -22
  107. package/src/templates/agents.ts +35 -6
  108. package/src/templates/base.ts +25 -6
  109. package/src/templates/index.ts +18 -1
  110. package/src/templates/server.ts +58 -1
  111. package/src/templates/shared.ts +15 -7
package/src/index.ts CHANGED
@@ -13,14 +13,22 @@ import {
13
13
  type StricliProcess,
14
14
  text_en,
15
15
  } from "@stricli/core";
16
+ import { type AppMapNodeKind, appMapNodeKinds } from "./app-map-schema.js";
16
17
  import {
17
18
  type CompletionShell,
18
19
  type CreateProviderName,
19
20
  completionShellChoices,
20
21
  createProviderChoices,
22
+ type DatabaseCommand,
21
23
  type DatabaseName,
24
+ type DatabaseSchemaDialect,
25
+ type DatabaseSchemaTable,
26
+ type DoctorFixOperationId,
22
27
  databaseChoices,
28
+ databaseSchemaDialectChoices,
29
+ databaseSchemaTableChoices,
23
30
  databaseStartCommand,
31
+ doctorFixOperationIds,
24
32
  type MakeFeatureAddon,
25
33
  type MakeFeatureRecipe,
26
34
  makeFeatureAddonChoices,
@@ -36,6 +44,7 @@ import {
36
44
  } from "./choices.js";
37
45
  // Type-only: the runtime module stays behind the create command's lazy loader.
38
46
  import type { CreateSelections } from "./create-prompts.js";
47
+ import type { ExplainTargetKind } from "./explain.js";
39
48
  import type { AddProviderPresetResult } from "./provider-add.js";
40
49
  import { getCliVersion } from "./version.js";
41
50
 
@@ -106,6 +115,13 @@ type RoutesFlags = {
106
115
  cwd?: string;
107
116
  };
108
117
 
118
+ type MapFlags = RoutesFlags & {
119
+ feature?: string;
120
+ kind?: readonly AppMapNodeKind[];
121
+ };
122
+
123
+ type ExplainFlags = RoutesFlags;
124
+
109
125
  type LintFlags = {
110
126
  json?: boolean;
111
127
  cwd?: string;
@@ -134,21 +150,6 @@ type DbFlags = {
134
150
  cwd?: string;
135
151
  };
136
152
 
137
- type DatabaseSchemaDialect = "sqlite" | "postgres" | "mysql";
138
- type DatabaseSchemaTable = "audit" | "idempotency" | "outbox";
139
-
140
- const databaseSchemaDialectChoices = [
141
- "sqlite",
142
- "postgres",
143
- "mysql",
144
- ] as const satisfies readonly DatabaseSchemaDialect[];
145
-
146
- const databaseSchemaTableChoices = [
147
- "audit",
148
- "idempotency",
149
- "outbox",
150
- ] as const satisfies readonly DatabaseSchemaTable[];
151
-
152
153
  type DbSchemaSyncFlags = DbFlags & {
153
154
  dialect?: DatabaseSchemaDialect;
154
155
  tables?: readonly DatabaseSchemaTable[];
@@ -249,6 +250,9 @@ type DoctorFlags = {
249
250
  json?: boolean;
250
251
  strict?: boolean;
251
252
  fix?: boolean;
253
+ dryRun?: boolean;
254
+ plan?: string;
255
+ only?: readonly DoctorFixOperationId[];
252
256
  cwd?: string;
253
257
  format?: OutputFormat;
254
258
  };
@@ -366,6 +370,21 @@ const routesFlagParameters = {
366
370
  cwd: cwdFlag,
367
371
  } satisfies FlagParametersForType<RoutesFlags, CliContext>;
368
372
 
373
+ const mapFlagParameters = {
374
+ json: jsonFlag,
375
+ feature: parsedStringFlag(
376
+ "Project one feature and its direct relationships.",
377
+ ),
378
+ kind: {
379
+ kind: "enum",
380
+ values: appMapNodeKinds,
381
+ optional: true,
382
+ variadic: ",",
383
+ brief: "Include only these node kinds. Accepts comma-separated values.",
384
+ },
385
+ cwd: cwdFlag,
386
+ } satisfies FlagParametersForType<MapFlags, CliContext>;
387
+
369
388
  const lintFlagParameters = {
370
389
  json: jsonFlag,
371
390
  cwd: cwdFlag,
@@ -585,6 +604,19 @@ const providerPresetPositional = {
585
604
  ],
586
605
  } as const;
587
606
 
607
+ function explainTargetPositional(kind: ExplainTargetKind) {
608
+ return {
609
+ kind: "tuple",
610
+ parameters: [
611
+ {
612
+ parse: parseString,
613
+ placeholder: kind,
614
+ brief: `${kind[0]?.toUpperCase()}${kind.slice(1)} to explain.`,
615
+ },
616
+ ],
617
+ } as const;
618
+ }
619
+
588
620
  function resolveOutputFormat(flags: {
589
621
  json?: boolean;
590
622
  format?: OutputFormat;
@@ -829,6 +861,98 @@ const routesCommand = buildCommand<RoutesFlags, [], CliContext>({
829
861
  },
830
862
  });
831
863
 
864
+ const mapCommand = buildCommand<MapFlags, [], CliContext>({
865
+ docs: {
866
+ brief: "Map the app's architecture and registered workflows.",
867
+ fullDescription:
868
+ "Build a deterministic graph of features, HTTP contracts, routes, use cases, policies, workflows, ports, providers, OpenAPI, tests, dependencies, and doctor findings.",
869
+ },
870
+ parameters: {
871
+ flags: mapFlagParameters,
872
+ },
873
+ loader: async () => {
874
+ const { formatAppMap, mapApp, projectAppMap } = await import(
875
+ "./app-map.js"
876
+ );
877
+
878
+ return async function runMap(this: CliContext, flags: MapFlags) {
879
+ const result = projectAppMap(
880
+ await mapApp({ cwd: flags.cwd, strict: true }),
881
+ {
882
+ feature: flags.feature,
883
+ kinds: flags.kind,
884
+ },
885
+ );
886
+ writeOutput(
887
+ this,
888
+ flags.json ? JSON.stringify(result, null, 2) : formatAppMap(result),
889
+ );
890
+ };
891
+ },
892
+ });
893
+
894
+ function explainCommand(kind: ExplainTargetKind) {
895
+ return buildCommand<ExplainFlags, [string], CliContext>({
896
+ docs: {
897
+ brief: `Explain one mapped Beignet ${kind}.`,
898
+ fullDescription:
899
+ "Returns source-backed relationships, conventions, findings, suggested files, and follow-up commands without generating advice or changing the app.",
900
+ },
901
+ parameters: {
902
+ flags: routesFlagParameters,
903
+ positional: explainTargetPositional(kind),
904
+ },
905
+ loader: async () => {
906
+ const { explainApp, formatExplain } = await import("./explain.js");
907
+
908
+ return async function runExplain(
909
+ this: CliContext,
910
+ flags: ExplainFlags,
911
+ target: string,
912
+ ) {
913
+ const result = await explainApp({ kind, target, cwd: flags.cwd });
914
+ writeOutput(
915
+ this,
916
+ flags.json ? JSON.stringify(result, null, 2) : formatExplain(result),
917
+ );
918
+ };
919
+ },
920
+ });
921
+ }
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
+
949
+ const explainRoutes = buildRouteMap({
950
+ docs: {
951
+ brief: "Explain mapped Beignet app concepts with source evidence.",
952
+ },
953
+ routes: explainRouteCommands,
954
+ });
955
+
832
956
  const doctorCommand = buildCommand<DoctorFlags, [], CliContext>({
833
957
  docs: {
834
958
  brief: "Inspect app wiring and framework conventions.",
@@ -848,27 +972,103 @@ const doctorCommand = buildCommand<DoctorFlags, [], CliContext>({
848
972
  withNegated: false,
849
973
  brief: "Apply low-risk fixes before reporting.",
850
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
+ },
851
989
  cwd: cwdFlag,
852
990
  format: formatFlag,
853
- },
991
+ } satisfies FlagParametersForType<DoctorFlags, CliContext>,
854
992
  },
855
993
  loader: async () => {
856
- const { applyDoctorFixes, formatDoctor, formatDoctorGithub, inspectApp } =
857
- 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");
858
1005
 
859
1006
  return async function runDoctor(this: CliContext, flags: DoctorFlags) {
860
1007
  const format = resolveOutputFormat(flags);
861
- const fixes = flags.fix
862
- ? 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({
863
1054
  cwd: flags.cwd,
864
1055
  strict: Boolean(flags.strict),
1056
+ planId: flags.plan,
1057
+ ...(only.length > 0 ? { fixIds: only } : {}),
865
1058
  })
866
- : [];
867
- 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({
868
1066
  cwd: flags.cwd,
869
1067
  strict: Boolean(flags.strict),
870
1068
  });
871
- result.fixes = fixes;
1069
+ const result = applied
1070
+ ? createDoctorFixInspectionResult(inspected, applied)
1071
+ : inspected;
872
1072
  writeOutput(
873
1073
  this,
874
1074
  format === "json"
@@ -1035,7 +1235,7 @@ const lintCommand = buildCommand<LintFlags, [], CliContext>({
1035
1235
  const mcpCommand = buildCommand<McpFlags, [], CliContext>({
1036
1236
  docs: {
1037
1237
  brief:
1038
- "Run a Model Context Protocol server exposing this app's routes, doctor, lint, and generators over stdio.",
1238
+ "Run a Model Context Protocol server exposing the app map, validation, and generators over stdio.",
1039
1239
  },
1040
1240
  parameters: {
1041
1241
  flags: {
@@ -1133,9 +1333,7 @@ const providerRoutes = buildRouteMap({
1133
1333
  },
1134
1334
  });
1135
1335
 
1136
- type DatabaseCommandName = "generate" | "migrate" | "reset" | "seed";
1137
-
1138
- function databaseCommand(command: DatabaseCommandName) {
1336
+ function databaseCommand(command: DatabaseCommand) {
1139
1337
  return buildCommand<DbFlags, [], CliContext>({
1140
1338
  docs: {
1141
1339
  brief: `Run the app's db:${command} script.`,
@@ -1144,13 +1342,18 @@ function databaseCommand(command: DatabaseCommandName) {
1144
1342
  flags: dbFlagParameters,
1145
1343
  },
1146
1344
  loader: async () => {
1147
- const { runDatabaseCommand } = await import("./db.js");
1345
+ const { defaultDatabaseCommandMaxOutputBytes, runDatabaseCommand } =
1346
+ await import("./db.js");
1148
1347
 
1149
1348
  return async function runDb(this: CliContext, flags: DbFlags) {
1349
+ const captureOutput = Boolean(flags.json);
1150
1350
  const result = await runDatabaseCommand({
1151
1351
  command,
1152
1352
  cwd: flags.cwd,
1153
- captureOutput: Boolean(flags.json),
1353
+ captureOutput,
1354
+ maxOutputBytes: captureOutput
1355
+ ? defaultDatabaseCommandMaxOutputBytes
1356
+ : undefined,
1154
1357
  dryRun: Boolean(flags.dryRun),
1155
1358
  });
1156
1359
 
@@ -1221,7 +1424,7 @@ const dbRoutes = buildRouteMap({
1221
1424
  reset: databaseCommand("reset"),
1222
1425
  schema: dbSchemaRoutes,
1223
1426
  seed: databaseCommand("seed"),
1224
- },
1427
+ } satisfies Record<DatabaseCommand | "schema", unknown>,
1225
1428
  });
1226
1429
 
1227
1430
  const taskRunCommand = buildCommand<TaskRunFlags, [string], CliContext>({
@@ -2109,7 +2312,7 @@ const makeRoutes = buildRouteMap({
2109
2312
  const rootRoutes = buildRouteMap({
2110
2313
  docs: {
2111
2314
  brief: "Beignet CLI",
2112
- fullDescription: `Create apps, generate framework files, inspect routes, and check Beignet conventions.
2315
+ fullDescription: `Create apps, generate framework files, map and explain architecture, inspect routes, and check Beignet conventions.
2113
2316
 
2114
2317
  Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
2115
2318
  },
@@ -2119,7 +2322,9 @@ Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
2119
2322
  create: createCommand,
2120
2323
  db: dbRoutes,
2121
2324
  doctor: doctorCommand,
2325
+ explain: explainRoutes,
2122
2326
  lint: lintCommand,
2327
+ map: mapCommand,
2123
2328
  make: makeRoutes,
2124
2329
  mcp: mcpCommand,
2125
2330
  outbox: outboxRoutes,
@@ -2431,6 +2636,7 @@ ${openStep}
2431
2636
 
2432
2637
  Inspect the app:
2433
2638
  ${cli} routes
2639
+ ${cli} map
2434
2640
  ${cli} check
2435
2641
 
2436
2642
  Generate a feature: