@nimbuslab/cli 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION-ROADMAP.md +201 -0
- package/dist/index.js +801 -69
- package/docs/CI-CD.md +11 -2
- package/package.json +1 -1
- package/src/commands/analyze.ts +210 -0
- package/src/commands/create.ts +432 -59
- package/src/commands/upgrade.ts +251 -0
- package/src/index.ts +19 -3
package/dist/index.js
CHANGED
|
@@ -147,7 +147,7 @@ var require_src = __commonJS((exports, module) => {
|
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
// src/index.ts
|
|
150
|
-
var
|
|
150
|
+
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
151
151
|
|
|
152
152
|
// node_modules/@clack/core/dist/index.mjs
|
|
153
153
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
@@ -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"));
|
|
983
|
-
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
|
-
}
|
|
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"));
|
|
989
1107
|
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,499 @@ 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
|
|
|
1891
|
+
// src/commands/analyze.ts
|
|
1892
|
+
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
1893
|
+
import { existsSync, readFileSync } from "fs";
|
|
1894
|
+
import { join as join2 } from "path";
|
|
1895
|
+
function detectPackageManager(dir) {
|
|
1896
|
+
if (existsSync(join2(dir, "bun.lockb")))
|
|
1897
|
+
return "bun";
|
|
1898
|
+
if (existsSync(join2(dir, "pnpm-lock.yaml")))
|
|
1899
|
+
return "pnpm";
|
|
1900
|
+
if (existsSync(join2(dir, "yarn.lock")))
|
|
1901
|
+
return "yarn";
|
|
1902
|
+
if (existsSync(join2(dir, "package-lock.json")))
|
|
1903
|
+
return "npm";
|
|
1904
|
+
return "unknown";
|
|
1905
|
+
}
|
|
1906
|
+
function detectMonorepo(dir, pkg) {
|
|
1907
|
+
if (existsSync(join2(dir, "turbo.json")))
|
|
1908
|
+
return "turborepo";
|
|
1909
|
+
if (existsSync(join2(dir, "nx.json")))
|
|
1910
|
+
return "nx";
|
|
1911
|
+
if (existsSync(join2(dir, "lerna.json")))
|
|
1912
|
+
return "lerna";
|
|
1913
|
+
if (pkg.workspaces)
|
|
1914
|
+
return "workspaces";
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
function detectFramework(deps) {
|
|
1918
|
+
if (deps["next"])
|
|
1919
|
+
return { name: "nextjs", version: deps["next"] };
|
|
1920
|
+
if (deps["@angular/core"])
|
|
1921
|
+
return { name: "angular", version: deps["@angular/core"] };
|
|
1922
|
+
if (deps["vue"])
|
|
1923
|
+
return { name: "vue", version: deps["vue"] };
|
|
1924
|
+
if (deps["svelte"])
|
|
1925
|
+
return { name: "svelte", version: deps["svelte"] };
|
|
1926
|
+
if (deps["react"] && !deps["next"])
|
|
1927
|
+
return { name: "react", version: deps["react"] };
|
|
1928
|
+
return { name: null, version: null };
|
|
1929
|
+
}
|
|
1930
|
+
function detectStyling(deps, devDeps) {
|
|
1931
|
+
const styling = [];
|
|
1932
|
+
const allDeps = { ...deps, ...devDeps };
|
|
1933
|
+
if (allDeps["tailwindcss"])
|
|
1934
|
+
styling.push(`tailwind@${allDeps["tailwindcss"]}`);
|
|
1935
|
+
if (allDeps["styled-components"])
|
|
1936
|
+
styling.push("styled-components");
|
|
1937
|
+
if (allDeps["@emotion/react"])
|
|
1938
|
+
styling.push("emotion");
|
|
1939
|
+
if (allDeps["sass"])
|
|
1940
|
+
styling.push("sass");
|
|
1941
|
+
if (allDeps["less"])
|
|
1942
|
+
styling.push("less");
|
|
1943
|
+
return styling.length > 0 ? styling : ["css"];
|
|
1944
|
+
}
|
|
1945
|
+
function detectAuth(deps) {
|
|
1946
|
+
if (deps["better-auth"])
|
|
1947
|
+
return "better-auth";
|
|
1948
|
+
if (deps["next-auth"])
|
|
1949
|
+
return "next-auth";
|
|
1950
|
+
if (deps["@clerk/nextjs"])
|
|
1951
|
+
return "clerk";
|
|
1952
|
+
if (deps["@auth0/nextjs-auth0"])
|
|
1953
|
+
return "auth0";
|
|
1954
|
+
if (deps["@supabase/supabase-js"])
|
|
1955
|
+
return "supabase";
|
|
1956
|
+
return null;
|
|
1957
|
+
}
|
|
1958
|
+
function detectDatabase(deps) {
|
|
1959
|
+
if (deps["drizzle-orm"])
|
|
1960
|
+
return "drizzle";
|
|
1961
|
+
if (deps["@prisma/client"])
|
|
1962
|
+
return "prisma";
|
|
1963
|
+
if (deps["typeorm"])
|
|
1964
|
+
return "typeorm";
|
|
1965
|
+
if (deps["mongoose"])
|
|
1966
|
+
return "mongoose";
|
|
1967
|
+
if (deps["pg"])
|
|
1968
|
+
return "pg";
|
|
1969
|
+
if (deps["mysql2"])
|
|
1970
|
+
return "mysql";
|
|
1971
|
+
return null;
|
|
1972
|
+
}
|
|
1973
|
+
function generateRecommendations(result) {
|
|
1974
|
+
const recs = [];
|
|
1975
|
+
if (result.packageManager !== "bun") {
|
|
1976
|
+
recs.push(`Migrar ${result.packageManager} -> bun (nimbus codemod bun)`);
|
|
1977
|
+
}
|
|
1978
|
+
if (result.framework === "nextjs" && result.frameworkVersion) {
|
|
1979
|
+
const majorVersion = parseInt(result.frameworkVersion.replace(/[^0-9]/g, "").slice(0, 2));
|
|
1980
|
+
if (majorVersion < 16) {
|
|
1981
|
+
recs.push(`Atualizar Next.js ${result.frameworkVersion} -> 16 (nimbus upgrade next)`);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
const tailwind = result.styling.find((s) => s.startsWith("tailwind"));
|
|
1985
|
+
if (tailwind) {
|
|
1986
|
+
const version = tailwind.split("@")[1] || "";
|
|
1987
|
+
if (version.startsWith("3")) {
|
|
1988
|
+
recs.push(`Atualizar Tailwind 3 -> 4 (nimbus upgrade tailwind)`);
|
|
1989
|
+
}
|
|
1990
|
+
} else if (!result.styling.includes("tailwind")) {
|
|
1991
|
+
recs.push(`Considerar adicionar Tailwind CSS (nimbus add tailwind)`);
|
|
1992
|
+
}
|
|
1993
|
+
if (result.dependencies["react"]) {
|
|
1994
|
+
const reactVersion = result.dependencies["react"];
|
|
1995
|
+
if (reactVersion.startsWith("18") || reactVersion.startsWith("^18")) {
|
|
1996
|
+
recs.push(`Atualizar React 18 -> 19 (nimbus upgrade react)`);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
if (result.database === "prisma") {
|
|
2000
|
+
recs.push(`Considerar migrar Prisma -> Drizzle (nimbus codemod drizzle)`);
|
|
2001
|
+
} else if (!result.database && result.framework === "nextjs") {
|
|
2002
|
+
recs.push(`Considerar adicionar banco de dados (nimbus add db)`);
|
|
2003
|
+
}
|
|
2004
|
+
if (!result.auth && result.framework === "nextjs") {
|
|
2005
|
+
recs.push(`Considerar adicionar autenticacao (nimbus add auth)`);
|
|
2006
|
+
} else if (result.auth === "next-auth") {
|
|
2007
|
+
recs.push(`Considerar migrar NextAuth -> Better Auth`);
|
|
2008
|
+
}
|
|
2009
|
+
if (result.monorepo === "workspaces" && !result.monorepo) {
|
|
2010
|
+
recs.push(`Considerar usar Turborepo para monorepo (nimbus add monorepo)`);
|
|
2011
|
+
}
|
|
2012
|
+
return recs;
|
|
2013
|
+
}
|
|
2014
|
+
async function analyze(args) {
|
|
2015
|
+
const targetDir = args[0] || ".";
|
|
2016
|
+
const absoluteDir = targetDir.startsWith("/") ? targetDir : join2(process.cwd(), targetDir);
|
|
2017
|
+
console.log();
|
|
2018
|
+
console.log(import_picocolors4.default.cyan(" Analisando projeto..."));
|
|
2019
|
+
console.log();
|
|
2020
|
+
const pkgPath = join2(absoluteDir, "package.json");
|
|
2021
|
+
if (!existsSync(pkgPath)) {
|
|
2022
|
+
console.log(import_picocolors4.default.red(" Erro: package.json nao encontrado"));
|
|
2023
|
+
console.log(import_picocolors4.default.dim(` Diretorio: ${absoluteDir}`));
|
|
2024
|
+
process.exit(1);
|
|
2025
|
+
}
|
|
2026
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
2027
|
+
const deps = pkg.dependencies || {};
|
|
2028
|
+
const devDeps = pkg.devDependencies || {};
|
|
2029
|
+
const framework = detectFramework(deps);
|
|
2030
|
+
const result = {
|
|
2031
|
+
name: pkg.name || "unknown",
|
|
2032
|
+
version: pkg.version || "0.0.0",
|
|
2033
|
+
framework: framework.name,
|
|
2034
|
+
frameworkVersion: framework.version,
|
|
2035
|
+
styling: detectStyling(deps, devDeps),
|
|
2036
|
+
packageManager: detectPackageManager(absoluteDir),
|
|
2037
|
+
monorepo: detectMonorepo(absoluteDir, pkg),
|
|
2038
|
+
auth: detectAuth(deps),
|
|
2039
|
+
database: detectDatabase(deps),
|
|
2040
|
+
typescript: existsSync(join2(absoluteDir, "tsconfig.json")),
|
|
2041
|
+
dependencies: deps,
|
|
2042
|
+
devDependencies: devDeps,
|
|
2043
|
+
recommendations: []
|
|
2044
|
+
};
|
|
2045
|
+
result.recommendations = generateRecommendations(result);
|
|
2046
|
+
console.log(import_picocolors4.default.bold(" Projeto: ") + import_picocolors4.default.cyan(result.name) + import_picocolors4.default.dim(` v${result.version}`));
|
|
2047
|
+
console.log();
|
|
2048
|
+
console.log(import_picocolors4.default.bold(" Stack Detectada:"));
|
|
2049
|
+
console.log(` Framework: ${result.framework ? import_picocolors4.default.green(result.framework + "@" + result.frameworkVersion) : import_picocolors4.default.dim("nenhum")}`);
|
|
2050
|
+
console.log(` Styling: ${result.styling.map((s) => import_picocolors4.default.green(s)).join(", ")}`);
|
|
2051
|
+
console.log(` Package Manager: ${result.packageManager === "bun" ? import_picocolors4.default.green(result.packageManager) : import_picocolors4.default.yellow(result.packageManager)}`);
|
|
2052
|
+
console.log(` TypeScript: ${result.typescript ? import_picocolors4.default.green("sim") : import_picocolors4.default.dim("nao")}`);
|
|
2053
|
+
console.log(` Monorepo: ${result.monorepo ? import_picocolors4.default.green(result.monorepo) : import_picocolors4.default.dim("nao")}`);
|
|
2054
|
+
console.log(` Auth: ${result.auth ? import_picocolors4.default.green(result.auth) : import_picocolors4.default.dim("nenhum")}`);
|
|
2055
|
+
console.log(` Database: ${result.database ? import_picocolors4.default.green(result.database) : import_picocolors4.default.dim("nenhum")}`);
|
|
2056
|
+
console.log();
|
|
2057
|
+
if (result.recommendations.length > 0) {
|
|
2058
|
+
console.log(import_picocolors4.default.bold(" Recomendacoes:"));
|
|
2059
|
+
result.recommendations.forEach((rec, i) => {
|
|
2060
|
+
console.log(import_picocolors4.default.yellow(` ${i + 1}. ${rec}`));
|
|
2061
|
+
});
|
|
2062
|
+
console.log();
|
|
2063
|
+
} else {
|
|
2064
|
+
console.log(import_picocolors4.default.green(" Projeto ja esta na stack recomendada!"));
|
|
2065
|
+
console.log();
|
|
2066
|
+
}
|
|
2067
|
+
if (args.includes("--json")) {
|
|
2068
|
+
console.log(import_picocolors4.default.dim(" JSON:"));
|
|
2069
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2070
|
+
}
|
|
2071
|
+
return result;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
// src/commands/upgrade.ts
|
|
2075
|
+
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
2076
|
+
var UPGRADE_PLANS = {
|
|
2077
|
+
next: (current) => {
|
|
2078
|
+
const major = parseInt(current.replace(/[^0-9]/g, "").slice(0, 2));
|
|
2079
|
+
if (major >= 16)
|
|
2080
|
+
return null;
|
|
2081
|
+
return {
|
|
2082
|
+
current,
|
|
2083
|
+
target: "16.x",
|
|
2084
|
+
complexity: major < 15 ? "high" : "medium",
|
|
2085
|
+
breakingChanges: [
|
|
2086
|
+
"next/image: Mudancas na API de otimizacao",
|
|
2087
|
+
"Middleware: Novo formato de config",
|
|
2088
|
+
"next.config: Algumas opcoes depreciadas",
|
|
2089
|
+
"Turbopack: Agora e o bundler padrao"
|
|
2090
|
+
],
|
|
2091
|
+
steps: [
|
|
2092
|
+
"Atualizar next para ^16.0.0",
|
|
2093
|
+
"Atualizar react para ^19.0.0",
|
|
2094
|
+
"Atualizar react-dom para ^19.0.0",
|
|
2095
|
+
"Revisar next.config.ts",
|
|
2096
|
+
"Testar build: bun run build",
|
|
2097
|
+
"Testar dev: bun dev"
|
|
2098
|
+
]
|
|
2099
|
+
};
|
|
2100
|
+
},
|
|
2101
|
+
react: (current) => {
|
|
2102
|
+
if (current.startsWith("19") || current.startsWith("^19"))
|
|
2103
|
+
return null;
|
|
2104
|
+
return {
|
|
2105
|
+
current,
|
|
2106
|
+
target: "19.x",
|
|
2107
|
+
complexity: "medium",
|
|
2108
|
+
breakingChanges: [
|
|
2109
|
+
"forwardRef: Nao mais necessario, ref e prop regular",
|
|
2110
|
+
"useContext: Pode ser substituido por use(Context)",
|
|
2111
|
+
"Suspense: Mudancas em fallback behavior",
|
|
2112
|
+
"Async components: Novo suporte nativo"
|
|
2113
|
+
],
|
|
2114
|
+
steps: [
|
|
2115
|
+
"Atualizar react para ^19.0.0",
|
|
2116
|
+
"Atualizar react-dom para ^19.0.0",
|
|
2117
|
+
"Atualizar @types/react para ^19.0.0",
|
|
2118
|
+
"Remover forwardRef (usar ref como prop)",
|
|
2119
|
+
"Revisar Suspense boundaries",
|
|
2120
|
+
"Testar todos os componentes"
|
|
2121
|
+
]
|
|
2122
|
+
};
|
|
2123
|
+
},
|
|
2124
|
+
tailwind: (current) => {
|
|
2125
|
+
if (current.startsWith("4") || current.startsWith("^4"))
|
|
2126
|
+
return null;
|
|
2127
|
+
return {
|
|
2128
|
+
current,
|
|
2129
|
+
target: "4.x",
|
|
2130
|
+
complexity: "medium",
|
|
2131
|
+
breakingChanges: [
|
|
2132
|
+
"Config: Agora e CSS-first (nao mais tailwind.config.js)",
|
|
2133
|
+
"@apply: Sintaxe mudou",
|
|
2134
|
+
"Cores: Novo sistema de tokens",
|
|
2135
|
+
"Plugins: API diferente"
|
|
2136
|
+
],
|
|
2137
|
+
steps: [
|
|
2138
|
+
"Atualizar tailwindcss para ^4.0.0",
|
|
2139
|
+
"Converter tailwind.config.js para CSS",
|
|
2140
|
+
"Atualizar globals.css com @import 'tailwindcss'",
|
|
2141
|
+
"Revisar @apply usages",
|
|
2142
|
+
"Atualizar plugins para v4",
|
|
2143
|
+
"Testar todas as paginas"
|
|
2144
|
+
]
|
|
2145
|
+
};
|
|
2146
|
+
},
|
|
2147
|
+
bun: () => ({
|
|
2148
|
+
current: "pnpm/npm/yarn",
|
|
2149
|
+
target: "bun",
|
|
2150
|
+
complexity: "low",
|
|
2151
|
+
breakingChanges: [
|
|
2152
|
+
"Lockfile: Formato diferente (bun.lockb)",
|
|
2153
|
+
"Scripts: Alguns podem precisar ajuste",
|
|
2154
|
+
"Workspaces: Sintaxe levemente diferente"
|
|
2155
|
+
],
|
|
2156
|
+
steps: [
|
|
2157
|
+
"Remover node_modules",
|
|
2158
|
+
"Remover pnpm-lock.yaml / package-lock.json / yarn.lock",
|
|
2159
|
+
"Executar: bun install",
|
|
2160
|
+
"Atualizar scripts no package.json (npx -> bunx)",
|
|
2161
|
+
"Atualizar CI/CD configs",
|
|
2162
|
+
"Testar: bun dev, bun build"
|
|
2163
|
+
]
|
|
2164
|
+
}),
|
|
2165
|
+
drizzle: () => ({
|
|
2166
|
+
current: "prisma",
|
|
2167
|
+
target: "drizzle",
|
|
2168
|
+
complexity: "high",
|
|
2169
|
+
breakingChanges: [
|
|
2170
|
+
"Schema: Formato TypeScript (nao mais .prisma)",
|
|
2171
|
+
"Queries: API completamente diferente",
|
|
2172
|
+
"Migrations: Sistema diferente",
|
|
2173
|
+
"Relations: Declaracao diferente"
|
|
2174
|
+
],
|
|
2175
|
+
steps: [
|
|
2176
|
+
"Instalar drizzle-orm e drizzle-kit",
|
|
2177
|
+
"Converter schema.prisma para drizzle/schema.ts",
|
|
2178
|
+
"Configurar drizzle.config.ts",
|
|
2179
|
+
"Gerar migrations: bunx drizzle-kit generate",
|
|
2180
|
+
"Atualizar todas as queries",
|
|
2181
|
+
"Atualizar auth config (se usar)",
|
|
2182
|
+
"Remover @prisma/client e prisma",
|
|
2183
|
+
"Testar todas as operacoes de banco"
|
|
2184
|
+
]
|
|
2185
|
+
})
|
|
2186
|
+
};
|
|
2187
|
+
async function upgrade(args) {
|
|
2188
|
+
const showPlan = args.includes("--plan");
|
|
2189
|
+
const target = args.find((a) => !a.startsWith("-"));
|
|
2190
|
+
console.log();
|
|
2191
|
+
if (showPlan || !target) {
|
|
2192
|
+
console.log(import_picocolors5.default.cyan(" Analisando projeto para plano de upgrade..."));
|
|
2193
|
+
console.log();
|
|
2194
|
+
const analysis = await analyze([".", "--quiet"]);
|
|
2195
|
+
console.log(import_picocolors5.default.bold(" Upgrades Disponiveis:"));
|
|
2196
|
+
console.log();
|
|
2197
|
+
let hasUpgrades = false;
|
|
2198
|
+
if (analysis.frameworkVersion && analysis.framework === "nextjs") {
|
|
2199
|
+
const planFn = UPGRADE_PLANS["next"];
|
|
2200
|
+
if (planFn) {
|
|
2201
|
+
const plan = planFn(analysis.frameworkVersion);
|
|
2202
|
+
if (plan) {
|
|
2203
|
+
hasUpgrades = true;
|
|
2204
|
+
printUpgradePlan("Next.js", plan);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (analysis.dependencies["react"]) {
|
|
2209
|
+
const planFn = UPGRADE_PLANS["react"];
|
|
2210
|
+
if (planFn) {
|
|
2211
|
+
const plan = planFn(analysis.dependencies["react"]);
|
|
2212
|
+
if (plan) {
|
|
2213
|
+
hasUpgrades = true;
|
|
2214
|
+
printUpgradePlan("React", plan);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
const tailwindDep = analysis.dependencies["tailwindcss"] || analysis.devDependencies["tailwindcss"];
|
|
2219
|
+
if (tailwindDep) {
|
|
2220
|
+
const planFn = UPGRADE_PLANS["tailwind"];
|
|
2221
|
+
if (planFn) {
|
|
2222
|
+
const plan = planFn(tailwindDep);
|
|
2223
|
+
if (plan) {
|
|
2224
|
+
hasUpgrades = true;
|
|
2225
|
+
printUpgradePlan("Tailwind CSS", plan);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
if (analysis.packageManager !== "bun") {
|
|
2230
|
+
const planFn = UPGRADE_PLANS["bun"];
|
|
2231
|
+
if (planFn) {
|
|
2232
|
+
const plan = planFn("");
|
|
2233
|
+
if (plan) {
|
|
2234
|
+
hasUpgrades = true;
|
|
2235
|
+
printUpgradePlan("Package Manager", plan);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
if (analysis.database === "prisma") {
|
|
2240
|
+
const planFn = UPGRADE_PLANS["drizzle"];
|
|
2241
|
+
if (planFn) {
|
|
2242
|
+
const plan = planFn("");
|
|
2243
|
+
if (plan) {
|
|
2244
|
+
hasUpgrades = true;
|
|
2245
|
+
printUpgradePlan("Database", plan);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
if (!hasUpgrades) {
|
|
2250
|
+
console.log(import_picocolors5.default.green(" Projeto ja esta atualizado!"));
|
|
2251
|
+
}
|
|
2252
|
+
console.log();
|
|
2253
|
+
console.log(import_picocolors5.default.dim(" Para executar um upgrade especifico:"));
|
|
2254
|
+
console.log(import_picocolors5.default.dim(" nimbus upgrade next"));
|
|
2255
|
+
console.log(import_picocolors5.default.dim(" nimbus upgrade tailwind"));
|
|
2256
|
+
console.log(import_picocolors5.default.dim(" nimbus upgrade bun"));
|
|
2257
|
+
console.log();
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
console.log(import_picocolors5.default.yellow(` Upgrade ${target} ainda nao implementado.`));
|
|
2261
|
+
console.log(import_picocolors5.default.dim(" Por enquanto, siga os passos do --plan manualmente."));
|
|
2262
|
+
console.log();
|
|
2263
|
+
}
|
|
2264
|
+
function printUpgradePlan(name, plan) {
|
|
2265
|
+
const complexityColor = {
|
|
2266
|
+
low: import_picocolors5.default.green,
|
|
2267
|
+
medium: import_picocolors5.default.yellow,
|
|
2268
|
+
high: import_picocolors5.default.red
|
|
2269
|
+
};
|
|
2270
|
+
console.log(` ${import_picocolors5.default.bold(name)}`);
|
|
2271
|
+
console.log(` ${import_picocolors5.default.dim("Atual:")} ${plan.current} ${import_picocolors5.default.dim("->")} ${import_picocolors5.default.cyan(plan.target)}`);
|
|
2272
|
+
console.log(` ${import_picocolors5.default.dim("Complexidade:")} ${complexityColor[plan.complexity](plan.complexity)}`);
|
|
2273
|
+
console.log();
|
|
2274
|
+
console.log(` ${import_picocolors5.default.dim("Breaking Changes:")}`);
|
|
2275
|
+
plan.breakingChanges.forEach((bc) => {
|
|
2276
|
+
console.log(` ${import_picocolors5.default.yellow("!")} ${bc}`);
|
|
2277
|
+
});
|
|
2278
|
+
console.log();
|
|
2279
|
+
console.log(` ${import_picocolors5.default.dim("Passos:")}`);
|
|
2280
|
+
plan.steps.forEach((step, i) => {
|
|
2281
|
+
console.log(` ${import_picocolors5.default.dim(`${i + 1}.`)} ${step}`);
|
|
2282
|
+
});
|
|
2283
|
+
console.log();
|
|
2284
|
+
}
|
|
2285
|
+
|
|
1568
2286
|
// src/index.ts
|
|
1569
2287
|
var PACKAGE_NAME = "@nimbuslab/cli";
|
|
1570
|
-
var CURRENT_VERSION = "0.
|
|
2288
|
+
var CURRENT_VERSION = "0.9.0";
|
|
1571
2289
|
var LOGO = `
|
|
1572
2290
|
\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
2291
|
\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
|
|
@@ -1603,18 +2321,18 @@ function showUpdateNotice(latestVersion) {
|
|
|
1603
2321
|
const line2 = ` Update with: ${command}`;
|
|
1604
2322
|
const maxLen = Math.max(line1.length, line2.length);
|
|
1605
2323
|
const border = "\u2500".repeat(maxLen + 2);
|
|
1606
|
-
console.log(
|
|
1607
|
-
console.log(
|
|
1608
|
-
console.log(
|
|
1609
|
-
console.log(
|
|
2324
|
+
console.log(import_picocolors6.default.yellow(` \u250C${border}\u2510`));
|
|
2325
|
+
console.log(import_picocolors6.default.yellow(` \u2502`) + import_picocolors6.default.white(line1.padEnd(maxLen + 1)) + import_picocolors6.default.yellow(`\u2502`));
|
|
2326
|
+
console.log(import_picocolors6.default.yellow(` \u2502`) + import_picocolors6.default.cyan(line2.padEnd(maxLen + 1)) + import_picocolors6.default.yellow(`\u2502`));
|
|
2327
|
+
console.log(import_picocolors6.default.yellow(` \u2514${border}\u2518`));
|
|
1610
2328
|
console.log();
|
|
1611
2329
|
}
|
|
1612
2330
|
async function main() {
|
|
1613
2331
|
const args = process.argv.slice(2);
|
|
1614
2332
|
const command = args[0];
|
|
1615
|
-
console.log(
|
|
1616
|
-
console.log(
|
|
1617
|
-
console.log(
|
|
2333
|
+
console.log(import_picocolors6.default.cyan(LOGO));
|
|
2334
|
+
console.log(import_picocolors6.default.white(" nimbuslab CLI"));
|
|
2335
|
+
console.log(import_picocolors6.default.dim(" Create awesome projects"));
|
|
1618
2336
|
console.log();
|
|
1619
2337
|
const latestVersion = await checkForUpdates();
|
|
1620
2338
|
if (latestVersion) {
|
|
@@ -1622,46 +2340,60 @@ async function main() {
|
|
|
1622
2340
|
}
|
|
1623
2341
|
if (!command || command === "create") {
|
|
1624
2342
|
await create(args.slice(1));
|
|
2343
|
+
} else if (command === "analyze") {
|
|
2344
|
+
await analyze(args.slice(1));
|
|
2345
|
+
} else if (command === "upgrade") {
|
|
2346
|
+
await upgrade(args.slice(1));
|
|
1625
2347
|
} else if (command === "help" || command === "--help" || command === "-h") {
|
|
1626
2348
|
showHelp();
|
|
1627
2349
|
} else if (command === "version" || command === "--version" || command === "-v") {
|
|
1628
2350
|
showVersion();
|
|
1629
2351
|
} else {
|
|
1630
|
-
console.log(
|
|
2352
|
+
console.log(import_picocolors6.default.red(`Unknown command: ${command}`));
|
|
1631
2353
|
showHelp();
|
|
1632
2354
|
process.exit(1);
|
|
1633
2355
|
}
|
|
1634
2356
|
}
|
|
1635
2357
|
function showHelp() {
|
|
1636
2358
|
console.log(`
|
|
1637
|
-
${
|
|
2359
|
+
${import_picocolors6.default.bold("Usage:")} nimbus [command] [options]
|
|
1638
2360
|
|
|
1639
|
-
${
|
|
2361
|
+
${import_picocolors6.default.bold("Commands:")}
|
|
1640
2362
|
create [name] Create a new project
|
|
2363
|
+
analyze [dir] Analyze project stack
|
|
2364
|
+
upgrade [target] Upgrade dependencies
|
|
1641
2365
|
help Show this help
|
|
1642
2366
|
version Show version
|
|
1643
2367
|
|
|
1644
|
-
${
|
|
2368
|
+
${import_picocolors6.default.bold("Templates:")}
|
|
1645
2369
|
--landing Landing page (Next.js 16 + Tailwind 4 + shadcn)
|
|
1646
|
-
--app Web app (Landing + Better Auth +
|
|
2370
|
+
--app Web app (Landing + Better Auth + Drizzle)
|
|
1647
2371
|
--turborepo Monorepo (Turborepo + apps/packages)
|
|
1648
2372
|
|
|
1649
|
-
${
|
|
2373
|
+
${import_picocolors6.default.bold("Analyze & Upgrade:")}
|
|
2374
|
+
analyze . Detect stack and show recommendations
|
|
2375
|
+
analyze --json Output as JSON
|
|
2376
|
+
upgrade --plan Show upgrade plan
|
|
2377
|
+
upgrade next Upgrade Next.js
|
|
2378
|
+
upgrade tailwind Upgrade Tailwind CSS
|
|
2379
|
+
|
|
2380
|
+
${import_picocolors6.default.bold("Options:")}
|
|
1650
2381
|
-y, --yes Accept defaults
|
|
1651
2382
|
--no-git Don't initialize Git
|
|
1652
2383
|
--no-install Don't install dependencies
|
|
1653
2384
|
--template <url> Use custom template
|
|
1654
2385
|
|
|
1655
|
-
${
|
|
1656
|
-
${
|
|
1657
|
-
${
|
|
1658
|
-
${
|
|
2386
|
+
${import_picocolors6.default.bold("Examples:")}
|
|
2387
|
+
${import_picocolors6.default.dim("$")} nimbus create my-landing --landing
|
|
2388
|
+
${import_picocolors6.default.dim("$")} nimbus create my-app --app
|
|
2389
|
+
${import_picocolors6.default.dim("$")} nimbus analyze ./my-project
|
|
2390
|
+
${import_picocolors6.default.dim("$")} nimbus upgrade --plan
|
|
1659
2391
|
`);
|
|
1660
2392
|
}
|
|
1661
2393
|
function showVersion() {
|
|
1662
2394
|
console.log(`${PACKAGE_NAME} v${CURRENT_VERSION}`);
|
|
1663
2395
|
}
|
|
1664
2396
|
main().catch((err) => {
|
|
1665
|
-
console.error(
|
|
2397
|
+
console.error(import_picocolors6.default.red("Erro:"), err.message);
|
|
1666
2398
|
process.exit(1);
|
|
1667
2399
|
});
|