stackkit-cli 0.4.3 → 0.4.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.
Files changed (102) hide show
  1. package/README.md +26 -2
  2. package/bin/stackkit.js +10 -1
  3. package/dist/commands/add.js +128 -10
  4. package/dist/commands/doctor.d.ts +11 -0
  5. package/dist/commands/doctor.js +483 -0
  6. package/dist/commands/list.d.ts +1 -1
  7. package/dist/commands/list.js +59 -38
  8. package/dist/index.js +11 -13
  9. package/dist/types/index.d.ts +29 -2
  10. package/dist/utils/config-utils.d.ts +2 -0
  11. package/dist/utils/config-utils.js +88 -0
  12. package/dist/utils/detect.js +12 -2
  13. package/dist/utils/env-editor.d.ts +0 -1
  14. package/dist/utils/env-editor.js +50 -25
  15. package/dist/utils/files.d.ts +8 -0
  16. package/dist/utils/files.js +51 -0
  17. package/dist/utils/js-conversion.d.ts +1 -0
  18. package/dist/utils/js-conversion.js +244 -0
  19. package/dist/utils/module-utils.d.ts +2 -0
  20. package/dist/utils/module-utils.js +461 -0
  21. package/dist/utils/package-manager.js +15 -31
  22. package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
  23. package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
  24. package/modules/auth/authjs/files/lib/auth.ts +41 -0
  25. package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
  26. package/modules/auth/authjs/module.json +95 -0
  27. package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
  28. package/modules/auth/better-auth/files/lib/auth.ts +75 -0
  29. package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
  30. package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
  31. package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
  32. package/modules/auth/better-auth/module.json +191 -0
  33. package/modules/database/mongoose/files/lib/db.ts +68 -0
  34. package/modules/database/mongoose/files/models/User.ts +34 -0
  35. package/modules/database/mongoose/module.json +55 -0
  36. package/modules/database/prisma/files/lib/prisma.ts +4 -0
  37. package/modules/database/prisma/files/prisma/schema.prisma +8 -0
  38. package/modules/database/prisma/files/prisma.config.ts +12 -0
  39. package/modules/database/prisma/module.json +122 -0
  40. package/package.json +1 -1
  41. package/templates/express/.env.example +1 -10
  42. package/templates/express/package.json +15 -21
  43. package/templates/express/src/app.ts +9 -29
  44. package/templates/express/src/config/env.ts +3 -14
  45. package/templates/express/src/features/health/health.controller.ts +18 -0
  46. package/templates/express/src/features/health/health.route.ts +9 -0
  47. package/templates/express/src/features/health/health.service.ts +6 -0
  48. package/templates/express/src/middlewares/error.middleware.ts +2 -2
  49. package/templates/express/src/server.ts +1 -1
  50. package/templates/express/template.json +1 -5
  51. package/templates/express/tsconfig.json +0 -1
  52. package/templates/nextjs/lib/env.ts +8 -0
  53. package/templates/nextjs/package.json +7 -7
  54. package/templates/react-vite/.env.example +1 -2
  55. package/templates/react-vite/.prettierignore +4 -0
  56. package/templates/react-vite/.prettierrc +9 -0
  57. package/templates/react-vite/README.md +22 -0
  58. package/templates/react-vite/package.json +16 -16
  59. package/templates/react-vite/src/router.tsx +0 -12
  60. package/templates/react-vite/vite.config.ts +0 -6
  61. package/dist/commands/init.d.ts +0 -10
  62. package/dist/commands/init.js +0 -157
  63. package/dist/utils/code-inject.d.ts +0 -14
  64. package/dist/utils/code-inject.js +0 -70
  65. package/dist/utils/json-editor.d.ts +0 -8
  66. package/dist/utils/json-editor.js +0 -45
  67. package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
  68. package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
  69. package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
  70. package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
  71. package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
  72. package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +0 -72
  73. package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
  74. package/modules/auth/better-auth-express/module.json +0 -61
  75. package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
  76. package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
  77. package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
  78. package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
  79. package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
  80. package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
  81. package/modules/auth/better-auth-nextjs/module.json +0 -62
  82. package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
  83. package/modules/auth/better-auth-react/module.json +0 -28
  84. package/modules/auth/clerk-express/files/lib/auth.ts +0 -7
  85. package/modules/auth/clerk-express/module.json +0 -34
  86. package/modules/auth/clerk-nextjs/files/lib/auth-provider.tsx +0 -5
  87. package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
  88. package/modules/auth/clerk-nextjs/module.json +0 -64
  89. package/modules/auth/clerk-react/files/lib/auth-provider.tsx +0 -15
  90. package/modules/auth/clerk-react/module.json +0 -28
  91. package/modules/database/mongoose-mongodb/files/lib/db.ts +0 -40
  92. package/modules/database/mongoose-mongodb/module.json +0 -55
  93. package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
  94. package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
  95. package/modules/database/prisma-mongodb/module.json +0 -60
  96. package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
  97. package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
  98. package/modules/database/prisma-postgresql/module.json +0 -60
  99. package/templates/react-vite/src/api/services/user.service.ts +0 -18
  100. package/templates/react-vite/src/pages/UserProfile.tsx +0 -40
  101. package/templates/react-vite/src/types/user.d.ts +0 -6
  102. /package/modules/auth/{better-auth-nextjs → better-auth}/files/api/auth/[...all]/route.ts +0 -0
@@ -0,0 +1,483 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.doctorCommand = doctorCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const logger_1 = require("../utils/logger");
11
+ // Constants for consistent messaging
12
+ const MESSAGES = {
13
+ NO_PACKAGE_JSON: "No package.json found in current directory or any parent directory.",
14
+ UNSUPPORTED_PROJECT: "Unsupported project type. Only Next.js, Express, and React projects are supported.",
15
+ NODE_TOO_OLD: (version) => `Node.js version ${version} is not supported. Minimum required: Node 18.`,
16
+ NODE_WARNING: (version) => `Node.js version ${version} is supported but Node 20+ is recommended.`,
17
+ NODE_SUCCESS: (version) => `Node.js version ${version} is supported.`,
18
+ ENV_EXAMPLE_MISSING: ".env.example file missing (recommended for documentation)",
19
+ ENV_EXAMPLE_FOUND: ".env.example file found",
20
+ ENV_MISSING: "No .env or .env.local file found",
21
+ ENV_FOUND: ".env/.env.local file found",
22
+ PRISMA_SCHEMA_MISSING: "Prisma schema missing (required for Prisma)",
23
+ PRISMA_SCHEMA_FOUND: "Prisma schema found",
24
+ AUTH_ROUTES_MISSING: "Auth routes not found (may need configuration)",
25
+ AUTH_ROUTES_FOUND: "Auth routes configured",
26
+ ENV_VARS_MISSING: (vars) => `Missing: ${vars.join(", ")}`,
27
+ ENV_VARS_PRESENT: (vars) => `Present: ${vars.join(", ")}`,
28
+ };
29
+ /**
30
+ * Main doctor command function that performs health checks on a StackKit project
31
+ * @param options Command line options
32
+ */
33
+ async function doctorCommand(options) {
34
+ try {
35
+ const report = await runDoctorChecks();
36
+ if (options.json) {
37
+ process.stdout.write(JSON.stringify(report, null, 2) + "\n");
38
+ return;
39
+ }
40
+ printDoctorReport(report, options.verbose || false);
41
+ // Exit with proper codes
42
+ const hasErrors = report.summary.errors > 0;
43
+ const hasWarnings = report.summary.warnings > 0;
44
+ const strictMode = options.strict || false;
45
+ if (hasErrors || (strictMode && hasWarnings)) {
46
+ process.exit(1);
47
+ }
48
+ else {
49
+ process.exit(0);
50
+ }
51
+ }
52
+ catch (error) {
53
+ logger_1.logger.error(`Doctor check failed: ${error instanceof Error ? error.message : String(error)}`);
54
+ process.exit(1);
55
+ }
56
+ }
57
+ /**
58
+ * Runs all doctor checks and generates a comprehensive health report
59
+ * @returns Promise resolving to a DoctorReport
60
+ */
61
+ async function runDoctorChecks() {
62
+ const checks = [];
63
+ // Find project root
64
+ const projectRoot = await findProjectRoot();
65
+ checks.push({
66
+ status: "success",
67
+ message: `Found project root: ${projectRoot}`,
68
+ });
69
+ // Read package.json
70
+ const packageJson = await readPackageJson(projectRoot);
71
+ // Detect package manager
72
+ const packageManager = await detectPackageManager(projectRoot);
73
+ // Detect project type
74
+ const projectType = detectProjectType(packageJson);
75
+ if (projectType === "unknown") {
76
+ checks.push({
77
+ status: "error",
78
+ message: MESSAGES.UNSUPPORTED_PROJECT,
79
+ });
80
+ }
81
+ else {
82
+ checks.push({
83
+ status: "success",
84
+ message: `Detected project type: ${projectType}`,
85
+ });
86
+ }
87
+ // Check Node version
88
+ const nodeVersionCheck = checkNodeVersion();
89
+ checks.push(nodeVersionCheck);
90
+ // Detect installed modules
91
+ const authModules = detectAuthModules(packageJson);
92
+ const databaseModules = detectDatabaseModules(packageJson);
93
+ // Check key files
94
+ const filesCheck = await checkKeyFiles(projectRoot, projectType, authModules, databaseModules);
95
+ checks.push(...filesCheck);
96
+ // Check env files
97
+ const envCheck = await checkEnvFiles(projectRoot, authModules, databaseModules);
98
+ checks.push(...envCheck.checks);
99
+ // Check conflicts
100
+ const conflicts = checkConflicts(authModules, databaseModules);
101
+ conflicts.forEach(conflict => {
102
+ checks.push({
103
+ status: "warning",
104
+ message: conflict,
105
+ });
106
+ });
107
+ // Build report
108
+ const report = {
109
+ project: {
110
+ type: projectType,
111
+ root: projectRoot,
112
+ packageManager,
113
+ },
114
+ runtime: {
115
+ nodeVersion: process.version,
116
+ nodeVersionStatus: nodeVersionCheck.status,
117
+ },
118
+ modules: {
119
+ auth: authModules,
120
+ database: databaseModules,
121
+ },
122
+ files: {
123
+ envExample: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example")),
124
+ env: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env")) || await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.local")),
125
+ prismaSchema: databaseModules.includes("prisma") ? await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma")) : undefined,
126
+ authRoutes: authModules.length > 0 ? await checkAuthRoutesExist(projectRoot, projectType) : undefined,
127
+ },
128
+ env: envCheck.envStatus,
129
+ conflicts,
130
+ checks,
131
+ summary: {
132
+ errors: checks.filter(c => c.status === "error").length,
133
+ warnings: checks.filter(c => c.status === "warning").length,
134
+ suggestions: generateSuggestions(),
135
+ },
136
+ };
137
+ return report;
138
+ }
139
+ /**
140
+ * Finds the project root by walking up directories until package.json is found
141
+ * @returns Promise resolving to the absolute path of the project root
142
+ * @throws Error if no package.json is found
143
+ */
144
+ async function findProjectRoot() {
145
+ let currentDir = process.cwd();
146
+ while (currentDir !== path_1.default.dirname(currentDir)) {
147
+ const packageJsonPath = path_1.default.join(currentDir, "package.json");
148
+ if (await fs_extra_1.default.pathExists(packageJsonPath)) {
149
+ return currentDir;
150
+ }
151
+ currentDir = path_1.default.dirname(currentDir);
152
+ }
153
+ throw new Error(MESSAGES.NO_PACKAGE_JSON);
154
+ }
155
+ /**
156
+ * Reads and parses package.json from the project root
157
+ * @param projectRoot Absolute path to the project root
158
+ * @returns Promise resolving to the parsed package.json object
159
+ */
160
+ async function readPackageJson(projectRoot) {
161
+ const packageJsonPath = path_1.default.join(projectRoot, "package.json");
162
+ return await fs_extra_1.default.readJSON(packageJsonPath);
163
+ }
164
+ async function detectPackageManager(projectRoot) {
165
+ const checks = [
166
+ { file: "pnpm-lock.yaml", manager: "pnpm" },
167
+ { file: "yarn.lock", manager: "yarn" },
168
+ { file: "package-lock.json", manager: "npm" },
169
+ ];
170
+ for (const check of checks) {
171
+ if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, check.file))) {
172
+ return check.manager;
173
+ }
174
+ }
175
+ return "npm";
176
+ }
177
+ function detectProjectType(packageJson) {
178
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
179
+ if (deps.next) {
180
+ return "nextjs";
181
+ }
182
+ else if (deps.express) {
183
+ return "express";
184
+ }
185
+ else if (deps.vite && deps.react) {
186
+ return "react-vite";
187
+ }
188
+ else if (deps.react) {
189
+ return "react";
190
+ }
191
+ return "unknown";
192
+ }
193
+ function checkNodeVersion() {
194
+ const version = process.version;
195
+ const majorVersion = parseInt(version.slice(1).split(".")[0]);
196
+ if (majorVersion < 18) {
197
+ return {
198
+ status: "error",
199
+ message: MESSAGES.NODE_TOO_OLD(version),
200
+ };
201
+ }
202
+ else if (majorVersion < 20) {
203
+ return {
204
+ status: "warning",
205
+ message: MESSAGES.NODE_WARNING(version),
206
+ };
207
+ }
208
+ else {
209
+ return {
210
+ status: "success",
211
+ message: MESSAGES.NODE_SUCCESS(version),
212
+ };
213
+ }
214
+ }
215
+ function detectAuthModules(packageJson) {
216
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
217
+ const modules = [];
218
+ if (deps["better-auth"]) {
219
+ modules.push("better-auth");
220
+ }
221
+ if (deps["next-auth"]) {
222
+ modules.push("authjs");
223
+ }
224
+ return modules;
225
+ }
226
+ function detectDatabaseModules(packageJson) {
227
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
228
+ const modules = [];
229
+ if (deps["@prisma/client"] || deps["prisma"]) {
230
+ modules.push("prisma");
231
+ }
232
+ if (deps["mongoose"]) {
233
+ modules.push("mongoose");
234
+ }
235
+ return modules;
236
+ }
237
+ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModules) {
238
+ const checks = [];
239
+ // Check .env.example
240
+ const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
241
+ checks.push({
242
+ status: envExampleExists ? "success" : "warning",
243
+ message: envExampleExists ? ".env.example file found" : ".env.example file missing (recommended for documentation)",
244
+ });
245
+ // Check Prisma schema
246
+ if (databaseModules.includes("prisma")) {
247
+ const schemaExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"));
248
+ checks.push({
249
+ status: schemaExists ? "success" : "error",
250
+ message: schemaExists ? "Prisma schema found" : "Prisma schema missing (required for Prisma)",
251
+ });
252
+ }
253
+ // Check auth routes
254
+ if (authModules.length > 0 && projectType === "nextjs") {
255
+ const authRoutesExist = await checkAuthRoutesExist(projectRoot, projectType);
256
+ checks.push({
257
+ status: authRoutesExist ? "success" : "warning",
258
+ message: authRoutesExist ? "Auth routes configured" : "Auth routes not found (may need configuration)",
259
+ });
260
+ }
261
+ return checks;
262
+ }
263
+ async function checkAuthRoutesExist(projectRoot, projectType) {
264
+ if (projectType !== "nextjs")
265
+ return true; // Skip for non-Next.js
266
+ const possiblePaths = [
267
+ "app/api/auth/[...nextauth]/route.ts",
268
+ "app/api/auth/[...nextauth]/route.js",
269
+ "src/app/api/auth/[...nextauth]/route.ts",
270
+ "src/app/api/auth/[...nextauth]/route.js",
271
+ "pages/api/auth/[...nextauth].ts",
272
+ "pages/api/auth/[...nextauth].js",
273
+ "src/pages/api/auth/[...nextauth].ts",
274
+ "src/pages/api/auth/[...nextauth].js",
275
+ ];
276
+ for (const routePath of possiblePaths) {
277
+ if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, routePath))) {
278
+ return true;
279
+ }
280
+ }
281
+ return false;
282
+ }
283
+ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
284
+ const checks = [];
285
+ const requiredKeys = [];
286
+ const missing = [];
287
+ const present = [];
288
+ // Determine required env keys
289
+ if (databaseModules.includes("prisma")) {
290
+ requiredKeys.push("DATABASE_URL");
291
+ }
292
+ if (authModules.includes("authjs")) {
293
+ requiredKeys.push("NEXTAUTH_SECRET", "NEXTAUTH_URL");
294
+ }
295
+ if (authModules.includes("better-auth")) {
296
+ requiredKeys.push("BETTER_AUTH_SECRET", "BETTER_AUTH_URL");
297
+ }
298
+ // Check env files
299
+ const envPaths = [".env", ".env.local"];
300
+ let envContent = "";
301
+ for (const envPath of envPaths) {
302
+ const fullPath = path_1.default.join(projectRoot, envPath);
303
+ if (await fs_extra_1.default.pathExists(fullPath)) {
304
+ envContent = await fs_extra_1.default.readFile(fullPath, "utf-8");
305
+ break;
306
+ }
307
+ }
308
+ if (!envContent) {
309
+ checks.push({
310
+ status: "warning",
311
+ message: "No .env or .env.local file found",
312
+ });
313
+ return { checks, envStatus: { missing: requiredKeys, present: [] } };
314
+ }
315
+ // Parse env content
316
+ const envLines = envContent.split("\n").map(line => line.trim()).filter(line => line && !line.startsWith("#"));
317
+ const envVars = new Set(envLines.map(line => line.split("=")[0]));
318
+ for (const key of requiredKeys) {
319
+ if (envVars.has(key)) {
320
+ present.push(key);
321
+ }
322
+ else {
323
+ missing.push(key);
324
+ }
325
+ }
326
+ if (missing.length > 0) {
327
+ checks.push({
328
+ status: "error",
329
+ message: `Missing required environment variables: ${missing.join(", ")}`,
330
+ });
331
+ }
332
+ else if (requiredKeys.length > 0) {
333
+ checks.push({
334
+ status: "success",
335
+ message: "All required environment variables are present",
336
+ });
337
+ }
338
+ return { checks, envStatus: { missing, present } };
339
+ }
340
+ function checkConflicts(authModules, databaseModules) {
341
+ const conflicts = [];
342
+ if (authModules.length > 1) {
343
+ conflicts.push(`Multiple auth providers detected: ${authModules.join(", ")}. Consider using only one.`);
344
+ }
345
+ if (databaseModules.length > 1) {
346
+ conflicts.push(`Multiple database providers detected: ${databaseModules.join(", ")}. Consider using only one.`);
347
+ }
348
+ return conflicts;
349
+ }
350
+ function generateSuggestions() {
351
+ const suggestions = [];
352
+ // Always show available commands
353
+ suggestions.push("stackkit add auth - Add authentication module");
354
+ suggestions.push("stackkit add db - Add database module");
355
+ suggestions.push("stackkit list - View available modules");
356
+ return suggestions;
357
+ }
358
+ /**
359
+ * Prints the doctor report to the console with appropriate formatting
360
+ * @param report The doctor report to print
361
+ * @param verbose Whether to show detailed check information
362
+ */
363
+ function printDoctorReport(report, verbose) {
364
+ logger_1.logger.header("🔍 StackKit Doctor Report");
365
+ logger_1.logger.newLine();
366
+ // Project info
367
+ logger_1.logger.log(chalk_1.default.bold("Project"));
368
+ logger_1.logger.log(` Type: ${report.project.type}`);
369
+ logger_1.logger.log(` Root: ${report.project.root}`);
370
+ logger_1.logger.log(` Package Manager: ${report.project.packageManager}`);
371
+ logger_1.logger.newLine();
372
+ // Runtime
373
+ logger_1.logger.log(chalk_1.default.bold("Runtime"));
374
+ if (report.runtime.nodeVersionStatus === "success") {
375
+ logger_1.logger.success(`Node.js: ${report.runtime.nodeVersion}`);
376
+ }
377
+ else if (report.runtime.nodeVersionStatus === "warning") {
378
+ logger_1.logger.warn(`Node.js: ${report.runtime.nodeVersion}`);
379
+ }
380
+ else {
381
+ logger_1.logger.error(`Node.js: ${report.runtime.nodeVersion}`);
382
+ }
383
+ logger_1.logger.newLine();
384
+ // Modules
385
+ logger_1.logger.log(chalk_1.default.bold("Modules"));
386
+ if (report.modules.auth.length > 0) {
387
+ logger_1.logger.log(` Auth: ${report.modules.auth.join(", ")}`);
388
+ }
389
+ else {
390
+ logger_1.logger.info("Auth: None");
391
+ }
392
+ if (report.modules.database.length > 0) {
393
+ logger_1.logger.log(` Database: ${report.modules.database.join(", ")}`);
394
+ }
395
+ else {
396
+ logger_1.logger.info("Database: None");
397
+ }
398
+ logger_1.logger.newLine();
399
+ // Files
400
+ logger_1.logger.log(chalk_1.default.bold("Files"));
401
+ if (report.files.envExample) {
402
+ logger_1.logger.success(MESSAGES.ENV_EXAMPLE_FOUND);
403
+ }
404
+ else {
405
+ logger_1.logger.warn(MESSAGES.ENV_EXAMPLE_MISSING);
406
+ logger_1.logger.log(" Hint: Helps other developers understand required environment variables");
407
+ }
408
+ if (report.files.env) {
409
+ logger_1.logger.success(MESSAGES.ENV_FOUND);
410
+ }
411
+ else {
412
+ logger_1.logger.warn(MESSAGES.ENV_MISSING);
413
+ logger_1.logger.log(" Hint: Required for local development and production deployment");
414
+ }
415
+ if (report.files.prismaSchema !== undefined) {
416
+ if (report.files.prismaSchema) {
417
+ logger_1.logger.success(MESSAGES.PRISMA_SCHEMA_FOUND);
418
+ }
419
+ else {
420
+ logger_1.logger.error(MESSAGES.PRISMA_SCHEMA_MISSING);
421
+ logger_1.logger.log(" Hint: Defines your database schema and is required for Prisma to work");
422
+ }
423
+ }
424
+ if (report.files.authRoutes !== undefined) {
425
+ if (report.files.authRoutes) {
426
+ logger_1.logger.success(MESSAGES.AUTH_ROUTES_FOUND);
427
+ }
428
+ else {
429
+ logger_1.logger.warn(MESSAGES.AUTH_ROUTES_MISSING);
430
+ logger_1.logger.log(" Hint: Authentication routes handle login/logout flows");
431
+ }
432
+ }
433
+ logger_1.logger.newLine();
434
+ // Environment
435
+ if (report.env.missing.length > 0 || report.env.present.length > 0) {
436
+ logger_1.logger.log(chalk_1.default.bold("Environment Variables"));
437
+ if (report.env.present.length > 0) {
438
+ logger_1.logger.success(MESSAGES.ENV_VARS_PRESENT(report.env.present));
439
+ }
440
+ if (report.env.missing.length > 0) {
441
+ logger_1.logger.error(MESSAGES.ENV_VARS_MISSING(report.env.missing));
442
+ }
443
+ logger_1.logger.newLine();
444
+ }
445
+ // Conflicts
446
+ if (report.conflicts.length > 0) {
447
+ logger_1.logger.log(chalk_1.default.bold("Conflicts"));
448
+ report.conflicts.forEach(conflict => {
449
+ logger_1.logger.warn(conflict);
450
+ });
451
+ logger_1.logger.newLine();
452
+ }
453
+ // Detailed checks if verbose
454
+ if (verbose) {
455
+ logger_1.logger.log(chalk_1.default.bold("Detailed Checks"));
456
+ report.checks.forEach(check => {
457
+ if (check.status === "success") {
458
+ logger_1.logger.success(check.message);
459
+ }
460
+ else if (check.status === "warning") {
461
+ logger_1.logger.warn(check.message);
462
+ }
463
+ else {
464
+ logger_1.logger.error(check.message);
465
+ }
466
+ if (check.details) {
467
+ logger_1.logger.log(` ${chalk_1.default.gray(check.details)}`);
468
+ }
469
+ });
470
+ logger_1.logger.newLine();
471
+ }
472
+ // Summary
473
+ logger_1.logger.log(chalk_1.default.bold("Summary"));
474
+ logger_1.logger.log(` Errors: ${report.summary.errors}`);
475
+ logger_1.logger.log(` Warnings: ${report.summary.warnings}`);
476
+ logger_1.logger.newLine();
477
+ if (report.summary.suggestions.length > 0) {
478
+ logger_1.logger.log(chalk_1.default.bold("Suggestions"));
479
+ report.summary.suggestions.forEach(suggestion => {
480
+ logger_1.logger.log(` • ${suggestion}`);
481
+ });
482
+ }
483
+ }
@@ -1,5 +1,5 @@
1
1
  interface ListOptions {
2
- templates?: boolean;
2
+ frameworks?: boolean;
3
3
  modules?: boolean;
4
4
  }
5
5
  export declare function listCommand(options: ListOptions): Promise<void>;
@@ -9,37 +9,35 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const logger_1 = require("../utils/logger");
11
11
  async function listCommand(options) {
12
- const showTemplates = !options.modules || options.templates;
13
- const showModules = !options.templates || options.modules;
12
+ const showFrameworks = !options.modules || options.frameworks;
13
+ const showModules = !options.frameworks || options.modules;
14
14
  try {
15
+ logger_1.logger.header("StackKit Resources");
15
16
  logger_1.logger.newLine();
16
- // List templates
17
- if (showTemplates) {
17
+ let hasFrameworks = false;
18
+ let hasModules = false;
19
+ // List frameworks
20
+ if (showFrameworks) {
18
21
  const templatesDir = path_1.default.join(__dirname, "..", "..", "templates");
19
- const templates = await getAvailableTemplates(templatesDir);
20
- logger_1.logger.log(chalk_1.default.bold.cyan("▸ TEMPLATES") + chalk_1.default.gray(` (${templates.length})`));
21
- logger_1.logger.newLine();
22
- if (templates.length === 0) {
23
- logger_1.logger.log(chalk_1.default.dim(" No templates available"));
24
- }
25
- else {
26
- templates.forEach((template) => {
27
- logger_1.logger.log(` ${chalk_1.default.cyan("•")} ${template.displayName}`);
22
+ const frameworks = await getAvailableFrameworks(templatesDir);
23
+ if (frameworks.length > 0) {
24
+ hasFrameworks = true;
25
+ logger_1.logger.log(chalk_1.default.bold.blue("FRAMEWORKS"));
26
+ frameworks.forEach((framework, index) => {
27
+ const isLast = index === frameworks.length - 1;
28
+ const prefix = isLast ? "└──" : "├──";
29
+ logger_1.logger.log(` ${chalk_1.default.gray(prefix)} ${chalk_1.default.cyan(framework.displayName)}`);
28
30
  });
31
+ logger_1.logger.newLine();
29
32
  }
30
- logger_1.logger.newLine();
31
33
  }
32
34
  // List modules
33
35
  if (showModules) {
34
36
  const modulesDir = path_1.default.join(__dirname, "..", "..", "modules");
35
37
  const modules = await getAvailableModules(modulesDir);
36
- logger_1.logger.log(chalk_1.default.bold.cyan("▸ MODULES") + chalk_1.default.gray(` (${modules.length})`));
37
- if (modules.length === 0) {
38
- logger_1.logger.newLine();
39
- logger_1.logger.log(chalk_1.default.dim(" No modules available"));
40
- logger_1.logger.newLine();
41
- }
42
- else {
38
+ if (modules.length > 0) {
39
+ hasModules = true;
40
+ logger_1.logger.log(chalk_1.default.bold.magenta("MODULES"));
43
41
  // Group by category
44
42
  const grouped = modules.reduce((acc, mod) => {
45
43
  if (!acc[mod.category]) {
@@ -48,36 +46,59 @@ async function listCommand(options) {
48
46
  acc[mod.category].push(mod);
49
47
  return acc;
50
48
  }, {});
51
- for (const [category, mods] of Object.entries(grouped)) {
52
- logger_1.logger.newLine();
53
- logger_1.logger.log(` ${chalk_1.default.yellow("→")} ${chalk_1.default.bold.yellow(category.toUpperCase())} ${chalk_1.default.dim(`(${mods.length})`)}`);
54
- mods.forEach((mod) => {
55
- logger_1.logger.log(` ${chalk_1.default.cyan("")} ${mod.displayName}`);
49
+ const categories = Object.keys(grouped);
50
+ categories.forEach((category, categoryIndex) => {
51
+ const mods = grouped[category];
52
+ const isLastCategory = categoryIndex === categories.length - 1;
53
+ const categoryPrefix = isLastCategory ? "└──" : "├──";
54
+ logger_1.logger.log(` ${chalk_1.default.gray(categoryPrefix)} ${chalk_1.default.yellow(formatCategoryName(category))} ${chalk_1.default.dim(`(${mods.length})`)}`);
55
+ mods.forEach((mod, modIndex) => {
56
+ const isLastMod = modIndex === mods.length - 1;
57
+ const modPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
58
+ logger_1.logger.log(` ${chalk_1.default.gray(modPrefix)} ${chalk_1.default.green(mod.displayName)}`);
59
+ // Show additional details for database modules
60
+ if (mod.category === "database" && mod.name === "prisma") {
61
+ const providerPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
62
+ logger_1.logger.log(` ${chalk_1.default.gray(providerPrefix)} ${chalk_1.default.dim("Providers: PostgreSQL, MongoDB, MySQL, SQLite")}`);
63
+ }
56
64
  });
57
- }
65
+ });
58
66
  logger_1.logger.newLine();
59
67
  }
60
68
  }
69
+ if (!hasFrameworks && !hasModules) {
70
+ logger_1.logger.log(chalk_1.default.dim("No resources available"));
71
+ logger_1.logger.newLine();
72
+ }
73
+ logger_1.logger.log(chalk_1.default.dim("Use 'stackkit add <module>' to add modules to your project"));
74
+ logger_1.logger.newLine();
61
75
  }
62
76
  catch (error) {
63
77
  logger_1.logger.error(`Failed to list resources: ${error.message}`);
64
78
  process.exit(1);
65
79
  }
66
80
  }
67
- async function getAvailableTemplates(templatesDir) {
81
+ async function getAvailableFrameworks(templatesDir) {
68
82
  if (!(await fs_extra_1.default.pathExists(templatesDir))) {
69
83
  return [];
70
84
  }
71
- const templateDirs = await fs_extra_1.default.readdir(templatesDir);
72
- const templates = [];
73
- for (const dir of templateDirs) {
74
- const metadataPath = path_1.default.join(templatesDir, dir, "template.json");
75
- if (await fs_extra_1.default.pathExists(metadataPath)) {
76
- const metadata = await fs_extra_1.default.readJSON(metadataPath);
77
- templates.push(metadata);
78
- }
79
- }
80
- return templates;
85
+ const frameworkDirs = await fs_extra_1.default.readdir(templatesDir);
86
+ const frameworks = frameworkDirs
87
+ .filter((dir) => dir !== "node_modules" && dir !== ".git")
88
+ .map((dir) => ({
89
+ name: dir,
90
+ displayName: formatFrameworkName(dir),
91
+ }));
92
+ return frameworks;
93
+ }
94
+ function formatFrameworkName(name) {
95
+ return name
96
+ .split("-")
97
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
98
+ .join(" ");
99
+ }
100
+ function formatCategoryName(name) {
101
+ return name.charAt(0).toUpperCase() + name.slice(1);
81
102
  }
82
103
  async function getAvailableModules(modulesDir) {
83
104
  if (!(await fs_extra_1.default.pathExists(modulesDir))) {
package/dist/index.js CHANGED
@@ -7,28 +7,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const commander_1 = require("commander");
9
9
  const add_1 = require("./commands/add");
10
- const init_1 = require("./commands/init");
10
+ const doctor_1 = require("./commands/doctor");
11
11
  const list_1 = require("./commands/list");
12
12
  const program = new commander_1.Command();
13
13
  program
14
14
  .name("stackkit")
15
15
  .description("Production-ready project generator and module CLI")
16
16
  .version("0.3.2");
17
- // Init command
18
- program
19
- .command("init [project-name]")
20
- .description("Create a new project from a template")
21
- .option("-t, --template <template>", "Template to use")
22
- .option("--pm <pm>", "Package manager to use (npm, yarn, pnpm, bun)")
23
- .option("--no-install", "Skip installing dependencies")
24
- .option("--no-git", "Skip git initialization")
25
- .option("-y, --yes", "Skip prompts and use defaults")
26
- .action(init_1.initCommand);
27
17
  // List command
28
18
  program
29
19
  .command("list")
30
- .description("List available templates and modules")
31
- .option("-t, --templates", "List only templates")
20
+ .description("List available frameworks and modules")
21
+ .option("-f, --frameworks", "List only frameworks")
32
22
  .option("-m, --modules", "List only modules")
33
23
  .action(list_1.listCommand);
34
24
  // Add command
@@ -40,6 +30,14 @@ program
40
30
  .option("--dry-run", "Show what would be changed without making changes")
41
31
  .option("--no-install", "Skip installing dependencies")
42
32
  .action(add_1.addCommand);
33
+ // Doctor command
34
+ program
35
+ .command("doctor")
36
+ .description("Check project health and compatibility with StackKit modules")
37
+ .option("--json", "Output results in JSON format")
38
+ .option("--verbose", "Show detailed information")
39
+ .option("--strict", "Treat warnings as errors")
40
+ .action(doctor_1.doctorCommand);
43
41
  // Error handling
44
42
  program.on("command:*", () => {
45
43
  // Use logger for error and info