@nimbuslab/cli 0.6.3 → 0.8.0

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.
@@ -1,14 +1,144 @@
1
1
  import * as p from "@clack/prompts"
2
2
  import pc from "picocolors"
3
- import { $} from "bun"
4
- import { rm } from "node:fs/promises"
3
+ import { $ } from "bun"
4
+ import { rm, mkdir } from "node:fs/promises"
5
5
  import { join } from "node:path"
6
6
 
7
+ // AI Assistant configs
8
+ const AI_CONFIGS: Record<string, { filename: string; content: (type: string) => string }> = {
9
+ claude: {
10
+ filename: "CLAUDE.md",
11
+ content: (type) => `# ${type === "landing" ? "Landing Page" : type === "app" ? "Web App" : "Monorepo"}
12
+
13
+ ## Stack
14
+ - Next.js 16 (App Router, Turbopack)
15
+ - React 19 (Server Components)
16
+ - TypeScript (strict)
17
+ - Tailwind CSS 4
18
+ - shadcn/ui
19
+ - Bun
20
+ ${type === "app" ? "- Better Auth\n- Drizzle + PostgreSQL" : ""}
21
+
22
+ ## Commands
23
+ \`\`\`bash
24
+ bun dev # Start development
25
+ bun build # Production build
26
+ bun lint # Run ESLint
27
+ ${type === "app" ? "bun setup # Setup database" : ""}
28
+ \`\`\`
29
+
30
+ ## Conventions
31
+ - Use \`bun\` for all package operations
32
+ - Server Components by default
33
+ - Dark mode first design
34
+ - Use \`cn()\` for conditional classes
35
+ - Add components: \`bunx --bun shadcn@latest add [component]\`
36
+ `,
37
+ },
38
+ cursor: {
39
+ filename: ".cursorrules",
40
+ content: (type) => `# Cursor Rules
41
+
42
+ Stack: Next.js 16, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Bun
43
+ ${type === "app" ? "Auth: Better Auth | DB: Drizzle + PostgreSQL" : ""}
44
+
45
+ - Server Components by default
46
+ - "use client" only when needed
47
+ - Tailwind utility classes
48
+ - cn() for conditional classes
49
+ - Dark mode first
50
+ `,
51
+ },
52
+ gemini: {
53
+ filename: ".gemini/GEMINI.md",
54
+ content: (type) => `# ${type === "landing" ? "Landing Page" : type === "app" ? "Web App" : "Monorepo"}
55
+
56
+ ## Stack
57
+ - Next.js 16 (App Router, Turbopack)
58
+ - React 19 (Server Components)
59
+ - TypeScript (strict)
60
+ - Tailwind CSS 4
61
+ - shadcn/ui
62
+ - Bun
63
+ ${type === "app" ? "- Better Auth\n- Drizzle + PostgreSQL" : ""}
64
+
65
+ ## Conventions
66
+ - Use \`bun\` for all package operations
67
+ - Server Components by default
68
+ - Dark mode first design
69
+ - Use \`cn()\` for conditional classes
70
+ `,
71
+ },
72
+ copilot: {
73
+ filename: ".github/copilot-instructions.md",
74
+ content: (type) => `# GitHub Copilot Instructions
75
+
76
+ Stack: Next.js 16, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Bun
77
+ ${type === "app" ? "Auth: Better Auth | DB: Drizzle + PostgreSQL" : ""}
78
+
79
+ ## Do
80
+ - Use TypeScript strict mode
81
+ - Prefer Server Components
82
+ - Use Tailwind for styling
83
+ - Use cn() for class merging
84
+
85
+ ## Don't
86
+ - Use CSS modules or styled-components
87
+ - Use class components
88
+ - Add unnecessary dependencies
89
+ `,
90
+ },
91
+ windsurf: {
92
+ filename: ".windsurfrules",
93
+ content: (type) => `# Windsurf Rules
94
+
95
+ Stack: Next.js 16, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Bun
96
+ ${type === "app" ? "Auth: Better Auth | DB: Drizzle + PostgreSQL" : ""}
97
+
98
+ - Server Components by default
99
+ - "use client" only when needed
100
+ - Tailwind utility classes
101
+ - cn() for conditional classes
102
+ - Dark mode first
103
+ `,
104
+ },
105
+ }
106
+
107
+ // GitHub CLI check
108
+ async function checkGitHubCli(): Promise<{
109
+ installed: boolean
110
+ authenticated: boolean
111
+ username: string | null
112
+ orgs: string[]
113
+ }> {
114
+ const checkCmd = process.platform === "win32" ? "where" : "which"
115
+ try {
116
+ const hasGh = await $`${checkCmd} gh`.quiet().nothrow()
117
+ if (hasGh.exitCode !== 0) {
118
+ return { installed: false, authenticated: false, username: null, orgs: [] }
119
+ }
120
+
121
+ const authStatus = await $`gh auth status`.quiet().nothrow()
122
+ if (authStatus.exitCode !== 0) {
123
+ return { installed: true, authenticated: false, username: null, orgs: [] }
124
+ }
125
+
126
+ const username = (await $`gh api user --jq '.login'`.quiet().text()).trim()
127
+ const orgsJson = await $`gh api user/orgs --jq '.[].login'`.quiet().text()
128
+ const orgs = orgsJson.trim().split("\n").filter(Boolean)
129
+
130
+ return { installed: true, authenticated: true, username, orgs }
131
+ } catch {
132
+ return { installed: false, authenticated: false, username: null, orgs: [] }
133
+ }
134
+ }
135
+
7
136
  // Templates privados (nimbuslab-templates) - uso interno
8
137
  const PRIVATE_TEMPLATES = {
9
138
  "fast": "nimbuslab-templates/fast-template",
10
139
  "fast+": "nimbuslab-templates/fastplus-template",
11
140
  "fast+-monorepo": "nimbuslab-templates/fastplus-monorepo-template",
141
+ "nimbus-core": "nimbuslab/nimbus-core",
12
142
  }
13
143
 
14
144
  // Templates públicos (nimbuslab)
@@ -39,7 +169,7 @@ async function isNimbuslabMember(): Promise<{ isMember: boolean; user: string |
39
169
  }
40
170
  }
41
171
 
42
- type ProjectType = "fast" | "fast+" | "landing" | "app" | "turborepo"
172
+ type ProjectType = "fast" | "fast+" | "landing" | "app" | "turborepo" | "nimbus-core"
43
173
 
44
174
  interface ProjectConfig {
45
175
  name: string
@@ -50,6 +180,9 @@ interface ProjectConfig {
50
180
  github: boolean
51
181
  githubOrg: string | null
52
182
  githubDescription: string
183
+ // Public template configs
184
+ theme: "dark" | "light" | "system"
185
+ aiAssistant: string | null
53
186
  // M26: Número do contrato (fast only)
54
187
  contractNumber: string
55
188
  // M21-M23: Configs de infra
@@ -75,6 +208,7 @@ interface CreateFlags {
75
208
  fast: boolean
76
209
  fastPlus: boolean
77
210
  fastTurborepo: boolean
211
+ core: boolean
78
212
  // Outros
79
213
  noGit: boolean
80
214
  noInstall: boolean
@@ -91,6 +225,7 @@ function parseFlags(args: string[]): { flags: CreateFlags; projectName: string |
91
225
  fast: false,
92
226
  fastPlus: false,
93
227
  fastTurborepo: false,
228
+ core: false,
94
229
  noGit: false,
95
230
  noInstall: false,
96
231
  railway: false,
@@ -117,6 +252,8 @@ function parseFlags(args: string[]): { flags: CreateFlags; projectName: string |
117
252
  flags.fastPlus = true
118
253
  } else if (arg === "--fast-turborepo") {
119
254
  flags.fastTurborepo = true
255
+ } else if (arg === "--core") {
256
+ flags.core = true
120
257
  } else if (arg === "--no-git") {
121
258
  flags.noGit = true
122
259
  } else if (arg === "--no-install") {
@@ -143,7 +280,7 @@ async function ensureRailwayCli(): Promise<boolean> {
143
280
 
144
281
  if (hasRailway) return true
145
282
 
146
- console.log(pc.yellow("Railway CLI nao encontrado. Instalando..."))
283
+ console.log(pc.yellow("Railway CLI not found. Installing..."))
147
284
  console.log()
148
285
 
149
286
  try {
@@ -152,11 +289,11 @@ async function ensureRailwayCli(): Promise<boolean> {
152
289
  } else {
153
290
  await $`curl -fsSL https://railway.app/install.sh | sh`.quiet()
154
291
  }
155
- console.log(pc.green("Railway CLI instalado com sucesso!"))
292
+ console.log(pc.green("Railway CLI installed successfully!"))
156
293
  return true
157
294
  } catch (error) {
158
- console.log(pc.red("Erro ao instalar Railway CLI."))
159
- console.log(pc.dim("Instale manualmente: https://docs.railway.app/guides/cli"))
295
+ console.log(pc.red("Error installing Railway CLI."))
296
+ console.log(pc.dim("Install manually: https://docs.railway.app/guides/cli"))
160
297
  return false
161
298
  }
162
299
  }
@@ -192,8 +329,8 @@ export async function create(args: string[]) {
192
329
  const hasGh = await $`${checkCmd} gh`.quiet().then(() => true).catch(() => false)
193
330
 
194
331
  if (!hasBun) {
195
- console.log(pc.red("Erro: Bun nao encontrado."))
196
- console.log(pc.dim("Instale em: https://bun.sh"))
332
+ console.log(pc.red("Error: Bun not found."))
333
+ console.log(pc.dim("Install from: https://bun.sh"))
197
334
  console.log()
198
335
  if (process.platform === "win32") {
199
336
  console.log(pc.cyan('powershell -c "irm bun.sh/install.ps1 | iex"'))
@@ -205,30 +342,17 @@ export async function create(args: string[]) {
205
342
  }
206
343
 
207
344
  if (!hasGit) {
208
- console.log(pc.red("Erro: Git nao encontrado."))
209
- console.log(pc.dim("Instale git para continuar."))
345
+ console.log(pc.red("Error: Git not found."))
346
+ console.log(pc.dim("Install git to continue."))
210
347
  process.exit(1)
211
348
  }
212
349
 
350
+ // GitHub CLI is optional for public templates
351
+ // Will be checked later if user wants to create a repo
213
352
  if (!hasGh) {
214
- console.log(pc.red("Erro: GitHub CLI (gh) nao encontrado."))
215
- console.log(pc.dim("Instale em: https://cli.github.com"))
353
+ console.log(pc.dim(" GitHub CLI not found (repo creation will be skipped)"))
354
+ console.log(pc.dim(" Install from: https://cli.github.com"))
216
355
  console.log()
217
- if (process.platform === "win32") {
218
- console.log(pc.cyan("winget install GitHub.cli"))
219
- } else {
220
- console.log(pc.cyan("sudo apt install gh # ou brew install gh"))
221
- }
222
- console.log()
223
- process.exit(1)
224
- }
225
-
226
- // Verifica se gh esta autenticado
227
- const ghAuth = await $`gh auth status`.quiet().then(() => true).catch(() => false)
228
- if (!ghAuth) {
229
- console.log(pc.red("Erro: GitHub CLI nao autenticado."))
230
- console.log(pc.dim("Execute: gh auth login"))
231
- process.exit(1)
232
356
  }
233
357
 
234
358
  // M30: Verificar/instalar Railway CLI
@@ -236,26 +360,27 @@ export async function create(args: string[]) {
236
360
  if (hasRailway) {
237
361
  const railwayAuth = await isRailwayAuthenticated()
238
362
  if (!railwayAuth) {
239
- console.log(pc.yellow("Railway CLI nao autenticado."))
240
- console.log(pc.dim("Execute: railway login"))
363
+ console.log(pc.yellow("Railway CLI not authenticated."))
364
+ console.log(pc.dim("Run: railway login"))
241
365
  console.log()
242
366
  }
243
367
  }
244
368
 
245
369
  const { flags, projectName } = parseFlags(args)
246
370
 
247
- p.intro(pc.bgCyan(pc.black(" Novo Projeto nimbuslab ")))
371
+ p.intro(pc.bgCyan(pc.black(" New nimbuslab Project ")))
248
372
 
249
373
  let config: ProjectConfig | symbol
250
374
 
251
375
  // Determina tipo baseado nas flags
252
- const hasTypeFlag = flags.landing || flags.app || flags.turborepo || flags.fast || flags.fastPlus || flags.fastTurborepo
376
+ const hasTypeFlag = flags.landing || flags.app || flags.turborepo || flags.fast || flags.fastPlus || flags.fastTurborepo || flags.core
253
377
  const typeFromFlag: ProjectType | null = flags.landing ? "landing"
254
378
  : flags.app ? "app"
255
379
  : flags.turborepo ? "turborepo"
256
380
  : flags.fastTurborepo ? "fast+"
257
381
  : flags.fastPlus ? "fast+"
258
382
  : flags.fast ? "fast"
383
+ : flags.core ? "nimbus-core"
259
384
  : null
260
385
  const monorepoFromFlag = flags.fastTurborepo
261
386
 
@@ -272,6 +397,8 @@ export async function create(args: string[]) {
272
397
  github: false,
273
398
  githubOrg: null,
274
399
  githubDescription: "",
400
+ theme: "dark" as const,
401
+ aiAssistant: null,
275
402
  contractNumber: "",
276
403
  resendApiKey: "",
277
404
  resendFromEmail: "",
@@ -284,10 +411,10 @@ export async function create(args: string[]) {
284
411
  }
285
412
 
286
413
  const typeLabel = flags.turborepo ? "fast+ (monorepo)" : config.type
287
- console.log(pc.dim(` Projeto: ${projectName}`))
288
- console.log(pc.dim(` Tipo: ${typeLabel}`))
289
- console.log(pc.dim(` Git: ${config.git ? "sim" : "nao"}`))
290
- console.log(pc.dim(` Instalar: ${config.install ? "sim" : "nao"}`))
414
+ console.log(pc.dim(` Project: ${projectName}`))
415
+ console.log(pc.dim(` Type: ${typeLabel}`))
416
+ console.log(pc.dim(` Git: ${config.git ? "yes" : "no"}`))
417
+ console.log(pc.dim(` Install: ${config.install ? "yes" : "no"}`))
291
418
  if (flags.railway) console.log(pc.dim(` Railway: configurar`))
292
419
  if (flags.template) console.log(pc.dim(` Template: ${flags.template}`))
293
420
  console.log()
@@ -305,7 +432,7 @@ export async function create(args: string[]) {
305
432
  }
306
433
  } else {
307
434
  // Fast+: cria projeto novo automaticamente
308
- console.log(pc.dim(` Criando projeto Railway: ${projectName}...`))
435
+ console.log(pc.dim(` Creating project Railway: ${projectName}...`))
309
436
  try {
310
437
  const result = await $`echo "" | railway init -n ${projectName} -w nimbuslab --json`.text()
311
438
  const newProject = JSON.parse(result)
@@ -316,7 +443,7 @@ export async function create(args: string[]) {
316
443
  }
317
444
  }
318
445
  } else {
319
- console.log(pc.yellow(` Railway: nao autenticado (railway login)`))
446
+ console.log(pc.yellow(` Railway: not authenticated (railway login)`))
320
447
  }
321
448
  console.log()
322
449
  }
@@ -325,13 +452,13 @@ export async function create(args: string[]) {
325
452
  }
326
453
 
327
454
  if (p.isCancel(config)) {
328
- p.cancel("Operacao cancelada")
455
+ p.cancel("Operation cancelled")
329
456
  process.exit(0)
330
457
  }
331
458
 
332
459
  await createProject(config as ProjectConfig)
333
460
 
334
- p.outro(pc.green("Projeto criado com sucesso!"))
461
+ p.outro(pc.green("Project created successfully!"))
335
462
 
336
463
  showNextSteps(config as ProjectConfig)
337
464
  }
@@ -341,18 +468,18 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
341
468
  const { isMember, user } = await isNimbuslabMember()
342
469
 
343
470
  // Saudacao
344
- const greeting = user ? `Ola, ${user}!` : "Ola!"
471
+ const greeting = user ? `Hello, ${user}!` : "Hello!"
345
472
  console.log(pc.dim(` ${greeting}`))
346
473
  console.log()
347
474
 
348
475
  const name = await p.text({
349
- message: "Nome do projeto:",
476
+ message: "Project name:",
350
477
  placeholder: "meu-projeto",
351
478
  initialValue: initialName,
352
479
  validate: (value) => {
353
- if (!value) return "Nome e obrigatorio"
480
+ if (!value) return "Name is required"
354
481
  if (!/^[a-z0-9-]+$/.test(value)) {
355
- return "Use apenas letras minusculas, numeros e hifens"
482
+ return "Use only lowercase letters, numbers and hyphens"
356
483
  }
357
484
  return undefined
358
485
  },
@@ -370,7 +497,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
370
497
  {
371
498
  value: "app",
372
499
  label: "Web App",
373
- hint: "Landing + Better Auth + Prisma",
500
+ hint: "Landing + Better Auth + Drizzle",
374
501
  },
375
502
  {
376
503
  value: "turborepo",
@@ -384,17 +511,22 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
384
511
  {
385
512
  value: "fast",
386
513
  label: "fast",
387
- hint: "Landing page metodologia fast",
514
+ hint: "Landing page fast methodology",
388
515
  },
389
516
  {
390
517
  value: "fast+",
391
518
  label: "fast+",
392
- hint: "SaaS completo",
519
+ hint: "Complete SaaS",
520
+ },
521
+ {
522
+ value: "nimbus-core",
523
+ label: "nimbus-core",
524
+ hint: "External projects (stealth mode)",
393
525
  },
394
526
  ] : []
395
527
 
396
528
  const type = await p.select({
397
- message: "Tipo de projeto:",
529
+ message: "Project type:",
398
530
  options: [...publicOptions, ...privateOptions],
399
531
  })
400
532
 
@@ -405,15 +537,58 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
405
537
 
406
538
  // Se não é membro e tentou usar template privado (via flag), bloqueia
407
539
  if (!isMember && !isPublicTemplate) {
408
- console.log(pc.red("Erro: Template disponivel apenas para membros nimbuslab"))
540
+ console.log(pc.red("Error: Template available only for nimbuslab members"))
409
541
  process.exit(1)
410
542
  }
411
543
 
544
+ // Fluxo especial para nimbus-core (projetos externos)
545
+ if (type === "nimbus-core") {
546
+ console.log()
547
+ console.log(pc.dim(" nimbus-core: Motor para projetos externos (stealth mode)"))
548
+ console.log()
549
+
550
+ // Sempre cria repo no GitHub (org nimbuslab, privado)
551
+ const createGithub = await p.confirm({
552
+ message: "Create GitHub repository? (nimbuslab, private)",
553
+ initialValue: true,
554
+ })
555
+ if (p.isCancel(createGithub)) return createGithub
556
+
557
+ // Perguntar URL do repo do cliente para clonar no workspace
558
+ const clientRepo = await p.text({
559
+ message: "Client repo URL (optional, to clone in workspace):",
560
+ placeholder: "git@github.com:client/repo.git",
561
+ })
562
+ if (p.isCancel(clientRepo)) return clientRepo
563
+
564
+ return {
565
+ name: name as string,
566
+ type: "nimbus-core" as ProjectType,
567
+ monorepo: false,
568
+ git: true, // sempre init git
569
+ install: false, // nimbus-core não tem package.json
570
+ github: createGithub as boolean,
571
+ githubOrg: "nimbuslab", // sempre na org nimbuslab
572
+ githubDescription: `nimbus-core for ${name} - external project`,
573
+ theme: "dark" as const,
574
+ aiAssistant: null,
575
+ contractNumber: "",
576
+ resendApiKey: "",
577
+ resendFromEmail: "",
578
+ contactEmail: "",
579
+ railwayProject: "",
580
+ railwayToken: "",
581
+ stagingUrl: "",
582
+ productionUrl: "",
583
+ customTemplate: (clientRepo as string) || null, // reusa campo para URL do cliente
584
+ }
585
+ }
586
+
412
587
  // Pergunta sobre monorepo apenas para fast+
413
588
  let monorepo = false
414
589
  if (type === "fast+") {
415
590
  const useMonorepo = await p.confirm({
416
- message: "Usar monorepo (Turborepo)?",
591
+ message: "Use monorepo (Turborepo)?",
417
592
  initialValue: false,
418
593
  })
419
594
  if (p.isCancel(useMonorepo)) return useMonorepo
@@ -421,7 +596,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
421
596
  }
422
597
 
423
598
  const git = await p.confirm({
424
- message: "Inicializar repositorio Git?",
599
+ message: "Initialize Git repository?",
425
600
  initialValue: true,
426
601
  })
427
602
 
@@ -434,7 +609,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
434
609
 
435
610
  if (git) {
436
611
  const createGithub = await p.confirm({
437
- message: "Criar repositorio no GitHub?",
612
+ message: "Create GitHub repository?",
438
613
  initialValue: false,
439
614
  })
440
615
 
@@ -445,12 +620,12 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
445
620
  if (github) {
446
621
  // M13: Selecao de organizacao GitHub
447
622
  const org = await p.select({
448
- message: "Organizacao GitHub:",
623
+ message: "GitHub organization:",
449
624
  options: [
450
- { value: "nimbuslab", label: "nimbuslab", hint: "Org principal" },
451
- { value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "Projetos de clientes" },
625
+ { value: "nimbuslab", label: "nimbuslab", hint: "Main org" },
626
+ { value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "Client projects" },
452
627
  { value: "nimbuslab-templates", label: "nimbuslab-templates", hint: "Templates" },
453
- { value: null, label: "Pessoal", hint: "Sem organizacao" },
628
+ { value: null, label: "Pessoal", hint: "Personal" },
454
629
  ],
455
630
  })
456
631
 
@@ -459,7 +634,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
459
634
 
460
635
  // M14: Descricao do repo
461
636
  const description = await p.text({
462
- message: "Descricao do repositorio:",
637
+ message: "Repository description:",
463
638
  placeholder: "Landing page para cliente X",
464
639
  initialValue: type === "fast" ? "Landing page fast by nimbuslab" : "SaaS fast+ by nimbuslab",
465
640
  })
@@ -473,18 +648,85 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
473
648
  let contractNumber = ""
474
649
  if (type === "fast") {
475
650
  const contract = await p.text({
476
- message: "Numero do contrato (ex: 001):",
651
+ message: "Contract number (ex: 001):",
477
652
  placeholder: "001",
478
- validate: (v) => v ? undefined : "Numero do contrato e obrigatorio para fast",
653
+ validate: (v) => v ? undefined : "Contract number is required for fast",
479
654
  })
480
655
  if (p.isCancel(contract)) return contract
481
656
  contractNumber = contract as string
482
657
  }
483
658
 
484
- // Templates públicos têm fluxo simplificado
659
+ // Templates públicos têm fluxo simplificado mas com configs globais
485
660
  if (isPublicTemplate) {
661
+ // Theme selection
662
+ const theme = await p.select({
663
+ message: "Default theme:",
664
+ options: [
665
+ { value: "dark", label: "Dark", hint: "recommended" },
666
+ { value: "light", label: "Light" },
667
+ { value: "system", label: "System", hint: "follows OS preference" },
668
+ ],
669
+ })
670
+ if (p.isCancel(theme)) return theme
671
+
672
+ // AI Assistant selection
673
+ const aiAssistant = await p.select({
674
+ message: "Which AI assistant do you use?",
675
+ options: [
676
+ { value: "claude", label: "Claude Code", hint: "Anthropic" },
677
+ { value: "cursor", label: "Cursor", hint: "AI-first editor" },
678
+ { value: "gemini", label: "Gemini CLI", hint: "Google" },
679
+ { value: "copilot", label: "GitHub Copilot" },
680
+ { value: "windsurf", label: "Windsurf", hint: "Codeium" },
681
+ { value: "none", label: "None", hint: "skip AI config" },
682
+ ],
683
+ })
684
+ if (p.isCancel(aiAssistant)) return aiAssistant
685
+
686
+ // GitHub repo for public templates (uses user's orgs)
687
+ let publicGithub = false
688
+ let publicGithubOrg: string | null = null
689
+
690
+ if (git) {
691
+ const gh = await checkGitHubCli()
692
+
693
+ if (gh.installed && gh.authenticated) {
694
+ const createRepo = await p.confirm({
695
+ message: "Create GitHub repository?",
696
+ initialValue: false,
697
+ })
698
+ if (p.isCancel(createRepo)) return createRepo
699
+
700
+ publicGithub = createRepo as boolean
701
+
702
+ if (publicGithub) {
703
+ const repoOptions: { value: string | null; label: string; hint?: string }[] = [
704
+ { value: gh.username, label: gh.username!, hint: "personal account" },
705
+ ...gh.orgs.map((org) => ({ value: org, label: org })),
706
+ ]
707
+
708
+ const repoOwner = await p.select({
709
+ message: "Where to create the repository?",
710
+ options: repoOptions,
711
+ })
712
+ if (p.isCancel(repoOwner)) return repoOwner
713
+ publicGithubOrg = repoOwner as string | null
714
+
715
+ const repoVisibility = await p.select({
716
+ message: "Repository visibility:",
717
+ options: [
718
+ { value: "private", label: "Private", hint: "recommended" },
719
+ { value: "public", label: "Public" },
720
+ ],
721
+ })
722
+ if (p.isCancel(repoVisibility)) return repoVisibility
723
+ githubDescription = repoVisibility as string // reusing for visibility
724
+ }
725
+ }
726
+ }
727
+
486
728
  const install = await p.confirm({
487
- message: "Instalar dependencias?",
729
+ message: "Install dependencies?",
488
730
  initialValue: true,
489
731
  })
490
732
 
@@ -496,9 +738,11 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
496
738
  monorepo: false,
497
739
  git: git as boolean,
498
740
  install: install as boolean,
499
- github,
500
- githubOrg,
741
+ github: publicGithub,
742
+ githubOrg: publicGithubOrg,
501
743
  githubDescription,
744
+ theme: theme as "dark" | "light" | "system",
745
+ aiAssistant: aiAssistant === "none" ? null : aiAssistant as string,
502
746
  contractNumber: "",
503
747
  resendApiKey: "",
504
748
  resendFromEmail: "",
@@ -533,11 +777,11 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
533
777
 
534
778
  // Menu de configuracoes opcionais
535
779
  const infraOptions = await p.multiselect({
536
- message: "O que deseja configurar agora?",
780
+ message: "What do you want to configure now?",
537
781
  options: [
538
- { value: "urls", label: "URLs", hint: "staging e producao" },
539
- { value: "resend", label: "Resend", hint: "emails do formulario" },
540
- { value: "railway", label: "Railway", hint: "deploy e hospedagem" },
782
+ { value: "urls", label: "URLs", hint: "staging and production" },
783
+ { value: "resend", label: "Resend", hint: "form emails" },
784
+ { value: "railway", label: "Railway", hint: "deploy and hosting" },
541
785
  ],
542
786
  required: false,
543
787
  })
@@ -549,10 +793,10 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
549
793
  // URLs
550
794
  if (configItems.includes("urls")) {
551
795
  console.log()
552
- console.log(pc.dim(" URLs do projeto"))
796
+ console.log(pc.dim(" Project URLs"))
553
797
 
554
798
  const staging = await p.text({
555
- message: "URL de staging:",
799
+ message: "Staging URL:",
556
800
  placeholder: defaultStagingUrl,
557
801
  initialValue: defaultStagingUrl,
558
802
  })
@@ -560,7 +804,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
560
804
  stagingUrl = staging as string
561
805
 
562
806
  const production = await p.text({
563
- message: "URL de producao:",
807
+ message: "Production URL:",
564
808
  placeholder: defaultStagingUrl.replace('.nimbuslab.net.br', '.com.br'),
565
809
  initialValue: "",
566
810
  })
@@ -581,7 +825,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
581
825
  resendApiKey = resendKey as string
582
826
 
583
827
  const fromEmail = await p.text({
584
- message: "Email de envio (from):",
828
+ message: "From email:",
585
829
  placeholder: defaultFromEmail,
586
830
  initialValue: defaultFromEmail,
587
831
  })
@@ -589,7 +833,7 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
589
833
  resendFromEmail = fromEmail as string
590
834
 
591
835
  const contact = await p.text({
592
- message: "Email de contato (recebe formularios):",
836
+ message: "Contact email (receives forms):",
593
837
  placeholder: defaultContactEmail,
594
838
  initialValue: defaultContactEmail,
595
839
  })
@@ -613,21 +857,21 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
613
857
  const fastProject = projects.find(p => p.toLowerCase().includes("fast by nimbuslab"))
614
858
  if (fastProject) {
615
859
  railwayProject = fastProject
616
- console.log(pc.green(` Projeto: ${fastProject} (automatico)`))
860
+ console.log(pc.green(` Project: ${fastProject} (automatico)`))
617
861
  } else {
618
- console.log(pc.yellow(" Projeto 'Fast by nimbuslab' nao encontrado."))
619
- console.log(pc.dim(" Configure RAILWAY_TOKEN manualmente no .env"))
862
+ console.log(pc.yellow(" Project 'Fast by nimbuslab' not found."))
863
+ console.log(pc.dim(" Configure manually in .env"))
620
864
  }
621
865
  } else {
622
866
  // Fast+ pode escolher projeto existente ou criar novo
623
867
  const projectOptions = [
624
868
  ...projects.map(proj => ({ value: proj, label: proj })),
625
- { value: "__new__", label: "Criar novo projeto", hint: "via railway init" },
626
- { value: "__skip__", label: "Pular", hint: "Configurar depois" },
869
+ { value: "__new__", label: "Create new project", hint: "via railway init" },
870
+ { value: "__skip__", label: "Skip", hint: "Configure later" },
627
871
  ]
628
872
 
629
873
  const selectedProject = await p.select({
630
- message: "Projeto Railway para este SaaS:",
874
+ message: "Railway project for this SaaS:",
631
875
  options: projectOptions,
632
876
  })
633
877
 
@@ -636,32 +880,32 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
636
880
  if (selectedProject === "__new__") {
637
881
  // Criar projeto via Railway CLI
638
882
  const projectNameForRailway = name as string
639
- console.log(pc.dim(` Criando projeto "${projectNameForRailway}" no Railway...`))
883
+ console.log(pc.dim(` Creating project "${projectNameForRailway}" on Railway...`))
640
884
  try {
641
885
  // Usa echo para passar input vazio e aceitar defaults (workspace)
642
886
  const result = await $`echo "" | railway init -n ${projectNameForRailway} -w nimbuslab --json`.text()
643
887
  const newProject = JSON.parse(result)
644
888
  railwayProject = newProject.name || projectNameForRailway
645
- console.log(pc.green(` Projeto "${railwayProject}" criado com sucesso!`))
889
+ console.log(pc.green(` Projeto "${railwayProject}" created successfully!`))
646
890
  console.log(pc.dim(` ID: ${newProject.id || "N/A"}`))
647
891
  } catch (error) {
648
- console.log(pc.yellow(" Erro ao criar projeto via CLI."))
649
- console.log(pc.dim(" Crie manualmente em: https://railway.app/new"))
892
+ console.log(pc.yellow(" Error creating project via CLI."))
893
+ console.log(pc.dim(" Create manually at: https://railway.app/new"))
650
894
  }
651
895
  } else if (selectedProject !== "__skip__") {
652
896
  railwayProject = selectedProject as string
653
- console.log(pc.green(` Projeto selecionado: ${railwayProject}`))
897
+ console.log(pc.green(` Project selected: ${railwayProject}`))
654
898
  }
655
899
  }
656
900
  } else {
657
901
  console.log()
658
- console.log(pc.yellow(" Railway: nao autenticado (railway login)"))
659
- console.log(pc.dim(" Configure RAILWAY_TOKEN manualmente no .env"))
902
+ console.log(pc.yellow(" Railway: not authenticated (railway login)"))
903
+ console.log(pc.dim(" Configure manually in .env"))
660
904
  }
661
905
  }
662
906
 
663
907
  const install = await p.confirm({
664
- message: "Instalar dependencias?",
908
+ message: "Install dependencies?",
665
909
  initialValue: true,
666
910
  })
667
911
 
@@ -676,6 +920,8 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
676
920
  github,
677
921
  githubOrg,
678
922
  githubDescription,
923
+ theme: "dark" as const,
924
+ aiAssistant: null,
679
925
  contractNumber,
680
926
  resendApiKey,
681
927
  resendFromEmail,
@@ -708,7 +954,7 @@ async function createProject(config: ProjectConfig) {
708
954
  // Verifica se é template público
709
955
  const isPublicTemplate = ["landing", "app", "turborepo"].includes(config.type)
710
956
 
711
- s.start(`Clonando template ${templateLabel}...`)
957
+ s.start(`Cloning template ${templateLabel}...`)
712
958
  try {
713
959
  if (isPublicTemplate) {
714
960
  // Templates públicos: usa HTTPS (não precisa de auth)
@@ -719,23 +965,79 @@ async function createProject(config: ProjectConfig) {
719
965
  await $`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet()
720
966
  }
721
967
  await rm(join(config.name, ".git"), { recursive: true, force: true })
722
- s.stop(`Template ${templateLabel} clonado`)
968
+ s.stop(`Template cloned`)
723
969
  } catch (error) {
724
- s.stop("Erro ao clonar template")
725
- throw new Error(`Falha ao clonar template ${templateRepo}. Verifique sua conexao ou acesso ao repositorio.`)
970
+ s.stop("Error cloning template")
971
+ throw new Error(`Failed to clone template ${templateRepo}. Check your connection or repository access.`)
726
972
  }
727
973
 
728
- // Update package.json
729
- s.start("Configurando projeto...")
730
- try {
731
- const pkgPath = `${config.name}/package.json`
732
- const pkg = await Bun.file(pkgPath).json()
733
- pkg.name = config.name
734
- await Bun.write(pkgPath, JSON.stringify(pkg, null, 2))
735
- s.stop("Projeto configurado")
736
- } catch (error) {
737
- s.stop("Erro ao configurar")
738
- // Continue anyway
974
+ // nimbus-core: clone client repo in workspace if provided
975
+ if (config.type === "nimbus-core" && config.customTemplate) {
976
+ const clientRepoUrl = config.customTemplate
977
+ s.start(`Cloning client repo in workspace...`)
978
+ try {
979
+ const projectName = clientRepoUrl.split("/").pop()?.replace(".git", "") || "client-project"
980
+ await $`git clone ${clientRepoUrl} ${config.name}/workspace/${projectName}`.quiet()
981
+ s.stop(`Client repo cloned: workspace/${projectName}`)
982
+ } catch (error) {
983
+ s.stop("Error cloning client repo")
984
+ console.log(pc.dim(" You can clone manually: cd workspace && git clone <url>"))
985
+ }
986
+ }
987
+
988
+ // Update package.json (skip for nimbus-core)
989
+ if (config.type !== "nimbus-core") {
990
+ s.start("Configuring project...")
991
+ try {
992
+ const pkgPath = `${config.name}/package.json`
993
+ const pkg = await Bun.file(pkgPath).json()
994
+ pkg.name = config.name
995
+ await Bun.write(pkgPath, JSON.stringify(pkg, null, 2))
996
+ s.stop("Project configured")
997
+ } catch (error) {
998
+ s.stop("Error configuring")
999
+ // Continue anyway
1000
+ }
1001
+ }
1002
+
1003
+ // Apply theme config (public templates only)
1004
+ if (isPublicTemplate && config.theme) {
1005
+ s.start(`Setting theme to ${config.theme}...`)
1006
+ try {
1007
+ const layoutPath = `${config.name}/src/app/layout.tsx`
1008
+ let layout = await Bun.file(layoutPath).text()
1009
+ layout = layout.replace(
1010
+ /defaultTheme="(dark|light|system)"/,
1011
+ `defaultTheme="${config.theme}"`
1012
+ )
1013
+ await Bun.write(layoutPath, layout)
1014
+ s.stop(`Theme set to ${config.theme}`)
1015
+ } catch {
1016
+ s.stop("Theme config skipped")
1017
+ }
1018
+ }
1019
+
1020
+ // Generate AI config (public templates only)
1021
+ if (isPublicTemplate && config.aiAssistant) {
1022
+ const aiConfig = AI_CONFIGS[config.aiAssistant]
1023
+ if (aiConfig) {
1024
+ s.start(`Generating ${config.aiAssistant} config...`)
1025
+ try {
1026
+ const content = aiConfig.content(config.type)
1027
+ const filePath = `${config.name}/${aiConfig.filename}`
1028
+
1029
+ // Create directory if needed
1030
+ if (aiConfig.filename.includes("/")) {
1031
+ const dir = aiConfig.filename.split("/").slice(0, -1).join("/")
1032
+ await mkdir(`${config.name}/${dir}`, { recursive: true })
1033
+ }
1034
+
1035
+ await Bun.write(filePath, content)
1036
+ s.stop(`${aiConfig.filename} created`)
1037
+ } catch {
1038
+ s.stop("AI config skipped")
1039
+ }
1040
+ }
739
1041
  }
740
1042
 
741
1043
  // Setup fast+ if selected
@@ -747,7 +1049,7 @@ async function createProject(config: ProjectConfig) {
747
1049
 
748
1050
  // Git init with full branch flow: main → staging → develop
749
1051
  if (config.git) {
750
- s.start("Inicializando Git...")
1052
+ s.start("Initializing Git...")
751
1053
  try {
752
1054
  const cwd = config.name
753
1055
  // Init with main branch (not master)
@@ -759,25 +1061,38 @@ async function createProject(config: ProjectConfig) {
759
1061
  await $`git checkout -b staging`.cwd(cwd).quiet()
760
1062
  await $`git checkout -b develop`.cwd(cwd).quiet()
761
1063
 
762
- s.stop("Git inicializado (main -> staging -> develop)")
1064
+ s.stop("Git initialized (main -> staging -> develop)")
763
1065
  } catch (error) {
764
- s.stop("Erro ao inicializar Git")
1066
+ s.stop("Error initializing Git")
765
1067
  }
766
1068
 
767
1069
  // Create GitHub repo if requested (M13 + M14)
768
1070
  if (config.github) {
769
- s.start("Criando repositorio no GitHub...")
1071
+ s.start("Creating GitHub repository...")
770
1072
  try {
771
1073
  const cwd = config.name
772
1074
  const repoName = config.githubOrg
773
1075
  ? `${config.githubOrg}/${config.name}`
774
1076
  : config.name
775
1077
 
776
- // Create repo with description (private by default for client projects)
777
- const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public"
1078
+ // For public templates, githubDescription contains visibility (private/public)
1079
+ // For private templates, use org-based visibility
1080
+ // nimbus-core is ALWAYS private
1081
+ let visibility: string
1082
+ if (config.type === "nimbus-core") {
1083
+ visibility = "--private" // nimbus-core sempre privado
1084
+ } else if (isPublicTemplate) {
1085
+ visibility = config.githubDescription === "public" ? "--public" : "--private"
1086
+ } else {
1087
+ visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public"
1088
+ }
778
1089
 
779
- // Criar repo sem push automático
780
- await $`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet()
1090
+ // Create repo
1091
+ if (isPublicTemplate) {
1092
+ await $`gh repo create ${repoName} ${visibility} --source . --remote origin`.cwd(cwd).quiet()
1093
+ } else {
1094
+ await $`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet()
1095
+ }
781
1096
 
782
1097
  // Push todas as branches na ordem correta: main -> staging -> develop
783
1098
  await $`git checkout main`.cwd(cwd).quiet()
@@ -787,10 +1102,10 @@ async function createProject(config: ProjectConfig) {
787
1102
  await $`git checkout develop`.cwd(cwd).quiet()
788
1103
  await $`git push -u origin develop`.cwd(cwd).quiet()
789
1104
 
790
- s.stop(`GitHub: ${repoName} criado`)
1105
+ s.stop(`GitHub: ${repoName}`)
791
1106
  } catch (error) {
792
- s.stop("Erro ao criar repositorio GitHub")
793
- console.log(pc.dim(" Voce pode criar manualmente com: gh repo create"))
1107
+ s.stop("Error creating GitHub repository")
1108
+ console.log(pc.dim(" You can create manually with: gh repo create"))
794
1109
  }
795
1110
  }
796
1111
  }
@@ -802,8 +1117,8 @@ async function createProject(config: ProjectConfig) {
802
1117
  await $`railway link -p ${config.railwayProject}`.cwd(config.name).quiet()
803
1118
  s.stop(`Railway linkado: ${config.railwayProject}`)
804
1119
  } catch (error) {
805
- s.stop("Erro ao linkar Railway")
806
- console.log(pc.dim(" Execute manualmente: railway link"))
1120
+ s.stop("Error linking Railway")
1121
+ console.log(pc.dim(" Run manually: railway link"))
807
1122
  }
808
1123
  }
809
1124
 
@@ -815,18 +1130,18 @@ async function createProject(config: ProjectConfig) {
815
1130
  await Bun.write(`${config.name}/.env`, envContent)
816
1131
  s.stop("Arquivo .env criado")
817
1132
  } catch (error) {
818
- s.stop("Erro ao criar .env")
1133
+ s.stop("Error creating .env")
819
1134
  }
820
1135
  }
821
1136
 
822
1137
  // Install deps
823
1138
  if (config.install) {
824
- s.start("Instalando dependencias (pode demorar)...")
1139
+ s.start("Installing dependencies (pode demorar)...")
825
1140
  try {
826
1141
  await $`bun install`.cwd(config.name).quiet()
827
- s.stop("Dependencias instaladas")
1142
+ s.stop("Dependencies installed")
828
1143
  } catch (error) {
829
- s.stop("Erro ao instalar dependencias")
1144
+ s.stop("Error installing dependencies")
830
1145
  }
831
1146
  }
832
1147
  }
@@ -856,7 +1171,7 @@ function generateEnvFile(config: ProjectConfig): string {
856
1171
  lines.push("")
857
1172
  lines.push("# Railway")
858
1173
  if (config.railwayProject) {
859
- lines.push(`# Projeto: ${config.railwayProject}`)
1174
+ lines.push(`# Project: ${config.railwayProject}`)
860
1175
  }
861
1176
  lines.push(`RAILWAY_TOKEN=${config.railwayToken || "# Configure com: railway link"}`)
862
1177
  }
@@ -877,27 +1192,65 @@ function generateEnvFile(config: ProjectConfig): string {
877
1192
 
878
1193
  function showNextSteps(config: ProjectConfig) {
879
1194
  const isPublicTemplate = ["landing", "app", "turborepo"].includes(config.type)
1195
+ const needsSetup = config.type === "app" // app needs bun setup for DB
880
1196
 
881
1197
  console.log()
882
- console.log(pc.bold("Proximos passos:"))
1198
+ console.log(pc.bold("Next steps:"))
883
1199
  console.log()
884
1200
  console.log(` ${pc.cyan("cd")} ${config.name}`)
885
1201
 
1202
+ // nimbus-core tem fluxo especial
1203
+ if (config.type === "nimbus-core") {
1204
+ console.log()
1205
+ console.log(pc.dim(" nimbus-core: Motor para projetos externos"))
1206
+ console.log()
1207
+ console.log(pc.dim(" Para clonar repo do cliente:"))
1208
+ console.log(` ${pc.cyan("cd")} workspace`)
1209
+ console.log(` ${pc.cyan("git clone")} <repo-do-cliente>`)
1210
+ console.log()
1211
+ console.log(pc.dim(" Para usar a Lola:"))
1212
+ console.log(` ${pc.cyan("gemini")} ${pc.dim("# Gemini CLI")}`)
1213
+ console.log(` ${pc.cyan("claude --append-system-prompt-file .claude/agents/lola.md")}`)
1214
+ console.log()
1215
+ console.log(pc.yellow(" STEALTH MODE: Commits sem mencao a nimbuslab/Lola/IA"))
1216
+ console.log()
1217
+
1218
+ // GitHub info
1219
+ if (config.github) {
1220
+ const repoUrl = `https://github.com/nimbuslab/${config.name}`
1221
+ console.log(pc.green(` GitHub (private): ${repoUrl}`))
1222
+ console.log()
1223
+ }
1224
+
1225
+ console.log(pc.dim(" Docs: See README.md for full instructions"))
1226
+ console.log()
1227
+ return
1228
+ }
1229
+
886
1230
  if (!config.install) {
887
1231
  console.log(` ${pc.cyan("bun")} install`)
888
1232
  }
889
1233
 
890
- // Templates públicos não têm bun setup
891
- if (!isPublicTemplate) {
1234
+ // Templates que precisam de setup adicional
1235
+ if (!isPublicTemplate || needsSetup) {
892
1236
  console.log(` ${pc.cyan("bun")} setup`)
893
1237
  }
1238
+
894
1239
  console.log(` ${pc.cyan("bun")} dev`)
895
1240
  console.log()
896
1241
 
1242
+ // Explicar o que o bun setup faz para o app
1243
+ if (needsSetup && isPublicTemplate) {
1244
+ console.log(pc.dim(" bun setup will:"))
1245
+ console.log(pc.dim(" - Start PostgreSQL with Docker"))
1246
+ console.log(pc.dim(" - Run database migrations"))
1247
+ console.log(pc.dim(" - Create demo user (demo@example.com / demo1234)"))
1248
+ console.log()
1249
+ }
1250
+
897
1251
  // Git flow info
898
1252
  if (config.git) {
899
- console.log(pc.dim(" Git flow: main -> staging -> develop (branch atual)"))
900
- console.log()
1253
+ console.log(pc.dim(" Git: main -> staging -> develop (current branch)"))
901
1254
 
902
1255
  // GitHub info
903
1256
  if (config.github) {
@@ -905,36 +1258,56 @@ function showNextSteps(config: ProjectConfig) {
905
1258
  ? `https://github.com/${config.githubOrg}/${config.name}`
906
1259
  : `https://github.com/${config.name}`
907
1260
  console.log(pc.green(` GitHub: ${repoUrl}`))
908
- console.log()
909
- } else {
910
- console.log(pc.yellow(" Dica: Para criar repo GitHub, use 'gh repo create' ou 'bun setup'."))
1261
+ }
1262
+ console.log()
1263
+ }
1264
+
1265
+ // Theme e AI info
1266
+ if (isPublicTemplate) {
1267
+ if (config.theme !== "dark") {
1268
+ console.log(pc.dim(` Theme: ${config.theme}`))
1269
+ }
1270
+ if (config.aiAssistant) {
1271
+ const aiConfig = AI_CONFIGS[config.aiAssistant]
1272
+ if (aiConfig) {
1273
+ console.log(pc.dim(` AI config: ${aiConfig.filename}`))
1274
+ }
1275
+ }
1276
+ if (config.theme !== "dark" || config.aiAssistant) {
911
1277
  console.log()
912
1278
  }
913
1279
  }
914
1280
 
915
1281
  if (config.type === "fast+") {
916
- console.log(pc.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"))
1282
+ console.log(pc.dim(" bun setup will:"))
1283
+ console.log(pc.dim(" - Start PostgreSQL with Docker"))
1284
+ console.log(pc.dim(" - Run database migrations"))
1285
+ console.log(pc.dim(" - Create demo user (demo@example.com / demo1234)"))
1286
+ console.log()
1287
+ console.log(pc.dim(" Tip: Configure DATABASE_URL and BETTER_AUTH_SECRET in .env"))
917
1288
  if (!config.railwayToken) {
918
- console.log(pc.dim(" Railway: Crie um projeto em https://railway.app/new"))
1289
+ console.log(pc.dim(" Railway: Create a project at https://railway.app/new"))
919
1290
  }
920
1291
  console.log()
921
1292
  }
922
1293
 
923
- // Info sobre .env
924
- if (config.resendApiKey || config.stagingUrl) {
925
- console.log(pc.green(" .env configurado com sucesso!"))
926
- console.log()
927
- } else {
928
- console.log(pc.yellow(" Dica: Configure .env manualmente ou use 'bun setup'."))
929
- console.log()
1294
+ // Info sobre .env (templates privados)
1295
+ if (!isPublicTemplate) {
1296
+ if (config.resendApiKey || config.stagingUrl) {
1297
+ console.log(pc.green(" .env configured!"))
1298
+ console.log()
1299
+ } else {
1300
+ console.log(pc.yellow(" Tip: Configure .env manually or use 'bun setup'."))
1301
+ console.log()
1302
+ }
930
1303
  }
931
1304
 
932
- // Info sobre templates públicos
1305
+ // Info sobre templates
933
1306
  if (isPublicTemplate) {
934
- console.log(pc.dim(" Template open source (MIT) by nimbuslab"))
935
- console.log(pc.dim(` Documentacao: https://github.com/nimbuslab/create-next-${config.type === "turborepo" ? "turborepo" : config.type}`))
1307
+ console.log(pc.dim(" Open source template (MIT) by nimbuslab"))
1308
+ console.log(pc.dim(` https://github.com/nimbuslab/create-next-${config.type === "turborepo" ? "turborepo" : config.type}`))
936
1309
  } else {
937
- console.log(pc.dim(" Documentacao: https://github.com/nimbuslab-templates"))
1310
+ console.log(pc.dim(" https://github.com/nimbuslab-templates"))
938
1311
  }
939
1312
  console.log()
940
1313
  }