create-better-t-stack 3.40.3 → 3.40.5
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 +1 -1
- package/dist/cli.mjs +10 -10
- package/dist/index.d.mts +5 -2
- package/dist/index.mjs +1 -1
- package/dist/{src-CP4utAOe.mjs → src-E3yd8mxg.mjs} +143 -42
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Follow the prompts to configure your project or use the `--yes` flag for default
|
|
|
30
30
|
## Requirements
|
|
31
31
|
|
|
32
32
|
- Node.js 22 or newer to run the CLI with Node.js. The selected framework may require a newer release; the CLI checks the exact stack before writing files.
|
|
33
|
-
- Bun 1.
|
|
33
|
+
- Bun 1.4 or newer is recommended. Bun 1.2.14 remains the minimum because generated workspaces use dependency catalogs.
|
|
34
34
|
- pnpm 10.26.0 or newer when using pnpm (catalogs and `allowBuilds`).
|
|
35
35
|
- npm 11.16.0 or newer when using npm (`allowScripts`).
|
|
36
36
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as types_exports, y as getLatestCLIVersion } from "./src-
|
|
2
|
+
import { i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as types_exports, y as getLatestCLIVersion } from "./src-E3yd8mxg.mjs";
|
|
3
3
|
import z from "zod";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -79,7 +79,7 @@ function getStackGuidance() {
|
|
|
79
79
|
"For project creation, build a full explicit config before calling bts_plan_project.",
|
|
80
80
|
"Always call bts_plan_project before bts_create_project.",
|
|
81
81
|
"Only call bts_create_project after the plan succeeds and matches the user's intent.",
|
|
82
|
-
"Use bts_plan_addons before bts_add_addons
|
|
82
|
+
"Use bts_plan_addons before bts_add_addons when adding addons or scaffolding a workspace package in an existing project."
|
|
83
83
|
],
|
|
84
84
|
createContract: {
|
|
85
85
|
requiresExplicitFields: [
|
|
@@ -224,12 +224,12 @@ function createBtsMcpServer() {
|
|
|
224
224
|
}
|
|
225
225
|
});
|
|
226
226
|
server.registerTool("bts_plan_addons", {
|
|
227
|
-
title: "Plan Better T Stack
|
|
228
|
-
description: "Validate and preview addon installation for an existing Better T Stack project without writing files. Always use this before bts_add_addons
|
|
227
|
+
title: "Plan Better T Stack Project Additions",
|
|
228
|
+
description: "Validate and preview addon installation or workspace package scaffolding for an existing Better T Stack project without writing files. Always use this before bts_add_addons.",
|
|
229
229
|
inputSchema: types_exports.AddInputSchema,
|
|
230
230
|
outputSchema: ToolResponseSchema,
|
|
231
231
|
annotations: {
|
|
232
|
-
title: "Plan Better T Stack
|
|
232
|
+
title: "Plan Better T Stack Project Additions",
|
|
233
233
|
readOnlyHint: true,
|
|
234
234
|
destructiveHint: false,
|
|
235
235
|
idempotentHint: true,
|
|
@@ -241,19 +241,19 @@ function createBtsMcpServer() {
|
|
|
241
241
|
...input,
|
|
242
242
|
dryRun: true
|
|
243
243
|
});
|
|
244
|
-
if (!result?.success) return formatToolError(result?.error ?? "Failed to plan
|
|
244
|
+
if (!result?.success) return formatToolError(result?.error ?? "Failed to plan project additions");
|
|
245
245
|
return formatToolSuccess(result);
|
|
246
246
|
} catch (error) {
|
|
247
247
|
return formatToolError(error);
|
|
248
248
|
}
|
|
249
249
|
});
|
|
250
250
|
server.registerTool("bts_add_addons", {
|
|
251
|
-
title: "
|
|
252
|
-
description: "Install addons
|
|
251
|
+
title: "Apply Better T Stack Project Additions",
|
|
252
|
+
description: "Install addons or scaffold a workspace package in an existing Better T Stack project using the same silent flow as add-json. Call this only after bts_plan_addons succeeds and the planned changes match the user's intent.",
|
|
253
253
|
inputSchema: types_exports.AddInputSchema,
|
|
254
254
|
outputSchema: ToolResponseSchema,
|
|
255
255
|
annotations: {
|
|
256
|
-
title: "
|
|
256
|
+
title: "Apply Better T Stack Project Additions",
|
|
257
257
|
destructiveHint: true,
|
|
258
258
|
idempotentHint: false,
|
|
259
259
|
openWorldHint: true
|
|
@@ -261,7 +261,7 @@ function createBtsMcpServer() {
|
|
|
261
261
|
}, async (input) => {
|
|
262
262
|
try {
|
|
263
263
|
const result = await add(input);
|
|
264
|
-
if (!result?.success) return formatToolError(result?.error ?? "Failed to
|
|
264
|
+
if (!result?.success) return formatToolError(result?.error ?? "Failed to update project");
|
|
265
265
|
return formatToolSuccess(result);
|
|
266
266
|
} catch (error) {
|
|
267
267
|
return formatToolError(error);
|
package/dist/index.d.mts
CHANGED
|
@@ -153,6 +153,7 @@ interface AddResult {
|
|
|
153
153
|
projectDir: string;
|
|
154
154
|
dryRun?: boolean;
|
|
155
155
|
plannedFileCount?: number;
|
|
156
|
+
addedPackage?: string;
|
|
156
157
|
error?: string;
|
|
157
158
|
}
|
|
158
159
|
//#endregion
|
|
@@ -349,6 +350,7 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
349
350
|
add: import("@trpc/server").TRPCMutationProcedure<{
|
|
350
351
|
input: {
|
|
351
352
|
addons?: ("none" | "pwa" | "tauri" | "electrobun" | "starlight" | "biome" | "lefthook" | "husky" | "mcp" | "turborepo" | "nx" | "vite-plus" | "fumadocs" | "ultracite" | "oxlint" | "opentui" | "wxt" | "skills" | "evlog")[] | undefined;
|
|
353
|
+
package?: string | undefined;
|
|
352
354
|
install?: boolean | undefined;
|
|
353
355
|
packageManager?: "bun" | "npm" | "pnpm" | undefined;
|
|
354
356
|
projectDir?: string | undefined;
|
|
@@ -360,6 +362,7 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
360
362
|
addJson: import("@trpc/server").TRPCMutationProcedure<{
|
|
361
363
|
input: {
|
|
362
364
|
addons?: ("none" | "pwa" | "tauri" | "electrobun" | "starlight" | "biome" | "lefthook" | "husky" | "mcp" | "turborepo" | "nx" | "vite-plus" | "fumadocs" | "ultracite" | "oxlint" | "opentui" | "wxt" | "skills" | "evlog")[] | undefined;
|
|
365
|
+
package?: string | undefined;
|
|
363
366
|
addonOptions?: {
|
|
364
367
|
wxt?: {
|
|
365
368
|
template: "svelte" | "solid" | "vanilla" | "vue" | "react";
|
|
@@ -473,9 +476,9 @@ declare function builder(): Promise<void>;
|
|
|
473
476
|
* ```
|
|
474
477
|
*/
|
|
475
478
|
declare function createVirtual(options: Partial<Omit<types_d_exports.ProjectConfig, "projectDir" | "relativePath">>): Promise<Result$1<VirtualFileTree$1, GeneratorError$1>>;
|
|
476
|
-
type AddOptions = Pick<types_d_exports.AddInput, "addons" | "addonOptions" | "install" | "packageManager" | "projectDir" | "dryRun">;
|
|
479
|
+
type AddOptions = Pick<types_d_exports.AddInput, "addons" | "addonOptions" | "package" | "install" | "packageManager" | "projectDir" | "dryRun">;
|
|
477
480
|
/**
|
|
478
|
-
* Programmatic API to add addons to an existing Better-T-Stack project.
|
|
481
|
+
* Programmatic API to add addons or a workspace package to an existing Better-T-Stack project.
|
|
479
482
|
*
|
|
480
483
|
* @example
|
|
481
484
|
* ```typescript
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as DirectoryConflictError, E as ValidationError, S as DatabaseSetupError, T as UserCancelledError, _ as ProjectLauncherSchema, a as TEMPLATE_COUNT, b as CLIError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as ProjectCreationError, x as CompatibilityError } from "./src-
|
|
2
|
+
import { C as DirectoryConflictError, E as ValidationError, S as DatabaseSetupError, T as UserCancelledError, _ as ProjectLauncherSchema, a as TEMPLATE_COUNT, b as CLIError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as ProjectCreationError, x as CompatibilityError } from "./src-E3yd8mxg.mjs";
|
|
3
3
|
export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, ProjectLauncherSchema, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
|
|
@@ -1843,8 +1843,8 @@ function getLocalToolRecommendations(config, versions) {
|
|
|
1843
1843
|
const rawVersion = versions.bun;
|
|
1844
1844
|
if (!rawVersion) return [];
|
|
1845
1845
|
const version = normalizeVersion(rawVersion);
|
|
1846
|
-
if (!version || !satisfies(version, PACKAGE_MANAGER_VERSION_RANGES.bun, { includePrerelease: true }) || satisfies(version, ">=1.
|
|
1847
|
-
return [`Bun ${version} meets the minimum requirement, but Bun 1.
|
|
1846
|
+
if (!version || !satisfies(version, PACKAGE_MANAGER_VERSION_RANGES.bun, { includePrerelease: true }) || satisfies(version, ">=1.4.0", { includePrerelease: true })) return [];
|
|
1847
|
+
return [`Bun ${version} meets the minimum requirement, but Bun 1.4 or newer is recommended. Run \`bun upgrade\`.`];
|
|
1848
1848
|
}
|
|
1849
1849
|
async function readToolVersion(tool) {
|
|
1850
1850
|
const result = await Result.tryPromise({
|
|
@@ -4653,6 +4653,68 @@ const TASK_RUNNER_ADDONS = [
|
|
|
4653
4653
|
"nx",
|
|
4654
4654
|
"vite-plus"
|
|
4655
4655
|
];
|
|
4656
|
+
const fileExistsErrorSchema = z.object({ code: z.literal("EEXIST") });
|
|
4657
|
+
const configPackageScopeSchema = z.object({ name: z.string().regex(/^@[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?\/config$/) }).transform(({ name }) => name.slice(0, -7));
|
|
4658
|
+
const rootTypescriptVersionSchema = z.object({ devDependencies: z.object({ typescript: z.string().min(1) }) }).transform(({ devDependencies }) => devDependencies.typescript);
|
|
4659
|
+
function isFileExistsError(cause) {
|
|
4660
|
+
return fileExistsErrorSchema.safeParse(cause).success;
|
|
4661
|
+
}
|
|
4662
|
+
async function reserveWorkspacePackage(projectDir, packageName) {
|
|
4663
|
+
const packageDir = path.join(projectDir, "packages", packageName);
|
|
4664
|
+
return Result.tryPromise({
|
|
4665
|
+
try: async () => {
|
|
4666
|
+
await fs.mkdir(packageDir);
|
|
4667
|
+
return packageDir;
|
|
4668
|
+
},
|
|
4669
|
+
catch: (cause) => new CLIError({
|
|
4670
|
+
message: isFileExistsError(cause) ? `Workspace package already exists: packages/${packageName}` : `Failed to reserve workspace package: packages/${packageName}`,
|
|
4671
|
+
cause
|
|
4672
|
+
})
|
|
4673
|
+
});
|
|
4674
|
+
}
|
|
4675
|
+
async function addWorkspacePackage(vfs, projectDir, packageName, packageManager) {
|
|
4676
|
+
const packageDir = path.join(projectDir, "packages", packageName);
|
|
4677
|
+
if (await fs.pathExists(packageDir)) return Result.err(new CLIError({ message: `Workspace package already exists: packages/${packageName}` }));
|
|
4678
|
+
const configPackagePath = path.join(projectDir, "packages", "config", "package.json");
|
|
4679
|
+
const packageScopeResult = await Result.tryPromise({
|
|
4680
|
+
try: async () => configPackageScopeSchema.parse(await fs.readJson(configPackagePath)),
|
|
4681
|
+
catch: (cause) => new CLIError({
|
|
4682
|
+
message: "Cannot determine the workspace package scope. Expected packages/config/package.json to have a name like @my-app/config.",
|
|
4683
|
+
cause
|
|
4684
|
+
})
|
|
4685
|
+
});
|
|
4686
|
+
if (packageScopeResult.isErr()) return Result.err(packageScopeResult.error);
|
|
4687
|
+
const typescriptVersionResult = await Result.tryPromise({
|
|
4688
|
+
try: async () => rootTypescriptVersionSchema.parse(await fs.readJson(path.join(projectDir, "package.json"))),
|
|
4689
|
+
catch: (cause) => new CLIError({
|
|
4690
|
+
message: "Cannot determine the TypeScript version. Expected package.json to declare devDependencies.typescript.",
|
|
4691
|
+
cause
|
|
4692
|
+
})
|
|
4693
|
+
});
|
|
4694
|
+
if (typescriptVersionResult.isErr()) return Result.err(typescriptVersionResult.error);
|
|
4695
|
+
const packageScope = packageScopeResult.value;
|
|
4696
|
+
const fullPackageName = `${packageScope}/${packageName}`;
|
|
4697
|
+
if (fullPackageName.length > 214) return Result.err(new CLIError({ message: "Workspace package name must not exceed 214 characters including its scope." }));
|
|
4698
|
+
const packagePath = `packages/${packageName}`;
|
|
4699
|
+
vfs.writeFile(`${packagePath}/package.json`, `${JSON.stringify({
|
|
4700
|
+
name: fullPackageName,
|
|
4701
|
+
version: "0.0.0",
|
|
4702
|
+
private: true,
|
|
4703
|
+
type: "module",
|
|
4704
|
+
exports: { ".": "./src/index.ts" },
|
|
4705
|
+
scripts: { "check-types": "tsc --noEmit" },
|
|
4706
|
+
devDependencies: {
|
|
4707
|
+
[`${packageScope}/config`]: packageManager === "npm" ? "*" : "workspace:*",
|
|
4708
|
+
typescript: typescriptVersionResult.value
|
|
4709
|
+
}
|
|
4710
|
+
}, null, 2)}\n`);
|
|
4711
|
+
vfs.writeFile(`${packagePath}/tsconfig.json`, `${JSON.stringify({
|
|
4712
|
+
extends: `${packageScope}/config/tsconfig.base.json`,
|
|
4713
|
+
include: ["src/**/*.ts"]
|
|
4714
|
+
}, null, 2)}\n`);
|
|
4715
|
+
vfs.writeFile(`${packagePath}/src/index.ts`, "export {};\n");
|
|
4716
|
+
return Result.ok(void 0);
|
|
4717
|
+
}
|
|
4656
4718
|
function mergeAddonOptions(existingAddonOptions, nextAddonOptions) {
|
|
4657
4719
|
if (!existingAddonOptions && !nextAddonOptions) return;
|
|
4658
4720
|
const mergeOption = (existing, next) => {
|
|
@@ -4738,7 +4800,7 @@ async function addHandlerInternal(input) {
|
|
|
4738
4800
|
let addonsToAdd;
|
|
4739
4801
|
if (input.addons && input.addons.length > 0) {
|
|
4740
4802
|
addonsToAdd = input.addons.filter((addon) => addon !== "none" && !existingConfig.addons.includes(addon));
|
|
4741
|
-
if (addonsToAdd.length === 0) {
|
|
4803
|
+
if (addonsToAdd.length === 0 && !input.package) {
|
|
4742
4804
|
if (!isSilent()) {
|
|
4743
4805
|
log.warn(pc.yellow("Nothing to add — those addons are already installed"));
|
|
4744
4806
|
outro(pc.dim("Project unchanged"));
|
|
@@ -4749,7 +4811,8 @@ async function addHandlerInternal(input) {
|
|
|
4749
4811
|
projectDir
|
|
4750
4812
|
});
|
|
4751
4813
|
}
|
|
4752
|
-
} else if (
|
|
4814
|
+
} else if (input.package) addonsToAdd = [];
|
|
4815
|
+
else if (isSilent()) return Result.err(new CLIError({ message: "Addons or a package are required in silent mode." }));
|
|
4753
4816
|
else {
|
|
4754
4817
|
const promptResult = await Result.tryPromise({
|
|
4755
4818
|
try: () => getAddonsToAdd(existingConfig),
|
|
@@ -4776,7 +4839,10 @@ async function addHandlerInternal(input) {
|
|
|
4776
4839
|
const updatedAddons = [...existingConfig.addons, ...addonsToAdd];
|
|
4777
4840
|
const addonsValidationResult = validateAddonsAgainstConfig(updatedAddons, existingConfig);
|
|
4778
4841
|
if (addonsValidationResult.isErr()) return Result.err(new CLIError({ message: addonsValidationResult.error.message }));
|
|
4779
|
-
if (!isSilent())
|
|
4842
|
+
if (!isSilent()) {
|
|
4843
|
+
const additions = [addonsToAdd.length > 0 ? formatConfigValue(addonsToAdd) : void 0, input.package ? `package ${input.package}` : void 0].filter(Boolean);
|
|
4844
|
+
log.info(`${pc.dim("Adding")} ${pc.cyan(additions.join(" and "))}`);
|
|
4845
|
+
}
|
|
4780
4846
|
const mergedAddonOptions = mergeAddonOptions(existingConfig.addonOptions, input.addonOptions);
|
|
4781
4847
|
const config = {
|
|
4782
4848
|
projectName: existingConfig.projectName,
|
|
@@ -4804,33 +4870,41 @@ async function addHandlerInternal(input) {
|
|
|
4804
4870
|
...config,
|
|
4805
4871
|
addons: updatedAddons
|
|
4806
4872
|
};
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4873
|
+
if (addonsToAdd.length > 0) {
|
|
4874
|
+
const requirementsResult = await checkLocalRequirements(updatedConfig);
|
|
4875
|
+
if (requirementsResult.isErr()) return Result.err(requirementsResult.error);
|
|
4876
|
+
if (!isSilent()) for (const warning of requirementsResult.value.warnings) log.warn(pc.yellow(warning));
|
|
4877
|
+
}
|
|
4878
|
+
if (!isSilent()) log.info(pc.dim("Preparing files…"));
|
|
4811
4879
|
const vfs = new VirtualFileSystem();
|
|
4812
|
-
|
|
4813
|
-
const
|
|
4814
|
-
if (
|
|
4815
|
-
|
|
4816
|
-
|
|
4880
|
+
if (input.package) {
|
|
4881
|
+
const packageResult = await addWorkspacePackage(vfs, projectDir, input.package, existingConfig.packageManager);
|
|
4882
|
+
if (packageResult.isErr()) return Result.err(packageResult.error);
|
|
4883
|
+
}
|
|
4884
|
+
if (addonsToAdd.length > 0) {
|
|
4885
|
+
for (const pkgPath of ADD_PACKAGE_JSON_PATHS) {
|
|
4886
|
+
const fullPath = path.join(projectDir, pkgPath);
|
|
4887
|
+
if (await fs.pathExists(fullPath)) {
|
|
4888
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
4889
|
+
vfs.writeFile(pkgPath, content);
|
|
4890
|
+
}
|
|
4817
4891
|
}
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4892
|
+
for (const filePath of ADD_TEXT_FILE_PATHS) {
|
|
4893
|
+
const fullPath = path.join(projectDir, filePath);
|
|
4894
|
+
if (await fs.pathExists(fullPath)) {
|
|
4895
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
4896
|
+
vfs.writeFile(filePath, content);
|
|
4897
|
+
}
|
|
4824
4898
|
}
|
|
4899
|
+
await processAddonTemplates(vfs, EMBEDDED_TEMPLATES, config);
|
|
4900
|
+
processAddonsDeps(vfs, config);
|
|
4901
|
+
if (addonsToAdd.includes("turborepo")) processTurboConfig(vfs, updatedConfig);
|
|
4902
|
+
if (addonsToAdd.includes("nx")) processNxConfig(vfs, updatedConfig);
|
|
4903
|
+
if (addonsToAdd.includes("vite-plus")) processVitePlusConfig(vfs, updatedConfig);
|
|
4904
|
+
if (updatedAddons.some((addon) => TASK_RUNNER_ADDONS.includes(addon))) processPackageConfigs(vfs, updatedConfig);
|
|
4905
|
+
if (updatedAddons.includes("vite-plus")) updateViteConfigImportsForVitePlus(vfs);
|
|
4906
|
+
if (shouldRefreshLefthook(addonsToAdd, updatedAddons)) refreshLefthookTemplate(vfs, updatedConfig);
|
|
4825
4907
|
}
|
|
4826
|
-
await processAddonTemplates(vfs, EMBEDDED_TEMPLATES, config);
|
|
4827
|
-
processAddonsDeps(vfs, config);
|
|
4828
|
-
if (addonsToAdd.includes("turborepo")) processTurboConfig(vfs, updatedConfig);
|
|
4829
|
-
if (addonsToAdd.includes("nx")) processNxConfig(vfs, updatedConfig);
|
|
4830
|
-
if (addonsToAdd.includes("vite-plus")) processVitePlusConfig(vfs, updatedConfig);
|
|
4831
|
-
if (updatedAddons.some((addon) => TASK_RUNNER_ADDONS.includes(addon))) processPackageConfigs(vfs, updatedConfig);
|
|
4832
|
-
if (updatedAddons.includes("vite-plus")) updateViteConfigImportsForVitePlus(vfs);
|
|
4833
|
-
if (shouldRefreshLefthook(addonsToAdd, updatedAddons)) refreshLefthookTemplate(vfs, updatedConfig);
|
|
4834
4908
|
const tree = {
|
|
4835
4909
|
root: vfs.toTree(""),
|
|
4836
4910
|
fileCount: vfs.getFileCount(),
|
|
@@ -4840,7 +4914,7 @@ async function addHandlerInternal(input) {
|
|
|
4840
4914
|
if (input.dryRun) {
|
|
4841
4915
|
if (!isSilent()) {
|
|
4842
4916
|
log.success(pc.green("Dry run passed · no files written"));
|
|
4843
|
-
log.message(pc.dim(`${vfs.getFileCount()}
|
|
4917
|
+
log.message(pc.dim(`${vfs.getFileCount()} files planned`));
|
|
4844
4918
|
outro(pc.dim("Project unchanged"));
|
|
4845
4919
|
}
|
|
4846
4920
|
return Result.ok({
|
|
@@ -4848,17 +4922,41 @@ async function addHandlerInternal(input) {
|
|
|
4848
4922
|
addedAddons: addonsToAdd,
|
|
4849
4923
|
projectDir,
|
|
4850
4924
|
dryRun: true,
|
|
4851
|
-
plannedFileCount: vfs.getFileCount()
|
|
4925
|
+
plannedFileCount: vfs.getFileCount(),
|
|
4926
|
+
addedPackage: input.package
|
|
4852
4927
|
});
|
|
4853
4928
|
}
|
|
4929
|
+
let reservedPackageDir;
|
|
4930
|
+
if (input.package) {
|
|
4931
|
+
const reservationResult = await reserveWorkspacePackage(projectDir, input.package);
|
|
4932
|
+
if (reservationResult.isErr()) return Result.err(reservationResult.error);
|
|
4933
|
+
reservedPackageDir = reservationResult.value;
|
|
4934
|
+
}
|
|
4854
4935
|
const writeResult = await writeTree(tree, projectDir);
|
|
4855
|
-
if (writeResult.isErr())
|
|
4856
|
-
|
|
4936
|
+
if (writeResult.isErr()) {
|
|
4937
|
+
if (reservedPackageDir && input.package) {
|
|
4938
|
+
const cleanupResult = await Result.tryPromise({
|
|
4939
|
+
try: () => fs.remove(reservedPackageDir),
|
|
4940
|
+
catch: (cause) => new CLIError({
|
|
4941
|
+
message: `Failed to clean up incomplete workspace package: packages/${input.package}`,
|
|
4942
|
+
cause
|
|
4943
|
+
})
|
|
4944
|
+
});
|
|
4945
|
+
if (cleanupResult.isErr()) return Result.err(new CLIError({
|
|
4946
|
+
message: `Failed to write files: ${writeResult.error.message}. ${cleanupResult.error.message}`,
|
|
4947
|
+
cause: cleanupResult.error
|
|
4948
|
+
}));
|
|
4949
|
+
}
|
|
4950
|
+
return Result.err(new CLIError({ message: `Failed to write files: ${writeResult.error.message}` }));
|
|
4951
|
+
}
|
|
4952
|
+
if (vfs.getFileCount() > 0 && !isSilent()) log.info(pc.dim(`Wrote ${vfs.getFileCount()} files`));
|
|
4857
4953
|
const setupResult = await Result.tryPromise({
|
|
4858
|
-
try: () =>
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4954
|
+
try: async () => {
|
|
4955
|
+
if (addonsToAdd.length > 0) await setupAddons({
|
|
4956
|
+
...config,
|
|
4957
|
+
addons: getSetupAddons(addonsToAdd, updatedAddons)
|
|
4958
|
+
});
|
|
4959
|
+
},
|
|
4862
4960
|
catch: (cause) => {
|
|
4863
4961
|
if (UserCancelledError.is(cause)) return cause;
|
|
4864
4962
|
return new CLIError({
|
|
@@ -4868,7 +4966,7 @@ async function addHandlerInternal(input) {
|
|
|
4868
4966
|
}
|
|
4869
4967
|
});
|
|
4870
4968
|
if (setupResult.isErr()) return Result.err(setupResult.error);
|
|
4871
|
-
await updateBtsConfig(projectDir, {
|
|
4969
|
+
if (addonsToAdd.length > 0) await updateBtsConfig(projectDir, {
|
|
4872
4970
|
addons: updatedAddons,
|
|
4873
4971
|
addonOptions: updatedConfig.addonOptions
|
|
4874
4972
|
});
|
|
@@ -4877,7 +4975,8 @@ async function addHandlerInternal(input) {
|
|
|
4877
4975
|
packageManager: config.packageManager
|
|
4878
4976
|
});
|
|
4879
4977
|
if (!isSilent()) {
|
|
4880
|
-
|
|
4978
|
+
const additions = [addonsToAdd.length > 0 ? formatConfigValue(addonsToAdd) : void 0, input.package ? `package ${input.package}` : void 0].filter(Boolean);
|
|
4979
|
+
log.success(pc.green(`Added ${additions.join(" and ")}`));
|
|
4881
4980
|
if (!input.install) {
|
|
4882
4981
|
const installCommand = config.packageManager === "npm" ? "npm install" : `${config.packageManager} install`;
|
|
4883
4982
|
log.message(`${pc.dim("Next step")}\n${pc.cyan(installCommand)}`);
|
|
@@ -4888,7 +4987,8 @@ async function addHandlerInternal(input) {
|
|
|
4888
4987
|
success: true,
|
|
4889
4988
|
addedAddons: addonsToAdd,
|
|
4890
4989
|
projectDir,
|
|
4891
|
-
plannedFileCount: vfs.getFileCount()
|
|
4990
|
+
plannedFileCount: vfs.getFileCount(),
|
|
4991
|
+
addedPackage: input.package
|
|
4892
4992
|
});
|
|
4893
4993
|
}
|
|
4894
4994
|
//#endregion
|
|
@@ -9168,8 +9268,9 @@ const router = t.router({
|
|
|
9168
9268
|
sponsors: t.procedure.meta({ description: "Show Better-T-Stack sponsors" }).mutation(() => showSponsorsCommand()),
|
|
9169
9269
|
docs: t.procedure.meta({ description: "Open Better-T-Stack documentation" }).mutation(() => openDocsCommand()),
|
|
9170
9270
|
builder: t.procedure.meta({ description: "Open the web-based stack builder" }).mutation(() => openBuilderCommand()),
|
|
9171
|
-
add: t.procedure.meta({ description: "Add addons to an existing Better-T-Stack project" }).input(z.object({
|
|
9271
|
+
add: t.procedure.meta({ description: "Add addons or a workspace package to an existing Better-T-Stack project" }).input(z.object({
|
|
9172
9272
|
addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
|
|
9273
|
+
package: types_exports.WorkspacePackageNameSchema.optional(),
|
|
9173
9274
|
install: z.boolean().optional().default(false).describe("Install dependencies after adding"),
|
|
9174
9275
|
packageManager: types_exports.PackageManagerSchema.optional().describe("Package manager to use"),
|
|
9175
9276
|
projectDir: z.string().optional().describe("Project directory (defaults to current)"),
|
|
@@ -9178,7 +9279,7 @@ const router = t.router({
|
|
|
9178
9279
|
await addHandler(input);
|
|
9179
9280
|
}),
|
|
9180
9281
|
addJson: t.procedure.meta({
|
|
9181
|
-
description: "Add addons from a raw JSON payload (agent-friendly)",
|
|
9282
|
+
description: "Add addons or a workspace package from a raw JSON payload (agent-friendly)",
|
|
9182
9283
|
jsonInput: "always"
|
|
9183
9284
|
}).input(types_exports.AddInputSchema).mutation(async ({ input }) => {
|
|
9184
9285
|
const result = await addHandler(input, { silent: true });
|
|
@@ -9342,7 +9443,7 @@ async function createVirtual(options) {
|
|
|
9342
9443
|
});
|
|
9343
9444
|
}
|
|
9344
9445
|
/**
|
|
9345
|
-
* Programmatic API to add addons to an existing Better-T-Stack project.
|
|
9446
|
+
* Programmatic API to add addons or a workspace package to an existing Better-T-Stack project.
|
|
9346
9447
|
*
|
|
9347
9448
|
* @example
|
|
9348
9449
|
* ```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.40.
|
|
3
|
+
"version": "3.40.5",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"prepublishOnly": "npm run build"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@better-t-stack/template-generator": "^3.40.
|
|
73
|
-
"@better-t-stack/types": "^3.40.
|
|
72
|
+
"@better-t-stack/template-generator": "^3.40.5",
|
|
73
|
+
"@better-t-stack/types": "^3.40.5",
|
|
74
74
|
"@clack/core": "^1.4.3",
|
|
75
75
|
"@clack/prompts": "^1.7.0",
|
|
76
76
|
"@modelcontextprotocol/sdk": "1.30.0",
|