create-better-fullstack 1.7.1 → 1.8.1
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/README.md +2 -2
- package/dist/addons-setup-CJwQAWFg.mjs +5 -0
- package/dist/{addons-setup-CGhYT2qC.mjs → addons-setup-CUmA_nra.mjs} +5 -5
- package/dist/{bts-config-bOXo9tbL.mjs → bts-config-YcroedMK.mjs} +45 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +258 -30
- package/dist/index.mjs +1084 -54
- package/dist/{mcp-BxEilSGM.mjs → mcp-DoPutOIG.mjs} +1 -1
- package/dist/mcp-entry.mjs +194 -41
- package/package.json +9 -9
- package/dist/addons-setup-BdKQJ1h6.mjs +0 -5
package/dist/mcp-entry.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as getLatestCLIVersion, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-
|
|
2
|
+
import { i as getLatestCLIVersion, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-YcroedMK.mjs";
|
|
3
3
|
import z from "zod";
|
|
4
|
-
import { AISchema, APISchema, AddonsSchema, AnalyticsSchema, AnimationSchema, AstroIntegrationSchema, AuthSchema, BackendSchema, CMSSchema, CSSFrameworkSchema, CachingSchema, DatabaseSchema, DatabaseSetupSchema, EcosystemSchema, EffectSchema, EmailSchema, ExamplesSchema, FeatureFlagsSchema, FileStorageSchema, FileUploadSchema, FormsSchema, FrontendSchema, GoApiSchema, GoAuthSchema, GoCliSchema, GoLoggingSchema, GoOrmSchema, GoWebFrameworkSchema, I18nSchema, JavaAuthSchema, JavaBuildToolSchema, JavaLibrariesSchema, JavaOrmSchema, JavaTestingLibrariesSchema, JavaWebFrameworkSchema, JobQueueSchema, LoggingSchema, OPTION_CATEGORY_METADATA, ORMSchema, ObservabilitySchema, PackageManagerSchema, PaymentsSchema, PythonAiSchema, PythonApiSchema, PythonAuthSchema, PythonGraphqlSchema, PythonOrmSchema, PythonQualitySchema, PythonTaskQueueSchema, PythonValidationSchema, PythonWebFrameworkSchema, RealtimeSchema, RuntimeSchema, RustApiSchema, RustAuthSchema, RustCachingSchema, RustCliSchema, RustErrorHandlingSchema, RustFrontendSchema, RustLibrariesSchema, RustLoggingSchema, RustOrmSchema, RustWebFrameworkSchema, SearchSchema, ServerDeploySchema, StateManagementSchema, TestingSchema, UILibrarySchema, ValidationSchema, WebDeploySchema, analyzeStackCompatibility } from "@better-fullstack/types";
|
|
4
|
+
import { AISchema, APISchema, AddonsSchema, AnalyticsSchema, AnimationSchema, AstroIntegrationSchema, AuthSchema, BackendSchema, CMSSchema, CSSFrameworkSchema, CachingSchema, DatabaseSchema, DatabaseSetupSchema, EcosystemSchema, EffectSchema, ElixirApiSchema, ElixirAuthSchema, ElixirCachingSchema, ElixirDeploySchema, ElixirEmailSchema, ElixirHttpSchema, ElixirJobsSchema, ElixirJsonSchema, ElixirObservabilitySchema, ElixirOrmSchema, ElixirQualitySchema, ElixirRealtimeSchema, ElixirTestingSchema, ElixirValidationSchema, ElixirWebFrameworkSchema, EmailSchema, ExamplesSchema, FeatureFlagsSchema, FileStorageSchema, FileUploadSchema, FormsSchema, FrontendSchema, GoApiSchema, GoAuthSchema, GoCliSchema, GoLoggingSchema, GoOrmSchema, GoWebFrameworkSchema, I18nSchema, JavaAuthSchema, JavaBuildToolSchema, JavaLibrariesSchema, JavaOrmSchema, JavaTestingLibrariesSchema, JavaWebFrameworkSchema, JobQueueSchema, LoggingSchema, MobileDeepLinkingSchema, MobileNavigationSchema, MobileOTASchema, MobilePushSchema, MobileStorageSchema, MobileTestingSchema, MobileUISchema, OPTION_CATEGORY_METADATA, ORMSchema, ObservabilitySchema, PackageManagerSchema, PaymentsSchema, PythonAiSchema, PythonApiSchema, PythonAuthSchema, PythonGraphqlSchema, PythonOrmSchema, PythonQualitySchema, PythonTaskQueueSchema, PythonValidationSchema, PythonWebFrameworkSchema, RealtimeSchema, RuntimeSchema, RustApiSchema, RustAuthSchema, RustCachingSchema, RustCliSchema, RustErrorHandlingSchema, RustFrontendSchema, RustLibrariesSchema, RustLoggingSchema, RustOrmSchema, RustWebFrameworkSchema, SearchSchema, ServerDeploySchema, StateManagementSchema, TestingSchema, UILibrarySchema, ValidationSchema, WebDeploySchema, analyzeStackCompatibility } from "@better-fullstack/types";
|
|
5
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
|
|
8
8
|
//#region src/mcp.ts
|
|
9
|
-
const INSTRUCTIONS = `Better-Fullstack scaffolds fullstack projects across TypeScript, Rust, Go, Python, and
|
|
9
|
+
const INSTRUCTIONS = `Better-Fullstack scaffolds fullstack projects across TypeScript, React Native, Rust, Go, Python, Java, and Elixir ecosystems with ${Object.values(OPTION_CATEGORY_METADATA).reduce((sum, metadata) => sum + metadata.options.length, 0)} configurable options.
|
|
10
10
|
|
|
11
11
|
RECOMMENDED WORKFLOW:
|
|
12
12
|
1. Call bfs_get_guidance to understand field semantics, required fields, and workflow rules.
|
|
@@ -24,7 +24,7 @@ CRITICAL RULES:
|
|
|
24
24
|
- Array fields: "frontend", "addons", "examples", "aiDocs", "rustLibraries", "pythonAi", "javaLibraries", and "javaTestingLibraries". Most other option fields are strings.
|
|
25
25
|
- "none" means "skip this feature entirely", not "use the default".
|
|
26
26
|
- Always specify "ecosystem" first — it determines which other fields are relevant.
|
|
27
|
-
- TypeScript-specific fields (frontend, backend, orm, etc.) are IGNORED for rust/python/go/java ecosystems.
|
|
27
|
+
- TypeScript web-specific fields (web frontend, backend, orm, etc.) are IGNORED for react-native/rust/python/go/java/elixir ecosystems.
|
|
28
28
|
- The compatibility engine auto-adjusts invalid combinations — always call bfs_check_compatibility first to see adjustments.`;
|
|
29
29
|
function getGuidance() {
|
|
30
30
|
return {
|
|
@@ -37,18 +37,20 @@ function getGuidance() {
|
|
|
37
37
|
"For existing projects: call bfs_plan_addition, then bfs_add_feature."
|
|
38
38
|
],
|
|
39
39
|
ecosystems: {
|
|
40
|
-
typescript: "Full-featured: frontend + backend + database + ORM + auth + payments + 20+ feature categories.",
|
|
40
|
+
typescript: "Full-featured web: frontend + backend + database + ORM + auth + payments + 20+ feature categories.",
|
|
41
|
+
"react-native": "Mobile: Expo/React Native frontend variants plus mobile navigation, UI, storage, testing, push, OTA, and deep linking.",
|
|
41
42
|
rust: "Backend/CLI: web framework (axum/actix-web), ORM (sea-orm/sqlx), gRPC, GraphQL, CLI tools.",
|
|
42
43
|
python: "Backend/AI: web framework (fastapi/django), ORM (sqlalchemy/sqlmodel), AI/ML integrations, task queues.",
|
|
43
44
|
go: "Backend/CLI: web framework (gin/echo), ORM (gorm/sqlc), gRPC, CLI tools, logging.",
|
|
44
|
-
java: "Backend/API: Spring Boot with Maven or Gradle Wrapper, optional Spring Data JPA, Spring Security, app libraries, and Java testing libraries."
|
|
45
|
+
java: "Backend/API: Spring Boot with Maven or Gradle Wrapper, optional Spring Data JPA, Spring Security, app libraries, and Java testing libraries.",
|
|
46
|
+
elixir: "Phoenix: Phoenix or Phoenix LiveView with Ecto SQL, PostgreSQL-ready config, REST or Absinthe, Channels/Presence, Oban, and Mix releases/Docker."
|
|
45
47
|
},
|
|
46
48
|
fieldRules: {
|
|
47
49
|
projectName: "kebab-case directory name. Required for bfs_create_project.",
|
|
48
50
|
ecosystem: "Must be set first. Determines which other fields are relevant.",
|
|
49
51
|
frontend: "ARRAY of strings. TypeScript only. Supports multiple frontends in one monorepo. Use [] for API-only.",
|
|
50
52
|
arrayFields: "Use arrays for frontend, addons, examples, aiDocs, rustLibraries, pythonAi, javaLibraries, and javaTestingLibraries. Use [] for \"none\" on multi-select fields.",
|
|
51
|
-
backend: "String. \"self\" means fullstack mode (Next.js/TanStack Start/Nuxt/Astro API routes). \"none\" for frontend-only.",
|
|
53
|
+
backend: "String. \"self\" means fullstack mode (Next.js/Vinext/TanStack Start/Nuxt/Astro API routes). \"none\" for frontend-only.",
|
|
52
54
|
runtime: "\"bun\" or \"node\". Must be \"none\" when backend is \"self\" or \"convex\".",
|
|
53
55
|
addons: "ARRAY of strings. Monorepo tools, code quality, desktop (tauri), browser extensions (wxt), etc.",
|
|
54
56
|
email: "String. TypeScript supports multiple providers; Rust, Python, Go, and Java currently support resend or none.",
|
|
@@ -58,17 +60,17 @@ function getGuidance() {
|
|
|
58
60
|
ambiguityRules: [
|
|
59
61
|
"If the user request leaves major stack choices unspecified, ASK the user before proceeding. Do not guess.",
|
|
60
62
|
"Do not infer addons, examples, or optional features the user did not mention. Default strings to \"none\" and multi-select arrays to [].",
|
|
61
|
-
"When the user says 'fullstack Next.js', use backend='self', frontend=['next'], runtime='none'.",
|
|
63
|
+
"When the user says 'fullstack Next.js', use backend='self', frontend=['next'], runtime='none'.\nWhen the user says 'fullstack Vinext', use backend='self', frontend=['vinext'], runtime='none'.",
|
|
62
64
|
"When the user says 'React + Hono', use frontend=['tanstack-router'] (or ask which React framework), backend='hono'."
|
|
63
65
|
],
|
|
64
66
|
criticalConstraints: [
|
|
65
|
-
"tRPC (api='trpc') only works with React-based frontends: next, react-router, tanstack-router, tanstack-start.",
|
|
67
|
+
"tRPC (api='trpc') only works with React-based frontends: next, vinext, react-router, tanstack-router, tanstack-start.",
|
|
66
68
|
"Use api='orpc' for svelte, solid, nuxt.",
|
|
67
69
|
"Angular: use api='none' (has built-in HttpClient).",
|
|
68
70
|
"Qwik: use backend='none', api='none' (built-in server).",
|
|
69
71
|
"NestJS and AdonisJS backends require runtime='node'.",
|
|
70
72
|
"Elysia backend requires runtime='bun'.",
|
|
71
|
-
"backend='self' only works with: next, tanstack-start, astro, nuxt, svelte, solid-start.",
|
|
73
|
+
"backend='self' only works with: next, vinext, tanstack-start, astro, nuxt, svelte, solid-start.",
|
|
72
74
|
"backend='convex' overrides: runtime=none, database=none, orm=none, api=none.",
|
|
73
75
|
"TypeORM + better-auth: unsupported (no adapter). Use auth='none' or orm='drizzle'.",
|
|
74
76
|
"Sequelize + better-auth: unsupported (no adapter). Use auth='none' or orm='drizzle'.",
|
|
@@ -112,6 +114,13 @@ const SCHEMA_MAP = {
|
|
|
112
114
|
i18n: I18nSchema,
|
|
113
115
|
search: SearchSchema,
|
|
114
116
|
fileStorage: FileStorageSchema,
|
|
117
|
+
mobileNavigation: MobileNavigationSchema,
|
|
118
|
+
mobileUI: MobileUISchema,
|
|
119
|
+
mobileStorage: MobileStorageSchema,
|
|
120
|
+
mobileTesting: MobileTestingSchema,
|
|
121
|
+
mobilePush: MobilePushSchema,
|
|
122
|
+
mobileOTA: MobileOTASchema,
|
|
123
|
+
mobileDeepLinking: MobileDeepLinkingSchema,
|
|
115
124
|
addons: AddonsSchema,
|
|
116
125
|
examples: ExamplesSchema,
|
|
117
126
|
packageManager: PackageManagerSchema,
|
|
@@ -149,7 +158,22 @@ const SCHEMA_MAP = {
|
|
|
149
158
|
javaOrm: JavaOrmSchema,
|
|
150
159
|
javaAuth: JavaAuthSchema,
|
|
151
160
|
javaLibraries: JavaLibrariesSchema,
|
|
152
|
-
javaTestingLibraries: JavaTestingLibrariesSchema
|
|
161
|
+
javaTestingLibraries: JavaTestingLibrariesSchema,
|
|
162
|
+
elixirWebFramework: ElixirWebFrameworkSchema,
|
|
163
|
+
elixirOrm: ElixirOrmSchema,
|
|
164
|
+
elixirAuth: ElixirAuthSchema,
|
|
165
|
+
elixirApi: ElixirApiSchema,
|
|
166
|
+
elixirRealtime: ElixirRealtimeSchema,
|
|
167
|
+
elixirJobs: ElixirJobsSchema,
|
|
168
|
+
elixirValidation: ElixirValidationSchema,
|
|
169
|
+
elixirHttp: ElixirHttpSchema,
|
|
170
|
+
elixirJson: ElixirJsonSchema,
|
|
171
|
+
elixirEmail: ElixirEmailSchema,
|
|
172
|
+
elixirCaching: ElixirCachingSchema,
|
|
173
|
+
elixirObservability: ElixirObservabilitySchema,
|
|
174
|
+
elixirTesting: ElixirTestingSchema,
|
|
175
|
+
elixirQuality: ElixirQualitySchema,
|
|
176
|
+
elixirDeploy: ElixirDeploySchema
|
|
153
177
|
};
|
|
154
178
|
const ECOSYSTEM_CATEGORIES = {
|
|
155
179
|
typescript: [
|
|
@@ -185,6 +209,17 @@ const ECOSYSTEM_CATEGORIES = {
|
|
|
185
209
|
"fileStorage",
|
|
186
210
|
"astroIntegration"
|
|
187
211
|
],
|
|
212
|
+
"react-native": [
|
|
213
|
+
"frontend",
|
|
214
|
+
"auth",
|
|
215
|
+
"mobileNavigation",
|
|
216
|
+
"mobileUI",
|
|
217
|
+
"mobileStorage",
|
|
218
|
+
"mobileTesting",
|
|
219
|
+
"mobilePush",
|
|
220
|
+
"mobileOTA",
|
|
221
|
+
"mobileDeepLinking"
|
|
222
|
+
],
|
|
188
223
|
rust: [
|
|
189
224
|
"rustWebFramework",
|
|
190
225
|
"rustFrontend",
|
|
@@ -241,6 +276,23 @@ const ECOSYSTEM_CATEGORIES = {
|
|
|
241
276
|
"caching",
|
|
242
277
|
"search"
|
|
243
278
|
],
|
|
279
|
+
elixir: [
|
|
280
|
+
"elixirWebFramework",
|
|
281
|
+
"elixirOrm",
|
|
282
|
+
"elixirAuth",
|
|
283
|
+
"elixirApi",
|
|
284
|
+
"elixirRealtime",
|
|
285
|
+
"elixirJobs",
|
|
286
|
+
"elixirValidation",
|
|
287
|
+
"elixirHttp",
|
|
288
|
+
"elixirJson",
|
|
289
|
+
"elixirEmail",
|
|
290
|
+
"elixirCaching",
|
|
291
|
+
"elixirObservability",
|
|
292
|
+
"elixirTesting",
|
|
293
|
+
"elixirQuality",
|
|
294
|
+
"elixirDeploy"
|
|
295
|
+
],
|
|
244
296
|
shared: [
|
|
245
297
|
"ecosystem",
|
|
246
298
|
"packageManager",
|
|
@@ -277,12 +329,16 @@ function getInstallCommand(ecosystem, projectName, packageManager, javaBuildTool
|
|
|
277
329
|
case "rust": return `cd ${projectName} && cargo build`;
|
|
278
330
|
case "python": return `cd ${projectName} && uv sync`;
|
|
279
331
|
case "go": return `cd ${projectName} && go mod tidy`;
|
|
332
|
+
case "elixir": return `cd ${projectName} && mix deps.get && mix compile && mix test`;
|
|
280
333
|
case "java":
|
|
281
334
|
if (javaWebFramework === "quarkus") return javaBuildTool === "gradle" ? `cd ${projectName} && ./gradlew test && ./gradlew quarkusDev` : `cd ${projectName} && ./mvnw test && ./mvnw quarkus:dev`;
|
|
282
335
|
return javaBuildTool === "gradle" ? `cd ${projectName} && ./gradlew test && ./gradlew bootRun` : `cd ${projectName} && ./mvnw test && ./mvnw spring-boot:run`;
|
|
283
336
|
default: return `cd ${projectName} && ${packageManager ?? "bun"} install`;
|
|
284
337
|
}
|
|
285
338
|
}
|
|
339
|
+
function mcpInputSchema(schema) {
|
|
340
|
+
return schema;
|
|
341
|
+
}
|
|
286
342
|
function filterCompatibilityResult(result, ecosystem) {
|
|
287
343
|
const { adjustedStack, changes } = result;
|
|
288
344
|
if (!adjustedStack) return {
|
|
@@ -306,14 +362,18 @@ function filterCompatibilityResult(result, ecosystem) {
|
|
|
306
362
|
}
|
|
307
363
|
function buildProjectConfig(input, overrides) {
|
|
308
364
|
const projectName = input.projectName ?? "my-project";
|
|
365
|
+
const ecosystem = input.ecosystem ?? "typescript";
|
|
366
|
+
const frontend = input.frontend ?? (ecosystem === "react-native" ? ["native-bare"] : ["tanstack-router"]);
|
|
367
|
+
const hasNativeFrontend = frontend.some((item) => item.startsWith("native-"));
|
|
368
|
+
const hasMobileProject = ecosystem === "react-native" || hasNativeFrontend;
|
|
309
369
|
return {
|
|
310
370
|
projectName,
|
|
311
371
|
projectDir: overrides?.projectDir ?? "/virtual",
|
|
312
372
|
relativePath: overrides ? `./${projectName}` : "./virtual",
|
|
313
|
-
ecosystem
|
|
314
|
-
frontend
|
|
315
|
-
backend: input.backend ?? "hono",
|
|
316
|
-
runtime: input.runtime ?? "bun",
|
|
373
|
+
ecosystem,
|
|
374
|
+
frontend,
|
|
375
|
+
backend: input.backend ?? (ecosystem === "react-native" ? "none" : "hono"),
|
|
376
|
+
runtime: input.runtime ?? (ecosystem === "react-native" ? "none" : "bun"),
|
|
317
377
|
database: input.database ?? "none",
|
|
318
378
|
orm: input.orm ?? "none",
|
|
319
379
|
api: input.api ?? "none",
|
|
@@ -321,13 +381,13 @@ function buildProjectConfig(input, overrides) {
|
|
|
321
381
|
payments: input.payments ?? "none",
|
|
322
382
|
email: input.email ?? "none",
|
|
323
383
|
fileUpload: input.fileUpload ?? "none",
|
|
324
|
-
effect: "none",
|
|
384
|
+
effect: input.effect ?? "none",
|
|
325
385
|
ai: input.ai ?? "none",
|
|
326
386
|
stateManagement: input.stateManagement ?? "none",
|
|
327
387
|
forms: input.forms ?? "none",
|
|
328
388
|
validation: input.validation ?? "none",
|
|
329
389
|
testing: input.testing ?? "none",
|
|
330
|
-
cssFramework: input.cssFramework ?? "tailwind",
|
|
390
|
+
cssFramework: input.cssFramework ?? (ecosystem === "react-native" ? "none" : "tailwind"),
|
|
331
391
|
uiLibrary: input.uiLibrary ?? "none",
|
|
332
392
|
shadcnBase: "radix",
|
|
333
393
|
shadcnStyle: "nova",
|
|
@@ -342,7 +402,14 @@ function buildProjectConfig(input, overrides) {
|
|
|
342
402
|
logging: input.logging ?? "none",
|
|
343
403
|
observability: input.observability ?? "none",
|
|
344
404
|
featureFlags: input.featureFlags ?? "none",
|
|
345
|
-
analytics: "none",
|
|
405
|
+
analytics: input.analytics ?? "none",
|
|
406
|
+
mobileNavigation: input.mobileNavigation ?? (hasMobileProject ? "expo-router" : "none"),
|
|
407
|
+
mobileUI: input.mobileUI ?? "none",
|
|
408
|
+
mobileStorage: input.mobileStorage ?? "none",
|
|
409
|
+
mobileTesting: input.mobileTesting ?? "none",
|
|
410
|
+
mobilePush: input.mobilePush ?? "none",
|
|
411
|
+
mobileOTA: input.mobileOTA ?? "none",
|
|
412
|
+
mobileDeepLinking: input.mobileDeepLinking ?? (hasMobileProject ? "expo-linking" : "none"),
|
|
346
413
|
cms: input.cms ?? "none",
|
|
347
414
|
caching: input.caching ?? "none",
|
|
348
415
|
i18n: input.i18n ?? "none",
|
|
@@ -389,7 +456,22 @@ function buildProjectConfig(input, overrides) {
|
|
|
389
456
|
javaOrm: input.javaOrm ?? "none",
|
|
390
457
|
javaAuth: input.javaAuth ?? "none",
|
|
391
458
|
javaLibraries: input.javaLibraries ?? [],
|
|
392
|
-
javaTestingLibraries: input.javaTestingLibraries ?? ["junit5"]
|
|
459
|
+
javaTestingLibraries: input.javaTestingLibraries ?? ["junit5"],
|
|
460
|
+
elixirWebFramework: input.elixirWebFramework ?? "phoenix",
|
|
461
|
+
elixirOrm: input.elixirOrm ?? "ecto-sql",
|
|
462
|
+
elixirAuth: input.elixirAuth ?? "none",
|
|
463
|
+
elixirApi: input.elixirApi ?? "rest",
|
|
464
|
+
elixirRealtime: input.elixirRealtime ?? "channels",
|
|
465
|
+
elixirJobs: input.elixirJobs ?? "none",
|
|
466
|
+
elixirValidation: input.elixirValidation ?? "ecto-changesets",
|
|
467
|
+
elixirHttp: input.elixirHttp ?? "req",
|
|
468
|
+
elixirJson: input.elixirJson ?? "jason",
|
|
469
|
+
elixirEmail: input.elixirEmail ?? "none",
|
|
470
|
+
elixirCaching: input.elixirCaching ?? "none",
|
|
471
|
+
elixirObservability: input.elixirObservability ?? "telemetry",
|
|
472
|
+
elixirTesting: input.elixirTesting ?? "ex_unit",
|
|
473
|
+
elixirQuality: input.elixirQuality ?? "credo",
|
|
474
|
+
elixirDeploy: input.elixirDeploy ?? "none"
|
|
393
475
|
};
|
|
394
476
|
}
|
|
395
477
|
function sanitizePath(input) {
|
|
@@ -399,7 +481,11 @@ function sanitizePath(input) {
|
|
|
399
481
|
}
|
|
400
482
|
function buildCompatibilityInput(input) {
|
|
401
483
|
const frontend = input.frontend;
|
|
484
|
+
const webFrontend = (frontend ?? []).filter((item) => !item.startsWith("native-"));
|
|
485
|
+
const nativeFrontend = (frontend ?? []).filter((item) => item.startsWith("native-"));
|
|
402
486
|
const addons = input.addons ?? [];
|
|
487
|
+
const ecosystem = input.ecosystem ?? "typescript";
|
|
488
|
+
const hasMobileProject = ecosystem === "react-native" || nativeFrontend.length > 0;
|
|
403
489
|
const codeQuality = addons.filter((a) => [
|
|
404
490
|
"biome",
|
|
405
491
|
"oxlint",
|
|
@@ -415,10 +501,10 @@ function buildCompatibilityInput(input) {
|
|
|
415
501
|
"none"
|
|
416
502
|
].includes(a));
|
|
417
503
|
return {
|
|
418
|
-
ecosystem
|
|
504
|
+
ecosystem,
|
|
419
505
|
projectName: input.projectName ?? null,
|
|
420
|
-
webFrontend
|
|
421
|
-
nativeFrontend
|
|
506
|
+
webFrontend,
|
|
507
|
+
nativeFrontend,
|
|
422
508
|
astroIntegration: input.astroIntegration ?? "none",
|
|
423
509
|
runtime: input.runtime ?? "bun",
|
|
424
510
|
backend: input.backend ?? "hono",
|
|
@@ -455,6 +541,13 @@ function buildCompatibilityInput(input) {
|
|
|
455
541
|
cms: input.cms ?? "none",
|
|
456
542
|
search: input.search ?? "none",
|
|
457
543
|
fileStorage: input.fileStorage ?? "none",
|
|
544
|
+
mobileNavigation: input.mobileNavigation ?? (hasMobileProject ? "expo-router" : "none"),
|
|
545
|
+
mobileUI: input.mobileUI ?? "none",
|
|
546
|
+
mobileStorage: input.mobileStorage ?? "none",
|
|
547
|
+
mobileTesting: input.mobileTesting ?? "none",
|
|
548
|
+
mobilePush: input.mobilePush ?? "none",
|
|
549
|
+
mobileOTA: input.mobileOTA ?? "none",
|
|
550
|
+
mobileDeepLinking: input.mobileDeepLinking ?? (hasMobileProject ? "expo-linking" : "none"),
|
|
458
551
|
codeQuality,
|
|
459
552
|
documentation,
|
|
460
553
|
appPlatforms,
|
|
@@ -499,7 +592,22 @@ function buildCompatibilityInput(input) {
|
|
|
499
592
|
javaOrm: input.javaOrm ?? "none",
|
|
500
593
|
javaAuth: input.javaAuth ?? "none",
|
|
501
594
|
javaLibraries: input.javaLibraries ?? [],
|
|
502
|
-
javaTestingLibraries: input.javaTestingLibraries ?? ["junit5"]
|
|
595
|
+
javaTestingLibraries: input.javaTestingLibraries ?? ["junit5"],
|
|
596
|
+
elixirWebFramework: input.elixirWebFramework ?? "phoenix",
|
|
597
|
+
elixirOrm: input.elixirOrm ?? "ecto-sql",
|
|
598
|
+
elixirAuth: input.elixirAuth ?? "none",
|
|
599
|
+
elixirApi: input.elixirApi ?? "rest",
|
|
600
|
+
elixirRealtime: input.elixirRealtime ?? "channels",
|
|
601
|
+
elixirJobs: input.elixirJobs ?? "none",
|
|
602
|
+
elixirValidation: input.elixirValidation ?? "ecto-changesets",
|
|
603
|
+
elixirHttp: input.elixirHttp ?? "req",
|
|
604
|
+
elixirJson: input.elixirJson ?? "jason",
|
|
605
|
+
elixirEmail: input.elixirEmail ?? "none",
|
|
606
|
+
elixirCaching: input.elixirCaching ?? "none",
|
|
607
|
+
elixirObservability: input.elixirObservability ?? "telemetry",
|
|
608
|
+
elixirTesting: input.elixirTesting ?? "ex_unit",
|
|
609
|
+
elixirQuality: input.elixirQuality ?? "credo",
|
|
610
|
+
elixirDeploy: input.elixirDeploy ?? "none"
|
|
503
611
|
};
|
|
504
612
|
}
|
|
505
613
|
function summarizeTree(tree) {
|
|
@@ -523,7 +631,7 @@ const COMPATIBILITY_RULES_MD = `# Better-Fullstack Compatibility Rules
|
|
|
523
631
|
## Backend Constraints
|
|
524
632
|
- **Convex**: Forces runtime=none, database=none, orm=none, api=none, dbSetup=none, serverDeploy=none. Removes incompatible frontends (Solid, SolidStart, Astro).
|
|
525
633
|
- **No backend (none)**: Clears auth, payments, database, orm, api, serverDeploy, search, fileStorage.
|
|
526
|
-
- **Fullstack (backend='self')**: Sets runtime=none, serverDeploy=none. Only works with: next, tanstack-start, astro, nuxt, svelte, solid-start.
|
|
634
|
+
- **Fullstack (backend='self')**: Sets runtime=none, serverDeploy=none. Only works with: next, vinext, tanstack-start, astro, nuxt, svelte, solid-start.
|
|
527
635
|
|
|
528
636
|
## Runtime Constraints
|
|
529
637
|
- NestJS and AdonisJS require runtime=node.
|
|
@@ -532,7 +640,7 @@ const COMPATIBILITY_RULES_MD = `# Better-Fullstack Compatibility Rules
|
|
|
532
640
|
- backend=self or backend=convex requires runtime=none.
|
|
533
641
|
|
|
534
642
|
## API Constraints
|
|
535
|
-
- tRPC only works with React-based frontends: next, react-router, tanstack-router, tanstack-start.
|
|
643
|
+
- tRPC only works with React-based frontends: next, vinext, react-router, tanstack-router, tanstack-start.
|
|
536
644
|
- Use oRPC for svelte, solid, nuxt.
|
|
537
645
|
- Angular: use api=none (has built-in HttpClient).
|
|
538
646
|
- Qwik: use backend=none, api=none (built-in server, no external APIs).
|
|
@@ -559,7 +667,7 @@ const COMPATIBILITY_RULES_MD = `# Better-Fullstack Compatibility Rules
|
|
|
559
667
|
- Java Sentry requires Maven or Gradle so the generated project can manage the SDK dependency.
|
|
560
668
|
|
|
561
669
|
## Ecosystem Isolation
|
|
562
|
-
- Rust, Python, Go, and
|
|
670
|
+
- Rust, Python, Go, Java, and Elixir ecosystems are independent — TypeScript fields are ignored.
|
|
563
671
|
- Each ecosystem generates a standalone project with its own build system.
|
|
564
672
|
`;
|
|
565
673
|
const GETTING_STARTED_MD = `# Getting Started with Better-Fullstack MCP
|
|
@@ -628,24 +736,25 @@ async function startMcpServer() {
|
|
|
628
736
|
instructions: INSTRUCTIONS,
|
|
629
737
|
capabilities: { logging: {} }
|
|
630
738
|
});
|
|
631
|
-
|
|
739
|
+
const registerTool = server.tool.bind(server);
|
|
740
|
+
registerTool("bfs_get_guidance", "Returns workflow rules, field semantics, ambiguity rules, and critical constraints. Call this FIRST before using other tools.", mcpInputSchema({}), async () => {
|
|
632
741
|
const guidance = getGuidance();
|
|
633
742
|
return { content: [{
|
|
634
743
|
type: "text",
|
|
635
744
|
text: JSON.stringify(guidance, null, 2)
|
|
636
745
|
}] };
|
|
637
746
|
});
|
|
638
|
-
|
|
747
|
+
registerTool("bfs_get_schema", "Returns valid options for a specific category (e.g., 'database', 'frontend', 'backend') or ALL categories. Use ecosystem to filter to relevant categories only.", mcpInputSchema({
|
|
639
748
|
category: z.string().optional().describe("Category name (e.g., 'database', 'orm', 'frontend'). Omit for all categories."),
|
|
640
749
|
ecosystem: EcosystemSchema.optional().describe("Filter categories to this ecosystem (e.g., 'rust' returns only Rust + shared categories).")
|
|
641
|
-
}, async ({ category, ecosystem }) => {
|
|
750
|
+
}), async ({ category, ecosystem }) => {
|
|
642
751
|
const result = getSchemaOptions(category, ecosystem);
|
|
643
752
|
return { content: [{
|
|
644
753
|
type: "text",
|
|
645
754
|
text: JSON.stringify(result, null, 2)
|
|
646
755
|
}] };
|
|
647
756
|
});
|
|
648
|
-
|
|
757
|
+
registerTool("bfs_check_compatibility", "Validates a stack combination and returns auto-adjusted selections with warnings. Call BEFORE creating a project to avoid invalid combinations.", mcpInputSchema({
|
|
649
758
|
ecosystem: EcosystemSchema.describe("Language ecosystem"),
|
|
650
759
|
frontend: z.array(z.string()).optional().describe("Web frontend frameworks (TypeScript only)"),
|
|
651
760
|
backend: z.string().optional().describe("Backend framework"),
|
|
@@ -674,6 +783,13 @@ async function startMcpServer() {
|
|
|
674
783
|
i18n: I18nSchema.optional().describe("Internationalization library"),
|
|
675
784
|
search: SearchSchema.optional().describe("Search engine"),
|
|
676
785
|
fileStorage: FileStorageSchema.optional().describe("File storage"),
|
|
786
|
+
mobileNavigation: MobileNavigationSchema.optional().describe("Mobile navigation"),
|
|
787
|
+
mobileUI: MobileUISchema.optional().describe("Mobile UI"),
|
|
788
|
+
mobileStorage: MobileStorageSchema.optional().describe("Mobile storage"),
|
|
789
|
+
mobileTesting: MobileTestingSchema.optional().describe("Mobile testing"),
|
|
790
|
+
mobilePush: MobilePushSchema.optional().describe("Mobile push notifications"),
|
|
791
|
+
mobileOTA: MobileOTASchema.optional().describe("Mobile OTA updates"),
|
|
792
|
+
mobileDeepLinking: MobileDeepLinkingSchema.optional().describe("Mobile deep linking"),
|
|
677
793
|
dbSetup: DatabaseSetupSchema.optional().describe("Database hosting provider"),
|
|
678
794
|
webDeploy: WebDeploySchema.optional().describe("Web deployment target"),
|
|
679
795
|
serverDeploy: ServerDeploySchema.optional().describe("Server deployment target"),
|
|
@@ -713,8 +829,23 @@ async function startMcpServer() {
|
|
|
713
829
|
javaOrm: JavaOrmSchema.optional().describe("Java ORM"),
|
|
714
830
|
javaAuth: JavaAuthSchema.optional().describe("Java authentication library"),
|
|
715
831
|
javaLibraries: z.array(JavaLibrariesSchema).optional().describe("Java application libraries"),
|
|
716
|
-
javaTestingLibraries: z.array(JavaTestingLibrariesSchema).optional().describe("Java testing libraries")
|
|
717
|
-
|
|
832
|
+
javaTestingLibraries: z.array(JavaTestingLibrariesSchema).optional().describe("Java testing libraries"),
|
|
833
|
+
elixirWebFramework: ElixirWebFrameworkSchema.optional().describe("Elixir web framework"),
|
|
834
|
+
elixirOrm: ElixirOrmSchema.optional().describe("Elixir persistence layer"),
|
|
835
|
+
elixirAuth: ElixirAuthSchema.optional().describe("Elixir authentication"),
|
|
836
|
+
elixirApi: ElixirApiSchema.optional().describe("Elixir API layer"),
|
|
837
|
+
elixirRealtime: ElixirRealtimeSchema.optional().describe("Elixir realtime feature"),
|
|
838
|
+
elixirJobs: ElixirJobsSchema.optional().describe("Elixir jobs and scheduling"),
|
|
839
|
+
elixirValidation: ElixirValidationSchema.optional().describe("Elixir validation/data"),
|
|
840
|
+
elixirHttp: ElixirHttpSchema.optional().describe("Elixir HTTP client"),
|
|
841
|
+
elixirJson: ElixirJsonSchema.optional().describe("Elixir JSON library"),
|
|
842
|
+
elixirEmail: ElixirEmailSchema.optional().describe("Elixir email library"),
|
|
843
|
+
elixirCaching: ElixirCachingSchema.optional().describe("Elixir caching library"),
|
|
844
|
+
elixirObservability: ElixirObservabilitySchema.optional().describe("Elixir observability"),
|
|
845
|
+
elixirTesting: ElixirTestingSchema.optional().describe("Elixir testing library"),
|
|
846
|
+
elixirQuality: ElixirQualitySchema.optional().describe("Elixir code quality/security"),
|
|
847
|
+
elixirDeploy: ElixirDeploySchema.optional().describe("Elixir deployment target")
|
|
848
|
+
}), async (input) => {
|
|
718
849
|
try {
|
|
719
850
|
const filtered = filterCompatibilityResult(analyzeStackCompatibility(buildCompatibilityInput(input)), input.ecosystem);
|
|
720
851
|
return { content: [{
|
|
@@ -764,6 +895,13 @@ async function startMcpServer() {
|
|
|
764
895
|
i18n: I18nSchema.optional().describe("Internationalization (i18n) library"),
|
|
765
896
|
cms: CMSSchema.optional().describe("CMS"),
|
|
766
897
|
fileStorage: FileStorageSchema.optional().describe("File storage"),
|
|
898
|
+
mobileNavigation: MobileNavigationSchema.optional().describe("Mobile navigation"),
|
|
899
|
+
mobileUI: MobileUISchema.optional().describe("Mobile UI"),
|
|
900
|
+
mobileStorage: MobileStorageSchema.optional().describe("Mobile storage"),
|
|
901
|
+
mobileTesting: MobileTestingSchema.optional().describe("Mobile testing"),
|
|
902
|
+
mobilePush: MobilePushSchema.optional().describe("Mobile push notifications"),
|
|
903
|
+
mobileOTA: MobileOTASchema.optional().describe("Mobile OTA updates"),
|
|
904
|
+
mobileDeepLinking: MobileDeepLinkingSchema.optional().describe("Mobile deep linking"),
|
|
767
905
|
fileUpload: FileUploadSchema.optional().describe("File upload"),
|
|
768
906
|
webDeploy: WebDeploySchema.optional().describe("Web deployment target"),
|
|
769
907
|
serverDeploy: ServerDeploySchema.optional().describe("Server deployment target"),
|
|
@@ -798,9 +936,24 @@ async function startMcpServer() {
|
|
|
798
936
|
javaOrm: JavaOrmSchema.optional().describe("Java ORM"),
|
|
799
937
|
javaAuth: JavaAuthSchema.optional().describe("Java authentication library"),
|
|
800
938
|
javaLibraries: z.array(JavaLibrariesSchema).optional().describe("Java application libraries"),
|
|
801
|
-
javaTestingLibraries: z.array(JavaTestingLibrariesSchema).optional().describe("Java testing libraries")
|
|
939
|
+
javaTestingLibraries: z.array(JavaTestingLibrariesSchema).optional().describe("Java testing libraries"),
|
|
940
|
+
elixirWebFramework: ElixirWebFrameworkSchema.optional().describe("Elixir web framework"),
|
|
941
|
+
elixirOrm: ElixirOrmSchema.optional().describe("Elixir persistence layer"),
|
|
942
|
+
elixirAuth: ElixirAuthSchema.optional().describe("Elixir authentication"),
|
|
943
|
+
elixirApi: ElixirApiSchema.optional().describe("Elixir API layer"),
|
|
944
|
+
elixirRealtime: ElixirRealtimeSchema.optional().describe("Elixir realtime feature"),
|
|
945
|
+
elixirJobs: ElixirJobsSchema.optional().describe("Elixir jobs and scheduling"),
|
|
946
|
+
elixirValidation: ElixirValidationSchema.optional().describe("Elixir validation/data"),
|
|
947
|
+
elixirHttp: ElixirHttpSchema.optional().describe("Elixir HTTP client"),
|
|
948
|
+
elixirJson: ElixirJsonSchema.optional().describe("Elixir JSON library"),
|
|
949
|
+
elixirEmail: ElixirEmailSchema.optional().describe("Elixir email library"),
|
|
950
|
+
elixirCaching: ElixirCachingSchema.optional().describe("Elixir caching library"),
|
|
951
|
+
elixirObservability: ElixirObservabilitySchema.optional().describe("Elixir observability"),
|
|
952
|
+
elixirTesting: ElixirTestingSchema.optional().describe("Elixir testing library"),
|
|
953
|
+
elixirQuality: ElixirQualitySchema.optional().describe("Elixir code quality/security"),
|
|
954
|
+
elixirDeploy: ElixirDeploySchema.optional().describe("Elixir deployment target")
|
|
802
955
|
};
|
|
803
|
-
|
|
956
|
+
registerTool("bfs_plan_project", "Dry-run: generates a project in-memory and returns the file tree WITHOUT writing to disk. Use this to preview what would be created.", mcpInputSchema(planCreateSchema), async (input) => {
|
|
804
957
|
try {
|
|
805
958
|
const { generateVirtualProject, EMBEDDED_TEMPLATES } = await import("@better-fullstack/template-generator");
|
|
806
959
|
const result = await generateVirtualProject({
|
|
@@ -837,10 +990,10 @@ async function startMcpServer() {
|
|
|
837
990
|
};
|
|
838
991
|
}
|
|
839
992
|
});
|
|
840
|
-
|
|
993
|
+
registerTool("bfs_create_project", "Creates a new fullstack project on disk. Dependencies are NOT installed (agent must tell user to install manually). Call bfs_plan_project first to preview.", mcpInputSchema({
|
|
841
994
|
...planCreateSchema,
|
|
842
995
|
projectName: z.string().describe("Project name (kebab-case). Will be the directory name.")
|
|
843
|
-
}, async (input) => {
|
|
996
|
+
}), async (input) => {
|
|
844
997
|
try {
|
|
845
998
|
const { generateVirtualProject, EMBEDDED_TEMPLATES } = await import("@better-fullstack/template-generator");
|
|
846
999
|
const { writeTreeToFilesystem } = await import("@better-fullstack/template-generator/fs-writer");
|
|
@@ -867,7 +1020,7 @@ async function startMcpServer() {
|
|
|
867
1020
|
await writeBtsConfig(config);
|
|
868
1021
|
let addonWarnings = [];
|
|
869
1022
|
if (config.addons.length > 0 && config.addons[0] !== "none") {
|
|
870
|
-
const { setupAddons } = await import("./addons-setup-
|
|
1023
|
+
const { setupAddons } = await import("./addons-setup-CJwQAWFg.mjs");
|
|
871
1024
|
addonWarnings = await setupAddons(config);
|
|
872
1025
|
}
|
|
873
1026
|
const installCmd = getInstallCommand(input.ecosystem ?? "typescript", projectName, input.packageManager, input.javaBuildTool, input.javaWebFramework);
|
|
@@ -891,12 +1044,12 @@ async function startMcpServer() {
|
|
|
891
1044
|
};
|
|
892
1045
|
}
|
|
893
1046
|
});
|
|
894
|
-
|
|
1047
|
+
registerTool("bfs_plan_addition", "Validates what would be added to an existing project. Reads the project config (bts.jsonc) and checks which addons are new.", mcpInputSchema({
|
|
895
1048
|
projectDir: z.string().describe("Absolute path to the existing project directory"),
|
|
896
1049
|
addons: z.array(AddonsSchema).optional().describe("Addons to add"),
|
|
897
1050
|
webDeploy: WebDeploySchema.optional().describe("Web deployment option"),
|
|
898
1051
|
serverDeploy: ServerDeploySchema.optional().describe("Server deployment option")
|
|
899
|
-
}, async ({ projectDir, addons, webDeploy, serverDeploy }) => {
|
|
1052
|
+
}), async ({ projectDir, addons, webDeploy, serverDeploy }) => {
|
|
900
1053
|
try {
|
|
901
1054
|
const safePath = sanitizePath(projectDir);
|
|
902
1055
|
const config = await readBtsConfig(safePath);
|
|
@@ -949,13 +1102,13 @@ async function startMcpServer() {
|
|
|
949
1102
|
};
|
|
950
1103
|
}
|
|
951
1104
|
});
|
|
952
|
-
|
|
1105
|
+
registerTool("bfs_add_feature", "Adds addons/features to an existing Better-Fullstack project. Dependencies are NOT installed. Call bfs_plan_addition first to validate.", mcpInputSchema({
|
|
953
1106
|
projectDir: z.string().describe("Absolute path to the existing project directory"),
|
|
954
1107
|
addons: z.array(AddonsSchema).optional().describe("Addons to add"),
|
|
955
1108
|
webDeploy: WebDeploySchema.optional().describe("Web deployment option"),
|
|
956
1109
|
serverDeploy: ServerDeploySchema.optional().describe("Server deployment option"),
|
|
957
1110
|
packageManager: PackageManagerSchema.optional().describe("Package manager to use")
|
|
958
|
-
}, async (input) => {
|
|
1111
|
+
}), async (input) => {
|
|
959
1112
|
try {
|
|
960
1113
|
const safePath = sanitizePath(input.projectDir);
|
|
961
1114
|
const { add } = await import("./index.mjs");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-fullstack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "Scaffold production-ready fullstack apps in seconds. Pick your stack from 425 options — the CLI wires everything together.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"algolia",
|
|
@@ -127,11 +127,11 @@
|
|
|
127
127
|
"prepublishOnly": "npm run build"
|
|
128
128
|
},
|
|
129
129
|
"dependencies": {
|
|
130
|
-
"@better-fullstack/template-generator": "^1.
|
|
131
|
-
"@better-fullstack/types": "^1.
|
|
130
|
+
"@better-fullstack/template-generator": "^1.8.1",
|
|
131
|
+
"@better-fullstack/types": "^1.8.1",
|
|
132
132
|
"@clack/core": "^0.5.0",
|
|
133
|
-
"@clack/prompts": "^1.
|
|
134
|
-
"@orpc/server": "^1.14.
|
|
133
|
+
"@clack/prompts": "^1.4.0",
|
|
134
|
+
"@orpc/server": "^1.14.3",
|
|
135
135
|
"consola": "^3.4.2",
|
|
136
136
|
"env-paths": "^4.0.0",
|
|
137
137
|
"execa": "^9.6.1",
|
|
@@ -146,13 +146,13 @@
|
|
|
146
146
|
"trpc-cli": "^0.12.1",
|
|
147
147
|
"ts-morph": "^27.0.2",
|
|
148
148
|
"yaml": "^2.9.0",
|
|
149
|
-
"zod": "4.3
|
|
149
|
+
"zod": "4.4.3"
|
|
150
150
|
},
|
|
151
151
|
"devDependencies": {
|
|
152
|
-
"@types/bun": "^1.3.
|
|
152
|
+
"@types/bun": "^1.3.14",
|
|
153
153
|
"@types/fs-extra": "^11.0.4",
|
|
154
|
-
"@types/node": "^25.
|
|
155
|
-
"publint": "^0.3.
|
|
154
|
+
"@types/node": "^25.9.1",
|
|
155
|
+
"publint": "^0.3.21",
|
|
156
156
|
"tsdown": "^0.18.2",
|
|
157
157
|
"typescript": "^5.9.3"
|
|
158
158
|
}
|