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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocerebro",
3
- "version": "0.4.17",
3
+ "version": "0.4.19",
4
4
  "description": "OCerebro - Sistema de Memoria para Agentes (Claude Code/MCP)",
5
5
  "main": "bin/ocerebro.js",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ocerebro"
7
- version = "0.4.17"
7
+ version = "0.4.19"
8
8
  description = "OCerebro - Sistema de Memoria para Agentes (Claude Code/MCP)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
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("project", help="Nome do projeto")
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 _memory(self, args: Dict[str, Any]) -> str:
442
- """Gera memória ativa e escreve no diretório nativo do Claude Code para auto-load."""
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 get_auto_mem_path, get_memory_index
453
- auto_mem_dir = get_auto_mem_path()
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
- pass # Falha silenciosa - não bloqueia o retorno
446
+ return Path.cwd().name
472
447
 
473
- return content
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
- "Status do Cerebro:",
572
- f" Session ID: {session_id}",
573
- f" Path: {self.cerebro_path.absolute()}",
550
+ "╔══════════════════════════════════════════════════════════════╗",
551
+ "║ 🧠 OCEREBRO STATUS ║",
552
+ "╚══════════════════════════════════════════════════════════════╝",
574
553
  "",
575
- "Storages:",
576
- f" Raw: {self.cerebro_path / 'raw'}",
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
- project = project_match.group(1).strip() if project_match else "unknown"
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 ""