ompa 0.2.0__tar.gz → 0.2.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ompa
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Universal AI agent memory layer — vault + palace + temporal knowledge graph
5
5
  Author-email: Micap AI <info@micap.ai>
6
6
  License: MIT
@@ -8,15 +8,14 @@ Project-URL: Homepage, https://github.com/jmiaie/ompa
8
8
  Project-URL: Repository, https://github.com/jmiaie/ompa
9
9
  Project-URL: Documentation, https://github.com/jmiaie/ompa#readme
10
10
  Project-URL: Bug Tracker, https://github.com/jmiaie/ompa/issues
11
- Keywords: ai,memory,agent,llm,rag,vector-database,mcp,claude,openclaw,coddex,gemini-cli,knowledge-graph,temporal,obsidian,vault
11
+ Keywords: ai,agent,memory,obsidian,knowledge-graph,mcp,claude,llm,rag,semantic-search
12
12
  Classifier: Development Status :: 4 - Beta
13
- Classifier: Environment :: Console
14
13
  Classifier: Intended Audience :: Developers
15
14
  Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Programming Language :: Python :: 3
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
19
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
20
19
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
21
  Requires-Python: >=3.10
@@ -25,7 +24,6 @@ License-File: LICENSE
25
24
  Requires-Dist: typer>=0.9.0
26
25
  Requires-Dist: rich>=13.0.0
27
26
  Requires-Dist: python-frontmatter>=1.1.0
28
- Requires-Dist: watchdog>=3.0.0
29
27
  Provides-Extra: dev
30
28
  Requires-Dist: pytest>=7.0.0; extra == "dev"
31
29
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -35,13 +33,15 @@ Provides-Extra: semantic
35
33
  Requires-Dist: numpy>=1.24.0; extra == "semantic"
36
34
  Requires-Dist: sentence-transformers>=2.2.0; extra == "semantic"
37
35
  Provides-Extra: all
38
- Requires-Dist: numpy>=1.24.0; extra == "all"
39
- Requires-Dist: sentence-transformers>=2.2.0; extra == "all"
40
- Requires-Dist: chromadb>=0.4.0; extra == "all"
36
+ Requires-Dist: ompa[semantic]; extra == "all"
41
37
  Dynamic: license-file
42
38
 
43
39
  # OMPA
44
40
 
41
+ [![PyPI](https://img.shields.io/pypi/v/ompa)](https://pypi.org/project/ompa/)
42
+ [![Python](https://img.shields.io/pypi/pyversions/ompa)](https://pypi.org/project/ompa/)
43
+ [![License](https://img.shields.io/pypi/l/ompa)](https://github.com/jmiaie/ompa/blob/main/LICENSE)
44
+
45
45
  > **Obsidian-MemPalace-Agnostic** — Universal AI agent memory layer
46
46
 
47
47
  OMPA gives any AI agent persistent memory with vault conventions, palace navigation, and a temporal knowledge graph.
@@ -57,10 +57,6 @@ This project is a synthesis of ideas and code from the AI agent memory community
57
57
 
58
58
  OMPA combines these into a framework-agnostic package that works with any AI agent runtime.
59
59
 
60
- [![PyPI version](https://img.shields.io/pypi/v/ompa)](https://pypi.org/project/ompa/)
61
- [![Python versions](https://img.shields.io/pypi/pyversions/ompa)](https://pypi.org/project/ompa/)
62
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
63
-
64
60
  ## The Problem
65
61
 
66
62
  Every AI agent starts empty every session. Important decisions get lost. Context grows expensive. `ANKI` prompts only get you so far.
@@ -78,11 +74,13 @@ OMPA gives any AI agent — **Claude Code, OpenClaw, Codex, Gemini CLI, or any c
78
74
 
79
75
  ```bash
80
76
  pip install ompa
77
+ ao init
78
+ ao status
79
+ ```
81
80
 
82
- # Initialize a vault
83
- ao init ./workspace
81
+ That's it -- you have a vault with persistent memory. Now use it in a session:
84
82
 
85
- # Run your agent session, then:
83
+ ```bash
86
84
  ao session-start # ~2K token context injection
87
85
  ao classify "We decided to go with Postgres" # Route to right folder
88
86
  ao search "authentication decisions" # Semantic search
@@ -122,7 +120,7 @@ ao wrap-up # Session summary + save to vault
122
120
 
123
121
  ### 15 Message Types
124
122
 
125
- DECISION, INCIDENT, WIN, ONE-ON-ONE, MEETING, PROJECT-UPDATE, PERSON-INFO, QUESTION, TASK, ARCHITECTURE, CODE, BRAIN-DUMP, WRAP-UP, STANDUP, UNKNOWN — each with routing hints that automatically file things in the right place.
123
+ DECISION, INCIDENT, WIN, LOSS, BLOCKER, QUESTION, SUGGESTION, REVIEW, BUG, FEATURE, LEARN, RETROSPECTIVE, ALERT, STATUS, CHORE — each with routing hints that automatically file things in the right place.
126
124
 
127
125
  ### Semantic Search (Zero API Cost)
128
126
 
@@ -137,13 +135,13 @@ ompa/
137
135
  ├── palace.py # Palace metadata (wings/rooms/drawers)
138
136
  ├── knowledge_graph.py # Temporal KG (SQLite triples)
139
137
  ├── hooks.py # 5 lifecycle hooks
140
- ├── classifier.py # 15 message types
138
+ ├── classifier.py # 15 message types
141
139
  ├── semantic.py # Local semantic search
142
140
  ├── mcp_server.py # MCP protocol server (14 tools)
143
- └── cli.py # 15 CLI commands
141
+ └── cli.py # CLI commands
144
142
  ```
145
143
 
146
- ## MCP Server (14 Tools)
144
+ ## MCP Server (15 Tools)
147
145
 
148
146
  Works with **Claude Desktop, Cursor, Windsurf** natively:
149
147
 
@@ -152,7 +150,7 @@ Works with **Claude Desktop, Cursor, Windsurf** natively:
152
150
  claude mcp add ompa -- python -m ompa.mcp_server
153
151
  ```
154
152
 
155
- Tools: `ao_session_start`, `ao_classify`, `ao_search`, `ao_kg_query`, `ao_kg_add`, `ao_kg_stats`, `ao_palace_wings`, `ao_palace_rooms`, `ao_palace_tunnel`, `ao_validate`, `ao_wrap_up`, `ao_status`, `ao_orphans`, `ao_init`
153
+ Tools: `ao_session_start`, `ao_classify`, `ao_search`, `ao_kg_query`, `ao_kg_add`, `ao_palace_wings`, `ao_palace_rooms`, `ao_palace_tunnel`, `ao_validate`, `ao_wrap_up`, `ao_status`, `ao_orphans`, `ao_init`, `ao_search`, `ao_stop`
156
154
 
157
155
  ## Python API
158
156
 
@@ -205,9 +203,8 @@ Unlike MemPalace (Claude Code + MCP only) or obsidian-mind (Claude Code hooks on
205
203
  ## Installation
206
204
 
207
205
  ```bash
208
- pip install ompa # Core (vault, palace, KG, CLI)
209
- pip install ompa[semantic] # + sentence-transformers for semantic search
210
- pip install ompa[all] # All optional dependencies
206
+ pip install ompa # Core only
207
+ pip install ompa[all] # All dependencies including sentence-transformers
211
208
  ```
212
209
 
213
210
  Requires Python 3.10+.
@@ -219,17 +216,17 @@ Because memory should not be coupled to your agent framework. Build once, use an
219
216
  ## Comparison
220
217
 
221
218
  | Feature | OMPA | MemPalace | obsidian-mind |
222
- |---------|------|-----------|---------------|
219
+ |---------|-----------------|------------|---------------|
223
220
  | Framework | Any | Claude Code | Claude Code |
224
221
  | Memory type | Vault + Palace + KG | Palace + KG | Vault only |
225
222
  | Semantic search | Local (free) | ChromaDB API | QMD (paid) |
226
- | Temporal KG | SQLite | SQLite | - |
227
- | MCP server | 14 tools | 15 tools | - |
228
- | CLI | 15 commands | - | - |
223
+ | Temporal KG | SQLite | SQLite | |
224
+ | MCP server | 15 tools | 15 tools | |
225
+ | CLI | 14 commands | | |
229
226
  | Hooks | 5 lifecycle | 3 lifecycle | 3 lifecycle |
230
227
  | Message types | 15 | 15 | 5 |
231
- | Verbatim storage | Yes | Yes | No |
232
- | Multi-agent | Yes | No | No |
228
+ | Verbatim storage | | | |
229
+ | Multi-agent | | | |
233
230
 
234
231
  ## License
235
232
 
@@ -1,5 +1,9 @@
1
1
  # OMPA
2
2
 
3
+ [![PyPI](https://img.shields.io/pypi/v/ompa)](https://pypi.org/project/ompa/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/ompa)](https://pypi.org/project/ompa/)
5
+ [![License](https://img.shields.io/pypi/l/ompa)](https://github.com/jmiaie/ompa/blob/main/LICENSE)
6
+
3
7
  > **Obsidian-MemPalace-Agnostic** — Universal AI agent memory layer
4
8
 
5
9
  OMPA gives any AI agent persistent memory with vault conventions, palace navigation, and a temporal knowledge graph.
@@ -15,10 +19,6 @@ This project is a synthesis of ideas and code from the AI agent memory community
15
19
 
16
20
  OMPA combines these into a framework-agnostic package that works with any AI agent runtime.
17
21
 
18
- [![PyPI version](https://img.shields.io/pypi/v/ompa)](https://pypi.org/project/ompa/)
19
- [![Python versions](https://img.shields.io/pypi/pyversions/ompa)](https://pypi.org/project/ompa/)
20
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
21
-
22
22
  ## The Problem
23
23
 
24
24
  Every AI agent starts empty every session. Important decisions get lost. Context grows expensive. `ANKI` prompts only get you so far.
@@ -36,11 +36,13 @@ OMPA gives any AI agent — **Claude Code, OpenClaw, Codex, Gemini CLI, or any c
36
36
 
37
37
  ```bash
38
38
  pip install ompa
39
+ ao init
40
+ ao status
41
+ ```
39
42
 
40
- # Initialize a vault
41
- ao init ./workspace
43
+ That's it -- you have a vault with persistent memory. Now use it in a session:
42
44
 
43
- # Run your agent session, then:
45
+ ```bash
44
46
  ao session-start # ~2K token context injection
45
47
  ao classify "We decided to go with Postgres" # Route to right folder
46
48
  ao search "authentication decisions" # Semantic search
@@ -80,7 +82,7 @@ ao wrap-up # Session summary + save to vault
80
82
 
81
83
  ### 15 Message Types
82
84
 
83
- DECISION, INCIDENT, WIN, ONE-ON-ONE, MEETING, PROJECT-UPDATE, PERSON-INFO, QUESTION, TASK, ARCHITECTURE, CODE, BRAIN-DUMP, WRAP-UP, STANDUP, UNKNOWN — each with routing hints that automatically file things in the right place.
85
+ DECISION, INCIDENT, WIN, LOSS, BLOCKER, QUESTION, SUGGESTION, REVIEW, BUG, FEATURE, LEARN, RETROSPECTIVE, ALERT, STATUS, CHORE — each with routing hints that automatically file things in the right place.
84
86
 
85
87
  ### Semantic Search (Zero API Cost)
86
88
 
@@ -95,13 +97,13 @@ ompa/
95
97
  ├── palace.py # Palace metadata (wings/rooms/drawers)
96
98
  ├── knowledge_graph.py # Temporal KG (SQLite triples)
97
99
  ├── hooks.py # 5 lifecycle hooks
98
- ├── classifier.py # 15 message types
100
+ ├── classifier.py # 15 message types
99
101
  ├── semantic.py # Local semantic search
100
102
  ├── mcp_server.py # MCP protocol server (14 tools)
101
- └── cli.py # 15 CLI commands
103
+ └── cli.py # CLI commands
102
104
  ```
103
105
 
104
- ## MCP Server (14 Tools)
106
+ ## MCP Server (15 Tools)
105
107
 
106
108
  Works with **Claude Desktop, Cursor, Windsurf** natively:
107
109
 
@@ -110,7 +112,7 @@ Works with **Claude Desktop, Cursor, Windsurf** natively:
110
112
  claude mcp add ompa -- python -m ompa.mcp_server
111
113
  ```
112
114
 
113
- Tools: `ao_session_start`, `ao_classify`, `ao_search`, `ao_kg_query`, `ao_kg_add`, `ao_kg_stats`, `ao_palace_wings`, `ao_palace_rooms`, `ao_palace_tunnel`, `ao_validate`, `ao_wrap_up`, `ao_status`, `ao_orphans`, `ao_init`
115
+ Tools: `ao_session_start`, `ao_classify`, `ao_search`, `ao_kg_query`, `ao_kg_add`, `ao_palace_wings`, `ao_palace_rooms`, `ao_palace_tunnel`, `ao_validate`, `ao_wrap_up`, `ao_status`, `ao_orphans`, `ao_init`, `ao_search`, `ao_stop`
114
116
 
115
117
  ## Python API
116
118
 
@@ -163,9 +165,8 @@ Unlike MemPalace (Claude Code + MCP only) or obsidian-mind (Claude Code hooks on
163
165
  ## Installation
164
166
 
165
167
  ```bash
166
- pip install ompa # Core (vault, palace, KG, CLI)
167
- pip install ompa[semantic] # + sentence-transformers for semantic search
168
- pip install ompa[all] # All optional dependencies
168
+ pip install ompa # Core only
169
+ pip install ompa[all] # All dependencies including sentence-transformers
169
170
  ```
170
171
 
171
172
  Requires Python 3.10+.
@@ -177,17 +178,17 @@ Because memory should not be coupled to your agent framework. Build once, use an
177
178
  ## Comparison
178
179
 
179
180
  | Feature | OMPA | MemPalace | obsidian-mind |
180
- |---------|------|-----------|---------------|
181
+ |---------|-----------------|------------|---------------|
181
182
  | Framework | Any | Claude Code | Claude Code |
182
183
  | Memory type | Vault + Palace + KG | Palace + KG | Vault only |
183
184
  | Semantic search | Local (free) | ChromaDB API | QMD (paid) |
184
- | Temporal KG | SQLite | SQLite | - |
185
- | MCP server | 14 tools | 15 tools | - |
186
- | CLI | 15 commands | - | - |
185
+ | Temporal KG | SQLite | SQLite | |
186
+ | MCP server | 15 tools | 15 tools | |
187
+ | CLI | 14 commands | | |
187
188
  | Hooks | 5 lifecycle | 3 lifecycle | 3 lifecycle |
188
189
  | Message types | 15 | 15 | 5 |
189
- | Verbatim storage | Yes | Yes | No |
190
- | Multi-agent | Yes | No | No |
190
+ | Verbatim storage | | | |
191
+ | Multi-agent | | | |
191
192
 
192
193
  ## License
193
194
 
@@ -15,7 +15,7 @@ Usage:
15
15
  ao.stop()
16
16
  """
17
17
 
18
- __version__ = "0.2.0"
18
+ __version__ = "0.2.2"
19
19
 
20
20
  from .core import Ompa
21
21
  from .vault import Vault, Note, VaultConfig
@@ -1,4 +1,5 @@
1
- """Allow running OMPA as: python -m ompa"""
1
+ """Allow running OMPA as a module: python -m ompa"""
2
+
2
3
  from ompa.cli import main
3
4
 
4
5
  if __name__ == "__main__":
@@ -2,6 +2,7 @@
2
2
  Message classification for routing and context injection.
3
3
  Classifies user messages into categories and injects routing hints.
4
4
  """
5
+
5
6
  import re
6
7
  from enum import Enum
7
8
  from dataclasses import dataclass
@@ -39,7 +40,7 @@ class MessageClassifier:
39
40
  Classifies user messages and provides routing guidance.
40
41
  Inspired by obsidian-minds classify-message.py but framework-agnostic.
41
42
  """
42
-
43
+
43
44
  # Regex patterns for classification
44
45
  PATTERNS = {
45
46
  MessageType.DECISION: [
@@ -109,7 +110,7 @@ class MessageClassifier:
109
110
  r"\b(start session|start work|starting)\b",
110
111
  ],
111
112
  }
112
-
113
+
113
114
  # Routing hints per message type
114
115
  ROUTING_HINTS = {
115
116
  MessageType.DECISION: [
@@ -173,7 +174,7 @@ class MessageClassifier:
173
174
  "Read brain/North Star.md first",
174
175
  ],
175
176
  }
176
-
177
+
177
178
  # Suggested folder locations
178
179
  FOLDER_MAP = {
179
180
  MessageType.DECISION: "work/active/",
@@ -191,20 +192,20 @@ class MessageClassifier:
191
192
  MessageType.WRAP_UP: "brain/",
192
193
  MessageType.STANDUP: "brain/",
193
194
  }
194
-
195
+
195
196
  def classify(self, message: str) -> Classification:
196
197
  """
197
198
  Classify a user message and return routing guidance.
198
-
199
+
199
200
  Args:
200
201
  message: The user message text
201
-
202
+
202
203
  Returns:
203
204
  Classification with type, confidence, hints, and suggested actions
204
205
  """
205
206
  message_lower = message.lower()
206
207
  scores = {}
207
-
208
+
208
209
  for msg_type, patterns in self.PATTERNS.items():
209
210
  score = 0
210
211
  for pattern in patterns:
@@ -212,32 +213,32 @@ class MessageClassifier:
212
213
  score += 1
213
214
  if score > 0:
214
215
  scores[msg_type] = score
215
-
216
+
216
217
  if not scores:
217
218
  return Classification(
218
219
  message_type=MessageType.UNKNOWN,
219
220
  confidence=0.0,
220
221
  routing_hints=["Process as normal conversation"],
221
222
  suggested_folder="thinking/",
222
- suggested_action="Continue conversation normally"
223
+ suggested_action="Continue conversation normally",
223
224
  )
224
-
225
+
225
226
  # Get highest scoring type
226
227
  best_type = max(scores, key=scores.get)
227
228
  confidence = min(scores[best_type] / 3.0, 1.0) # Normalize to 0-1
228
-
229
+
229
230
  # For short messages, reduce confidence
230
231
  if len(message.split()) < 5:
231
232
  confidence *= 0.7
232
-
233
+
233
234
  return Classification(
234
235
  message_type=best_type,
235
236
  confidence=confidence,
236
237
  routing_hints=self.ROUTING_HINTS.get(best_type, []),
237
238
  suggested_folder=self.FOLDER_MAP.get(best_type, "thinking/"),
238
- suggested_action=self._get_action(best_type)
239
+ suggested_action=self._get_action(best_type),
239
240
  )
240
-
241
+
241
242
  def _get_action(self, msg_type: MessageType) -> str:
242
243
  """Get the primary action for a message type."""
243
244
  actions = {
@@ -258,7 +259,7 @@ class MessageClassifier:
258
259
  MessageType.UNKNOWN: "Continue conversation",
259
260
  }
260
261
  return actions.get(msg_type, "Continue conversation")
261
-
262
+
262
263
  def get_routing_hint(self, message: str) -> str:
263
264
  """
264
265
  Get a single-line routing hint for a message.
@@ -2,13 +2,14 @@
2
2
  CLI for OMPA.
3
3
  Run with: ao <command> or ao-mcp <command>
4
4
  """
5
+
5
6
  from pathlib import Path
6
7
 
7
8
  import typer
8
9
  from rich.console import Console
9
10
  from rich.table import Table
10
11
 
11
- from ompa import Ompa, Palace, KnowledgeGraph
12
+ from ompa import Ompa
12
13
 
13
14
  app = typer.Typer(help="OMPA — Universal AI agent memory layer")
14
15
  console = Console()
@@ -20,6 +21,7 @@ def init(
20
21
  ):
21
22
  """Initialize vault + palace structure."""
22
23
  from ompa import Vault
24
+
23
25
  vault = Vault(vault_path)
24
26
  stats = vault.get_stats()
25
27
  ao = Ompa(vault_path, enable_semantic=False)
@@ -45,16 +47,12 @@ def status(
45
47
  console.print(f" Total notes: {vault_stats['total_notes']}")
46
48
  console.print(f" Brain notes: {vault_stats['brain_notes']}")
47
49
  console.print(f" Orphans: {vault_stats['orphans']}")
48
-
49
50
  console.print("[bold]Palace[/bold]")
50
51
  console.print(f" Wings: {palace_stats['wing_count']}")
51
52
  console.print(f" Rooms: {palace_stats['room_count']}")
52
53
  console.print(f" Drawers: {palace_stats['drawer_count']}")
53
- console.print(f" Tunnels: {palace_stats['tunnel_count']}")
54
-
55
54
  console.print("[bold]Knowledge Graph[/bold]")
56
55
  console.print(f" Entities: {kg_stats['entity_count']}")
57
- console.print(f" Triples: {kg_stats['triple_count']}")
58
56
  console.print(f" Current facts: {kg_stats['current_facts']}")
59
57
 
60
58
 
@@ -102,7 +100,11 @@ def search(
102
100
  table.add_column("Excerpt")
103
101
 
104
102
  for r in results:
105
- excerpt = r.content_excerpt[:80] + "..." if len(r.content_excerpt) > 80 else r.content_excerpt
103
+ excerpt = (
104
+ r.content_excerpt[:80] + "..."
105
+ if len(r.content_excerpt) > 80
106
+ else r.content_excerpt
107
+ )
106
108
  table.add_row(f"{r.score:.2f}", r.match_type, r.path, excerpt)
107
109
 
108
110
  console.print(table)
@@ -120,7 +122,11 @@ def orphans(
120
122
  else:
121
123
  console.print(f"[yellow]Found {len(orphan_notes)} orphan notes:[/yellow]")
122
124
  for note in orphan_notes:
123
- rel = note.path.relative_to(vault_path) if note.path.is_relative_to(vault_path) else note.path
125
+ rel = (
126
+ note.path.relative_to(vault_path)
127
+ if note.path.is_relative_to(vault_path)
128
+ else note.path
129
+ )
124
130
  console.print(f" - {rel}")
125
131
 
126
132
 
@@ -184,7 +190,9 @@ def tunnel(
184
190
  else:
185
191
  console.print(f"[bold]Tunnels between {wing_a} and {wing_b}:[/bold]")
186
192
  for t in tunnels:
187
- console.print(f" - {t['wing_a']}/{t['room']} <-> {t['wing_b']}/{t['room']}")
193
+ console.print(
194
+ f" - {t['wing_a']}/{t['room']} <-> {t['wing_b']}/{t['room']}"
195
+ )
188
196
 
189
197
 
190
198
  @app.command()
@@ -205,10 +213,7 @@ def kg_query(
205
213
  if as_of:
206
214
  console.print(f" (as of {as_of})")
207
215
  for t in triples:
208
- validity = f"({t.valid_from}"
209
- validity += f" to {t.valid_to}" if t.valid_to else ""
210
- validity += ")"
211
- console.print(f" {t.subject} --{t.predicate}--> {t.object} {validity}")
216
+ console.print(f" {t.subject} --{t.predicate}--> {t.object}")
212
217
 
213
218
 
214
219
  @app.command()
@@ -226,7 +231,7 @@ def kg_timeline(
226
231
 
227
232
  console.print(f"[bold]Timeline: {entity}[/bold]")
228
233
  for event in timeline:
229
- date_str = event["date"] or "(no date)"
234
+ date_str = event.get("date", "unknown")
230
235
  console.print(f" [{date_str}] {event['label']}")
231
236
 
232
237
 
@@ -249,6 +254,7 @@ def validate(
249
254
  """Validate all notes in the vault."""
250
255
  ao = Ompa(vault_path, enable_semantic=False)
251
256
  from ompa import Vault
257
+
252
258
  vault = Vault(vault_path)
253
259
  notes = vault.list_notes()
254
260
 
@@ -259,15 +265,17 @@ def validate(
259
265
  if result["warnings"]:
260
266
  total += 1
261
267
  for w in result["warnings"]:
262
- rel = note.path.relative_to(vault_path) if note.path.is_relative_to(vault_path) else note.path
268
+ rel = (
269
+ note.path.relative_to(vault_path)
270
+ if note.path.is_relative_to(vault_path)
271
+ else note.path
272
+ )
263
273
  warnings_list.append(f" {rel}: {w}")
264
274
 
265
275
  if warnings_list:
266
- console.print(f"[yellow]Found issues in {total} notes:[/yellow]")
267
- for w in warnings_list[:20]:
276
+ console.print(f"[yellow]Found {total} notes with warnings:[/yellow]")
277
+ for w in warnings_list:
268
278
  console.print(w)
269
- if len(warnings_list) > 20:
270
- console.print(f" ... and {len(warnings_list) - 20} more")
271
279
  else:
272
280
  console.print("[green]All notes valid![/green]")
273
281
 
@@ -279,11 +287,10 @@ def rebuild_index(
279
287
  """Rebuild the semantic search index."""
280
288
  ao = Ompa(vault_path, enable_semantic=True)
281
289
  count = ao.rebuild_index()
282
- console.print(f"[green]Indexed {count} files[/green]")
290
+ console.print(f"[green]Rebuilt index: {count} files indexed[/green]")
283
291
 
284
292
 
285
293
  def main():
286
- """Main entry point."""
287
294
  app()
288
295
 
289
296
 
@@ -2,6 +2,7 @@
2
2
  OMPA — Universal AI Agent Memory Layer
3
3
  Core module integrating vault, palace, KG, hooks, classifier, and semantic search.
4
4
  """
5
+
5
6
  from pathlib import Path
6
7
  from typing import Optional
7
8
 
@@ -9,7 +10,7 @@ from .vault import Vault
9
10
  from .palace import Palace
10
11
  from .knowledge_graph import KnowledgeGraph
11
12
  from .hooks import HookManager, HookResult
12
- from .classifier import MessageClassifier, Classification, MessageType
13
+ from .classifier import MessageClassifier, Classification
13
14
  from .semantic import SemanticIndex, SearchResult
14
15
 
15
16
 
@@ -45,7 +46,9 @@ class Ompa:
45
46
  # Core systems
46
47
  self.vault = Vault(self.vault_path)
47
48
  self.palace = Palace(self.vault_path / ".palace")
48
- self.kg = KnowledgeGraph(db_path=str(self.vault_path / ".palace" / "knowledge_graph.sqlite3"))
49
+ self.kg = KnowledgeGraph(
50
+ db_path=str(self.vault_path / ".palace" / "knowledge_graph.sqlite3")
51
+ )
49
52
  self.classifier = MessageClassifier()
50
53
  self.hooks = HookManager(self.vault_path, agent_name=self.agent_name)
51
54
 
@@ -152,8 +155,12 @@ class Ompa:
152
155
 
153
156
  self.palace.create_room(wing, room)
154
157
  self.palace.link_drawer(wing, room, str(path))
155
- except Exception:
156
- pass # Silently skip palace errors
158
+ except Exception as e:
159
+ import logging
160
+
161
+ logging.getLogger(__name__).debug(
162
+ "Palace auto-add failed for %s: %s", file_path, e
163
+ )
157
164
 
158
165
  # -------------------------------------------------------------------------
159
166
  # Classification
@@ -286,7 +293,9 @@ class Ompa:
286
293
  source: str = None,
287
294
  ) -> None:
288
295
  """Add a fact to the knowledge graph."""
289
- self.kg.add_triple(subject, predicate, object, valid_from=valid_from, source=source)
296
+ self.kg.add_triple(
297
+ subject, predicate, object, valid_from=valid_from, source=source
298
+ )
290
299
 
291
300
  def kg_query(self, entity: str, as_of: str = None) -> list:
292
301
  """Query the knowledge graph."""