@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.
- package/dist/index.js +473 -150
- package/docs/CI-CD.md +181 -0
- package/package.json +1 -1
- package/src/commands/create.ts +513 -140
- package/src/index.ts +20 -20
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") {
|
|
@@ -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
|
|
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
|
|
292
|
+
console.log(pc.green("Railway CLI installed successfully!"))
|
|
156
293
|
return true
|
|
157
294
|
} catch (error) {
|
|
158
|
-
console.log(pc.red("
|
|
159
|
-
console.log(pc.dim("
|
|
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("
|
|
196
|
-
console.log(pc.dim("
|
|
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("
|
|
209
|
-
console.log(pc.dim("
|
|
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.
|
|
215
|
-
console.log(pc.dim("
|
|
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
|
|
240
|
-
console.log(pc.dim("
|
|
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("
|
|
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(`
|
|
288
|
-
console.log(pc.dim(`
|
|
289
|
-
console.log(pc.dim(` Git: ${config.git ? "
|
|
290
|
-
console.log(pc.dim(`
|
|
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(`
|
|
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:
|
|
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("
|
|
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("
|
|
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 ? `
|
|
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: "
|
|
476
|
+
message: "Project name:",
|
|
350
477
|
placeholder: "meu-projeto",
|
|
351
478
|
initialValue: initialName,
|
|
352
479
|
validate: (value) => {
|
|
353
|
-
if (!value) return "
|
|
480
|
+
if (!value) return "Name is required"
|
|
354
481
|
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
355
|
-
return "Use
|
|
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 +
|
|
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
|
|
514
|
+
hint: "Landing page fast methodology",
|
|
388
515
|
},
|
|
389
516
|
{
|
|
390
517
|
value: "fast+",
|
|
391
518
|
label: "fast+",
|
|
392
|
-
hint: "SaaS
|
|
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: "
|
|
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("
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
623
|
+
message: "GitHub organization:",
|
|
449
624
|
options: [
|
|
450
|
-
{ value: "nimbuslab", label: "nimbuslab", hint: "
|
|
451
|
-
{ value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
651
|
+
message: "Contract number (ex: 001):",
|
|
477
652
|
placeholder: "001",
|
|
478
|
-
validate: (v) => v ? undefined : "
|
|
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: "
|
|
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: "
|
|
780
|
+
message: "What do you want to configure now?",
|
|
537
781
|
options: [
|
|
538
|
-
{ value: "urls", label: "URLs", hint: "staging
|
|
539
|
-
{ value: "resend", label: "Resend", hint: "emails
|
|
540
|
-
{ value: "railway", label: "Railway", hint: "deploy
|
|
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
|
|
796
|
+
console.log(pc.dim(" Project URLs"))
|
|
553
797
|
|
|
554
798
|
const staging = await p.text({
|
|
555
|
-
message: "URL
|
|
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
|
|
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: "
|
|
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: "
|
|
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(`
|
|
860
|
+
console.log(pc.green(` Project: ${fastProject} (automatico)`))
|
|
617
861
|
} else {
|
|
618
|
-
console.log(pc.yellow("
|
|
619
|
-
console.log(pc.dim(" Configure
|
|
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: "
|
|
626
|
-
{ value: "__skip__", label: "
|
|
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: "
|
|
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(`
|
|
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}"
|
|
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("
|
|
649
|
-
console.log(pc.dim("
|
|
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(`
|
|
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:
|
|
659
|
-
console.log(pc.dim(" Configure
|
|
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: "
|
|
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(`
|
|
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
|
|
968
|
+
s.stop(`Template cloned`)
|
|
723
969
|
} catch (error) {
|
|
724
|
-
s.stop("
|
|
725
|
-
throw new Error(`
|
|
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
|
-
//
|
|
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
|
|
@@ -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("
|
|
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
|
|
1064
|
+
s.stop("Git initialized (main -> staging -> develop)")
|
|
763
1065
|
} catch (error) {
|
|
764
|
-
s.stop("
|
|
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("
|
|
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
|
-
//
|
|
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,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}
|
|
1105
|
+
s.stop(`GitHub: ${repoName}`)
|
|
791
1106
|
} catch (error) {
|
|
792
|
-
s.stop("
|
|
793
|
-
console.log(pc.dim("
|
|
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("
|
|
806
|
-
console.log(pc.dim("
|
|
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("
|
|
1133
|
+
s.stop("Error creating .env")
|
|
819
1134
|
}
|
|
820
1135
|
}
|
|
821
1136
|
|
|
822
1137
|
// Install deps
|
|
823
1138
|
if (config.install) {
|
|
824
|
-
s.start("
|
|
1139
|
+
s.start("Installing dependencies (pode demorar)...")
|
|
825
1140
|
try {
|
|
826
1141
|
await $`bun install`.cwd(config.name).quiet()
|
|
827
|
-
s.stop("
|
|
1142
|
+
s.stop("Dependencies installed")
|
|
828
1143
|
} catch (error) {
|
|
829
|
-
s.stop("
|
|
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(`#
|
|
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("
|
|
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
|
-
console.log(pc.dim(" Railway:
|
|
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
|
-
console.log(pc.dim("
|
|
935
|
-
console.log(pc.dim(`
|
|
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("
|
|
1310
|
+
console.log(pc.dim(" https://github.com/nimbuslab-templates"))
|
|
938
1311
|
}
|
|
939
1312
|
console.log()
|
|
940
1313
|
}
|