@nimbuslab/cli 0.7.0 → 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 +373 -50
- package/docs/CI-CD.md +11 -2
- package/package.json +1 -1
- package/src/commands/create.ts +432 -59
- package/src/index.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -844,12 +844,133 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
844
844
|
// src/commands/create.ts
|
|
845
845
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
846
846
|
var {$: $2 } = globalThis.Bun;
|
|
847
|
-
import { rm } from "fs/promises";
|
|
847
|
+
import { rm, mkdir } from "fs/promises";
|
|
848
848
|
import { join } from "path";
|
|
849
|
+
var AI_CONFIGS = {
|
|
850
|
+
claude: {
|
|
851
|
+
filename: "CLAUDE.md",
|
|
852
|
+
content: (type) => `# ${type === "landing" ? "Landing Page" : type === "app" ? "Web App" : "Monorepo"}
|
|
853
|
+
|
|
854
|
+
## Stack
|
|
855
|
+
- Next.js 16 (App Router, Turbopack)
|
|
856
|
+
- React 19 (Server Components)
|
|
857
|
+
- TypeScript (strict)
|
|
858
|
+
- Tailwind CSS 4
|
|
859
|
+
- shadcn/ui
|
|
860
|
+
- Bun
|
|
861
|
+
${type === "app" ? `- Better Auth
|
|
862
|
+
- Drizzle + PostgreSQL` : ""}
|
|
863
|
+
|
|
864
|
+
## Commands
|
|
865
|
+
\`\`\`bash
|
|
866
|
+
bun dev # Start development
|
|
867
|
+
bun build # Production build
|
|
868
|
+
bun lint # Run ESLint
|
|
869
|
+
${type === "app" ? "bun setup # Setup database" : ""}
|
|
870
|
+
\`\`\`
|
|
871
|
+
|
|
872
|
+
## Conventions
|
|
873
|
+
- Use \`bun\` for all package operations
|
|
874
|
+
- Server Components by default
|
|
875
|
+
- Dark mode first design
|
|
876
|
+
- Use \`cn()\` for conditional classes
|
|
877
|
+
- Add components: \`bunx --bun shadcn@latest add [component]\`
|
|
878
|
+
`
|
|
879
|
+
},
|
|
880
|
+
cursor: {
|
|
881
|
+
filename: ".cursorrules",
|
|
882
|
+
content: (type) => `# Cursor Rules
|
|
883
|
+
|
|
884
|
+
Stack: Next.js 16, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Bun
|
|
885
|
+
${type === "app" ? "Auth: Better Auth | DB: Drizzle + PostgreSQL" : ""}
|
|
886
|
+
|
|
887
|
+
- Server Components by default
|
|
888
|
+
- "use client" only when needed
|
|
889
|
+
- Tailwind utility classes
|
|
890
|
+
- cn() for conditional classes
|
|
891
|
+
- Dark mode first
|
|
892
|
+
`
|
|
893
|
+
},
|
|
894
|
+
gemini: {
|
|
895
|
+
filename: ".gemini/GEMINI.md",
|
|
896
|
+
content: (type) => `# ${type === "landing" ? "Landing Page" : type === "app" ? "Web App" : "Monorepo"}
|
|
897
|
+
|
|
898
|
+
## Stack
|
|
899
|
+
- Next.js 16 (App Router, Turbopack)
|
|
900
|
+
- React 19 (Server Components)
|
|
901
|
+
- TypeScript (strict)
|
|
902
|
+
- Tailwind CSS 4
|
|
903
|
+
- shadcn/ui
|
|
904
|
+
- Bun
|
|
905
|
+
${type === "app" ? `- Better Auth
|
|
906
|
+
- Drizzle + PostgreSQL` : ""}
|
|
907
|
+
|
|
908
|
+
## Conventions
|
|
909
|
+
- Use \`bun\` for all package operations
|
|
910
|
+
- Server Components by default
|
|
911
|
+
- Dark mode first design
|
|
912
|
+
- Use \`cn()\` for conditional classes
|
|
913
|
+
`
|
|
914
|
+
},
|
|
915
|
+
copilot: {
|
|
916
|
+
filename: ".github/copilot-instructions.md",
|
|
917
|
+
content: (type) => `# GitHub Copilot Instructions
|
|
918
|
+
|
|
919
|
+
Stack: Next.js 16, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Bun
|
|
920
|
+
${type === "app" ? "Auth: Better Auth | DB: Drizzle + PostgreSQL" : ""}
|
|
921
|
+
|
|
922
|
+
## Do
|
|
923
|
+
- Use TypeScript strict mode
|
|
924
|
+
- Prefer Server Components
|
|
925
|
+
- Use Tailwind for styling
|
|
926
|
+
- Use cn() for class merging
|
|
927
|
+
|
|
928
|
+
## Don't
|
|
929
|
+
- Use CSS modules or styled-components
|
|
930
|
+
- Use class components
|
|
931
|
+
- Add unnecessary dependencies
|
|
932
|
+
`
|
|
933
|
+
},
|
|
934
|
+
windsurf: {
|
|
935
|
+
filename: ".windsurfrules",
|
|
936
|
+
content: (type) => `# Windsurf Rules
|
|
937
|
+
|
|
938
|
+
Stack: Next.js 16, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Bun
|
|
939
|
+
${type === "app" ? "Auth: Better Auth | DB: Drizzle + PostgreSQL" : ""}
|
|
940
|
+
|
|
941
|
+
- Server Components by default
|
|
942
|
+
- "use client" only when needed
|
|
943
|
+
- Tailwind utility classes
|
|
944
|
+
- cn() for conditional classes
|
|
945
|
+
- Dark mode first
|
|
946
|
+
`
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
async function checkGitHubCli() {
|
|
950
|
+
const checkCmd = process.platform === "win32" ? "where" : "which";
|
|
951
|
+
try {
|
|
952
|
+
const hasGh = await $2`${checkCmd} gh`.quiet().nothrow();
|
|
953
|
+
if (hasGh.exitCode !== 0) {
|
|
954
|
+
return { installed: false, authenticated: false, username: null, orgs: [] };
|
|
955
|
+
}
|
|
956
|
+
const authStatus = await $2`gh auth status`.quiet().nothrow();
|
|
957
|
+
if (authStatus.exitCode !== 0) {
|
|
958
|
+
return { installed: true, authenticated: false, username: null, orgs: [] };
|
|
959
|
+
}
|
|
960
|
+
const username = (await $2`gh api user --jq '.login'`.quiet().text()).trim();
|
|
961
|
+
const orgsJson = await $2`gh api user/orgs --jq '.[].login'`.quiet().text();
|
|
962
|
+
const orgs = orgsJson.trim().split(`
|
|
963
|
+
`).filter(Boolean);
|
|
964
|
+
return { installed: true, authenticated: true, username, orgs };
|
|
965
|
+
} catch {
|
|
966
|
+
return { installed: false, authenticated: false, username: null, orgs: [] };
|
|
967
|
+
}
|
|
968
|
+
}
|
|
849
969
|
var PRIVATE_TEMPLATES = {
|
|
850
970
|
fast: "nimbuslab-templates/fast-template",
|
|
851
971
|
"fast+": "nimbuslab-templates/fastplus-template",
|
|
852
|
-
"fast+-monorepo": "nimbuslab-templates/fastplus-monorepo-template"
|
|
972
|
+
"fast+-monorepo": "nimbuslab-templates/fastplus-monorepo-template",
|
|
973
|
+
"nimbus-core": "nimbuslab/nimbus-core"
|
|
853
974
|
};
|
|
854
975
|
var PUBLIC_TEMPLATES = {
|
|
855
976
|
landing: "nimbuslab/create-next-landing",
|
|
@@ -877,6 +998,7 @@ function parseFlags(args) {
|
|
|
877
998
|
fast: false,
|
|
878
999
|
fastPlus: false,
|
|
879
1000
|
fastTurborepo: false,
|
|
1001
|
+
core: false,
|
|
880
1002
|
noGit: false,
|
|
881
1003
|
noInstall: false,
|
|
882
1004
|
railway: false,
|
|
@@ -900,6 +1022,8 @@ function parseFlags(args) {
|
|
|
900
1022
|
flags.fastPlus = true;
|
|
901
1023
|
} else if (arg === "--fast-turborepo") {
|
|
902
1024
|
flags.fastTurborepo = true;
|
|
1025
|
+
} else if (arg === "--core") {
|
|
1026
|
+
flags.core = true;
|
|
903
1027
|
} else if (arg === "--no-git") {
|
|
904
1028
|
flags.noGit = true;
|
|
905
1029
|
} else if (arg === "--no-install") {
|
|
@@ -978,22 +1102,9 @@ async function create(args) {
|
|
|
978
1102
|
process.exit(1);
|
|
979
1103
|
}
|
|
980
1104
|
if (!hasGh) {
|
|
981
|
-
console.log(import_picocolors3.default.
|
|
982
|
-
console.log(import_picocolors3.default.dim("Install from: https://cli.github.com"));
|
|
1105
|
+
console.log(import_picocolors3.default.dim(" GitHub CLI not found (repo creation will be skipped)"));
|
|
1106
|
+
console.log(import_picocolors3.default.dim(" Install from: https://cli.github.com"));
|
|
983
1107
|
console.log();
|
|
984
|
-
if (process.platform === "win32") {
|
|
985
|
-
console.log(import_picocolors3.default.cyan("winget install GitHub.cli"));
|
|
986
|
-
} else {
|
|
987
|
-
console.log(import_picocolors3.default.cyan("sudo apt install gh # ou brew install gh"));
|
|
988
|
-
}
|
|
989
|
-
console.log();
|
|
990
|
-
process.exit(1);
|
|
991
|
-
}
|
|
992
|
-
const ghAuth = await $2`gh auth status`.quiet().then(() => true).catch(() => false);
|
|
993
|
-
if (!ghAuth) {
|
|
994
|
-
console.log(import_picocolors3.default.red("Error: GitHub CLI not authenticated."));
|
|
995
|
-
console.log(import_picocolors3.default.dim("Run: gh auth login"));
|
|
996
|
-
process.exit(1);
|
|
997
1108
|
}
|
|
998
1109
|
const hasRailway = await ensureRailwayCli();
|
|
999
1110
|
if (hasRailway) {
|
|
@@ -1007,8 +1118,8 @@ async function create(args) {
|
|
|
1007
1118
|
const { flags, projectName } = parseFlags(args);
|
|
1008
1119
|
Ie(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" New nimbuslab Project ")));
|
|
1009
1120
|
let config;
|
|
1010
|
-
const hasTypeFlag = flags.landing || flags.app || flags.turborepo || flags.fast || flags.fastPlus || flags.fastTurborepo;
|
|
1011
|
-
const typeFromFlag = flags.landing ? "landing" : flags.app ? "app" : flags.turborepo ? "turborepo" : flags.fastTurborepo ? "fast+" : flags.fastPlus ? "fast+" : flags.fast ? "fast" : null;
|
|
1121
|
+
const hasTypeFlag = flags.landing || flags.app || flags.turborepo || flags.fast || flags.fastPlus || flags.fastTurborepo || flags.core;
|
|
1122
|
+
const typeFromFlag = flags.landing ? "landing" : flags.app ? "app" : flags.turborepo ? "turborepo" : flags.fastTurborepo ? "fast+" : flags.fastPlus ? "fast+" : flags.fast ? "fast" : flags.core ? "nimbus-core" : null;
|
|
1012
1123
|
const monorepoFromFlag = flags.fastTurborepo;
|
|
1013
1124
|
if ((flags.yes || hasTypeFlag) && projectName) {
|
|
1014
1125
|
const defaultType = flags.landing || flags.app || flags.turborepo ? "landing" : "fast";
|
|
@@ -1021,6 +1132,8 @@ async function create(args) {
|
|
|
1021
1132
|
github: false,
|
|
1022
1133
|
githubOrg: null,
|
|
1023
1134
|
githubDescription: "",
|
|
1135
|
+
theme: "dark",
|
|
1136
|
+
aiAssistant: null,
|
|
1024
1137
|
contractNumber: "",
|
|
1025
1138
|
resendApiKey: "",
|
|
1026
1139
|
resendFromEmail: "",
|
|
@@ -1107,7 +1220,7 @@ async function promptConfig(initialName, flags) {
|
|
|
1107
1220
|
{
|
|
1108
1221
|
value: "app",
|
|
1109
1222
|
label: "Web App",
|
|
1110
|
-
hint: "Landing + Better Auth +
|
|
1223
|
+
hint: "Landing + Better Auth + Drizzle"
|
|
1111
1224
|
},
|
|
1112
1225
|
{
|
|
1113
1226
|
value: "turborepo",
|
|
@@ -1125,6 +1238,11 @@ async function promptConfig(initialName, flags) {
|
|
|
1125
1238
|
value: "fast+",
|
|
1126
1239
|
label: "fast+",
|
|
1127
1240
|
hint: "Complete SaaS"
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
value: "nimbus-core",
|
|
1244
|
+
label: "nimbus-core",
|
|
1245
|
+
hint: "External projects (stealth mode)"
|
|
1128
1246
|
}
|
|
1129
1247
|
] : [];
|
|
1130
1248
|
const type = await ve({
|
|
@@ -1138,6 +1256,44 @@ async function promptConfig(initialName, flags) {
|
|
|
1138
1256
|
console.log(import_picocolors3.default.red("Error: Template available only for nimbuslab members"));
|
|
1139
1257
|
process.exit(1);
|
|
1140
1258
|
}
|
|
1259
|
+
if (type === "nimbus-core") {
|
|
1260
|
+
console.log();
|
|
1261
|
+
console.log(import_picocolors3.default.dim(" nimbus-core: Motor para projetos externos (stealth mode)"));
|
|
1262
|
+
console.log();
|
|
1263
|
+
const createGithub = await ye({
|
|
1264
|
+
message: "Create GitHub repository? (nimbuslab, private)",
|
|
1265
|
+
initialValue: true
|
|
1266
|
+
});
|
|
1267
|
+
if (pD(createGithub))
|
|
1268
|
+
return createGithub;
|
|
1269
|
+
const clientRepo = await he({
|
|
1270
|
+
message: "Client repo URL (optional, to clone in workspace):",
|
|
1271
|
+
placeholder: "git@github.com:client/repo.git"
|
|
1272
|
+
});
|
|
1273
|
+
if (pD(clientRepo))
|
|
1274
|
+
return clientRepo;
|
|
1275
|
+
return {
|
|
1276
|
+
name,
|
|
1277
|
+
type: "nimbus-core",
|
|
1278
|
+
monorepo: false,
|
|
1279
|
+
git: true,
|
|
1280
|
+
install: false,
|
|
1281
|
+
github: createGithub,
|
|
1282
|
+
githubOrg: "nimbuslab",
|
|
1283
|
+
githubDescription: `nimbus-core for ${name} - external project`,
|
|
1284
|
+
theme: "dark",
|
|
1285
|
+
aiAssistant: null,
|
|
1286
|
+
contractNumber: "",
|
|
1287
|
+
resendApiKey: "",
|
|
1288
|
+
resendFromEmail: "",
|
|
1289
|
+
contactEmail: "",
|
|
1290
|
+
railwayProject: "",
|
|
1291
|
+
railwayToken: "",
|
|
1292
|
+
stagingUrl: "",
|
|
1293
|
+
productionUrl: "",
|
|
1294
|
+
customTemplate: clientRepo || null
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1141
1297
|
let monorepo = false;
|
|
1142
1298
|
if (type === "fast+") {
|
|
1143
1299
|
const useMonorepo = await ye({
|
|
@@ -1200,6 +1356,66 @@ async function promptConfig(initialName, flags) {
|
|
|
1200
1356
|
contractNumber = contract;
|
|
1201
1357
|
}
|
|
1202
1358
|
if (isPublicTemplate) {
|
|
1359
|
+
const theme = await ve({
|
|
1360
|
+
message: "Default theme:",
|
|
1361
|
+
options: [
|
|
1362
|
+
{ value: "dark", label: "Dark", hint: "recommended" },
|
|
1363
|
+
{ value: "light", label: "Light" },
|
|
1364
|
+
{ value: "system", label: "System", hint: "follows OS preference" }
|
|
1365
|
+
]
|
|
1366
|
+
});
|
|
1367
|
+
if (pD(theme))
|
|
1368
|
+
return theme;
|
|
1369
|
+
const aiAssistant = await ve({
|
|
1370
|
+
message: "Which AI assistant do you use?",
|
|
1371
|
+
options: [
|
|
1372
|
+
{ value: "claude", label: "Claude Code", hint: "Anthropic" },
|
|
1373
|
+
{ value: "cursor", label: "Cursor", hint: "AI-first editor" },
|
|
1374
|
+
{ value: "gemini", label: "Gemini CLI", hint: "Google" },
|
|
1375
|
+
{ value: "copilot", label: "GitHub Copilot" },
|
|
1376
|
+
{ value: "windsurf", label: "Windsurf", hint: "Codeium" },
|
|
1377
|
+
{ value: "none", label: "None", hint: "skip AI config" }
|
|
1378
|
+
]
|
|
1379
|
+
});
|
|
1380
|
+
if (pD(aiAssistant))
|
|
1381
|
+
return aiAssistant;
|
|
1382
|
+
let publicGithub = false;
|
|
1383
|
+
let publicGithubOrg = null;
|
|
1384
|
+
if (git) {
|
|
1385
|
+
const gh = await checkGitHubCli();
|
|
1386
|
+
if (gh.installed && gh.authenticated) {
|
|
1387
|
+
const createRepo = await ye({
|
|
1388
|
+
message: "Create GitHub repository?",
|
|
1389
|
+
initialValue: false
|
|
1390
|
+
});
|
|
1391
|
+
if (pD(createRepo))
|
|
1392
|
+
return createRepo;
|
|
1393
|
+
publicGithub = createRepo;
|
|
1394
|
+
if (publicGithub) {
|
|
1395
|
+
const repoOptions = [
|
|
1396
|
+
{ value: gh.username, label: gh.username, hint: "personal account" },
|
|
1397
|
+
...gh.orgs.map((org) => ({ value: org, label: org }))
|
|
1398
|
+
];
|
|
1399
|
+
const repoOwner = await ve({
|
|
1400
|
+
message: "Where to create the repository?",
|
|
1401
|
+
options: repoOptions
|
|
1402
|
+
});
|
|
1403
|
+
if (pD(repoOwner))
|
|
1404
|
+
return repoOwner;
|
|
1405
|
+
publicGithubOrg = repoOwner;
|
|
1406
|
+
const repoVisibility = await ve({
|
|
1407
|
+
message: "Repository visibility:",
|
|
1408
|
+
options: [
|
|
1409
|
+
{ value: "private", label: "Private", hint: "recommended" },
|
|
1410
|
+
{ value: "public", label: "Public" }
|
|
1411
|
+
]
|
|
1412
|
+
});
|
|
1413
|
+
if (pD(repoVisibility))
|
|
1414
|
+
return repoVisibility;
|
|
1415
|
+
githubDescription = repoVisibility;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1203
1419
|
const install2 = await ye({
|
|
1204
1420
|
message: "Install dependencies?",
|
|
1205
1421
|
initialValue: true
|
|
@@ -1212,9 +1428,11 @@ async function promptConfig(initialName, flags) {
|
|
|
1212
1428
|
monorepo: false,
|
|
1213
1429
|
git,
|
|
1214
1430
|
install: install2,
|
|
1215
|
-
github,
|
|
1216
|
-
githubOrg,
|
|
1431
|
+
github: publicGithub,
|
|
1432
|
+
githubOrg: publicGithubOrg,
|
|
1217
1433
|
githubDescription,
|
|
1434
|
+
theme,
|
|
1435
|
+
aiAssistant: aiAssistant === "none" ? null : aiAssistant,
|
|
1218
1436
|
contractNumber: "",
|
|
1219
1437
|
resendApiKey: "",
|
|
1220
1438
|
resendFromEmail: "",
|
|
@@ -1362,6 +1580,8 @@ async function promptConfig(initialName, flags) {
|
|
|
1362
1580
|
github,
|
|
1363
1581
|
githubOrg,
|
|
1364
1582
|
githubDescription,
|
|
1583
|
+
theme: "dark",
|
|
1584
|
+
aiAssistant: null,
|
|
1365
1585
|
contractNumber,
|
|
1366
1586
|
resendApiKey,
|
|
1367
1587
|
resendFromEmail,
|
|
@@ -1400,15 +1620,59 @@ async function createProject(config) {
|
|
|
1400
1620
|
s.stop("Error cloning template");
|
|
1401
1621
|
throw new Error(`Failed to clone template ${templateRepo}. Check your connection or repository access.`);
|
|
1402
1622
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1623
|
+
if (config.type === "nimbus-core" && config.customTemplate) {
|
|
1624
|
+
const clientRepoUrl = config.customTemplate;
|
|
1625
|
+
s.start(`Cloning client repo in workspace...`);
|
|
1626
|
+
try {
|
|
1627
|
+
const projectName = clientRepoUrl.split("/").pop()?.replace(".git", "") || "client-project";
|
|
1628
|
+
await $2`git clone ${clientRepoUrl} ${config.name}/workspace/${projectName}`.quiet();
|
|
1629
|
+
s.stop(`Client repo cloned: workspace/${projectName}`);
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
s.stop("Error cloning client repo");
|
|
1632
|
+
console.log(import_picocolors3.default.dim(" You can clone manually: cd workspace && git clone <url>"));
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
if (config.type !== "nimbus-core") {
|
|
1636
|
+
s.start("Configuring project...");
|
|
1637
|
+
try {
|
|
1638
|
+
const pkgPath = `${config.name}/package.json`;
|
|
1639
|
+
const pkg = await Bun.file(pkgPath).json();
|
|
1640
|
+
pkg.name = config.name;
|
|
1641
|
+
await Bun.write(pkgPath, JSON.stringify(pkg, null, 2));
|
|
1642
|
+
s.stop("Project configured");
|
|
1643
|
+
} catch (error) {
|
|
1644
|
+
s.stop("Error configuring");
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
if (isPublicTemplate && config.theme) {
|
|
1648
|
+
s.start(`Setting theme to ${config.theme}...`);
|
|
1649
|
+
try {
|
|
1650
|
+
const layoutPath = `${config.name}/src/app/layout.tsx`;
|
|
1651
|
+
let layout = await Bun.file(layoutPath).text();
|
|
1652
|
+
layout = layout.replace(/defaultTheme="(dark|light|system)"/, `defaultTheme="${config.theme}"`);
|
|
1653
|
+
await Bun.write(layoutPath, layout);
|
|
1654
|
+
s.stop(`Theme set to ${config.theme}`);
|
|
1655
|
+
} catch {
|
|
1656
|
+
s.stop("Theme config skipped");
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
if (isPublicTemplate && config.aiAssistant) {
|
|
1660
|
+
const aiConfig = AI_CONFIGS[config.aiAssistant];
|
|
1661
|
+
if (aiConfig) {
|
|
1662
|
+
s.start(`Generating ${config.aiAssistant} config...`);
|
|
1663
|
+
try {
|
|
1664
|
+
const content = aiConfig.content(config.type);
|
|
1665
|
+
const filePath = `${config.name}/${aiConfig.filename}`;
|
|
1666
|
+
if (aiConfig.filename.includes("/")) {
|
|
1667
|
+
const dir = aiConfig.filename.split("/").slice(0, -1).join("/");
|
|
1668
|
+
await mkdir(`${config.name}/${dir}`, { recursive: true });
|
|
1669
|
+
}
|
|
1670
|
+
await Bun.write(filePath, content);
|
|
1671
|
+
s.stop(`${aiConfig.filename} created`);
|
|
1672
|
+
} catch {
|
|
1673
|
+
s.stop("AI config skipped");
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1412
1676
|
}
|
|
1413
1677
|
if (config.type === "fast+") {
|
|
1414
1678
|
s.start("Configurando fast+ (SaaS)...");
|
|
@@ -1432,15 +1696,26 @@ async function createProject(config) {
|
|
|
1432
1696
|
try {
|
|
1433
1697
|
const cwd = config.name;
|
|
1434
1698
|
const repoName = config.githubOrg ? `${config.githubOrg}/${config.name}` : config.name;
|
|
1435
|
-
|
|
1436
|
-
|
|
1699
|
+
let visibility;
|
|
1700
|
+
if (config.type === "nimbus-core") {
|
|
1701
|
+
visibility = "--private";
|
|
1702
|
+
} else if (isPublicTemplate) {
|
|
1703
|
+
visibility = config.githubDescription === "public" ? "--public" : "--private";
|
|
1704
|
+
} else {
|
|
1705
|
+
visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public";
|
|
1706
|
+
}
|
|
1707
|
+
if (isPublicTemplate) {
|
|
1708
|
+
await $2`gh repo create ${repoName} ${visibility} --source . --remote origin`.cwd(cwd).quiet();
|
|
1709
|
+
} else {
|
|
1710
|
+
await $2`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet();
|
|
1711
|
+
}
|
|
1437
1712
|
await $2`git checkout main`.cwd(cwd).quiet();
|
|
1438
1713
|
await $2`git push -u origin main`.cwd(cwd).quiet();
|
|
1439
1714
|
await $2`git checkout staging`.cwd(cwd).quiet();
|
|
1440
1715
|
await $2`git push -u origin staging`.cwd(cwd).quiet();
|
|
1441
1716
|
await $2`git checkout develop`.cwd(cwd).quiet();
|
|
1442
1717
|
await $2`git push -u origin develop`.cwd(cwd).quiet();
|
|
1443
|
-
s.stop(`GitHub: ${repoName}
|
|
1718
|
+
s.stop(`GitHub: ${repoName}`);
|
|
1444
1719
|
} catch (error) {
|
|
1445
1720
|
s.stop("Error creating GitHub repository");
|
|
1446
1721
|
console.log(import_picocolors3.default.dim(" You can create manually with: gh repo create"));
|
|
@@ -1518,56 +1793,104 @@ function generateEnvFile(config) {
|
|
|
1518
1793
|
}
|
|
1519
1794
|
function showNextSteps(config) {
|
|
1520
1795
|
const isPublicTemplate = ["landing", "app", "turborepo"].includes(config.type);
|
|
1796
|
+
const needsSetup = config.type === "app";
|
|
1521
1797
|
console.log();
|
|
1522
1798
|
console.log(import_picocolors3.default.bold("Next steps:"));
|
|
1523
1799
|
console.log();
|
|
1524
1800
|
console.log(` ${import_picocolors3.default.cyan("cd")} ${config.name}`);
|
|
1801
|
+
if (config.type === "nimbus-core") {
|
|
1802
|
+
console.log();
|
|
1803
|
+
console.log(import_picocolors3.default.dim(" nimbus-core: Motor para projetos externos"));
|
|
1804
|
+
console.log();
|
|
1805
|
+
console.log(import_picocolors3.default.dim(" Para clonar repo do cliente:"));
|
|
1806
|
+
console.log(` ${import_picocolors3.default.cyan("cd")} workspace`);
|
|
1807
|
+
console.log(` ${import_picocolors3.default.cyan("git clone")} <repo-do-cliente>`);
|
|
1808
|
+
console.log();
|
|
1809
|
+
console.log(import_picocolors3.default.dim(" Para usar a Lola:"));
|
|
1810
|
+
console.log(` ${import_picocolors3.default.cyan("gemini")} ${import_picocolors3.default.dim("# Gemini CLI")}`);
|
|
1811
|
+
console.log(` ${import_picocolors3.default.cyan("claude --append-system-prompt-file .claude/agents/lola.md")}`);
|
|
1812
|
+
console.log();
|
|
1813
|
+
console.log(import_picocolors3.default.yellow(" STEALTH MODE: Commits sem mencao a nimbuslab/Lola/IA"));
|
|
1814
|
+
console.log();
|
|
1815
|
+
if (config.github) {
|
|
1816
|
+
const repoUrl = `https://github.com/nimbuslab/${config.name}`;
|
|
1817
|
+
console.log(import_picocolors3.default.green(` GitHub (private): ${repoUrl}`));
|
|
1818
|
+
console.log();
|
|
1819
|
+
}
|
|
1820
|
+
console.log(import_picocolors3.default.dim(" Docs: See README.md for full instructions"));
|
|
1821
|
+
console.log();
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1525
1824
|
if (!config.install) {
|
|
1526
1825
|
console.log(` ${import_picocolors3.default.cyan("bun")} install`);
|
|
1527
1826
|
}
|
|
1528
|
-
if (!isPublicTemplate) {
|
|
1827
|
+
if (!isPublicTemplate || needsSetup) {
|
|
1529
1828
|
console.log(` ${import_picocolors3.default.cyan("bun")} setup`);
|
|
1530
1829
|
}
|
|
1531
1830
|
console.log(` ${import_picocolors3.default.cyan("bun")} dev`);
|
|
1532
1831
|
console.log();
|
|
1533
|
-
if (
|
|
1534
|
-
console.log(import_picocolors3.default.dim("
|
|
1832
|
+
if (needsSetup && isPublicTemplate) {
|
|
1833
|
+
console.log(import_picocolors3.default.dim(" bun setup will:"));
|
|
1834
|
+
console.log(import_picocolors3.default.dim(" - Start PostgreSQL with Docker"));
|
|
1835
|
+
console.log(import_picocolors3.default.dim(" - Run database migrations"));
|
|
1836
|
+
console.log(import_picocolors3.default.dim(" - Create demo user (demo@example.com / demo1234)"));
|
|
1535
1837
|
console.log();
|
|
1838
|
+
}
|
|
1839
|
+
if (config.git) {
|
|
1840
|
+
console.log(import_picocolors3.default.dim(" Git: main -> staging -> develop (current branch)"));
|
|
1536
1841
|
if (config.github) {
|
|
1537
1842
|
const repoUrl = config.githubOrg ? `https://github.com/${config.githubOrg}/${config.name}` : `https://github.com/${config.name}`;
|
|
1538
1843
|
console.log(import_picocolors3.default.green(` GitHub: ${repoUrl}`));
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1844
|
+
}
|
|
1845
|
+
console.log();
|
|
1846
|
+
}
|
|
1847
|
+
if (isPublicTemplate) {
|
|
1848
|
+
if (config.theme !== "dark") {
|
|
1849
|
+
console.log(import_picocolors3.default.dim(` Theme: ${config.theme}`));
|
|
1850
|
+
}
|
|
1851
|
+
if (config.aiAssistant) {
|
|
1852
|
+
const aiConfig = AI_CONFIGS[config.aiAssistant];
|
|
1853
|
+
if (aiConfig) {
|
|
1854
|
+
console.log(import_picocolors3.default.dim(` AI config: ${aiConfig.filename}`));
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
if (config.theme !== "dark" || config.aiAssistant) {
|
|
1542
1858
|
console.log();
|
|
1543
1859
|
}
|
|
1544
1860
|
}
|
|
1545
1861
|
if (config.type === "fast+") {
|
|
1546
|
-
console.log(import_picocolors3.default.dim("
|
|
1862
|
+
console.log(import_picocolors3.default.dim(" bun setup will:"));
|
|
1863
|
+
console.log(import_picocolors3.default.dim(" - Start PostgreSQL with Docker"));
|
|
1864
|
+
console.log(import_picocolors3.default.dim(" - Run database migrations"));
|
|
1865
|
+
console.log(import_picocolors3.default.dim(" - Create demo user (demo@example.com / demo1234)"));
|
|
1866
|
+
console.log();
|
|
1867
|
+
console.log(import_picocolors3.default.dim(" Tip: Configure DATABASE_URL and BETTER_AUTH_SECRET in .env"));
|
|
1547
1868
|
if (!config.railwayToken) {
|
|
1548
1869
|
console.log(import_picocolors3.default.dim(" Railway: Create a project at https://railway.app/new"));
|
|
1549
1870
|
}
|
|
1550
1871
|
console.log();
|
|
1551
1872
|
}
|
|
1552
|
-
if (
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1873
|
+
if (!isPublicTemplate) {
|
|
1874
|
+
if (config.resendApiKey || config.stagingUrl) {
|
|
1875
|
+
console.log(import_picocolors3.default.green(" .env configured!"));
|
|
1876
|
+
console.log();
|
|
1877
|
+
} else {
|
|
1878
|
+
console.log(import_picocolors3.default.yellow(" Tip: Configure .env manually or use 'bun setup'."));
|
|
1879
|
+
console.log();
|
|
1880
|
+
}
|
|
1558
1881
|
}
|
|
1559
1882
|
if (isPublicTemplate) {
|
|
1560
1883
|
console.log(import_picocolors3.default.dim(" Open source template (MIT) by nimbuslab"));
|
|
1561
|
-
console.log(import_picocolors3.default.dim(`
|
|
1884
|
+
console.log(import_picocolors3.default.dim(` https://github.com/nimbuslab/create-next-${config.type === "turborepo" ? "turborepo" : config.type}`));
|
|
1562
1885
|
} else {
|
|
1563
|
-
console.log(import_picocolors3.default.dim("
|
|
1886
|
+
console.log(import_picocolors3.default.dim(" https://github.com/nimbuslab-templates"));
|
|
1564
1887
|
}
|
|
1565
1888
|
console.log();
|
|
1566
1889
|
}
|
|
1567
1890
|
|
|
1568
1891
|
// src/index.ts
|
|
1569
1892
|
var PACKAGE_NAME = "@nimbuslab/cli";
|
|
1570
|
-
var CURRENT_VERSION = "0.
|
|
1893
|
+
var CURRENT_VERSION = "0.8.0";
|
|
1571
1894
|
var LOGO = `
|
|
1572
1895
|
\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
1573
1896
|
\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
|
package/docs/CI-CD.md
CHANGED
|
@@ -161,12 +161,21 @@ O npm suporta autenticacao via OIDC, eliminando necessidade de tokens.
|
|
|
161
161
|
|
|
162
162
|
### 2026-01-26: Publicacao falhando silenciosamente
|
|
163
163
|
|
|
164
|
-
**Problema**: CI mostrava sucesso mas versao nao aparecia no npm.
|
|
164
|
+
**Problema**: CI mostrava sucesso (`+ @nimbuslab/cli@0.6.x`) mas versao nao aparecia no npm. Tarball retornava 404 com mensagem "Access token expired or revoked".
|
|
165
165
|
|
|
166
|
-
**Causa**: OIDC Trusted Publishing falhando silenciosamente
|
|
166
|
+
**Causa**: OIDC Trusted Publishing falhando silenciosamente. Os tarballs eram criados mas ficavam inacessiveis.
|
|
167
167
|
|
|
168
168
|
**Solucao**: Adicionar `NODE_AUTH_TOKEN` como fallback junto com OIDC.
|
|
169
169
|
|
|
170
|
+
```yaml
|
|
171
|
+
- name: Publish to npm
|
|
172
|
+
run: npm publish --access public
|
|
173
|
+
env:
|
|
174
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Importante**: Mesmo com OIDC configurado, SEMPRE manter o `NODE_AUTH_TOKEN` como fallback. O token deve ser do tipo "Granular Access Token" (nao "Classic", que foi deprecado em dez/2025).
|
|
178
|
+
|
|
170
179
|
---
|
|
171
180
|
|
|
172
181
|
*Ultima atualizacao: 2026-01-26*
|
package/package.json
CHANGED
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
|
}
|
package/src/index.ts
CHANGED