@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.
- package/CHANGELOG.md +19 -0
- package/README.md +139 -17
- package/dist/analysis/source-index.d.ts +38 -0
- package/dist/analysis/source-index.d.ts.map +1 -0
- package/dist/analysis/source-index.js +271 -0
- package/dist/analysis/source-index.js.map +1 -0
- package/dist/analysis/workspace.d.ts +16 -0
- package/dist/analysis/workspace.d.ts.map +1 -0
- package/dist/analysis/workspace.js +134 -0
- package/dist/analysis/workspace.js.map +1 -0
- package/dist/app-map-schema.d.ts +5 -0
- package/dist/app-map-schema.d.ts.map +1 -0
- package/dist/app-map-schema.js +26 -0
- package/dist/app-map-schema.js.map +1 -0
- package/dist/app-map.d.ts +96 -0
- package/dist/app-map.d.ts.map +1 -0
- package/dist/app-map.js +1141 -0
- package/dist/app-map.js.map +1 -0
- package/dist/check.d.ts +7 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +20 -5
- package/dist/check.js.map +1 -1
- package/dist/choices.d.ts +18 -0
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +35 -0
- package/dist/choices.js.map +1 -1
- package/dist/db.d.ts +49 -13
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +202 -21
- package/dist/db.js.map +1 -1
- package/dist/doctor-fixes.d.ts +64 -0
- package/dist/doctor-fixes.d.ts.map +1 -0
- package/dist/doctor-fixes.js +142 -0
- package/dist/doctor-fixes.js.map +1 -0
- package/dist/explain.d.ts +100 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +606 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +165 -21
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +33 -9
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +406 -119
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +10 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +5 -2
- package/dist/lib.js.map +1 -1
- package/dist/make/shared.js +3 -3
- package/dist/make/shared.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +63 -2
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +188 -12
- package/dist/mcp.js.map +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +10 -0
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +167 -13
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts +6 -0
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +63 -15
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +35 -6
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +13 -7
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +8 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +3 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +55 -1
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +2 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +8 -5
- package/dist/templates/shared.js.map +1 -1
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +48 -6
- package/src/analysis/source-index.ts +395 -0
- package/src/analysis/workspace.ts +180 -0
- package/src/app-map-schema.ts +28 -0
- package/src/app-map.ts +1705 -0
- package/src/check.ts +27 -4
- package/src/choices.ts +57 -0
- package/src/db.ts +276 -28
- package/src/doctor-fixes.ts +252 -0
- package/src/explain.ts +894 -0
- package/src/index.ts +237 -31
- package/src/inspect.ts +567 -147
- package/src/lib.ts +64 -1
- package/src/make/shared.ts +3 -3
- package/src/make.ts +90 -2
- package/src/mcp.ts +292 -12
- package/src/preflight.ts +14 -0
- package/src/provider-add.ts +211 -12
- package/src/provider-audit.ts +127 -22
- package/src/templates/agents.ts +35 -6
- package/src/templates/base.ts +25 -6
- package/src/templates/index.ts +18 -1
- package/src/templates/server.ts +58 -1
- package/src/templates/shared.ts +15 -7
package/src/lib.ts
CHANGED
|
@@ -6,16 +6,79 @@
|
|
|
6
6
|
* loads command modules lazily.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export type {
|
|
10
|
+
AppMapEdge,
|
|
11
|
+
AppMapEdgeKind,
|
|
12
|
+
AppMapNode,
|
|
13
|
+
AppMapNodeKind,
|
|
14
|
+
AppMapNodeStatus,
|
|
15
|
+
AppMapOptions,
|
|
16
|
+
AppMapProjectionOptions,
|
|
17
|
+
AppMapResult,
|
|
18
|
+
AppMapSource,
|
|
19
|
+
AppMapSummary,
|
|
20
|
+
AppMapUnresolvedReference,
|
|
21
|
+
} from "./app-map.js";
|
|
22
|
+
export {
|
|
23
|
+
appMapNodeKinds,
|
|
24
|
+
formatAppMap,
|
|
25
|
+
mapApp,
|
|
26
|
+
projectAppMap,
|
|
27
|
+
} from "./app-map.js";
|
|
9
28
|
export type { CreateOptions } from "./create.js";
|
|
10
29
|
export { createProject } from "./create.js";
|
|
11
|
-
export {
|
|
30
|
+
export type {
|
|
31
|
+
DatabaseCommand,
|
|
32
|
+
DatabaseSchemaDialect,
|
|
33
|
+
DatabaseSchemaTable,
|
|
34
|
+
RunDatabaseCommandOptions,
|
|
35
|
+
RunDatabaseCommandResult,
|
|
36
|
+
SyncDatabaseSchemaOptions,
|
|
37
|
+
SyncDatabaseSchemaResult,
|
|
38
|
+
} from "./db.js";
|
|
39
|
+
export { runDatabaseCommand, syncDatabaseSchema } from "./db.js";
|
|
40
|
+
export type {
|
|
41
|
+
DoctorFixApplyResult,
|
|
42
|
+
DoctorFixFileChange,
|
|
43
|
+
DoctorFixOperation,
|
|
44
|
+
DoctorFixOperationId,
|
|
45
|
+
DoctorFixPlan,
|
|
46
|
+
InspectFix,
|
|
47
|
+
} from "./doctor-fixes.js";
|
|
48
|
+
export { doctorFixOperationIds } from "./doctor-fixes.js";
|
|
49
|
+
export type {
|
|
50
|
+
ExplainAppOptions,
|
|
51
|
+
ExplainAppResult,
|
|
52
|
+
ExplainEvidence,
|
|
53
|
+
ExplainFinding,
|
|
54
|
+
ExplainRelatedNode,
|
|
55
|
+
ExplainRelationship,
|
|
56
|
+
ExplainSuggestedAction,
|
|
57
|
+
ExplainSuggestedFile,
|
|
58
|
+
ExplainTarget,
|
|
59
|
+
ExplainTargetKind,
|
|
60
|
+
} from "./explain.js";
|
|
61
|
+
export {
|
|
62
|
+
explainApp,
|
|
63
|
+
explainTargetKinds,
|
|
64
|
+
formatExplain,
|
|
65
|
+
} from "./explain.js";
|
|
12
66
|
export { main } from "./index.js";
|
|
67
|
+
export type {
|
|
68
|
+
ApplyDoctorFixPlanOptions,
|
|
69
|
+
DoctorFixInspectionResult,
|
|
70
|
+
DoctorFixPlanResult,
|
|
71
|
+
} from "./inspect.js";
|
|
13
72
|
export {
|
|
14
73
|
applyDoctorFixes,
|
|
74
|
+
applyDoctorFixPlan,
|
|
15
75
|
formatDoctor,
|
|
76
|
+
formatDoctorFixPlan,
|
|
77
|
+
formatDoctorFixPlanGithub,
|
|
16
78
|
formatDoctorGithub,
|
|
17
79
|
formatRoutes,
|
|
18
80
|
inspectApp,
|
|
81
|
+
planDoctorFixes,
|
|
19
82
|
} from "./inspect.js";
|
|
20
83
|
export { formatLint, formatLintGithub, lintApp } from "./lint.js";
|
|
21
84
|
export type { MakeFeatureAddon, MakeFeatureRecipe } from "./make.js";
|
package/src/make/shared.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
currentGeneratedPackageScripts,
|
|
20
20
|
externalVersions,
|
|
21
|
-
|
|
21
|
+
isLegacyGeneratedPackageScript,
|
|
22
22
|
} from "../templates/shared.js";
|
|
23
23
|
|
|
24
24
|
export type MakeResult = {
|
|
@@ -1096,7 +1096,7 @@ export async function updatePackageScripts(
|
|
|
1096
1096
|
for (const [name, command] of Object.entries(scripts)) {
|
|
1097
1097
|
if (
|
|
1098
1098
|
!packageJson.scripts[name] ||
|
|
1099
|
-
packageJson.scripts[name]
|
|
1099
|
+
isLegacyGeneratedPackageScript(name, packageJson.scripts[name])
|
|
1100
1100
|
) {
|
|
1101
1101
|
packageJson.scripts[name] = command;
|
|
1102
1102
|
}
|
|
@@ -1162,7 +1162,7 @@ function updateGeneratedPackageScripts(scripts: Record<string, string>): void {
|
|
|
1162
1162
|
currentGeneratedPackageScripts,
|
|
1163
1163
|
)) {
|
|
1164
1164
|
if (
|
|
1165
|
-
|
|
1165
|
+
isLegacyGeneratedPackageScript(name, scripts[name]) ||
|
|
1166
1166
|
(name === "test" && !scripts[name])
|
|
1167
1167
|
) {
|
|
1168
1168
|
scripts[name] = command;
|
package/src/make.ts
CHANGED
|
@@ -1090,6 +1090,7 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1090
1090
|
await updateOutboxRegistry(targetDir, names, "jobs", config, {
|
|
1091
1091
|
dryRun: true,
|
|
1092
1092
|
});
|
|
1093
|
+
await updateInngestJobRegistry(targetDir, names, config, { dryRun: true });
|
|
1093
1094
|
|
|
1094
1095
|
const result = await makeFeatureArtifact({
|
|
1095
1096
|
targetDir,
|
|
@@ -1116,6 +1117,9 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1116
1117
|
await applyOutboxPortWiring(result, targetDir, config, {
|
|
1117
1118
|
dryRun: Boolean(options.dryRun),
|
|
1118
1119
|
});
|
|
1120
|
+
await applyInngestJobRegistryUpdate(result, targetDir, names, config, {
|
|
1121
|
+
dryRun: Boolean(options.dryRun),
|
|
1122
|
+
});
|
|
1119
1123
|
|
|
1120
1124
|
return result;
|
|
1121
1125
|
}
|
|
@@ -3814,6 +3818,92 @@ async function applyOutboxRegistryUpdate(
|
|
|
3814
3818
|
}
|
|
3815
3819
|
}
|
|
3816
3820
|
|
|
3821
|
+
function inngestRegistryPath(config: ResolvedBeignetConfig): string {
|
|
3822
|
+
return path.posix.join(path.posix.dirname(config.paths.server), "inngest.ts");
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
async function appUsesInngestJobs(targetDir: string): Promise<boolean> {
|
|
3826
|
+
const source = await readOptionalFile(path.join(targetDir, "package.json"));
|
|
3827
|
+
if (source === undefined) return false;
|
|
3828
|
+
|
|
3829
|
+
try {
|
|
3830
|
+
const packageJson = JSON.parse(source) as PackageJsonLike;
|
|
3831
|
+
return Boolean(
|
|
3832
|
+
packageJson.dependencies?.["@beignet/provider-jobs-inngest"] ??
|
|
3833
|
+
packageJson.devDependencies?.["@beignet/provider-jobs-inngest"],
|
|
3834
|
+
);
|
|
3835
|
+
} catch {
|
|
3836
|
+
return false;
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
async function updateInngestJobRegistry(
|
|
3841
|
+
targetDir: string,
|
|
3842
|
+
names: JobNames,
|
|
3843
|
+
config: ResolvedBeignetConfig,
|
|
3844
|
+
options: { dryRun: boolean },
|
|
3845
|
+
): Promise<WriteGeneratedFileResult | undefined> {
|
|
3846
|
+
if (!(await appUsesInngestJobs(targetDir))) return undefined;
|
|
3847
|
+
|
|
3848
|
+
const registryPath = inngestRegistryPath(config);
|
|
3849
|
+
const destination = path.join(targetDir, registryPath);
|
|
3850
|
+
const existing = await readOptionalFile(destination);
|
|
3851
|
+
if (existing === undefined) {
|
|
3852
|
+
throw new Error(
|
|
3853
|
+
`@beignet/provider-jobs-inngest is installed, but ${registryPath} is missing. Run \`beignet provider add jobs-inngest\` to create the execution registry before generating jobs.`,
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
const featureIndexPath = path.join(
|
|
3858
|
+
featureArtifactDir(names, "jobs", config),
|
|
3859
|
+
"index.ts",
|
|
3860
|
+
);
|
|
3861
|
+
const registryName = `${names.featureSingularCamel}Jobs`;
|
|
3862
|
+
const importLine = `import { ${registryName} } from "${aliasModule(featureIndexPath)}";`;
|
|
3863
|
+
let next = existing;
|
|
3864
|
+
if (!next.includes(importLine)) next = insertAfterImports(next, importLine);
|
|
3865
|
+
|
|
3866
|
+
const registry = arrayInitializerInfo(next, "inngestJobs");
|
|
3867
|
+
if (!registry) {
|
|
3868
|
+
throw new Error(
|
|
3869
|
+
`Could not find \`export const inngestJobs = [...]\` in ${registryPath}. Restore the generated registry or register ${registryName} manually.`,
|
|
3870
|
+
);
|
|
3871
|
+
}
|
|
3872
|
+
if (!identifiersFromArrayExpression(registry.text).has(registryName)) {
|
|
3873
|
+
const updatedArray = appendToArrayExpression(registry.text, [
|
|
3874
|
+
`...${registryName}`,
|
|
3875
|
+
]);
|
|
3876
|
+
next = `${next.slice(0, registry.start)}${updatedArray}${next.slice(
|
|
3877
|
+
registry.end,
|
|
3878
|
+
)}`;
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
if (next === existing) return "skipped";
|
|
3882
|
+
if (!options.dryRun) await writeFile(destination, next);
|
|
3883
|
+
return "updated";
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3886
|
+
async function applyInngestJobRegistryUpdate(
|
|
3887
|
+
result: MakeJobResult,
|
|
3888
|
+
targetDir: string,
|
|
3889
|
+
names: JobNames,
|
|
3890
|
+
config: ResolvedBeignetConfig,
|
|
3891
|
+
options: { dryRun: boolean },
|
|
3892
|
+
): Promise<void> {
|
|
3893
|
+
const registryResult = await updateInngestJobRegistry(
|
|
3894
|
+
targetDir,
|
|
3895
|
+
names,
|
|
3896
|
+
config,
|
|
3897
|
+
options,
|
|
3898
|
+
);
|
|
3899
|
+
if (registryResult === undefined) return;
|
|
3900
|
+
|
|
3901
|
+
const registryPath = inngestRegistryPath(config);
|
|
3902
|
+
if (registryResult === "updated") result.updatedFiles.push(registryPath);
|
|
3903
|
+
if (registryResult === "skipped") result.skippedFiles.push(registryPath);
|
|
3904
|
+
if (!result.files.includes(registryPath)) result.files.push(registryPath);
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3817
3907
|
function schemasFile(
|
|
3818
3908
|
names: ResourceNames,
|
|
3819
3909
|
mode: ResourceGenerationMode,
|
|
@@ -5145,8 +5235,6 @@ export const ${names.jobExportName} = defineJob("${names.jobName}", {
|
|
|
5145
5235
|
\tpayload: ${names.payloadSchemaName},
|
|
5146
5236
|
\tretry: retry.exponential({
|
|
5147
5237
|
\t\tattempts: 3,
|
|
5148
|
-
\t\tinitialDelay: "1s",
|
|
5149
|
-
\t\tmaxDelay: "1m",
|
|
5150
5238
|
\t}),
|
|
5151
5239
|
\tasync handle({ payload, ctx }) {
|
|
5152
5240
|
\t\tctx.ports.logger.info("Job handled", {
|
package/src/mcp.ts
CHANGED
|
@@ -2,8 +2,31 @@ import path from "node:path";
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { mapApp, projectAppMap } from "./app-map.js";
|
|
6
|
+
import { appMapNodeKinds } from "./app-map-schema.js";
|
|
7
|
+
import { checkApp } from "./check.js";
|
|
8
|
+
import {
|
|
9
|
+
databaseCommandChoices,
|
|
10
|
+
databaseSchemaDialectChoices,
|
|
11
|
+
databaseSchemaTableChoices,
|
|
12
|
+
doctorFixOperationIds,
|
|
13
|
+
type ProviderPresetName,
|
|
14
|
+
providerPresetChoices,
|
|
15
|
+
} from "./choices.js";
|
|
16
|
+
import {
|
|
17
|
+
defaultDatabaseCommandMaxOutputBytes,
|
|
18
|
+
defaultDatabaseCommandTimeoutMs,
|
|
19
|
+
runDatabaseCommand,
|
|
20
|
+
syncDatabaseSchema,
|
|
21
|
+
} from "./db.js";
|
|
22
|
+
import { explainApp, explainTargetKinds } from "./explain.js";
|
|
23
|
+
import {
|
|
24
|
+
applyDoctorFixesWithResult,
|
|
25
|
+
applyDoctorFixPlan,
|
|
26
|
+
createDoctorFixInspectionResult,
|
|
27
|
+
inspectApp,
|
|
28
|
+
planDoctorFixes,
|
|
29
|
+
} from "./inspect.js";
|
|
7
30
|
import { lintApp } from "./lint.js";
|
|
8
31
|
import {
|
|
9
32
|
type MakeResourceResult,
|
|
@@ -68,6 +91,8 @@ const makeArtifactChoices = [
|
|
|
68
91
|
"use-case",
|
|
69
92
|
] as const;
|
|
70
93
|
|
|
94
|
+
const defaultMcpCheckTimeoutMs = 10 * 60 * 1_000;
|
|
95
|
+
|
|
71
96
|
type MakeArtifact = (typeof makeArtifactChoices)[number];
|
|
72
97
|
|
|
73
98
|
const makeInputSchema = {
|
|
@@ -325,7 +350,7 @@ async function runProviderAddTool(
|
|
|
325
350
|
}
|
|
326
351
|
|
|
327
352
|
/**
|
|
328
|
-
* Build the Beignet MCP server with
|
|
353
|
+
* Build the Beignet MCP server with app-map, validation, and generator tools.
|
|
329
354
|
* Every tool resolves against the app directory captured at launch.
|
|
330
355
|
*/
|
|
331
356
|
export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
@@ -335,6 +360,207 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
335
360
|
version: options.version,
|
|
336
361
|
});
|
|
337
362
|
|
|
363
|
+
server.registerTool(
|
|
364
|
+
"app_map",
|
|
365
|
+
{
|
|
366
|
+
description:
|
|
367
|
+
"Return a deterministic graph of this Beignet app: features, contracts, routes, use cases, authorization, workflows, ports, providers, OpenAPI, tests, dependency edges, and validation findings. Project by feature or node kind to keep agent context focused.",
|
|
368
|
+
inputSchema: {
|
|
369
|
+
feature: z
|
|
370
|
+
.string()
|
|
371
|
+
.optional()
|
|
372
|
+
.describe("Project one feature and its direct relationships."),
|
|
373
|
+
kinds: z
|
|
374
|
+
.array(z.enum(appMapNodeKinds))
|
|
375
|
+
.optional()
|
|
376
|
+
.describe("Include only these app-map node kinds."),
|
|
377
|
+
includeDiagnostics: z
|
|
378
|
+
.boolean()
|
|
379
|
+
.optional()
|
|
380
|
+
.describe("Include doctor and lint findings. Defaults to true."),
|
|
381
|
+
},
|
|
382
|
+
annotations: { readOnlyHint: true },
|
|
383
|
+
},
|
|
384
|
+
async (input) =>
|
|
385
|
+
safeToolResult(async () => {
|
|
386
|
+
const result = await mapApp({ cwd, strict: true });
|
|
387
|
+
return jsonResult(
|
|
388
|
+
projectAppMap(result, {
|
|
389
|
+
feature: input.feature,
|
|
390
|
+
kinds: input.kinds,
|
|
391
|
+
includeDiagnostics: input.includeDiagnostics,
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
394
|
+
}),
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
server.registerTool(
|
|
398
|
+
"explain",
|
|
399
|
+
{
|
|
400
|
+
description:
|
|
401
|
+
"Explain one mapped Beignet concept or diagnostic using deterministic source evidence. Supports every app-map node kind and returns relationships, relevant conventions, findings, suggested files, and follow-up commands without changing the app.",
|
|
402
|
+
inputSchema: {
|
|
403
|
+
kind: z.enum(explainTargetKinds).describe("Mapped concept to explain."),
|
|
404
|
+
target: z
|
|
405
|
+
.string()
|
|
406
|
+
.min(1)
|
|
407
|
+
.describe(
|
|
408
|
+
"Stable ID, runtime name, declaration name, source selector, applicable concept alias, or diagnostic code.",
|
|
409
|
+
),
|
|
410
|
+
},
|
|
411
|
+
annotations: { readOnlyHint: true },
|
|
412
|
+
},
|
|
413
|
+
async (input) =>
|
|
414
|
+
safeToolResult(async () =>
|
|
415
|
+
jsonResult(
|
|
416
|
+
await explainApp({ cwd, kind: input.kind, target: input.target }),
|
|
417
|
+
),
|
|
418
|
+
),
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
server.registerTool(
|
|
422
|
+
"check",
|
|
423
|
+
{
|
|
424
|
+
description:
|
|
425
|
+
"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.",
|
|
426
|
+
inputSchema: {
|
|
427
|
+
preflight: z
|
|
428
|
+
.boolean()
|
|
429
|
+
.optional()
|
|
430
|
+
.describe(
|
|
431
|
+
"Include the environment preflight check before app package scripts. Defaults to false.",
|
|
432
|
+
),
|
|
433
|
+
timeoutMs: z
|
|
434
|
+
.number()
|
|
435
|
+
.int()
|
|
436
|
+
.min(1_000)
|
|
437
|
+
.max(60 * 60 * 1_000)
|
|
438
|
+
.optional()
|
|
439
|
+
.describe(
|
|
440
|
+
"Maximum duration for each app package script in milliseconds. Defaults to 600000 (10 minutes).",
|
|
441
|
+
),
|
|
442
|
+
},
|
|
443
|
+
annotations: {
|
|
444
|
+
readOnlyHint: false,
|
|
445
|
+
destructiveHint: true,
|
|
446
|
+
idempotentHint: false,
|
|
447
|
+
openWorldHint: true,
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
async (input, extra) =>
|
|
451
|
+
safeToolResult(async () =>
|
|
452
|
+
jsonResult(
|
|
453
|
+
await checkApp({
|
|
454
|
+
cwd,
|
|
455
|
+
preflight: input.preflight ?? false,
|
|
456
|
+
scriptTimeoutMs: input.timeoutMs ?? defaultMcpCheckTimeoutMs,
|
|
457
|
+
signal: extra.signal,
|
|
458
|
+
}),
|
|
459
|
+
),
|
|
460
|
+
),
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
server.registerTool(
|
|
464
|
+
"db",
|
|
465
|
+
{
|
|
466
|
+
description:
|
|
467
|
+
"Run one app-owned database lifecycle command and return exactly the versioned beignet db <command> --json result. Supports generate, migrate, seed, and reset; captures a bounded output tail, stops after the configured timeout, and cancels the complete child process tree with the MCP request. Dry runs validate prerequisites and report the command without executing it.",
|
|
468
|
+
inputSchema: {
|
|
469
|
+
command: z
|
|
470
|
+
.enum(databaseCommandChoices)
|
|
471
|
+
.describe("Database lifecycle command to run."),
|
|
472
|
+
dryRun: z
|
|
473
|
+
.boolean()
|
|
474
|
+
.optional()
|
|
475
|
+
.describe(
|
|
476
|
+
"Validate prerequisites and report the app-owned script without executing it. This does not simulate SQL or data changes.",
|
|
477
|
+
),
|
|
478
|
+
timeoutMs: z
|
|
479
|
+
.number()
|
|
480
|
+
.int()
|
|
481
|
+
.min(1_000)
|
|
482
|
+
.max(60 * 60 * 1_000)
|
|
483
|
+
.optional()
|
|
484
|
+
.describe(
|
|
485
|
+
"Maximum command duration in milliseconds. Defaults to 600000 (10 minutes).",
|
|
486
|
+
),
|
|
487
|
+
},
|
|
488
|
+
annotations: {
|
|
489
|
+
readOnlyHint: false,
|
|
490
|
+
destructiveHint: true,
|
|
491
|
+
idempotentHint: false,
|
|
492
|
+
openWorldHint: true,
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
async (input, extra) =>
|
|
496
|
+
safeToolResult(async () =>
|
|
497
|
+
jsonResult(
|
|
498
|
+
await runDatabaseCommand({
|
|
499
|
+
command: input.command,
|
|
500
|
+
cwd,
|
|
501
|
+
captureOutput: true,
|
|
502
|
+
maxOutputBytes: defaultDatabaseCommandMaxOutputBytes,
|
|
503
|
+
signal: extra.signal,
|
|
504
|
+
timeoutMs: input.timeoutMs ?? defaultDatabaseCommandTimeoutMs,
|
|
505
|
+
dryRun: input.dryRun,
|
|
506
|
+
}),
|
|
507
|
+
),
|
|
508
|
+
),
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
server.registerTool(
|
|
512
|
+
"db_schema_sync",
|
|
513
|
+
{
|
|
514
|
+
description:
|
|
515
|
+
"Idempotently sync app-owned Drizzle schema re-exports for Beignet provider tables and return exactly the versioned beignet db schema sync --json result. Supports dry-run planning and never generates or applies SQL migrations.",
|
|
516
|
+
inputSchema: {
|
|
517
|
+
dialect: z
|
|
518
|
+
.enum(databaseSchemaDialectChoices)
|
|
519
|
+
.optional()
|
|
520
|
+
.describe(
|
|
521
|
+
"Drizzle dialect. Inferred from registered providers or drizzle.config.* when omitted.",
|
|
522
|
+
),
|
|
523
|
+
tables: z
|
|
524
|
+
.array(z.enum(databaseSchemaTableChoices))
|
|
525
|
+
.min(1)
|
|
526
|
+
.optional()
|
|
527
|
+
.describe(
|
|
528
|
+
"Beignet provider tables to re-export. Defaults to audit, idempotency, and outbox.",
|
|
529
|
+
),
|
|
530
|
+
output: z
|
|
531
|
+
.string()
|
|
532
|
+
.min(1)
|
|
533
|
+
.optional()
|
|
534
|
+
.describe(
|
|
535
|
+
"App-relative schema output. Defaults to infra/db/schema/beignet.ts.",
|
|
536
|
+
),
|
|
537
|
+
dryRun: z
|
|
538
|
+
.boolean()
|
|
539
|
+
.optional()
|
|
540
|
+
.describe("Preview schema source changes without writing files."),
|
|
541
|
+
},
|
|
542
|
+
annotations: {
|
|
543
|
+
readOnlyHint: false,
|
|
544
|
+
destructiveHint: true,
|
|
545
|
+
idempotentHint: true,
|
|
546
|
+
openWorldHint: false,
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
async (input, extra) =>
|
|
550
|
+
safeToolResult(async () =>
|
|
551
|
+
jsonResult(
|
|
552
|
+
await syncDatabaseSchema({
|
|
553
|
+
cwd,
|
|
554
|
+
dialect: input.dialect,
|
|
555
|
+
tables: input.tables,
|
|
556
|
+
output: input.output,
|
|
557
|
+
dryRun: input.dryRun,
|
|
558
|
+
signal: extra.signal,
|
|
559
|
+
}),
|
|
560
|
+
),
|
|
561
|
+
),
|
|
562
|
+
);
|
|
563
|
+
|
|
338
564
|
server.registerTool(
|
|
339
565
|
"routes",
|
|
340
566
|
{
|
|
@@ -363,7 +589,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
363
589
|
"doctor",
|
|
364
590
|
{
|
|
365
591
|
description:
|
|
366
|
-
"Check app wiring and Beignet conventions, returning diagnostics
|
|
592
|
+
"Check app wiring and Beignet conventions, returning diagnostics and the detected convention as JSON. Strict mode (the default) is the CI bar and includes CI-oriented warnings. Read-only; use doctor_fix_plan before a guarded doctor_fix repair.",
|
|
367
593
|
inputSchema: {
|
|
368
594
|
strict: z
|
|
369
595
|
.boolean()
|
|
@@ -383,28 +609,82 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
383
609
|
}),
|
|
384
610
|
);
|
|
385
611
|
|
|
612
|
+
server.registerTool(
|
|
613
|
+
"doctor_fix_plan",
|
|
614
|
+
{
|
|
615
|
+
description:
|
|
616
|
+
"Plan every currently available low-risk doctor repair without changing files. Returns stable operation IDs, exact unified patches, file hashes, and a plan ID for guarded doctor_fix application.",
|
|
617
|
+
inputSchema: {
|
|
618
|
+
strict: z
|
|
619
|
+
.boolean()
|
|
620
|
+
.optional()
|
|
621
|
+
.describe("Include CI-oriented warnings. Defaults to true."),
|
|
622
|
+
},
|
|
623
|
+
annotations: {
|
|
624
|
+
readOnlyHint: true,
|
|
625
|
+
destructiveHint: false,
|
|
626
|
+
idempotentHint: true,
|
|
627
|
+
openWorldHint: false,
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
async (input) =>
|
|
631
|
+
safeToolResult(async () => {
|
|
632
|
+
return jsonResult(
|
|
633
|
+
await planDoctorFixes({ cwd, strict: input.strict ?? true }),
|
|
634
|
+
);
|
|
635
|
+
}),
|
|
636
|
+
);
|
|
637
|
+
|
|
386
638
|
server.registerTool(
|
|
387
639
|
"doctor_fix",
|
|
388
640
|
{
|
|
389
641
|
description:
|
|
390
|
-
"Apply low-risk doctor
|
|
642
|
+
"Apply low-risk doctor repairs, then re-check the app. With no planId, preserves the legacy apply-all behavior. Pass the planId returned by doctor_fix_plan for stale-file protection, optionally with fixIds to select repair operations.",
|
|
391
643
|
inputSchema: {
|
|
392
644
|
strict: z
|
|
393
645
|
.boolean()
|
|
394
646
|
.optional()
|
|
395
647
|
.describe("Include CI-oriented warnings. Defaults to true."),
|
|
648
|
+
planId: z
|
|
649
|
+
.string()
|
|
650
|
+
.min(1)
|
|
651
|
+
.optional()
|
|
652
|
+
.describe("Plan ID returned by doctor_fix_plan."),
|
|
653
|
+
fixIds: z
|
|
654
|
+
.array(z.enum(doctorFixOperationIds))
|
|
655
|
+
.min(1)
|
|
656
|
+
.optional()
|
|
657
|
+
.describe("Repair operation IDs to apply. Requires planId."),
|
|
658
|
+
},
|
|
659
|
+
annotations: {
|
|
660
|
+
readOnlyHint: false,
|
|
661
|
+
destructiveHint: true,
|
|
662
|
+
idempotentHint: true,
|
|
663
|
+
openWorldHint: false,
|
|
396
664
|
},
|
|
397
665
|
},
|
|
398
666
|
async (input) =>
|
|
399
667
|
safeToolResult(async () => {
|
|
400
668
|
const strict = input.strict ?? true;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
669
|
+
if (input.fixIds && !input.planId) {
|
|
670
|
+
throw new Error(
|
|
671
|
+
"doctor_fix fixIds require planId from doctor_fix_plan.",
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
const applied = input.planId
|
|
675
|
+
? await applyDoctorFixPlan({
|
|
676
|
+
cwd,
|
|
677
|
+
strict,
|
|
678
|
+
planId: input.planId,
|
|
679
|
+
...(input.fixIds ? { fixIds: input.fixIds } : {}),
|
|
680
|
+
})
|
|
681
|
+
: await applyDoctorFixesWithResult({ cwd, strict });
|
|
682
|
+
return jsonResult(
|
|
683
|
+
createDoctorFixInspectionResult(
|
|
684
|
+
await inspectApp({ cwd, strict }),
|
|
685
|
+
applied,
|
|
686
|
+
),
|
|
687
|
+
);
|
|
408
688
|
}),
|
|
409
689
|
);
|
|
410
690
|
|
package/src/preflight.ts
CHANGED
|
@@ -245,6 +245,20 @@ export async function runPreflight(
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
const inngestDev = env.INNGEST_DEV?.trim().toLowerCase();
|
|
249
|
+
if (
|
|
250
|
+
env.NODE_ENV === "production" &&
|
|
251
|
+
inngestDev !== undefined &&
|
|
252
|
+
!["", "0", "false"].includes(inngestDev)
|
|
253
|
+
) {
|
|
254
|
+
findings.push({
|
|
255
|
+
severity: "error",
|
|
256
|
+
code: "BEIGNET_PREFLIGHT_INNGEST_DEV_IN_PRODUCTION",
|
|
257
|
+
message:
|
|
258
|
+
"INNGEST_DEV enables the local Inngest dev server and must be disabled in production.",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
248
262
|
const exampleSource = await readFileIfExists(
|
|
249
263
|
path.join(options.cwd, ".env.example"),
|
|
250
264
|
);
|