create-better-t-stack 3.23.1 → 3.25.3
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 +19 -18
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +222 -837
- package/dist/index.mjs +1 -1
- package/dist/{src-COTG6r9y.mjs → src-BsKaExN8.mjs} +204 -270
- package/dist/virtual.d.mts +1 -1
- package/package.json +4 -4
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as __reExport, t as __exportAll } from "./chunk-BtN16TXe.mjs";
|
|
3
3
|
import { getAllJsonSchemas } from "@better-t-stack/types/json-schema";
|
|
4
|
-
import {
|
|
4
|
+
import { initTRPC } from "@trpc/server";
|
|
5
5
|
import { Result, Result as Result$1, TaggedError } from "better-result";
|
|
6
6
|
import { createCli } from "trpc-cli";
|
|
7
7
|
import z from "zod";
|
|
8
|
-
import {
|
|
8
|
+
import { cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
9
9
|
import pc from "picocolors";
|
|
10
|
+
import path from "node:path";
|
|
10
11
|
import envPaths from "env-paths";
|
|
11
12
|
import fs from "fs-extra";
|
|
12
|
-
import path from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { desktopWebFrontends as desktopWebFrontends$3 } from "@better-t-stack/types";
|
|
14
15
|
import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
|
|
15
|
-
import
|
|
16
|
+
import { consola, createConsola } from "consola";
|
|
17
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
18
|
import gradient from "gradient-string";
|
|
17
19
|
import { $, execa } from "execa";
|
|
18
20
|
import { writeTree } from "@better-t-stack/template-generator/fs-writer";
|
|
19
21
|
import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
|
|
20
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
21
22
|
import { applyEdits, modify, parse } from "jsonc-parser";
|
|
22
|
-
import os
|
|
23
|
+
import os from "node:os";
|
|
23
24
|
import { format } from "oxfmt";
|
|
24
25
|
//#region src/utils/get-package-manager.ts
|
|
25
26
|
const getUserPkgManager = () => {
|
|
@@ -70,14 +71,8 @@ const ADDON_COMPATIBILITY = {
|
|
|
70
71
|
"solid",
|
|
71
72
|
"next"
|
|
72
73
|
],
|
|
73
|
-
tauri:
|
|
74
|
-
|
|
75
|
-
"react-router",
|
|
76
|
-
"nuxt",
|
|
77
|
-
"svelte",
|
|
78
|
-
"solid",
|
|
79
|
-
"next"
|
|
80
|
-
],
|
|
74
|
+
tauri: desktopWebFrontends$3,
|
|
75
|
+
electrobun: desktopWebFrontends$3,
|
|
81
76
|
biome: [],
|
|
82
77
|
husky: [],
|
|
83
78
|
lefthook: [],
|
|
@@ -85,7 +80,6 @@ const ADDON_COMPATIBILITY = {
|
|
|
85
80
|
nx: [],
|
|
86
81
|
starlight: [],
|
|
87
82
|
ultracite: [],
|
|
88
|
-
ruler: [],
|
|
89
83
|
mcp: [],
|
|
90
84
|
oxlint: [],
|
|
91
85
|
fumadocs: [],
|
|
@@ -95,6 +89,109 @@ const ADDON_COMPATIBILITY = {
|
|
|
95
89
|
none: []
|
|
96
90
|
};
|
|
97
91
|
//#endregion
|
|
92
|
+
//#region src/utils/context.ts
|
|
93
|
+
const cliStorage = new AsyncLocalStorage();
|
|
94
|
+
function defaultContext() {
|
|
95
|
+
return {
|
|
96
|
+
navigation: {
|
|
97
|
+
isFirstPrompt: false,
|
|
98
|
+
lastPromptShownUI: false
|
|
99
|
+
},
|
|
100
|
+
silent: false,
|
|
101
|
+
verbose: false
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function getContext() {
|
|
105
|
+
const ctx = cliStorage.getStore();
|
|
106
|
+
if (!ctx) return defaultContext();
|
|
107
|
+
return ctx;
|
|
108
|
+
}
|
|
109
|
+
function tryGetContext() {
|
|
110
|
+
return cliStorage.getStore();
|
|
111
|
+
}
|
|
112
|
+
function isSilent() {
|
|
113
|
+
return getContext().silent;
|
|
114
|
+
}
|
|
115
|
+
function isFirstPrompt() {
|
|
116
|
+
return getContext().navigation.isFirstPrompt;
|
|
117
|
+
}
|
|
118
|
+
function didLastPromptShowUI() {
|
|
119
|
+
return getContext().navigation.lastPromptShownUI;
|
|
120
|
+
}
|
|
121
|
+
function setIsFirstPrompt$1(value) {
|
|
122
|
+
const ctx = tryGetContext();
|
|
123
|
+
if (ctx) ctx.navigation.isFirstPrompt = value;
|
|
124
|
+
}
|
|
125
|
+
function setLastPromptShownUI(value) {
|
|
126
|
+
const ctx = tryGetContext();
|
|
127
|
+
if (ctx) ctx.navigation.lastPromptShownUI = value;
|
|
128
|
+
}
|
|
129
|
+
async function runWithContextAsync(options, fn) {
|
|
130
|
+
const ctx = {
|
|
131
|
+
navigation: {
|
|
132
|
+
isFirstPrompt: false,
|
|
133
|
+
lastPromptShownUI: false
|
|
134
|
+
},
|
|
135
|
+
silent: options.silent ?? false,
|
|
136
|
+
verbose: options.verbose ?? false,
|
|
137
|
+
projectDir: options.projectDir,
|
|
138
|
+
projectName: options.projectName,
|
|
139
|
+
packageManager: options.packageManager
|
|
140
|
+
};
|
|
141
|
+
return cliStorage.run(ctx, fn);
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/utils/terminal-output.ts
|
|
145
|
+
const noopSpinner = {
|
|
146
|
+
start() {},
|
|
147
|
+
stop() {},
|
|
148
|
+
message() {}
|
|
149
|
+
};
|
|
150
|
+
function createSpinner() {
|
|
151
|
+
return isSilent() ? noopSpinner : spinner();
|
|
152
|
+
}
|
|
153
|
+
const baseConsola = createConsola({
|
|
154
|
+
...consola.options,
|
|
155
|
+
formatOptions: {
|
|
156
|
+
...consola.options.formatOptions,
|
|
157
|
+
date: false
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
const cliLog = {
|
|
161
|
+
info(message) {
|
|
162
|
+
if (!isSilent()) log.info(message);
|
|
163
|
+
},
|
|
164
|
+
warn(message) {
|
|
165
|
+
if (!isSilent()) log.warn(message);
|
|
166
|
+
},
|
|
167
|
+
success(message) {
|
|
168
|
+
if (!isSilent()) log.success(message);
|
|
169
|
+
},
|
|
170
|
+
error(message) {
|
|
171
|
+
if (!isSilent()) log.error(message);
|
|
172
|
+
},
|
|
173
|
+
message(message) {
|
|
174
|
+
if (!isSilent()) log.message(message);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const cliConsola = {
|
|
178
|
+
error(message) {
|
|
179
|
+
if (!isSilent()) baseConsola.error(message);
|
|
180
|
+
},
|
|
181
|
+
warn(message) {
|
|
182
|
+
if (!isSilent()) baseConsola.warn(message);
|
|
183
|
+
},
|
|
184
|
+
info(message) {
|
|
185
|
+
if (!isSilent()) baseConsola.info(message);
|
|
186
|
+
},
|
|
187
|
+
fatal(message) {
|
|
188
|
+
if (!isSilent()) baseConsola.fatal(message);
|
|
189
|
+
},
|
|
190
|
+
box(message) {
|
|
191
|
+
if (!isSilent()) baseConsola.box(message);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
//#endregion
|
|
98
195
|
//#region src/utils/errors.ts
|
|
99
196
|
/**
|
|
100
197
|
* User cancelled the operation (e.g., Ctrl+C in prompts)
|
|
@@ -180,7 +277,7 @@ function databaseSetupError(provider, message, cause) {
|
|
|
180
277
|
*/
|
|
181
278
|
function displayError(error) {
|
|
182
279
|
if (UserCancelledError.is(error)) cancel(pc.red(error.message));
|
|
183
|
-
else
|
|
280
|
+
else cliConsola.error(pc.red(error.message));
|
|
184
281
|
}
|
|
185
282
|
//#endregion
|
|
186
283
|
//#region src/utils/get-latest-cli-version.ts
|
|
@@ -496,7 +593,7 @@ function displaySponsorsBox(sponsors) {
|
|
|
496
593
|
if (website) output += ` ${pc.dim("Website:")} ${website}\n`;
|
|
497
594
|
if (idx < sponsors.specialSponsors.length - 1) output += "\n";
|
|
498
595
|
});
|
|
499
|
-
|
|
596
|
+
cliConsola.box(output);
|
|
500
597
|
}
|
|
501
598
|
function formatPostInstallSpecialSponsorsSection(sponsors) {
|
|
502
599
|
if (sponsors.specialSponsors.length === 0) return "";
|
|
@@ -785,58 +882,6 @@ function validateExamplesCompatibility(examples, backend, database, frontend, ap
|
|
|
785
882
|
return Result.ok(void 0);
|
|
786
883
|
}
|
|
787
884
|
//#endregion
|
|
788
|
-
//#region src/utils/context.ts
|
|
789
|
-
const cliStorage = new AsyncLocalStorage();
|
|
790
|
-
function defaultContext() {
|
|
791
|
-
return {
|
|
792
|
-
navigation: {
|
|
793
|
-
isFirstPrompt: false,
|
|
794
|
-
lastPromptShownUI: false
|
|
795
|
-
},
|
|
796
|
-
silent: false,
|
|
797
|
-
verbose: false
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
function getContext() {
|
|
801
|
-
const ctx = cliStorage.getStore();
|
|
802
|
-
if (!ctx) return defaultContext();
|
|
803
|
-
return ctx;
|
|
804
|
-
}
|
|
805
|
-
function tryGetContext() {
|
|
806
|
-
return cliStorage.getStore();
|
|
807
|
-
}
|
|
808
|
-
function isSilent() {
|
|
809
|
-
return getContext().silent;
|
|
810
|
-
}
|
|
811
|
-
function isFirstPrompt() {
|
|
812
|
-
return getContext().navigation.isFirstPrompt;
|
|
813
|
-
}
|
|
814
|
-
function didLastPromptShowUI() {
|
|
815
|
-
return getContext().navigation.lastPromptShownUI;
|
|
816
|
-
}
|
|
817
|
-
function setIsFirstPrompt$1(value) {
|
|
818
|
-
const ctx = tryGetContext();
|
|
819
|
-
if (ctx) ctx.navigation.isFirstPrompt = value;
|
|
820
|
-
}
|
|
821
|
-
function setLastPromptShownUI(value) {
|
|
822
|
-
const ctx = tryGetContext();
|
|
823
|
-
if (ctx) ctx.navigation.lastPromptShownUI = value;
|
|
824
|
-
}
|
|
825
|
-
async function runWithContextAsync(options, fn) {
|
|
826
|
-
const ctx = {
|
|
827
|
-
navigation: {
|
|
828
|
-
isFirstPrompt: false,
|
|
829
|
-
lastPromptShownUI: false
|
|
830
|
-
},
|
|
831
|
-
silent: options.silent ?? false,
|
|
832
|
-
verbose: options.verbose ?? false,
|
|
833
|
-
projectDir: options.projectDir,
|
|
834
|
-
projectName: options.projectName,
|
|
835
|
-
packageManager: options.packageManager
|
|
836
|
-
};
|
|
837
|
-
return cliStorage.run(ctx, fn);
|
|
838
|
-
}
|
|
839
|
-
//#endregion
|
|
840
885
|
//#region src/utils/navigation.ts
|
|
841
886
|
const GO_BACK_SYMBOL = Symbol("clack:goBack");
|
|
842
887
|
function isGoBack(value) {
|
|
@@ -1094,6 +1139,10 @@ function getAddonDisplay(addon) {
|
|
|
1094
1139
|
label = "Tauri";
|
|
1095
1140
|
hint = "Build native desktop apps from your web frontend";
|
|
1096
1141
|
break;
|
|
1142
|
+
case "electrobun":
|
|
1143
|
+
label = "Electrobun";
|
|
1144
|
+
hint = "Wrap web frontends in a lightweight desktop shell";
|
|
1145
|
+
break;
|
|
1097
1146
|
case "biome":
|
|
1098
1147
|
label = "Biome";
|
|
1099
1148
|
hint = "Format, lint, and more";
|
|
@@ -1106,10 +1155,6 @@ function getAddonDisplay(addon) {
|
|
|
1106
1155
|
label = "Ultracite";
|
|
1107
1156
|
hint = "Zero-config Biome preset with AI integration";
|
|
1108
1157
|
break;
|
|
1109
|
-
case "ruler":
|
|
1110
|
-
label = "Ruler";
|
|
1111
|
-
hint = "Centralize your AI rules";
|
|
1112
|
-
break;
|
|
1113
1158
|
case "lefthook":
|
|
1114
1159
|
label = "Lefthook";
|
|
1115
1160
|
hint = "Fast and powerful Git hooks manager";
|
|
@@ -1164,14 +1209,11 @@ const ADDON_GROUPS = {
|
|
|
1164
1209
|
"Platform Extensions": [
|
|
1165
1210
|
"pwa",
|
|
1166
1211
|
"tauri",
|
|
1212
|
+
"electrobun",
|
|
1167
1213
|
"opentui",
|
|
1168
1214
|
"wxt"
|
|
1169
1215
|
],
|
|
1170
|
-
"AI & Agent Tools": [
|
|
1171
|
-
"ruler",
|
|
1172
|
-
"skills",
|
|
1173
|
-
"mcp"
|
|
1174
|
-
]
|
|
1216
|
+
"AI & Agent Tools": ["skills", "mcp"]
|
|
1175
1217
|
};
|
|
1176
1218
|
function createGroupedOptions() {
|
|
1177
1219
|
return Object.fromEntries(Object.keys(ADDON_GROUPS).map((group) => [group, []]));
|
|
@@ -1418,33 +1460,6 @@ function getPackageRunnerPrefix(packageManager) {
|
|
|
1418
1460
|
}
|
|
1419
1461
|
}
|
|
1420
1462
|
//#endregion
|
|
1421
|
-
//#region src/utils/terminal-output.ts
|
|
1422
|
-
const noopSpinner = {
|
|
1423
|
-
start() {},
|
|
1424
|
-
stop() {},
|
|
1425
|
-
message() {}
|
|
1426
|
-
};
|
|
1427
|
-
function createSpinner() {
|
|
1428
|
-
return isSilent() ? noopSpinner : spinner();
|
|
1429
|
-
}
|
|
1430
|
-
const cliLog = {
|
|
1431
|
-
info(message) {
|
|
1432
|
-
if (!isSilent()) log.info(message);
|
|
1433
|
-
},
|
|
1434
|
-
warn(message) {
|
|
1435
|
-
if (!isSilent()) log.warn(message);
|
|
1436
|
-
},
|
|
1437
|
-
success(message) {
|
|
1438
|
-
if (!isSilent()) log.success(message);
|
|
1439
|
-
},
|
|
1440
|
-
error(message) {
|
|
1441
|
-
if (!isSilent()) log.error(message);
|
|
1442
|
-
},
|
|
1443
|
-
message(message) {
|
|
1444
|
-
if (!isSilent()) log.message(message);
|
|
1445
|
-
}
|
|
1446
|
-
};
|
|
1447
|
-
//#endregion
|
|
1448
1463
|
//#region src/helpers/addons/fumadocs-setup.ts
|
|
1449
1464
|
const TEMPLATES$2 = {
|
|
1450
1465
|
"next-mdx": {
|
|
@@ -1939,118 +1954,6 @@ async function setupOxlint(projectDir, packageManager) {
|
|
|
1939
1954
|
});
|
|
1940
1955
|
}
|
|
1941
1956
|
//#endregion
|
|
1942
|
-
//#region src/helpers/addons/ruler-setup.ts
|
|
1943
|
-
const DEFAULT_ASSISTANTS = [
|
|
1944
|
-
"agentsmd",
|
|
1945
|
-
"claude",
|
|
1946
|
-
"codex",
|
|
1947
|
-
"cursor"
|
|
1948
|
-
];
|
|
1949
|
-
async function setupRuler(config) {
|
|
1950
|
-
if (shouldSkipExternalCommands()) return Result.ok(void 0);
|
|
1951
|
-
const { packageManager, projectDir } = config;
|
|
1952
|
-
cliLog.info("Setting up Ruler...");
|
|
1953
|
-
const rulerDir = path.join(projectDir, ".ruler");
|
|
1954
|
-
if (!await fs.pathExists(rulerDir)) {
|
|
1955
|
-
cliLog.error(pc.red("Ruler template directory not found. Please ensure ruler addon is properly installed."));
|
|
1956
|
-
return Result.ok(void 0);
|
|
1957
|
-
}
|
|
1958
|
-
const EDITORS = {
|
|
1959
|
-
agentsmd: { label: "Agents.md" },
|
|
1960
|
-
aider: { label: "Aider" },
|
|
1961
|
-
amazonqcli: { label: "Amazon Q CLI" },
|
|
1962
|
-
amp: { label: "AMP" },
|
|
1963
|
-
antigravity: { label: "Antigravity" },
|
|
1964
|
-
augmentcode: { label: "AugmentCode" },
|
|
1965
|
-
claude: { label: "Claude Code" },
|
|
1966
|
-
cline: { label: "Cline" },
|
|
1967
|
-
codex: { label: "OpenAI Codex CLI" },
|
|
1968
|
-
copilot: { label: "GitHub Copilot" },
|
|
1969
|
-
crush: { label: "Crush" },
|
|
1970
|
-
cursor: { label: "Cursor" },
|
|
1971
|
-
factory: { label: "Factory" },
|
|
1972
|
-
firebase: { label: "Firebase Studio" },
|
|
1973
|
-
firebender: { label: "Firebender" },
|
|
1974
|
-
"gemini-cli": { label: "Gemini CLI" },
|
|
1975
|
-
goose: { label: "Goose" },
|
|
1976
|
-
"jetbrains-ai": { label: "JetBrains AI" },
|
|
1977
|
-
jules: { label: "Jules" },
|
|
1978
|
-
junie: { label: "Junie" },
|
|
1979
|
-
kilocode: { label: "Kilo Code" },
|
|
1980
|
-
kiro: { label: "Kiro" },
|
|
1981
|
-
mistral: { label: "Mistral" },
|
|
1982
|
-
opencode: { label: "OpenCode" },
|
|
1983
|
-
openhands: { label: "Open Hands" },
|
|
1984
|
-
pi: { label: "Pi" },
|
|
1985
|
-
qwen: { label: "Qwen" },
|
|
1986
|
-
roo: { label: "RooCode" },
|
|
1987
|
-
trae: { label: "Trae AI" },
|
|
1988
|
-
warp: { label: "Warp" },
|
|
1989
|
-
windsurf: { label: "Windsurf" },
|
|
1990
|
-
zed: { label: "Zed" }
|
|
1991
|
-
};
|
|
1992
|
-
const configuredAssistants = config.addonOptions?.ruler?.assistants;
|
|
1993
|
-
let selectedEditors = configuredAssistants ? [...configuredAssistants] : [];
|
|
1994
|
-
if (selectedEditors.length === 0 && configuredAssistants === void 0) if (isSilent()) selectedEditors = [...DEFAULT_ASSISTANTS];
|
|
1995
|
-
else {
|
|
1996
|
-
const promptSelection = await autocompleteMultiselect({
|
|
1997
|
-
message: "Select AI assistants for Ruler",
|
|
1998
|
-
options: Object.entries(EDITORS).map(([key, v]) => ({
|
|
1999
|
-
value: key,
|
|
2000
|
-
label: v.label
|
|
2001
|
-
})),
|
|
2002
|
-
required: false
|
|
2003
|
-
});
|
|
2004
|
-
if (isCancel(promptSelection)) return userCancelled("Operation cancelled");
|
|
2005
|
-
selectedEditors = [...promptSelection];
|
|
2006
|
-
}
|
|
2007
|
-
if (selectedEditors.length === 0) {
|
|
2008
|
-
cliLog.info("No AI assistants selected. To apply rules later, run:");
|
|
2009
|
-
cliLog.info(pc.cyan(`${getPackageExecutionCommand(packageManager, "@intellectronica/ruler@latest apply --local-only")}`));
|
|
2010
|
-
return Result.ok(void 0);
|
|
2011
|
-
}
|
|
2012
|
-
const configFile = path.join(rulerDir, "ruler.toml");
|
|
2013
|
-
let updatedConfig = await fs.readFile(configFile, "utf-8");
|
|
2014
|
-
const defaultAgentsLine = `default_agents = [${selectedEditors.map((editor) => `"${editor}"`).join(", ")}]`;
|
|
2015
|
-
updatedConfig = updatedConfig.replace(/default_agents = \[\]/, defaultAgentsLine);
|
|
2016
|
-
await fs.writeFile(configFile, updatedConfig);
|
|
2017
|
-
await addRulerScriptToPackageJson(projectDir, packageManager);
|
|
2018
|
-
const s = createSpinner();
|
|
2019
|
-
s.start("Applying rules with Ruler...");
|
|
2020
|
-
const applyResult = await Result.tryPromise({
|
|
2021
|
-
try: async () => {
|
|
2022
|
-
const rulerApplyArgs = getPackageExecutionArgs(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`);
|
|
2023
|
-
await $({
|
|
2024
|
-
cwd: projectDir,
|
|
2025
|
-
env: { CI: "true" }
|
|
2026
|
-
})`${rulerApplyArgs}`;
|
|
2027
|
-
},
|
|
2028
|
-
catch: (e) => new AddonSetupError({
|
|
2029
|
-
addon: "ruler",
|
|
2030
|
-
message: `Failed to apply rules: ${e instanceof Error ? e.message : String(e)}`,
|
|
2031
|
-
cause: e
|
|
2032
|
-
})
|
|
2033
|
-
});
|
|
2034
|
-
if (applyResult.isErr()) {
|
|
2035
|
-
s.stop(pc.red("Failed to apply rules"));
|
|
2036
|
-
return applyResult;
|
|
2037
|
-
}
|
|
2038
|
-
s.stop("Applied rules with Ruler");
|
|
2039
|
-
return Result.ok(void 0);
|
|
2040
|
-
}
|
|
2041
|
-
async function addRulerScriptToPackageJson(projectDir, packageManager) {
|
|
2042
|
-
const rootPackageJsonPath = path.join(projectDir, "package.json");
|
|
2043
|
-
if (!await fs.pathExists(rootPackageJsonPath)) {
|
|
2044
|
-
cliLog.warn("Root package.json not found, skipping ruler:apply script addition");
|
|
2045
|
-
return;
|
|
2046
|
-
}
|
|
2047
|
-
const packageJson = await fs.readJson(rootPackageJsonPath);
|
|
2048
|
-
if (!packageJson.scripts) packageJson.scripts = {};
|
|
2049
|
-
const rulerApplyCommand = getPackageExecutionCommand(packageManager, "@intellectronica/ruler@latest apply --local-only");
|
|
2050
|
-
packageJson.scripts["ruler:apply"] = rulerApplyCommand;
|
|
2051
|
-
await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 });
|
|
2052
|
-
}
|
|
2053
|
-
//#endregion
|
|
2054
1957
|
//#region src/helpers/addons/skills-setup.ts
|
|
2055
1958
|
const SKILL_SOURCES = {
|
|
2056
1959
|
"vercel-labs/agent-skills": { label: "Vercel Agent Skills" },
|
|
@@ -2442,14 +2345,32 @@ async function setupStarlight(config) {
|
|
|
2442
2345
|
}
|
|
2443
2346
|
//#endregion
|
|
2444
2347
|
//#region src/helpers/addons/tauri-setup.ts
|
|
2348
|
+
function getWebFrontend(frontend) {
|
|
2349
|
+
return frontend.find((value) => types_exports.desktopWebFrontends.includes(value));
|
|
2350
|
+
}
|
|
2351
|
+
function getTauriDevUrl(frontend) {
|
|
2352
|
+
switch (getWebFrontend(frontend)) {
|
|
2353
|
+
case "react-router":
|
|
2354
|
+
case "svelte": return "http://localhost:5173";
|
|
2355
|
+
case "astro": return "http://localhost:4321";
|
|
2356
|
+
default: return "http://localhost:3001";
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
function getTauriFrontendDist(frontend) {
|
|
2360
|
+
switch (getWebFrontend(frontend)) {
|
|
2361
|
+
case "react-router": return "../build/client";
|
|
2362
|
+
case "tanstack-start": return "../dist/client";
|
|
2363
|
+
case "next": return "../out";
|
|
2364
|
+
case "nuxt": return "../.output/public";
|
|
2365
|
+
case "svelte": return "../build";
|
|
2366
|
+
default: return "../dist";
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
function getTauriBeforeBuildCommand(packageManager, frontend) {
|
|
2370
|
+
return frontend.includes("nuxt") ? `${packageManager} run generate` : `${packageManager} run build`;
|
|
2371
|
+
}
|
|
2445
2372
|
function buildTauriInitArgs(config) {
|
|
2446
2373
|
const { packageManager, frontend, projectDir } = config;
|
|
2447
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
2448
|
-
const hasNuxt = frontend.includes("nuxt");
|
|
2449
|
-
const hasSvelte = frontend.includes("svelte");
|
|
2450
|
-
const hasNext = frontend.includes("next");
|
|
2451
|
-
const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
|
|
2452
|
-
const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
|
|
2453
2374
|
return [
|
|
2454
2375
|
...getPackageRunnerPrefix(packageManager),
|
|
2455
2376
|
"@tauri-apps/cli@latest",
|
|
@@ -2460,13 +2381,13 @@ function buildTauriInitArgs(config) {
|
|
|
2460
2381
|
"--window-title",
|
|
2461
2382
|
path.basename(projectDir),
|
|
2462
2383
|
"--frontend-dist",
|
|
2463
|
-
|
|
2384
|
+
getTauriFrontendDist(frontend),
|
|
2464
2385
|
"--dev-url",
|
|
2465
|
-
|
|
2386
|
+
getTauriDevUrl(frontend),
|
|
2466
2387
|
"--before-dev-command",
|
|
2467
2388
|
`${packageManager} run dev`,
|
|
2468
2389
|
"--before-build-command",
|
|
2469
|
-
|
|
2390
|
+
getTauriBeforeBuildCommand(packageManager, frontend)
|
|
2470
2391
|
];
|
|
2471
2392
|
}
|
|
2472
2393
|
async function setupTauri(config) {
|
|
@@ -2942,7 +2863,7 @@ async function runSetup(setupFn) {
|
|
|
2942
2863
|
const result = await setupFn();
|
|
2943
2864
|
if (result.isErr()) {
|
|
2944
2865
|
if (UserCancelledError.is(result.error)) throw result.error;
|
|
2945
|
-
|
|
2866
|
+
cliConsola.error(pc.red(result.error.message));
|
|
2946
2867
|
}
|
|
2947
2868
|
}
|
|
2948
2869
|
async function runAddonStep(addon, step) {
|
|
@@ -2954,16 +2875,12 @@ async function runAddonStep(addon, step) {
|
|
|
2954
2875
|
cause: e
|
|
2955
2876
|
})
|
|
2956
2877
|
});
|
|
2957
|
-
if (result.isErr())
|
|
2878
|
+
if (result.isErr()) cliConsola.error(pc.red(result.error.message));
|
|
2958
2879
|
}
|
|
2959
2880
|
async function setupAddons(config) {
|
|
2960
2881
|
const { addons, frontend, projectDir } = config;
|
|
2961
|
-
const
|
|
2962
|
-
|
|
2963
|
-
const hasSvelteFrontend = frontend.includes("svelte");
|
|
2964
|
-
const hasSolidFrontend = frontend.includes("solid");
|
|
2965
|
-
const hasNextFrontend = frontend.includes("next");
|
|
2966
|
-
if (addons.includes("tauri") && (hasReactWebFrontend || hasNuxtFrontend || hasSvelteFrontend || hasSolidFrontend || hasNextFrontend)) await runSetup(() => setupTauri(config));
|
|
2882
|
+
const hasWebFrontend = frontend.some((value) => types_exports.desktopWebFrontends.includes(value));
|
|
2883
|
+
if (addons.includes("tauri") && hasWebFrontend) await runSetup(() => setupTauri(config));
|
|
2967
2884
|
const hasUltracite = addons.includes("ultracite");
|
|
2968
2885
|
const hasBiome = addons.includes("biome");
|
|
2969
2886
|
const hasHusky = addons.includes("husky");
|
|
@@ -2989,7 +2906,6 @@ async function setupAddons(config) {
|
|
|
2989
2906
|
if (addons.includes("fumadocs")) await runSetup(() => setupFumadocs(config));
|
|
2990
2907
|
if (addons.includes("opentui")) await runSetup(() => setupTui(config));
|
|
2991
2908
|
if (addons.includes("wxt")) await runSetup(() => setupWxt(config));
|
|
2992
|
-
if (addons.includes("ruler")) await runSetup(() => setupRuler(config));
|
|
2993
2909
|
if (addons.includes("skills")) await runSetup(() => setupSkills(config));
|
|
2994
2910
|
if (addons.includes("mcp")) await runSetup(() => setupMcp(config));
|
|
2995
2911
|
}
|
|
@@ -4013,7 +3929,7 @@ async function getProjectName(initialName) {
|
|
|
4013
3929
|
if (initialName === ".") return initialName;
|
|
4014
3930
|
if (!validateDirectoryName(path.basename(initialName))) {
|
|
4015
3931
|
if (isPathWithinCwd$1(path.resolve(process.cwd(), initialName))) return initialName;
|
|
4016
|
-
|
|
3932
|
+
cliConsola.error(pc.red("Project path must be within current directory"));
|
|
4017
3933
|
}
|
|
4018
3934
|
}
|
|
4019
3935
|
let isValid = false;
|
|
@@ -5823,7 +5739,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5823
5739
|
return Result.ok(void 0);
|
|
5824
5740
|
}
|
|
5825
5741
|
setupSpinner.start("Checking Turso CLI availability...");
|
|
5826
|
-
const platform = os
|
|
5742
|
+
const platform = os.platform();
|
|
5827
5743
|
const isMac = platform === "darwin";
|
|
5828
5744
|
if (platform === "win32") {
|
|
5829
5745
|
setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
|
|
@@ -5931,6 +5847,11 @@ async function setupTurso(config, cliInput) {
|
|
|
5931
5847
|
}
|
|
5932
5848
|
//#endregion
|
|
5933
5849
|
//#region src/helpers/core/db-setup.ts
|
|
5850
|
+
/**
|
|
5851
|
+
* Database setup - CLI-only operations
|
|
5852
|
+
* Calls external database provider CLIs (turso, neon, prisma-postgres, etc.)
|
|
5853
|
+
* Dependencies are handled by the generator's db-deps processor
|
|
5854
|
+
*/
|
|
5934
5855
|
async function setupDatabase(config, cliInput) {
|
|
5935
5856
|
const { database, dbSetup, backend, projectDir } = config;
|
|
5936
5857
|
if (backend === "convex" || database === "none") {
|
|
@@ -5946,7 +5867,7 @@ async function setupDatabase(config, cliInput) {
|
|
|
5946
5867
|
const result = await setupFn();
|
|
5947
5868
|
if (result.isErr()) {
|
|
5948
5869
|
if (UserCancelledError.is(result.error)) throw result.error;
|
|
5949
|
-
|
|
5870
|
+
cliConsola.error(pc.red(result.error.message));
|
|
5950
5871
|
}
|
|
5951
5872
|
}
|
|
5952
5873
|
const resolvedCliInput = {
|
|
@@ -6032,7 +5953,7 @@ function getDockerInstallInstructions(platform, database) {
|
|
|
6032
5953
|
return `${pc.yellow("IMPORTANT:")} Docker required for ${databaseName}. Install for ${platformName}:\n${pc.blue(installUrl)}`;
|
|
6033
5954
|
}
|
|
6034
5955
|
async function getDockerStatus(database) {
|
|
6035
|
-
const platform = os
|
|
5956
|
+
const platform = os.platform();
|
|
6036
5957
|
if (!await isDockerInstalled()) return {
|
|
6037
5958
|
installed: false,
|
|
6038
5959
|
running: false,
|
|
@@ -6050,6 +5971,18 @@ async function getDockerStatus(database) {
|
|
|
6050
5971
|
}
|
|
6051
5972
|
//#endregion
|
|
6052
5973
|
//#region src/helpers/core/post-installation.ts
|
|
5974
|
+
function getDesktopStaticBuildNote(frontend) {
|
|
5975
|
+
const staticBuildFrontends = new Map([
|
|
5976
|
+
["tanstack-start", "TanStack Start"],
|
|
5977
|
+
["next", "Next.js"],
|
|
5978
|
+
["nuxt", "Nuxt"],
|
|
5979
|
+
["svelte", "SvelteKit"],
|
|
5980
|
+
["astro", "Astro"]
|
|
5981
|
+
]);
|
|
5982
|
+
const staticBuildFrontend = frontend.find((value) => staticBuildFrontends.has(value));
|
|
5983
|
+
if (!staticBuildFrontend) return "";
|
|
5984
|
+
return `${pc.yellow("NOTE:")} Desktop builds package static web assets.\n ${staticBuildFrontends.get(staticBuildFrontend)} needs a static/export web build before desktop packaging will work.`;
|
|
5985
|
+
}
|
|
6053
5986
|
async function displayPostInstallInstructions(config) {
|
|
6054
5987
|
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
6055
5988
|
const isConvex = backend === "convex";
|
|
@@ -6060,7 +5993,8 @@ async function displayPostInstallInstructions(config) {
|
|
|
6060
5993
|
const hasLefthook = addons?.includes("lefthook");
|
|
6061
5994
|
const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
|
|
6062
5995
|
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6063
|
-
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
5996
|
+
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
|
|
5997
|
+
const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
|
|
6064
5998
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
6065
5999
|
const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
|
|
6066
6000
|
const lintingInstructions = hasGitHooksOrLinting ? getLintingInstructions(runCmd) : "";
|
|
@@ -6070,19 +6004,12 @@ async function displayPostInstallInstructions(config) {
|
|
|
6070
6004
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
6071
6005
|
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
6072
6006
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6073
|
-
const hasWeb = frontend?.some((f) =>
|
|
6074
|
-
"tanstack-router",
|
|
6075
|
-
"react-router",
|
|
6076
|
-
"next",
|
|
6077
|
-
"tanstack-start",
|
|
6078
|
-
"nuxt",
|
|
6079
|
-
"svelte",
|
|
6080
|
-
"solid"
|
|
6081
|
-
].includes(f));
|
|
6007
|
+
const hasWeb = frontend?.some((f) => types_exports.desktopWebFrontends.includes(f));
|
|
6082
6008
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
6083
6009
|
const hasReactRouter = frontend?.includes("react-router");
|
|
6084
6010
|
const hasSvelte = frontend?.includes("svelte");
|
|
6085
|
-
const
|
|
6011
|
+
const hasAstro = frontend?.includes("astro");
|
|
6012
|
+
const webPort = hasReactRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
|
|
6086
6013
|
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
6087
6014
|
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
6088
6015
|
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
@@ -6118,6 +6045,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6118
6045
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
6119
6046
|
if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
|
|
6120
6047
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
6048
|
+
if (electrobunInstructions) output += `\n${electrobunInstructions.trim()}\n`;
|
|
6121
6049
|
if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
|
|
6122
6050
|
if (lefthookInstructions) output += `\n${lefthookInstructions.trim()}\n`;
|
|
6123
6051
|
if (lintingInstructions) output += `\n${lintingInstructions.trim()}\n`;
|
|
@@ -6134,7 +6062,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6134
6062
|
if (specialSponsorsSection) output += `\n${specialSponsorsSection.trim()}\n`;
|
|
6135
6063
|
output += `\n${pc.bold("Like Better-T-Stack?")} Please consider giving us a star\n on GitHub:\n`;
|
|
6136
6064
|
output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
|
|
6137
|
-
|
|
6065
|
+
cliConsola.box(output);
|
|
6138
6066
|
}
|
|
6139
6067
|
function getNativeInstructions(isConvex, isBackendSelf, frontend, runCmd) {
|
|
6140
6068
|
const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
|
|
@@ -6194,8 +6122,13 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
6194
6122
|
} else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
|
|
6195
6123
|
return instructions.length ? `${pc.bold("Database commands:")}\n${instructions.join("\n")}` : "";
|
|
6196
6124
|
}
|
|
6197
|
-
function getTauriInstructions(runCmd) {
|
|
6198
|
-
|
|
6125
|
+
function getTauriInstructions(runCmd, frontend) {
|
|
6126
|
+
const staticBuildNote = getDesktopStaticBuildNote(frontend);
|
|
6127
|
+
return `\n${pc.bold("Desktop app with Tauri:")}\n${pc.cyan("•")} Start desktop app: ${`cd apps/web && ${runCmd} desktop:dev`}\n${pc.cyan("•")} Build desktop app: ${`cd apps/web && ${runCmd} desktop:build`}\n${pc.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies.\n See: https://v2.tauri.app/start/prerequisites/${staticBuildNote ? `\n${staticBuildNote}` : ""}`;
|
|
6128
|
+
}
|
|
6129
|
+
function getElectrobunInstructions(runCmd, frontend) {
|
|
6130
|
+
const staticBuildNote = getDesktopStaticBuildNote(frontend);
|
|
6131
|
+
return `\n${pc.bold("Desktop app with Electrobun:")}\n${pc.cyan("•")} Start desktop app with HMR: ${`${runCmd} dev:desktop`}\n${pc.cyan("•")} Build stable desktop app (DMG/App): ${`${runCmd} build:desktop`}\n${pc.cyan("•")} Build canary desktop app: ${`${runCmd} build:desktop:canary`}\n${pc.yellow("NOTE:")} Electrobun wraps your web frontend in a desktop shell.\n See: https://blackboard.sh/electrobun/docs/${staticBuildNote ? `\n${staticBuildNote}` : ""}`;
|
|
6199
6132
|
}
|
|
6200
6133
|
function getPwaInstructions() {
|
|
6201
6134
|
return `\n${pc.bold("PWA with React Router v7:")}\n${pc.yellow("NOTE:")} There is a known compatibility issue between VitePWA\n and React Router v7. See:\n https://github.com/vite-pwa/vite-plugin-pwa/issues/809`;
|
|
@@ -6296,7 +6229,7 @@ async function setPackageManagerVersion(projectDir, packageManager) {
|
|
|
6296
6229
|
if (!await fs.pathExists(pkgJsonPath)) return Result.ok(void 0);
|
|
6297
6230
|
const versionResult = await Result.tryPromise({
|
|
6298
6231
|
try: async () => {
|
|
6299
|
-
const { stdout } = await $({ cwd: os
|
|
6232
|
+
const { stdout } = await $({ cwd: os.tmpdir() })`${packageManager} -v`;
|
|
6300
6233
|
return stdout.trim();
|
|
6301
6234
|
},
|
|
6302
6235
|
catch: () => null
|
|
@@ -6374,7 +6307,7 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
|
|
|
6374
6307
|
return Result.gen(async function* () {
|
|
6375
6308
|
if (!isSilent() && input.renderTitle !== false) renderTitle();
|
|
6376
6309
|
if (!isSilent()) intro(pc.magenta("Creating a new Better-T-Stack project"));
|
|
6377
|
-
if (!isSilent() && input.yolo)
|
|
6310
|
+
if (!isSilent() && input.yolo) cliConsola.fatal("YOLO mode enabled - skipping checks. Things may break!");
|
|
6378
6311
|
let currentPathInput;
|
|
6379
6312
|
if (isSilent()) currentPathInput = yield* Result.await(resolveProjectNameForSilent(input));
|
|
6380
6313
|
else if (input.yes && input.projectName) currentPathInput = input.projectName;
|
|
@@ -6652,6 +6585,7 @@ const SchemaNameSchema = z.enum([
|
|
|
6652
6585
|
"betterTStackConfig",
|
|
6653
6586
|
"initResult"
|
|
6654
6587
|
]).default("all");
|
|
6588
|
+
const t = initTRPC.meta().create();
|
|
6655
6589
|
function getCliSchemaJson() {
|
|
6656
6590
|
return createCli({
|
|
6657
6591
|
router,
|
|
@@ -6668,8 +6602,8 @@ function getSchemaResult(name) {
|
|
|
6668
6602
|
if (name === "cli") return getCliSchemaJson();
|
|
6669
6603
|
return schemas[name];
|
|
6670
6604
|
}
|
|
6671
|
-
const router =
|
|
6672
|
-
create:
|
|
6605
|
+
const router = t.router({
|
|
6606
|
+
create: t.procedure.meta({
|
|
6673
6607
|
description: "Create a new Better-T-Stack project",
|
|
6674
6608
|
default: true,
|
|
6675
6609
|
negateBooleans: true
|
|
@@ -6700,7 +6634,7 @@ const router = os.router({
|
|
|
6700
6634
|
disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
|
|
6701
6635
|
manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup"),
|
|
6702
6636
|
dbSetupOptions: types_exports.DbSetupOptionsSchema.optional().describe("Structured database setup options")
|
|
6703
|
-
})])).
|
|
6637
|
+
})])).mutation(async ({ input }) => {
|
|
6704
6638
|
const [projectName, options] = input;
|
|
6705
6639
|
const result = await createProjectHandler({
|
|
6706
6640
|
projectName,
|
|
@@ -6708,41 +6642,41 @@ const router = os.router({
|
|
|
6708
6642
|
});
|
|
6709
6643
|
if (options.verbose || options.dryRun) return result;
|
|
6710
6644
|
}),
|
|
6711
|
-
createJson:
|
|
6645
|
+
createJson: t.procedure.meta({
|
|
6712
6646
|
description: "Create a project from a raw JSON payload (agent-friendly)",
|
|
6713
6647
|
jsonInput: true
|
|
6714
|
-
}).input(types_exports.CreateInputSchema).
|
|
6648
|
+
}).input(types_exports.CreateInputSchema).mutation(async ({ input }) => {
|
|
6715
6649
|
const result = await createProjectHandler(input, { silent: true });
|
|
6716
6650
|
if (!result) throw new UserCancelledError({ message: "Operation cancelled" });
|
|
6717
6651
|
if (!result.success) throw new CLIError({ message: result.error || "Unknown error occurred" });
|
|
6718
6652
|
return result;
|
|
6719
6653
|
}),
|
|
6720
|
-
schema:
|
|
6721
|
-
sponsors:
|
|
6722
|
-
docs:
|
|
6723
|
-
builder:
|
|
6724
|
-
add:
|
|
6654
|
+
schema: t.procedure.meta({ description: "Show runtime CLI and input schemas as JSON" }).input(z.object({ name: SchemaNameSchema.describe("Schema name to inspect") })).query(({ input }) => getSchemaResult(input.name)),
|
|
6655
|
+
sponsors: t.procedure.meta({ description: "Show Better-T-Stack sponsors" }).mutation(() => showSponsorsCommand()),
|
|
6656
|
+
docs: t.procedure.meta({ description: "Open Better-T-Stack documentation" }).mutation(() => openDocsCommand()),
|
|
6657
|
+
builder: t.procedure.meta({ description: "Open the web-based stack builder" }).mutation(() => openBuilderCommand()),
|
|
6658
|
+
add: t.procedure.meta({ description: "Add addons to an existing Better-T-Stack project" }).input(z.object({
|
|
6725
6659
|
addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
|
|
6726
6660
|
install: z.boolean().optional().default(false).describe("Install dependencies after adding"),
|
|
6727
6661
|
packageManager: types_exports.PackageManagerSchema.optional().describe("Package manager to use"),
|
|
6728
6662
|
projectDir: z.string().optional().describe("Project directory (defaults to current)")
|
|
6729
|
-
})).
|
|
6663
|
+
})).mutation(async ({ input }) => {
|
|
6730
6664
|
await addHandler(input);
|
|
6731
6665
|
}),
|
|
6732
|
-
addJson:
|
|
6666
|
+
addJson: t.procedure.meta({
|
|
6733
6667
|
description: "Add addons from a raw JSON payload (agent-friendly)",
|
|
6734
6668
|
jsonInput: true
|
|
6735
|
-
}).input(types_exports.AddInputSchema).
|
|
6669
|
+
}).input(types_exports.AddInputSchema).mutation(async ({ input }) => {
|
|
6736
6670
|
const result = await addHandler(input, { silent: true });
|
|
6737
6671
|
if (!result) throw new UserCancelledError({ message: "Operation cancelled" });
|
|
6738
6672
|
if (!result.success) throw new CLIError({ message: result.error || "Unknown error occurred" });
|
|
6739
6673
|
return result;
|
|
6740
6674
|
}),
|
|
6741
|
-
history:
|
|
6675
|
+
history: t.procedure.meta({ description: "Show project creation history" }).input(z.object({
|
|
6742
6676
|
limit: z.number().optional().default(10).describe("Number of entries to show"),
|
|
6743
6677
|
clear: z.boolean().optional().default(false).describe("Clear all history"),
|
|
6744
6678
|
json: z.boolean().optional().default(false).describe("Output as JSON")
|
|
6745
|
-
})).
|
|
6679
|
+
})).mutation(async ({ input }) => {
|
|
6746
6680
|
await historyHandler(input);
|
|
6747
6681
|
})
|
|
6748
6682
|
});
|