ocerebro 0.4.17 → 0.4.19
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/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli/main.py +11 -2
- package/src/mcp/server.py +66 -46
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/cli/main.py
CHANGED
|
@@ -94,7 +94,13 @@ class CerebroCLI:
|
|
|
94
94
|
self.raw_storage.append(checkpoint_event)
|
|
95
95
|
return f"Checkpoint criado: {draft_name} ({len(result.events)} eventos)"
|
|
96
96
|
|
|
97
|
-
def memory(self, project: str, output: Optional[Path] = None) -> str:
|
|
97
|
+
def memory(self, project: str = None, output: Optional[Path] = None) -> str:
|
|
98
|
+
if not project:
|
|
99
|
+
try:
|
|
100
|
+
from src.core.paths import get_git_root
|
|
101
|
+
project = get_git_root().name
|
|
102
|
+
except Exception:
|
|
103
|
+
project = Path.cwd().name
|
|
98
104
|
content = self.memory_view.generate(project)
|
|
99
105
|
if output:
|
|
100
106
|
output.write_text(content, encoding="utf-8")
|
|
@@ -432,7 +438,10 @@ def main():
|
|
|
432
438
|
|
|
433
439
|
# Comando: memory
|
|
434
440
|
memory_parser = subparsers.add_parser("memory", help="Visualizar memória ativa")
|
|
435
|
-
memory_parser.add_argument(
|
|
441
|
+
memory_parser.add_argument(
|
|
442
|
+
"project", nargs="?", default=None,
|
|
443
|
+
help="Nome do projeto (opcional, detecta pelo git root)"
|
|
444
|
+
)
|
|
436
445
|
memory_parser.add_argument("--output", type=Path, help="Arquivo de saída")
|
|
437
446
|
|
|
438
447
|
# Comando: search
|
package/src/mcp/server.py
CHANGED
|
@@ -138,10 +138,9 @@ class CerebroMCP:
|
|
|
138
138
|
"properties": {
|
|
139
139
|
"project": {
|
|
140
140
|
"type": "string",
|
|
141
|
-
"description": "Nome do projeto"
|
|
141
|
+
"description": "Nome do projeto (omitir = auto-detectar pelo working directory)"
|
|
142
142
|
}
|
|
143
|
-
}
|
|
144
|
-
"required": ["project"]
|
|
143
|
+
}
|
|
145
144
|
}
|
|
146
145
|
),
|
|
147
146
|
Tool(
|
|
@@ -438,39 +437,19 @@ class CerebroMCP:
|
|
|
438
437
|
except Exception as e:
|
|
439
438
|
return [TextContent(type="text", text=f"Erro: {str(e)}")]
|
|
440
439
|
|
|
441
|
-
def
|
|
442
|
-
"""
|
|
443
|
-
project = args.get("project")
|
|
444
|
-
if not project:
|
|
445
|
-
return "Erro: project é obrigatório"
|
|
446
|
-
|
|
447
|
-
content = self.memory_view.generate(project)
|
|
448
|
-
|
|
449
|
-
# FIX 4: Escreve MEMORY.md no diretório nativo do Claude Code
|
|
450
|
-
# Assim o Claude Code carrega automaticamente na próxima sessão
|
|
440
|
+
def _detect_project(self) -> str:
|
|
441
|
+
"""Detecta projeto pelo git root do cwd. Nome exato da pasta."""
|
|
451
442
|
try:
|
|
452
|
-
from src.core.paths import
|
|
453
|
-
|
|
454
|
-
auto_mem_dir.mkdir(parents=True, exist_ok=True)
|
|
455
|
-
index_path = get_memory_index(auto_mem_dir)
|
|
456
|
-
|
|
457
|
-
# Gera conteúdo compatível com o formato que Claude Code espera
|
|
458
|
-
# Formato: # <title>\n\n- [type] filename (date): description
|
|
459
|
-
claude_format_lines = ["# OCerebro - Memória Ativa", ""]
|
|
460
|
-
claude_format_lines.append(f"## {project}")
|
|
461
|
-
claude_format_lines.append("")
|
|
462
|
-
|
|
463
|
-
# Parse do conteúdo gerado para extrair itens
|
|
464
|
-
for line in content.splitlines():
|
|
465
|
-
if line.startswith("- ["):
|
|
466
|
-
claude_format_lines.append(line)
|
|
467
|
-
|
|
468
|
-
claude_content = "\n".join(claude_format_lines)
|
|
469
|
-
index_path.write_text(claude_content, encoding="utf-8")
|
|
443
|
+
from src.core.paths import get_git_root
|
|
444
|
+
return get_git_root().name
|
|
470
445
|
except Exception:
|
|
471
|
-
|
|
446
|
+
return Path.cwd().name
|
|
472
447
|
|
|
473
|
-
|
|
448
|
+
def _memory(self, args: Dict[str, Any]) -> str:
|
|
449
|
+
"""Gera memória ativa e escreve no diretório nativo do Claude Code para auto-load."""
|
|
450
|
+
project = args.get("project") or self._detect_project()
|
|
451
|
+
path = self.memory_view.write_to_file(project)
|
|
452
|
+
return self.memory_view.generate(project)
|
|
474
453
|
|
|
475
454
|
def _search(self, args: Dict[str, Any]) -> str:
|
|
476
455
|
"""Busca memórias"""
|
|
@@ -564,27 +543,65 @@ class CerebroMCP:
|
|
|
564
543
|
return f"Promoção falhou: {result.metadata.get('reason', 'desconhecido')}"
|
|
565
544
|
|
|
566
545
|
def _status(self) -> str:
|
|
567
|
-
"""Status do sistema"""
|
|
546
|
+
"""Status do sistema com contagem de memórias por tipo"""
|
|
568
547
|
session_id = self.session_manager.get_session_id()
|
|
569
548
|
|
|
570
549
|
lines = [
|
|
571
|
-
"
|
|
572
|
-
|
|
573
|
-
|
|
550
|
+
"╔══════════════════════════════════════════════════════════════╗",
|
|
551
|
+
"║ 🧠 OCEREBRO STATUS ║",
|
|
552
|
+
"╚══════════════════════════════════════════════════════════════╝",
|
|
574
553
|
"",
|
|
575
|
-
"
|
|
576
|
-
f"
|
|
577
|
-
f" Working: {self.cerebro_path / 'working'}",
|
|
578
|
-
f" Official: {self.cerebro_path / 'official'}",
|
|
554
|
+
f"Session ID: {session_id}",
|
|
555
|
+
f"Path: {self.cerebro_path.absolute()}",
|
|
579
556
|
"",
|
|
580
|
-
"Índice:",
|
|
581
|
-
f" Metadata DB: {self.cerebro_path / 'index' / 'metadata.db'}",
|
|
582
|
-
f" Embeddings DB: {self.cerebro_path / 'index' / 'embeddings.db'}",
|
|
583
|
-
f" Entities DB: {self.cerebro_path / 'index' / 'entities.db'}"
|
|
584
557
|
]
|
|
585
558
|
|
|
559
|
+
# Contagem de memórias por tipo (entities DB)
|
|
560
|
+
try:
|
|
561
|
+
import sqlite3
|
|
562
|
+
conn = sqlite3.connect(str(self.cerebro_path / "index" / "entities.db"))
|
|
563
|
+
cursor = conn.cursor()
|
|
564
|
+
cursor.execute("SELECT entity_type, COUNT(*) FROM entities GROUP BY entity_type ORDER BY COUNT(*) DESC")
|
|
565
|
+
type_counts = cursor.fetchall()
|
|
566
|
+
conn.close()
|
|
567
|
+
|
|
568
|
+
if type_counts:
|
|
569
|
+
total = sum(c for _, c in type_counts)
|
|
570
|
+
lines.append(f"📊 Memórias: {total} total")
|
|
571
|
+
lines.append("")
|
|
572
|
+
lines.append("Por tipo:")
|
|
573
|
+
for entity_type, count in type_counts:
|
|
574
|
+
icon = self._get_type_icon(entity_type)
|
|
575
|
+
lines.append(f" {icon} {entity_type}: {count}")
|
|
576
|
+
else:
|
|
577
|
+
lines.append("📊 Memórias: 0")
|
|
578
|
+
except Exception:
|
|
579
|
+
lines.append("📊 Memórias: (banco não acessível)")
|
|
580
|
+
|
|
581
|
+
lines.append("")
|
|
582
|
+
lines.append("Storages:")
|
|
583
|
+
lines.append(f" 📁 Raw: {self.cerebro_path / 'raw'}")
|
|
584
|
+
lines.append(f" 📝 Working: {self.cerebro_path / 'working'}")
|
|
585
|
+
lines.append(f" 📋 Official: {self.cerebro_path / 'official'}")
|
|
586
|
+
|
|
586
587
|
return "\n".join(lines)
|
|
587
588
|
|
|
589
|
+
def _get_type_icon(self, entity_type: str) -> str:
|
|
590
|
+
"""Retorna ícone para tipo de memória"""
|
|
591
|
+
icons = {
|
|
592
|
+
"USER": "👤",
|
|
593
|
+
"FEEDBACK": "💬",
|
|
594
|
+
"PROJECT": "📂",
|
|
595
|
+
"REFERENCE": "🔗",
|
|
596
|
+
"TAG": "🏷️",
|
|
597
|
+
"TYPE": "📌",
|
|
598
|
+
"META": "⚙️",
|
|
599
|
+
"DECISION": "✅",
|
|
600
|
+
"ERROR": "❌",
|
|
601
|
+
"DRAFT": "📝",
|
|
602
|
+
}
|
|
603
|
+
return icons.get(entity_type, "📄")
|
|
604
|
+
|
|
588
605
|
def _hooks(self, args: Dict[str, Any]) -> str:
|
|
589
606
|
"""Lista e gerencia hooks customizados"""
|
|
590
607
|
action = args.get("action", "list")
|
|
@@ -777,7 +794,10 @@ Uma chamada por memória. O sistema salva e indexa automaticamente.
|
|
|
777
794
|
m_type = type_match.group(1).strip() if type_match else "project"
|
|
778
795
|
if not project:
|
|
779
796
|
project_match = re.search(r'project:\s*(.*)', content)
|
|
780
|
-
|
|
797
|
+
if project_match and project_match.group(1).strip():
|
|
798
|
+
project = project_match.group(1).strip()
|
|
799
|
+
else:
|
|
800
|
+
project = self._detect_project()
|
|
781
801
|
if not tags:
|
|
782
802
|
tags_match = re.search(r'tags:\s*(.*)', content)
|
|
783
803
|
tags = tags_match.group(1).strip() if tags_match else ""
|