@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.
- package/MIGRATION-ROADMAP.md +201 -0
- package/dist/index.js +801 -69
- package/docs/CI-CD.md +11 -2
- package/package.json +1 -1
- package/src/commands/analyze.ts +210 -0
- package/src/commands/create.ts +432 -59
- package/src/commands/upgrade.ts +251 -0
- package/src/index.ts +19 -3
package/src/commands/create.ts
CHANGED
|
@@ -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.
|
|
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 +
|
|
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
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
//
|
|
777
|
-
|
|
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
|
-
//
|
|
780
|
-
|
|
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}
|
|
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
|
|
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
|
|
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
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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("
|
|
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 (
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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
|
|
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(`
|
|
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("
|
|
1310
|
+
console.log(pc.dim(" https://github.com/nimbuslab-templates"))
|
|
938
1311
|
}
|
|
939
1312
|
console.log()
|
|
940
1313
|
}
|