kaven-cli 0.1.0-alpha.1 → 0.3.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 (50) hide show
  1. package/README.md +284 -45
  2. package/README.pt-BR.md +334 -0
  3. package/dist/commands/auth/login.js +97 -19
  4. package/dist/commands/auth/logout.js +4 -6
  5. package/dist/commands/auth/whoami.js +12 -11
  6. package/dist/commands/cache/index.js +43 -0
  7. package/dist/commands/config/index.js +128 -0
  8. package/dist/commands/init/index.js +209 -0
  9. package/dist/commands/init-ci/index.js +153 -0
  10. package/dist/commands/license/index.js +10 -0
  11. package/dist/commands/license/status.js +44 -0
  12. package/dist/commands/license/tier-table.js +46 -0
  13. package/dist/commands/marketplace/browse.js +219 -0
  14. package/dist/commands/marketplace/install.js +233 -29
  15. package/dist/commands/marketplace/list.js +94 -16
  16. package/dist/commands/module/doctor.js +143 -38
  17. package/dist/commands/module/publish.js +291 -0
  18. package/dist/commands/upgrade/check.js +162 -0
  19. package/dist/commands/upgrade/index.js +218 -0
  20. package/dist/core/AuthService.js +207 -14
  21. package/dist/core/CacheManager.js +151 -0
  22. package/dist/core/ConfigManager.js +165 -0
  23. package/dist/core/EnvManager.js +196 -0
  24. package/dist/core/ErrorRecovery.js +191 -0
  25. package/dist/core/LicenseService.js +118 -0
  26. package/dist/core/ModuleDoctor.js +290 -4
  27. package/dist/core/ModuleInstaller.js +136 -2
  28. package/dist/core/ProjectInitializer.js +154 -0
  29. package/dist/core/RegistryResolver.js +94 -0
  30. package/dist/core/ScriptRunner.js +72 -0
  31. package/dist/core/SignatureVerifier.js +75 -0
  32. package/dist/index.js +265 -20
  33. package/dist/infrastructure/MarketplaceClient.js +388 -64
  34. package/dist/infrastructure/errors.js +61 -0
  35. package/dist/types/auth.js +2 -0
  36. package/dist/types/marketplace.js +2 -0
  37. package/package.json +23 -4
  38. package/dist/commands/modules/add.js +0 -53
  39. package/dist/commands/modules/list.js +0 -40
  40. package/dist/commands/modules/remove.js +0 -54
  41. package/dist/core/api/KavenApiClient.js +0 -61
  42. package/dist/core/auth/AuthManager.js +0 -91
  43. package/dist/core/modules/Injector.js +0 -86
  44. package/dist/core/modules/ModuleInstaller.js +0 -63
  45. package/dist/core/modules/ModuleManager.js +0 -59
  46. package/dist/core/modules/ModuleRemover.js +0 -60
  47. package/dist/lib/config.js +0 -66
  48. package/dist/lib/errors.js +0 -32
  49. package/dist/lib/logger.js +0 -70
  50. package/dist/types/module.js +0 -49
@@ -6,39 +6,117 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.authLogin = authLogin;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const ora_1 = __importDefault(require("ora"));
9
+ const open_1 = __importDefault(require("open"));
9
10
  const AuthService_1 = require("../../core/AuthService");
11
+ const MarketplaceClient_1 = require("../../infrastructure/MarketplaceClient");
10
12
  const TelemetryBuffer_1 = require("../../infrastructure/TelemetryBuffer");
13
+ /**
14
+ * Sleep helper for polling delays
15
+ */
16
+ function sleep(ms) {
17
+ return new Promise((resolve) => setTimeout(resolve, ms));
18
+ }
19
+ /**
20
+ * Poll for access token with exponential backoff
21
+ */
22
+ async function pollForToken(client, deviceCode, expiresIn, initialInterval, spinner) {
23
+ const deadline = Date.now() + expiresIn * 1000;
24
+ const backoffIntervals = [5, 10, 15, 20]; // seconds - exponential backoff
25
+ let backoffIndex = 0;
26
+ let interval = initialInterval;
27
+ while (Date.now() < deadline) {
28
+ // Update countdown spinner
29
+ const remaining = Math.ceil((deadline - Date.now()) / 1000);
30
+ const mm = Math.floor(remaining / 60);
31
+ const ss = String(remaining % 60).padStart(2, '0');
32
+ spinner.text = `Waiting for authorization... (expires in ${mm}:${ss})`;
33
+ // Wait for next poll
34
+ await sleep(interval * 1000);
35
+ try {
36
+ const result = await client.pollDeviceToken(deviceCode);
37
+ // Success - return tokens
38
+ if (result.status === 'success' && result.tokens) {
39
+ return result.tokens;
40
+ }
41
+ // Handle different status codes
42
+ switch (result.status) {
43
+ case 'slow_down':
44
+ // Increase interval by 5s as requested by server
45
+ interval += 5;
46
+ break;
47
+ case 'access_denied':
48
+ throw new Error('Authorization denied by user. Try again with \'kaven auth login\'.');
49
+ case 'expired_token':
50
+ throw new Error('Device code expired. Run \'kaven auth login\' again.');
51
+ case 'authorization_pending':
52
+ // Continue polling - apply exponential backoff
53
+ if (backoffIndex < backoffIntervals.length - 1) {
54
+ backoffIndex++;
55
+ interval = backoffIntervals[backoffIndex];
56
+ }
57
+ break;
58
+ }
59
+ }
60
+ catch (error) {
61
+ // Re-throw our custom errors
62
+ if (error.message.includes('denied') ||
63
+ error.message.includes('expired')) {
64
+ throw error;
65
+ }
66
+ // Network errors
67
+ const nodeError = error;
68
+ if (nodeError.code === 'ECONNREFUSED' || nodeError.code === 'ENOTFOUND') {
69
+ throw new Error('Network error. Check your connection and try again.');
70
+ }
71
+ // Unknown error
72
+ throw error;
73
+ }
74
+ }
75
+ // Timeout - device code expired
76
+ throw new Error('Device code expired. Run \'kaven auth login\' again.');
77
+ }
78
+ /**
79
+ * Main login command - OAuth 2.0 Device Authorization Grant (RFC 8628)
80
+ */
11
81
  async function authLogin() {
12
82
  const telemetry = TelemetryBuffer_1.TelemetryBuffer.getInstance();
13
83
  const startTime = Date.now();
14
84
  telemetry.capture("cli.auth.login.start");
85
+ const client = new MarketplaceClient_1.MarketplaceClient();
15
86
  const authService = new AuthService_1.AuthService();
16
- console.log(chalk_1.default.blue("🔐 Iniciando fluxo de autenticação...\n"));
17
- const spinner = (0, ora_1.default)("Gerando código de dispositivo...").start();
87
+ console.log(chalk_1.default.blue("🔐 Starting authentication flow...\n"));
88
+ const spinner = (0, ora_1.default)("Requesting device code from marketplace...").start();
18
89
  try {
19
- // Simulando atraso da rede
20
- await new Promise((resolve) => setTimeout(resolve, 1000));
21
- const deviceCode = "KAVEN-777-BINGO";
22
- const verificationUrl = "https://kaven.sh/device";
90
+ // Step 1: Request device code
91
+ const { device_code, user_code, verification_uri, expires_in, interval } = await client.requestDeviceCode();
23
92
  spinner.stop();
24
- console.log(chalk_1.default.yellow("Para continuar o login, acesse a URL abaixo e insira o código:"));
25
- console.log(chalk_1.default.bold(`\n URL: ${verificationUrl}`));
26
- console.log(chalk_1.default.bold(` Código: ${deviceCode}\n`));
27
- const pollSpinner = (0, ora_1.default)("Aguardando autorização no navegador...").start();
28
- // Simular polling bem-sucedido após 3 segundos
29
- await new Promise((resolve) => setTimeout(resolve, 3000));
30
- const mockToken = "jwt-mock-token-for-kaven-explorador";
31
- await authService.storeToken(mockToken);
32
- pollSpinner.succeed(chalk_1.default.green("Autenticação realizada com sucesso!"));
33
- console.log(chalk_1.default.gray("\nSeu token foi salvo com segurança em ~/.kaven/auth.json"));
34
- telemetry.capture("cli.auth.login.success", {}, Date.now() - startTime);
93
+ // Step 2: Display code and open browser
94
+ console.log(chalk_1.default.yellow("To complete login, follow these steps:\n"));
95
+ console.log(chalk_1.default.bold(` Your verification code: ${chalk_1.default.cyan(user_code)}\n`));
96
+ try {
97
+ await (0, open_1.default)(verification_uri);
98
+ console.log(chalk_1.default.dim(" ✓ Browser opened automatically"));
99
+ }
100
+ catch {
101
+ console.log(chalk_1.default.yellow(` Open this URL in your browser:`));
102
+ console.log(chalk_1.default.underline(` ${verification_uri}\n`));
103
+ }
104
+ // Step 3: Poll for token with exponential backoff
105
+ const pollSpinner = (0, ora_1.default)('Waiting for authorization...').start();
106
+ const tokens = await pollForToken(client, device_code, expires_in, interval, pollSpinner);
107
+ // Step 4: Store tokens securely
108
+ await authService.saveTokens(tokens);
109
+ pollSpinner.succeed(chalk_1.default.green(`Logged in as ${chalk_1.default.bold(tokens.user.email)}`));
110
+ console.log(chalk_1.default.dim(` Tier: ${tokens.user.tier}`));
111
+ console.log(chalk_1.default.gray("\n Your credentials were saved securely in ~/.kaven/auth.json\n"));
112
+ telemetry.capture("cli.auth.login.success", { tier: tokens.user.tier }, Date.now() - startTime);
35
113
  await telemetry.flush();
36
114
  }
37
115
  catch (error) {
38
116
  telemetry.capture("cli.auth.login.error", { error: error.message }, Date.now() - startTime);
39
117
  await telemetry.flush();
40
- spinner.fail(chalk_1.default.red("Erro ao realizar login."));
41
- console.error(error);
118
+ spinner.fail(chalk_1.default.red("Authentication failed"));
119
+ console.error(chalk_1.default.red(`\n Error: ${error.message}\n`));
42
120
  process.exit(1);
43
121
  }
44
122
  }
@@ -5,21 +5,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.authLogout = authLogout;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
- const ora_1 = __importDefault(require("ora"));
9
8
  const AuthService_1 = require("../../core/AuthService");
10
9
  async function authLogout() {
11
10
  const authService = new AuthService_1.AuthService();
12
- const spinner = (0, ora_1.default)("Encerrando sessão...").start();
13
11
  try {
14
12
  if (!(await authService.isAuthenticated())) {
15
- spinner.info(chalk_1.default.yellow("Você não está autenticado."));
13
+ console.log(chalk_1.default.yellow("You are not authenticated."));
16
14
  return;
17
15
  }
18
- await authService.clearToken();
19
- spinner.succeed(chalk_1.default.green("Sessão encerrada com sucesso!"));
16
+ await authService.logout();
17
+ console.log(chalk_1.default.green("Logged out successfully."));
20
18
  }
21
19
  catch {
22
- spinner.fail(chalk_1.default.red("Erro ao realizar logout."));
20
+ console.error(chalk_1.default.red("Error during logout."));
23
21
  process.exit(1);
24
22
  }
25
23
  }
@@ -12,24 +12,25 @@ async function authWhoami() {
12
12
  telemetry.capture("cli.auth.whoami.start");
13
13
  const authService = new AuthService_1.AuthService();
14
14
  try {
15
- const userInfo = await authService.getUserInfo();
16
- if (!userInfo) {
17
- console.log(chalk_1.default.yellow("Você não está autenticado."));
18
- console.log(chalk_1.default.gray("Use 'kaven auth login' para entrar."));
15
+ const info = await authService.getWhoamiInfo();
16
+ if (!info) {
17
+ console.log(chalk_1.default.yellow("You are not authenticated."));
18
+ console.log(chalk_1.default.gray("Use 'kaven auth login' to sign in."));
19
19
  telemetry.capture("cli.auth.whoami.not_authenticated");
20
20
  await telemetry.flush();
21
21
  return;
22
22
  }
23
23
  telemetry.capture("cli.auth.whoami.authenticated");
24
- console.log(chalk_1.default.blue("👤 Usuário logado:"));
25
- console.log(`${chalk_1.default.bold("ID:")} ${userInfo.id}`);
26
- console.log(`${chalk_1.default.bold("E-mail:")} ${userInfo.email}`);
27
- if (userInfo.name) {
28
- console.log(`${chalk_1.default.bold("Nome:")} ${userInfo.name}`);
29
- }
24
+ console.log();
25
+ console.log(` ${chalk_1.default.bold("Email:")} ${info.email}`);
26
+ console.log(` ${chalk_1.default.bold("GitHub:")} ${info.githubId}`);
27
+ console.log(` ${chalk_1.default.bold("Tier:")} ${info.tier}`);
28
+ console.log(` ${chalk_1.default.bold("Session:")} ${info.sessionExpiry}`);
29
+ console.log();
30
30
  }
31
31
  catch {
32
- console.error(chalk_1.default.red("Erro ao verificar status de autenticação."));
32
+ console.error(chalk_1.default.red("Error checking authentication status."));
33
33
  process.exit(1);
34
34
  }
35
+ await telemetry.flush();
35
36
  }
@@ -0,0 +1,43 @@
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.cacheStatus = cacheStatus;
7
+ exports.cacheClear = cacheClear;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const CacheManager_1 = require("../../core/CacheManager");
10
+ function formatBytes(bytes) {
11
+ if (bytes === 0)
12
+ return "0 B";
13
+ const k = 1024;
14
+ const sizes = ["B", "KB", "MB", "GB"];
15
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
16
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
17
+ }
18
+ async function cacheStatus() {
19
+ const manager = (0, CacheManager_1.getCacheManager)();
20
+ const stats = await manager.stats();
21
+ console.log(chalk_1.default.bold("\nKaven CLI Cache Status\n"));
22
+ console.log(` Cache directory: ${chalk_1.default.cyan(manager.cacheDir)}`);
23
+ console.log(` Total size: ${chalk_1.default.cyan(formatBytes(stats.totalSize))}`);
24
+ console.log(` Cached entries: ${chalk_1.default.cyan(stats.entries.toString())}`);
25
+ if (stats.oldest) {
26
+ console.log(` Oldest entry: ${chalk_1.default.gray(stats.oldest.toLocaleString())}`);
27
+ }
28
+ if (stats.newest) {
29
+ console.log(` Newest entry: ${chalk_1.default.gray(stats.newest.toLocaleString())}`);
30
+ }
31
+ console.log();
32
+ console.log(chalk_1.default.gray("Run 'kaven cache clear' to remove all cached data."));
33
+ }
34
+ async function cacheClear() {
35
+ const manager = (0, CacheManager_1.getCacheManager)();
36
+ const stats = await manager.stats();
37
+ if (stats.entries === 0) {
38
+ console.log(chalk_1.default.gray("Cache is already empty."));
39
+ return;
40
+ }
41
+ await manager.clear();
42
+ console.log(chalk_1.default.green(`Cache cleared: ${stats.entries} entries (${formatBytes(stats.totalSize)}) removed.`));
43
+ }
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.configSet = configSet;
40
+ exports.configGet = configGet;
41
+ exports.configView = configView;
42
+ exports.configReset = configReset;
43
+ const chalk_1 = __importDefault(require("chalk"));
44
+ const ConfigManager_1 = require("../../core/ConfigManager");
45
+ /**
46
+ * C2.4: Config set — Persist value to ~/.kaven/config.json
47
+ */
48
+ async function configSet(key, value) {
49
+ if (!key || !value) {
50
+ console.error(chalk_1.default.red("Error: Both key and value are required"));
51
+ console.error(chalk_1.default.gray("Usage: kaven config set KEY VALUE"));
52
+ process.exit(1);
53
+ }
54
+ await ConfigManager_1.configManager.initialize();
55
+ try {
56
+ await ConfigManager_1.configManager.set(key, value);
57
+ console.log(chalk_1.default.green(`✅ Set ${chalk_1.default.bold(key)} = ${chalk_1.default.bold(value)}`));
58
+ }
59
+ catch (error) {
60
+ console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
61
+ process.exit(1);
62
+ }
63
+ }
64
+ /**
65
+ * C2.4: Config get — Read with defaults
66
+ */
67
+ async function configGet(key, options) {
68
+ if (!key) {
69
+ console.error(chalk_1.default.red("Error: Key is required"));
70
+ console.error(chalk_1.default.gray("Usage: kaven config get KEY"));
71
+ process.exit(1);
72
+ }
73
+ await ConfigManager_1.configManager.initialize();
74
+ try {
75
+ const value = ConfigManager_1.configManager.get(key);
76
+ if (options.json) {
77
+ console.log(JSON.stringify({ [key]: value }, null, 2));
78
+ }
79
+ else {
80
+ console.log(value);
81
+ }
82
+ }
83
+ catch (error) {
84
+ console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
85
+ process.exit(1);
86
+ }
87
+ }
88
+ /**
89
+ * C2.4: Config view — Show all configuration
90
+ */
91
+ async function configView(options) {
92
+ await ConfigManager_1.configManager.initialize();
93
+ const config = ConfigManager_1.configManager.getAll();
94
+ if (options.json) {
95
+ console.log(JSON.stringify(config, null, 2));
96
+ }
97
+ else {
98
+ console.log(chalk_1.default.bold("Kaven Configuration:"));
99
+ console.log(chalk_1.default.gray(`Location: ${ConfigManager_1.configManager.getConfigDir()}/config.json`));
100
+ console.log();
101
+ const entries = Object.entries(config);
102
+ const maxKeyLen = Math.max(...entries.map(([k]) => k.length));
103
+ for (const [key, value] of entries) {
104
+ const padding = " ".repeat(maxKeyLen - key.length);
105
+ const displayValue = typeof value === "object"
106
+ ? JSON.stringify(value, null, 2).replace(/\n/g, "\n" + " ".repeat(maxKeyLen + 3))
107
+ : value;
108
+ console.log(` ${key}${padding} ${chalk_1.default.cyan(displayValue)}`);
109
+ }
110
+ }
111
+ }
112
+ /**
113
+ * C2.4: Config reset — Reset to defaults
114
+ */
115
+ async function configReset() {
116
+ const { confirm } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
117
+ const confirmed = await confirm({
118
+ message: "Are you sure you want to reset config to defaults?",
119
+ default: false,
120
+ });
121
+ if (!confirmed) {
122
+ console.log(chalk_1.default.yellow("Cancelled."));
123
+ return;
124
+ }
125
+ await ConfigManager_1.configManager.initialize();
126
+ await ConfigManager_1.configManager.reset();
127
+ console.log(chalk_1.default.green("✅ Config reset to defaults"));
128
+ }
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.initProject = initProject;
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const ora_1 = __importDefault(require("ora"));
42
+ const path_1 = __importDefault(require("path"));
43
+ const fs_extra_1 = __importDefault(require("fs-extra"));
44
+ const ProjectInitializer_1 = require("../../core/ProjectInitializer");
45
+ const ConfigManager_1 = require("../../core/ConfigManager");
46
+ async function promptAnswers(projectName) {
47
+ // Dynamic import to keep startup fast and avoid issues if not installed
48
+ const { input, select } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
49
+ // Load defaults from config if available
50
+ await ConfigManager_1.configManager.initialize();
51
+ const existingDefaults = ConfigManager_1.configManager.getAll().projectDefaults || {};
52
+ const dbUrl = await input({
53
+ message: "Database URL (PostgreSQL):",
54
+ default: existingDefaults.dbUrl ||
55
+ `postgresql://user:password@localhost:5432/${projectName}`,
56
+ });
57
+ const emailProvider = await select({
58
+ message: "Email provider:",
59
+ choices: [
60
+ { name: "Postmark", value: "postmark" },
61
+ { name: "Resend", value: "resend" },
62
+ { name: "AWS SES", value: "ses" },
63
+ { name: "SMTP", value: "smtp" },
64
+ ],
65
+ default: existingDefaults.emailProvider || "postmark",
66
+ });
67
+ const locale = await input({
68
+ message: "Default locale:",
69
+ default: existingDefaults.locale || "en-US",
70
+ });
71
+ const currency = await input({
72
+ message: "Default currency:",
73
+ default: existingDefaults.currency || "USD",
74
+ });
75
+ return { dbUrl, emailProvider, locale, currency };
76
+ }
77
+ async function initProject(projectName, options) {
78
+ const initializer = new ProjectInitializer_1.ProjectInitializer();
79
+ // Determine project name interactively if not provided
80
+ let resolvedName = projectName ?? "";
81
+ if (!resolvedName) {
82
+ if (options.defaults) {
83
+ resolvedName = "my-kaven-app";
84
+ }
85
+ else {
86
+ const { input } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
87
+ resolvedName = await input({
88
+ message: "Project name:",
89
+ default: "my-kaven-app",
90
+ });
91
+ }
92
+ }
93
+ // Validate name
94
+ const validation = initializer.validateName(resolvedName);
95
+ if (!validation.valid) {
96
+ console.error(chalk_1.default.red(`Error: ${validation.reason}`));
97
+ console.error(chalk_1.default.gray("Try: kaven init my-project-name (use lowercase letters and hyphens)"));
98
+ process.exit(1);
99
+ }
100
+ const name = resolvedName;
101
+ const targetDir = path_1.default.resolve(process.cwd(), name);
102
+ // Check if directory already exists
103
+ if ((await fs_extra_1.default.pathExists(targetDir)) && !options.force) {
104
+ console.error(chalk_1.default.red(`Error: Directory "${name}" already exists. Use --force to overwrite.`));
105
+ process.exit(1);
106
+ }
107
+ // Get prompt answers or use defaults
108
+ let answers;
109
+ if (options.defaults) {
110
+ // Try to load from config, then fallback to options
111
+ await ConfigManager_1.configManager.initialize();
112
+ const configDefaults = ConfigManager_1.configManager.getAll().projectDefaults || {};
113
+ answers = {
114
+ dbUrl: options.dbUrl ||
115
+ configDefaults.dbUrl ||
116
+ `postgresql://user:password@localhost:5432/${name}`,
117
+ emailProvider: options.emailProvider || configDefaults.emailProvider || "postmark",
118
+ locale: options.locale || configDefaults.locale || "en-US",
119
+ currency: options.currency || configDefaults.currency || "USD",
120
+ };
121
+ }
122
+ else {
123
+ answers = await promptAnswers(name);
124
+ }
125
+ console.log();
126
+ // Clone template
127
+ const cloneSpinner = (0, ora_1.default)("Cloning kaven-template...").start();
128
+ try {
129
+ await initializer.cloneTemplate(targetDir);
130
+ cloneSpinner.succeed("Template cloned successfully");
131
+ }
132
+ catch (error) {
133
+ cloneSpinner.fail("Failed to clone template");
134
+ console.error(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
135
+ console.error(chalk_1.default.gray("Try: ensure git is installed and you have internet access"));
136
+ process.exit(1);
137
+ }
138
+ // Remove .git directory
139
+ const gitRemoveSpinner = (0, ora_1.default)("Removing .git directory...").start();
140
+ await initializer.removeGitDir(targetDir);
141
+ gitRemoveSpinner.succeed(".git directory removed");
142
+ // Replace placeholders
143
+ const placeholderSpinner = (0, ora_1.default)("Configuring project files...").start();
144
+ await initializer.replacePlaceholders(targetDir, {
145
+ ...answers,
146
+ projectName: name,
147
+ });
148
+ placeholderSpinner.succeed("Project files configured");
149
+ // Run pnpm install
150
+ if (!options.skipInstall) {
151
+ const installSpinner = (0, ora_1.default)("Installing dependencies (pnpm install)...").start();
152
+ try {
153
+ await initializer.runInstall(targetDir);
154
+ installSpinner.succeed("Dependencies installed");
155
+ }
156
+ catch (error) {
157
+ installSpinner.warn("Dependency installation failed — run pnpm install manually");
158
+ console.error(chalk_1.default.gray(error instanceof Error ? error.message : String(error)));
159
+ }
160
+ }
161
+ // Init git
162
+ if (!options.skipGit) {
163
+ const gitSpinner = (0, ora_1.default)("Initializing git repository...").start();
164
+ try {
165
+ await initializer.initGit(targetDir);
166
+ gitSpinner.succeed("Git repository initialized");
167
+ }
168
+ catch (error) {
169
+ gitSpinner.warn("Git init failed — initialize manually");
170
+ console.error(chalk_1.default.gray(error instanceof Error ? error.message : String(error)));
171
+ }
172
+ }
173
+ // Health check
174
+ const healthCheckSpinner = (0, ora_1.default)("Running health check...").start();
175
+ const health = await initializer.healthCheck(targetDir);
176
+ if (health.healthy) {
177
+ healthCheckSpinner.succeed("Health check passed");
178
+ }
179
+ else {
180
+ healthCheckSpinner.warn("Health check found issues:");
181
+ for (const issue of health.issues) {
182
+ console.log(chalk_1.default.yellow(` ⚠ ${issue}`));
183
+ }
184
+ }
185
+ // Success message
186
+ console.log();
187
+ console.log(chalk_1.default.green("✅ Project created successfully!"));
188
+ console.log();
189
+ console.log(chalk_1.default.bold("Next steps:"));
190
+ console.log(chalk_1.default.cyan(` cd ${name}`));
191
+ console.log(chalk_1.default.cyan(" cp .env.example .env"));
192
+ console.log(chalk_1.default.cyan(" npx prisma migrate dev"));
193
+ console.log(chalk_1.default.cyan(" pnpm dev"));
194
+ console.log();
195
+ console.log(chalk_1.default.gray("For more help, visit: https://docs.kaven.sh/getting-started"));
196
+ // Save project defaults to config for future use
197
+ await ConfigManager_1.configManager.initialize();
198
+ try {
199
+ await ConfigManager_1.configManager.set("projectDefaults", {
200
+ dbUrl: answers.dbUrl,
201
+ emailProvider: answers.emailProvider,
202
+ locale: answers.locale,
203
+ currency: answers.currency,
204
+ });
205
+ }
206
+ catch {
207
+ // Non-critical, continue if config save fails
208
+ }
209
+ }