@create-lft-app/cli 1.0.1 → 1.0.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/dist/src/index.js CHANGED
@@ -1,64 +1,61 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import path5 from "path";
5
- import { confirm as confirm2 } from "@inquirer/prompts";
6
-
7
- // src/config/index.ts
8
- import fs from "fs/promises";
9
- import path from "path";
10
- import os from "os";
11
- import { createCipheriv, createDecipheriv, randomBytes, createHash } from "crypto";
12
- import nodeMachineId from "node-machine-id";
13
- import { input, password, select, confirm } from "@inquirer/prompts";
4
+ import path4 from "path";
5
+ import { confirm } from "@inquirer/prompts";
14
6
 
15
- // src/ui/logger.ts
16
- import chalk from "chalk";
17
- var logger = {
18
- info: (message) => {
19
- console.log(chalk.blue("\u2139"), message);
20
- },
21
- success: (message) => {
22
- console.log(chalk.green("\u2714"), message);
23
- },
24
- warning: (message) => {
25
- console.log(chalk.yellow("\u26A0"), message);
26
- },
27
- error: (message) => {
28
- console.log(chalk.red("\u2716"), message);
29
- },
30
- step: (step, total, message) => {
31
- console.log(chalk.cyan(`[${step}/${total}]`), message);
32
- },
33
- newLine: () => {
34
- console.log();
7
+ // src/config/static-config.ts
8
+ var STATIC_CONFIG = {
9
+ github: {
10
+ token: "__GITHUB_TOKEN__",
11
+ username: "__GITHUB_USERNAME__",
12
+ org: "__GITHUB_ORG__"
35
13
  },
36
- divider: () => {
37
- console.log(chalk.gray("\u2500".repeat(50)));
38
- },
39
- title: (message) => {
40
- console.log(chalk.bold.white(message));
41
- },
42
- subtitle: (message) => {
43
- console.log(chalk.gray(message));
14
+ supabase: {
15
+ token: "__SUPABASE_TOKEN__",
16
+ orgId: "__SUPABASE_ORG_ID__",
17
+ region: "us-east-1"
44
18
  },
45
- link: (label, url) => {
46
- console.log(` ${chalk.gray(label + ":")} ${chalk.cyan.underline(url)}`);
47
- },
48
- list: (items) => {
49
- items.forEach((item) => {
50
- console.log(chalk.gray(" \u2022"), item);
51
- });
52
- },
53
- table: (rows) => {
54
- const maxLabelLength = Math.max(...rows.map((r) => r.label.length));
55
- rows.forEach(({ label, value }) => {
56
- const paddedLabel = label.padEnd(maxLabelLength);
57
- console.log(` ${chalk.gray(paddedLabel)} ${value}`);
58
- });
19
+ jira: {
20
+ email: "__JIRA_EMAIL__",
21
+ token: "__JIRA_TOKEN__",
22
+ domain: "__JIRA_DOMAIN__"
59
23
  }
60
24
  };
61
25
 
26
+ // src/config/index.ts
27
+ async function loadConfig() {
28
+ return {
29
+ version: "1.0.0",
30
+ credentials: {
31
+ github: {
32
+ token: STATIC_CONFIG.github.token,
33
+ username: STATIC_CONFIG.github.username
34
+ },
35
+ supabase: {
36
+ accessToken: STATIC_CONFIG.supabase.token,
37
+ organizationId: STATIC_CONFIG.supabase.orgId
38
+ },
39
+ jira: {
40
+ email: STATIC_CONFIG.jira.email,
41
+ apiToken: STATIC_CONFIG.jira.token,
42
+ domain: STATIC_CONFIG.jira.domain
43
+ }
44
+ },
45
+ defaults: {
46
+ githubOrg: STATIC_CONFIG.github.org || void 0,
47
+ supabaseRegion: STATIC_CONFIG.supabase.region,
48
+ jiraProjectType: "software"
49
+ }
50
+ };
51
+ }
52
+ async function hasConfig() {
53
+ return STATIC_CONFIG.github.token !== "__GITHUB_TOKEN__" && STATIC_CONFIG.supabase.token !== "__SUPABASE_TOKEN__" && STATIC_CONFIG.jira.token !== "__JIRA_TOKEN__";
54
+ }
55
+
56
+ // src/services/github.ts
57
+ import { Octokit } from "octokit";
58
+
62
59
  // src/ui/spinner.ts
63
60
  import ora from "ora";
64
61
  function createSpinner(text) {
@@ -79,38 +76,7 @@ async function withSpinner(text, fn, successText) {
79
76
  }
80
77
  }
81
78
 
82
- // src/config/index.ts
83
- var { machineIdSync } = nodeMachineId;
84
- var CONFIG_FILE = path.join(os.homedir(), ".lftrc");
85
- var ALGORITHM = "aes-256-gcm";
86
- function getEncryptionKey() {
87
- const machineId = machineIdSync();
88
- return createHash("sha256").update(machineId + "lft-secret").digest();
89
- }
90
- function decryptConfig(encrypted) {
91
- const { iv, tag, data } = JSON.parse(encrypted);
92
- const key = getEncryptionKey();
93
- const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, "hex"));
94
- decipher.setAuthTag(Buffer.from(tag, "hex"));
95
- let decrypted = decipher.update(data, "hex", "utf8");
96
- decrypted += decipher.final("utf8");
97
- return JSON.parse(decrypted);
98
- }
99
- async function hasConfig() {
100
- try {
101
- await fs.access(CONFIG_FILE);
102
- return true;
103
- } catch {
104
- return false;
105
- }
106
- }
107
- async function loadConfig() {
108
- const encrypted = await fs.readFile(CONFIG_FILE, "utf8");
109
- return decryptConfig(encrypted);
110
- }
111
-
112
79
  // src/services/github.ts
113
- import { Octokit } from "octokit";
114
80
  async function createGitHubRepo(projectName, config) {
115
81
  const octokit = new Octokit({ auth: config.credentials.github.token });
116
82
  const org = config.defaults.githubOrg;
@@ -216,16 +182,16 @@ async function createSupabaseProject(projectName, config) {
216
182
  }
217
183
  function generateSecurePassword() {
218
184
  const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
219
- let password2 = "";
185
+ let password = "";
220
186
  for (let i = 0; i < 32; i++) {
221
- password2 += chars.charAt(Math.floor(Math.random() * chars.length));
187
+ password += chars.charAt(Math.floor(Math.random() * chars.length));
222
188
  }
223
- return password2;
189
+ return password;
224
190
  }
225
191
 
226
192
  // src/utils/validation.ts
227
- import fs2 from "fs";
228
- import path2 from "path";
193
+ import fs from "fs";
194
+ import path from "path";
229
195
  function validateProjectName(name) {
230
196
  if (!name || name.trim() === "") {
231
197
  return { valid: false, error: "El nombre del proyecto no puede estar vac\xEDo" };
@@ -249,8 +215,8 @@ function validateProjectName(name) {
249
215
  if (name.length > 50) {
250
216
  return { valid: false, error: "El nombre no puede tener m\xE1s de 50 caracteres" };
251
217
  }
252
- const projectPath = path2.resolve(process.cwd(), name);
253
- if (fs2.existsSync(projectPath)) {
218
+ const projectPath = path.resolve(process.cwd(), name);
219
+ if (fs.existsSync(projectPath)) {
254
220
  return { valid: false, error: `El directorio "${name}" ya existe` };
255
221
  }
256
222
  return { valid: true };
@@ -354,59 +320,59 @@ async function scaffoldNextJs(projectName, projectPath) {
354
320
 
355
321
  // src/steps/copy-template.ts
356
322
  import { cp, mkdir, readFile, writeFile } from "fs/promises";
357
- import path3 from "path";
323
+ import path2 from "path";
358
324
  import { fileURLToPath } from "url";
359
325
  var __filename2 = fileURLToPath(import.meta.url);
360
- var __dirname2 = path3.dirname(__filename2);
326
+ var __dirname2 = path2.dirname(__filename2);
361
327
  async function copyTemplate(projectPath) {
362
328
  await withSpinner(
363
329
  "Copiando template LFT...",
364
330
  async () => {
365
- const templatesDir = path3.join(__dirname2, "..", "..", "templates");
366
- const srcDir = path3.join(projectPath, "src");
331
+ const templatesDir = path2.join(__dirname2, "..", "..", "templates");
332
+ const srcDir = path2.join(projectPath, "src");
367
333
  await cp(
368
- path3.join(templatesDir, "components", "ui"),
369
- path3.join(srcDir, "components", "ui"),
334
+ path2.join(templatesDir, "components", "ui"),
335
+ path2.join(srcDir, "components", "ui"),
370
336
  { recursive: true }
371
337
  );
372
338
  await cp(
373
- path3.join(templatesDir, "components", "layout"),
374
- path3.join(srcDir, "components", "layout"),
339
+ path2.join(templatesDir, "components", "layout"),
340
+ path2.join(srcDir, "components", "layout"),
375
341
  { recursive: true }
376
342
  );
377
343
  await cp(
378
- path3.join(templatesDir, "components", "dashboard"),
379
- path3.join(srcDir, "components", "dashboard"),
344
+ path2.join(templatesDir, "components", "dashboard"),
345
+ path2.join(srcDir, "components", "dashboard"),
380
346
  { recursive: true }
381
347
  );
382
- await mkdir(path3.join(srcDir, "lib"), { recursive: true });
348
+ await mkdir(path2.join(srcDir, "lib"), { recursive: true });
383
349
  await cp(
384
- path3.join(templatesDir, "lib", "utils.ts"),
385
- path3.join(srcDir, "lib", "utils.ts")
350
+ path2.join(templatesDir, "lib", "utils.ts"),
351
+ path2.join(srcDir, "lib", "utils.ts")
386
352
  );
387
- await mkdir(path3.join(srcDir, "hooks"), { recursive: true });
353
+ await mkdir(path2.join(srcDir, "hooks"), { recursive: true });
388
354
  await cp(
389
- path3.join(templatesDir, "hooks"),
390
- path3.join(srcDir, "hooks"),
355
+ path2.join(templatesDir, "hooks"),
356
+ path2.join(srcDir, "hooks"),
391
357
  { recursive: true }
392
358
  );
393
359
  await cp(
394
- path3.join(templatesDir, "app", "layout.tsx"),
395
- path3.join(srcDir, "app", "layout.tsx")
360
+ path2.join(templatesDir, "app", "layout.tsx"),
361
+ path2.join(srcDir, "app", "layout.tsx")
396
362
  );
397
363
  await cp(
398
- path3.join(templatesDir, "app", "page.tsx"),
399
- path3.join(srcDir, "app", "page.tsx")
364
+ path2.join(templatesDir, "app", "page.tsx"),
365
+ path2.join(srcDir, "app", "page.tsx")
400
366
  );
401
- await mkdir(path3.join(srcDir, "app", "dashboard"), { recursive: true });
367
+ await mkdir(path2.join(srcDir, "app", "dashboard"), { recursive: true });
402
368
  await cp(
403
- path3.join(templatesDir, "app", "dashboard", "page.tsx"),
404
- path3.join(srcDir, "app", "dashboard", "page.tsx")
369
+ path2.join(templatesDir, "app", "dashboard", "page.tsx"),
370
+ path2.join(srcDir, "app", "dashboard", "page.tsx")
405
371
  );
406
- await mkdir(path3.join(srcDir, "app", "auth", "login"), { recursive: true });
372
+ await mkdir(path2.join(srcDir, "app", "auth", "login"), { recursive: true });
407
373
  await cp(
408
- path3.join(templatesDir, "app", "auth", "login", "page.tsx"),
409
- path3.join(srcDir, "app", "auth", "login", "page.tsx")
374
+ path2.join(templatesDir, "app", "auth", "login", "page.tsx"),
375
+ path2.join(srcDir, "app", "auth", "login", "page.tsx")
410
376
  );
411
377
  await mergeGlobalStyles(projectPath, templatesDir);
412
378
  },
@@ -414,8 +380,8 @@ async function copyTemplate(projectPath) {
414
380
  );
415
381
  }
416
382
  async function mergeGlobalStyles(projectPath, templatesDir) {
417
- const templateCssPath = path3.join(templatesDir, "app", "globals.css");
418
- const projectCssPath = path3.join(projectPath, "src", "app", "globals.css");
383
+ const templateCssPath = path2.join(templatesDir, "app", "globals.css");
384
+ const projectCssPath = path2.join(projectPath, "src", "app", "globals.css");
419
385
  try {
420
386
  const templateCss = await readFile(templateCssPath, "utf-8");
421
387
  const existingCss = await readFile(projectCssPath, "utf-8");
@@ -492,7 +458,7 @@ async function installDependencies(projectPath) {
492
458
 
493
459
  // src/steps/create-env.ts
494
460
  import { writeFile as writeFile2, readFile as readFile2, appendFile } from "fs/promises";
495
- import path4 from "path";
461
+ import path3 from "path";
496
462
  async function createEnvFile(projectPath, supabaseKeys) {
497
463
  await withSpinner(
498
464
  "Creando archivo .env.local...",
@@ -503,10 +469,10 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}
503
469
  SUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}
504
470
  `;
505
471
  await writeFile2(
506
- path4.join(projectPath, ".env.local"),
472
+ path3.join(projectPath, ".env.local"),
507
473
  envContent
508
474
  );
509
- const gitignorePath = path4.join(projectPath, ".gitignore");
475
+ const gitignorePath = path3.join(projectPath, ".gitignore");
510
476
  try {
511
477
  const gitignore = await readFile2(gitignorePath, "utf-8");
512
478
  if (!gitignore.includes(".env.local")) {
@@ -550,6 +516,53 @@ async function setupGit(projectPath, remoteUrl) {
550
516
  );
551
517
  }
552
518
 
519
+ // src/ui/logger.ts
520
+ import chalk from "chalk";
521
+ var logger = {
522
+ info: (message) => {
523
+ console.log(chalk.blue("\u2139"), message);
524
+ },
525
+ success: (message) => {
526
+ console.log(chalk.green("\u2714"), message);
527
+ },
528
+ warning: (message) => {
529
+ console.log(chalk.yellow("\u26A0"), message);
530
+ },
531
+ error: (message) => {
532
+ console.log(chalk.red("\u2716"), message);
533
+ },
534
+ step: (step, total, message) => {
535
+ console.log(chalk.cyan(`[${step}/${total}]`), message);
536
+ },
537
+ newLine: () => {
538
+ console.log();
539
+ },
540
+ divider: () => {
541
+ console.log(chalk.gray("\u2500".repeat(50)));
542
+ },
543
+ title: (message) => {
544
+ console.log(chalk.bold.white(message));
545
+ },
546
+ subtitle: (message) => {
547
+ console.log(chalk.gray(message));
548
+ },
549
+ link: (label, url) => {
550
+ console.log(` ${chalk.gray(label + ":")} ${chalk.cyan.underline(url)}`);
551
+ },
552
+ list: (items) => {
553
+ items.forEach((item) => {
554
+ console.log(chalk.gray(" \u2022"), item);
555
+ });
556
+ },
557
+ table: (rows) => {
558
+ const maxLabelLength = Math.max(...rows.map((r) => r.label.length));
559
+ rows.forEach(({ label, value }) => {
560
+ const paddedLabel = label.padEnd(maxLabelLength);
561
+ console.log(` ${chalk.gray(paddedLabel)} ${value}`);
562
+ });
563
+ }
564
+ };
565
+
553
566
  // src/ui/banner.ts
554
567
  import boxen from "boxen";
555
568
  import chalk2 from "chalk";
@@ -591,7 +604,7 @@ async function createProject(projectName, options = {}) {
591
604
  if (!validation.valid) {
592
605
  throw new Error(validation.error);
593
606
  }
594
- const projectPath = path5.resolve(process.cwd(), projectName);
607
+ const projectPath = path4.resolve(process.cwd(), projectName);
595
608
  if (!await hasConfig()) {
596
609
  logger.warning('No se encontr\xF3 configuraci\xF3n. Ejecuta "create-lft-app config" primero.');
597
610
  throw new Error("Configuraci\xF3n no encontrada");
@@ -614,7 +627,7 @@ async function createProject(projectName, options = {}) {
614
627
  logger.table(resources);
615
628
  logger.newLine();
616
629
  if (!options.autoConfirm) {
617
- const shouldContinue = await confirm2({
630
+ const shouldContinue = await confirm({
618
631
  message: "\xBFContinuar con la creaci\xF3n?",
619
632
  default: true
620
633
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/config/index.ts","../../src/ui/logger.ts","../../src/ui/spinner.ts","../../src/services/github.ts","../../src/services/supabase.ts","../../src/utils/validation.ts","../../src/services/jira.ts","../../src/steps/scaffold-nextjs.ts","../../src/steps/copy-template.ts","../../src/steps/install-deps.ts","../../src/steps/create-env.ts","../../src/steps/setup-git.ts","../../src/ui/banner.ts"],"sourcesContent":["import path from 'path';\nimport { confirm } from '@inquirer/prompts';\nimport { loadConfig, hasConfig } from './config/index.js';\nimport { createGitHubRepo } from './services/github.js';\nimport { createSupabaseProject } from './services/supabase.js';\nimport { createJiraProject } from './services/jira.js';\nimport { scaffoldNextJs } from './steps/scaffold-nextjs.js';\nimport { copyTemplate } from './steps/copy-template.js';\nimport { installDependencies } from './steps/install-deps.js';\nimport { createEnvFile } from './steps/create-env.js';\nimport { setupGit } from './steps/setup-git.js';\nimport { logger } from './ui/logger.js';\nimport { showSuccessBanner } from './ui/banner.js';\nimport { validateProjectName } from './utils/validation.js';\n\nexport interface CreateProjectOptions {\n skipGithub?: boolean;\n skipSupabase?: boolean;\n skipJira?: boolean;\n skipGit?: boolean;\n autoConfirm?: boolean;\n}\n\nexport async function createProject(\n projectName: string,\n options: CreateProjectOptions = {}\n): Promise<void> {\n // Validar nombre del proyecto\n const validation = validateProjectName(projectName);\n if (!validation.valid) {\n throw new Error(validation.error);\n }\n\n // Verificar que el directorio no exista\n const projectPath = path.resolve(process.cwd(), projectName);\n\n // Cargar configuración\n if (!await hasConfig()) {\n logger.warning('No se encontró configuración. Ejecuta \"create-lft-app config\" primero.');\n throw new Error('Configuración no encontrada');\n }\n\n const config = await loadConfig();\n\n // Mostrar resumen\n logger.newLine();\n logger.title('Resumen de recursos a crear:');\n logger.newLine();\n\n const resources = [];\n if (!options.skipGithub) {\n resources.push({ label: 'GitHub', value: `${config.defaults.githubOrg || config.credentials.github.username}/${projectName} (privado)` });\n }\n if (!options.skipSupabase) {\n resources.push({ label: 'Supabase', value: `${projectName} en ${config.defaults.supabaseRegion}` });\n }\n if (!options.skipJira) {\n resources.push({ label: 'Jira', value: `Proyecto \"${projectName}\" en ${config.credentials.jira.domain}` });\n }\n resources.push({ label: 'Next.js', value: 'App Router + TypeScript + Tailwind + Dashboard' });\n\n logger.table(resources);\n logger.newLine();\n\n // Confirmar\n if (!options.autoConfirm) {\n const shouldContinue = await confirm({\n message: '¿Continuar con la creación?',\n default: true,\n });\n\n if (!shouldContinue) {\n logger.info('Operación cancelada');\n return;\n }\n }\n\n logger.newLine();\n logger.divider();\n logger.newLine();\n\n const urls: { github?: string; supabase?: string; jira?: string } = {};\n let supabaseKeys: { url: string; anonKey: string; serviceKey: string } | undefined;\n\n // Crear recursos externos en paralelo\n const externalTasks: Promise<void>[] = [];\n\n if (!options.skipGithub) {\n externalTasks.push(\n createGitHubRepo(projectName, config).then((url) => {\n urls.github = url;\n })\n );\n }\n\n if (!options.skipSupabase) {\n externalTasks.push(\n createSupabaseProject(projectName, config).then((result) => {\n urls.supabase = result.url;\n supabaseKeys = result;\n })\n );\n }\n\n if (!options.skipJira) {\n externalTasks.push(\n createJiraProject(projectName, config).then((url) => {\n urls.jira = url;\n })\n );\n }\n\n // Esperar a que terminen los recursos externos\n await Promise.all(externalTasks);\n\n // Scaffold Next.js\n await scaffoldNextJs(projectName, projectPath);\n\n // Copiar template\n await copyTemplate(projectPath);\n\n // Instalar dependencias\n await installDependencies(projectPath);\n\n // Crear .env.local\n if (supabaseKeys) {\n await createEnvFile(projectPath, supabaseKeys);\n }\n\n // Setup git\n if (!options.skipGit && urls.github) {\n await setupGit(projectPath, urls.github);\n }\n\n // Mostrar resumen final\n logger.newLine();\n showSuccessBanner(projectName, urls);\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport os from 'os';\nimport { createCipheriv, createDecipheriv, randomBytes, createHash } from 'crypto';\nimport nodeMachineId from 'node-machine-id';\nconst { machineIdSync } = nodeMachineId;\nimport { input, password, select, confirm } from '@inquirer/prompts';\nimport { logger } from '../ui/logger.js';\nimport { withSpinner } from '../ui/spinner.js';\n\nconst CONFIG_FILE = path.join(os.homedir(), '.lftrc');\nconst ALGORITHM = 'aes-256-gcm';\n\nexport interface LFTConfig {\n version: string;\n credentials: {\n github: {\n token: string;\n username: string;\n };\n supabase: {\n accessToken: string;\n organizationId: string;\n };\n jira: {\n email: string;\n apiToken: string;\n domain: string;\n };\n };\n defaults: {\n githubOrg?: string;\n supabaseRegion: string;\n jiraProjectType: string;\n };\n}\n\nfunction getEncryptionKey(): Buffer {\n const machineId = machineIdSync();\n return createHash('sha256').update(machineId + 'lft-secret').digest();\n}\n\nfunction encryptConfig(config: LFTConfig): string {\n const key = getEncryptionKey();\n const iv = randomBytes(16);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n\n let encrypted = cipher.update(JSON.stringify(config), 'utf8', 'hex');\n encrypted += cipher.final('hex');\n\n const authTag = cipher.getAuthTag();\n return JSON.stringify({\n iv: iv.toString('hex'),\n tag: authTag.toString('hex'),\n data: encrypted,\n });\n}\n\nfunction decryptConfig(encrypted: string): LFTConfig {\n const { iv, tag, data } = JSON.parse(encrypted);\n const key = getEncryptionKey();\n\n const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, 'hex'));\n decipher.setAuthTag(Buffer.from(tag, 'hex'));\n\n let decrypted = decipher.update(data, 'hex', 'utf8');\n decrypted += decipher.final('utf8');\n\n return JSON.parse(decrypted);\n}\n\nexport async function hasConfig(): Promise<boolean> {\n try {\n await fs.access(CONFIG_FILE);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function loadConfig(): Promise<LFTConfig> {\n const encrypted = await fs.readFile(CONFIG_FILE, 'utf8');\n return decryptConfig(encrypted);\n}\n\nexport async function saveConfig(config: LFTConfig): Promise<void> {\n const encrypted = encryptConfig(config);\n await fs.writeFile(CONFIG_FILE, encrypted, 'utf8');\n}\n\nexport async function showConfig(): Promise<void> {\n if (!await hasConfig()) {\n logger.warning('No hay configuración guardada');\n logger.info('Ejecuta \"create-lft-app config\" para configurar');\n return;\n }\n\n const config = await loadConfig();\n\n logger.newLine();\n logger.title('Configuración actual:');\n logger.newLine();\n\n logger.subtitle('GitHub:');\n logger.table([\n { label: 'Usuario', value: config.credentials.github.username },\n { label: 'Token', value: '***' + config.credentials.github.token.slice(-4) },\n { label: 'Org por defecto', value: config.defaults.githubOrg || '(ninguna)' },\n ]);\n\n logger.newLine();\n logger.subtitle('Supabase:');\n logger.table([\n { label: 'Org ID', value: config.credentials.supabase.organizationId },\n { label: 'Token', value: '***' + config.credentials.supabase.accessToken.slice(-4) },\n { label: 'Región', value: config.defaults.supabaseRegion },\n ]);\n\n logger.newLine();\n logger.subtitle('Jira:');\n logger.table([\n { label: 'Email', value: config.credentials.jira.email },\n { label: 'Dominio', value: config.credentials.jira.domain },\n { label: 'Token', value: '***' + config.credentials.jira.apiToken.slice(-4) },\n ]);\n}\n\nexport async function resetConfig(): Promise<void> {\n if (!await hasConfig()) {\n logger.info('No hay configuración para resetear');\n return;\n }\n\n const shouldReset = await confirm({\n message: '¿Estás seguro de que quieres eliminar la configuración?',\n default: false,\n });\n\n if (shouldReset) {\n await fs.unlink(CONFIG_FILE);\n logger.success('Configuración eliminada');\n }\n}\n\ninterface ConfigureOptions {\n onlyGithub?: boolean;\n onlySupabase?: boolean;\n onlyJira?: boolean;\n}\n\nexport async function configureCredentials(options: ConfigureOptions = {}): Promise<void> {\n let config: LFTConfig;\n\n if (await hasConfig()) {\n config = await loadConfig();\n logger.info('Configuración existente encontrada. Actualizando...');\n } else {\n config = {\n version: '1.0.0',\n credentials: {\n github: { token: '', username: '' },\n supabase: { accessToken: '', organizationId: '' },\n jira: { email: '', apiToken: '', domain: '' },\n },\n defaults: {\n supabaseRegion: 'us-east-1',\n jiraProjectType: 'software',\n },\n };\n }\n\n const configureAll = !options.onlyGithub && !options.onlySupabase && !options.onlyJira;\n\n // GitHub\n if (configureAll || options.onlyGithub) {\n logger.newLine();\n logger.title('Configuración de GitHub');\n logger.subtitle('Necesitas un Personal Access Token con permisos: repo, read:org');\n logger.link('Crear token', 'https://github.com/settings/tokens/new');\n logger.newLine();\n\n const githubToken = await password({\n message: 'GitHub Personal Access Token:',\n mask: '*',\n });\n\n // Validar token y obtener username\n const isValid = await withSpinner(\n 'Validando token de GitHub...',\n async () => {\n const response = await fetch('https://api.github.com/user', {\n headers: { Authorization: `Bearer ${githubToken}` },\n });\n if (!response.ok) return null;\n return response.json() as Promise<{ login: string }>;\n }\n );\n\n if (!isValid) {\n throw new Error('Token de GitHub inválido');\n }\n\n config.credentials.github.token = githubToken;\n config.credentials.github.username = isValid.login;\n logger.success(`Conectado como: ${isValid.login}`);\n\n const useOrg = await confirm({\n message: '¿Quieres usar una organización por defecto?',\n default: false,\n });\n\n if (useOrg) {\n config.defaults.githubOrg = await input({\n message: 'Nombre de la organización:',\n });\n }\n }\n\n // Supabase\n if (configureAll || options.onlySupabase) {\n logger.newLine();\n logger.title('Configuración de Supabase');\n logger.subtitle('Necesitas un Access Token de la Management API');\n logger.link('Crear token', 'https://supabase.com/dashboard/account/tokens');\n logger.newLine();\n\n const supabaseToken = await password({\n message: 'Supabase Access Token:',\n mask: '*',\n });\n\n // Obtener organizaciones\n const orgs = await withSpinner(\n 'Obteniendo organizaciones de Supabase...',\n async () => {\n const response = await fetch('https://api.supabase.com/v1/organizations', {\n headers: { Authorization: `Bearer ${supabaseToken}` },\n });\n if (!response.ok) return null;\n return response.json() as Promise<Array<{ id: string; name: string }>>;\n }\n );\n\n if (!orgs || orgs.length === 0) {\n throw new Error('Token de Supabase inválido o no tienes organizaciones');\n }\n\n config.credentials.supabase.accessToken = supabaseToken;\n\n if (orgs.length === 1) {\n config.credentials.supabase.organizationId = orgs[0].id;\n logger.success(`Usando organización: ${orgs[0].name}`);\n } else {\n const selectedOrg = await select({\n message: 'Selecciona la organización:',\n choices: orgs.map((org) => ({ name: org.name, value: org.id })),\n });\n config.credentials.supabase.organizationId = selectedOrg;\n }\n\n config.defaults.supabaseRegion = await select({\n message: 'Región por defecto:',\n choices: [\n { name: 'US East (Virginia)', value: 'us-east-1' },\n { name: 'US West (Oregon)', value: 'us-west-1' },\n { name: 'EU West (Ireland)', value: 'eu-west-1' },\n { name: 'AP Southeast (Singapore)', value: 'ap-southeast-1' },\n { name: 'AP Northeast (Tokyo)', value: 'ap-northeast-1' },\n { name: 'SA East (São Paulo)', value: 'sa-east-1' },\n ],\n default: 'us-east-1',\n });\n }\n\n // Jira\n if (configureAll || options.onlyJira) {\n logger.newLine();\n logger.title('Configuración de Jira');\n logger.subtitle('Necesitas un API Token de Atlassian');\n logger.link('Crear token', 'https://id.atlassian.com/manage-profile/security/api-tokens');\n logger.newLine();\n\n const jiraEmail = await input({\n message: 'Email de Atlassian:',\n });\n\n const jiraToken = await password({\n message: 'Jira API Token:',\n mask: '*',\n });\n\n const jiraDomain = await input({\n message: 'Dominio de Jira (ej: empresa.atlassian.net):',\n validate: (value) => {\n if (!value.includes('.atlassian.net')) {\n return 'El dominio debe terminar en .atlassian.net';\n }\n return true;\n },\n });\n\n // Validar credenciales\n const isValid = await withSpinner(\n 'Validando credenciales de Jira...',\n async () => {\n const auth = Buffer.from(`${jiraEmail}:${jiraToken}`).toString('base64');\n const response = await fetch(`https://${jiraDomain}/rest/api/3/myself`, {\n headers: { Authorization: `Basic ${auth}` },\n });\n return response.ok;\n }\n );\n\n if (!isValid) {\n throw new Error('Credenciales de Jira inválidas');\n }\n\n config.credentials.jira.email = jiraEmail;\n config.credentials.jira.apiToken = jiraToken;\n config.credentials.jira.domain = jiraDomain;\n logger.success('Credenciales de Jira válidas');\n }\n\n // Guardar configuración\n await saveConfig(config);\n logger.newLine();\n logger.success('Configuración guardada en ~/.lftrc');\n}\n","import chalk from 'chalk';\n\nexport const logger = {\n info: (message: string) => {\n console.log(chalk.blue('ℹ'), message);\n },\n\n success: (message: string) => {\n console.log(chalk.green('✔'), message);\n },\n\n warning: (message: string) => {\n console.log(chalk.yellow('⚠'), message);\n },\n\n error: (message: string) => {\n console.log(chalk.red('✖'), message);\n },\n\n step: (step: number, total: number, message: string) => {\n console.log(chalk.cyan(`[${step}/${total}]`), message);\n },\n\n newLine: () => {\n console.log();\n },\n\n divider: () => {\n console.log(chalk.gray('─'.repeat(50)));\n },\n\n title: (message: string) => {\n console.log(chalk.bold.white(message));\n },\n\n subtitle: (message: string) => {\n console.log(chalk.gray(message));\n },\n\n link: (label: string, url: string) => {\n console.log(` ${chalk.gray(label + ':')} ${chalk.cyan.underline(url)}`);\n },\n\n list: (items: string[]) => {\n items.forEach((item) => {\n console.log(chalk.gray(' •'), item);\n });\n },\n\n table: (rows: Array<{ label: string; value: string }>) => {\n const maxLabelLength = Math.max(...rows.map((r) => r.label.length));\n rows.forEach(({ label, value }) => {\n const paddedLabel = label.padEnd(maxLabelLength);\n console.log(` ${chalk.gray(paddedLabel)} ${value}`);\n });\n },\n};\n","import ora, { type Ora } from 'ora';\n\nexport function createSpinner(text: string): Ora {\n return ora({\n text,\n spinner: 'dots',\n });\n}\n\nexport async function withSpinner<T>(\n text: string,\n fn: () => Promise<T>,\n successText?: string\n): Promise<T> {\n const spinner = createSpinner(text).start();\n\n try {\n const result = await fn();\n spinner.succeed(successText || text);\n return result;\n } catch (error) {\n spinner.fail();\n throw error;\n }\n}\n","import { Octokit } from 'octokit';\nimport { withSpinner } from '../ui/spinner.js';\nimport type { LFTConfig } from '../config/index.js';\n\nexport async function createGitHubRepo(\n projectName: string,\n config: LFTConfig\n): Promise<string> {\n const octokit = new Octokit({ auth: config.credentials.github.token });\n const org = config.defaults.githubOrg;\n\n return withSpinner(\n 'Creando repositorio en GitHub...',\n async () => {\n let repo;\n\n if (org) {\n // Crear en organización\n repo = await octokit.rest.repos.createInOrg({\n org,\n name: projectName,\n private: true,\n auto_init: false,\n description: `Proyecto ${projectName} creado con create-lft-app`,\n });\n } else {\n // Crear en cuenta personal\n repo = await octokit.rest.repos.createForAuthenticatedUser({\n name: projectName,\n private: true,\n auto_init: false,\n description: `Proyecto ${projectName} creado con create-lft-app`,\n });\n }\n\n return repo.data.html_url;\n },\n `Repositorio creado: ${org || config.credentials.github.username}/${projectName}`\n );\n}\n\nexport async function validateGitHubToken(token: string): Promise<{ valid: boolean; username?: string }> {\n try {\n const octokit = new Octokit({ auth: token });\n const { data } = await octokit.rest.users.getAuthenticated();\n return { valid: true, username: data.login };\n } catch {\n return { valid: false };\n }\n}\n","import { withSpinner, createSpinner } from '../ui/spinner.js';\nimport type { LFTConfig } from '../config/index.js';\n\ninterface SupabaseProjectResponse {\n id: string;\n name: string;\n organization_id: string;\n region: string;\n status: string;\n}\n\ninterface SupabaseApiKey {\n name: string;\n api_key: string;\n}\n\nexport interface SupabaseProjectResult {\n url: string;\n anonKey: string;\n serviceKey: string;\n}\n\nconst SUPABASE_API_URL = 'https://api.supabase.com/v1';\n\nasync function waitForProjectReady(\n projectId: string,\n token: string,\n maxAttempts = 60\n): Promise<void> {\n const spinner = createSpinner('Provisionando base de datos (esto puede tomar ~2 minutos)...').start();\n\n for (let i = 0; i < maxAttempts; i++) {\n const response = await fetch(`${SUPABASE_API_URL}/projects/${projectId}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (response.ok) {\n const project = (await response.json()) as SupabaseProjectResponse;\n if (project.status === 'ACTIVE_HEALTHY') {\n spinner.succeed('Base de datos provisionada');\n return;\n }\n }\n\n // Esperar 5 segundos antes del siguiente intento\n await new Promise((resolve) => setTimeout(resolve, 5000));\n spinner.text = `Provisionando base de datos... (${Math.floor((i + 1) * 5 / 60)}min ${((i + 1) * 5) % 60}s)`;\n }\n\n spinner.fail('Timeout esperando a que el proyecto esté listo');\n throw new Error('Timeout: el proyecto de Supabase no se activó a tiempo');\n}\n\nasync function getProjectApiKeys(\n projectId: string,\n token: string\n): Promise<{ anonKey: string; serviceKey: string }> {\n const response = await fetch(`${SUPABASE_API_URL}/projects/${projectId}/api-keys`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) {\n throw new Error('No se pudieron obtener las API keys de Supabase');\n }\n\n const keys = (await response.json()) as SupabaseApiKey[];\n\n const anonKey = keys.find((k) => k.name === 'anon')?.api_key;\n const serviceKey = keys.find((k) => k.name === 'service_role')?.api_key;\n\n if (!anonKey || !serviceKey) {\n throw new Error('No se encontraron las API keys necesarias');\n }\n\n return { anonKey, serviceKey };\n}\n\nexport async function createSupabaseProject(\n projectName: string,\n config: LFTConfig\n): Promise<SupabaseProjectResult> {\n const token = config.credentials.supabase.accessToken;\n const orgId = config.credentials.supabase.organizationId;\n const region = config.defaults.supabaseRegion;\n\n // Generar una contraseña segura para la base de datos\n const dbPassword = generateSecurePassword();\n\n // Crear proyecto\n const project = await withSpinner(\n 'Creando proyecto en Supabase...',\n async () => {\n const response = await fetch(`${SUPABASE_API_URL}/projects`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n name: projectName,\n organization_id: orgId,\n region,\n plan: 'free',\n db_pass: dbPassword,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Error creando proyecto Supabase: ${error}`);\n }\n\n return response.json() as Promise<SupabaseProjectResponse>;\n }\n );\n\n // Esperar a que el proyecto esté listo\n await waitForProjectReady(project.id, token);\n\n // Obtener API keys\n const { anonKey, serviceKey } = await getProjectApiKeys(project.id, token);\n\n const projectUrl = `https://${project.id}.supabase.co`;\n\n return {\n url: projectUrl,\n anonKey,\n serviceKey,\n };\n}\n\nfunction generateSecurePassword(): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';\n let password = '';\n for (let i = 0; i < 32; i++) {\n password += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return password;\n}\n","import fs from 'fs';\nimport path from 'path';\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n // Verificar que no esté vacío\n if (!name || name.trim() === '') {\n return { valid: false, error: 'El nombre del proyecto no puede estar vacío' };\n }\n\n // Verificar caracteres válidos (solo letras, números, guiones y guiones bajos)\n const validPattern = /^[a-zA-Z0-9_-]+$/;\n if (!validPattern.test(name)) {\n return {\n valid: false,\n error: 'El nombre solo puede contener letras, números, guiones (-) y guiones bajos (_)',\n };\n }\n\n // Verificar que no empiece con guión o número\n if (/^[-_0-9]/.test(name)) {\n return {\n valid: false,\n error: 'El nombre debe empezar con una letra',\n };\n }\n\n // Verificar longitud\n if (name.length < 2) {\n return { valid: false, error: 'El nombre debe tener al menos 2 caracteres' };\n }\n\n if (name.length > 50) {\n return { valid: false, error: 'El nombre no puede tener más de 50 caracteres' };\n }\n\n // Verificar que el directorio no exista\n const projectPath = path.resolve(process.cwd(), name);\n if (fs.existsSync(projectPath)) {\n return { valid: false, error: `El directorio \"${name}\" ya existe` };\n }\n\n return { valid: true };\n}\n\nexport function generateJiraKey(projectName: string): string {\n // Generar key de Jira: máximo 10 caracteres, solo mayúsculas\n const cleaned = projectName\n .replace(/[^a-zA-Z0-9]/g, '')\n .toUpperCase()\n .slice(0, 10);\n\n return cleaned || 'PROJ';\n}\n","import { withSpinner } from '../ui/spinner.js';\nimport { generateJiraKey } from '../utils/validation.js';\nimport type { LFTConfig } from '../config/index.js';\n\ninterface JiraProjectResponse {\n id: string;\n key: string;\n name: string;\n self: string;\n}\n\nexport async function createJiraProject(\n projectName: string,\n config: LFTConfig\n): Promise<string> {\n const { email, apiToken, domain } = config.credentials.jira;\n const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');\n const projectKey = generateJiraKey(projectName);\n\n return withSpinner(\n 'Creando proyecto en Jira...',\n async () => {\n // Primero obtener el leadAccountId del usuario actual\n const meResponse = await fetch(`https://${domain}/rest/api/3/myself`, {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!meResponse.ok) {\n throw new Error('No se pudo obtener información del usuario de Jira');\n }\n\n const me = (await meResponse.json()) as { accountId: string };\n\n // Crear el proyecto\n const response = await fetch(`https://${domain}/rest/api/3/project`, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n key: projectKey,\n name: projectName,\n projectTypeKey: 'software',\n projectTemplateKey: 'com.pyxis.greenhopper.jira:gh-simplified-agility-scrum',\n leadAccountId: me.accountId,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n\n // Si el key ya existe, intentar con un sufijo numérico\n if (error.includes('project key')) {\n const newKey = `${projectKey}${Date.now().toString().slice(-4)}`;\n const retryResponse = await fetch(`https://${domain}/rest/api/3/project`, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n key: newKey,\n name: projectName,\n projectTypeKey: 'software',\n projectTemplateKey: 'com.pyxis.greenhopper.jira:gh-simplified-agility-scrum',\n leadAccountId: me.accountId,\n }),\n });\n\n if (!retryResponse.ok) {\n throw new Error(`Error creando proyecto Jira: ${await retryResponse.text()}`);\n }\n\n const project = (await retryResponse.json()) as JiraProjectResponse;\n return `https://${domain}/browse/${project.key}`;\n }\n\n throw new Error(`Error creando proyecto Jira: ${error}`);\n }\n\n const project = (await response.json()) as JiraProjectResponse;\n return `https://${domain}/browse/${project.key}`;\n },\n `Proyecto Jira creado: ${projectKey}`\n );\n}\n\nexport async function validateJiraCredentials(\n email: string,\n apiToken: string,\n domain: string\n): Promise<boolean> {\n try {\n const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');\n const response = await fetch(`https://${domain}/rest/api/3/myself`, {\n headers: { Authorization: `Basic ${auth}` },\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function scaffoldNextJs(\n projectName: string,\n projectPath: string\n): Promise<void> {\n await withSpinner(\n 'Inicializando proyecto Next.js...',\n async () => {\n await execa('npx', [\n 'create-next-app@latest',\n projectName,\n '--typescript',\n '--tailwind',\n '--eslint',\n '--app',\n '--turbopack',\n '--src-dir',\n '--import-alias', '@/*',\n '--use-npm',\n ], {\n cwd: process.cwd(),\n stdio: 'pipe',\n });\n },\n 'Proyecto Next.js inicializado'\n );\n}\n","import { cp, mkdir, readFile, writeFile } from 'fs/promises';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { withSpinner } from '../ui/spinner.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport async function copyTemplate(projectPath: string): Promise<void> {\n await withSpinner(\n 'Copiando template LFT...',\n async () => {\n // Ruta a los templates (relativa al dist)\n const templatesDir = path.join(__dirname, '..', '..', 'templates');\n const srcDir = path.join(projectPath, 'src');\n\n // Copiar componentes UI\n await cp(\n path.join(templatesDir, 'components', 'ui'),\n path.join(srcDir, 'components', 'ui'),\n { recursive: true }\n );\n\n // Copiar componentes de layout\n await cp(\n path.join(templatesDir, 'components', 'layout'),\n path.join(srcDir, 'components', 'layout'),\n { recursive: true }\n );\n\n // Copiar componentes de dashboard\n await cp(\n path.join(templatesDir, 'components', 'dashboard'),\n path.join(srcDir, 'components', 'dashboard'),\n { recursive: true }\n );\n\n // Copiar lib/utils.ts\n await mkdir(path.join(srcDir, 'lib'), { recursive: true });\n await cp(\n path.join(templatesDir, 'lib', 'utils.ts'),\n path.join(srcDir, 'lib', 'utils.ts')\n );\n\n // Copiar hooks\n await mkdir(path.join(srcDir, 'hooks'), { recursive: true });\n await cp(\n path.join(templatesDir, 'hooks'),\n path.join(srcDir, 'hooks'),\n { recursive: true }\n );\n\n // Copiar páginas de app\n await cp(\n path.join(templatesDir, 'app', 'layout.tsx'),\n path.join(srcDir, 'app', 'layout.tsx')\n );\n\n await cp(\n path.join(templatesDir, 'app', 'page.tsx'),\n path.join(srcDir, 'app', 'page.tsx')\n );\n\n // Copiar dashboard page\n await mkdir(path.join(srcDir, 'app', 'dashboard'), { recursive: true });\n await cp(\n path.join(templatesDir, 'app', 'dashboard', 'page.tsx'),\n path.join(srcDir, 'app', 'dashboard', 'page.tsx')\n );\n\n // Copiar auth/login page\n await mkdir(path.join(srcDir, 'app', 'auth', 'login'), { recursive: true });\n await cp(\n path.join(templatesDir, 'app', 'auth', 'login', 'page.tsx'),\n path.join(srcDir, 'app', 'auth', 'login', 'page.tsx')\n );\n\n // Merge globals.css\n await mergeGlobalStyles(projectPath, templatesDir);\n },\n 'Template LFT copiado (47 componentes + páginas)'\n );\n}\n\nasync function mergeGlobalStyles(\n projectPath: string,\n templatesDir: string\n): Promise<void> {\n const templateCssPath = path.join(templatesDir, 'app', 'globals.css');\n const projectCssPath = path.join(projectPath, 'src', 'app', 'globals.css');\n\n try {\n const templateCss = await readFile(templateCssPath, 'utf-8');\n const existingCss = await readFile(projectCssPath, 'utf-8');\n\n // Mantener las directivas de Tailwind del proyecto y agregar custom CSS\n const merged = existingCss + '\\n\\n/* LFT Custom Styles */\\n' + templateCss;\n await writeFile(projectCssPath, merged);\n } catch {\n // Si no existe el template CSS, no hacer nada\n }\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nconst TEMPLATE_DEPENDENCIES = [\n // Radix UI primitives\n '@radix-ui/react-accordion',\n '@radix-ui/react-alert-dialog',\n '@radix-ui/react-avatar',\n '@radix-ui/react-checkbox',\n '@radix-ui/react-collapsible',\n '@radix-ui/react-dialog',\n '@radix-ui/react-dropdown-menu',\n '@radix-ui/react-label',\n '@radix-ui/react-popover',\n '@radix-ui/react-progress',\n '@radix-ui/react-radio-group',\n '@radix-ui/react-scroll-area',\n '@radix-ui/react-select',\n '@radix-ui/react-separator',\n '@radix-ui/react-slider',\n '@radix-ui/react-slot',\n '@radix-ui/react-switch',\n '@radix-ui/react-tabs',\n '@radix-ui/react-tooltip',\n\n // UI Utilities\n 'class-variance-authority',\n 'clsx',\n 'tailwind-merge',\n\n // Icons\n 'lucide-react',\n\n // Form handling\n 'react-hook-form',\n '@hookform/resolvers',\n\n // Command menu\n 'cmdk',\n\n // Date picker\n 'react-day-picker',\n 'date-fns',\n\n // Toast notifications\n 'sonner',\n\n // Validation\n 'zod',\n\n // Supabase client\n '@supabase/supabase-js',\n '@supabase/ssr',\n];\n\nconst TEMPLATE_DEV_DEPENDENCIES = [\n 'tailwindcss-animate',\n];\n\nexport async function installDependencies(projectPath: string): Promise<void> {\n await withSpinner(\n `Instalando dependencias (${TEMPLATE_DEPENDENCIES.length} paquetes)...`,\n async () => {\n // Instalar dependencias de producción\n await execa('npm', ['install', ...TEMPLATE_DEPENDENCIES], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n\n // Instalar dependencias de desarrollo\n await execa('npm', ['install', '-D', ...TEMPLATE_DEV_DEPENDENCIES], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n },\n 'Dependencias instaladas'\n );\n}\n","import { writeFile, readFile, appendFile } from 'fs/promises';\nimport path from 'path';\nimport { withSpinner } from '../ui/spinner.js';\n\ninterface SupabaseKeys {\n url: string;\n anonKey: string;\n serviceKey: string;\n}\n\nexport async function createEnvFile(\n projectPath: string,\n supabaseKeys: SupabaseKeys\n): Promise<void> {\n await withSpinner(\n 'Creando archivo .env.local...',\n async () => {\n const envContent = `# Supabase\nNEXT_PUBLIC_SUPABASE_URL=${supabaseKeys.url}\nNEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}\nSUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}\n`;\n\n await writeFile(\n path.join(projectPath, '.env.local'),\n envContent\n );\n\n // Asegurar que .env.local esté en .gitignore\n const gitignorePath = path.join(projectPath, '.gitignore');\n try {\n const gitignore = await readFile(gitignorePath, 'utf-8');\n if (!gitignore.includes('.env.local')) {\n await appendFile(gitignorePath, '\\n# Environment variables\\n.env.local\\n.env*.local\\n');\n }\n } catch {\n // Si no existe .gitignore, crearlo\n await writeFile(gitignorePath, '# Environment variables\\n.env.local\\n.env*.local\\n');\n }\n },\n 'Archivo .env.local creado con credenciales de Supabase'\n );\n}\n","import { simpleGit } from 'simple-git';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function setupGit(\n projectPath: string,\n remoteUrl: string\n): Promise<void> {\n const git = simpleGit(projectPath);\n\n await withSpinner(\n 'Configurando Git...',\n async () => {\n // Verificar si ya es un repo git\n const isRepo = await git.checkIsRepo();\n\n if (!isRepo) {\n await git.init();\n }\n\n // Agregar remote\n try {\n await git.addRemote('origin', remoteUrl);\n } catch {\n // Remote ya existe, actualizarlo\n await git.remote(['set-url', 'origin', remoteUrl]);\n }\n\n // Stage todos los archivos\n await git.add('.');\n\n // Commit inicial\n await git.commit('Initial commit - created with create-lft-app', {\n '--author': 'create-lft-app <noreply@lft.dev>',\n });\n\n // Renombrar branch a main si es necesario\n try {\n await git.branch(['-M', 'main']);\n } catch {\n // Ya está en main\n }\n\n // Push\n await git.push(['--set-upstream', 'origin', 'main']);\n },\n 'Git configurado y código pusheado'\n );\n}\n","import boxen from 'boxen';\nimport chalk from 'chalk';\n\nexport function showBanner(): void {\n const title = chalk.bold.cyan('create-lft-app');\n const version = chalk.gray('v1.0.0');\n const description = chalk.white('Scaffolding para proyectos LFT');\n\n const banner = boxen(`${title} ${version}\\n${description}`, {\n padding: 1,\n margin: 1,\n borderStyle: 'round',\n borderColor: 'cyan',\n });\n\n console.log(banner);\n}\n\nexport function showSuccessBanner(projectName: string, urls: {\n github?: string;\n supabase?: string;\n jira?: string;\n}): void {\n const lines = [\n chalk.green.bold(`Proyecto \"${projectName}\" creado exitosamente!`),\n '',\n chalk.white('Directorio:') + ` ./${projectName}`,\n '',\n ];\n\n if (urls.github || urls.supabase || urls.jira) {\n lines.push(chalk.white('Enlaces:'));\n if (urls.github) {\n lines.push(` ${chalk.gray('GitHub:')} ${chalk.cyan(urls.github)}`);\n }\n if (urls.supabase) {\n lines.push(` ${chalk.gray('Supabase:')} ${chalk.cyan(urls.supabase)}`);\n }\n if (urls.jira) {\n lines.push(` ${chalk.gray('Jira:')} ${chalk.cyan(urls.jira)}`);\n }\n lines.push('');\n }\n\n lines.push(chalk.white('Siguiente pasos:'));\n lines.push(` ${chalk.cyan('cd')} ${projectName}`);\n lines.push(` ${chalk.cyan('npm run dev')}`);\n\n const banner = boxen(lines.join('\\n'), {\n padding: 1,\n margin: 1,\n borderStyle: 'round',\n borderColor: 'green',\n });\n\n console.log(banner);\n}\n"],"mappings":"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","names":["path","confirm","password","fs","path","project","path","__filename","__dirname","path","execa","execa","writeFile","readFile","path","writeFile","path","readFile","chalk","chalk","path","confirm"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/config/static-config.ts","../../src/config/index.ts","../../src/services/github.ts","../../src/ui/spinner.ts","../../src/services/supabase.ts","../../src/utils/validation.ts","../../src/services/jira.ts","../../src/steps/scaffold-nextjs.ts","../../src/steps/copy-template.ts","../../src/steps/install-deps.ts","../../src/steps/create-env.ts","../../src/steps/setup-git.ts","../../src/ui/logger.ts","../../src/ui/banner.ts"],"sourcesContent":["import path from 'path';\nimport { confirm } from '@inquirer/prompts';\nimport { loadConfig, hasConfig } from './config/index.js';\nimport { createGitHubRepo } from './services/github.js';\nimport { createSupabaseProject } from './services/supabase.js';\nimport { createJiraProject } from './services/jira.js';\nimport { scaffoldNextJs } from './steps/scaffold-nextjs.js';\nimport { copyTemplate } from './steps/copy-template.js';\nimport { installDependencies } from './steps/install-deps.js';\nimport { createEnvFile } from './steps/create-env.js';\nimport { setupGit } from './steps/setup-git.js';\nimport { logger } from './ui/logger.js';\nimport { showSuccessBanner } from './ui/banner.js';\nimport { validateProjectName } from './utils/validation.js';\n\nexport interface CreateProjectOptions {\n skipGithub?: boolean;\n skipSupabase?: boolean;\n skipJira?: boolean;\n skipGit?: boolean;\n autoConfirm?: boolean;\n}\n\nexport async function createProject(\n projectName: string,\n options: CreateProjectOptions = {}\n): Promise<void> {\n // Validar nombre del proyecto\n const validation = validateProjectName(projectName);\n if (!validation.valid) {\n throw new Error(validation.error);\n }\n\n // Verificar que el directorio no exista\n const projectPath = path.resolve(process.cwd(), projectName);\n\n // Cargar configuración\n if (!await hasConfig()) {\n logger.warning('No se encontró configuración. Ejecuta \"create-lft-app config\" primero.');\n throw new Error('Configuración no encontrada');\n }\n\n const config = await loadConfig();\n\n // Mostrar resumen\n logger.newLine();\n logger.title('Resumen de recursos a crear:');\n logger.newLine();\n\n const resources = [];\n if (!options.skipGithub) {\n resources.push({ label: 'GitHub', value: `${config.defaults.githubOrg || config.credentials.github.username}/${projectName} (privado)` });\n }\n if (!options.skipSupabase) {\n resources.push({ label: 'Supabase', value: `${projectName} en ${config.defaults.supabaseRegion}` });\n }\n if (!options.skipJira) {\n resources.push({ label: 'Jira', value: `Proyecto \"${projectName}\" en ${config.credentials.jira.domain}` });\n }\n resources.push({ label: 'Next.js', value: 'App Router + TypeScript + Tailwind + Dashboard' });\n\n logger.table(resources);\n logger.newLine();\n\n // Confirmar\n if (!options.autoConfirm) {\n const shouldContinue = await confirm({\n message: '¿Continuar con la creación?',\n default: true,\n });\n\n if (!shouldContinue) {\n logger.info('Operación cancelada');\n return;\n }\n }\n\n logger.newLine();\n logger.divider();\n logger.newLine();\n\n const urls: { github?: string; supabase?: string; jira?: string } = {};\n let supabaseKeys: { url: string; anonKey: string; serviceKey: string } | undefined;\n\n // Crear recursos externos en paralelo\n const externalTasks: Promise<void>[] = [];\n\n if (!options.skipGithub) {\n externalTasks.push(\n createGitHubRepo(projectName, config).then((url) => {\n urls.github = url;\n })\n );\n }\n\n if (!options.skipSupabase) {\n externalTasks.push(\n createSupabaseProject(projectName, config).then((result) => {\n urls.supabase = result.url;\n supabaseKeys = result;\n })\n );\n }\n\n if (!options.skipJira) {\n externalTasks.push(\n createJiraProject(projectName, config).then((url) => {\n urls.jira = url;\n })\n );\n }\n\n // Esperar a que terminen los recursos externos\n await Promise.all(externalTasks);\n\n // Scaffold Next.js\n await scaffoldNextJs(projectName, projectPath);\n\n // Copiar template\n await copyTemplate(projectPath);\n\n // Instalar dependencias\n await installDependencies(projectPath);\n\n // Crear .env.local\n if (supabaseKeys) {\n await createEnvFile(projectPath, supabaseKeys);\n }\n\n // Setup git\n if (!options.skipGit && urls.github) {\n await setupGit(projectPath, urls.github);\n }\n\n // Mostrar resumen final\n logger.newLine();\n showSuccessBanner(projectName, urls);\n}\n","// ================================================\n// CONFIGURACIÓN ESTÁTICA - CREDENCIALES EMBEBIDAS\n// ================================================\n// Edita estos valores antes de hacer build y publish\n\nexport const STATIC_CONFIG = {\n github: {\n token: '__GITHUB_TOKEN__',\n username: '__GITHUB_USERNAME__',\n org: '__GITHUB_ORG__',\n },\n supabase: {\n token: '__SUPABASE_TOKEN__',\n orgId: '__SUPABASE_ORG_ID__',\n region: 'us-east-1',\n },\n jira: {\n email: '__JIRA_EMAIL__',\n token: '__JIRA_TOKEN__',\n domain: '__JIRA_DOMAIN__',\n },\n};\n","import { STATIC_CONFIG } from './static-config.js';\n\nexport interface LFTConfig {\n version: string;\n credentials: {\n github: {\n token: string;\n username: string;\n };\n supabase: {\n accessToken: string;\n organizationId: string;\n };\n jira: {\n email: string;\n apiToken: string;\n domain: string;\n };\n };\n defaults: {\n githubOrg?: string;\n supabaseRegion: string;\n jiraProjectType: string;\n };\n}\n\nexport async function loadConfig(): Promise<LFTConfig> {\n return {\n version: '1.0.0',\n credentials: {\n github: {\n token: STATIC_CONFIG.github.token,\n username: STATIC_CONFIG.github.username,\n },\n supabase: {\n accessToken: STATIC_CONFIG.supabase.token,\n organizationId: STATIC_CONFIG.supabase.orgId,\n },\n jira: {\n email: STATIC_CONFIG.jira.email,\n apiToken: STATIC_CONFIG.jira.token,\n domain: STATIC_CONFIG.jira.domain,\n },\n },\n defaults: {\n githubOrg: STATIC_CONFIG.github.org || undefined,\n supabaseRegion: STATIC_CONFIG.supabase.region,\n jiraProjectType: 'software',\n },\n };\n}\n\nexport async function hasConfig(): Promise<boolean> {\n return (\n STATIC_CONFIG.github.token !== '__GITHUB_TOKEN__' &&\n STATIC_CONFIG.supabase.token !== '__SUPABASE_TOKEN__' &&\n STATIC_CONFIG.jira.token !== '__JIRA_TOKEN__'\n );\n}\n","import { Octokit } from 'octokit';\nimport { withSpinner } from '../ui/spinner.js';\nimport type { LFTConfig } from '../config/index.js';\n\nexport async function createGitHubRepo(\n projectName: string,\n config: LFTConfig\n): Promise<string> {\n const octokit = new Octokit({ auth: config.credentials.github.token });\n const org = config.defaults.githubOrg;\n\n return withSpinner(\n 'Creando repositorio en GitHub...',\n async () => {\n let repo;\n\n if (org) {\n // Crear en organización\n repo = await octokit.rest.repos.createInOrg({\n org,\n name: projectName,\n private: true,\n auto_init: false,\n description: `Proyecto ${projectName} creado con create-lft-app`,\n });\n } else {\n // Crear en cuenta personal\n repo = await octokit.rest.repos.createForAuthenticatedUser({\n name: projectName,\n private: true,\n auto_init: false,\n description: `Proyecto ${projectName} creado con create-lft-app`,\n });\n }\n\n return repo.data.html_url;\n },\n `Repositorio creado: ${org || config.credentials.github.username}/${projectName}`\n );\n}\n\nexport async function validateGitHubToken(token: string): Promise<{ valid: boolean; username?: string }> {\n try {\n const octokit = new Octokit({ auth: token });\n const { data } = await octokit.rest.users.getAuthenticated();\n return { valid: true, username: data.login };\n } catch {\n return { valid: false };\n }\n}\n","import ora, { type Ora } from 'ora';\n\nexport function createSpinner(text: string): Ora {\n return ora({\n text,\n spinner: 'dots',\n });\n}\n\nexport async function withSpinner<T>(\n text: string,\n fn: () => Promise<T>,\n successText?: string\n): Promise<T> {\n const spinner = createSpinner(text).start();\n\n try {\n const result = await fn();\n spinner.succeed(successText || text);\n return result;\n } catch (error) {\n spinner.fail();\n throw error;\n }\n}\n","import { withSpinner, createSpinner } from '../ui/spinner.js';\nimport type { LFTConfig } from '../config/index.js';\n\ninterface SupabaseProjectResponse {\n id: string;\n name: string;\n organization_id: string;\n region: string;\n status: string;\n}\n\ninterface SupabaseApiKey {\n name: string;\n api_key: string;\n}\n\nexport interface SupabaseProjectResult {\n url: string;\n anonKey: string;\n serviceKey: string;\n}\n\nconst SUPABASE_API_URL = 'https://api.supabase.com/v1';\n\nasync function waitForProjectReady(\n projectId: string,\n token: string,\n maxAttempts = 60\n): Promise<void> {\n const spinner = createSpinner('Provisionando base de datos (esto puede tomar ~2 minutos)...').start();\n\n for (let i = 0; i < maxAttempts; i++) {\n const response = await fetch(`${SUPABASE_API_URL}/projects/${projectId}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (response.ok) {\n const project = (await response.json()) as SupabaseProjectResponse;\n if (project.status === 'ACTIVE_HEALTHY') {\n spinner.succeed('Base de datos provisionada');\n return;\n }\n }\n\n // Esperar 5 segundos antes del siguiente intento\n await new Promise((resolve) => setTimeout(resolve, 5000));\n spinner.text = `Provisionando base de datos... (${Math.floor((i + 1) * 5 / 60)}min ${((i + 1) * 5) % 60}s)`;\n }\n\n spinner.fail('Timeout esperando a que el proyecto esté listo');\n throw new Error('Timeout: el proyecto de Supabase no se activó a tiempo');\n}\n\nasync function getProjectApiKeys(\n projectId: string,\n token: string\n): Promise<{ anonKey: string; serviceKey: string }> {\n const response = await fetch(`${SUPABASE_API_URL}/projects/${projectId}/api-keys`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) {\n throw new Error('No se pudieron obtener las API keys de Supabase');\n }\n\n const keys = (await response.json()) as SupabaseApiKey[];\n\n const anonKey = keys.find((k) => k.name === 'anon')?.api_key;\n const serviceKey = keys.find((k) => k.name === 'service_role')?.api_key;\n\n if (!anonKey || !serviceKey) {\n throw new Error('No se encontraron las API keys necesarias');\n }\n\n return { anonKey, serviceKey };\n}\n\nexport async function createSupabaseProject(\n projectName: string,\n config: LFTConfig\n): Promise<SupabaseProjectResult> {\n const token = config.credentials.supabase.accessToken;\n const orgId = config.credentials.supabase.organizationId;\n const region = config.defaults.supabaseRegion;\n\n // Generar una contraseña segura para la base de datos\n const dbPassword = generateSecurePassword();\n\n // Crear proyecto\n const project = await withSpinner(\n 'Creando proyecto en Supabase...',\n async () => {\n const response = await fetch(`${SUPABASE_API_URL}/projects`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n name: projectName,\n organization_id: orgId,\n region,\n plan: 'free',\n db_pass: dbPassword,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Error creando proyecto Supabase: ${error}`);\n }\n\n return response.json() as Promise<SupabaseProjectResponse>;\n }\n );\n\n // Esperar a que el proyecto esté listo\n await waitForProjectReady(project.id, token);\n\n // Obtener API keys\n const { anonKey, serviceKey } = await getProjectApiKeys(project.id, token);\n\n const projectUrl = `https://${project.id}.supabase.co`;\n\n return {\n url: projectUrl,\n anonKey,\n serviceKey,\n };\n}\n\nfunction generateSecurePassword(): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';\n let password = '';\n for (let i = 0; i < 32; i++) {\n password += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return password;\n}\n","import fs from 'fs';\nimport path from 'path';\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n // Verificar que no esté vacío\n if (!name || name.trim() === '') {\n return { valid: false, error: 'El nombre del proyecto no puede estar vacío' };\n }\n\n // Verificar caracteres válidos (solo letras, números, guiones y guiones bajos)\n const validPattern = /^[a-zA-Z0-9_-]+$/;\n if (!validPattern.test(name)) {\n return {\n valid: false,\n error: 'El nombre solo puede contener letras, números, guiones (-) y guiones bajos (_)',\n };\n }\n\n // Verificar que no empiece con guión o número\n if (/^[-_0-9]/.test(name)) {\n return {\n valid: false,\n error: 'El nombre debe empezar con una letra',\n };\n }\n\n // Verificar longitud\n if (name.length < 2) {\n return { valid: false, error: 'El nombre debe tener al menos 2 caracteres' };\n }\n\n if (name.length > 50) {\n return { valid: false, error: 'El nombre no puede tener más de 50 caracteres' };\n }\n\n // Verificar que el directorio no exista\n const projectPath = path.resolve(process.cwd(), name);\n if (fs.existsSync(projectPath)) {\n return { valid: false, error: `El directorio \"${name}\" ya existe` };\n }\n\n return { valid: true };\n}\n\nexport function generateJiraKey(projectName: string): string {\n // Generar key de Jira: máximo 10 caracteres, solo mayúsculas\n const cleaned = projectName\n .replace(/[^a-zA-Z0-9]/g, '')\n .toUpperCase()\n .slice(0, 10);\n\n return cleaned || 'PROJ';\n}\n","import { withSpinner } from '../ui/spinner.js';\nimport { generateJiraKey } from '../utils/validation.js';\nimport type { LFTConfig } from '../config/index.js';\n\ninterface JiraProjectResponse {\n id: string;\n key: string;\n name: string;\n self: string;\n}\n\nexport async function createJiraProject(\n projectName: string,\n config: LFTConfig\n): Promise<string> {\n const { email, apiToken, domain } = config.credentials.jira;\n const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');\n const projectKey = generateJiraKey(projectName);\n\n return withSpinner(\n 'Creando proyecto en Jira...',\n async () => {\n // Primero obtener el leadAccountId del usuario actual\n const meResponse = await fetch(`https://${domain}/rest/api/3/myself`, {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!meResponse.ok) {\n throw new Error('No se pudo obtener información del usuario de Jira');\n }\n\n const me = (await meResponse.json()) as { accountId: string };\n\n // Crear el proyecto\n const response = await fetch(`https://${domain}/rest/api/3/project`, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n key: projectKey,\n name: projectName,\n projectTypeKey: 'software',\n projectTemplateKey: 'com.pyxis.greenhopper.jira:gh-simplified-agility-scrum',\n leadAccountId: me.accountId,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n\n // Si el key ya existe, intentar con un sufijo numérico\n if (error.includes('project key')) {\n const newKey = `${projectKey}${Date.now().toString().slice(-4)}`;\n const retryResponse = await fetch(`https://${domain}/rest/api/3/project`, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n key: newKey,\n name: projectName,\n projectTypeKey: 'software',\n projectTemplateKey: 'com.pyxis.greenhopper.jira:gh-simplified-agility-scrum',\n leadAccountId: me.accountId,\n }),\n });\n\n if (!retryResponse.ok) {\n throw new Error(`Error creando proyecto Jira: ${await retryResponse.text()}`);\n }\n\n const project = (await retryResponse.json()) as JiraProjectResponse;\n return `https://${domain}/browse/${project.key}`;\n }\n\n throw new Error(`Error creando proyecto Jira: ${error}`);\n }\n\n const project = (await response.json()) as JiraProjectResponse;\n return `https://${domain}/browse/${project.key}`;\n },\n `Proyecto Jira creado: ${projectKey}`\n );\n}\n\nexport async function validateJiraCredentials(\n email: string,\n apiToken: string,\n domain: string\n): Promise<boolean> {\n try {\n const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');\n const response = await fetch(`https://${domain}/rest/api/3/myself`, {\n headers: { Authorization: `Basic ${auth}` },\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function scaffoldNextJs(\n projectName: string,\n projectPath: string\n): Promise<void> {\n await withSpinner(\n 'Inicializando proyecto Next.js...',\n async () => {\n await execa('npx', [\n 'create-next-app@latest',\n projectName,\n '--typescript',\n '--tailwind',\n '--eslint',\n '--app',\n '--turbopack',\n '--src-dir',\n '--import-alias', '@/*',\n '--use-npm',\n ], {\n cwd: process.cwd(),\n stdio: 'pipe',\n });\n },\n 'Proyecto Next.js inicializado'\n );\n}\n","import { cp, mkdir, readFile, writeFile } from 'fs/promises';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { withSpinner } from '../ui/spinner.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport async function copyTemplate(projectPath: string): Promise<void> {\n await withSpinner(\n 'Copiando template LFT...',\n async () => {\n // Ruta a los templates (relativa al dist)\n const templatesDir = path.join(__dirname, '..', '..', 'templates');\n const srcDir = path.join(projectPath, 'src');\n\n // Copiar componentes UI\n await cp(\n path.join(templatesDir, 'components', 'ui'),\n path.join(srcDir, 'components', 'ui'),\n { recursive: true }\n );\n\n // Copiar componentes de layout\n await cp(\n path.join(templatesDir, 'components', 'layout'),\n path.join(srcDir, 'components', 'layout'),\n { recursive: true }\n );\n\n // Copiar componentes de dashboard\n await cp(\n path.join(templatesDir, 'components', 'dashboard'),\n path.join(srcDir, 'components', 'dashboard'),\n { recursive: true }\n );\n\n // Copiar lib/utils.ts\n await mkdir(path.join(srcDir, 'lib'), { recursive: true });\n await cp(\n path.join(templatesDir, 'lib', 'utils.ts'),\n path.join(srcDir, 'lib', 'utils.ts')\n );\n\n // Copiar hooks\n await mkdir(path.join(srcDir, 'hooks'), { recursive: true });\n await cp(\n path.join(templatesDir, 'hooks'),\n path.join(srcDir, 'hooks'),\n { recursive: true }\n );\n\n // Copiar páginas de app\n await cp(\n path.join(templatesDir, 'app', 'layout.tsx'),\n path.join(srcDir, 'app', 'layout.tsx')\n );\n\n await cp(\n path.join(templatesDir, 'app', 'page.tsx'),\n path.join(srcDir, 'app', 'page.tsx')\n );\n\n // Copiar dashboard page\n await mkdir(path.join(srcDir, 'app', 'dashboard'), { recursive: true });\n await cp(\n path.join(templatesDir, 'app', 'dashboard', 'page.tsx'),\n path.join(srcDir, 'app', 'dashboard', 'page.tsx')\n );\n\n // Copiar auth/login page\n await mkdir(path.join(srcDir, 'app', 'auth', 'login'), { recursive: true });\n await cp(\n path.join(templatesDir, 'app', 'auth', 'login', 'page.tsx'),\n path.join(srcDir, 'app', 'auth', 'login', 'page.tsx')\n );\n\n // Merge globals.css\n await mergeGlobalStyles(projectPath, templatesDir);\n },\n 'Template LFT copiado (47 componentes + páginas)'\n );\n}\n\nasync function mergeGlobalStyles(\n projectPath: string,\n templatesDir: string\n): Promise<void> {\n const templateCssPath = path.join(templatesDir, 'app', 'globals.css');\n const projectCssPath = path.join(projectPath, 'src', 'app', 'globals.css');\n\n try {\n const templateCss = await readFile(templateCssPath, 'utf-8');\n const existingCss = await readFile(projectCssPath, 'utf-8');\n\n // Mantener las directivas de Tailwind del proyecto y agregar custom CSS\n const merged = existingCss + '\\n\\n/* LFT Custom Styles */\\n' + templateCss;\n await writeFile(projectCssPath, merged);\n } catch {\n // Si no existe el template CSS, no hacer nada\n }\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nconst TEMPLATE_DEPENDENCIES = [\n // Radix UI primitives\n '@radix-ui/react-accordion',\n '@radix-ui/react-alert-dialog',\n '@radix-ui/react-avatar',\n '@radix-ui/react-checkbox',\n '@radix-ui/react-collapsible',\n '@radix-ui/react-dialog',\n '@radix-ui/react-dropdown-menu',\n '@radix-ui/react-label',\n '@radix-ui/react-popover',\n '@radix-ui/react-progress',\n '@radix-ui/react-radio-group',\n '@radix-ui/react-scroll-area',\n '@radix-ui/react-select',\n '@radix-ui/react-separator',\n '@radix-ui/react-slider',\n '@radix-ui/react-slot',\n '@radix-ui/react-switch',\n '@radix-ui/react-tabs',\n '@radix-ui/react-tooltip',\n\n // UI Utilities\n 'class-variance-authority',\n 'clsx',\n 'tailwind-merge',\n\n // Icons\n 'lucide-react',\n\n // Form handling\n 'react-hook-form',\n '@hookform/resolvers',\n\n // Command menu\n 'cmdk',\n\n // Date picker\n 'react-day-picker',\n 'date-fns',\n\n // Toast notifications\n 'sonner',\n\n // Validation\n 'zod',\n\n // Supabase client\n '@supabase/supabase-js',\n '@supabase/ssr',\n];\n\nconst TEMPLATE_DEV_DEPENDENCIES = [\n 'tailwindcss-animate',\n];\n\nexport async function installDependencies(projectPath: string): Promise<void> {\n await withSpinner(\n `Instalando dependencias (${TEMPLATE_DEPENDENCIES.length} paquetes)...`,\n async () => {\n // Instalar dependencias de producción\n await execa('npm', ['install', ...TEMPLATE_DEPENDENCIES], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n\n // Instalar dependencias de desarrollo\n await execa('npm', ['install', '-D', ...TEMPLATE_DEV_DEPENDENCIES], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n },\n 'Dependencias instaladas'\n );\n}\n","import { writeFile, readFile, appendFile } from 'fs/promises';\nimport path from 'path';\nimport { withSpinner } from '../ui/spinner.js';\n\ninterface SupabaseKeys {\n url: string;\n anonKey: string;\n serviceKey: string;\n}\n\nexport async function createEnvFile(\n projectPath: string,\n supabaseKeys: SupabaseKeys\n): Promise<void> {\n await withSpinner(\n 'Creando archivo .env.local...',\n async () => {\n const envContent = `# Supabase\nNEXT_PUBLIC_SUPABASE_URL=${supabaseKeys.url}\nNEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}\nSUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}\n`;\n\n await writeFile(\n path.join(projectPath, '.env.local'),\n envContent\n );\n\n // Asegurar que .env.local esté en .gitignore\n const gitignorePath = path.join(projectPath, '.gitignore');\n try {\n const gitignore = await readFile(gitignorePath, 'utf-8');\n if (!gitignore.includes('.env.local')) {\n await appendFile(gitignorePath, '\\n# Environment variables\\n.env.local\\n.env*.local\\n');\n }\n } catch {\n // Si no existe .gitignore, crearlo\n await writeFile(gitignorePath, '# Environment variables\\n.env.local\\n.env*.local\\n');\n }\n },\n 'Archivo .env.local creado con credenciales de Supabase'\n );\n}\n","import { simpleGit } from 'simple-git';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function setupGit(\n projectPath: string,\n remoteUrl: string\n): Promise<void> {\n const git = simpleGit(projectPath);\n\n await withSpinner(\n 'Configurando Git...',\n async () => {\n // Verificar si ya es un repo git\n const isRepo = await git.checkIsRepo();\n\n if (!isRepo) {\n await git.init();\n }\n\n // Agregar remote\n try {\n await git.addRemote('origin', remoteUrl);\n } catch {\n // Remote ya existe, actualizarlo\n await git.remote(['set-url', 'origin', remoteUrl]);\n }\n\n // Stage todos los archivos\n await git.add('.');\n\n // Commit inicial\n await git.commit('Initial commit - created with create-lft-app', {\n '--author': 'create-lft-app <noreply@lft.dev>',\n });\n\n // Renombrar branch a main si es necesario\n try {\n await git.branch(['-M', 'main']);\n } catch {\n // Ya está en main\n }\n\n // Push\n await git.push(['--set-upstream', 'origin', 'main']);\n },\n 'Git configurado y código pusheado'\n );\n}\n","import chalk from 'chalk';\n\nexport const logger = {\n info: (message: string) => {\n console.log(chalk.blue('ℹ'), message);\n },\n\n success: (message: string) => {\n console.log(chalk.green('✔'), message);\n },\n\n warning: (message: string) => {\n console.log(chalk.yellow('⚠'), message);\n },\n\n error: (message: string) => {\n console.log(chalk.red('✖'), message);\n },\n\n step: (step: number, total: number, message: string) => {\n console.log(chalk.cyan(`[${step}/${total}]`), message);\n },\n\n newLine: () => {\n console.log();\n },\n\n divider: () => {\n console.log(chalk.gray('─'.repeat(50)));\n },\n\n title: (message: string) => {\n console.log(chalk.bold.white(message));\n },\n\n subtitle: (message: string) => {\n console.log(chalk.gray(message));\n },\n\n link: (label: string, url: string) => {\n console.log(` ${chalk.gray(label + ':')} ${chalk.cyan.underline(url)}`);\n },\n\n list: (items: string[]) => {\n items.forEach((item) => {\n console.log(chalk.gray(' •'), item);\n });\n },\n\n table: (rows: Array<{ label: string; value: string }>) => {\n const maxLabelLength = Math.max(...rows.map((r) => r.label.length));\n rows.forEach(({ label, value }) => {\n const paddedLabel = label.padEnd(maxLabelLength);\n console.log(` ${chalk.gray(paddedLabel)} ${value}`);\n });\n },\n};\n","import boxen from 'boxen';\nimport chalk from 'chalk';\n\nexport function showBanner(): void {\n const title = chalk.bold.cyan('create-lft-app');\n const version = chalk.gray('v1.0.0');\n const description = chalk.white('Scaffolding para proyectos LFT');\n\n const banner = boxen(`${title} ${version}\\n${description}`, {\n padding: 1,\n margin: 1,\n borderStyle: 'round',\n borderColor: 'cyan',\n });\n\n console.log(banner);\n}\n\nexport function showSuccessBanner(projectName: string, urls: {\n github?: string;\n supabase?: string;\n jira?: string;\n}): void {\n const lines = [\n chalk.green.bold(`Proyecto \"${projectName}\" creado exitosamente!`),\n '',\n chalk.white('Directorio:') + ` ./${projectName}`,\n '',\n ];\n\n if (urls.github || urls.supabase || urls.jira) {\n lines.push(chalk.white('Enlaces:'));\n if (urls.github) {\n lines.push(` ${chalk.gray('GitHub:')} ${chalk.cyan(urls.github)}`);\n }\n if (urls.supabase) {\n lines.push(` ${chalk.gray('Supabase:')} ${chalk.cyan(urls.supabase)}`);\n }\n if (urls.jira) {\n lines.push(` ${chalk.gray('Jira:')} ${chalk.cyan(urls.jira)}`);\n }\n lines.push('');\n }\n\n lines.push(chalk.white('Siguiente pasos:'));\n lines.push(` ${chalk.cyan('cd')} ${projectName}`);\n lines.push(` ${chalk.cyan('npm run dev')}`);\n\n const banner = boxen(lines.join('\\n'), {\n padding: 1,\n margin: 1,\n borderStyle: 'round',\n borderColor: 'green',\n });\n\n console.log(banner);\n}\n"],"mappings":"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","names":["path","project","path","__filename","__dirname","path","execa","execa","writeFile","readFile","path","writeFile","path","readFile","chalk","chalk","path"]}