@create-lft-app/cli 1.1.0 → 1.1.2

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/bin/cli.js CHANGED
@@ -5,6 +5,7 @@ import { program } from "commander";
5
5
 
6
6
  // src/index.ts
7
7
  import path3 from "path";
8
+ import { existsSync as existsSync3 } from "fs";
8
9
  import { confirm } from "@inquirer/prompts";
9
10
 
10
11
  // src/config/index.ts
@@ -110,6 +111,51 @@ async function withSpinner(text, fn, successText) {
110
111
  }
111
112
  }
112
113
 
114
+ // src/services/github.ts
115
+ async function createGitHubRepo(projectName, config) {
116
+ const octokit = new Octokit({ auth: config.credentials.github.token });
117
+ const org = config.defaults.githubOrg;
118
+ const owner = org || config.credentials.github.username;
119
+ return withSpinner(
120
+ "Creando repositorio en GitHub...",
121
+ async () => {
122
+ let repo;
123
+ if (org) {
124
+ repo = await octokit.rest.repos.createInOrg({
125
+ org,
126
+ name: projectName,
127
+ private: true,
128
+ auto_init: false,
129
+ description: `Proyecto ${projectName} creado con create-lft-app`
130
+ });
131
+ } else {
132
+ repo = await octokit.rest.repos.createForAuthenticatedUser({
133
+ name: projectName,
134
+ private: true,
135
+ auto_init: false,
136
+ description: `Proyecto ${projectName} creado con create-lft-app`
137
+ });
138
+ }
139
+ return repo.data.ssh_url;
140
+ },
141
+ `GitHub: ${owner}/${projectName}`
142
+ );
143
+ }
144
+ async function checkGitHubRepoExists(projectName, config) {
145
+ const octokit = new Octokit({ auth: config.credentials.github.token });
146
+ const org = config.defaults.githubOrg;
147
+ const owner = org || config.credentials.github.username;
148
+ try {
149
+ const existing = await octokit.rest.repos.get({
150
+ owner,
151
+ repo: projectName
152
+ });
153
+ return { exists: true, url: existing.data.ssh_url };
154
+ } catch {
155
+ return { exists: false };
156
+ }
157
+ }
158
+
113
159
  // src/ui/logger.ts
114
160
  import chalk from "chalk";
115
161
  var logger = {
@@ -157,46 +203,6 @@ var logger = {
157
203
  }
158
204
  };
159
205
 
160
- // src/services/github.ts
161
- async function createGitHubRepo(projectName, config) {
162
- const octokit = new Octokit({ auth: config.credentials.github.token });
163
- const org = config.defaults.githubOrg;
164
- const owner = org || config.credentials.github.username;
165
- try {
166
- const existing = await octokit.rest.repos.get({
167
- owner,
168
- repo: projectName
169
- });
170
- logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
171
- return existing.data.html_url;
172
- } catch {
173
- }
174
- return withSpinner(
175
- "Creando repositorio en GitHub...",
176
- async () => {
177
- let repo;
178
- if (org) {
179
- repo = await octokit.rest.repos.createInOrg({
180
- org,
181
- name: projectName,
182
- private: true,
183
- auto_init: false,
184
- description: `Proyecto ${projectName} creado con create-lft-app`
185
- });
186
- } else {
187
- repo = await octokit.rest.repos.createForAuthenticatedUser({
188
- name: projectName,
189
- private: true,
190
- auto_init: false,
191
- description: `Proyecto ${projectName} creado con create-lft-app`
192
- });
193
- }
194
- return repo.data.html_url;
195
- },
196
- `GitHub: ${owner}/${projectName}`
197
- );
198
- }
199
-
200
206
  // src/services/supabase.ts
201
207
  var SUPABASE_API_URL = "https://api.supabase.com/v1";
202
208
  async function waitForProjectReady(projectId, token, maxAttempts = 60) {
@@ -297,6 +303,29 @@ function generateSecurePassword() {
297
303
  }
298
304
  return password;
299
305
  }
306
+ async function checkSupabaseProjectExists(projectName, config) {
307
+ const token = config.credentials.supabase.accessToken;
308
+ const existing = await findExistingProject(projectName, token);
309
+ if (existing) {
310
+ return { exists: true, url: `https://${existing.id}.supabase.co`, projectId: existing.id };
311
+ }
312
+ return { exists: false };
313
+ }
314
+ async function getExistingSupabaseKeys(projectId, config) {
315
+ return withSpinner(
316
+ "Obteniendo credenciales de Supabase...",
317
+ async () => {
318
+ const token = config.credentials.supabase.accessToken;
319
+ const { anonKey, serviceKey } = await getProjectApiKeys(projectId, token);
320
+ return {
321
+ url: `https://${projectId}.supabase.co`,
322
+ anonKey,
323
+ serviceKey
324
+ };
325
+ },
326
+ "Supabase: credenciales obtenidas"
327
+ );
328
+ }
300
329
 
301
330
  // src/utils/validation.ts
302
331
  function validateProjectName(name) {
@@ -348,11 +377,6 @@ async function createJiraProject(projectName, config) {
348
377
  const { email, apiToken, domain } = config.credentials.jira;
349
378
  const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
350
379
  const projectKey = generateJiraKey(projectName);
351
- const existing = await findExistingJiraProject(projectName, auth, domain);
352
- if (existing) {
353
- logger.success(`Jira: ${existing.key} (ya existe)`);
354
- return `https://${domain}/browse/${existing.key}`;
355
- }
356
380
  return withSpinner(
357
381
  "Creando proyecto en Jira...",
358
382
  async () => {
@@ -412,6 +436,15 @@ async function createJiraProject(projectName, config) {
412
436
  `Proyecto Jira: ${projectKey}`
413
437
  );
414
438
  }
439
+ async function checkJiraProjectExists(projectName, config) {
440
+ const { email, apiToken, domain } = config.credentials.jira;
441
+ const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
442
+ const existing = await findExistingJiraProject(projectName, auth, domain);
443
+ if (existing) {
444
+ return { exists: true, url: `https://${domain}/browse/${existing.key}` };
445
+ }
446
+ return { exists: false };
447
+ }
415
448
 
416
449
  // src/steps/scaffold-nextjs.ts
417
450
  import { execa } from "execa";
@@ -492,10 +525,7 @@ async function setupGit(projectPath, remoteUrl) {
492
525
  await withSpinner(
493
526
  "Configurando Git...",
494
527
  async () => {
495
- const isRepo = await git.checkIsRepo();
496
- if (!isRepo) {
497
- await git.init();
498
- }
528
+ await git.init();
499
529
  try {
500
530
  await git.addRemote("origin", remoteUrl);
501
531
  } catch {
@@ -518,11 +548,75 @@ async function setupGit(projectPath, remoteUrl) {
518
548
  // src/ui/banner.ts
519
549
  import boxen from "boxen";
520
550
  import chalk2 from "chalk";
551
+ import { readFileSync as readFileSync2 } from "fs";
552
+ import { fileURLToPath } from "url";
553
+ import { dirname, join } from "path";
554
+ function getVersion() {
555
+ try {
556
+ const __filename2 = fileURLToPath(import.meta.url);
557
+ const __dirname2 = dirname(__filename2);
558
+ const pkgPath = join(__dirname2, "..", "..", "package.json");
559
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
560
+ return pkg.version || "0.0.0";
561
+ } catch {
562
+ return "0.0.0";
563
+ }
564
+ }
565
+ function getKangarooArt() {
566
+ const c = chalk2.cyan;
567
+ return c(`
568
+ \u2588\u2588 \u2588\u2588\u2588
569
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
570
+ \u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
571
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
572
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
573
+ \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588
574
+ \u2588\u2588 \u2588 \u2588\u2588\u2588\u2588\u2588
575
+ \u2588\u2588 \u2588 \u2588\u2588\u2588\u2588
576
+ \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588
577
+ \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
578
+ \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588
579
+ \u2588\u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588
580
+ \u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
581
+ \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
582
+ \u2588\u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
583
+ \u2588\u2588 \u2588 \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
584
+ \u2588\u2588 \u2588\u2588 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
585
+ \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588
586
+ \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588
587
+ \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
588
+ \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
589
+ \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
590
+ \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
591
+ \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588
592
+ \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
593
+ \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588
594
+ \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588
595
+ \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588
596
+ \u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588
597
+ \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588
598
+ \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588
599
+ \u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588
600
+ \u2588 \u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588
601
+ \u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588
602
+ \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588
603
+ \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588 \u2588 \u2588
604
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588
605
+ \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588
606
+ \u2588\u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
607
+ \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588
608
+ \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
609
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588
610
+ \u2588\u2588
611
+ `);
612
+ }
521
613
  function showBanner() {
614
+ const version = getVersion();
522
615
  const title = chalk2.bold.cyan("create-lft-app");
523
- const version = chalk2.gray("v1.0.0");
616
+ const versionText = chalk2.gray(`v${version}`);
524
617
  const description = chalk2.white("Scaffolding para proyectos LFT");
525
- const banner = boxen(`${title} ${version}
618
+ console.log(getKangarooArt());
619
+ const banner = boxen(`${title} ${versionText}
526
620
  ${description}`, {
527
621
  padding: 1,
528
622
  margin: 1,
@@ -575,25 +669,72 @@ async function createProject(projectName, options = {}) {
575
669
  throw new Error("Configuraci\xF3n no encontrada");
576
670
  }
577
671
  const config = await loadConfig();
672
+ logger.info("Verificando recursos existentes...");
673
+ const status = {
674
+ github: { exists: false },
675
+ supabase: { exists: false },
676
+ jira: { exists: false },
677
+ nextjs: { exists: existsSync3(projectPath) }
678
+ };
679
+ const checks = [];
680
+ if (!options.skipGithub) {
681
+ checks.push(
682
+ checkGitHubRepoExists(projectName, config).then((result) => {
683
+ status.github = result;
684
+ })
685
+ );
686
+ }
687
+ if (!options.skipSupabase) {
688
+ checks.push(
689
+ checkSupabaseProjectExists(projectName, config).then((result) => {
690
+ status.supabase = result;
691
+ })
692
+ );
693
+ }
694
+ if (!options.skipJira) {
695
+ checks.push(
696
+ checkJiraProjectExists(projectName, config).then((result) => {
697
+ status.jira = result;
698
+ })
699
+ );
700
+ }
701
+ await Promise.all(checks);
578
702
  logger.newLine();
579
- logger.title("Resumen de recursos a crear:");
703
+ logger.title("Resumen de recursos:");
580
704
  logger.newLine();
581
705
  const resources = [];
706
+ const owner = config.defaults.githubOrg || config.credentials.github.username;
582
707
  if (!options.skipGithub) {
583
- resources.push({ label: "GitHub", value: `${config.defaults.githubOrg || config.credentials.github.username}/${projectName} (privado)` });
708
+ const githubStatus = status.github.exists ? "\u2713 ya existe" : "se crear\xE1";
709
+ resources.push({
710
+ label: "GitHub",
711
+ value: `${owner}/${projectName} (${githubStatus})`
712
+ });
584
713
  }
585
714
  if (!options.skipSupabase) {
586
- resources.push({ label: "Supabase", value: `${projectName} en ${config.defaults.supabaseRegion}` });
715
+ const supabaseStatus = status.supabase.exists ? "\u2713 ya existe" : "se crear\xE1";
716
+ resources.push({
717
+ label: "Supabase",
718
+ value: `${projectName} en ${config.defaults.supabaseRegion} (${supabaseStatus})`
719
+ });
587
720
  }
588
721
  if (!options.skipJira) {
589
- resources.push({ label: "Jira", value: `Proyecto "${projectName}" en ${config.credentials.jira.domain}` });
722
+ const jiraStatus = status.jira.exists ? "\u2713 ya existe" : "se crear\xE1";
723
+ resources.push({
724
+ label: "Jira",
725
+ value: `Proyecto "${projectName}" (${jiraStatus})`
726
+ });
590
727
  }
591
- resources.push({ label: "Next.js", value: "App Router + TypeScript + Tailwind + Dashboard" });
728
+ const nextjsStatus = status.nextjs.exists ? "\u2713 ya existe" : "se crear\xE1";
729
+ resources.push({
730
+ label: "Next.js",
731
+ value: `App Router + TypeScript + Tailwind (${nextjsStatus})`
732
+ });
592
733
  logger.table(resources);
593
734
  logger.newLine();
594
735
  if (!options.autoConfirm) {
595
736
  const shouldContinue = await confirm({
596
- message: "\xBFContinuar con la creaci\xF3n?",
737
+ message: "\xBFContinuar?",
597
738
  default: true
598
739
  });
599
740
  if (!shouldContinue) {
@@ -606,36 +747,43 @@ async function createProject(projectName, options = {}) {
606
747
  logger.newLine();
607
748
  const urls = {};
608
749
  let supabaseKeys;
609
- const externalTasks = [];
610
750
  if (!options.skipGithub) {
611
- externalTasks.push(
612
- createGitHubRepo(projectName, config).then((url) => {
613
- urls.github = url;
614
- })
615
- );
751
+ if (status.github.exists) {
752
+ urls.github = status.github.url;
753
+ logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
754
+ } else {
755
+ urls.github = await createGitHubRepo(projectName, config);
756
+ }
757
+ }
758
+ if (!options.skipJira) {
759
+ if (status.jira.exists) {
760
+ urls.jira = status.jira.url;
761
+ logger.success(`Jira: ${projectName} (ya existe)`);
762
+ } else {
763
+ urls.jira = await createJiraProject(projectName, config);
764
+ }
616
765
  }
617
766
  if (!options.skipSupabase) {
618
- externalTasks.push(
619
- createSupabaseProject(projectName, config).then((result) => {
620
- urls.supabase = result.url;
621
- supabaseKeys = result;
622
- })
623
- );
767
+ if (status.supabase.exists && status.supabase.projectId) {
768
+ const result = await getExistingSupabaseKeys(status.supabase.projectId, config);
769
+ urls.supabase = result.url;
770
+ supabaseKeys = result;
771
+ } else {
772
+ const result = await createSupabaseProject(projectName, config);
773
+ urls.supabase = result.url;
774
+ supabaseKeys = result;
775
+ }
624
776
  }
625
- if (!options.skipJira) {
626
- externalTasks.push(
627
- createJiraProject(projectName, config).then((url) => {
628
- urls.jira = url;
629
- })
630
- );
777
+ if (!status.nextjs.exists) {
778
+ await scaffoldNextJs(projectName, projectPath);
779
+ await installDependencies(projectPath);
780
+ } else {
781
+ logger.success(`Next.js: ${projectName} (ya existe)`);
631
782
  }
632
- await Promise.all(externalTasks);
633
- await scaffoldNextJs(projectName, projectPath);
634
- await installDependencies(projectPath);
635
783
  if (supabaseKeys) {
636
784
  await createEnvFile(projectPath, supabaseKeys);
637
785
  }
638
- if (!options.skipGit && urls.github) {
786
+ if (!options.skipGit && urls.github && !status.nextjs.exists) {
639
787
  await setupGit(projectPath, urls.github);
640
788
  }
641
789
  logger.newLine();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../bin/cli.ts","../../src/index.ts","../../src/config/index.ts","../../src/services/github.ts","../../src/ui/spinner.ts","../../src/ui/logger.ts","../../src/services/supabase.ts","../../src/utils/validation.ts","../../src/services/jira.ts","../../src/steps/scaffold-nextjs.ts","../../src/steps/install-deps.ts","../../src/steps/create-env.ts","../../src/steps/setup-git.ts","../../src/ui/banner.ts"],"sourcesContent":["import { program } from 'commander';\nimport { createProject } from '../src/index.js';\nimport { hasConfig, setConfigPath, getConfigPath } from '../src/config/index.js';\nimport { showBanner } from '../src/ui/banner.js';\nimport { logger } from '../src/ui/logger.js';\n\nconst packageJson = {\n name: 'create-lft-app',\n version: '1.0.0',\n description: 'CLI para crear proyectos LFT con Next.js, GitHub, Supabase y Jira',\n};\n\nprogram\n .name('create-lft-app')\n .description(packageJson.description)\n .version(packageJson.version);\n\nprogram\n .argument('[project-name]', 'Nombre del proyecto a crear')\n .requiredOption('--config <path>', 'Ruta al archivo de credenciales JSON (obligatorio)')\n .option('--skip-github', 'No crear repositorio en GitHub')\n .option('--skip-supabase', 'No crear proyecto en Supabase')\n .option('--skip-jira', 'No crear workspace en Jira')\n .option('--skip-git', 'No inicializar git ni hacer push')\n .option('-y, --yes', 'Aceptar todas las confirmaciones')\n .action(async (projectName: string | undefined, options) => {\n showBanner();\n\n // Establecer la ruta del archivo de config\n setConfigPath(options.config);\n\n if (!projectName) {\n logger.error('Debes especificar un nombre de proyecto');\n logger.info('Uso: create-lft-app <nombre-proyecto> --config <ruta-credenciales>');\n process.exit(1);\n }\n\n if (!await hasConfig()) {\n const configPath = getConfigPath();\n logger.error(`No se encontró el archivo de credenciales: ${configPath}`);\n logger.newLine();\n logger.info('El archivo debe tener el siguiente formato:');\n logger.newLine();\n console.log(`{\n \"github\": {\n \"token\": \"ghp_xxxxxxxxxxxx\",\n \"username\": \"tu-usuario\",\n \"org\": \"tu-organizacion\"\n },\n \"supabase\": {\n \"token\": \"sbp_xxxxxxxxxxxx\",\n \"orgId\": \"tu-org-id\",\n \"region\": \"us-east-1\"\n },\n \"jira\": {\n \"email\": \"tu-email@ejemplo.com\",\n \"token\": \"ATATT3xxxxxxxxxxxx\",\n \"domain\": \"tu-dominio.atlassian.net\"\n }\n}`);\n process.exit(1);\n }\n\n try {\n await createProject(projectName, {\n skipGithub: options.skipGithub,\n skipSupabase: options.skipSupabase,\n skipJira: options.skipJira,\n skipGit: options.skipGit,\n autoConfirm: options.yes,\n });\n } catch (error) {\n if (error instanceof Error) {\n logger.error(error.message);\n }\n process.exit(1);\n }\n });\n\nprogram.parse();\n","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 { 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 (incluye template LFT completo)\n await scaffoldNextJs(projectName, 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 { existsSync, readFileSync } from 'fs';\n\n// Variable para almacenar la ruta del config\nlet configFilePath: string = '';\n\ninterface ConfigFile {\n github: {\n token: string;\n username: string;\n org: string;\n };\n supabase: {\n token: string;\n orgId: string;\n region: string;\n };\n jira: {\n email: string;\n token: string;\n domain: string;\n };\n}\n\n// Cache de la configuración cargada\nlet loadedConfig: ConfigFile | null = null;\n\n/**\n * Establece la ruta del archivo de configuración\n */\nexport function setConfigPath(path: string): void {\n configFilePath = path;\n loadedConfig = null;\n}\n\n/**\n * Obtiene la ruta actual del archivo de configuración\n */\nexport function getConfigPath(): string {\n return configFilePath;\n}\n\n/**\n * Carga la configuración desde el archivo JSON\n */\nfunction loadConfigFromFile(): ConfigFile | null {\n if (loadedConfig) {\n return loadedConfig;\n }\n\n if (!configFilePath || !existsSync(configFilePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(configFilePath, 'utf-8');\n const parsed = JSON.parse(content);\n\n loadedConfig = {\n github: {\n token: parsed.github?.token || '',\n username: parsed.github?.username || '',\n org: parsed.github?.org || '',\n },\n supabase: {\n token: parsed.supabase?.token || '',\n orgId: parsed.supabase?.orgId || '',\n region: parsed.supabase?.region || 'us-east-1',\n },\n jira: {\n email: parsed.jira?.email || '',\n token: parsed.jira?.token || '',\n domain: parsed.jira?.domain || '',\n },\n };\n\n return loadedConfig;\n } catch {\n return null;\n }\n}\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 const config = loadConfigFromFile();\n\n if (!config) {\n throw new Error('No se pudo cargar la configuración');\n }\n\n return {\n version: '1.0.0',\n credentials: {\n github: {\n token: config.github.token,\n username: config.github.username,\n },\n supabase: {\n accessToken: config.supabase.token,\n organizationId: config.supabase.orgId,\n },\n jira: {\n email: config.jira.email,\n apiToken: config.jira.token,\n domain: config.jira.domain,\n },\n },\n defaults: {\n githubOrg: config.github.org || undefined,\n supabaseRegion: config.supabase.region,\n jiraProjectType: 'software',\n },\n };\n}\n\nexport async function hasConfig(): Promise<boolean> {\n const config = loadConfigFromFile();\n\n if (!config) {\n return false;\n }\n\n return (\n config.github.token !== '' &&\n config.supabase.token !== '' &&\n config.jira.token !== ''\n );\n}\n","import { Octokit } from 'octokit';\nimport { withSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.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 const owner = org || config.credentials.github.username;\n\n // Verificar si el repo ya existe\n try {\n const existing = await octokit.rest.repos.get({\n owner,\n repo: projectName,\n });\n logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);\n return existing.data.html_url;\n } catch {\n // No existe, continuar con la creación\n }\n\n return withSpinner(\n 'Creando repositorio en GitHub...',\n async () => {\n let repo;\n\n if (org) {\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 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 `GitHub: ${owner}/${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 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 { withSpinner, createSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.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\nasync function findExistingProject(\n projectName: string,\n token: string\n): Promise<SupabaseProjectResponse | null> {\n const response = await fetch(`${SUPABASE_API_URL}/projects`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) return null;\n\n const projects = (await response.json()) as SupabaseProjectResponse[];\n return projects.find((p) => p.name === projectName) || null;\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 // Verificar si ya existe un proyecto con ese nombre\n const existing = await findExistingProject(projectName, token);\n if (existing) {\n logger.success(`Supabase: ${projectName} (ya existe)`);\n const { anonKey, serviceKey } = await getProjectApiKeys(existing.id, token);\n return {\n url: `https://${existing.id}.supabase.co`,\n anonKey,\n serviceKey,\n };\n }\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","export 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 // No verificamos si el directorio existe porque ahora manejamos recursos existentes\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 { logger } from '../ui/logger.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\nasync function findExistingJiraProject(\n projectName: string,\n auth: string,\n domain: string\n): Promise<JiraProjectResponse | null> {\n const response = await fetch(\n `https://${domain}/rest/api/3/project/search?query=${encodeURIComponent(projectName)}`,\n {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { values: JiraProjectResponse[] };\n return data.values.find((p) => p.name === projectName) || null;\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 // Verificar si ya existe un proyecto con ese nombre\n const existing = await findExistingJiraProject(projectName, auth, domain);\n if (existing) {\n logger.success(`Jira: ${existing.key} (ya existe)`);\n return `https://${domain}/browse/${existing.key}`;\n }\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: ${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 { existsSync } from 'fs';\nimport path from 'path';\nimport { withSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.js';\n\nexport async function scaffoldNextJs(\n projectName: string,\n projectPath: string\n): Promise<void> {\n const targetDir = path.join(process.cwd(), projectName);\n\n if (existsSync(targetDir)) {\n logger.success(`Next.js: ${projectName} (ya existe)`);\n return;\n }\n\n await withSpinner(\n 'Inicializando proyecto Next.js con template LFT...',\n async () => {\n await execa('npx', [\n '@create-lft-app/nextjs@latest',\n projectName,\n '--cwd', process.cwd(),\n ], {\n cwd: process.cwd(),\n stdio: 'pipe',\n });\n },\n `Next.js: ${projectName}`\n );\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function installDependencies(projectPath: string): Promise<void> {\n await withSpinner(\n 'Instalando dependencias...',\n async () => {\n await execa('npm', ['install'], {\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","path","anonKey","serviceKey","project","existsSync","existsSync","execa","execa","path","path","chalk","path"]}
1
+ {"version":3,"sources":["../../bin/cli.ts","../../src/index.ts","../../src/config/index.ts","../../src/services/github.ts","../../src/ui/spinner.ts","../../src/ui/logger.ts","../../src/services/supabase.ts","../../src/utils/validation.ts","../../src/services/jira.ts","../../src/steps/scaffold-nextjs.ts","../../src/steps/install-deps.ts","../../src/steps/create-env.ts","../../src/steps/setup-git.ts","../../src/ui/banner.ts"],"sourcesContent":["import { program } from 'commander';\nimport { createProject } from '../src/index.js';\nimport { hasConfig, setConfigPath, getConfigPath } from '../src/config/index.js';\nimport { showBanner } from '../src/ui/banner.js';\nimport { logger } from '../src/ui/logger.js';\n\nconst packageJson = {\n name: 'create-lft-app',\n version: '1.0.0',\n description: 'CLI para crear proyectos LFT con Next.js, GitHub, Supabase y Jira',\n};\n\nprogram\n .name('create-lft-app')\n .description(packageJson.description)\n .version(packageJson.version);\n\nprogram\n .argument('[project-name]', 'Nombre del proyecto a crear')\n .requiredOption('--config <path>', 'Ruta al archivo de credenciales JSON (obligatorio)')\n .option('--skip-github', 'No crear repositorio en GitHub')\n .option('--skip-supabase', 'No crear proyecto en Supabase')\n .option('--skip-jira', 'No crear workspace en Jira')\n .option('--skip-git', 'No inicializar git ni hacer push')\n .option('-y, --yes', 'Aceptar todas las confirmaciones')\n .action(async (projectName: string | undefined, options) => {\n showBanner();\n\n // Establecer la ruta del archivo de config\n setConfigPath(options.config);\n\n if (!projectName) {\n logger.error('Debes especificar un nombre de proyecto');\n logger.info('Uso: create-lft-app <nombre-proyecto> --config <ruta-credenciales>');\n process.exit(1);\n }\n\n if (!await hasConfig()) {\n const configPath = getConfigPath();\n logger.error(`No se encontró el archivo de credenciales: ${configPath}`);\n logger.newLine();\n logger.info('El archivo debe tener el siguiente formato:');\n logger.newLine();\n console.log(`{\n \"github\": {\n \"token\": \"ghp_xxxxxxxxxxxx\",\n \"username\": \"tu-usuario\",\n \"org\": \"tu-organizacion\"\n },\n \"supabase\": {\n \"token\": \"sbp_xxxxxxxxxxxx\",\n \"orgId\": \"tu-org-id\",\n \"region\": \"us-east-1\"\n },\n \"jira\": {\n \"email\": \"tu-email@ejemplo.com\",\n \"token\": \"ATATT3xxxxxxxxxxxx\",\n \"domain\": \"tu-dominio.atlassian.net\"\n }\n}`);\n process.exit(1);\n }\n\n try {\n await createProject(projectName, {\n skipGithub: options.skipGithub,\n skipSupabase: options.skipSupabase,\n skipJira: options.skipJira,\n skipGit: options.skipGit,\n autoConfirm: options.yes,\n });\n } catch (error) {\n if (error instanceof Error) {\n logger.error(error.message);\n }\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import path from 'path';\nimport { existsSync } from 'fs';\nimport { confirm } from '@inquirer/prompts';\nimport { loadConfig, hasConfig } from './config/index.js';\nimport { createGitHubRepo, checkGitHubRepoExists } from './services/github.js';\nimport { createSupabaseProject, checkSupabaseProjectExists, getExistingSupabaseKeys } from './services/supabase.js';\nimport { createJiraProject, checkJiraProjectExists } from './services/jira.js';\nimport { scaffoldNextJs } from './steps/scaffold-nextjs.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\ninterface ResourceStatus {\n github: { exists: boolean; url?: string };\n supabase: { exists: boolean; url?: string; projectId?: string };\n jira: { exists: boolean; url?: string };\n nextjs: { exists: 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 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 // Verificar recursos existentes en paralelo\n logger.info('Verificando recursos existentes...');\n\n const status: ResourceStatus = {\n github: { exists: false },\n supabase: { exists: false },\n jira: { exists: false },\n nextjs: { exists: existsSync(projectPath) },\n };\n\n const checks: Promise<void>[] = [];\n\n if (!options.skipGithub) {\n checks.push(\n checkGitHubRepoExists(projectName, config).then((result) => {\n status.github = result;\n })\n );\n }\n\n if (!options.skipSupabase) {\n checks.push(\n checkSupabaseProjectExists(projectName, config).then((result) => {\n status.supabase = result;\n })\n );\n }\n\n if (!options.skipJira) {\n checks.push(\n checkJiraProjectExists(projectName, config).then((result) => {\n status.jira = result;\n })\n );\n }\n\n await Promise.all(checks);\n\n // Mostrar resumen con estado\n logger.newLine();\n logger.title('Resumen de recursos:');\n logger.newLine();\n\n const resources = [];\n const owner = config.defaults.githubOrg || config.credentials.github.username;\n\n if (!options.skipGithub) {\n const githubStatus = status.github.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'GitHub',\n value: `${owner}/${projectName} (${githubStatus})`\n });\n }\n\n if (!options.skipSupabase) {\n const supabaseStatus = status.supabase.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'Supabase',\n value: `${projectName} en ${config.defaults.supabaseRegion} (${supabaseStatus})`\n });\n }\n\n if (!options.skipJira) {\n const jiraStatus = status.jira.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'Jira',\n value: `Proyecto \"${projectName}\" (${jiraStatus})`\n });\n }\n\n const nextjsStatus = status.nextjs.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'Next.js',\n value: `App Router + TypeScript + Tailwind (${nextjsStatus})`\n });\n\n logger.table(resources);\n logger.newLine();\n\n // Confirmar\n if (!options.autoConfirm) {\n const shouldContinue = await confirm({\n message: '¿Continuar?',\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 secuencialmente (para evitar conflictos de spinners)\n if (!options.skipGithub) {\n if (status.github.exists) {\n urls.github = status.github.url;\n logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);\n } else {\n urls.github = await createGitHubRepo(projectName, config);\n }\n }\n\n if (!options.skipJira) {\n if (status.jira.exists) {\n urls.jira = status.jira.url;\n logger.success(`Jira: ${projectName} (ya existe)`);\n } else {\n urls.jira = await createJiraProject(projectName, config);\n }\n }\n\n if (!options.skipSupabase) {\n if (status.supabase.exists && status.supabase.projectId) {\n const result = await getExistingSupabaseKeys(status.supabase.projectId, config);\n urls.supabase = result.url;\n supabaseKeys = result;\n } else {\n const result = await createSupabaseProject(projectName, config);\n urls.supabase = result.url;\n supabaseKeys = result;\n }\n }\n\n // Scaffold Next.js (incluye template LFT completo)\n if (!status.nextjs.exists) {\n await scaffoldNextJs(projectName, projectPath);\n await installDependencies(projectPath);\n } else {\n logger.success(`Next.js: ${projectName} (ya existe)`);\n }\n\n // Crear .env.local\n if (supabaseKeys) {\n await createEnvFile(projectPath, supabaseKeys);\n }\n\n // Setup git\n if (!options.skipGit && urls.github && !status.nextjs.exists) {\n await setupGit(projectPath, urls.github);\n }\n\n // Mostrar resumen final\n logger.newLine();\n showSuccessBanner(projectName, urls);\n}\n","import { existsSync, readFileSync } from 'fs';\n\n// Variable para almacenar la ruta del config\nlet configFilePath: string = '';\n\ninterface ConfigFile {\n github: {\n token: string;\n username: string;\n org: string;\n };\n supabase: {\n token: string;\n orgId: string;\n region: string;\n };\n jira: {\n email: string;\n token: string;\n domain: string;\n };\n}\n\n// Cache de la configuración cargada\nlet loadedConfig: ConfigFile | null = null;\n\n/**\n * Establece la ruta del archivo de configuración\n */\nexport function setConfigPath(path: string): void {\n configFilePath = path;\n loadedConfig = null;\n}\n\n/**\n * Obtiene la ruta actual del archivo de configuración\n */\nexport function getConfigPath(): string {\n return configFilePath;\n}\n\n/**\n * Carga la configuración desde el archivo JSON\n */\nfunction loadConfigFromFile(): ConfigFile | null {\n if (loadedConfig) {\n return loadedConfig;\n }\n\n if (!configFilePath || !existsSync(configFilePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(configFilePath, 'utf-8');\n const parsed = JSON.parse(content);\n\n loadedConfig = {\n github: {\n token: parsed.github?.token || '',\n username: parsed.github?.username || '',\n org: parsed.github?.org || '',\n },\n supabase: {\n token: parsed.supabase?.token || '',\n orgId: parsed.supabase?.orgId || '',\n region: parsed.supabase?.region || 'us-east-1',\n },\n jira: {\n email: parsed.jira?.email || '',\n token: parsed.jira?.token || '',\n domain: parsed.jira?.domain || '',\n },\n };\n\n return loadedConfig;\n } catch {\n return null;\n }\n}\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 const config = loadConfigFromFile();\n\n if (!config) {\n throw new Error('No se pudo cargar la configuración');\n }\n\n return {\n version: '1.0.0',\n credentials: {\n github: {\n token: config.github.token,\n username: config.github.username,\n },\n supabase: {\n accessToken: config.supabase.token,\n organizationId: config.supabase.orgId,\n },\n jira: {\n email: config.jira.email,\n apiToken: config.jira.token,\n domain: config.jira.domain,\n },\n },\n defaults: {\n githubOrg: config.github.org || undefined,\n supabaseRegion: config.supabase.region,\n jiraProjectType: 'software',\n },\n };\n}\n\nexport async function hasConfig(): Promise<boolean> {\n const config = loadConfigFromFile();\n\n if (!config) {\n return false;\n }\n\n return (\n config.github.token !== '' &&\n config.supabase.token !== '' &&\n config.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 const owner = org || config.credentials.github.username;\n\n return withSpinner(\n 'Creando repositorio en GitHub...',\n async () => {\n let repo;\n\n if (org) {\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 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.ssh_url;\n },\n `GitHub: ${owner}/${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\nexport async function checkGitHubRepoExists(\n projectName: string,\n config: LFTConfig\n): Promise<{ exists: boolean; url?: string }> {\n const octokit = new Octokit({ auth: config.credentials.github.token });\n const org = config.defaults.githubOrg;\n const owner = org || config.credentials.github.username;\n\n try {\n const existing = await octokit.rest.repos.get({\n owner,\n repo: projectName,\n });\n return { exists: true, url: existing.data.ssh_url };\n } catch {\n return { exists: 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 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 { withSpinner, createSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.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\nasync function findExistingProject(\n projectName: string,\n token: string\n): Promise<SupabaseProjectResponse | null> {\n const response = await fetch(`${SUPABASE_API_URL}/projects`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) return null;\n\n const projects = (await response.json()) as SupabaseProjectResponse[];\n return projects.find((p) => p.name === projectName) || null;\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 // Verificar si ya existe un proyecto con ese nombre\n const existing = await findExistingProject(projectName, token);\n if (existing) {\n logger.success(`Supabase: ${projectName} (ya existe)`);\n const { anonKey, serviceKey } = await getProjectApiKeys(existing.id, token);\n return {\n url: `https://${existing.id}.supabase.co`,\n anonKey,\n serviceKey,\n };\n }\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\nexport async function checkSupabaseProjectExists(\n projectName: string,\n config: LFTConfig\n): Promise<{ exists: boolean; url?: string; projectId?: string }> {\n const token = config.credentials.supabase.accessToken;\n const existing = await findExistingProject(projectName, token);\n\n if (existing) {\n return { exists: true, url: `https://${existing.id}.supabase.co`, projectId: existing.id };\n }\n return { exists: false };\n}\n\nexport async function getExistingSupabaseKeys(\n projectId: string,\n config: LFTConfig\n): Promise<SupabaseProjectResult> {\n return withSpinner(\n 'Obteniendo credenciales de Supabase...',\n async () => {\n const token = config.credentials.supabase.accessToken;\n const { anonKey, serviceKey } = await getProjectApiKeys(projectId, token);\n return {\n url: `https://${projectId}.supabase.co`,\n anonKey,\n serviceKey,\n };\n },\n 'Supabase: credenciales obtenidas'\n );\n}\n","export 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 // No verificamos si el directorio existe porque ahora manejamos recursos existentes\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\nasync function findExistingJiraProject(\n projectName: string,\n auth: string,\n domain: string\n): Promise<JiraProjectResponse | null> {\n const response = await fetch(\n `https://${domain}/rest/api/3/project/search?query=${encodeURIComponent(projectName)}`,\n {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { values: JiraProjectResponse[] };\n return data.values.find((p) => p.name === projectName) || null;\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: ${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\nexport async function checkJiraProjectExists(\n projectName: string,\n config: LFTConfig\n): Promise<{ exists: boolean; url?: string }> {\n const { email, apiToken, domain } = config.credentials.jira;\n const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');\n\n const existing = await findExistingJiraProject(projectName, auth, domain);\n if (existing) {\n return { exists: true, url: `https://${domain}/browse/${existing.key}` };\n }\n return { exists: false };\n}\n","import { execa } from 'execa';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { withSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.js';\n\nexport async function scaffoldNextJs(\n projectName: string,\n projectPath: string\n): Promise<void> {\n const targetDir = path.join(process.cwd(), projectName);\n\n if (existsSync(targetDir)) {\n logger.success(`Next.js: ${projectName} (ya existe)`);\n return;\n }\n\n await withSpinner(\n 'Inicializando proyecto Next.js con template LFT...',\n async () => {\n await execa('npx', [\n '@create-lft-app/nextjs@latest',\n projectName,\n '--cwd', process.cwd(),\n ], {\n cwd: process.cwd(),\n stdio: 'pipe',\n });\n },\n `Next.js: ${projectName}`\n );\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function installDependencies(projectPath: string): Promise<void> {\n await withSpinner(\n 'Instalando dependencias...',\n async () => {\n await execa('npm', ['install'], {\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 // Inicializar git (simple-git requiere que el directorio sea un repo)\n await git.init();\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';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\nfunction getVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const pkgPath = join(__dirname, '..', '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version || '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nfunction getKangarooArt(): string {\n const c = chalk.cyan;\n return c(` \n ██ ███ \n ██████████ \n █ ████████ \n ███████████ \n ███████████ \n █████ █████ \n ██ █ █████ \n ██ █ ████ \n ██ ██ ███ ██ \n ███ ███████████████ \n ███ ██ █████ \n ████ █ ██████ \n ████ ██ ██████████ \n ██ ██ ███████████ \n ██ █ █ ███ ██████████ \n ██ █ ███ █████████████ \n ██ ██ █ █████████ \n ██ ██ █████████ ██████ \n ██ ███ █████ ███████ ██████ \n ███ ██████ ██████████████████ \n ███ █████████████ ████████████████ \n ██ ██████████████████████████████████ \n ██ █████ ███████████████ \n ██ ███████ \n ██ ████████ \n ██ █████ ███ \n ██ ██ ██████ ██ \n ██ ███ ███████ ██ \n ███ █ ████ ██ ███████ ██ \n ███ ██ █████████ ███ ███████ ██ \n ███ ██ ███████████████ ████████ ██ \n █████ ██ ███████ ██ ██ ████ ██ \n █ ███ ███ ████████ ██ ███ ██ █ \n ███ ████ ████ ███████ ██ ██ ██ ██ \n ███ ████████ ███████ ██ ██ ██ ██ \n █████ █████████ ██ █ █ █ \n ███████████████ ███ ██ ██ █ \n ██████ ██ ███ ██ █████ \n ████ ███ ██ █████████ \n █████ ████ ██████████ ███ \n ████ ████ █████████ \n ███████ \n ██ \n`);\n}\n\nexport function showBanner(): void {\n const version = getVersion();\n const title = chalk.bold.cyan('create-lft-app');\n const versionText = chalk.gray(`v${version}`);\n const description = chalk.white('Scaffolding para proyectos LFT');\n\n console.log(getKangarooArt());\n\n const banner = boxen(`${title} ${versionText}\\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","existsSync","path","anonKey","serviceKey","project","existsSync","existsSync","execa","execa","path","path","chalk","readFileSync","__filename","__dirname","path","existsSync"]}
package/dist/src/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import path3 from "path";
5
+ import { existsSync as existsSync3 } from "fs";
5
6
  import { confirm } from "@inquirer/prompts";
6
7
 
7
8
  // src/config/index.ts
@@ -100,6 +101,51 @@ async function withSpinner(text, fn, successText) {
100
101
  }
101
102
  }
102
103
 
104
+ // src/services/github.ts
105
+ async function createGitHubRepo(projectName, config) {
106
+ const octokit = new Octokit({ auth: config.credentials.github.token });
107
+ const org = config.defaults.githubOrg;
108
+ const owner = org || config.credentials.github.username;
109
+ return withSpinner(
110
+ "Creando repositorio en GitHub...",
111
+ async () => {
112
+ let repo;
113
+ if (org) {
114
+ repo = await octokit.rest.repos.createInOrg({
115
+ org,
116
+ name: projectName,
117
+ private: true,
118
+ auto_init: false,
119
+ description: `Proyecto ${projectName} creado con create-lft-app`
120
+ });
121
+ } else {
122
+ repo = await octokit.rest.repos.createForAuthenticatedUser({
123
+ name: projectName,
124
+ private: true,
125
+ auto_init: false,
126
+ description: `Proyecto ${projectName} creado con create-lft-app`
127
+ });
128
+ }
129
+ return repo.data.ssh_url;
130
+ },
131
+ `GitHub: ${owner}/${projectName}`
132
+ );
133
+ }
134
+ async function checkGitHubRepoExists(projectName, config) {
135
+ const octokit = new Octokit({ auth: config.credentials.github.token });
136
+ const org = config.defaults.githubOrg;
137
+ const owner = org || config.credentials.github.username;
138
+ try {
139
+ const existing = await octokit.rest.repos.get({
140
+ owner,
141
+ repo: projectName
142
+ });
143
+ return { exists: true, url: existing.data.ssh_url };
144
+ } catch {
145
+ return { exists: false };
146
+ }
147
+ }
148
+
103
149
  // src/ui/logger.ts
104
150
  import chalk from "chalk";
105
151
  var logger = {
@@ -147,46 +193,6 @@ var logger = {
147
193
  }
148
194
  };
149
195
 
150
- // src/services/github.ts
151
- async function createGitHubRepo(projectName, config) {
152
- const octokit = new Octokit({ auth: config.credentials.github.token });
153
- const org = config.defaults.githubOrg;
154
- const owner = org || config.credentials.github.username;
155
- try {
156
- const existing = await octokit.rest.repos.get({
157
- owner,
158
- repo: projectName
159
- });
160
- logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
161
- return existing.data.html_url;
162
- } catch {
163
- }
164
- return withSpinner(
165
- "Creando repositorio en GitHub...",
166
- async () => {
167
- let repo;
168
- if (org) {
169
- repo = await octokit.rest.repos.createInOrg({
170
- org,
171
- name: projectName,
172
- private: true,
173
- auto_init: false,
174
- description: `Proyecto ${projectName} creado con create-lft-app`
175
- });
176
- } else {
177
- repo = await octokit.rest.repos.createForAuthenticatedUser({
178
- name: projectName,
179
- private: true,
180
- auto_init: false,
181
- description: `Proyecto ${projectName} creado con create-lft-app`
182
- });
183
- }
184
- return repo.data.html_url;
185
- },
186
- `GitHub: ${owner}/${projectName}`
187
- );
188
- }
189
-
190
196
  // src/services/supabase.ts
191
197
  var SUPABASE_API_URL = "https://api.supabase.com/v1";
192
198
  async function waitForProjectReady(projectId, token, maxAttempts = 60) {
@@ -287,6 +293,29 @@ function generateSecurePassword() {
287
293
  }
288
294
  return password;
289
295
  }
296
+ async function checkSupabaseProjectExists(projectName, config) {
297
+ const token = config.credentials.supabase.accessToken;
298
+ const existing = await findExistingProject(projectName, token);
299
+ if (existing) {
300
+ return { exists: true, url: `https://${existing.id}.supabase.co`, projectId: existing.id };
301
+ }
302
+ return { exists: false };
303
+ }
304
+ async function getExistingSupabaseKeys(projectId, config) {
305
+ return withSpinner(
306
+ "Obteniendo credenciales de Supabase...",
307
+ async () => {
308
+ const token = config.credentials.supabase.accessToken;
309
+ const { anonKey, serviceKey } = await getProjectApiKeys(projectId, token);
310
+ return {
311
+ url: `https://${projectId}.supabase.co`,
312
+ anonKey,
313
+ serviceKey
314
+ };
315
+ },
316
+ "Supabase: credenciales obtenidas"
317
+ );
318
+ }
290
319
 
291
320
  // src/utils/validation.ts
292
321
  function validateProjectName(name) {
@@ -338,11 +367,6 @@ async function createJiraProject(projectName, config) {
338
367
  const { email, apiToken, domain } = config.credentials.jira;
339
368
  const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
340
369
  const projectKey = generateJiraKey(projectName);
341
- const existing = await findExistingJiraProject(projectName, auth, domain);
342
- if (existing) {
343
- logger.success(`Jira: ${existing.key} (ya existe)`);
344
- return `https://${domain}/browse/${existing.key}`;
345
- }
346
370
  return withSpinner(
347
371
  "Creando proyecto en Jira...",
348
372
  async () => {
@@ -402,6 +426,15 @@ async function createJiraProject(projectName, config) {
402
426
  `Proyecto Jira: ${projectKey}`
403
427
  );
404
428
  }
429
+ async function checkJiraProjectExists(projectName, config) {
430
+ const { email, apiToken, domain } = config.credentials.jira;
431
+ const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
432
+ const existing = await findExistingJiraProject(projectName, auth, domain);
433
+ if (existing) {
434
+ return { exists: true, url: `https://${domain}/browse/${existing.key}` };
435
+ }
436
+ return { exists: false };
437
+ }
405
438
 
406
439
  // src/steps/scaffold-nextjs.ts
407
440
  import { execa } from "execa";
@@ -482,10 +515,7 @@ async function setupGit(projectPath, remoteUrl) {
482
515
  await withSpinner(
483
516
  "Configurando Git...",
484
517
  async () => {
485
- const isRepo = await git.checkIsRepo();
486
- if (!isRepo) {
487
- await git.init();
488
- }
518
+ await git.init();
489
519
  try {
490
520
  await git.addRemote("origin", remoteUrl);
491
521
  } catch {
@@ -552,25 +582,72 @@ async function createProject(projectName, options = {}) {
552
582
  throw new Error("Configuraci\xF3n no encontrada");
553
583
  }
554
584
  const config = await loadConfig();
585
+ logger.info("Verificando recursos existentes...");
586
+ const status = {
587
+ github: { exists: false },
588
+ supabase: { exists: false },
589
+ jira: { exists: false },
590
+ nextjs: { exists: existsSync3(projectPath) }
591
+ };
592
+ const checks = [];
593
+ if (!options.skipGithub) {
594
+ checks.push(
595
+ checkGitHubRepoExists(projectName, config).then((result) => {
596
+ status.github = result;
597
+ })
598
+ );
599
+ }
600
+ if (!options.skipSupabase) {
601
+ checks.push(
602
+ checkSupabaseProjectExists(projectName, config).then((result) => {
603
+ status.supabase = result;
604
+ })
605
+ );
606
+ }
607
+ if (!options.skipJira) {
608
+ checks.push(
609
+ checkJiraProjectExists(projectName, config).then((result) => {
610
+ status.jira = result;
611
+ })
612
+ );
613
+ }
614
+ await Promise.all(checks);
555
615
  logger.newLine();
556
- logger.title("Resumen de recursos a crear:");
616
+ logger.title("Resumen de recursos:");
557
617
  logger.newLine();
558
618
  const resources = [];
619
+ const owner = config.defaults.githubOrg || config.credentials.github.username;
559
620
  if (!options.skipGithub) {
560
- resources.push({ label: "GitHub", value: `${config.defaults.githubOrg || config.credentials.github.username}/${projectName} (privado)` });
621
+ const githubStatus = status.github.exists ? "\u2713 ya existe" : "se crear\xE1";
622
+ resources.push({
623
+ label: "GitHub",
624
+ value: `${owner}/${projectName} (${githubStatus})`
625
+ });
561
626
  }
562
627
  if (!options.skipSupabase) {
563
- resources.push({ label: "Supabase", value: `${projectName} en ${config.defaults.supabaseRegion}` });
628
+ const supabaseStatus = status.supabase.exists ? "\u2713 ya existe" : "se crear\xE1";
629
+ resources.push({
630
+ label: "Supabase",
631
+ value: `${projectName} en ${config.defaults.supabaseRegion} (${supabaseStatus})`
632
+ });
564
633
  }
565
634
  if (!options.skipJira) {
566
- resources.push({ label: "Jira", value: `Proyecto "${projectName}" en ${config.credentials.jira.domain}` });
635
+ const jiraStatus = status.jira.exists ? "\u2713 ya existe" : "se crear\xE1";
636
+ resources.push({
637
+ label: "Jira",
638
+ value: `Proyecto "${projectName}" (${jiraStatus})`
639
+ });
567
640
  }
568
- resources.push({ label: "Next.js", value: "App Router + TypeScript + Tailwind + Dashboard" });
641
+ const nextjsStatus = status.nextjs.exists ? "\u2713 ya existe" : "se crear\xE1";
642
+ resources.push({
643
+ label: "Next.js",
644
+ value: `App Router + TypeScript + Tailwind (${nextjsStatus})`
645
+ });
569
646
  logger.table(resources);
570
647
  logger.newLine();
571
648
  if (!options.autoConfirm) {
572
649
  const shouldContinue = await confirm({
573
- message: "\xBFContinuar con la creaci\xF3n?",
650
+ message: "\xBFContinuar?",
574
651
  default: true
575
652
  });
576
653
  if (!shouldContinue) {
@@ -583,36 +660,43 @@ async function createProject(projectName, options = {}) {
583
660
  logger.newLine();
584
661
  const urls = {};
585
662
  let supabaseKeys;
586
- const externalTasks = [];
587
663
  if (!options.skipGithub) {
588
- externalTasks.push(
589
- createGitHubRepo(projectName, config).then((url) => {
590
- urls.github = url;
591
- })
592
- );
664
+ if (status.github.exists) {
665
+ urls.github = status.github.url;
666
+ logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
667
+ } else {
668
+ urls.github = await createGitHubRepo(projectName, config);
669
+ }
670
+ }
671
+ if (!options.skipJira) {
672
+ if (status.jira.exists) {
673
+ urls.jira = status.jira.url;
674
+ logger.success(`Jira: ${projectName} (ya existe)`);
675
+ } else {
676
+ urls.jira = await createJiraProject(projectName, config);
677
+ }
593
678
  }
594
679
  if (!options.skipSupabase) {
595
- externalTasks.push(
596
- createSupabaseProject(projectName, config).then((result) => {
597
- urls.supabase = result.url;
598
- supabaseKeys = result;
599
- })
600
- );
680
+ if (status.supabase.exists && status.supabase.projectId) {
681
+ const result = await getExistingSupabaseKeys(status.supabase.projectId, config);
682
+ urls.supabase = result.url;
683
+ supabaseKeys = result;
684
+ } else {
685
+ const result = await createSupabaseProject(projectName, config);
686
+ urls.supabase = result.url;
687
+ supabaseKeys = result;
688
+ }
601
689
  }
602
- if (!options.skipJira) {
603
- externalTasks.push(
604
- createJiraProject(projectName, config).then((url) => {
605
- urls.jira = url;
606
- })
607
- );
690
+ if (!status.nextjs.exists) {
691
+ await scaffoldNextJs(projectName, projectPath);
692
+ await installDependencies(projectPath);
693
+ } else {
694
+ logger.success(`Next.js: ${projectName} (ya existe)`);
608
695
  }
609
- await Promise.all(externalTasks);
610
- await scaffoldNextJs(projectName, projectPath);
611
- await installDependencies(projectPath);
612
696
  if (supabaseKeys) {
613
697
  await createEnvFile(projectPath, supabaseKeys);
614
698
  }
615
- if (!options.skipGit && urls.github) {
699
+ if (!options.skipGit && urls.github && !status.nextjs.exists) {
616
700
  await setupGit(projectPath, urls.github);
617
701
  }
618
702
  logger.newLine();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/config/index.ts","../../src/services/github.ts","../../src/ui/spinner.ts","../../src/ui/logger.ts","../../src/services/supabase.ts","../../src/utils/validation.ts","../../src/services/jira.ts","../../src/steps/scaffold-nextjs.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 { 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 (incluye template LFT completo)\n await scaffoldNextJs(projectName, 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 { existsSync, readFileSync } from 'fs';\n\n// Variable para almacenar la ruta del config\nlet configFilePath: string = '';\n\ninterface ConfigFile {\n github: {\n token: string;\n username: string;\n org: string;\n };\n supabase: {\n token: string;\n orgId: string;\n region: string;\n };\n jira: {\n email: string;\n token: string;\n domain: string;\n };\n}\n\n// Cache de la configuración cargada\nlet loadedConfig: ConfigFile | null = null;\n\n/**\n * Establece la ruta del archivo de configuración\n */\nexport function setConfigPath(path: string): void {\n configFilePath = path;\n loadedConfig = null;\n}\n\n/**\n * Obtiene la ruta actual del archivo de configuración\n */\nexport function getConfigPath(): string {\n return configFilePath;\n}\n\n/**\n * Carga la configuración desde el archivo JSON\n */\nfunction loadConfigFromFile(): ConfigFile | null {\n if (loadedConfig) {\n return loadedConfig;\n }\n\n if (!configFilePath || !existsSync(configFilePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(configFilePath, 'utf-8');\n const parsed = JSON.parse(content);\n\n loadedConfig = {\n github: {\n token: parsed.github?.token || '',\n username: parsed.github?.username || '',\n org: parsed.github?.org || '',\n },\n supabase: {\n token: parsed.supabase?.token || '',\n orgId: parsed.supabase?.orgId || '',\n region: parsed.supabase?.region || 'us-east-1',\n },\n jira: {\n email: parsed.jira?.email || '',\n token: parsed.jira?.token || '',\n domain: parsed.jira?.domain || '',\n },\n };\n\n return loadedConfig;\n } catch {\n return null;\n }\n}\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 const config = loadConfigFromFile();\n\n if (!config) {\n throw new Error('No se pudo cargar la configuración');\n }\n\n return {\n version: '1.0.0',\n credentials: {\n github: {\n token: config.github.token,\n username: config.github.username,\n },\n supabase: {\n accessToken: config.supabase.token,\n organizationId: config.supabase.orgId,\n },\n jira: {\n email: config.jira.email,\n apiToken: config.jira.token,\n domain: config.jira.domain,\n },\n },\n defaults: {\n githubOrg: config.github.org || undefined,\n supabaseRegion: config.supabase.region,\n jiraProjectType: 'software',\n },\n };\n}\n\nexport async function hasConfig(): Promise<boolean> {\n const config = loadConfigFromFile();\n\n if (!config) {\n return false;\n }\n\n return (\n config.github.token !== '' &&\n config.supabase.token !== '' &&\n config.jira.token !== ''\n );\n}\n","import { Octokit } from 'octokit';\nimport { withSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.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 const owner = org || config.credentials.github.username;\n\n // Verificar si el repo ya existe\n try {\n const existing = await octokit.rest.repos.get({\n owner,\n repo: projectName,\n });\n logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);\n return existing.data.html_url;\n } catch {\n // No existe, continuar con la creación\n }\n\n return withSpinner(\n 'Creando repositorio en GitHub...',\n async () => {\n let repo;\n\n if (org) {\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 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 `GitHub: ${owner}/${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 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 { withSpinner, createSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.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\nasync function findExistingProject(\n projectName: string,\n token: string\n): Promise<SupabaseProjectResponse | null> {\n const response = await fetch(`${SUPABASE_API_URL}/projects`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) return null;\n\n const projects = (await response.json()) as SupabaseProjectResponse[];\n return projects.find((p) => p.name === projectName) || null;\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 // Verificar si ya existe un proyecto con ese nombre\n const existing = await findExistingProject(projectName, token);\n if (existing) {\n logger.success(`Supabase: ${projectName} (ya existe)`);\n const { anonKey, serviceKey } = await getProjectApiKeys(existing.id, token);\n return {\n url: `https://${existing.id}.supabase.co`,\n anonKey,\n serviceKey,\n };\n }\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","export 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 // No verificamos si el directorio existe porque ahora manejamos recursos existentes\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 { logger } from '../ui/logger.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\nasync function findExistingJiraProject(\n projectName: string,\n auth: string,\n domain: string\n): Promise<JiraProjectResponse | null> {\n const response = await fetch(\n `https://${domain}/rest/api/3/project/search?query=${encodeURIComponent(projectName)}`,\n {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { values: JiraProjectResponse[] };\n return data.values.find((p) => p.name === projectName) || null;\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 // Verificar si ya existe un proyecto con ese nombre\n const existing = await findExistingJiraProject(projectName, auth, domain);\n if (existing) {\n logger.success(`Jira: ${existing.key} (ya existe)`);\n return `https://${domain}/browse/${existing.key}`;\n }\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: ${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 { existsSync } from 'fs';\nimport path from 'path';\nimport { withSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.js';\n\nexport async function scaffoldNextJs(\n projectName: string,\n projectPath: string\n): Promise<void> {\n const targetDir = path.join(process.cwd(), projectName);\n\n if (existsSync(targetDir)) {\n logger.success(`Next.js: ${projectName} (ya existe)`);\n return;\n }\n\n await withSpinner(\n 'Inicializando proyecto Next.js con template LFT...',\n async () => {\n await execa('npx', [\n '@create-lft-app/nextjs@latest',\n projectName,\n '--cwd', process.cwd(),\n ], {\n cwd: process.cwd(),\n stdio: 'pipe',\n });\n },\n `Next.js: ${projectName}`\n );\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function installDependencies(projectPath: string): Promise<void> {\n await withSpinner(\n 'Instalando dependencias...',\n async () => {\n await execa('npm', ['install'], {\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","anonKey","serviceKey","project","existsSync","existsSync","execa","execa","path","path","chalk","chalk","path"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/config/index.ts","../../src/services/github.ts","../../src/ui/spinner.ts","../../src/ui/logger.ts","../../src/services/supabase.ts","../../src/utils/validation.ts","../../src/services/jira.ts","../../src/steps/scaffold-nextjs.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 { existsSync } from 'fs';\nimport { confirm } from '@inquirer/prompts';\nimport { loadConfig, hasConfig } from './config/index.js';\nimport { createGitHubRepo, checkGitHubRepoExists } from './services/github.js';\nimport { createSupabaseProject, checkSupabaseProjectExists, getExistingSupabaseKeys } from './services/supabase.js';\nimport { createJiraProject, checkJiraProjectExists } from './services/jira.js';\nimport { scaffoldNextJs } from './steps/scaffold-nextjs.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\ninterface ResourceStatus {\n github: { exists: boolean; url?: string };\n supabase: { exists: boolean; url?: string; projectId?: string };\n jira: { exists: boolean; url?: string };\n nextjs: { exists: 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 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 // Verificar recursos existentes en paralelo\n logger.info('Verificando recursos existentes...');\n\n const status: ResourceStatus = {\n github: { exists: false },\n supabase: { exists: false },\n jira: { exists: false },\n nextjs: { exists: existsSync(projectPath) },\n };\n\n const checks: Promise<void>[] = [];\n\n if (!options.skipGithub) {\n checks.push(\n checkGitHubRepoExists(projectName, config).then((result) => {\n status.github = result;\n })\n );\n }\n\n if (!options.skipSupabase) {\n checks.push(\n checkSupabaseProjectExists(projectName, config).then((result) => {\n status.supabase = result;\n })\n );\n }\n\n if (!options.skipJira) {\n checks.push(\n checkJiraProjectExists(projectName, config).then((result) => {\n status.jira = result;\n })\n );\n }\n\n await Promise.all(checks);\n\n // Mostrar resumen con estado\n logger.newLine();\n logger.title('Resumen de recursos:');\n logger.newLine();\n\n const resources = [];\n const owner = config.defaults.githubOrg || config.credentials.github.username;\n\n if (!options.skipGithub) {\n const githubStatus = status.github.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'GitHub',\n value: `${owner}/${projectName} (${githubStatus})`\n });\n }\n\n if (!options.skipSupabase) {\n const supabaseStatus = status.supabase.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'Supabase',\n value: `${projectName} en ${config.defaults.supabaseRegion} (${supabaseStatus})`\n });\n }\n\n if (!options.skipJira) {\n const jiraStatus = status.jira.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'Jira',\n value: `Proyecto \"${projectName}\" (${jiraStatus})`\n });\n }\n\n const nextjsStatus = status.nextjs.exists ? '✓ ya existe' : 'se creará';\n resources.push({\n label: 'Next.js',\n value: `App Router + TypeScript + Tailwind (${nextjsStatus})`\n });\n\n logger.table(resources);\n logger.newLine();\n\n // Confirmar\n if (!options.autoConfirm) {\n const shouldContinue = await confirm({\n message: '¿Continuar?',\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 secuencialmente (para evitar conflictos de spinners)\n if (!options.skipGithub) {\n if (status.github.exists) {\n urls.github = status.github.url;\n logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);\n } else {\n urls.github = await createGitHubRepo(projectName, config);\n }\n }\n\n if (!options.skipJira) {\n if (status.jira.exists) {\n urls.jira = status.jira.url;\n logger.success(`Jira: ${projectName} (ya existe)`);\n } else {\n urls.jira = await createJiraProject(projectName, config);\n }\n }\n\n if (!options.skipSupabase) {\n if (status.supabase.exists && status.supabase.projectId) {\n const result = await getExistingSupabaseKeys(status.supabase.projectId, config);\n urls.supabase = result.url;\n supabaseKeys = result;\n } else {\n const result = await createSupabaseProject(projectName, config);\n urls.supabase = result.url;\n supabaseKeys = result;\n }\n }\n\n // Scaffold Next.js (incluye template LFT completo)\n if (!status.nextjs.exists) {\n await scaffoldNextJs(projectName, projectPath);\n await installDependencies(projectPath);\n } else {\n logger.success(`Next.js: ${projectName} (ya existe)`);\n }\n\n // Crear .env.local\n if (supabaseKeys) {\n await createEnvFile(projectPath, supabaseKeys);\n }\n\n // Setup git\n if (!options.skipGit && urls.github && !status.nextjs.exists) {\n await setupGit(projectPath, urls.github);\n }\n\n // Mostrar resumen final\n logger.newLine();\n showSuccessBanner(projectName, urls);\n}\n","import { existsSync, readFileSync } from 'fs';\n\n// Variable para almacenar la ruta del config\nlet configFilePath: string = '';\n\ninterface ConfigFile {\n github: {\n token: string;\n username: string;\n org: string;\n };\n supabase: {\n token: string;\n orgId: string;\n region: string;\n };\n jira: {\n email: string;\n token: string;\n domain: string;\n };\n}\n\n// Cache de la configuración cargada\nlet loadedConfig: ConfigFile | null = null;\n\n/**\n * Establece la ruta del archivo de configuración\n */\nexport function setConfigPath(path: string): void {\n configFilePath = path;\n loadedConfig = null;\n}\n\n/**\n * Obtiene la ruta actual del archivo de configuración\n */\nexport function getConfigPath(): string {\n return configFilePath;\n}\n\n/**\n * Carga la configuración desde el archivo JSON\n */\nfunction loadConfigFromFile(): ConfigFile | null {\n if (loadedConfig) {\n return loadedConfig;\n }\n\n if (!configFilePath || !existsSync(configFilePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(configFilePath, 'utf-8');\n const parsed = JSON.parse(content);\n\n loadedConfig = {\n github: {\n token: parsed.github?.token || '',\n username: parsed.github?.username || '',\n org: parsed.github?.org || '',\n },\n supabase: {\n token: parsed.supabase?.token || '',\n orgId: parsed.supabase?.orgId || '',\n region: parsed.supabase?.region || 'us-east-1',\n },\n jira: {\n email: parsed.jira?.email || '',\n token: parsed.jira?.token || '',\n domain: parsed.jira?.domain || '',\n },\n };\n\n return loadedConfig;\n } catch {\n return null;\n }\n}\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 const config = loadConfigFromFile();\n\n if (!config) {\n throw new Error('No se pudo cargar la configuración');\n }\n\n return {\n version: '1.0.0',\n credentials: {\n github: {\n token: config.github.token,\n username: config.github.username,\n },\n supabase: {\n accessToken: config.supabase.token,\n organizationId: config.supabase.orgId,\n },\n jira: {\n email: config.jira.email,\n apiToken: config.jira.token,\n domain: config.jira.domain,\n },\n },\n defaults: {\n githubOrg: config.github.org || undefined,\n supabaseRegion: config.supabase.region,\n jiraProjectType: 'software',\n },\n };\n}\n\nexport async function hasConfig(): Promise<boolean> {\n const config = loadConfigFromFile();\n\n if (!config) {\n return false;\n }\n\n return (\n config.github.token !== '' &&\n config.supabase.token !== '' &&\n config.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 const owner = org || config.credentials.github.username;\n\n return withSpinner(\n 'Creando repositorio en GitHub...',\n async () => {\n let repo;\n\n if (org) {\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 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.ssh_url;\n },\n `GitHub: ${owner}/${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\nexport async function checkGitHubRepoExists(\n projectName: string,\n config: LFTConfig\n): Promise<{ exists: boolean; url?: string }> {\n const octokit = new Octokit({ auth: config.credentials.github.token });\n const org = config.defaults.githubOrg;\n const owner = org || config.credentials.github.username;\n\n try {\n const existing = await octokit.rest.repos.get({\n owner,\n repo: projectName,\n });\n return { exists: true, url: existing.data.ssh_url };\n } catch {\n return { exists: 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 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 { withSpinner, createSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.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\nasync function findExistingProject(\n projectName: string,\n token: string\n): Promise<SupabaseProjectResponse | null> {\n const response = await fetch(`${SUPABASE_API_URL}/projects`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) return null;\n\n const projects = (await response.json()) as SupabaseProjectResponse[];\n return projects.find((p) => p.name === projectName) || null;\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 // Verificar si ya existe un proyecto con ese nombre\n const existing = await findExistingProject(projectName, token);\n if (existing) {\n logger.success(`Supabase: ${projectName} (ya existe)`);\n const { anonKey, serviceKey } = await getProjectApiKeys(existing.id, token);\n return {\n url: `https://${existing.id}.supabase.co`,\n anonKey,\n serviceKey,\n };\n }\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\nexport async function checkSupabaseProjectExists(\n projectName: string,\n config: LFTConfig\n): Promise<{ exists: boolean; url?: string; projectId?: string }> {\n const token = config.credentials.supabase.accessToken;\n const existing = await findExistingProject(projectName, token);\n\n if (existing) {\n return { exists: true, url: `https://${existing.id}.supabase.co`, projectId: existing.id };\n }\n return { exists: false };\n}\n\nexport async function getExistingSupabaseKeys(\n projectId: string,\n config: LFTConfig\n): Promise<SupabaseProjectResult> {\n return withSpinner(\n 'Obteniendo credenciales de Supabase...',\n async () => {\n const token = config.credentials.supabase.accessToken;\n const { anonKey, serviceKey } = await getProjectApiKeys(projectId, token);\n return {\n url: `https://${projectId}.supabase.co`,\n anonKey,\n serviceKey,\n };\n },\n 'Supabase: credenciales obtenidas'\n );\n}\n","export 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 // No verificamos si el directorio existe porque ahora manejamos recursos existentes\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\nasync function findExistingJiraProject(\n projectName: string,\n auth: string,\n domain: string\n): Promise<JiraProjectResponse | null> {\n const response = await fetch(\n `https://${domain}/rest/api/3/project/search?query=${encodeURIComponent(projectName)}`,\n {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { values: JiraProjectResponse[] };\n return data.values.find((p) => p.name === projectName) || null;\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: ${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\nexport async function checkJiraProjectExists(\n projectName: string,\n config: LFTConfig\n): Promise<{ exists: boolean; url?: string }> {\n const { email, apiToken, domain } = config.credentials.jira;\n const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');\n\n const existing = await findExistingJiraProject(projectName, auth, domain);\n if (existing) {\n return { exists: true, url: `https://${domain}/browse/${existing.key}` };\n }\n return { exists: false };\n}\n","import { execa } from 'execa';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { withSpinner } from '../ui/spinner.js';\nimport { logger } from '../ui/logger.js';\n\nexport async function scaffoldNextJs(\n projectName: string,\n projectPath: string\n): Promise<void> {\n const targetDir = path.join(process.cwd(), projectName);\n\n if (existsSync(targetDir)) {\n logger.success(`Next.js: ${projectName} (ya existe)`);\n return;\n }\n\n await withSpinner(\n 'Inicializando proyecto Next.js con template LFT...',\n async () => {\n await execa('npx', [\n '@create-lft-app/nextjs@latest',\n projectName,\n '--cwd', process.cwd(),\n ], {\n cwd: process.cwd(),\n stdio: 'pipe',\n });\n },\n `Next.js: ${projectName}`\n );\n}\n","import { execa } from 'execa';\nimport { withSpinner } from '../ui/spinner.js';\n\nexport async function installDependencies(projectPath: string): Promise<void> {\n await withSpinner(\n 'Instalando dependencias...',\n async () => {\n await execa('npm', ['install'], {\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 // Inicializar git (simple-git requiere que el directorio sea un repo)\n await git.init();\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';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\nfunction getVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const pkgPath = join(__dirname, '..', '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version || '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nfunction getKangarooArt(): string {\n const c = chalk.cyan;\n return c(` \n ██ ███ \n ██████████ \n █ ████████ \n ███████████ \n ███████████ \n █████ █████ \n ██ █ █████ \n ██ █ ████ \n ██ ██ ███ ██ \n ███ ███████████████ \n ███ ██ █████ \n ████ █ ██████ \n ████ ██ ██████████ \n ██ ██ ███████████ \n ██ █ █ ███ ██████████ \n ██ █ ███ █████████████ \n ██ ██ █ █████████ \n ██ ██ █████████ ██████ \n ██ ███ █████ ███████ ██████ \n ███ ██████ ██████████████████ \n ███ █████████████ ████████████████ \n ██ ██████████████████████████████████ \n ██ █████ ███████████████ \n ██ ███████ \n ██ ████████ \n ██ █████ ███ \n ██ ██ ██████ ██ \n ██ ███ ███████ ██ \n ███ █ ████ ██ ███████ ██ \n ███ ██ █████████ ███ ███████ ██ \n ███ ██ ███████████████ ████████ ██ \n █████ ██ ███████ ██ ██ ████ ██ \n █ ███ ███ ████████ ██ ███ ██ █ \n ███ ████ ████ ███████ ██ ██ ██ ██ \n ███ ████████ ███████ ██ ██ ██ ██ \n █████ █████████ ██ █ █ █ \n ███████████████ ███ ██ ██ █ \n ██████ ██ ███ ██ █████ \n ████ ███ ██ █████████ \n █████ ████ ██████████ ███ \n ████ ████ █████████ \n ███████ \n ██ \n`);\n}\n\nexport function showBanner(): void {\n const version = getVersion();\n const title = chalk.bold.cyan('create-lft-app');\n const versionText = chalk.gray(`v${version}`);\n const description = chalk.white('Scaffolding para proyectos LFT');\n\n console.log(getKangarooArt());\n\n const banner = boxen(`${title} ${versionText}\\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","existsSync","anonKey","serviceKey","project","existsSync","existsSync","execa","execa","path","path","chalk","chalk","path","existsSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@create-lft-app/cli",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "CLI para crear proyectos LFT con Next.js, GitHub, Supabase y Jira",
5
5
  "type": "module",
6
6
  "bin": {