ocerebro 0.1.9 → 0.2.1
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/cerebro/cerebro_setup.py +70 -17
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli/main.py +5 -0
- package/src/mcp/server.py +91 -11
package/cerebro/cerebro_setup.py
CHANGED
|
@@ -187,41 +187,77 @@ def merge_configs(existing: dict, new: dict) -> dict:
|
|
|
187
187
|
return result
|
|
188
188
|
|
|
189
189
|
|
|
190
|
-
def setup_slash_commands(project_path: Path) -> bool:
|
|
191
|
-
"""Cria slash commands /cerebro no .claude/commands/ do projeto."""
|
|
190
|
+
def setup_slash_commands(project_path: Path | None = None, global_commands: bool = True) -> bool:
|
|
191
|
+
"""Cria slash commands /cerebro no .claude/commands/ do projeto e global."""
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
if project_path:
|
|
194
|
+
commands_dir = project_path / ".claude" / "commands"
|
|
195
|
+
commands_dir.mkdir(parents=True, exist_ok=True)
|
|
195
196
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
dream_cmd = commands_dir / "cerebro-dream.md"
|
|
198
|
+
if not dream_cmd.exists():
|
|
199
|
+
dream_cmd.write_text("""---
|
|
199
200
|
description: Extrair memórias da sessão atual
|
|
200
201
|
---
|
|
201
202
|
Execute: ocerebro dream --since 7 --apply
|
|
202
203
|
Mostre o relatório completo do que foi salvo.
|
|
203
204
|
""", encoding="utf-8")
|
|
204
|
-
|
|
205
|
+
print(f"[OK] Slash command criado: {dream_cmd}")
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
status_cmd = commands_dir / "cerebro-status.md"
|
|
208
|
+
if not status_cmd.exists():
|
|
209
|
+
status_cmd.write_text("""---
|
|
209
210
|
description: Ver status da memória do projeto
|
|
210
211
|
---
|
|
211
212
|
Execute: ocerebro status
|
|
212
213
|
Liste quantas memórias existem por tipo.
|
|
213
214
|
""", encoding="utf-8")
|
|
214
|
-
|
|
215
|
+
print(f"[OK] Slash command criado: {status_cmd}")
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
gc_cmd = commands_dir / "cerebro-gc.md"
|
|
218
|
+
if not gc_cmd.exists():
|
|
219
|
+
gc_cmd.write_text("""---
|
|
219
220
|
description: Limpeza de memórias antigas
|
|
220
221
|
---
|
|
221
222
|
Execute: ocerebro gc --threshold 30
|
|
222
223
|
Mostre o que será arquivado antes de confirmar.
|
|
223
224
|
""", encoding="utf-8")
|
|
224
|
-
|
|
225
|
+
print(f"[OK] Slash command criado: {gc_cmd}")
|
|
226
|
+
|
|
227
|
+
# Slash commands globais em ~/.claude/commands/
|
|
228
|
+
if global_commands:
|
|
229
|
+
global_commands_dir = Path.home() / ".claude" / "commands"
|
|
230
|
+
global_commands_dir.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
|
|
232
|
+
dream_global = global_commands_dir / "cerebro-dream.md"
|
|
233
|
+
if not dream_global.exists():
|
|
234
|
+
dream_global.write_text("""---
|
|
235
|
+
description: Extrair memórias da sessão atual (global)
|
|
236
|
+
---
|
|
237
|
+
Execute: ocerebro dream --since 7 --apply
|
|
238
|
+
Mostre o relatório completo do que foi salvo.
|
|
239
|
+
""", encoding="utf-8")
|
|
240
|
+
print(f"[OK] Slash command global criado: {dream_global}")
|
|
241
|
+
|
|
242
|
+
status_global = global_commands_dir / "cerebro-status.md"
|
|
243
|
+
if not status_global.exists():
|
|
244
|
+
status_global.write_text("""---
|
|
245
|
+
description: Ver status da memória (global)
|
|
246
|
+
---
|
|
247
|
+
Execute: ocerebro status
|
|
248
|
+
Liste quantas memórias existem por tipo.
|
|
249
|
+
""", encoding="utf-8")
|
|
250
|
+
print(f"[OK] Slash command global criado: {status_global}")
|
|
251
|
+
|
|
252
|
+
gc_global = global_commands_dir / "cerebro-gc.md"
|
|
253
|
+
if not gc_global.exists():
|
|
254
|
+
gc_global.write_text("""---
|
|
255
|
+
description: Limpeza de memórias antigas (global)
|
|
256
|
+
---
|
|
257
|
+
Execute: ocerebro gc --threshold 30
|
|
258
|
+
Mostre o que será arquivado antes de confirmar.
|
|
259
|
+
""", encoding="utf-8")
|
|
260
|
+
print(f"[OK] Slash command global criado: {gc_global}")
|
|
225
261
|
|
|
226
262
|
return True
|
|
227
263
|
|
|
@@ -358,6 +394,23 @@ def setup_claude(auto: bool = True) -> bool:
|
|
|
358
394
|
existing_config["mcp"] = {}
|
|
359
395
|
existing_config["mcp"]["enabled"] = True
|
|
360
396
|
|
|
397
|
+
# Adiciona hook para dream automatico ao final da sessao
|
|
398
|
+
if "hooks" not in existing_config:
|
|
399
|
+
existing_config["hooks"] = {}
|
|
400
|
+
|
|
401
|
+
# Hook Stop: roda dream ao final de cada sessao
|
|
402
|
+
existing_config["hooks"]["Stop"] = [
|
|
403
|
+
{
|
|
404
|
+
"matcher": "",
|
|
405
|
+
"hooks": [
|
|
406
|
+
{
|
|
407
|
+
"type": "command",
|
|
408
|
+
"command": f"{python_cmd} -m src.cli.main dream --since 1 --apply --silent"
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
|
|
361
414
|
config_path.write_text(
|
|
362
415
|
json.dumps(existing_config, indent=2, ensure_ascii=False),
|
|
363
416
|
encoding="utf-8"
|
|
@@ -503,7 +556,7 @@ def main():
|
|
|
503
556
|
project = Path(sys.argv[2]) if len(sys.argv) > 2 else Path.cwd()
|
|
504
557
|
setup_ocerebro_dir(project)
|
|
505
558
|
setup_hooks(project)
|
|
506
|
-
setup_slash_commands(project)
|
|
559
|
+
setup_slash_commands(project=project)
|
|
507
560
|
setup_claude(auto=True)
|
|
508
561
|
sys.exit(0)
|
|
509
562
|
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/cli/main.py
CHANGED
|
@@ -363,6 +363,7 @@ def main():
|
|
|
363
363
|
dream_parser = subparsers.add_parser("dream", help="Extração automática de memórias")
|
|
364
364
|
dream_parser.add_argument("--since", type=int, default=7, dest="since_days")
|
|
365
365
|
dream_parser.add_argument("--apply", action="store_true", dest="apply")
|
|
366
|
+
dream_parser.add_argument("--silent", action="store_true", dest="silent", help="Não imprimir output (para hooks)")
|
|
366
367
|
|
|
367
368
|
# Comando: remember
|
|
368
369
|
remember_parser = subparsers.add_parser("remember", help="Revisão e promoção de memórias")
|
|
@@ -434,6 +435,8 @@ def main():
|
|
|
434
435
|
)
|
|
435
436
|
elif args.command == "dream":
|
|
436
437
|
result = cli.dream(since_days=args.since_days, dry_run=not args.apply)
|
|
438
|
+
if getattr(args, 'silent', False):
|
|
439
|
+
sys.exit(0)
|
|
437
440
|
elif args.command == "remember":
|
|
438
441
|
result = cli.remember(dry_run=not args.apply)
|
|
439
442
|
elif args.command == "gc":
|
|
@@ -442,6 +445,8 @@ def main():
|
|
|
442
445
|
parser.print_help()
|
|
443
446
|
sys.exit(1)
|
|
444
447
|
|
|
448
|
+
if getattr(args, 'silent', False):
|
|
449
|
+
sys.exit(0)
|
|
445
450
|
print(result)
|
|
446
451
|
|
|
447
452
|
|
package/src/mcp/server.py
CHANGED
|
@@ -282,7 +282,7 @@ class CerebroMCP:
|
|
|
282
282
|
),
|
|
283
283
|
Tool(
|
|
284
284
|
name="cerebro_dream",
|
|
285
|
-
description="
|
|
285
|
+
description="Prepara prompt para extração de memórias - use cerebro_capture_memory após receber o prompt",
|
|
286
286
|
inputSchema={
|
|
287
287
|
"type": "object",
|
|
288
288
|
"properties": {
|
|
@@ -290,15 +290,28 @@ class CerebroMCP:
|
|
|
290
290
|
"type": "integer",
|
|
291
291
|
"description": "Dias para analisar (padrão: 7)",
|
|
292
292
|
"default": 7
|
|
293
|
-
},
|
|
294
|
-
"dry_run": {
|
|
295
|
-
"type": "boolean",
|
|
296
|
-
"description": "Se True, apenas simula (padrão: True)",
|
|
297
|
-
"default": True
|
|
298
293
|
}
|
|
299
294
|
}
|
|
300
295
|
}
|
|
301
296
|
),
|
|
297
|
+
Tool(
|
|
298
|
+
name="cerebro_capture_memory",
|
|
299
|
+
description="Captura memórias da conversa atual e salva nos arquivos - requer prompt do cerebro_dream",
|
|
300
|
+
inputSchema={
|
|
301
|
+
"type": "object",
|
|
302
|
+
"properties": {
|
|
303
|
+
"prompt": {
|
|
304
|
+
"type": "string",
|
|
305
|
+
"description": "Prompt de extração retornado por cerebro_dream"
|
|
306
|
+
},
|
|
307
|
+
"memory_dir": {
|
|
308
|
+
"type": "string",
|
|
309
|
+
"description": "Diretório de memória (opcional)"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
"required": ["prompt"]
|
|
313
|
+
}
|
|
314
|
+
),
|
|
302
315
|
Tool(
|
|
303
316
|
name="cerebro_remember",
|
|
304
317
|
description="Revisão e promoção de memórias (replica /remember do Claude Code) - classifica memórias por tipo e detecta duplicatas/conflitos entre camadas",
|
|
@@ -366,6 +379,8 @@ class CerebroMCP:
|
|
|
366
379
|
result = self._remember(arguments)
|
|
367
380
|
elif name == "cerebro_gc":
|
|
368
381
|
result = self._gc(arguments)
|
|
382
|
+
elif name == "cerebro_capture_memory":
|
|
383
|
+
result = self._capture_memory(arguments)
|
|
369
384
|
else:
|
|
370
385
|
return [TextContent(type="text", text=f"Ferramenta desconhecida: {name}")]
|
|
371
386
|
|
|
@@ -598,13 +613,78 @@ class CerebroMCP:
|
|
|
598
613
|
return self.memory_diff.generate_report(result, format=format)
|
|
599
614
|
|
|
600
615
|
def _dream(self, args: Dict[str, Any]) -> str:
|
|
601
|
-
"""
|
|
602
|
-
|
|
603
|
-
|
|
616
|
+
"""Prepara prompt para extração de memórias.
|
|
617
|
+
|
|
618
|
+
Retorna o prompt de extração + instruções para usar cerebro_capture_memory.
|
|
619
|
+
"""
|
|
620
|
+
from src.consolidation.dream import (
|
|
621
|
+
run_dream,
|
|
622
|
+
build_extract_dream_prompt,
|
|
623
|
+
scan_memory_files,
|
|
624
|
+
format_memory_manifest,
|
|
625
|
+
count_transcript_messages
|
|
626
|
+
)
|
|
604
627
|
|
|
628
|
+
since_days = args.get("since_days", 7)
|
|
605
629
|
memory_dir = get_auto_mem_path()
|
|
606
|
-
|
|
607
|
-
|
|
630
|
+
|
|
631
|
+
# Scan de memórias existentes
|
|
632
|
+
existing = scan_memory_files(memory_dir)
|
|
633
|
+
existing_manifest = format_memory_manifest(existing)
|
|
634
|
+
|
|
635
|
+
# Contagem de mensagens novas
|
|
636
|
+
message_count = count_transcript_messages(since_days)
|
|
637
|
+
|
|
638
|
+
if message_count == 0:
|
|
639
|
+
return "Nenhuma mensagem nova nos últimos {} dias. O prompt de extração não será gerado.".format(since_days)
|
|
640
|
+
|
|
641
|
+
# Build do prompt
|
|
642
|
+
prompt_sections = build_extract_dream_prompt(
|
|
643
|
+
new_message_count=message_count,
|
|
644
|
+
existing_memories=existing_manifest,
|
|
645
|
+
memory_dir=memory_dir,
|
|
646
|
+
)
|
|
647
|
+
full_prompt = "\n".join(prompt_sections)
|
|
648
|
+
|
|
649
|
+
return f"""=== PROMPT DE EXTRAÇÃO ({message_count} mensagens, {since_days} dias) ===
|
|
650
|
+
|
|
651
|
+
{full_prompt}
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
INSTRUÇÃO: Copie este prompt e use a ferramenta cerebro_capture_memory com:
|
|
655
|
+
{{"prompt": "<cole o prompt acima>"}}
|
|
656
|
+
|
|
657
|
+
Ou execute manualmente: ocerebro dream --since {since_days} --apply
|
|
658
|
+
"""
|
|
659
|
+
|
|
660
|
+
def _capture_memory(self, args: Dict[str, Any]) -> str:
|
|
661
|
+
"""Captura memórias usando o prompt fornecido.
|
|
662
|
+
|
|
663
|
+
Esta ferramenta deve ser chamada após cerebro_dream retornar o prompt.
|
|
664
|
+
"""
|
|
665
|
+
prompt = args.get("prompt")
|
|
666
|
+
if not prompt:
|
|
667
|
+
return "Erro: 'prompt' é obrigatório para cerebro_capture_memory"
|
|
668
|
+
|
|
669
|
+
memory_dir_str = args.get("memory_dir")
|
|
670
|
+
memory_dir = Path(memory_dir_str) if memory_dir_str else get_auto_mem_path()
|
|
671
|
+
|
|
672
|
+
# Instrui o Claude a usar o prompt para extrair memórias
|
|
673
|
+
return f"""Prompt de extração recebido.
|
|
674
|
+
|
|
675
|
+
Diretório de memória: {memory_dir}
|
|
676
|
+
|
|
677
|
+
O Claude deve agora analisar a conversa usando este prompt e salvar as memórias
|
|
678
|
+
nos arquivos .md apropriados em {memory_dir}
|
|
679
|
+
|
|
680
|
+
Formato esperado:
|
|
681
|
+
- user_*.md para memórias sobre o usuário
|
|
682
|
+
- feedback_*.md para feedbacks e convenções
|
|
683
|
+
- project_*.md para decisões e fatos do projeto
|
|
684
|
+
- reference_*.md para ponteiros externos
|
|
685
|
+
|
|
686
|
+
Use FileWrite para criar os arquivos de memória.
|
|
687
|
+
"""
|
|
608
688
|
|
|
609
689
|
def _remember(self, args: Dict[str, Any]) -> str:
|
|
610
690
|
"""Revisão e promoção de memórias"""
|