oghma 0.0.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- oghma/__init__.py +1 -3
- oghma/cli.py +342 -0
- oghma/config.py +262 -0
- oghma/daemon.py +198 -0
- oghma/embedder.py +107 -0
- oghma/exporter.py +177 -0
- oghma/extractor.py +180 -0
- oghma/mcp_server.py +112 -0
- oghma/migration.py +63 -0
- oghma/parsers/__init__.py +26 -0
- oghma/parsers/base.py +24 -0
- oghma/parsers/claude_code.py +62 -0
- oghma/parsers/codex.py +84 -0
- oghma/parsers/openclaw.py +64 -0
- oghma/parsers/opencode.py +90 -0
- oghma/storage.py +753 -0
- oghma/watcher.py +97 -0
- oghma-0.3.0.dist-info/METADATA +26 -0
- oghma-0.3.0.dist-info/RECORD +22 -0
- {oghma-0.0.1.dist-info → oghma-0.3.0.dist-info}/WHEEL +2 -1
- oghma-0.3.0.dist-info/entry_points.txt +3 -0
- oghma-0.3.0.dist-info/top_level.txt +1 -0
- oghma-0.0.1.dist-info/METADATA +0 -33
- oghma-0.0.1.dist-info/RECORD +0 -4
oghma/watcher.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, cast
|
|
4
|
+
|
|
5
|
+
from oghma.config import Config
|
|
6
|
+
from oghma.parsers import Message, get_parser_for_file
|
|
7
|
+
from oghma.storage import Storage
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Watcher:
|
|
13
|
+
"""Discovers and tracks transcript files."""
|
|
14
|
+
|
|
15
|
+
BLOCKED_DIRS = {".venv", "node_modules", ".git", "__pycache__", "dist", "build"}
|
|
16
|
+
|
|
17
|
+
def __init__(self, config: Config, storage: Storage):
|
|
18
|
+
self.config = config
|
|
19
|
+
self.storage = storage
|
|
20
|
+
|
|
21
|
+
def discover_files(self) -> list[Path]:
|
|
22
|
+
"""Find all transcript files from configured tool paths."""
|
|
23
|
+
all_files: list[Path] = []
|
|
24
|
+
tools = cast(dict[str, Any], self.config.get("tools") or {})
|
|
25
|
+
|
|
26
|
+
for tool_name, tool_config in tools.items():
|
|
27
|
+
if not tool_config.get("enabled", False):
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
paths = tool_config.get("paths") or []
|
|
31
|
+
for pattern in paths:
|
|
32
|
+
files = self._expand_glob_pattern(Path(pattern).expanduser(), tool_name)
|
|
33
|
+
all_files.extend(files)
|
|
34
|
+
|
|
35
|
+
return sorted(set(all_files))
|
|
36
|
+
|
|
37
|
+
def _expand_glob_pattern(self, pattern: Path, tool_name: str) -> list[Path]:
|
|
38
|
+
"""Expand a glob pattern and filter blocked directories."""
|
|
39
|
+
files: list[Path] = []
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
base_path = pattern.parent
|
|
43
|
+
glob_pattern = pattern.name
|
|
44
|
+
|
|
45
|
+
if "*" in str(base_path):
|
|
46
|
+
matched_paths = Path(str(base_path)).parent.glob(base_path.name)
|
|
47
|
+
for matched_dir in matched_paths:
|
|
48
|
+
if matched_dir.is_dir():
|
|
49
|
+
files.extend(matched_dir.glob(glob_pattern))
|
|
50
|
+
else:
|
|
51
|
+
if base_path.exists():
|
|
52
|
+
files.extend(base_path.glob(glob_pattern))
|
|
53
|
+
|
|
54
|
+
files = [f for f in files if f.is_file() and self._is_allowed(f)]
|
|
55
|
+
except (OSError, PermissionError):
|
|
56
|
+
logger.warning(f"Failed to expand pattern: {pattern}")
|
|
57
|
+
|
|
58
|
+
return files
|
|
59
|
+
|
|
60
|
+
def _is_allowed(self, path: Path) -> bool:
|
|
61
|
+
"""Check if path is allowed (not in blocked directories)."""
|
|
62
|
+
parts = path.parts
|
|
63
|
+
return not any(part in self.BLOCKED_DIRS for part in parts)
|
|
64
|
+
|
|
65
|
+
def get_changed_files(self) -> list[Path]:
|
|
66
|
+
"""Return files that changed since last check (based on mtime)."""
|
|
67
|
+
all_files = self.discover_files()
|
|
68
|
+
changed_files: list[Path] = []
|
|
69
|
+
|
|
70
|
+
for file_path in all_files:
|
|
71
|
+
if not file_path.exists():
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
current_mtime = file_path.stat().st_mtime
|
|
75
|
+
current_size = file_path.stat().st_size
|
|
76
|
+
|
|
77
|
+
state = self.storage.get_extraction_state(str(file_path))
|
|
78
|
+
|
|
79
|
+
if state is None:
|
|
80
|
+
changed_files.append(file_path)
|
|
81
|
+
elif current_mtime > state["last_mtime"] or current_size != state["last_size"]:
|
|
82
|
+
changed_files.append(file_path)
|
|
83
|
+
|
|
84
|
+
return changed_files
|
|
85
|
+
|
|
86
|
+
def parse_file(self, file_path: Path) -> list[Message]:
|
|
87
|
+
"""Parse a transcript file using appropriate parser."""
|
|
88
|
+
parser = get_parser_for_file(file_path)
|
|
89
|
+
if parser:
|
|
90
|
+
return parser.parse(file_path)
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
def should_process(self, messages: list[Message]) -> bool:
|
|
94
|
+
"""Check if file has enough messages to process."""
|
|
95
|
+
daemon_config = cast(dict[str, Any], self.config.get("daemon") or {})
|
|
96
|
+
min_messages = daemon_config.get("min_messages", 6)
|
|
97
|
+
return len(messages) >= min_messages
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oghma
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Unified AI memory layer for coding assistants
|
|
5
|
+
Author: Oghma Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Requires-Dist: click>=8.0
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Requires-Dist: openai>=1.0
|
|
18
|
+
Requires-Dist: rich>=13.0
|
|
19
|
+
Requires-Dist: mcp[cli]>=1.0
|
|
20
|
+
Requires-Dist: sqlite-vec>=0.1.0
|
|
21
|
+
Provides-Extra: local
|
|
22
|
+
Requires-Dist: sentence-transformers>=2.0; extra == "local"
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
oghma/__init__.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
|
|
2
|
+
oghma/cli.py,sha256=2eb6TZKTj5UiaSID3LD-WyG0ozET2akZSU95aV6Ud_M,11965
|
|
3
|
+
oghma/config.py,sha256=AAaeIkzJEGJGWmJQJGtwQGpN661XoVB5Ih3hGV0VAjI,7663
|
|
4
|
+
oghma/daemon.py,sha256=FBxUzMqcgMhJEaGo3aZWuRE7OdoClIXL4w7wp0-g1Rg,6397
|
|
5
|
+
oghma/embedder.py,sha256=vtTENDoLM2yZL8eGb_itlHj7nDLalo2eiV-_W_-HuLk,3397
|
|
6
|
+
oghma/exporter.py,sha256=npBN_qsxQINy79A9pxa8XmtBOgYsUtdkAywka12O_4Q,6319
|
|
7
|
+
oghma/extractor.py,sha256=pqBCoWeMJK-paGcax8mZZcvJa7DmKrRJUGsHkqivRCE,6137
|
|
8
|
+
oghma/mcp_server.py,sha256=SShDGvaJ0wXLS2Gosnv9XW9RLHej-IQDnseEfqRMiGc,3189
|
|
9
|
+
oghma/migration.py,sha256=gb9ExvAW4nGm4-yJy2F7xuMF4DyzGK0x3F21OVg5XwM,1613
|
|
10
|
+
oghma/storage.py,sha256=lS7qC00O7Sa2P5FbKKg2ole66MrD2hNA_I8AGKKV3rs,26614
|
|
11
|
+
oghma/watcher.py,sha256=gcjuFCPMBLjt9IYHeveh_JmoKIK_AlqE9d5CSWKGrP0,3497
|
|
12
|
+
oghma/parsers/__init__.py,sha256=y2ymSDU2tuJByXyyMb3e6wRWSy8huR10vrSvegzn3Ak,703
|
|
13
|
+
oghma/parsers/base.py,sha256=3ZJOVtGobgCfjIL5zs5cGdo8FL5zeuINkVl3KOFiFvk,445
|
|
14
|
+
oghma/parsers/claude_code.py,sha256=Y28t7Y9Fn2iaUOtuiGugej7UsiEUirrxcUxEgIhuV9E,2001
|
|
15
|
+
oghma/parsers/codex.py,sha256=QaF5qhtCuPgKBXqVM-dJv0Fr5HZXsN4rYD6N65yJy54,2854
|
|
16
|
+
oghma/parsers/openclaw.py,sha256=F4PLsTDwOd7_7kdOhVBF5GZ0bpgdtPW-MN3GvbOdAwo,2028
|
|
17
|
+
oghma/parsers/opencode.py,sha256=Oz7NmH3hzTQ26JfwJTLuqwagcQUzhE7Du2N-kAj8_Dg,2738
|
|
18
|
+
oghma-0.3.0.dist-info/METADATA,sha256=KXrUxq3VDJU9-ykw_tKGgqWPJQh9az1gG2zjL3g6BjE,909
|
|
19
|
+
oghma-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
20
|
+
oghma-0.3.0.dist-info/entry_points.txt,sha256=1-W3Whiw00K0K63mYCazzNmlkxqSQjZ1A-6yyhq3fl0,75
|
|
21
|
+
oghma-0.3.0.dist-info/top_level.txt,sha256=i-cl63fF-H6gwspRwyLbnuQpMTymwaiib6B5YvQPuos,6
|
|
22
|
+
oghma-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
oghma
|
oghma-0.0.1.dist-info/METADATA
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: oghma
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: Unified AI memory layer — aggregate context across coding tools
|
|
5
|
-
Project-URL: Homepage, https://github.com/terry-li-hm/oghma
|
|
6
|
-
Project-URL: Repository, https://github.com/terry-li-hm/oghma
|
|
7
|
-
Author-email: Terry Li <terry.li.hm@gmail.com>
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
Keywords: ai,claude,coding-assistant,context,llm,memory
|
|
10
|
-
Classifier: Development Status :: 1 - Planning
|
|
11
|
-
Classifier: Environment :: Console
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
|
|
19
|
-
# Oghma
|
|
20
|
-
|
|
21
|
-
Unified AI memory layer — aggregate context across coding tools.
|
|
22
|
-
|
|
23
|
-
🚧 **Coming soon** — package name reserved.
|
|
24
|
-
|
|
25
|
-
## Vision
|
|
26
|
-
|
|
27
|
-
AI memory should be user-owned and tool-agnostic, not locked into provider silos.
|
|
28
|
-
|
|
29
|
-
Oghma passively watches conversation transcripts from multiple AI coding tools (Claude Code, Codex, OpenClaw, OpenCode, Cursor) and extracts memories, so any tool can access your full context.
|
|
30
|
-
|
|
31
|
-
## Links
|
|
32
|
-
|
|
33
|
-
- [GitHub](https://github.com/terry-li-hm/oghma)
|
oghma-0.0.1.dist-info/RECORD
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
oghma/__init__.py,sha256=LAU5q2GOG4tWxcsHGqqw_KEnVIP_TGIQcdSIQ5wO26g,64
|
|
2
|
-
oghma-0.0.1.dist-info/METADATA,sha256=m0Fc5KvQxtqY4N_ZrH2jexRGNuJraRa1qELZmqfhab4,1191
|
|
3
|
-
oghma-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
4
|
-
oghma-0.0.1.dist-info/RECORD,,
|