@nimbuslab/cli 0.8.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.
@@ -0,0 +1,201 @@
1
+ # Roadmap - Sistema de Migracao nimbuslab
2
+
3
+ > Lola Migration Assistant - Facilitar migracoes de projetos para o ecossistema nimbuslab
4
+
5
+ ---
6
+
7
+ ## Visao Geral
8
+
9
+ O sistema de migracao permite:
10
+ 1. **Analisar** projetos existentes (detectar stack)
11
+ 2. **Planejar** caminho de migracao
12
+ 3. **Executar** transformacoes automaticas (codemods)
13
+ 4. **Verificar** resultado final
14
+
15
+ ---
16
+
17
+ ## Fase 1: Fundacao (v0.9.0)
18
+
19
+ ### M80: Estrutura base do sistema de migracao
20
+ - [ ] Criar `src/commands/migrate.ts`
21
+ - [ ] Criar `src/commands/analyze.ts`
22
+ - [ ] Criar `src/commands/upgrade.ts`
23
+ - [ ] Estrutura de pastas para codemods
24
+
25
+ ### M81: Comando `nimbus analyze`
26
+ - [ ] Detectar package.json (name, version, dependencies)
27
+ - [ ] Detectar framework (Next.js, React, Angular, Vue, etc)
28
+ - [ ] Detectar styling (Tailwind, CSS Modules, styled-components)
29
+ - [ ] Detectar package manager (bun, pnpm, npm, yarn)
30
+ - [ ] Detectar monorepo (Turborepo, Nx, Lerna)
31
+ - [ ] Detectar auth (Better Auth, NextAuth, Clerk, etc)
32
+ - [ ] Detectar DB (Drizzle, Prisma, TypeORM, etc)
33
+ - [ ] Output: JSON com analise completa
34
+
35
+ ### M82: Comando `nimbus upgrade --plan`
36
+ - [ ] Comparar versoes atuais com recomendadas
37
+ - [ ] Listar breaking changes conhecidos
38
+ - [ ] Gerar plano de upgrade
39
+ - [ ] Estimar complexidade (low/medium/high)
40
+
41
+ ---
42
+
43
+ ## Fase 2: Codemods (v0.10.0)
44
+
45
+ ### M83: Infraestrutura de codemods
46
+ - [ ] Criar `src/codemods/` estrutura
47
+ - [ ] Runner de codemods (jscodeshift ou ts-morph)
48
+ - [ ] Sistema de dry-run (preview)
49
+ - [ ] Rollback automatico em caso de erro
50
+
51
+ ### M84: Codemod Tailwind 3 -> 4
52
+ - [ ] Migrar classes depreciadas
53
+ - [ ] Atualizar config (tailwind.config.js -> CSS)
54
+ - [ ] Converter @apply para novo formato
55
+ - [ ] Atualizar imports
56
+
57
+ ### M85: Codemod React 18 -> 19
58
+ - [ ] Remover forwardRef (nao mais necessario)
59
+ - [ ] Atualizar tipos (ref como prop)
60
+ - [ ] Ajustar Suspense boundaries
61
+ - [ ] Atualizar async components
62
+
63
+ ### M86: Codemod pnpm -> bun
64
+ - [ ] Converter pnpm-lock.yaml para bun.lockb
65
+ - [ ] Atualizar scripts no package.json
66
+ - [ ] Remover .npmrc especifico pnpm
67
+ - [ ] Atualizar CI/CD configs
68
+
69
+ ### M87: Codemod Prisma -> Drizzle
70
+ - [ ] Converter schema.prisma para drizzle schema
71
+ - [ ] Gerar migrations Drizzle
72
+ - [ ] Atualizar queries (findMany -> select, etc)
73
+ - [ ] Atualizar auth config
74
+
75
+ ---
76
+
77
+ ## Fase 3: Templates Modulares (v0.11.0)
78
+
79
+ ### M88: Refatorar templates em camadas
80
+ - [ ] Extrair `layers/base` (tsconfig, eslint, prettier)
81
+ - [ ] Extrair `layers/nextjs` (next.config, app structure)
82
+ - [ ] Extrair `layers/tailwind` (tailwind config, globals.css)
83
+ - [ ] Extrair `layers/shadcn` (components.json, ui/)
84
+ - [ ] Extrair `layers/auth` (Better Auth setup)
85
+ - [ ] Extrair `layers/db` (Drizzle setup)
86
+ - [ ] Extrair `layers/monorepo` (Turborepo config)
87
+
88
+ ### M89: Comando `nimbus add <layer>`
89
+ - [ ] `nimbus add auth` - Adiciona Better Auth
90
+ - [ ] `nimbus add db` - Adiciona Drizzle
91
+ - [ ] `nimbus add shadcn` - Adiciona shadcn/ui
92
+ - [ ] `nimbus add monorepo` - Converte para Turborepo
93
+ - [ ] Detectar conflitos antes de aplicar
94
+ - [ ] Merge inteligente de configs
95
+
96
+ ---
97
+
98
+ ## Fase 4: Migracao Assistida (v0.12.0)
99
+
100
+ ### M90: Lola Migration Assistant
101
+ - [ ] Criar `.claude/agents/lola-migrate.md`
102
+ - [ ] Criar `.gemini/lola-migrate.md`
103
+ - [ ] Prompts especializados para migracao
104
+ - [ ] Checklist interativo
105
+ - [ ] Documentacao de decisoes
106
+
107
+ ### M91: Comando `nimbus migrate`
108
+ - [ ] `nimbus migrate --from=angular` - Plano Angular -> Next.js
109
+ - [ ] `nimbus migrate --from=php` - Plano PHP -> Next.js
110
+ - [ ] `nimbus migrate --from=vue` - Plano Vue -> React
111
+ - [ ] Gerar plano detalhado (markdown)
112
+ - [ ] Estimar esforco (horas/dias)
113
+
114
+ ### M92: Migration Guides
115
+ - [ ] `knowledge/migrations/nextjs-15-to-16.md`
116
+ - [ ] `knowledge/migrations/tailwind-3-to-4.md`
117
+ - [ ] `knowledge/migrations/prisma-to-drizzle.md`
118
+ - [ ] `knowledge/migrations/pnpm-to-bun.md`
119
+ - [ ] `knowledge/migrations/angular-to-nextjs.md`
120
+ - [ ] `knowledge/migrations/php-laravel-to-nextjs.md`
121
+
122
+ ---
123
+
124
+ ## Fase 5: Automacao Completa (v1.0.0)
125
+
126
+ ### M93: Pipeline de migracao
127
+ - [ ] `nimbus migrate --execute` (aplica codemods)
128
+ - [ ] `nimbus migrate --verify` (roda build + lint + tests)
129
+ - [ ] Relatorio de migracao (o que mudou, o que revisar)
130
+ - [ ] Integracao com CI/CD
131
+
132
+ ### M94: Atualizacao automatica
133
+ - [ ] `nimbus upgrade` detecta atualizacoes disponiveis
134
+ - [ ] `nimbus upgrade --all` aplica todas seguras
135
+ - [ ] Notificacao de breaking changes
136
+ - [ ] Changelog automatico
137
+
138
+ ---
139
+
140
+ ## Prioridade de Implementacao
141
+
142
+ | Milestone | Prioridade | Dependencia |
143
+ |-----------|------------|-------------|
144
+ | M80 | Alta | - |
145
+ | M81 | Alta | M80 |
146
+ | M82 | Alta | M81 |
147
+ | M83 | Alta | M80 |
148
+ | M84 | Media | M83 |
149
+ | M85 | Media | M83 |
150
+ | M86 | Media | M83 |
151
+ | M87 | Media | M83 |
152
+ | M88 | Media | M80 |
153
+ | M89 | Media | M88 |
154
+ | M90 | Alta | M80 |
155
+ | M91 | Media | M81, M83 |
156
+ | M92 | Media | - |
157
+ | M93 | Baixa | M91 |
158
+ | M94 | Baixa | M93 |
159
+
160
+ ---
161
+
162
+ ## Stack dos Codemods
163
+
164
+ - **Parser:** ts-morph (TypeScript AST)
165
+ - **Runner:** Custom (baseado em jscodeshift patterns)
166
+ - **Configs:** JSON transforms
167
+ - **CSS:** PostCSS para Tailwind
168
+
169
+ ---
170
+
171
+ ## Exemplo de Uso Final
172
+
173
+ ```bash
174
+ # Analisar projeto
175
+ nimbus analyze ./meu-projeto
176
+ # Output: Next.js 15, React 18, Tailwind 3, pnpm, Prisma
177
+
178
+ # Ver plano de upgrade
179
+ nimbus upgrade --plan
180
+ # Output: Recomendado: Next 16, React 19, Tailwind 4, bun, Drizzle
181
+
182
+ # Upgrade especifico
183
+ nimbus upgrade tailwind
184
+ # Executa codemod Tailwind 3 -> 4
185
+
186
+ # Adicionar camada
187
+ nimbus add auth
188
+ # Adiciona Better Auth ao projeto
189
+
190
+ # Migracao completa
191
+ nimbus migrate --from=angular --plan
192
+ # Gera plano de migracao Angular -> Next.js
193
+
194
+ # Usar Lola para assistir
195
+ claude --agent lola-migrate
196
+ # Lola especializada em migracoes
197
+ ```
198
+
199
+ ---
200
+
201
+ *Ultima atualizacao: Janeiro 2026*
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 import_picocolors4 = __toESM(require_picocolors(), 1);
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);
@@ -1888,9 +1888,404 @@ function showNextSteps(config) {
1888
1888
  console.log();
1889
1889
  }
1890
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
+
1891
2286
  // src/index.ts
1892
2287
  var PACKAGE_NAME = "@nimbuslab/cli";
1893
- var CURRENT_VERSION = "0.8.0";
2288
+ var CURRENT_VERSION = "0.9.0";
1894
2289
  var LOGO = `
1895
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
1896
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
@@ -1926,18 +2321,18 @@ function showUpdateNotice(latestVersion) {
1926
2321
  const line2 = ` Update with: ${command}`;
1927
2322
  const maxLen = Math.max(line1.length, line2.length);
1928
2323
  const border = "\u2500".repeat(maxLen + 2);
1929
- console.log(import_picocolors4.default.yellow(` \u250C${border}\u2510`));
1930
- console.log(import_picocolors4.default.yellow(` \u2502`) + import_picocolors4.default.white(line1.padEnd(maxLen + 1)) + import_picocolors4.default.yellow(`\u2502`));
1931
- console.log(import_picocolors4.default.yellow(` \u2502`) + import_picocolors4.default.cyan(line2.padEnd(maxLen + 1)) + import_picocolors4.default.yellow(`\u2502`));
1932
- console.log(import_picocolors4.default.yellow(` \u2514${border}\u2518`));
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`));
1933
2328
  console.log();
1934
2329
  }
1935
2330
  async function main() {
1936
2331
  const args = process.argv.slice(2);
1937
2332
  const command = args[0];
1938
- console.log(import_picocolors4.default.cyan(LOGO));
1939
- console.log(import_picocolors4.default.white(" nimbuslab CLI"));
1940
- console.log(import_picocolors4.default.dim(" Create awesome projects"));
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"));
1941
2336
  console.log();
1942
2337
  const latestVersion = await checkForUpdates();
1943
2338
  if (latestVersion) {
@@ -1945,46 +2340,60 @@ async function main() {
1945
2340
  }
1946
2341
  if (!command || command === "create") {
1947
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));
1948
2347
  } else if (command === "help" || command === "--help" || command === "-h") {
1949
2348
  showHelp();
1950
2349
  } else if (command === "version" || command === "--version" || command === "-v") {
1951
2350
  showVersion();
1952
2351
  } else {
1953
- console.log(import_picocolors4.default.red(`Unknown command: ${command}`));
2352
+ console.log(import_picocolors6.default.red(`Unknown command: ${command}`));
1954
2353
  showHelp();
1955
2354
  process.exit(1);
1956
2355
  }
1957
2356
  }
1958
2357
  function showHelp() {
1959
2358
  console.log(`
1960
- ${import_picocolors4.default.bold("Usage:")} nimbus [command] [options]
2359
+ ${import_picocolors6.default.bold("Usage:")} nimbus [command] [options]
1961
2360
 
1962
- ${import_picocolors4.default.bold("Commands:")}
2361
+ ${import_picocolors6.default.bold("Commands:")}
1963
2362
  create [name] Create a new project
2363
+ analyze [dir] Analyze project stack
2364
+ upgrade [target] Upgrade dependencies
1964
2365
  help Show this help
1965
2366
  version Show version
1966
2367
 
1967
- ${import_picocolors4.default.bold("Templates:")}
2368
+ ${import_picocolors6.default.bold("Templates:")}
1968
2369
  --landing Landing page (Next.js 16 + Tailwind 4 + shadcn)
1969
- --app Web app (Landing + Better Auth + Prisma)
2370
+ --app Web app (Landing + Better Auth + Drizzle)
1970
2371
  --turborepo Monorepo (Turborepo + apps/packages)
1971
2372
 
1972
- ${import_picocolors4.default.bold("Options:")}
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:")}
1973
2381
  -y, --yes Accept defaults
1974
2382
  --no-git Don't initialize Git
1975
2383
  --no-install Don't install dependencies
1976
2384
  --template <url> Use custom template
1977
2385
 
1978
- ${import_picocolors4.default.bold("Examples:")}
1979
- ${import_picocolors4.default.dim("$")} nimbus create my-landing --landing
1980
- ${import_picocolors4.default.dim("$")} nimbus create my-app --app
1981
- ${import_picocolors4.default.dim("$")} nimbus create my-monorepo --turborepo
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
1982
2391
  `);
1983
2392
  }
1984
2393
  function showVersion() {
1985
2394
  console.log(`${PACKAGE_NAME} v${CURRENT_VERSION}`);
1986
2395
  }
1987
2396
  main().catch((err) => {
1988
- console.error(import_picocolors4.default.red("Erro:"), err.message);
2397
+ console.error(import_picocolors6.default.red("Erro:"), err.message);
1989
2398
  process.exit(1);
1990
2399
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nimbuslab/cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "CLI para criar projetos nimbuslab",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,210 @@
1
+ import * as p from "@clack/prompts"
2
+ import pc from "picocolors"
3
+ import { existsSync, readFileSync } from "node:fs"
4
+ import { join } from "node:path"
5
+
6
+ interface AnalysisResult {
7
+ name: string
8
+ version: string
9
+ framework: string | null
10
+ frameworkVersion: string | null
11
+ styling: string[]
12
+ packageManager: string
13
+ monorepo: string | null
14
+ auth: string | null
15
+ database: string | null
16
+ typescript: boolean
17
+ dependencies: Record<string, string>
18
+ devDependencies: Record<string, string>
19
+ recommendations: string[]
20
+ }
21
+
22
+ function detectPackageManager(dir: string): string {
23
+ if (existsSync(join(dir, "bun.lockb"))) return "bun"
24
+ if (existsSync(join(dir, "pnpm-lock.yaml"))) return "pnpm"
25
+ if (existsSync(join(dir, "yarn.lock"))) return "yarn"
26
+ if (existsSync(join(dir, "package-lock.json"))) return "npm"
27
+ return "unknown"
28
+ }
29
+
30
+ function detectMonorepo(dir: string, pkg: any): string | null {
31
+ if (existsSync(join(dir, "turbo.json"))) return "turborepo"
32
+ if (existsSync(join(dir, "nx.json"))) return "nx"
33
+ if (existsSync(join(dir, "lerna.json"))) return "lerna"
34
+ if (pkg.workspaces) return "workspaces"
35
+ return null
36
+ }
37
+
38
+ function detectFramework(deps: Record<string, string>): { name: string | null; version: string | null } {
39
+ if (deps["next"]) return { name: "nextjs", version: deps["next"] }
40
+ if (deps["@angular/core"]) return { name: "angular", version: deps["@angular/core"] }
41
+ if (deps["vue"]) return { name: "vue", version: deps["vue"] }
42
+ if (deps["svelte"]) return { name: "svelte", version: deps["svelte"] }
43
+ if (deps["react"] && !deps["next"]) return { name: "react", version: deps["react"] }
44
+ return { name: null, version: null }
45
+ }
46
+
47
+ function detectStyling(deps: Record<string, string>, devDeps: Record<string, string>): string[] {
48
+ const styling: string[] = []
49
+ const allDeps = { ...deps, ...devDeps }
50
+
51
+ if (allDeps["tailwindcss"]) styling.push(`tailwind@${allDeps["tailwindcss"]}`)
52
+ if (allDeps["styled-components"]) styling.push("styled-components")
53
+ if (allDeps["@emotion/react"]) styling.push("emotion")
54
+ if (allDeps["sass"]) styling.push("sass")
55
+ if (allDeps["less"]) styling.push("less")
56
+
57
+ return styling.length > 0 ? styling : ["css"]
58
+ }
59
+
60
+ function detectAuth(deps: Record<string, string>): string | null {
61
+ if (deps["better-auth"]) return "better-auth"
62
+ if (deps["next-auth"]) return "next-auth"
63
+ if (deps["@clerk/nextjs"]) return "clerk"
64
+ if (deps["@auth0/nextjs-auth0"]) return "auth0"
65
+ if (deps["@supabase/supabase-js"]) return "supabase"
66
+ return null
67
+ }
68
+
69
+ function detectDatabase(deps: Record<string, string>): string | null {
70
+ if (deps["drizzle-orm"]) return "drizzle"
71
+ if (deps["@prisma/client"]) return "prisma"
72
+ if (deps["typeorm"]) return "typeorm"
73
+ if (deps["mongoose"]) return "mongoose"
74
+ if (deps["pg"]) return "pg"
75
+ if (deps["mysql2"]) return "mysql"
76
+ return null
77
+ }
78
+
79
+ function generateRecommendations(result: AnalysisResult): string[] {
80
+ const recs: string[] = []
81
+
82
+ // Package manager
83
+ if (result.packageManager !== "bun") {
84
+ recs.push(`Migrar ${result.packageManager} -> bun (nimbus codemod bun)`)
85
+ }
86
+
87
+ // Framework version
88
+ if (result.framework === "nextjs" && result.frameworkVersion) {
89
+ const majorVersion = parseInt(result.frameworkVersion.replace(/[^0-9]/g, "").slice(0, 2))
90
+ if (majorVersion < 16) {
91
+ recs.push(`Atualizar Next.js ${result.frameworkVersion} -> 16 (nimbus upgrade next)`)
92
+ }
93
+ }
94
+
95
+ // Tailwind
96
+ const tailwind = result.styling.find(s => s.startsWith("tailwind"))
97
+ if (tailwind) {
98
+ const version = tailwind.split("@")[1] || ""
99
+ if (version.startsWith("3")) {
100
+ recs.push(`Atualizar Tailwind 3 -> 4 (nimbus upgrade tailwind)`)
101
+ }
102
+ } else if (!result.styling.includes("tailwind")) {
103
+ recs.push(`Considerar adicionar Tailwind CSS (nimbus add tailwind)`)
104
+ }
105
+
106
+ // React version
107
+ if (result.dependencies["react"]) {
108
+ const reactVersion = result.dependencies["react"]
109
+ if (reactVersion.startsWith("18") || reactVersion.startsWith("^18")) {
110
+ recs.push(`Atualizar React 18 -> 19 (nimbus upgrade react)`)
111
+ }
112
+ }
113
+
114
+ // Database
115
+ if (result.database === "prisma") {
116
+ recs.push(`Considerar migrar Prisma -> Drizzle (nimbus codemod drizzle)`)
117
+ } else if (!result.database && result.framework === "nextjs") {
118
+ recs.push(`Considerar adicionar banco de dados (nimbus add db)`)
119
+ }
120
+
121
+ // Auth
122
+ if (!result.auth && result.framework === "nextjs") {
123
+ recs.push(`Considerar adicionar autenticacao (nimbus add auth)`)
124
+ } else if (result.auth === "next-auth") {
125
+ recs.push(`Considerar migrar NextAuth -> Better Auth`)
126
+ }
127
+
128
+ // Monorepo
129
+ if (result.monorepo === "workspaces" && !result.monorepo) {
130
+ recs.push(`Considerar usar Turborepo para monorepo (nimbus add monorepo)`)
131
+ }
132
+
133
+ return recs
134
+ }
135
+
136
+ export async function analyze(args: string[]) {
137
+ const targetDir = args[0] || "."
138
+ const absoluteDir = targetDir.startsWith("/") ? targetDir : join(process.cwd(), targetDir)
139
+
140
+ console.log()
141
+ console.log(pc.cyan(" Analisando projeto..."))
142
+ console.log()
143
+
144
+ // Check if package.json exists
145
+ const pkgPath = join(absoluteDir, "package.json")
146
+ if (!existsSync(pkgPath)) {
147
+ console.log(pc.red(" Erro: package.json nao encontrado"))
148
+ console.log(pc.dim(` Diretorio: ${absoluteDir}`))
149
+ process.exit(1)
150
+ }
151
+
152
+ // Read package.json
153
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"))
154
+ const deps = pkg.dependencies || {}
155
+ const devDeps = pkg.devDependencies || {}
156
+
157
+ // Detect everything
158
+ const framework = detectFramework(deps)
159
+ const result: AnalysisResult = {
160
+ name: pkg.name || "unknown",
161
+ version: pkg.version || "0.0.0",
162
+ framework: framework.name,
163
+ frameworkVersion: framework.version,
164
+ styling: detectStyling(deps, devDeps),
165
+ packageManager: detectPackageManager(absoluteDir),
166
+ monorepo: detectMonorepo(absoluteDir, pkg),
167
+ auth: detectAuth(deps),
168
+ database: detectDatabase(deps),
169
+ typescript: existsSync(join(absoluteDir, "tsconfig.json")),
170
+ dependencies: deps,
171
+ devDependencies: devDeps,
172
+ recommendations: [],
173
+ }
174
+
175
+ // Generate recommendations
176
+ result.recommendations = generateRecommendations(result)
177
+
178
+ // Display results
179
+ console.log(pc.bold(" Projeto: ") + pc.cyan(result.name) + pc.dim(` v${result.version}`))
180
+ console.log()
181
+
182
+ console.log(pc.bold(" Stack Detectada:"))
183
+ console.log(` Framework: ${result.framework ? pc.green(result.framework + "@" + result.frameworkVersion) : pc.dim("nenhum")}`)
184
+ console.log(` Styling: ${result.styling.map(s => pc.green(s)).join(", ")}`)
185
+ console.log(` Package Manager: ${result.packageManager === "bun" ? pc.green(result.packageManager) : pc.yellow(result.packageManager)}`)
186
+ console.log(` TypeScript: ${result.typescript ? pc.green("sim") : pc.dim("nao")}`)
187
+ console.log(` Monorepo: ${result.monorepo ? pc.green(result.monorepo) : pc.dim("nao")}`)
188
+ console.log(` Auth: ${result.auth ? pc.green(result.auth) : pc.dim("nenhum")}`)
189
+ console.log(` Database: ${result.database ? pc.green(result.database) : pc.dim("nenhum")}`)
190
+ console.log()
191
+
192
+ if (result.recommendations.length > 0) {
193
+ console.log(pc.bold(" Recomendacoes:"))
194
+ result.recommendations.forEach((rec, i) => {
195
+ console.log(pc.yellow(` ${i + 1}. ${rec}`))
196
+ })
197
+ console.log()
198
+ } else {
199
+ console.log(pc.green(" Projeto ja esta na stack recomendada!"))
200
+ console.log()
201
+ }
202
+
203
+ // JSON output option
204
+ if (args.includes("--json")) {
205
+ console.log(pc.dim(" JSON:"))
206
+ console.log(JSON.stringify(result, null, 2))
207
+ }
208
+
209
+ return result
210
+ }
@@ -0,0 +1,251 @@
1
+ import * as p from "@clack/prompts"
2
+ import pc from "picocolors"
3
+ import { analyze } from "./analyze"
4
+
5
+ interface UpgradePlan {
6
+ current: string
7
+ target: string
8
+ breakingChanges: string[]
9
+ steps: string[]
10
+ complexity: "low" | "medium" | "high"
11
+ }
12
+
13
+ const UPGRADE_PLANS: Record<string, (currentVersion: string) => UpgradePlan | null> = {
14
+ next: (current) => {
15
+ const major = parseInt(current.replace(/[^0-9]/g, "").slice(0, 2))
16
+ if (major >= 16) return null
17
+
18
+ return {
19
+ current: current,
20
+ target: "16.x",
21
+ complexity: major < 15 ? "high" : "medium",
22
+ breakingChanges: [
23
+ "next/image: Mudancas na API de otimizacao",
24
+ "Middleware: Novo formato de config",
25
+ "next.config: Algumas opcoes depreciadas",
26
+ "Turbopack: Agora e o bundler padrao",
27
+ ],
28
+ steps: [
29
+ "Atualizar next para ^16.0.0",
30
+ "Atualizar react para ^19.0.0",
31
+ "Atualizar react-dom para ^19.0.0",
32
+ "Revisar next.config.ts",
33
+ "Testar build: bun run build",
34
+ "Testar dev: bun dev",
35
+ ],
36
+ }
37
+ },
38
+
39
+ react: (current) => {
40
+ if (current.startsWith("19") || current.startsWith("^19")) return null
41
+
42
+ return {
43
+ current: current,
44
+ target: "19.x",
45
+ complexity: "medium",
46
+ breakingChanges: [
47
+ "forwardRef: Nao mais necessario, ref e prop regular",
48
+ "useContext: Pode ser substituido por use(Context)",
49
+ "Suspense: Mudancas em fallback behavior",
50
+ "Async components: Novo suporte nativo",
51
+ ],
52
+ steps: [
53
+ "Atualizar react para ^19.0.0",
54
+ "Atualizar react-dom para ^19.0.0",
55
+ "Atualizar @types/react para ^19.0.0",
56
+ "Remover forwardRef (usar ref como prop)",
57
+ "Revisar Suspense boundaries",
58
+ "Testar todos os componentes",
59
+ ],
60
+ }
61
+ },
62
+
63
+ tailwind: (current) => {
64
+ if (current.startsWith("4") || current.startsWith("^4")) return null
65
+
66
+ return {
67
+ current: current,
68
+ target: "4.x",
69
+ complexity: "medium",
70
+ breakingChanges: [
71
+ "Config: Agora e CSS-first (nao mais tailwind.config.js)",
72
+ "@apply: Sintaxe mudou",
73
+ "Cores: Novo sistema de tokens",
74
+ "Plugins: API diferente",
75
+ ],
76
+ steps: [
77
+ "Atualizar tailwindcss para ^4.0.0",
78
+ "Converter tailwind.config.js para CSS",
79
+ "Atualizar globals.css com @import 'tailwindcss'",
80
+ "Revisar @apply usages",
81
+ "Atualizar plugins para v4",
82
+ "Testar todas as paginas",
83
+ ],
84
+ }
85
+ },
86
+
87
+ bun: () => ({
88
+ current: "pnpm/npm/yarn",
89
+ target: "bun",
90
+ complexity: "low",
91
+ breakingChanges: [
92
+ "Lockfile: Formato diferente (bun.lockb)",
93
+ "Scripts: Alguns podem precisar ajuste",
94
+ "Workspaces: Sintaxe levemente diferente",
95
+ ],
96
+ steps: [
97
+ "Remover node_modules",
98
+ "Remover pnpm-lock.yaml / package-lock.json / yarn.lock",
99
+ "Executar: bun install",
100
+ "Atualizar scripts no package.json (npx -> bunx)",
101
+ "Atualizar CI/CD configs",
102
+ "Testar: bun dev, bun build",
103
+ ],
104
+ }),
105
+
106
+ drizzle: () => ({
107
+ current: "prisma",
108
+ target: "drizzle",
109
+ complexity: "high",
110
+ breakingChanges: [
111
+ "Schema: Formato TypeScript (nao mais .prisma)",
112
+ "Queries: API completamente diferente",
113
+ "Migrations: Sistema diferente",
114
+ "Relations: Declaracao diferente",
115
+ ],
116
+ steps: [
117
+ "Instalar drizzle-orm e drizzle-kit",
118
+ "Converter schema.prisma para drizzle/schema.ts",
119
+ "Configurar drizzle.config.ts",
120
+ "Gerar migrations: bunx drizzle-kit generate",
121
+ "Atualizar todas as queries",
122
+ "Atualizar auth config (se usar)",
123
+ "Remover @prisma/client e prisma",
124
+ "Testar todas as operacoes de banco",
125
+ ],
126
+ }),
127
+ }
128
+
129
+ export async function upgrade(args: string[]) {
130
+ const showPlan = args.includes("--plan")
131
+ const target = args.find(a => !a.startsWith("-"))
132
+
133
+ console.log()
134
+
135
+ if (showPlan || !target) {
136
+ // Analyze first
137
+ console.log(pc.cyan(" Analisando projeto para plano de upgrade..."))
138
+ console.log()
139
+
140
+ const analysis = await analyze([".", "--quiet"])
141
+
142
+ console.log(pc.bold(" Upgrades Disponiveis:"))
143
+ console.log()
144
+
145
+ let hasUpgrades = false
146
+
147
+ // Check Next.js
148
+ if (analysis.frameworkVersion && analysis.framework === "nextjs") {
149
+ const planFn = UPGRADE_PLANS["next"]
150
+ if (planFn) {
151
+ const plan = planFn(analysis.frameworkVersion)
152
+ if (plan) {
153
+ hasUpgrades = true
154
+ printUpgradePlan("Next.js", plan)
155
+ }
156
+ }
157
+ }
158
+
159
+ // Check React
160
+ if (analysis.dependencies["react"]) {
161
+ const planFn = UPGRADE_PLANS["react"]
162
+ if (planFn) {
163
+ const plan = planFn(analysis.dependencies["react"])
164
+ if (plan) {
165
+ hasUpgrades = true
166
+ printUpgradePlan("React", plan)
167
+ }
168
+ }
169
+ }
170
+
171
+ // Check Tailwind
172
+ const tailwindDep = analysis.dependencies["tailwindcss"] || analysis.devDependencies["tailwindcss"]
173
+ if (tailwindDep) {
174
+ const planFn = UPGRADE_PLANS["tailwind"]
175
+ if (planFn) {
176
+ const plan = planFn(tailwindDep)
177
+ if (plan) {
178
+ hasUpgrades = true
179
+ printUpgradePlan("Tailwind CSS", plan)
180
+ }
181
+ }
182
+ }
183
+
184
+ // Check package manager
185
+ if (analysis.packageManager !== "bun") {
186
+ const planFn = UPGRADE_PLANS["bun"]
187
+ if (planFn) {
188
+ const plan = planFn("")
189
+ if (plan) {
190
+ hasUpgrades = true
191
+ printUpgradePlan("Package Manager", plan)
192
+ }
193
+ }
194
+ }
195
+
196
+ // Check Prisma -> Drizzle
197
+ if (analysis.database === "prisma") {
198
+ const planFn = UPGRADE_PLANS["drizzle"]
199
+ if (planFn) {
200
+ const plan = planFn("")
201
+ if (plan) {
202
+ hasUpgrades = true
203
+ printUpgradePlan("Database", plan)
204
+ }
205
+ }
206
+ }
207
+
208
+ if (!hasUpgrades) {
209
+ console.log(pc.green(" Projeto ja esta atualizado!"))
210
+ }
211
+
212
+ console.log()
213
+ console.log(pc.dim(" Para executar um upgrade especifico:"))
214
+ console.log(pc.dim(" nimbus upgrade next"))
215
+ console.log(pc.dim(" nimbus upgrade tailwind"))
216
+ console.log(pc.dim(" nimbus upgrade bun"))
217
+ console.log()
218
+
219
+ return
220
+ }
221
+
222
+ // Execute specific upgrade
223
+ console.log(pc.yellow(` Upgrade ${target} ainda nao implementado.`))
224
+ console.log(pc.dim(" Por enquanto, siga os passos do --plan manualmente."))
225
+ console.log()
226
+ }
227
+
228
+ function printUpgradePlan(name: string, plan: UpgradePlan) {
229
+ const complexityColor = {
230
+ low: pc.green,
231
+ medium: pc.yellow,
232
+ high: pc.red,
233
+ }
234
+
235
+ console.log(` ${pc.bold(name)}`)
236
+ console.log(` ${pc.dim("Atual:")} ${plan.current} ${pc.dim("->")} ${pc.cyan(plan.target)}`)
237
+ console.log(` ${pc.dim("Complexidade:")} ${complexityColor[plan.complexity](plan.complexity)}`)
238
+ console.log()
239
+
240
+ console.log(` ${pc.dim("Breaking Changes:")}`)
241
+ plan.breakingChanges.forEach(bc => {
242
+ console.log(` ${pc.yellow("!")} ${bc}`)
243
+ })
244
+ console.log()
245
+
246
+ console.log(` ${pc.dim("Passos:")}`)
247
+ plan.steps.forEach((step, i) => {
248
+ console.log(` ${pc.dim(`${i + 1}.`)} ${step}`)
249
+ })
250
+ console.log()
251
+ }
package/src/index.ts CHANGED
@@ -3,9 +3,11 @@
3
3
  import * as p from "@clack/prompts"
4
4
  import pc from "picocolors"
5
5
  import { create } from "./commands/create"
6
+ import { analyze } from "./commands/analyze"
7
+ import { upgrade } from "./commands/upgrade"
6
8
 
7
9
  const PACKAGE_NAME = "@nimbuslab/cli"
8
- const CURRENT_VERSION = "0.8.0"
10
+ const CURRENT_VERSION = "0.9.0"
9
11
 
10
12
  const LOGO = `
11
13
  ███╗ ██╗██╗███╗ ███╗██████╗ ██╗ ██╗███████╗
@@ -74,6 +76,10 @@ async function main() {
74
76
 
75
77
  if (!command || command === "create") {
76
78
  await create(args.slice(1))
79
+ } else if (command === "analyze") {
80
+ await analyze(args.slice(1))
81
+ } else if (command === "upgrade") {
82
+ await upgrade(args.slice(1))
77
83
  } else if (command === "help" || command === "--help" || command === "-h") {
78
84
  showHelp()
79
85
  } else if (command === "version" || command === "--version" || command === "-v") {
@@ -91,14 +97,23 @@ ${pc.bold("Usage:")} nimbus [command] [options]
91
97
 
92
98
  ${pc.bold("Commands:")}
93
99
  create [name] Create a new project
100
+ analyze [dir] Analyze project stack
101
+ upgrade [target] Upgrade dependencies
94
102
  help Show this help
95
103
  version Show version
96
104
 
97
105
  ${pc.bold("Templates:")}
98
106
  --landing Landing page (Next.js 16 + Tailwind 4 + shadcn)
99
- --app Web app (Landing + Better Auth + Prisma)
107
+ --app Web app (Landing + Better Auth + Drizzle)
100
108
  --turborepo Monorepo (Turborepo + apps/packages)
101
109
 
110
+ ${pc.bold("Analyze & Upgrade:")}
111
+ analyze . Detect stack and show recommendations
112
+ analyze --json Output as JSON
113
+ upgrade --plan Show upgrade plan
114
+ upgrade next Upgrade Next.js
115
+ upgrade tailwind Upgrade Tailwind CSS
116
+
102
117
  ${pc.bold("Options:")}
103
118
  -y, --yes Accept defaults
104
119
  --no-git Don't initialize Git
@@ -108,7 +123,8 @@ ${pc.bold("Options:")}
108
123
  ${pc.bold("Examples:")}
109
124
  ${pc.dim("$")} nimbus create my-landing --landing
110
125
  ${pc.dim("$")} nimbus create my-app --app
111
- ${pc.dim("$")} nimbus create my-monorepo --turborepo
126
+ ${pc.dim("$")} nimbus analyze ./my-project
127
+ ${pc.dim("$")} nimbus upgrade --plan
112
128
  `)
113
129
  }
114
130