@nimbuslab/cli 0.7.0 → 0.9.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") {
@@ -210,25 +347,12 @@ export async function create(args: string[]) {
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("Error: GitHub CLI (gh) not found."))
215
- console.log(pc.dim("Install from: 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("Error: GitHub CLI not authenticated."))
230
- console.log(pc.dim("Run: gh auth login"))
231
- process.exit(1)
232
356
  }
233
357
 
234
358
  // M30: Verificar/instalar Railway CLI
@@ -249,13 +373,14 @@ export async function create(args: string[]) {
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: "",
@@ -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",
@@ -391,6 +518,11 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
391
518
  label: "fast+",
392
519
  hint: "Complete SaaS",
393
520
  },
521
+ {
522
+ value: "nimbus-core",
523
+ label: "nimbus-core",
524
+ hint: "External projects (stealth mode)",
525
+ },
394
526
  ] : []
395
527
 
396
528
  const type = await p.select({
@@ -409,6 +541,49 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
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+") {
@@ -481,8 +656,75 @@ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<
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
729
  message: "Install dependencies?",
488
730
  initialValue: true,
@@ -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: "",
@@ -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,
@@ -725,17 +971,73 @@ async function createProject(config: ProjectConfig) {
725
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("Configuring project...")
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("Project configured")
736
- } catch (error) {
737
- s.stop("Error configuring")
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
@@ -773,11 +1075,24 @@ async function createProject(config: ProjectConfig) {
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,7 +1102,7 @@ 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
1107
  s.stop("Error creating GitHub repository")
793
1108
  console.log(pc.dim(" You can create manually with: gh repo create"))
@@ -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
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 (current branch)"))
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(" Tip: To create GitHub repo, 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(" Tip: For 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
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 configured successfully!"))
926
- console.log()
927
- } else {
928
- console.log(pc.yellow(" Tip: Configure .env manually or 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
1307
  console.log(pc.dim(" Open source template (MIT) by nimbuslab"))
935
- console.log(pc.dim(` Documentation: https://github.com/nimbuslab/create-next-${config.type === "turborepo" ? "turborepo" : config.type}`))
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(" Documentation: https://github.com/nimbuslab-templates"))
1310
+ console.log(pc.dim(" https://github.com/nimbuslab-templates"))
938
1311
  }
939
1312
  console.log()
940
1313
  }