create-sprint 0.0.2 → 0.0.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/dist/cli.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+ import { runCLI } from "./index";
4
+ const args = process.argv.slice(2);
5
+ if (args.includes("--help") || args.includes("-h")) {
6
+ console.log("\nšŸš€ Sprint - Quickly API Framework\n");
7
+ console.log("Usage: npx create-sprint [options]");
8
+ console.log("\nOptions:");
9
+ console.log(" --ts, --typescript Create TypeScript project");
10
+ console.log(" --js, --javascript Create JavaScript project");
11
+ console.log(" --name <name> Project name (use '.' for current directory)");
12
+ console.log(" --no-install Skip automatic dependency installation");
13
+ console.log(" --telemetry <type> Telemetry: none, sentry, glitchtip, discord");
14
+ console.log(" --docker Add Docker support");
15
+ console.log(" --yes, -y Skip all prompts (use defaults)");
16
+ console.log(" --help, -h Show this help message");
17
+ console.log("\nExamples:");
18
+ console.log(" npx create-sprint Interactive mode");
19
+ console.log(" npx create-sprint -y Create TypeScript project with defaults");
20
+ console.log(" npx create-sprint --ts --name my-api");
21
+ console.log(" npx create-sprint --js --name . --telemetry sentry --docker\n");
22
+ process.exit(0);
23
+ }
24
+ runCLI(args).catch(console.error);
@@ -0,0 +1,373 @@
1
+ export function getTypeScriptPackageJson(name, telemetry) {
2
+ const deps = {
3
+ "sprint-es": "^0.0.24",
4
+ dotenv: "^17.0.0",
5
+ };
6
+ const devDeps = {
7
+ "@types/node": "^22.0.0",
8
+ "tsx": "^4.19.0",
9
+ typescript: "^5.6.0",
10
+ vite: "^6.0.0",
11
+ };
12
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
13
+ deps["@sentry/node"] = "^8.0.0";
14
+ }
15
+ else if (telemetry === "discord") {
16
+ deps["axios"] = "^1.6.0";
17
+ }
18
+ return {
19
+ name: name === "." ? "sprint-app" : name,
20
+ version: "0.0.1",
21
+ description: "Sprint API",
22
+ main: "dist/index.js",
23
+ scripts: {
24
+ build: "vite build",
25
+ start: "NODE_ENV=production node dist/index.js",
26
+ dev: "NODE_ENV=development tsx src/index.ts",
27
+ },
28
+ dependencies: deps,
29
+ devDependencies: devDeps,
30
+ };
31
+ }
32
+ export function getJavaScriptPackageJson(name, telemetry) {
33
+ const deps = {
34
+ "sprint-es": "^0.0.24",
35
+ dotenv: "^17.0.0",
36
+ };
37
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
38
+ deps["@sentry/node"] = "^8.0.0";
39
+ }
40
+ else if (telemetry === "discord") {
41
+ deps["axios"] = "^1.6.0";
42
+ }
43
+ return {
44
+ name: name === "." ? "sprint-app" : name,
45
+ version: "0.0.1",
46
+ description: "Sprint API",
47
+ main: "src/index.js",
48
+ type: "module",
49
+ scripts: {
50
+ start: "NODE_ENV=production node src/index.js",
51
+ dev: "NODE_ENV=development node --watch src/index.js",
52
+ },
53
+ dependencies: deps,
54
+ };
55
+ }
56
+ export function getTsConfig() {
57
+ return JSON.stringify({
58
+ compilerOptions: {
59
+ target: "ES2020",
60
+ module: "ESNext",
61
+ moduleResolution: "bundler",
62
+ lib: ["ES2020"],
63
+ outDir: "./dist",
64
+ rootDir: "./src",
65
+ strict: true,
66
+ esModuleInterop: true,
67
+ skipLibCheck: true,
68
+ forceConsistentCasingInFileNames: true,
69
+ resolveJsonModule: true,
70
+ declaration: true,
71
+ declarationMap: true,
72
+ sourceMap: true,
73
+ tabWidth: 4,
74
+ },
75
+ include: ["src/**/*"],
76
+ exclude: ["node_modules", "dist"],
77
+ }, null, 2);
78
+ }
79
+ export function getViteConfig() {
80
+ return `import { defineConfig } from "vite";
81
+ import { resolve } from "path";
82
+
83
+ export default defineConfig({
84
+ build: {
85
+ lib: {
86
+ entry: resolve(__dirname, "src/index.ts"),
87
+ formats: ["es"],
88
+ fileName: "index",
89
+ },
90
+ outDir: "dist",
91
+ rollupOptions: {
92
+ external: ["sprint-es", "express", "cors", "morgan", "serve-favicon", "dotenv"],
93
+ },
94
+ target: "ES2020",
95
+ },
96
+ resolve: {
97
+ alias: {
98
+ "@": resolve(__dirname, "src"),
99
+ },
100
+ },
101
+ });
102
+ `;
103
+ }
104
+ export function getMainFile(language) {
105
+ if (language === "typescript") {
106
+ return `import Sprint from "sprint-es";
107
+ import { config } from "./sprint.config";
108
+ import homeRouter from "./routes/home";
109
+
110
+ const app = new Sprint(config);
111
+
112
+ app.use(homeRouter);
113
+ `;
114
+ }
115
+ return `import Sprint from "sprint-es";
116
+ import { config } from "./sprint.config.js";
117
+ import homeRouter from "./routes/home.js";
118
+
119
+ const app = new Sprint(config);
120
+
121
+ app.use(homeRouter);
122
+ `;
123
+ }
124
+ export function getHomeRoute(language) {
125
+ if (language === "typescript") {
126
+ return `import { Router } from "sprint-es";
127
+
128
+ const router = Router();
129
+
130
+ router.get("/", (req, res) => {
131
+ res.json({
132
+ message: "Hello World",
133
+ status: "ok"
134
+ });
135
+ });
136
+
137
+ export default router;
138
+ `;
139
+ }
140
+ return `import { Router } from "sprint-es";
141
+
142
+ const router = Router();
143
+
144
+ router.get("/", (req, res) => {
145
+ res.json({
146
+ message: "Hello World",
147
+ status: "ok"
148
+ });
149
+ });
150
+
151
+ export default router;
152
+ `;
153
+ }
154
+ export function getDockerfile(language) {
155
+ if (language === "typescript") {
156
+ return `FROM node:20-alpine
157
+
158
+ WORKDIR /app
159
+
160
+ COPY package*.json ./
161
+
162
+ RUN npm ci
163
+
164
+ COPY . .
165
+
166
+ RUN npm run build
167
+
168
+ EXPOSE 3000
169
+
170
+ CMD ["npm", "start"]
171
+ `;
172
+ }
173
+ return `FROM node:20-alpine
174
+
175
+ WORKDIR /app
176
+
177
+ COPY package*.json ./
178
+
179
+ RUN npm ci
180
+
181
+ COPY . .
182
+
183
+ EXPOSE 3000
184
+
185
+ CMD ["npm", "start"]
186
+ `;
187
+ }
188
+ export function getDockerCompose(language) {
189
+ return `version: "3.8"
190
+
191
+ services:
192
+ app:
193
+ build: .
194
+ ports:
195
+ - "3000:3000"
196
+ environment:
197
+ - NODE_ENV=production
198
+ - PORT=3000
199
+ restart: unless-stopped
200
+ `;
201
+ }
202
+ export function getGitignore() {
203
+ return `# Dependencies
204
+ node_modules/
205
+ npm-debug.log*
206
+ yarn-debug.log*
207
+ yarn-error.log*
208
+
209
+ # Build
210
+ dist/
211
+ build/
212
+ *.tsbuildinfo
213
+
214
+ # Environment
215
+ .env.development
216
+ .env.production
217
+ .env.local
218
+ .env.*.local
219
+
220
+ # IDE
221
+ .vscode/
222
+ .idea/
223
+ *.swp
224
+ *.swo
225
+ *~
226
+
227
+ # OS
228
+ .DS_Store
229
+ Thumbs.db
230
+
231
+ # Logs
232
+ logs/
233
+ *.log
234
+
235
+ # Test
236
+ coverage/
237
+
238
+ # Temporary
239
+ tmp/
240
+ temp/
241
+ `;
242
+ }
243
+ export function getDockerIgnore() {
244
+ return `node_modules
245
+ npm-debug.log
246
+ .env
247
+ .env.*
248
+ .git
249
+ .gitignore
250
+ README.md
251
+ dist
252
+ build
253
+ coverage
254
+ .vscode
255
+ .idea
256
+ *.log
257
+ tmp
258
+ temp
259
+ `;
260
+ }
261
+ export function getSprintConfigFile(language, telemetry) {
262
+ if (language === "typescript") {
263
+ let config = `import type { SprintOptions } from "sprint-es";
264
+
265
+ export const config: SprintOptions = {
266
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000
267
+ };
268
+
269
+ `;
270
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
271
+ config += `import { initTelemetry } from "sprint-es/telemetry";
272
+
273
+ initTelemetry({
274
+ provider: "${telemetry}",
275
+ dsn: process.env.SENTRY_DSN || "",
276
+ environment: process.env.NODE_ENV || "development"
277
+ });
278
+ `;
279
+ }
280
+ else if (telemetry === "discord") {
281
+ config += `import { initTelemetry } from "sprint-es/telemetry";
282
+
283
+ initTelemetry({
284
+ provider: "discord",
285
+ webhookUrl: process.env.DISCORD_WEBHOOK_URL || ""
286
+ });
287
+ `;
288
+ }
289
+ return config;
290
+ }
291
+ let config = `export const config = {
292
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000
293
+ };
294
+ `;
295
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
296
+ config += `
297
+ import { initTelemetry } from "sprint-es/telemetry";
298
+
299
+ initTelemetry({
300
+ provider: "${telemetry}",
301
+ dsn: process.env.SENTRY_DSN || "",
302
+ environment: process.env.NODE_ENV || "development"
303
+ });
304
+ `;
305
+ }
306
+ else if (telemetry === "discord") {
307
+ config += `
308
+ import { initTelemetry } from "sprint-es/telemetry";
309
+
310
+ initTelemetry({
311
+ provider: "discord",
312
+ webhookUrl: process.env.DISCORD_WEBHOOK_URL || ""
313
+ });
314
+ `;
315
+ }
316
+ return config;
317
+ }
318
+ export function getEnvExample(telemetry) {
319
+ let env = `PORT=3000
320
+
321
+ # Development: npm run dev (NODE_ENV=development)
322
+ # Production: npm start (NODE_ENV=production)
323
+ `;
324
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
325
+ env += `
326
+ # Sentry / GlitchTip (use GlitchTip DSN for self-hosted)
327
+ SENTRY_DSN=
328
+ `;
329
+ }
330
+ else if (telemetry === "discord") {
331
+ env += `
332
+ # Discord Webhook URL for error notifications
333
+ DISCORD_WEBHOOK_URL=
334
+ `;
335
+ }
336
+ return env;
337
+ }
338
+ export function getEnvDevelopment(telemetry) {
339
+ let env = `NODE_ENV=development
340
+ PORT=3000
341
+ `;
342
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
343
+ env += `
344
+ # Sentry / GlitchTip
345
+ SENTRY_DSN=
346
+ `;
347
+ }
348
+ else if (telemetry === "discord") {
349
+ env += `
350
+ # Discord Webhook URL
351
+ DISCORD_WEBHOOK_URL=
352
+ `;
353
+ }
354
+ return env;
355
+ }
356
+ export function getEnvProduction(telemetry) {
357
+ let env = `NODE_ENV=production
358
+ PORT=3000
359
+ `;
360
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
361
+ env += `
362
+ # Sentry / GlitchTip
363
+ SENTRY_DSN=
364
+ `;
365
+ }
366
+ else if (telemetry === "discord") {
367
+ env += `
368
+ # Discord Webhook URL
369
+ DISCORD_WEBHOOK_URL=
370
+ `;
371
+ }
372
+ return env;
373
+ }
package/dist/index.js ADDED
@@ -0,0 +1,201 @@
1
+ import { execSync } from "child_process";
2
+ import { existsSync } from "fs";
3
+ import { mkdir, writeFile } from "fs/promises";
4
+ import { join } from "path";
5
+ import { input, select, confirm } from "@inquirer/prompts";
6
+ import { validateProjectName } from "./validators";
7
+ import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvExample, getEnvDevelopment, getEnvProduction } from "./generators";
8
+ export async function runCLI(args) {
9
+ const options = parseArgs(args);
10
+ console.log("\nšŸš€ Welcome to Sprint - Quickly API Framework\n");
11
+ let projectName = options.projectName;
12
+ let language = options.language;
13
+ const telemetry = options.telemetry;
14
+ const useDocker = options.docker;
15
+ if (!projectName) {
16
+ projectName = await getProjectName();
17
+ }
18
+ const error = validateProjectName(projectName);
19
+ if (error) {
20
+ console.error(`\nāŒ Error: ${error}\n`);
21
+ process.exit(1);
22
+ }
23
+ if (!language) {
24
+ language = await selectLanguage();
25
+ }
26
+ console.log(`\nāœ… Creating Sprint project: ${projectName === "." ? "current directory" : projectName} with ${language === "typescript" ? "TypeScript" : "JavaScript"}\n`);
27
+ await createProject(projectName, language, telemetry, useDocker);
28
+ console.log("\nāœ… Project created successfully!");
29
+ let installDeps = true;
30
+ if (options.skipInstall) {
31
+ installDeps = false;
32
+ }
33
+ else if (!options.skipPrompts) {
34
+ installDeps = await confirm({
35
+ message: "Do you want to install dependencies now?",
36
+ default: true,
37
+ });
38
+ }
39
+ if (installDeps) {
40
+ console.log("\nšŸ“¦ Installing dependencies...\n");
41
+ const targetDir = projectName === "." ? process.cwd() : join(process.cwd(), projectName);
42
+ try {
43
+ execSync("npm install", { cwd: targetDir, stdio: "inherit" });
44
+ console.log("\nāœ… Dependencies installed successfully!");
45
+ }
46
+ catch {
47
+ console.error("\nāŒ Error installing dependencies. Please run 'npm install' manually.");
48
+ }
49
+ }
50
+ console.log("\nšŸ“¦ Next steps:");
51
+ const cdCmd = projectName === "." ? "" : `cd ${projectName} && `;
52
+ if (!installDeps) {
53
+ console.log(` ${cdCmd}npm install`);
54
+ }
55
+ console.log(` ${cdCmd}npm run dev`);
56
+ console.log("\n");
57
+ }
58
+ function parseArgs(args) {
59
+ const options = {};
60
+ const hasTs = args.includes("--ts") || args.includes("--typescript");
61
+ const hasJs = args.includes("--js") || args.includes("--javascript");
62
+ const hasName = args.indexOf("--name");
63
+ const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
64
+ if (args.includes("--yes") || args.includes("-y")) {
65
+ options.skipPrompts = true;
66
+ options.language = "typescript";
67
+ }
68
+ else if (hasTs) {
69
+ options.language = "typescript";
70
+ }
71
+ else if (hasJs) {
72
+ options.language = "javascript";
73
+ }
74
+ if (hasName !== -1 && args[hasName + 1]) {
75
+ options.projectName = args[hasName + 1];
76
+ }
77
+ if (args.includes("--current")) {
78
+ options.projectName = ".";
79
+ }
80
+ if (args.includes("--docker")) {
81
+ options.docker = true;
82
+ }
83
+ if (args.includes("--no-install")) {
84
+ options.skipInstall = true;
85
+ }
86
+ if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
87
+ options.telemetry = telemetryArg;
88
+ }
89
+ return options;
90
+ }
91
+ async function getProjectName() {
92
+ const name = await input({
93
+ message: "Enter project name:",
94
+ validate: (value) => {
95
+ return validateProjectName(value) || true;
96
+ },
97
+ });
98
+ return name;
99
+ }
100
+ async function selectLanguage() {
101
+ const language = await select({
102
+ message: "Select your preferred language:",
103
+ choices: [
104
+ {
105
+ name: "TypeScript",
106
+ value: "typescript",
107
+ description: "Recommended - Type safety and better developer experience",
108
+ },
109
+ {
110
+ name: "JavaScript",
111
+ value: "javascript",
112
+ description: "Vanilla JavaScript for simpler projects",
113
+ },
114
+ ],
115
+ });
116
+ return language;
117
+ }
118
+ async function selectTelemetry() {
119
+ const telemetry = await select({
120
+ message: "Select error tracking/telemetry solution:",
121
+ choices: [
122
+ {
123
+ name: "None",
124
+ value: "none",
125
+ description: "No error tracking integration",
126
+ },
127
+ {
128
+ name: "Sentry",
129
+ value: "sentry",
130
+ description: "Full-featured error tracking (free tier available)",
131
+ },
132
+ {
133
+ name: "GlitchTip",
134
+ value: "glitchtip",
135
+ description: "Simple error tracking, can be self-hosted",
136
+ },
137
+ {
138
+ name: "Discord Webhook",
139
+ value: "discord",
140
+ description: "Send error notifications to Discord channel",
141
+ },
142
+ ],
143
+ });
144
+ return telemetry;
145
+ }
146
+ async function createProject(projectName, language, telemetryArg, useDockerArg) {
147
+ const isCurrentDir = projectName === ".";
148
+ const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
149
+ if (!isCurrentDir && existsSync(targetDir)) {
150
+ console.error(`Error: Directory ${projectName} already exists`);
151
+ process.exit(1);
152
+ }
153
+ if (!isCurrentDir) {
154
+ await mkdir(targetDir, { recursive: true });
155
+ }
156
+ let telemetry = telemetryArg || "none";
157
+ if (!telemetryArg) {
158
+ telemetry = await selectTelemetry();
159
+ }
160
+ let useDocker = useDockerArg || false;
161
+ if (!useDockerArg) {
162
+ useDocker = await confirm({
163
+ message: "Do you want to add Docker support?",
164
+ default: false,
165
+ });
166
+ }
167
+ let pkgJson;
168
+ if (language === "typescript") {
169
+ pkgJson = getTypeScriptPackageJson(projectName, telemetry);
170
+ }
171
+ else {
172
+ pkgJson = getJavaScriptPackageJson(projectName, telemetry);
173
+ }
174
+ await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
175
+ if (language === "typescript") {
176
+ await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
177
+ await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
178
+ await writeFile(join(targetDir, "sprint.config.ts"), getSprintConfigFile(language, telemetry));
179
+ }
180
+ else {
181
+ await writeFile(join(targetDir, "sprint.config.js"), getSprintConfigFile(language, telemetry));
182
+ }
183
+ const srcDir = join(targetDir, "src");
184
+ await mkdir(srcDir, { recursive: true });
185
+ await mkdir(join(srcDir, "middlewares"), { recursive: true });
186
+ await mkdir(join(srcDir, "routes"), { recursive: true });
187
+ await mkdir(join(srcDir, "controllers"), { recursive: true });
188
+ await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
189
+ await writeFile(join(srcDir, "controllers", ".gitkeep"), "");
190
+ await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getMainFile(language));
191
+ await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
192
+ await writeFile(join(targetDir, ".env.example"), getEnvExample(telemetry));
193
+ await writeFile(join(targetDir, ".env.development"), getEnvDevelopment(telemetry));
194
+ await writeFile(join(targetDir, ".env.production"), getEnvProduction(telemetry));
195
+ await writeFile(join(targetDir, ".gitignore"), getGitignore());
196
+ if (useDocker) {
197
+ await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
198
+ await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
199
+ await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
200
+ }
201
+ }
@@ -0,0 +1,25 @@
1
+ export function validateProjectName(name) {
2
+ if (!name.trim())
3
+ return "Please enter a project name";
4
+ const n = name.trim();
5
+ if (n !== n.toLowerCase()) {
6
+ return "Project name must be lowercase";
7
+ }
8
+ if (n.length > 214) {
9
+ return "Project name must be less than 214 characters";
10
+ }
11
+ if (n.startsWith("-") || n.startsWith(".")) {
12
+ return "Project name cannot start with - or .";
13
+ }
14
+ if (/[~!@#$%^&*(){}[\]<>?:]/.test(n)) {
15
+ return "Project name cannot contain special characters (only letters, numbers, - and _)";
16
+ }
17
+ if (n !== encodeURIComponent(n)) {
18
+ return "Project name must be URL-safe";
19
+ }
20
+ const reserved = ["node_modules", "favicon.ico"];
21
+ if (reserved.includes(n.toLowerCase())) {
22
+ return `Cannot use "${n}" as project name`;
23
+ }
24
+ return null;
25
+ }
package/package.json CHANGED
@@ -1,37 +1,38 @@
1
1
  {
2
- "name": "create-sprint",
3
- "version": "0.0.2",
4
- "description": "Create a new Sprint API project",
5
- "type": "module",
6
- "bin": {
7
- "create-sprint": "./bin/cli.js",
8
- "sprint": "./bin/cli.js"
9
- },
10
- "files": [
11
- "bin/**/*"
12
- ],
13
- "scripts": {
14
- "build": "tsc && node -e \"const fs=require('fs');const p='bin/cli.js';const c=fs.readFileSync(p,'utf8');fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c)\"",
15
- "prepublishOnly": "npm run build",
16
- "start": "node bin/cli.js"
17
- },
18
- "keywords": [
19
- "sprint",
20
- "api",
21
- "framework",
22
- "init",
23
- "create"
24
- ],
25
- "author": "TPEOficial LLC",
26
- "license": "Apache-2.0",
27
- "dependencies": {
28
- "@inquirer/prompts": "^7.10.1"
29
- },
30
- "devDependencies": {
31
- "tsx": "^4.19.0",
32
- "typescript": "^5.9.3"
33
- },
34
- "engines": {
35
- "node": ">=18.0.0"
36
- }
37
- }
2
+ "name": "create-sprint",
3
+ "version": "0.0.5",
4
+ "description": "Create a new Sprint API project",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-sprint": "./dist/cli.js",
8
+ "sprint": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*",
12
+ "src/**/*"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && node -e \"const fs=require('fs');const p='dist/cli.js';const c=fs.readFileSync(p,'utf8');fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c)\"",
16
+ "prepublishOnly": "npm run build",
17
+ "start": "node dist/cli.js"
18
+ },
19
+ "keywords": [
20
+ "sprint",
21
+ "api",
22
+ "framework",
23
+ "init",
24
+ "create"
25
+ ],
26
+ "author": "TPEOficial LLC",
27
+ "license": "Apache-2.0",
28
+ "dependencies": {
29
+ "@inquirer/prompts": "^7.10.1"
30
+ },
31
+ "devDependencies": {
32
+ "tsx": "^4.19.0",
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ }
38
+ }