@nimbuslab/cli 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +380 -176
- package/package.json +1 -1
- package/src/commands/lola.ts +476 -196
package/src/commands/lola.ts
CHANGED
|
@@ -11,6 +11,24 @@ const USER_MEMORY = join(HOME, ".claude", "USER_MEMORY.md")
|
|
|
11
11
|
const LOLA_MEMORY_URL = "https://lola.nimbuslab.com.br/sse"
|
|
12
12
|
const LOLA_MEMORY_NAME = "lola-memory"
|
|
13
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
|
+
// Helpers de detecção
|
|
22
|
+
function hasClaudeCLI(): boolean {
|
|
23
|
+
const result = Bun.spawnSync(["which", "claude"], { stdout: "pipe" })
|
|
24
|
+
return result.exitCode === 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hasGeminiCLI(): boolean {
|
|
28
|
+
const result = Bun.spawnSync(["which", "gemini"], { stdout: "pipe" })
|
|
29
|
+
return result.exitCode === 0
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
async function installLolaMemoryMCP(): Promise<void> {
|
|
15
33
|
console.log()
|
|
16
34
|
console.log(pc.cyan(" Configurando lola-memory MCP..."))
|
|
@@ -61,261 +79,206 @@ async function installLolaMemoryMCP(): Promise<void> {
|
|
|
61
79
|
console.log(pc.dim(" memory_stats - Ver estatisticas"))
|
|
62
80
|
}
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
// ============================================
|
|
83
|
+
// GEMINI CLI
|
|
84
|
+
// ============================================
|
|
85
|
+
|
|
86
|
+
async function installGeminiCLI(): Promise<boolean> {
|
|
68
87
|
console.log()
|
|
88
|
+
console.log(pc.cyan(" Instalando Gemini CLI..."))
|
|
69
89
|
|
|
70
|
-
const
|
|
90
|
+
const result = Bun.spawnSync(["npm", "install", "-g", "@google/gemini-cli"], {
|
|
91
|
+
stdout: "inherit",
|
|
92
|
+
stderr: "inherit",
|
|
93
|
+
})
|
|
71
94
|
|
|
72
|
-
if (
|
|
73
|
-
console.log(pc.
|
|
74
|
-
console.log(pc.
|
|
95
|
+
if (result.exitCode !== 0) {
|
|
96
|
+
console.log(pc.red(" Erro ao instalar Gemini CLI"))
|
|
97
|
+
console.log(pc.dim(" Tente manualmente: npm install -g @google/gemini-cli"))
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
75
100
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
stdout: "pipe",
|
|
80
|
-
})
|
|
81
|
-
const hasLocalChanges = statusCheck.stdout.toString().trim().length > 0
|
|
101
|
+
console.log(pc.green(" Gemini CLI instalado!"))
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
82
104
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Bun.spawnSync(["git", "stash", "--quiet"], {
|
|
87
|
-
cwd: LOLA_DIR,
|
|
88
|
-
stdout: "pipe",
|
|
89
|
-
stderr: "pipe",
|
|
90
|
-
})
|
|
91
|
-
}
|
|
105
|
+
async function installGeminiMCP(): Promise<void> {
|
|
106
|
+
console.log()
|
|
107
|
+
console.log(pc.cyan(" Configurando MCP no Gemini..."))
|
|
92
108
|
|
|
93
|
-
|
|
94
|
-
const result = Bun.spawnSync(["git", "pull", "--quiet"], {
|
|
95
|
-
cwd: LOLA_DIR,
|
|
96
|
-
stdout: "inherit",
|
|
97
|
-
stderr: "inherit",
|
|
98
|
-
})
|
|
109
|
+
const settingsPath = join(GEMINI_DIR, "settings.json")
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
cwd: LOLA_DIR,
|
|
105
|
-
stdout: "pipe",
|
|
106
|
-
stderr: "pipe",
|
|
107
|
-
})
|
|
111
|
+
// Criar diretório se não existir
|
|
112
|
+
if (!existsSync(GEMINI_DIR)) {
|
|
113
|
+
await Bun.$`mkdir -p ${GEMINI_DIR}`
|
|
114
|
+
}
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
// Ler settings existente ou criar novo
|
|
117
|
+
let settings: Record<string, unknown> = {}
|
|
118
|
+
if (existsSync(settingsPath)) {
|
|
119
|
+
try {
|
|
120
|
+
const content = await Bun.file(settingsPath).text()
|
|
121
|
+
settings = JSON.parse(content)
|
|
122
|
+
} catch {
|
|
123
|
+
// Se falhar, começa do zero
|
|
113
124
|
}
|
|
125
|
+
}
|
|
114
126
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
// Verificar se já tem lola-memory configurado
|
|
128
|
+
const mcpServers = (settings.mcpServers || {}) as Record<string, unknown>
|
|
129
|
+
if (mcpServers[LOLA_MEMORY_NAME]) {
|
|
130
|
+
console.log(pc.green(" MCP lola-memory ja configurado no Gemini"))
|
|
131
|
+
return
|
|
132
|
+
}
|
|
119
133
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
134
|
+
// Adicionar lola-memory
|
|
135
|
+
mcpServers[LOLA_MEMORY_NAME] = {
|
|
136
|
+
url: LOLA_MEMORY_URL
|
|
137
|
+
}
|
|
138
|
+
settings.mcpServers = mcpServers
|
|
123
139
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
140
|
+
// Salvar
|
|
141
|
+
await Bun.write(settingsPath, JSON.stringify(settings, null, 2))
|
|
142
|
+
console.log(pc.green(" MCP lola-memory adicionado ao Gemini!"))
|
|
143
|
+
}
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
process.exit(1)
|
|
133
|
-
}
|
|
145
|
+
async function installGeminiSystemPrompt(): Promise<void> {
|
|
146
|
+
console.log()
|
|
147
|
+
console.log(pc.cyan(" Configurando GEMINI.md..."))
|
|
134
148
|
|
|
135
|
-
|
|
149
|
+
// Criar diretório se não existir
|
|
150
|
+
if (!existsSync(GEMINI_DIR)) {
|
|
151
|
+
await Bun.$`mkdir -p ${GEMINI_DIR}`
|
|
136
152
|
}
|
|
137
153
|
|
|
138
|
-
//
|
|
139
|
-
const isWindows = process.platform === "win32"
|
|
154
|
+
// Copiar o agent da Lola (adaptar claude.md para gemini)
|
|
140
155
|
const lolaAgent = join(LOLA_DIR, "agents", "claude.md")
|
|
141
156
|
|
|
157
|
+
if (!existsSync(lolaAgent)) {
|
|
158
|
+
console.log(pc.yellow(" Agent Lola nao encontrado"))
|
|
159
|
+
console.log(pc.dim(" Rode 'nimbus lola install' primeiro"))
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Ler o agent do Claude
|
|
164
|
+
let content = await Bun.file(lolaAgent).text()
|
|
165
|
+
|
|
166
|
+
// Remover frontmatter do Claude (não suportado no Gemini)
|
|
167
|
+
content = content.replace(/^---[\s\S]*?---\n/, "")
|
|
168
|
+
|
|
169
|
+
// Adaptar menções específicas
|
|
170
|
+
content = content.replace(/Claude Code/g, "Gemini CLI")
|
|
171
|
+
content = content.replace(/Claude CLI/g, "Gemini CLI")
|
|
172
|
+
|
|
173
|
+
// Adicionar header para Gemini
|
|
174
|
+
const geminiContent = `# Lola - Code Agent (Gemini)
|
|
175
|
+
|
|
176
|
+
> Este arquivo configura a Lola para o Gemini CLI
|
|
177
|
+
> Gerado automaticamente por: nimbus lola install
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
${content}`
|
|
182
|
+
|
|
183
|
+
await Bun.write(GEMINI_SETTINGS, geminiContent)
|
|
184
|
+
console.log(pc.green(" GEMINI.md criado!"))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function createGeminiCommand(): Promise<void> {
|
|
188
|
+
const isWindows = process.platform === "win32"
|
|
189
|
+
const lolaAgent = GEMINI_SETTINGS
|
|
190
|
+
|
|
142
191
|
console.log()
|
|
143
|
-
console.log(pc.cyan(" Configurando comando lola..."))
|
|
192
|
+
console.log(pc.cyan(" Configurando comando lola-gemini..."))
|
|
144
193
|
|
|
145
194
|
if (isWindows) {
|
|
146
195
|
// Windows: adicionar função ao PowerShell profile
|
|
147
|
-
// PowerShell 5.x usa WindowsPowerShell, PowerShell 7+ usa PowerShell
|
|
148
|
-
const ps5ProfileDir = join(HOME, "Documents", "WindowsPowerShell")
|
|
149
196
|
const ps7ProfileDir = join(HOME, "Documents", "PowerShell")
|
|
150
197
|
const profileName = "Microsoft.PowerShell_profile.ps1"
|
|
198
|
+
const profilePath = join(ps7ProfileDir, profileName)
|
|
151
199
|
|
|
152
|
-
const
|
|
153
|
-
# Lola - Code Agent da nimbuslab
|
|
154
|
-
function lola {
|
|
200
|
+
const lolaGeminiFunction = `
|
|
201
|
+
# Lola (Gemini) - Code Agent da nimbuslab
|
|
202
|
+
function lola-gemini {
|
|
155
203
|
param([Parameter(ValueFromRemainingArguments=$true)]$args)
|
|
156
|
-
|
|
157
|
-
if (Test-Path $agent) {
|
|
158
|
-
claude --append-system-prompt-file $agent @args
|
|
159
|
-
} else {
|
|
160
|
-
Write-Host "Agente Lola nao encontrado. Rode: nimbus lola install"
|
|
161
|
-
}
|
|
204
|
+
gemini @args
|
|
162
205
|
}
|
|
163
206
|
`
|
|
164
207
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{ dir: ps7ProfileDir, name: "PowerShell 7+" },
|
|
169
|
-
]
|
|
170
|
-
|
|
171
|
-
let addedToAny = false
|
|
172
|
-
|
|
173
|
-
for (const { dir, name } of profiles) {
|
|
174
|
-
const profilePath = join(dir, profileName)
|
|
175
|
-
|
|
176
|
-
// Criar diretório se não existir
|
|
177
|
-
if (!existsSync(dir)) {
|
|
178
|
-
await Bun.$`mkdir -p ${dir}`
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Verificar se já tem a função no profile
|
|
182
|
-
let profileContent = ""
|
|
183
|
-
if (existsSync(profilePath)) {
|
|
184
|
-
profileContent = await Bun.file(profilePath).text()
|
|
185
|
-
}
|
|
208
|
+
if (!existsSync(ps7ProfileDir)) {
|
|
209
|
+
await Bun.$`mkdir -p ${ps7ProfileDir}`
|
|
210
|
+
}
|
|
186
211
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
addedToAny = true
|
|
191
|
-
}
|
|
212
|
+
let profileContent = ""
|
|
213
|
+
if (existsSync(profilePath)) {
|
|
214
|
+
profileContent = await Bun.file(profilePath).text()
|
|
192
215
|
}
|
|
193
216
|
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
console.log()
|
|
197
|
-
console.log(pc.yellow(" IMPORTANTE (Windows):"))
|
|
198
|
-
console.log(pc.dim(" Se o comando 'lola' nao funcionar, execute primeiro:"))
|
|
199
|
-
console.log(pc.cyan(" Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser"))
|
|
200
|
-
console.log(pc.dim(" Isso habilita execucao de scripts no PowerShell."))
|
|
217
|
+
if (!profileContent.includes("function lola-gemini")) {
|
|
218
|
+
await Bun.write(profilePath, profileContent + lolaGeminiFunction)
|
|
219
|
+
console.log(pc.green(" Funcao lola-gemini adicionada ao PowerShell"))
|
|
201
220
|
} else {
|
|
202
|
-
console.log(pc.green(" Funcao lola ja existe
|
|
221
|
+
console.log(pc.green(" Funcao lola-gemini ja existe"))
|
|
203
222
|
}
|
|
204
223
|
} else {
|
|
205
224
|
// Linux/macOS: criar script em ~/.local/bin
|
|
206
225
|
const binDir = join(HOME, ".local", "bin")
|
|
207
|
-
const
|
|
226
|
+
const lolaGeminiScript = join(binDir, "lola-gemini")
|
|
208
227
|
|
|
209
|
-
if (!existsSync(
|
|
228
|
+
if (!existsSync(lolaGeminiScript)) {
|
|
210
229
|
await Bun.$`mkdir -p ${binDir}`
|
|
211
230
|
|
|
212
231
|
const unixScript = `#!/bin/bash
|
|
213
|
-
# lola - Code Agent da nimbuslab
|
|
214
|
-
LOLA_AGENT="${lolaAgent}"
|
|
232
|
+
# lola-gemini - Code Agent da nimbuslab (Gemini)
|
|
215
233
|
|
|
216
234
|
if [[ "\$1" == "-h" || "\$1" == "--help" ]]; then
|
|
217
|
-
echo "lola - Code Agent da nimbuslab"
|
|
218
|
-
echo "Uso: lola [args]"
|
|
219
|
-
echo " lola
|
|
220
|
-
echo " lola --resume Resume sessao anterior"
|
|
235
|
+
echo "lola-gemini - Code Agent da nimbuslab (Gemini)"
|
|
236
|
+
echo "Uso: lola-gemini [args]"
|
|
237
|
+
echo " lola-gemini Abre Gemini CLI com a Lola"
|
|
221
238
|
exit 0
|
|
222
239
|
fi
|
|
223
240
|
|
|
224
|
-
|
|
225
|
-
exec claude --append-system-prompt-file "\$LOLA_AGENT" "\$@"
|
|
226
|
-
else
|
|
227
|
-
echo "Agente Lola nao encontrado: \$LOLA_AGENT"
|
|
228
|
-
echo "Rode: nimbus lola install"
|
|
229
|
-
exit 1
|
|
230
|
-
fi
|
|
241
|
+
exec gemini "\$@"
|
|
231
242
|
`
|
|
232
|
-
await Bun.write(
|
|
233
|
-
await Bun.$`chmod +x ${
|
|
243
|
+
await Bun.write(lolaGeminiScript, unixScript)
|
|
244
|
+
await Bun.$`chmod +x ${lolaGeminiScript}`
|
|
234
245
|
|
|
235
|
-
console.log(pc.green(` Script lola criado em ${
|
|
236
|
-
|
|
237
|
-
// Verificar se ~/.local/bin está no PATH
|
|
238
|
-
const pathEnv = process.env.PATH || ""
|
|
239
|
-
if (!pathEnv.includes(".local/bin")) {
|
|
240
|
-
console.log()
|
|
241
|
-
console.log(pc.yellow(" IMPORTANTE: Adicione ~/.local/bin ao seu PATH"))
|
|
242
|
-
console.log(pc.dim(" Adicione ao seu ~/.bashrc ou ~/.zshrc:"))
|
|
243
|
-
console.log(pc.dim(" export PATH=\"$HOME/.local/bin:$PATH\""))
|
|
244
|
-
}
|
|
246
|
+
console.log(pc.green(` Script lola-gemini criado em ${lolaGeminiScript}`))
|
|
245
247
|
} else {
|
|
246
|
-
console.log(pc.green(" Script lola ja existe"))
|
|
248
|
+
console.log(pc.green(" Script lola-gemini ja existe"))
|
|
247
249
|
}
|
|
248
250
|
}
|
|
251
|
+
}
|
|
249
252
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
console.log()
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// Pegar info do git automaticamente
|
|
260
|
-
const gitUserResult = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
|
|
261
|
-
const gitEmailResult = Bun.spawnSync(["git", "config", "user.email"], { stdout: "pipe" })
|
|
262
|
-
|
|
263
|
-
const gitUser = gitUserResult.stdout.toString().trim() || "Dev"
|
|
264
|
-
const gitEmail = gitEmailResult.stdout.toString().trim() || ""
|
|
265
|
-
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME || "local"
|
|
266
|
-
const today = new Date().toISOString().split("T")[0]
|
|
267
|
-
|
|
268
|
-
await Bun.$`mkdir -p ${claudeDir}`
|
|
269
|
-
|
|
270
|
-
const content = `# User Memory
|
|
271
|
-
|
|
272
|
-
Memoria persistente para sessoes Claude Code
|
|
273
|
-
|
|
274
|
-
---
|
|
275
|
-
|
|
276
|
-
## Dev
|
|
277
|
-
|
|
278
|
-
**Nome:** ${gitUser}
|
|
279
|
-
**Email:** ${gitEmail}
|
|
280
|
-
**Maquina:** ${hostname}
|
|
281
|
-
**Instalacao:** ${today}
|
|
282
|
-
|
|
283
|
-
---
|
|
284
|
-
|
|
285
|
-
## Configuracoes da Lola
|
|
286
|
-
|
|
287
|
-
\`\`\`
|
|
288
|
-
lola_profile: millennial
|
|
289
|
-
\`\`\`
|
|
290
|
-
|
|
291
|
-
---
|
|
253
|
+
async function setupGemini(): Promise<void> {
|
|
254
|
+
// Verificar/instalar Gemini CLI
|
|
255
|
+
if (!hasGeminiCLI()) {
|
|
256
|
+
const installed = await installGeminiCLI()
|
|
257
|
+
if (!installed) return
|
|
258
|
+
} else {
|
|
259
|
+
console.log(pc.green(" Gemini CLI ja instalado"))
|
|
260
|
+
}
|
|
292
261
|
|
|
293
|
-
|
|
262
|
+
// Configurar MCP
|
|
263
|
+
await installGeminiMCP()
|
|
294
264
|
|
|
295
|
-
|
|
265
|
+
// Configurar GEMINI.md
|
|
266
|
+
await installGeminiSystemPrompt()
|
|
296
267
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
await Bun.write(USER_MEMORY, content)
|
|
300
|
-
console.log(pc.green(` USER_MEMORY.md criado para ${gitUser}!`))
|
|
301
|
-
}
|
|
268
|
+
// Criar comando lola-gemini
|
|
269
|
+
await createGeminiCommand()
|
|
302
270
|
|
|
303
271
|
console.log()
|
|
304
|
-
console.log(pc.green("
|
|
305
|
-
console.log()
|
|
306
|
-
console.log(pc.bold(" Para usar a Lola:"))
|
|
307
|
-
console.log(pc.dim(" lola ") + pc.white("# Iniciar sessao"))
|
|
308
|
-
console.log()
|
|
309
|
-
console.log(pc.bold(" lola-memory (conhecimento compartilhado):"))
|
|
310
|
-
console.log(pc.dim(" remember \"query\" ") + pc.white("# Buscar conhecimento"))
|
|
311
|
-
console.log(pc.dim(" learn \"content\" ") + pc.white("# Salvar conhecimento"))
|
|
312
|
-
console.log(pc.dim(" memory_stats ") + pc.white("# Ver estatisticas"))
|
|
272
|
+
console.log(pc.green(" Gemini configurado!"))
|
|
313
273
|
console.log()
|
|
314
|
-
console.log(pc.bold(" Para
|
|
315
|
-
console.log(pc.dim("
|
|
274
|
+
console.log(pc.bold(" Para usar:"))
|
|
275
|
+
console.log(pc.dim(" lola-gemini ") + pc.white("# Iniciar sessao com Gemini"))
|
|
276
|
+
console.log(pc.dim(" gemini ") + pc.white("# Gemini puro (sem Lola)"))
|
|
316
277
|
console.log()
|
|
278
|
+
console.log(pc.dim(" Na primeira execucao, faca login com sua conta Google."))
|
|
317
279
|
}
|
|
318
280
|
|
|
281
|
+
|
|
319
282
|
async function suggestImprovement(message: string): Promise<void> {
|
|
320
283
|
if (!message) {
|
|
321
284
|
console.log(pc.red(" Erro: forneça uma mensagem"))
|
|
@@ -427,7 +390,7 @@ async function onboardDev(): Promise<void> {
|
|
|
427
390
|
if (!existsSync(LOLA_DIR)) {
|
|
428
391
|
console.log(pc.yellow(" Lola nao instalada. Instalando primeiro..."))
|
|
429
392
|
console.log()
|
|
430
|
-
await
|
|
393
|
+
await installLolaBase()
|
|
431
394
|
console.log()
|
|
432
395
|
}
|
|
433
396
|
|
|
@@ -684,11 +647,328 @@ async function runQuiz(): Promise<void> {
|
|
|
684
647
|
console.log()
|
|
685
648
|
}
|
|
686
649
|
|
|
650
|
+
async function installInteractive(): Promise<void> {
|
|
651
|
+
console.log()
|
|
652
|
+
console.log(pc.cyan(" Lola - Code Agent da nimbuslab"))
|
|
653
|
+
console.log(pc.dim(" ==============================="))
|
|
654
|
+
console.log()
|
|
655
|
+
|
|
656
|
+
// Detectar CLIs instaladas
|
|
657
|
+
const hasClaude = hasClaudeCLI()
|
|
658
|
+
const hasGemini = hasGeminiCLI()
|
|
659
|
+
|
|
660
|
+
console.log(pc.dim(" Detectando agents..."))
|
|
661
|
+
console.log(pc.dim(` Claude CLI: ${hasClaude ? pc.green("instalado") : pc.yellow("nao encontrado")}`))
|
|
662
|
+
console.log(pc.dim(` Gemini CLI: ${hasGemini ? pc.green("instalado") : pc.yellow("nao encontrado")}`))
|
|
663
|
+
console.log()
|
|
664
|
+
|
|
665
|
+
// Perguntar qual agent configurar
|
|
666
|
+
const agentChoice = await p.select({
|
|
667
|
+
message: "Qual agent deseja configurar?",
|
|
668
|
+
options: [
|
|
669
|
+
{
|
|
670
|
+
value: "all",
|
|
671
|
+
label: "Todos (Recomendado)",
|
|
672
|
+
hint: "Configura Claude e Gemini",
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
value: "claude",
|
|
676
|
+
label: "Apenas Claude",
|
|
677
|
+
hint: hasClaude ? "Ja instalado" : "Sera instalado",
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
value: "gemini",
|
|
681
|
+
label: "Apenas Gemini",
|
|
682
|
+
hint: hasGemini ? "Ja instalado" : "Sera instalado",
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
if (p.isCancel(agentChoice)) {
|
|
688
|
+
p.cancel("Instalacao cancelada")
|
|
689
|
+
process.exit(0)
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const choice = agentChoice as AgentType
|
|
693
|
+
|
|
694
|
+
// Instalar ~/.lola primeiro (necessário para ambos)
|
|
695
|
+
await installLolaBase()
|
|
696
|
+
|
|
697
|
+
// Configurar agents escolhidos
|
|
698
|
+
if (choice === "claude" || choice === "all") {
|
|
699
|
+
console.log()
|
|
700
|
+
console.log(pc.bgBlue(pc.white(" CLAUDE ")))
|
|
701
|
+
await setupClaude()
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (choice === "gemini" || choice === "all") {
|
|
705
|
+
console.log()
|
|
706
|
+
console.log(pc.bgMagenta(pc.white(" GEMINI ")))
|
|
707
|
+
await setupGemini()
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Resumo final
|
|
711
|
+
console.log()
|
|
712
|
+
console.log(pc.green(" ==============================="))
|
|
713
|
+
console.log(pc.green(" Instalacao concluida!"))
|
|
714
|
+
console.log(pc.green(" ==============================="))
|
|
715
|
+
console.log()
|
|
716
|
+
|
|
717
|
+
if (choice === "claude" || choice === "all") {
|
|
718
|
+
console.log(pc.dim(" lola ") + pc.white("# Claude com Lola"))
|
|
719
|
+
}
|
|
720
|
+
if (choice === "gemini" || choice === "all") {
|
|
721
|
+
console.log(pc.dim(" lola-gemini ") + pc.white("# Gemini com Lola"))
|
|
722
|
+
}
|
|
723
|
+
console.log()
|
|
724
|
+
console.log(pc.bold(" lola-memory (compartilhado entre agents):"))
|
|
725
|
+
console.log(pc.dim(" remember, learn, memory_stats"))
|
|
726
|
+
console.log()
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async function installLolaBase(): Promise<void> {
|
|
730
|
+
console.log()
|
|
731
|
+
console.log(pc.cyan(" Instalando base Lola (~/.lola)..."))
|
|
732
|
+
|
|
733
|
+
const isUpdate = existsSync(LOLA_DIR)
|
|
734
|
+
|
|
735
|
+
if (isUpdate) {
|
|
736
|
+
console.log(pc.dim(` Lola ja instalada em ${LOLA_DIR}`))
|
|
737
|
+
console.log(pc.dim(" Atualizando..."))
|
|
738
|
+
|
|
739
|
+
// Verificar se tem mudancas locais
|
|
740
|
+
const statusCheck = Bun.spawnSync(["git", "status", "--porcelain"], {
|
|
741
|
+
cwd: LOLA_DIR,
|
|
742
|
+
stdout: "pipe",
|
|
743
|
+
})
|
|
744
|
+
const hasLocalChanges = statusCheck.stdout.toString().trim().length > 0
|
|
745
|
+
|
|
746
|
+
// Stash se tiver mudancas locais
|
|
747
|
+
if (hasLocalChanges) {
|
|
748
|
+
console.log(pc.dim(" Salvando mudancas locais..."))
|
|
749
|
+
Bun.spawnSync(["git", "stash", "--quiet"], {
|
|
750
|
+
cwd: LOLA_DIR,
|
|
751
|
+
stdout: "pipe",
|
|
752
|
+
stderr: "pipe",
|
|
753
|
+
})
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Pull
|
|
757
|
+
const result = Bun.spawnSync(["git", "pull", "--quiet"], {
|
|
758
|
+
cwd: LOLA_DIR,
|
|
759
|
+
stdout: "inherit",
|
|
760
|
+
stderr: "inherit",
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
// Restaurar mudancas locais se tinha
|
|
764
|
+
if (hasLocalChanges) {
|
|
765
|
+
console.log(pc.dim(" Restaurando mudancas locais..."))
|
|
766
|
+
const stashPop = Bun.spawnSync(["git", "stash", "pop", "--quiet"], {
|
|
767
|
+
cwd: LOLA_DIR,
|
|
768
|
+
stdout: "pipe",
|
|
769
|
+
stderr: "pipe",
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
if (stashPop.exitCode !== 0) {
|
|
773
|
+
console.log(pc.yellow(" Aviso: conflito ao restaurar mudancas locais"))
|
|
774
|
+
console.log(pc.dim(" Verifique ~/.lola e resolva manualmente: git stash pop"))
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (result.exitCode !== 0) {
|
|
779
|
+
console.log(pc.red(" Erro ao atualizar Lola"))
|
|
780
|
+
process.exit(1)
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
console.log(pc.green(" Atualizado!"))
|
|
784
|
+
} else {
|
|
785
|
+
console.log(pc.dim(` Clonando em ${LOLA_DIR}...`))
|
|
786
|
+
|
|
787
|
+
const result = Bun.spawnSync(["git", "clone", "--quiet", LOLA_REPO, LOLA_DIR], {
|
|
788
|
+
stdout: "inherit",
|
|
789
|
+
stderr: "inherit",
|
|
790
|
+
})
|
|
791
|
+
|
|
792
|
+
if (result.exitCode !== 0) {
|
|
793
|
+
console.log(pc.red(" Erro ao clonar repositorio"))
|
|
794
|
+
console.log(pc.dim(" Verifique se tem acesso ao repo nimbuslab/lola"))
|
|
795
|
+
process.exit(1)
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
console.log(pc.green(" Instalado!"))
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
async function setupClaude(): Promise<void> {
|
|
803
|
+
if (!hasClaudeCLI()) {
|
|
804
|
+
console.log(pc.yellow(" Claude CLI nao encontrado"))
|
|
805
|
+
console.log(pc.dim(" Instale: https://claude.ai/download"))
|
|
806
|
+
console.log(pc.dim(" Depois rode 'nimbus lola install' novamente"))
|
|
807
|
+
return
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
console.log(pc.green(" Claude CLI encontrado"))
|
|
811
|
+
|
|
812
|
+
// Criar comando lola
|
|
813
|
+
const isWindows = process.platform === "win32"
|
|
814
|
+
const lolaAgent = join(LOLA_DIR, "agents", "claude.md")
|
|
815
|
+
|
|
816
|
+
console.log()
|
|
817
|
+
console.log(pc.cyan(" Configurando comando lola..."))
|
|
818
|
+
|
|
819
|
+
if (isWindows) {
|
|
820
|
+
const ps5ProfileDir = join(HOME, "Documents", "WindowsPowerShell")
|
|
821
|
+
const ps7ProfileDir = join(HOME, "Documents", "PowerShell")
|
|
822
|
+
const profileName = "Microsoft.PowerShell_profile.ps1"
|
|
823
|
+
|
|
824
|
+
const lolaFunction = `
|
|
825
|
+
# Lola - Code Agent da nimbuslab
|
|
826
|
+
function lola {
|
|
827
|
+
param([Parameter(ValueFromRemainingArguments=$true)]$args)
|
|
828
|
+
$agent = "$env:USERPROFILE\\.lola\\agents\\claude.md"
|
|
829
|
+
if (Test-Path $agent) {
|
|
830
|
+
claude --append-system-prompt-file $agent @args
|
|
831
|
+
} else {
|
|
832
|
+
Write-Host "Agente Lola nao encontrado. Rode: nimbus lola install"
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
`
|
|
836
|
+
|
|
837
|
+
const profiles = [
|
|
838
|
+
{ dir: ps5ProfileDir, name: "PowerShell 5.x" },
|
|
839
|
+
{ dir: ps7ProfileDir, name: "PowerShell 7+" },
|
|
840
|
+
]
|
|
841
|
+
|
|
842
|
+
let addedToAny = false
|
|
843
|
+
|
|
844
|
+
for (const { dir, name } of profiles) {
|
|
845
|
+
const profilePath = join(dir, profileName)
|
|
846
|
+
|
|
847
|
+
if (!existsSync(dir)) {
|
|
848
|
+
await Bun.$`mkdir -p ${dir}`
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
let profileContent = ""
|
|
852
|
+
if (existsSync(profilePath)) {
|
|
853
|
+
profileContent = await Bun.file(profilePath).text()
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
if (!profileContent.includes("function lola")) {
|
|
857
|
+
await Bun.write(profilePath, profileContent + lolaFunction)
|
|
858
|
+
console.log(pc.green(` Funcao lola adicionada ao ${name} profile`))
|
|
859
|
+
addedToAny = true
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (addedToAny) {
|
|
864
|
+
console.log(pc.yellow(" Reinicie o PowerShell para usar o comando 'lola'"))
|
|
865
|
+
} else {
|
|
866
|
+
console.log(pc.green(" Funcao lola ja existe"))
|
|
867
|
+
}
|
|
868
|
+
} else {
|
|
869
|
+
const binDir = join(HOME, ".local", "bin")
|
|
870
|
+
const lolaScript = join(binDir, "lola")
|
|
871
|
+
|
|
872
|
+
if (!existsSync(lolaScript)) {
|
|
873
|
+
await Bun.$`mkdir -p ${binDir}`
|
|
874
|
+
|
|
875
|
+
const unixScript = `#!/bin/bash
|
|
876
|
+
# lola - Code Agent da nimbuslab
|
|
877
|
+
LOLA_AGENT="${lolaAgent}"
|
|
878
|
+
|
|
879
|
+
if [[ "\$1" == "-h" || "\$1" == "--help" ]]; then
|
|
880
|
+
echo "lola - Code Agent da nimbuslab"
|
|
881
|
+
echo "Uso: lola [args]"
|
|
882
|
+
echo " lola Abre Claude CLI com a Lola"
|
|
883
|
+
echo " lola --resume Resume sessao anterior"
|
|
884
|
+
exit 0
|
|
885
|
+
fi
|
|
886
|
+
|
|
887
|
+
if [[ -f "\$LOLA_AGENT" ]]; then
|
|
888
|
+
exec claude --append-system-prompt-file "\$LOLA_AGENT" "\$@"
|
|
889
|
+
else
|
|
890
|
+
echo "Agente Lola nao encontrado: \$LOLA_AGENT"
|
|
891
|
+
echo "Rode: nimbus lola install"
|
|
892
|
+
exit 1
|
|
893
|
+
fi
|
|
894
|
+
`
|
|
895
|
+
await Bun.write(lolaScript, unixScript)
|
|
896
|
+
await Bun.$`chmod +x ${lolaScript}`
|
|
897
|
+
|
|
898
|
+
console.log(pc.green(` Script lola criado em ${lolaScript}`))
|
|
899
|
+
|
|
900
|
+
const pathEnv = process.env.PATH || ""
|
|
901
|
+
if (!pathEnv.includes(".local/bin")) {
|
|
902
|
+
console.log()
|
|
903
|
+
console.log(pc.yellow(" IMPORTANTE: Adicione ~/.local/bin ao seu PATH"))
|
|
904
|
+
console.log(pc.dim(" export PATH=\"$HOME/.local/bin:$PATH\""))
|
|
905
|
+
}
|
|
906
|
+
} else {
|
|
907
|
+
console.log(pc.green(" Script lola ja existe"))
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Instalar MCP lola-memory
|
|
912
|
+
await installLolaMemoryMCP()
|
|
913
|
+
|
|
914
|
+
// Verificar USER_MEMORY.md
|
|
915
|
+
const claudeDir = join(HOME, ".claude")
|
|
916
|
+
if (!existsSync(USER_MEMORY)) {
|
|
917
|
+
console.log()
|
|
918
|
+
console.log(pc.cyan(" Configurando USER_MEMORY.md..."))
|
|
919
|
+
|
|
920
|
+
const gitUserResult = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
|
|
921
|
+
const gitEmailResult = Bun.spawnSync(["git", "config", "user.email"], { stdout: "pipe" })
|
|
922
|
+
|
|
923
|
+
const gitUser = gitUserResult.stdout.toString().trim() || "Dev"
|
|
924
|
+
const gitEmail = gitEmailResult.stdout.toString().trim() || ""
|
|
925
|
+
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME || "local"
|
|
926
|
+
const today = new Date().toISOString().split("T")[0]
|
|
927
|
+
|
|
928
|
+
await Bun.$`mkdir -p ${claudeDir}`
|
|
929
|
+
|
|
930
|
+
const content = `# User Memory
|
|
931
|
+
|
|
932
|
+
Memoria persistente para sessoes Claude Code
|
|
933
|
+
|
|
934
|
+
---
|
|
935
|
+
|
|
936
|
+
## Dev
|
|
937
|
+
|
|
938
|
+
**Nome:** ${gitUser}
|
|
939
|
+
**Email:** ${gitEmail}
|
|
940
|
+
**Maquina:** ${hostname}
|
|
941
|
+
**Instalacao:** ${today}
|
|
942
|
+
|
|
943
|
+
---
|
|
944
|
+
|
|
945
|
+
## Configuracoes da Lola
|
|
946
|
+
|
|
947
|
+
\`\`\`
|
|
948
|
+
lola_profile: millennial
|
|
949
|
+
\`\`\`
|
|
950
|
+
|
|
951
|
+
---
|
|
952
|
+
|
|
953
|
+
## Ultima Sessao
|
|
954
|
+
|
|
955
|
+
(sera preenchido automaticamente)
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
`
|
|
959
|
+
await Bun.write(USER_MEMORY, content)
|
|
960
|
+
console.log(pc.green(` USER_MEMORY.md criado para ${gitUser}!`))
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
console.log()
|
|
964
|
+
console.log(pc.green(" Claude configurado!"))
|
|
965
|
+
}
|
|
966
|
+
|
|
687
967
|
export async function lola(args: string[]) {
|
|
688
968
|
const subcommand = args[0]
|
|
689
969
|
|
|
690
970
|
if (!subcommand || subcommand === "install" || subcommand === "sync") {
|
|
691
|
-
await
|
|
971
|
+
await installInteractive()
|
|
692
972
|
} else if (subcommand === "onboard") {
|
|
693
973
|
await onboardDev()
|
|
694
974
|
} else if (subcommand === "quiz") {
|