@nimbuslab/cli 0.16.4 → 0.16.5
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/README.md +41 -75
- package/dist/index.js +4 -3
- package/package.json +4 -1
- package/.github/workflows/publish.yml +0 -67
- package/CLAUDE.md +0 -106
- package/MIGRATION-ROADMAP.md +0 -201
- package/bun.lock +0 -36
- package/docs/CI-CD.md +0 -181
- package/docs/analyze.md +0 -148
- package/docs/create.md +0 -219
- package/docs/migrate.md +0 -177
- package/docs/package.md +0 -229
- package/docs/upgrade.md +0 -152
- package/src/commands/analyze.ts +0 -210
- package/src/commands/create.ts +0 -1323
- package/src/commands/lola.ts +0 -1029
- package/src/commands/update.ts +0 -334
- package/src/commands/upgrade.ts +0 -251
- package/src/index.ts +0 -161
- package/tsconfig.json +0 -29
package/src/commands/lola.ts
DELETED
|
@@ -1,1029 +0,0 @@
|
|
|
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
|
-
import { homedir } from "node:os"
|
|
6
|
-
|
|
7
|
-
const HOME = homedir()
|
|
8
|
-
const LOLA_DIR = join(HOME, ".lola")
|
|
9
|
-
const LOLA_REPO = "git@github.com:nimbuslab/lola.git"
|
|
10
|
-
const USER_MEMORY = join(HOME, ".claude", "USER_MEMORY.md")
|
|
11
|
-
const LOLA_MEMORY_URL = "https://lola.nimbuslab.com.br/sse"
|
|
12
|
-
const LOLA_MEMORY_NAME = "lola-memory"
|
|
13
|
-
|
|
14
|
-
// Gemini CLI
|
|
15
|
-
const GEMINI_DIR = join(HOME, ".gemini")
|
|
16
|
-
const GEMINI_SETTINGS = join(GEMINI_DIR, "GEMINI.md")
|
|
17
|
-
|
|
18
|
-
// Tipos de agent
|
|
19
|
-
type AgentType = "claude" | "gemini" | "all"
|
|
20
|
-
|
|
21
|
-
// Comando para verificar executavel (which no Unix, where no Windows)
|
|
22
|
-
const CHECK_CMD = process.platform === "win32" ? "where" : "which"
|
|
23
|
-
|
|
24
|
-
// Helpers de detecção
|
|
25
|
-
function hasClaudeCLI(): boolean {
|
|
26
|
-
const result = Bun.spawnSync([CHECK_CMD, "claude"], { stdout: "pipe", stderr: "pipe" })
|
|
27
|
-
return result.exitCode === 0
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function hasGeminiCLI(): boolean {
|
|
31
|
-
const result = Bun.spawnSync([CHECK_CMD, "gemini"], { stdout: "pipe", stderr: "pipe" })
|
|
32
|
-
return result.exitCode === 0
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function installLolaMemoryMCP(): Promise<void> {
|
|
36
|
-
console.log()
|
|
37
|
-
console.log(pc.cyan(" Configurando lola-memory MCP..."))
|
|
38
|
-
|
|
39
|
-
// Verificar se claude CLI existe
|
|
40
|
-
const claudeCheck = Bun.spawnSync([CHECK_CMD, "claude"], { stdout: "pipe", stderr: "pipe" })
|
|
41
|
-
if (claudeCheck.exitCode !== 0) {
|
|
42
|
-
console.log(pc.yellow(" Claude CLI nao encontrado, pulando MCP"))
|
|
43
|
-
console.log(pc.dim(" Instale o Claude CLI e rode 'nimbus lola install' novamente"))
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Verificar se MCP já está configurado
|
|
48
|
-
const mcpList = Bun.spawnSync(["claude", "mcp", "list"], { stdout: "pipe", stderr: "pipe" })
|
|
49
|
-
const mcpOutput = mcpList.stdout.toString()
|
|
50
|
-
|
|
51
|
-
if (mcpOutput.includes(LOLA_MEMORY_NAME)) {
|
|
52
|
-
console.log(pc.green(" MCP lola-memory ja configurado"))
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Adicionar MCP lola-memory
|
|
57
|
-
console.log(pc.dim(" Adicionando MCP lola-memory..."))
|
|
58
|
-
|
|
59
|
-
const result = Bun.spawnSync([
|
|
60
|
-
"claude", "mcp", "add",
|
|
61
|
-
"-t", "sse",
|
|
62
|
-
"-s", "user",
|
|
63
|
-
LOLA_MEMORY_NAME,
|
|
64
|
-
"--",
|
|
65
|
-
"bunx", "mcp-remote", LOLA_MEMORY_URL
|
|
66
|
-
], {
|
|
67
|
-
stdout: "inherit",
|
|
68
|
-
stderr: "inherit",
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
if (result.exitCode !== 0) {
|
|
72
|
-
console.log(pc.yellow(" Erro ao adicionar MCP, pode ser adicionado manualmente:"))
|
|
73
|
-
console.log(pc.dim(` claude mcp add -t sse -s user ${LOLA_MEMORY_NAME} -- bunx mcp-remote ${LOLA_MEMORY_URL}`))
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
console.log(pc.green(" MCP lola-memory instalado!"))
|
|
78
|
-
console.log()
|
|
79
|
-
console.log(pc.dim(" Comandos disponiveis:"))
|
|
80
|
-
console.log(pc.dim(" remember \"query\" - Buscar conhecimento"))
|
|
81
|
-
console.log(pc.dim(" learn \"content\" - Salvar conhecimento"))
|
|
82
|
-
console.log(pc.dim(" memory_stats - Ver estatisticas"))
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ============================================
|
|
86
|
-
// GEMINI CLI
|
|
87
|
-
// ============================================
|
|
88
|
-
|
|
89
|
-
async function installGeminiCLI(): Promise<boolean> {
|
|
90
|
-
console.log()
|
|
91
|
-
console.log(pc.cyan(" Instalando Gemini CLI..."))
|
|
92
|
-
|
|
93
|
-
const result = Bun.spawnSync(["npm", "install", "-g", "@google/gemini-cli"], {
|
|
94
|
-
stdout: "inherit",
|
|
95
|
-
stderr: "inherit",
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
if (result.exitCode !== 0) {
|
|
99
|
-
console.log(pc.red(" Erro ao instalar Gemini CLI"))
|
|
100
|
-
console.log(pc.dim(" Tente manualmente: npm install -g @google/gemini-cli"))
|
|
101
|
-
return false
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
console.log(pc.green(" Gemini CLI instalado!"))
|
|
105
|
-
return true
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function installGeminiMCP(): Promise<void> {
|
|
109
|
-
console.log()
|
|
110
|
-
console.log(pc.cyan(" Configurando MCP no Gemini..."))
|
|
111
|
-
|
|
112
|
-
const settingsPath = join(GEMINI_DIR, "settings.json")
|
|
113
|
-
|
|
114
|
-
// Criar diretório se não existir
|
|
115
|
-
if (!existsSync(GEMINI_DIR)) {
|
|
116
|
-
await Bun.$`mkdir -p ${GEMINI_DIR}`
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Ler settings existente ou criar novo
|
|
120
|
-
let settings: Record<string, unknown> = {}
|
|
121
|
-
if (existsSync(settingsPath)) {
|
|
122
|
-
try {
|
|
123
|
-
const content = await Bun.file(settingsPath).text()
|
|
124
|
-
settings = JSON.parse(content)
|
|
125
|
-
} catch {
|
|
126
|
-
// Se falhar, começa do zero
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Verificar se já tem lola-memory configurado
|
|
131
|
-
const mcpServers = (settings.mcpServers || {}) as Record<string, unknown>
|
|
132
|
-
if (mcpServers[LOLA_MEMORY_NAME]) {
|
|
133
|
-
console.log(pc.green(" MCP lola-memory ja configurado no Gemini"))
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Adicionar lola-memory
|
|
138
|
-
mcpServers[LOLA_MEMORY_NAME] = {
|
|
139
|
-
url: LOLA_MEMORY_URL
|
|
140
|
-
}
|
|
141
|
-
settings.mcpServers = mcpServers
|
|
142
|
-
|
|
143
|
-
// Salvar
|
|
144
|
-
await Bun.write(settingsPath, JSON.stringify(settings, null, 2))
|
|
145
|
-
console.log(pc.green(" MCP lola-memory adicionado ao Gemini!"))
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function installGeminiSystemPrompt(): Promise<void> {
|
|
149
|
-
console.log()
|
|
150
|
-
console.log(pc.cyan(" Configurando GEMINI.md..."))
|
|
151
|
-
|
|
152
|
-
// Criar diretório se não existir
|
|
153
|
-
if (!existsSync(GEMINI_DIR)) {
|
|
154
|
-
await Bun.$`mkdir -p ${GEMINI_DIR}`
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Copiar o agent da Lola (adaptar claude.md para gemini)
|
|
158
|
-
const lolaAgent = join(LOLA_DIR, "agents", "claude.md")
|
|
159
|
-
|
|
160
|
-
if (!existsSync(lolaAgent)) {
|
|
161
|
-
console.log(pc.yellow(" Agent Lola nao encontrado"))
|
|
162
|
-
console.log(pc.dim(" Rode 'nimbus lola install' primeiro"))
|
|
163
|
-
return
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Ler o agent do Claude
|
|
167
|
-
let content = await Bun.file(lolaAgent).text()
|
|
168
|
-
|
|
169
|
-
// Remover frontmatter do Claude (não suportado no Gemini)
|
|
170
|
-
content = content.replace(/^---[\s\S]*?---\n/, "")
|
|
171
|
-
|
|
172
|
-
// Adaptar menções específicas
|
|
173
|
-
content = content.replace(/Claude Code/g, "Gemini CLI")
|
|
174
|
-
content = content.replace(/Claude CLI/g, "Gemini CLI")
|
|
175
|
-
|
|
176
|
-
// Adicionar header para Gemini
|
|
177
|
-
const geminiContent = `# Lola - Code Agent (Gemini)
|
|
178
|
-
|
|
179
|
-
> Este arquivo configura a Lola para o Gemini CLI
|
|
180
|
-
> Gerado automaticamente por: nimbus lola install
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
${content}`
|
|
185
|
-
|
|
186
|
-
await Bun.write(GEMINI_SETTINGS, geminiContent)
|
|
187
|
-
console.log(pc.green(" GEMINI.md criado!"))
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async function createGeminiCommand(): Promise<void> {
|
|
191
|
-
const isWindows = process.platform === "win32"
|
|
192
|
-
const lolaAgent = GEMINI_SETTINGS
|
|
193
|
-
|
|
194
|
-
console.log()
|
|
195
|
-
console.log(pc.cyan(" Configurando comando lola-gemini..."))
|
|
196
|
-
|
|
197
|
-
if (isWindows) {
|
|
198
|
-
// Windows: adicionar função ao PowerShell profile
|
|
199
|
-
const ps7ProfileDir = join(HOME, "Documents", "PowerShell")
|
|
200
|
-
const profileName = "Microsoft.PowerShell_profile.ps1"
|
|
201
|
-
const profilePath = join(ps7ProfileDir, profileName)
|
|
202
|
-
|
|
203
|
-
const lolaGeminiFunction = `
|
|
204
|
-
# Lola (Gemini) - Code Agent da nimbuslab
|
|
205
|
-
function lola-gemini {
|
|
206
|
-
param([Parameter(ValueFromRemainingArguments=$true)]$args)
|
|
207
|
-
gemini @args
|
|
208
|
-
}
|
|
209
|
-
`
|
|
210
|
-
|
|
211
|
-
if (!existsSync(ps7ProfileDir)) {
|
|
212
|
-
await Bun.$`mkdir -p ${ps7ProfileDir}`
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
let profileContent = ""
|
|
216
|
-
if (existsSync(profilePath)) {
|
|
217
|
-
profileContent = await Bun.file(profilePath).text()
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (!profileContent.includes("function lola-gemini")) {
|
|
221
|
-
await Bun.write(profilePath, profileContent + lolaGeminiFunction)
|
|
222
|
-
console.log(pc.green(" Funcao lola-gemini adicionada ao PowerShell"))
|
|
223
|
-
} else {
|
|
224
|
-
console.log(pc.green(" Funcao lola-gemini ja existe"))
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
// Linux/macOS: criar script em ~/.local/bin
|
|
228
|
-
const binDir = join(HOME, ".local", "bin")
|
|
229
|
-
const lolaGeminiScript = join(binDir, "lola-gemini")
|
|
230
|
-
|
|
231
|
-
if (!existsSync(lolaGeminiScript)) {
|
|
232
|
-
await Bun.$`mkdir -p ${binDir}`
|
|
233
|
-
|
|
234
|
-
const unixScript = `#!/bin/bash
|
|
235
|
-
# lola-gemini - Code Agent da nimbuslab (Gemini)
|
|
236
|
-
|
|
237
|
-
if [[ "\$1" == "-h" || "\$1" == "--help" ]]; then
|
|
238
|
-
echo "lola-gemini - Code Agent da nimbuslab (Gemini)"
|
|
239
|
-
echo "Uso: lola-gemini [args]"
|
|
240
|
-
echo " lola-gemini Abre Gemini CLI com a Lola"
|
|
241
|
-
exit 0
|
|
242
|
-
fi
|
|
243
|
-
|
|
244
|
-
exec gemini "\$@"
|
|
245
|
-
`
|
|
246
|
-
await Bun.write(lolaGeminiScript, unixScript)
|
|
247
|
-
await Bun.$`chmod +x ${lolaGeminiScript}`
|
|
248
|
-
|
|
249
|
-
console.log(pc.green(` Script lola-gemini criado em ${lolaGeminiScript}`))
|
|
250
|
-
} else {
|
|
251
|
-
console.log(pc.green(" Script lola-gemini ja existe"))
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async function setupGemini(): Promise<void> {
|
|
257
|
-
// Verificar/instalar Gemini CLI
|
|
258
|
-
if (!hasGeminiCLI()) {
|
|
259
|
-
const installed = await installGeminiCLI()
|
|
260
|
-
if (!installed) return
|
|
261
|
-
} else {
|
|
262
|
-
console.log(pc.green(" Gemini CLI ja instalado"))
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Configurar MCP
|
|
266
|
-
await installGeminiMCP()
|
|
267
|
-
|
|
268
|
-
// Configurar GEMINI.md
|
|
269
|
-
await installGeminiSystemPrompt()
|
|
270
|
-
|
|
271
|
-
// Criar comando lola-gemini
|
|
272
|
-
await createGeminiCommand()
|
|
273
|
-
|
|
274
|
-
console.log()
|
|
275
|
-
console.log(pc.green(" Gemini configurado!"))
|
|
276
|
-
console.log()
|
|
277
|
-
console.log(pc.bold(" Para usar:"))
|
|
278
|
-
console.log(pc.dim(" lola-gemini ") + pc.white("# Iniciar sessao com Gemini"))
|
|
279
|
-
console.log(pc.dim(" gemini ") + pc.white("# Gemini puro (sem Lola)"))
|
|
280
|
-
console.log()
|
|
281
|
-
console.log(pc.dim(" Na primeira execucao, faca login com sua conta Google."))
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
async function suggestImprovement(message: string): Promise<void> {
|
|
286
|
-
if (!message) {
|
|
287
|
-
console.log(pc.red(" Erro: forneça uma mensagem"))
|
|
288
|
-
console.log(pc.dim(" Uso: nimbus lola suggest \"sua sugestao aqui\""))
|
|
289
|
-
process.exit(1)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Verificar gh cli
|
|
293
|
-
const ghCheck = Bun.spawnSync([CHECK_CMD, "gh"], { stderr: "pipe" })
|
|
294
|
-
if (ghCheck.exitCode !== 0) {
|
|
295
|
-
console.log(pc.red(" Erro: GitHub CLI (gh) nao encontrado"))
|
|
296
|
-
console.log(pc.dim(" Instale: https://cli.github.com"))
|
|
297
|
-
process.exit(1)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Verificar autenticacao
|
|
301
|
-
const authCheck = Bun.spawnSync(["gh", "auth", "status"], {
|
|
302
|
-
stdout: "pipe",
|
|
303
|
-
stderr: "pipe",
|
|
304
|
-
})
|
|
305
|
-
if (authCheck.exitCode !== 0) {
|
|
306
|
-
console.log(pc.red(" Erro: GitHub CLI nao autenticado"))
|
|
307
|
-
console.log(pc.dim(" Execute: gh auth login"))
|
|
308
|
-
process.exit(1)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Pegar info do usuario
|
|
312
|
-
const gitUser = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
|
|
313
|
-
const gitEmail = Bun.spawnSync(["git", "config", "user.email"], { stdout: "pipe" })
|
|
314
|
-
|
|
315
|
-
const userName = gitUser.stdout.toString().trim() || "Dev"
|
|
316
|
-
const userEmail = gitEmail.stdout.toString().trim() || ""
|
|
317
|
-
|
|
318
|
-
console.log()
|
|
319
|
-
console.log(pc.cyan(" Criando sugestao..."))
|
|
320
|
-
|
|
321
|
-
const date = new Date().toISOString().split("T")[0]
|
|
322
|
-
const body = `## Sugestao
|
|
323
|
-
|
|
324
|
-
${message}
|
|
325
|
-
|
|
326
|
-
---
|
|
327
|
-
|
|
328
|
-
**Enviado por:** ${userName}
|
|
329
|
-
**Email:** ${userEmail}
|
|
330
|
-
**Data:** ${date}
|
|
331
|
-
|
|
332
|
-
---
|
|
333
|
-
*Criado via \`nimbus lola suggest\`*`
|
|
334
|
-
|
|
335
|
-
const title = `Sugestao: ${message.slice(0, 50)}${message.length > 50 ? "..." : ""}`
|
|
336
|
-
|
|
337
|
-
const result = Bun.spawnSync([
|
|
338
|
-
"gh", "issue", "create",
|
|
339
|
-
"--repo", "nimbuslab/lola",
|
|
340
|
-
"--title", title,
|
|
341
|
-
"--body", body,
|
|
342
|
-
"--label", "sugestao",
|
|
343
|
-
], {
|
|
344
|
-
stdout: "inherit",
|
|
345
|
-
stderr: "inherit",
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
if (result.exitCode !== 0) {
|
|
349
|
-
console.log(pc.red(" Erro ao criar issue"))
|
|
350
|
-
process.exit(1)
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
console.log()
|
|
354
|
-
console.log(pc.green(" Sugestao enviada! Hugo vai revisar."))
|
|
355
|
-
console.log()
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const PROFILES = {
|
|
359
|
-
millennial: {
|
|
360
|
-
label: "Millennial",
|
|
361
|
-
hint: "Nerd 90s/2000s, DBZ, Matrix, MSN",
|
|
362
|
-
emoji: "",
|
|
363
|
-
},
|
|
364
|
-
genz: {
|
|
365
|
-
label: "Gen Z",
|
|
366
|
-
hint: "Direto, girias atuais, slay",
|
|
367
|
-
emoji: "",
|
|
368
|
-
},
|
|
369
|
-
profissional: {
|
|
370
|
-
label: "Profissional",
|
|
371
|
-
hint: "100% tecnico, sem referencias",
|
|
372
|
-
emoji: "",
|
|
373
|
-
},
|
|
374
|
-
nerd: {
|
|
375
|
-
label: "Nerd",
|
|
376
|
-
hint: "Star Trek, vim jokes, deep tech",
|
|
377
|
-
emoji: "",
|
|
378
|
-
},
|
|
379
|
-
chill: {
|
|
380
|
-
label: "Chill",
|
|
381
|
-
hint: "Relaxado, vibes cafe, positivo",
|
|
382
|
-
emoji: "",
|
|
383
|
-
},
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async function onboardDev(): Promise<void> {
|
|
387
|
-
console.log()
|
|
388
|
-
console.log(pc.cyan(" Lola - Onboarding"))
|
|
389
|
-
console.log(pc.dim(" ================="))
|
|
390
|
-
console.log()
|
|
391
|
-
|
|
392
|
-
// Verificar se Lola está instalada
|
|
393
|
-
if (!existsSync(LOLA_DIR)) {
|
|
394
|
-
console.log(pc.yellow(" Lola nao instalada. Instalando primeiro..."))
|
|
395
|
-
console.log()
|
|
396
|
-
await installLolaBase()
|
|
397
|
-
console.log()
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Pegar nome do git config
|
|
401
|
-
const gitUser = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
|
|
402
|
-
const defaultName = gitUser.stdout.toString().trim() || ""
|
|
403
|
-
|
|
404
|
-
p.intro(pc.bgCyan(pc.black(" Bem-vindo a nimbuslab! ")))
|
|
405
|
-
|
|
406
|
-
// Nome do dev
|
|
407
|
-
const devName = await p.text({
|
|
408
|
-
message: "Qual seu nome?",
|
|
409
|
-
placeholder: "Seu nome",
|
|
410
|
-
initialValue: defaultName,
|
|
411
|
-
validate: (v) => v ? undefined : "Nome e obrigatorio",
|
|
412
|
-
})
|
|
413
|
-
|
|
414
|
-
if (p.isCancel(devName)) {
|
|
415
|
-
p.cancel("Onboarding cancelado")
|
|
416
|
-
process.exit(0)
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Perfil preferido
|
|
420
|
-
const profile = await p.select({
|
|
421
|
-
message: "Qual perfil da Lola voce prefere?",
|
|
422
|
-
options: Object.entries(PROFILES).map(([value, { label, hint }]) => ({
|
|
423
|
-
value,
|
|
424
|
-
label,
|
|
425
|
-
hint,
|
|
426
|
-
})),
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
if (p.isCancel(profile)) {
|
|
430
|
-
p.cancel("Onboarding cancelado")
|
|
431
|
-
process.exit(0)
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Atualizar USER_MEMORY.md
|
|
435
|
-
const claudeDir = join(HOME, ".claude")
|
|
436
|
-
await Bun.$`mkdir -p ${claudeDir}`
|
|
437
|
-
|
|
438
|
-
const content = `# User Memory
|
|
439
|
-
|
|
440
|
-
Memoria persistente para sessoes Claude Code
|
|
441
|
-
|
|
442
|
-
---
|
|
443
|
-
|
|
444
|
-
## Dev
|
|
445
|
-
|
|
446
|
-
**Nome:** ${devName}
|
|
447
|
-
**Maquina:** ${process.env.HOSTNAME || "local"}
|
|
448
|
-
**Onboarding:** ${new Date().toISOString().split("T")[0]}
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
|
-
## Configuracoes da Lola
|
|
453
|
-
|
|
454
|
-
\`\`\`
|
|
455
|
-
lola_profile: ${profile}
|
|
456
|
-
\`\`\`
|
|
457
|
-
|
|
458
|
-
---
|
|
459
|
-
|
|
460
|
-
## Ultima Sessao
|
|
461
|
-
|
|
462
|
-
(sera preenchido automaticamente)
|
|
463
|
-
|
|
464
|
-
---
|
|
465
|
-
`
|
|
466
|
-
|
|
467
|
-
await Bun.write(USER_MEMORY, content)
|
|
468
|
-
|
|
469
|
-
p.outro(pc.green("Onboarding concluido!"))
|
|
470
|
-
|
|
471
|
-
// Resumo
|
|
472
|
-
console.log()
|
|
473
|
-
console.log(pc.bold(" Resumo:"))
|
|
474
|
-
console.log(pc.dim(" Nome: ") + pc.white(devName as string))
|
|
475
|
-
console.log(pc.dim(" Perfil: ") + pc.white(PROFILES[profile as keyof typeof PROFILES].label))
|
|
476
|
-
console.log()
|
|
477
|
-
console.log(pc.bold(" Proximos passos:"))
|
|
478
|
-
console.log(pc.dim(" 1. ") + pc.white("lola") + pc.dim(" - Iniciar sessao com a Lola"))
|
|
479
|
-
console.log(pc.dim(" 2. ") + pc.white("nimbus create meu-projeto --fast") + pc.dim(" - Criar projeto"))
|
|
480
|
-
console.log(pc.dim(" 3. ") + pc.white("lola suggest \"sua ideia\"") + pc.dim(" - Sugerir melhoria"))
|
|
481
|
-
console.log()
|
|
482
|
-
console.log(pc.dim(" Docs: ~/.lola/README.md"))
|
|
483
|
-
console.log()
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Quiz sobre a nimbuslab
|
|
487
|
-
interface QuizQuestion {
|
|
488
|
-
question: string
|
|
489
|
-
options: { value: string; label: string }[]
|
|
490
|
-
correct: string
|
|
491
|
-
explanation: string
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const QUIZ_QUESTIONS: QuizQuestion[] = [
|
|
495
|
-
{
|
|
496
|
-
question: "Quais sao os 4 valores da nimbuslab?",
|
|
497
|
-
options: [
|
|
498
|
-
{ value: "a", label: "Velocidade, Qualidade, Preco, Entrega" },
|
|
499
|
-
{ value: "b", label: "Cocriacao, Inovacao, Evolucao, Humanizacao" },
|
|
500
|
-
{ value: "c", label: "Codigo, Design, Marketing, Vendas" },
|
|
501
|
-
{ value: "d", label: "Agil, Lean, Scrum, Kanban" },
|
|
502
|
-
],
|
|
503
|
-
correct: "b",
|
|
504
|
-
explanation: "Cocriacao (construimos junto), Inovacao (buscamos o novo), Evolucao (aprendizado continuo), Humanizacao (tecnologia a servico das pessoas).",
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
question: "Quais sao os 4 pilares da filosofia fast?",
|
|
508
|
-
options: [
|
|
509
|
-
{ value: "a", label: "Analise, Planejamento, Execucao, Documentacao" },
|
|
510
|
-
{ value: "b", label: "Design, Codigo, Teste, Deploy" },
|
|
511
|
-
{ value: "c", label: "Briefing, Prototipo, Desenvolvimento, Lancamento" },
|
|
512
|
-
{ value: "d", label: "Discovery, Definition, Development, Delivery" },
|
|
513
|
-
],
|
|
514
|
-
correct: "a",
|
|
515
|
-
explanation: "Filosofia fast: 1. Analise (entender), 2. Planejamento (definir caminho), 3. Execucao (implementar), 4. Documentacao (registrar).",
|
|
516
|
-
},
|
|
517
|
-
{
|
|
518
|
-
question: "Qual package manager a nimbuslab usa como padrao?",
|
|
519
|
-
options: [
|
|
520
|
-
{ value: "a", label: "npm" },
|
|
521
|
-
{ value: "b", label: "yarn" },
|
|
522
|
-
{ value: "c", label: "pnpm" },
|
|
523
|
-
{ value: "d", label: "bun" },
|
|
524
|
-
],
|
|
525
|
-
correct: "d",
|
|
526
|
-
explanation: "Bun e o package manager padrao. Mais rapido e com menos dependencias.",
|
|
527
|
-
},
|
|
528
|
-
{
|
|
529
|
-
question: "Qual o estilo padrao do shadcn/ui na nimbuslab?",
|
|
530
|
-
options: [
|
|
531
|
-
{ value: "a", label: "new-york" },
|
|
532
|
-
{ value: "b", label: "default" },
|
|
533
|
-
{ value: "c", label: "minimal" },
|
|
534
|
-
{ value: "d", label: "custom" },
|
|
535
|
-
],
|
|
536
|
-
correct: "b",
|
|
537
|
-
explanation: "Usamos o estilo 'default' do shadcn/ui. Nunca 'new-york'.",
|
|
538
|
-
},
|
|
539
|
-
{
|
|
540
|
-
question: "Em projetos externos (stealth mode), a Lola deve:",
|
|
541
|
-
options: [
|
|
542
|
-
{ value: "a", label: "Sempre mencionar a nimbuslab nos commits" },
|
|
543
|
-
{ value: "b", label: "Usar assinatura 'Co-authored-by: Lola'" },
|
|
544
|
-
{ value: "c", label: "Nunca mencionar nimbuslab, Lola ou IA" },
|
|
545
|
-
{ value: "d", label: "Adicionar badge da nimbuslab no README" },
|
|
546
|
-
],
|
|
547
|
-
correct: "c",
|
|
548
|
-
explanation: "Stealth mode: commits sem mencao a nimbuslab/Lola/IA. O cliente nao precisa saber dos bastidores.",
|
|
549
|
-
},
|
|
550
|
-
{
|
|
551
|
-
question: "Qual a versao minima do Next.js usada na stack nimbuslab?",
|
|
552
|
-
options: [
|
|
553
|
-
{ value: "a", label: "Next.js 13" },
|
|
554
|
-
{ value: "b", label: "Next.js 14" },
|
|
555
|
-
{ value: "c", label: "Next.js 15" },
|
|
556
|
-
{ value: "d", label: "Next.js 16" },
|
|
557
|
-
],
|
|
558
|
-
correct: "d",
|
|
559
|
-
explanation: "Stack atual: Next.js 16+ com App Router e Turbopack.",
|
|
560
|
-
},
|
|
561
|
-
{
|
|
562
|
-
question: "Quem pode aprovar e mergear mudancas no repositorio da Lola?",
|
|
563
|
-
options: [
|
|
564
|
-
{ value: "a", label: "Qualquer dev da nimbuslab" },
|
|
565
|
-
{ value: "b", label: "Apenas o Hugo" },
|
|
566
|
-
{ value: "c", label: "Qualquer pessoa com acesso ao repo" },
|
|
567
|
-
{ value: "d", label: "A propria Lola via automacao" },
|
|
568
|
-
],
|
|
569
|
-
correct: "b",
|
|
570
|
-
explanation: "Apenas o Hugo pode aprovar e mergear. Devs sugerem via 'lola suggest', Hugo revisa.",
|
|
571
|
-
},
|
|
572
|
-
{
|
|
573
|
-
question: "Em commits da nimbuslab, qual o idioma correto?",
|
|
574
|
-
options: [
|
|
575
|
-
{ value: "a", label: "Ingles" },
|
|
576
|
-
{ value: "b", label: "Portugues (BR)" },
|
|
577
|
-
{ value: "c", label: "Depende do projeto" },
|
|
578
|
-
{ value: "d", label: "Spanglish" },
|
|
579
|
-
],
|
|
580
|
-
correct: "b",
|
|
581
|
-
explanation: "Commits e PRs em Portugues (BR). Codigo e comentarios em Ingles.",
|
|
582
|
-
},
|
|
583
|
-
]
|
|
584
|
-
|
|
585
|
-
async function runQuiz(): Promise<void> {
|
|
586
|
-
console.log()
|
|
587
|
-
console.log(pc.cyan(" Quiz nimbuslab"))
|
|
588
|
-
console.log(pc.dim(" =============="))
|
|
589
|
-
console.log()
|
|
590
|
-
console.log(pc.dim(" Teste seus conhecimentos sobre a nimbuslab!"))
|
|
591
|
-
console.log(pc.dim(" 8 perguntas sobre valores, filosofia e stack."))
|
|
592
|
-
console.log()
|
|
593
|
-
|
|
594
|
-
let score = 0
|
|
595
|
-
const results: { question: string; correct: boolean; explanation: string }[] = []
|
|
596
|
-
|
|
597
|
-
for (let i = 0; i < QUIZ_QUESTIONS.length; i++) {
|
|
598
|
-
const q = QUIZ_QUESTIONS[i]!
|
|
599
|
-
|
|
600
|
-
const answer = await p.select({
|
|
601
|
-
message: `${i + 1}. ${q.question}`,
|
|
602
|
-
options: q.options,
|
|
603
|
-
})
|
|
604
|
-
|
|
605
|
-
if (p.isCancel(answer)) {
|
|
606
|
-
p.cancel("Quiz cancelado")
|
|
607
|
-
process.exit(0)
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
const isCorrect = answer === q.correct
|
|
611
|
-
if (isCorrect) {
|
|
612
|
-
score++
|
|
613
|
-
console.log(pc.green(" Correto!"))
|
|
614
|
-
} else {
|
|
615
|
-
console.log(pc.red(" Incorreto."))
|
|
616
|
-
}
|
|
617
|
-
console.log(pc.dim(` ${q.explanation}`))
|
|
618
|
-
console.log()
|
|
619
|
-
|
|
620
|
-
results.push({
|
|
621
|
-
question: q.question,
|
|
622
|
-
correct: isCorrect,
|
|
623
|
-
explanation: q.explanation,
|
|
624
|
-
})
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Resultado final
|
|
628
|
-
const percentage = Math.round((score / QUIZ_QUESTIONS.length) * 100)
|
|
629
|
-
const passed = percentage >= 75
|
|
630
|
-
|
|
631
|
-
console.log(pc.bold(" Resultado:"))
|
|
632
|
-
console.log()
|
|
633
|
-
|
|
634
|
-
if (passed) {
|
|
635
|
-
console.log(pc.green(` ${score}/${QUIZ_QUESTIONS.length} (${percentage}%) - Aprovado!`))
|
|
636
|
-
console.log()
|
|
637
|
-
if (percentage === 100) {
|
|
638
|
-
console.log(pc.cyan(" Perfeito! Voce conhece bem a nimbuslab."))
|
|
639
|
-
} else {
|
|
640
|
-
console.log(pc.cyan(" Muito bem! Voce esta pronto para trabalhar."))
|
|
641
|
-
}
|
|
642
|
-
} else {
|
|
643
|
-
console.log(pc.yellow(` ${score}/${QUIZ_QUESTIONS.length} (${percentage}%) - Precisa revisar`))
|
|
644
|
-
console.log()
|
|
645
|
-
console.log(pc.dim(" Revise os conceitos em:"))
|
|
646
|
-
console.log(pc.dim(" ~/.lola/core/values.md"))
|
|
647
|
-
console.log(pc.dim(" ~/.lola/core/philosophy.md"))
|
|
648
|
-
console.log(pc.dim(" ~/.lola/modules/stack.md"))
|
|
649
|
-
}
|
|
650
|
-
console.log()
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
async function installInteractive(): Promise<void> {
|
|
654
|
-
console.log()
|
|
655
|
-
console.log(pc.cyan(" Lola - Code Agent da nimbuslab"))
|
|
656
|
-
console.log(pc.dim(" ==============================="))
|
|
657
|
-
console.log()
|
|
658
|
-
|
|
659
|
-
// Detectar CLIs instaladas
|
|
660
|
-
const hasClaude = hasClaudeCLI()
|
|
661
|
-
const hasGemini = hasGeminiCLI()
|
|
662
|
-
|
|
663
|
-
console.log(pc.dim(" Detectando agents..."))
|
|
664
|
-
console.log(pc.dim(` Claude CLI: ${hasClaude ? pc.green("instalado") : pc.yellow("nao encontrado")}`))
|
|
665
|
-
console.log(pc.dim(` Gemini CLI: ${hasGemini ? pc.green("instalado") : pc.yellow("nao encontrado")}`))
|
|
666
|
-
console.log()
|
|
667
|
-
|
|
668
|
-
// Perguntar qual agent configurar
|
|
669
|
-
const agentChoice = await p.select({
|
|
670
|
-
message: "Qual agent deseja configurar?",
|
|
671
|
-
options: [
|
|
672
|
-
{
|
|
673
|
-
value: "all",
|
|
674
|
-
label: "Todos (Recomendado)",
|
|
675
|
-
hint: "Configura Claude e Gemini",
|
|
676
|
-
},
|
|
677
|
-
{
|
|
678
|
-
value: "claude",
|
|
679
|
-
label: "Apenas Claude",
|
|
680
|
-
hint: hasClaude ? "Ja instalado" : "Sera instalado",
|
|
681
|
-
},
|
|
682
|
-
{
|
|
683
|
-
value: "gemini",
|
|
684
|
-
label: "Apenas Gemini",
|
|
685
|
-
hint: hasGemini ? "Ja instalado" : "Sera instalado",
|
|
686
|
-
},
|
|
687
|
-
],
|
|
688
|
-
})
|
|
689
|
-
|
|
690
|
-
if (p.isCancel(agentChoice)) {
|
|
691
|
-
p.cancel("Instalacao cancelada")
|
|
692
|
-
process.exit(0)
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const choice = agentChoice as AgentType
|
|
696
|
-
|
|
697
|
-
// Instalar ~/.lola primeiro (necessário para ambos)
|
|
698
|
-
await installLolaBase()
|
|
699
|
-
|
|
700
|
-
// Configurar agents escolhidos
|
|
701
|
-
if (choice === "claude" || choice === "all") {
|
|
702
|
-
console.log()
|
|
703
|
-
console.log(pc.bgBlue(pc.white(" CLAUDE ")))
|
|
704
|
-
await setupClaude()
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
if (choice === "gemini" || choice === "all") {
|
|
708
|
-
console.log()
|
|
709
|
-
console.log(pc.bgMagenta(pc.white(" GEMINI ")))
|
|
710
|
-
await setupGemini()
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Resumo final
|
|
714
|
-
console.log()
|
|
715
|
-
console.log(pc.green(" ==============================="))
|
|
716
|
-
console.log(pc.green(" Instalacao concluida!"))
|
|
717
|
-
console.log(pc.green(" ==============================="))
|
|
718
|
-
console.log()
|
|
719
|
-
|
|
720
|
-
if (choice === "claude" || choice === "all") {
|
|
721
|
-
console.log(pc.dim(" lola ") + pc.white("# Claude com Lola"))
|
|
722
|
-
}
|
|
723
|
-
if (choice === "gemini" || choice === "all") {
|
|
724
|
-
console.log(pc.dim(" lola-gemini ") + pc.white("# Gemini com Lola"))
|
|
725
|
-
}
|
|
726
|
-
console.log()
|
|
727
|
-
console.log(pc.bold(" lola-memory (compartilhado entre agents):"))
|
|
728
|
-
console.log(pc.dim(" remember, learn, memory_stats"))
|
|
729
|
-
console.log()
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
async function installLolaBase(): Promise<void> {
|
|
733
|
-
console.log()
|
|
734
|
-
console.log(pc.cyan(" Instalando base Lola (~/.lola)..."))
|
|
735
|
-
|
|
736
|
-
const isUpdate = existsSync(LOLA_DIR)
|
|
737
|
-
|
|
738
|
-
if (isUpdate) {
|
|
739
|
-
console.log(pc.dim(` Lola ja instalada em ${LOLA_DIR}`))
|
|
740
|
-
console.log(pc.dim(" Atualizando..."))
|
|
741
|
-
|
|
742
|
-
// Verificar se tem mudancas locais
|
|
743
|
-
const statusCheck = Bun.spawnSync(["git", "status", "--porcelain"], {
|
|
744
|
-
cwd: LOLA_DIR,
|
|
745
|
-
stdout: "pipe",
|
|
746
|
-
})
|
|
747
|
-
const hasLocalChanges = statusCheck.stdout.toString().trim().length > 0
|
|
748
|
-
|
|
749
|
-
// Stash se tiver mudancas locais
|
|
750
|
-
if (hasLocalChanges) {
|
|
751
|
-
console.log(pc.dim(" Salvando mudancas locais..."))
|
|
752
|
-
Bun.spawnSync(["git", "stash", "--quiet"], {
|
|
753
|
-
cwd: LOLA_DIR,
|
|
754
|
-
stdout: "pipe",
|
|
755
|
-
stderr: "pipe",
|
|
756
|
-
})
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Pull
|
|
760
|
-
const result = Bun.spawnSync(["git", "pull", "--quiet"], {
|
|
761
|
-
cwd: LOLA_DIR,
|
|
762
|
-
stdout: "inherit",
|
|
763
|
-
stderr: "inherit",
|
|
764
|
-
})
|
|
765
|
-
|
|
766
|
-
// Restaurar mudancas locais se tinha
|
|
767
|
-
if (hasLocalChanges) {
|
|
768
|
-
console.log(pc.dim(" Restaurando mudancas locais..."))
|
|
769
|
-
const stashPop = Bun.spawnSync(["git", "stash", "pop", "--quiet"], {
|
|
770
|
-
cwd: LOLA_DIR,
|
|
771
|
-
stdout: "pipe",
|
|
772
|
-
stderr: "pipe",
|
|
773
|
-
})
|
|
774
|
-
|
|
775
|
-
if (stashPop.exitCode !== 0) {
|
|
776
|
-
console.log(pc.yellow(" Aviso: conflito ao restaurar mudancas locais"))
|
|
777
|
-
console.log(pc.dim(" Verifique ~/.lola e resolva manualmente: git stash pop"))
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
if (result.exitCode !== 0) {
|
|
782
|
-
console.log(pc.red(" Erro ao atualizar Lola"))
|
|
783
|
-
process.exit(1)
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
console.log(pc.green(" Atualizado!"))
|
|
787
|
-
} else {
|
|
788
|
-
console.log(pc.dim(` Clonando em ${LOLA_DIR}...`))
|
|
789
|
-
|
|
790
|
-
const result = Bun.spawnSync(["git", "clone", "--quiet", LOLA_REPO, LOLA_DIR], {
|
|
791
|
-
stdout: "inherit",
|
|
792
|
-
stderr: "inherit",
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
if (result.exitCode !== 0) {
|
|
796
|
-
console.log(pc.red(" Erro ao clonar repositorio"))
|
|
797
|
-
console.log(pc.dim(" Verifique se tem acesso ao repo nimbuslab/lola"))
|
|
798
|
-
process.exit(1)
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
console.log(pc.green(" Instalado!"))
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
async function setupClaude(): Promise<void> {
|
|
806
|
-
if (!hasClaudeCLI()) {
|
|
807
|
-
console.log(pc.yellow(" Claude CLI nao encontrado"))
|
|
808
|
-
console.log(pc.dim(" Instale: https://claude.ai/download"))
|
|
809
|
-
console.log(pc.dim(" Depois rode 'nimbus lola install' novamente"))
|
|
810
|
-
return
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
console.log(pc.green(" Claude CLI encontrado"))
|
|
814
|
-
|
|
815
|
-
// Criar comando lola
|
|
816
|
-
const isWindows = process.platform === "win32"
|
|
817
|
-
const lolaAgent = join(LOLA_DIR, "agents", "claude.md")
|
|
818
|
-
|
|
819
|
-
console.log()
|
|
820
|
-
console.log(pc.cyan(" Configurando comando lola..."))
|
|
821
|
-
|
|
822
|
-
if (isWindows) {
|
|
823
|
-
const ps5ProfileDir = join(HOME, "Documents", "WindowsPowerShell")
|
|
824
|
-
const ps7ProfileDir = join(HOME, "Documents", "PowerShell")
|
|
825
|
-
const profileName = "Microsoft.PowerShell_profile.ps1"
|
|
826
|
-
|
|
827
|
-
const lolaFunction = `
|
|
828
|
-
# Lola - Code Agent da nimbuslab
|
|
829
|
-
function lola {
|
|
830
|
-
param([Parameter(ValueFromRemainingArguments=$true)]$args)
|
|
831
|
-
$agent = "$env:USERPROFILE\\.lola\\agents\\claude.md"
|
|
832
|
-
if (Test-Path $agent) {
|
|
833
|
-
claude --append-system-prompt-file $agent @args
|
|
834
|
-
} else {
|
|
835
|
-
Write-Host "Agente Lola nao encontrado. Rode: nimbus lola install"
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
`
|
|
839
|
-
|
|
840
|
-
const profiles = [
|
|
841
|
-
{ dir: ps5ProfileDir, name: "PowerShell 5.x" },
|
|
842
|
-
{ dir: ps7ProfileDir, name: "PowerShell 7+" },
|
|
843
|
-
]
|
|
844
|
-
|
|
845
|
-
let addedToAny = false
|
|
846
|
-
|
|
847
|
-
for (const { dir, name } of profiles) {
|
|
848
|
-
const profilePath = join(dir, profileName)
|
|
849
|
-
|
|
850
|
-
if (!existsSync(dir)) {
|
|
851
|
-
await Bun.$`mkdir -p ${dir}`
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
let profileContent = ""
|
|
855
|
-
if (existsSync(profilePath)) {
|
|
856
|
-
profileContent = await Bun.file(profilePath).text()
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
if (!profileContent.includes("function lola")) {
|
|
860
|
-
await Bun.write(profilePath, profileContent + lolaFunction)
|
|
861
|
-
console.log(pc.green(` Funcao lola adicionada ao ${name} profile`))
|
|
862
|
-
addedToAny = true
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if (addedToAny) {
|
|
867
|
-
console.log(pc.yellow(" Reinicie o PowerShell para usar o comando 'lola'"))
|
|
868
|
-
} else {
|
|
869
|
-
console.log(pc.green(" Funcao lola ja existe"))
|
|
870
|
-
}
|
|
871
|
-
} else {
|
|
872
|
-
const binDir = join(HOME, ".local", "bin")
|
|
873
|
-
const lolaScript = join(binDir, "lola")
|
|
874
|
-
|
|
875
|
-
if (!existsSync(lolaScript)) {
|
|
876
|
-
await Bun.$`mkdir -p ${binDir}`
|
|
877
|
-
|
|
878
|
-
const unixScript = `#!/bin/bash
|
|
879
|
-
# lola - Code Agent da nimbuslab
|
|
880
|
-
LOLA_AGENT="${lolaAgent}"
|
|
881
|
-
|
|
882
|
-
if [[ "\$1" == "-h" || "\$1" == "--help" ]]; then
|
|
883
|
-
echo "lola - Code Agent da nimbuslab"
|
|
884
|
-
echo "Uso: lola [args]"
|
|
885
|
-
echo " lola Abre Claude CLI com a Lola"
|
|
886
|
-
echo " lola --resume Resume sessao anterior"
|
|
887
|
-
exit 0
|
|
888
|
-
fi
|
|
889
|
-
|
|
890
|
-
if [[ -f "\$LOLA_AGENT" ]]; then
|
|
891
|
-
exec claude --append-system-prompt-file "\$LOLA_AGENT" "\$@"
|
|
892
|
-
else
|
|
893
|
-
echo "Agente Lola nao encontrado: \$LOLA_AGENT"
|
|
894
|
-
echo "Rode: nimbus lola install"
|
|
895
|
-
exit 1
|
|
896
|
-
fi
|
|
897
|
-
`
|
|
898
|
-
await Bun.write(lolaScript, unixScript)
|
|
899
|
-
await Bun.$`chmod +x ${lolaScript}`
|
|
900
|
-
|
|
901
|
-
console.log(pc.green(` Script lola criado em ${lolaScript}`))
|
|
902
|
-
|
|
903
|
-
const pathEnv = process.env.PATH || ""
|
|
904
|
-
if (!pathEnv.includes(".local/bin")) {
|
|
905
|
-
console.log()
|
|
906
|
-
console.log(pc.yellow(" IMPORTANTE: Adicione ~/.local/bin ao seu PATH"))
|
|
907
|
-
console.log(pc.dim(" export PATH=\"$HOME/.local/bin:$PATH\""))
|
|
908
|
-
}
|
|
909
|
-
} else {
|
|
910
|
-
console.log(pc.green(" Script lola ja existe"))
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
// Instalar MCP lola-memory
|
|
915
|
-
await installLolaMemoryMCP()
|
|
916
|
-
|
|
917
|
-
// Verificar USER_MEMORY.md
|
|
918
|
-
const claudeDir = join(HOME, ".claude")
|
|
919
|
-
if (!existsSync(USER_MEMORY)) {
|
|
920
|
-
console.log()
|
|
921
|
-
console.log(pc.cyan(" Configurando USER_MEMORY.md..."))
|
|
922
|
-
|
|
923
|
-
const gitUserResult = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
|
|
924
|
-
const gitEmailResult = Bun.spawnSync(["git", "config", "user.email"], { stdout: "pipe" })
|
|
925
|
-
|
|
926
|
-
const gitUser = gitUserResult.stdout.toString().trim() || "Dev"
|
|
927
|
-
const gitEmail = gitEmailResult.stdout.toString().trim() || ""
|
|
928
|
-
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME || "local"
|
|
929
|
-
const today = new Date().toISOString().split("T")[0]
|
|
930
|
-
|
|
931
|
-
await Bun.$`mkdir -p ${claudeDir}`
|
|
932
|
-
|
|
933
|
-
const content = `# User Memory
|
|
934
|
-
|
|
935
|
-
Memoria persistente para sessoes Claude Code
|
|
936
|
-
|
|
937
|
-
---
|
|
938
|
-
|
|
939
|
-
## Dev
|
|
940
|
-
|
|
941
|
-
**Nome:** ${gitUser}
|
|
942
|
-
**Email:** ${gitEmail}
|
|
943
|
-
**Maquina:** ${hostname}
|
|
944
|
-
**Instalacao:** ${today}
|
|
945
|
-
|
|
946
|
-
---
|
|
947
|
-
|
|
948
|
-
## Configuracoes da Lola
|
|
949
|
-
|
|
950
|
-
\`\`\`
|
|
951
|
-
lola_profile: millennial
|
|
952
|
-
\`\`\`
|
|
953
|
-
|
|
954
|
-
---
|
|
955
|
-
|
|
956
|
-
## Ultima Sessao
|
|
957
|
-
|
|
958
|
-
(sera preenchido automaticamente)
|
|
959
|
-
|
|
960
|
-
---
|
|
961
|
-
`
|
|
962
|
-
await Bun.write(USER_MEMORY, content)
|
|
963
|
-
console.log(pc.green(` USER_MEMORY.md criado para ${gitUser}!`))
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
console.log()
|
|
967
|
-
console.log(pc.green(" Claude configurado!"))
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// Aliases para subcomandos
|
|
971
|
-
const SUBCOMMAND_ALIASES: Record<string, string> = {
|
|
972
|
-
i: "install",
|
|
973
|
-
s: "suggest",
|
|
974
|
-
o: "onboard",
|
|
975
|
-
q: "quiz",
|
|
976
|
-
h: "help",
|
|
977
|
-
sync: "install",
|
|
978
|
-
"--help": "help",
|
|
979
|
-
"-h": "help",
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
export async function lola(args: string[]) {
|
|
983
|
-
const rawSubcommand = args[0]
|
|
984
|
-
const subcommand = rawSubcommand ? (SUBCOMMAND_ALIASES[rawSubcommand] || rawSubcommand) : null
|
|
985
|
-
|
|
986
|
-
if (!subcommand || subcommand === "install") {
|
|
987
|
-
await installInteractive()
|
|
988
|
-
} else if (subcommand === "onboard") {
|
|
989
|
-
await onboardDev()
|
|
990
|
-
} else if (subcommand === "quiz") {
|
|
991
|
-
await runQuiz()
|
|
992
|
-
} else if (subcommand === "suggest") {
|
|
993
|
-
const message = args.slice(1).join(" ")
|
|
994
|
-
await suggestImprovement(message)
|
|
995
|
-
} else if (subcommand === "help") {
|
|
996
|
-
showLolaHelp()
|
|
997
|
-
} else {
|
|
998
|
-
console.log(pc.red(` Subcomando desconhecido: ${rawSubcommand}`))
|
|
999
|
-
showLolaHelp()
|
|
1000
|
-
process.exit(1)
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
function showLolaHelp() {
|
|
1005
|
-
console.log()
|
|
1006
|
-
console.log(pc.bold(" Lola - Code Agent da nimbuslab"))
|
|
1007
|
-
console.log()
|
|
1008
|
-
console.log(pc.bold(" Uso:") + " nimbus lola [subcomando]")
|
|
1009
|
-
console.log()
|
|
1010
|
-
console.log(pc.bold(" Subcomandos:"))
|
|
1011
|
-
console.log(pc.dim(" install, i ") + pc.white("Instalar/atualizar Lola + MCP"))
|
|
1012
|
-
console.log(pc.dim(" onboard, o ") + pc.white("Configurar perfil (novos devs)"))
|
|
1013
|
-
console.log(pc.dim(" quiz, q ") + pc.white("Quiz sobre a nimbuslab"))
|
|
1014
|
-
console.log(pc.dim(" suggest, s ") + pc.white("Sugerir melhoria (cria issue)"))
|
|
1015
|
-
console.log(pc.dim(" help, h ") + pc.white("Mostrar esta ajuda"))
|
|
1016
|
-
console.log()
|
|
1017
|
-
console.log(pc.bold(" Exemplos:"))
|
|
1018
|
-
console.log(pc.dim(" $ nimbus lola i"))
|
|
1019
|
-
console.log(pc.dim(" $ nimbus lola o"))
|
|
1020
|
-
console.log(pc.dim(" $ nimbus lola q"))
|
|
1021
|
-
console.log(pc.dim(" $ nimbus lola s \"adicionar suporte a X\""))
|
|
1022
|
-
console.log()
|
|
1023
|
-
console.log(pc.bold(" lola-memory (dentro do Claude):"))
|
|
1024
|
-
console.log(pc.dim(" remember \"query\" ") + pc.white("Buscar conhecimento"))
|
|
1025
|
-
console.log(pc.dim(" learn \"content\" ") + pc.white("Salvar conhecimento"))
|
|
1026
|
-
console.log(pc.dim(" memory_stats ") + pc.white("Ver estatisticas"))
|
|
1027
|
-
console.log(pc.dim(" get_context ") + pc.white("Perfil + memorias recentes"))
|
|
1028
|
-
console.log()
|
|
1029
|
-
}
|