@nimbuslab/cli 0.8.0 → 0.10.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
+ import * as p from "@clack/prompts"
2
+ import pc from "picocolors"
3
+ import { existsSync } from "node:fs"
4
+ import { join } from "node:path"
5
+
6
+ const LOLA_DIR = join(process.env.HOME || "~", ".lola")
7
+ const LOLA_REPO = "git@github.com:nimbuslab/lola.git"
8
+ const USER_MEMORY = join(process.env.HOME || "~", ".claude", "USER_MEMORY.md")
9
+
10
+ async function installLola(): Promise<void> {
11
+ console.log()
12
+ console.log(pc.cyan(" Lola - Code Agent da nimbuslab"))
13
+ console.log(pc.dim(" ==============================="))
14
+ console.log()
15
+
16
+ const isUpdate = existsSync(LOLA_DIR)
17
+
18
+ if (isUpdate) {
19
+ console.log(pc.dim(` Lola ja instalada em ${LOLA_DIR}`))
20
+ console.log(pc.cyan(" Atualizando..."))
21
+
22
+ const result = Bun.spawnSync(["git", "pull", "--quiet"], {
23
+ cwd: LOLA_DIR,
24
+ stdout: "inherit",
25
+ stderr: "inherit",
26
+ })
27
+
28
+ if (result.exitCode !== 0) {
29
+ console.log(pc.red(" Erro ao atualizar Lola"))
30
+ process.exit(1)
31
+ }
32
+
33
+ console.log(pc.green(" Atualizado!"))
34
+ } else {
35
+ console.log(pc.cyan(` Instalando Lola em ${LOLA_DIR}...`))
36
+
37
+ const result = Bun.spawnSync(["git", "clone", "--quiet", LOLA_REPO, LOLA_DIR], {
38
+ stdout: "inherit",
39
+ stderr: "inherit",
40
+ })
41
+
42
+ if (result.exitCode !== 0) {
43
+ console.log(pc.red(" Erro ao clonar repositorio"))
44
+ console.log(pc.dim(" Verifique se tem acesso ao repo nimbuslab/lola"))
45
+ process.exit(1)
46
+ }
47
+
48
+ console.log(pc.green(" Instalado!"))
49
+ }
50
+
51
+ // Verificar USER_MEMORY.md
52
+ const claudeDir = join(process.env.HOME || "~", ".claude")
53
+ if (!existsSync(USER_MEMORY)) {
54
+ console.log()
55
+ console.log(pc.cyan(" Criando USER_MEMORY.md..."))
56
+
57
+ await Bun.$`mkdir -p ${claudeDir}`
58
+
59
+ const content = `# User Memory
60
+
61
+ Memoria persistente para sessoes Claude Code
62
+
63
+ ---
64
+
65
+ ## Configuracoes da Lola
66
+
67
+ \`\`\`
68
+ lola_profile: millennial
69
+ \`\`\`
70
+
71
+ ---
72
+
73
+ ## Ultima Sessao
74
+
75
+ (sera preenchido automaticamente)
76
+
77
+ ---
78
+ `
79
+ await Bun.write(USER_MEMORY, content)
80
+ console.log(pc.green(" USER_MEMORY.md criado!"))
81
+ }
82
+
83
+ console.log()
84
+ console.log(pc.green(" Instalacao concluida!"))
85
+ console.log()
86
+ console.log(pc.bold(" Para usar a Lola:"))
87
+ console.log(pc.dim(" lola ") + pc.white("# Iniciar sessao"))
88
+ console.log(pc.dim(" lola suggest \"x\" ") + pc.white("# Sugerir melhoria"))
89
+ console.log()
90
+ console.log(pc.bold(" Para mudar perfil, edite ~/.claude/USER_MEMORY.md:"))
91
+ console.log(pc.dim(" lola_profile: millennial|genz|profissional|nerd|chill"))
92
+ console.log()
93
+ }
94
+
95
+ async function suggestImprovement(message: string): Promise<void> {
96
+ if (!message) {
97
+ console.log(pc.red(" Erro: forneça uma mensagem"))
98
+ console.log(pc.dim(" Uso: nimbus lola suggest \"sua sugestao aqui\""))
99
+ process.exit(1)
100
+ }
101
+
102
+ // Verificar gh cli
103
+ const ghCheck = Bun.spawnSync(["which", "gh"])
104
+ if (ghCheck.exitCode !== 0) {
105
+ console.log(pc.red(" Erro: GitHub CLI (gh) nao encontrado"))
106
+ console.log(pc.dim(" Instale: https://cli.github.com"))
107
+ process.exit(1)
108
+ }
109
+
110
+ // Verificar autenticacao
111
+ const authCheck = Bun.spawnSync(["gh", "auth", "status"], {
112
+ stdout: "pipe",
113
+ stderr: "pipe",
114
+ })
115
+ if (authCheck.exitCode !== 0) {
116
+ console.log(pc.red(" Erro: GitHub CLI nao autenticado"))
117
+ console.log(pc.dim(" Execute: gh auth login"))
118
+ process.exit(1)
119
+ }
120
+
121
+ // Pegar info do usuario
122
+ const gitUser = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
123
+ const gitEmail = Bun.spawnSync(["git", "config", "user.email"], { stdout: "pipe" })
124
+
125
+ const userName = gitUser.stdout.toString().trim() || "Dev"
126
+ const userEmail = gitEmail.stdout.toString().trim() || ""
127
+
128
+ console.log()
129
+ console.log(pc.cyan(" Criando sugestao..."))
130
+
131
+ const date = new Date().toISOString().split("T")[0]
132
+ const body = `## Sugestao
133
+
134
+ ${message}
135
+
136
+ ---
137
+
138
+ **Enviado por:** ${userName}
139
+ **Email:** ${userEmail}
140
+ **Data:** ${date}
141
+
142
+ ---
143
+ *Criado via \`nimbus lola suggest\`*`
144
+
145
+ const title = `Sugestao: ${message.slice(0, 50)}${message.length > 50 ? "..." : ""}`
146
+
147
+ const result = Bun.spawnSync([
148
+ "gh", "issue", "create",
149
+ "--repo", "nimbuslab/lola",
150
+ "--title", title,
151
+ "--body", body,
152
+ "--label", "sugestao",
153
+ ], {
154
+ stdout: "inherit",
155
+ stderr: "inherit",
156
+ })
157
+
158
+ if (result.exitCode !== 0) {
159
+ console.log(pc.red(" Erro ao criar issue"))
160
+ process.exit(1)
161
+ }
162
+
163
+ console.log()
164
+ console.log(pc.green(" Sugestao enviada! Hugo vai revisar."))
165
+ console.log()
166
+ }
167
+
168
+ export async function lola(args: string[]) {
169
+ const subcommand = args[0]
170
+
171
+ if (!subcommand || subcommand === "install" || subcommand === "sync") {
172
+ await installLola()
173
+ } else if (subcommand === "suggest") {
174
+ const message = args.slice(1).join(" ")
175
+ await suggestImprovement(message)
176
+ } else if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
177
+ showLolaHelp()
178
+ } else {
179
+ console.log(pc.red(` Subcomando desconhecido: ${subcommand}`))
180
+ showLolaHelp()
181
+ process.exit(1)
182
+ }
183
+ }
184
+
185
+ function showLolaHelp() {
186
+ console.log()
187
+ console.log(pc.bold(" Lola - Code Agent da nimbuslab"))
188
+ console.log()
189
+ console.log(pc.bold(" Uso:") + " nimbus lola [subcomando]")
190
+ console.log()
191
+ console.log(pc.bold(" Subcomandos:"))
192
+ console.log(pc.dim(" install ") + pc.white("Instalar/atualizar Lola"))
193
+ console.log(pc.dim(" sync ") + pc.white("Alias para install"))
194
+ console.log(pc.dim(" suggest \"msg\" ") + pc.white("Sugerir melhoria (cria issue)"))
195
+ console.log(pc.dim(" help ") + pc.white("Mostrar esta ajuda"))
196
+ console.log()
197
+ console.log(pc.bold(" Exemplos:"))
198
+ console.log(pc.dim(" $ nimbus lola install"))
199
+ console.log(pc.dim(" $ nimbus lola suggest \"adicionar suporte a X\""))
200
+ console.log()
201
+ }
@@ -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,12 @@
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"
8
+ import { lola } from "./commands/lola"
6
9
 
7
10
  const PACKAGE_NAME = "@nimbuslab/cli"
8
- const CURRENT_VERSION = "0.8.0"
11
+ const CURRENT_VERSION = "0.9.0"
9
12
 
10
13
  const LOGO = `
11
14
  ███╗ ██╗██╗███╗ ███╗██████╗ ██╗ ██╗███████╗
@@ -74,6 +77,12 @@ async function main() {
74
77
 
75
78
  if (!command || command === "create") {
76
79
  await create(args.slice(1))
80
+ } else if (command === "analyze") {
81
+ await analyze(args.slice(1))
82
+ } else if (command === "upgrade") {
83
+ await upgrade(args.slice(1))
84
+ } else if (command === "lola") {
85
+ await lola(args.slice(1))
77
86
  } else if (command === "help" || command === "--help" || command === "-h") {
78
87
  showHelp()
79
88
  } else if (command === "version" || command === "--version" || command === "-v") {
@@ -91,24 +100,41 @@ ${pc.bold("Usage:")} nimbus [command] [options]
91
100
 
92
101
  ${pc.bold("Commands:")}
93
102
  create [name] Create a new project
103
+ analyze [dir] Analyze project stack
104
+ upgrade [target] Upgrade dependencies
105
+ lola [action] Lola - Code Agent
94
106
  help Show this help
95
107
  version Show version
96
108
 
97
109
  ${pc.bold("Templates:")}
98
110
  --landing Landing page (Next.js 16 + Tailwind 4 + shadcn)
99
- --app Web app (Landing + Better Auth + Prisma)
111
+ --app Web app (Landing + Better Auth + Drizzle)
100
112
  --turborepo Monorepo (Turborepo + apps/packages)
101
113
 
114
+ ${pc.bold("Analyze & Upgrade:")}
115
+ analyze . Detect stack and show recommendations
116
+ analyze --json Output as JSON
117
+ upgrade --plan Show upgrade plan
118
+ upgrade next Upgrade Next.js
119
+ upgrade tailwind Upgrade Tailwind CSS
120
+
102
121
  ${pc.bold("Options:")}
103
122
  -y, --yes Accept defaults
104
123
  --no-git Don't initialize Git
105
124
  --no-install Don't install dependencies
106
125
  --template <url> Use custom template
107
126
 
127
+ ${pc.bold("Lola (Code Agent):")}
128
+ lola install Install/update Lola
129
+ lola suggest Suggest improvement (creates issue)
130
+
108
131
  ${pc.bold("Examples:")}
109
132
  ${pc.dim("$")} nimbus create my-landing --landing
110
133
  ${pc.dim("$")} nimbus create my-app --app
111
- ${pc.dim("$")} nimbus create my-monorepo --turborepo
134
+ ${pc.dim("$")} nimbus analyze ./my-project
135
+ ${pc.dim("$")} nimbus upgrade --plan
136
+ ${pc.dim("$")} nimbus lola install
137
+ ${pc.dim("$")} nimbus lola suggest "add support for X"
112
138
  `)
113
139
  }
114
140