@nimbuslab/cli 0.14.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/.github/workflows/publish.yml +25 -0
- package/dist/index.js +484 -185
- package/package.json +1 -1
- package/src/commands/lola.ts +480 -167
- package/src/commands/update.ts +133 -35
- package/src/index.ts +4 -3
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,228 +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
|
-
stderr: "inherit",
|
|
80
|
-
})
|
|
101
|
+
console.log(pc.green(" Gemini CLI instalado!"))
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
81
104
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
105
|
+
async function installGeminiMCP(): Promise<void> {
|
|
106
|
+
console.log()
|
|
107
|
+
console.log(pc.cyan(" Configurando MCP no Gemini..."))
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
} else {
|
|
89
|
-
console.log(pc.cyan(` Instalando Lola em ${LOLA_DIR}...`))
|
|
109
|
+
const settingsPath = join(GEMINI_DIR, "settings.json")
|
|
90
110
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
111
|
+
// Criar diretório se não existir
|
|
112
|
+
if (!existsSync(GEMINI_DIR)) {
|
|
113
|
+
await Bun.$`mkdir -p ${GEMINI_DIR}`
|
|
114
|
+
}
|
|
95
115
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
100
124
|
}
|
|
125
|
+
}
|
|
101
126
|
|
|
102
|
-
|
|
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
|
|
103
132
|
}
|
|
104
133
|
|
|
105
|
-
//
|
|
106
|
-
|
|
134
|
+
// Adicionar lola-memory
|
|
135
|
+
mcpServers[LOLA_MEMORY_NAME] = {
|
|
136
|
+
url: LOLA_MEMORY_URL
|
|
137
|
+
}
|
|
138
|
+
settings.mcpServers = mcpServers
|
|
139
|
+
|
|
140
|
+
// Salvar
|
|
141
|
+
await Bun.write(settingsPath, JSON.stringify(settings, null, 2))
|
|
142
|
+
console.log(pc.green(" MCP lola-memory adicionado ao Gemini!"))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function installGeminiSystemPrompt(): Promise<void> {
|
|
146
|
+
console.log()
|
|
147
|
+
console.log(pc.cyan(" Configurando GEMINI.md..."))
|
|
148
|
+
|
|
149
|
+
// Criar diretório se não existir
|
|
150
|
+
if (!existsSync(GEMINI_DIR)) {
|
|
151
|
+
await Bun.$`mkdir -p ${GEMINI_DIR}`
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Copiar o agent da Lola (adaptar claude.md para gemini)
|
|
107
155
|
const lolaAgent = join(LOLA_DIR, "agents", "claude.md")
|
|
108
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
|
+
|
|
109
191
|
console.log()
|
|
110
|
-
console.log(pc.cyan(" Configurando comando lola..."))
|
|
192
|
+
console.log(pc.cyan(" Configurando comando lola-gemini..."))
|
|
111
193
|
|
|
112
194
|
if (isWindows) {
|
|
113
195
|
// Windows: adicionar função ao PowerShell profile
|
|
114
|
-
// PowerShell 5.x usa WindowsPowerShell, PowerShell 7+ usa PowerShell
|
|
115
|
-
const ps5ProfileDir = join(HOME, "Documents", "WindowsPowerShell")
|
|
116
196
|
const ps7ProfileDir = join(HOME, "Documents", "PowerShell")
|
|
117
197
|
const profileName = "Microsoft.PowerShell_profile.ps1"
|
|
198
|
+
const profilePath = join(ps7ProfileDir, profileName)
|
|
118
199
|
|
|
119
|
-
const
|
|
120
|
-
# Lola - Code Agent da nimbuslab
|
|
121
|
-
function lola {
|
|
200
|
+
const lolaGeminiFunction = `
|
|
201
|
+
# Lola (Gemini) - Code Agent da nimbuslab
|
|
202
|
+
function lola-gemini {
|
|
122
203
|
param([Parameter(ValueFromRemainingArguments=$true)]$args)
|
|
123
|
-
|
|
124
|
-
if (Test-Path $agent) {
|
|
125
|
-
claude --append-system-prompt-file $agent @args
|
|
126
|
-
} else {
|
|
127
|
-
Write-Host "Agente Lola nao encontrado. Rode: nimbus lola install"
|
|
128
|
-
}
|
|
204
|
+
gemini @args
|
|
129
205
|
}
|
|
130
206
|
`
|
|
131
207
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{ dir: ps7ProfileDir, name: "PowerShell 7+" },
|
|
136
|
-
]
|
|
137
|
-
|
|
138
|
-
let addedToAny = false
|
|
139
|
-
|
|
140
|
-
for (const { dir, name } of profiles) {
|
|
141
|
-
const profilePath = join(dir, profileName)
|
|
142
|
-
|
|
143
|
-
// Criar diretório se não existir
|
|
144
|
-
if (!existsSync(dir)) {
|
|
145
|
-
await Bun.$`mkdir -p ${dir}`
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Verificar se já tem a função no profile
|
|
149
|
-
let profileContent = ""
|
|
150
|
-
if (existsSync(profilePath)) {
|
|
151
|
-
profileContent = await Bun.file(profilePath).text()
|
|
152
|
-
}
|
|
208
|
+
if (!existsSync(ps7ProfileDir)) {
|
|
209
|
+
await Bun.$`mkdir -p ${ps7ProfileDir}`
|
|
210
|
+
}
|
|
153
211
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
addedToAny = true
|
|
158
|
-
}
|
|
212
|
+
let profileContent = ""
|
|
213
|
+
if (existsSync(profilePath)) {
|
|
214
|
+
profileContent = await Bun.file(profilePath).text()
|
|
159
215
|
}
|
|
160
216
|
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
console.log()
|
|
164
|
-
console.log(pc.yellow(" IMPORTANTE (Windows):"))
|
|
165
|
-
console.log(pc.dim(" Se o comando 'lola' nao funcionar, execute primeiro:"))
|
|
166
|
-
console.log(pc.cyan(" Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser"))
|
|
167
|
-
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"))
|
|
168
220
|
} else {
|
|
169
|
-
console.log(pc.green(" Funcao lola ja existe
|
|
221
|
+
console.log(pc.green(" Funcao lola-gemini ja existe"))
|
|
170
222
|
}
|
|
171
223
|
} else {
|
|
172
224
|
// Linux/macOS: criar script em ~/.local/bin
|
|
173
225
|
const binDir = join(HOME, ".local", "bin")
|
|
174
|
-
const
|
|
226
|
+
const lolaGeminiScript = join(binDir, "lola-gemini")
|
|
175
227
|
|
|
176
|
-
if (!existsSync(
|
|
228
|
+
if (!existsSync(lolaGeminiScript)) {
|
|
177
229
|
await Bun.$`mkdir -p ${binDir}`
|
|
178
230
|
|
|
179
231
|
const unixScript = `#!/bin/bash
|
|
180
|
-
# lola - Code Agent da nimbuslab
|
|
181
|
-
LOLA_AGENT="${lolaAgent}"
|
|
232
|
+
# lola-gemini - Code Agent da nimbuslab (Gemini)
|
|
182
233
|
|
|
183
234
|
if [[ "\$1" == "-h" || "\$1" == "--help" ]]; then
|
|
184
|
-
echo "lola - Code Agent da nimbuslab"
|
|
185
|
-
echo "Uso: lola [args]"
|
|
186
|
-
echo " lola
|
|
187
|
-
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"
|
|
188
238
|
exit 0
|
|
189
239
|
fi
|
|
190
240
|
|
|
191
|
-
|
|
192
|
-
exec claude --append-system-prompt-file "\$LOLA_AGENT" "\$@"
|
|
193
|
-
else
|
|
194
|
-
echo "Agente Lola nao encontrado: \$LOLA_AGENT"
|
|
195
|
-
echo "Rode: nimbus lola install"
|
|
196
|
-
exit 1
|
|
197
|
-
fi
|
|
241
|
+
exec gemini "\$@"
|
|
198
242
|
`
|
|
199
|
-
await Bun.write(
|
|
200
|
-
await Bun.$`chmod +x ${
|
|
243
|
+
await Bun.write(lolaGeminiScript, unixScript)
|
|
244
|
+
await Bun.$`chmod +x ${lolaGeminiScript}`
|
|
201
245
|
|
|
202
|
-
console.log(pc.green(` Script lola criado em ${
|
|
203
|
-
|
|
204
|
-
// Verificar se ~/.local/bin está no PATH
|
|
205
|
-
const pathEnv = process.env.PATH || ""
|
|
206
|
-
if (!pathEnv.includes(".local/bin")) {
|
|
207
|
-
console.log()
|
|
208
|
-
console.log(pc.yellow(" IMPORTANTE: Adicione ~/.local/bin ao seu PATH"))
|
|
209
|
-
console.log(pc.dim(" Adicione ao seu ~/.bashrc ou ~/.zshrc:"))
|
|
210
|
-
console.log(pc.dim(" export PATH=\"$HOME/.local/bin:$PATH\""))
|
|
211
|
-
}
|
|
246
|
+
console.log(pc.green(` Script lola-gemini criado em ${lolaGeminiScript}`))
|
|
212
247
|
} else {
|
|
213
|
-
console.log(pc.green(" Script lola ja existe"))
|
|
248
|
+
console.log(pc.green(" Script lola-gemini ja existe"))
|
|
214
249
|
}
|
|
215
250
|
}
|
|
251
|
+
}
|
|
216
252
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
console.log()
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// Pegar info do git automaticamente
|
|
227
|
-
const gitUserResult = Bun.spawnSync(["git", "config", "user.name"], { stdout: "pipe" })
|
|
228
|
-
const gitEmailResult = Bun.spawnSync(["git", "config", "user.email"], { stdout: "pipe" })
|
|
229
|
-
|
|
230
|
-
const gitUser = gitUserResult.stdout.toString().trim() || "Dev"
|
|
231
|
-
const gitEmail = gitEmailResult.stdout.toString().trim() || ""
|
|
232
|
-
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME || "local"
|
|
233
|
-
const today = new Date().toISOString().split("T")[0]
|
|
234
|
-
|
|
235
|
-
await Bun.$`mkdir -p ${claudeDir}`
|
|
236
|
-
|
|
237
|
-
const content = `# User Memory
|
|
238
|
-
|
|
239
|
-
Memoria persistente para sessoes Claude Code
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
## Dev
|
|
244
|
-
|
|
245
|
-
**Nome:** ${gitUser}
|
|
246
|
-
**Email:** ${gitEmail}
|
|
247
|
-
**Maquina:** ${hostname}
|
|
248
|
-
**Instalacao:** ${today}
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## Configuracoes da Lola
|
|
253
|
-
|
|
254
|
-
\`\`\`
|
|
255
|
-
lola_profile: millennial
|
|
256
|
-
\`\`\`
|
|
257
|
-
|
|
258
|
-
---
|
|
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
|
+
}
|
|
259
261
|
|
|
260
|
-
|
|
262
|
+
// Configurar MCP
|
|
263
|
+
await installGeminiMCP()
|
|
261
264
|
|
|
262
|
-
|
|
265
|
+
// Configurar GEMINI.md
|
|
266
|
+
await installGeminiSystemPrompt()
|
|
263
267
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
await Bun.write(USER_MEMORY, content)
|
|
267
|
-
console.log(pc.green(` USER_MEMORY.md criado para ${gitUser}!`))
|
|
268
|
-
}
|
|
268
|
+
// Criar comando lola-gemini
|
|
269
|
+
await createGeminiCommand()
|
|
269
270
|
|
|
270
271
|
console.log()
|
|
271
|
-
console.log(pc.green("
|
|
272
|
-
console.log()
|
|
273
|
-
console.log(pc.bold(" Para usar a Lola:"))
|
|
274
|
-
console.log(pc.dim(" lola ") + pc.white("# Iniciar sessao"))
|
|
272
|
+
console.log(pc.green(" Gemini configurado!"))
|
|
275
273
|
console.log()
|
|
276
|
-
console.log(pc.bold("
|
|
277
|
-
console.log(pc.dim("
|
|
278
|
-
console.log(pc.dim("
|
|
279
|
-
console.log(pc.dim(" memory_stats ") + pc.white("# Ver estatisticas"))
|
|
280
|
-
console.log()
|
|
281
|
-
console.log(pc.bold(" Para mudar perfil, edite ~/.claude/USER_MEMORY.md:"))
|
|
282
|
-
console.log(pc.dim(" lola_profile: millennial|genz|profissional|nerd|chill"))
|
|
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)"))
|
|
283
277
|
console.log()
|
|
278
|
+
console.log(pc.dim(" Na primeira execucao, faca login com sua conta Google."))
|
|
284
279
|
}
|
|
285
280
|
|
|
281
|
+
|
|
286
282
|
async function suggestImprovement(message: string): Promise<void> {
|
|
287
283
|
if (!message) {
|
|
288
284
|
console.log(pc.red(" Erro: forneça uma mensagem"))
|
|
@@ -394,7 +390,7 @@ async function onboardDev(): Promise<void> {
|
|
|
394
390
|
if (!existsSync(LOLA_DIR)) {
|
|
395
391
|
console.log(pc.yellow(" Lola nao instalada. Instalando primeiro..."))
|
|
396
392
|
console.log()
|
|
397
|
-
await
|
|
393
|
+
await installLolaBase()
|
|
398
394
|
console.log()
|
|
399
395
|
}
|
|
400
396
|
|
|
@@ -651,11 +647,328 @@ async function runQuiz(): Promise<void> {
|
|
|
651
647
|
console.log()
|
|
652
648
|
}
|
|
653
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
|
+
|
|
654
967
|
export async function lola(args: string[]) {
|
|
655
968
|
const subcommand = args[0]
|
|
656
969
|
|
|
657
970
|
if (!subcommand || subcommand === "install" || subcommand === "sync") {
|
|
658
|
-
await
|
|
971
|
+
await installInteractive()
|
|
659
972
|
} else if (subcommand === "onboard") {
|
|
660
973
|
await onboardDev()
|
|
661
974
|
} else if (subcommand === "quiz") {
|