agmem 0.1.1__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.
- agmem-0.1.1.dist-info/METADATA +656 -0
- agmem-0.1.1.dist-info/RECORD +67 -0
- agmem-0.1.1.dist-info/WHEEL +5 -0
- agmem-0.1.1.dist-info/entry_points.txt +2 -0
- agmem-0.1.1.dist-info/licenses/LICENSE +21 -0
- agmem-0.1.1.dist-info/top_level.txt +1 -0
- memvcs/__init__.py +9 -0
- memvcs/cli.py +178 -0
- memvcs/commands/__init__.py +23 -0
- memvcs/commands/add.py +258 -0
- memvcs/commands/base.py +23 -0
- memvcs/commands/blame.py +169 -0
- memvcs/commands/branch.py +110 -0
- memvcs/commands/checkout.py +101 -0
- memvcs/commands/clean.py +76 -0
- memvcs/commands/clone.py +91 -0
- memvcs/commands/commit.py +174 -0
- memvcs/commands/daemon.py +267 -0
- memvcs/commands/diff.py +157 -0
- memvcs/commands/fsck.py +203 -0
- memvcs/commands/garden.py +107 -0
- memvcs/commands/graph.py +151 -0
- memvcs/commands/init.py +61 -0
- memvcs/commands/log.py +103 -0
- memvcs/commands/mcp.py +59 -0
- memvcs/commands/merge.py +88 -0
- memvcs/commands/pull.py +65 -0
- memvcs/commands/push.py +143 -0
- memvcs/commands/reflog.py +52 -0
- memvcs/commands/remote.py +51 -0
- memvcs/commands/reset.py +98 -0
- memvcs/commands/search.py +163 -0
- memvcs/commands/serve.py +54 -0
- memvcs/commands/show.py +125 -0
- memvcs/commands/stash.py +97 -0
- memvcs/commands/status.py +112 -0
- memvcs/commands/tag.py +117 -0
- memvcs/commands/test.py +132 -0
- memvcs/commands/tree.py +156 -0
- memvcs/core/__init__.py +21 -0
- memvcs/core/config_loader.py +245 -0
- memvcs/core/constants.py +12 -0
- memvcs/core/diff.py +380 -0
- memvcs/core/gardener.py +466 -0
- memvcs/core/hooks.py +151 -0
- memvcs/core/knowledge_graph.py +381 -0
- memvcs/core/merge.py +474 -0
- memvcs/core/objects.py +323 -0
- memvcs/core/pii_scanner.py +343 -0
- memvcs/core/refs.py +447 -0
- memvcs/core/remote.py +278 -0
- memvcs/core/repository.py +522 -0
- memvcs/core/schema.py +414 -0
- memvcs/core/staging.py +227 -0
- memvcs/core/storage/__init__.py +72 -0
- memvcs/core/storage/base.py +359 -0
- memvcs/core/storage/gcs.py +308 -0
- memvcs/core/storage/local.py +182 -0
- memvcs/core/storage/s3.py +369 -0
- memvcs/core/test_runner.py +371 -0
- memvcs/core/vector_store.py +313 -0
- memvcs/integrations/__init__.py +5 -0
- memvcs/integrations/mcp_server.py +267 -0
- memvcs/integrations/web_ui/__init__.py +1 -0
- memvcs/integrations/web_ui/server.py +352 -0
- memvcs/utils/__init__.py +9 -0
- memvcs/utils/helpers.py +178 -0
memvcs/utils/helpers.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Helper utility functions."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def find_repo_root(start_path: Optional[Path] = None) -> Optional[Path]:
|
|
10
|
+
"""
|
|
11
|
+
Find the repository root by looking for .mem directory.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
start_path: Path to start searching from (default: current directory)
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Path to repository root or None if not found
|
|
18
|
+
"""
|
|
19
|
+
if start_path is None:
|
|
20
|
+
start_path = Path('.').resolve()
|
|
21
|
+
|
|
22
|
+
current = start_path
|
|
23
|
+
|
|
24
|
+
while current != current.parent:
|
|
25
|
+
if (current / '.mem').exists():
|
|
26
|
+
return current
|
|
27
|
+
current = current.parent
|
|
28
|
+
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def format_timestamp(timestamp_str: str, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
|
|
33
|
+
"""
|
|
34
|
+
Format an ISO timestamp string.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
timestamp_str: ISO format timestamp
|
|
38
|
+
format_str: Output format string
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Formatted timestamp string
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
# Handle 'Z' suffix
|
|
45
|
+
if timestamp_str.endswith('Z'):
|
|
46
|
+
timestamp_str = timestamp_str[:-1]
|
|
47
|
+
|
|
48
|
+
dt = datetime.fromisoformat(timestamp_str)
|
|
49
|
+
return dt.strftime(format_str)
|
|
50
|
+
except (ValueError, TypeError):
|
|
51
|
+
return timestamp_str
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def shorten_hash(hash_id: str, length: int = 8) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Shorten a hash for display.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
hash_id: Full hash string
|
|
60
|
+
length: Length of shortened hash
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Shortened hash
|
|
64
|
+
"""
|
|
65
|
+
if len(hash_id) <= length:
|
|
66
|
+
return hash_id
|
|
67
|
+
return hash_id[:length]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def human_readable_size(size_bytes: int) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Convert bytes to human readable format.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
size_bytes: Size in bytes
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Human readable string (e.g., "1.5 MB")
|
|
79
|
+
"""
|
|
80
|
+
if size_bytes < 1024:
|
|
81
|
+
return f"{size_bytes} B"
|
|
82
|
+
elif size_bytes < 1024 ** 2:
|
|
83
|
+
return f"{size_bytes / 1024:.1f} KB"
|
|
84
|
+
elif size_bytes < 1024 ** 3:
|
|
85
|
+
return f"{size_bytes / (1024 ** 2):.1f} MB"
|
|
86
|
+
else:
|
|
87
|
+
return f"{size_bytes / (1024 ** 3):.1f} GB"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def parse_memory_type(filepath: str) -> str:
|
|
91
|
+
"""
|
|
92
|
+
Parse memory type from file path.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
filepath: Path to memory file
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Memory type ('episodic', 'semantic', 'procedural', 'unknown')
|
|
99
|
+
"""
|
|
100
|
+
path_lower = filepath.lower()
|
|
101
|
+
|
|
102
|
+
if 'episodic' in path_lower:
|
|
103
|
+
return 'episodic'
|
|
104
|
+
elif 'semantic' in path_lower:
|
|
105
|
+
return 'semantic'
|
|
106
|
+
elif 'procedural' in path_lower or 'workflow' in path_lower:
|
|
107
|
+
return 'procedural'
|
|
108
|
+
elif 'checkpoint' in path_lower:
|
|
109
|
+
return 'checkpoint'
|
|
110
|
+
elif 'summary' in path_lower:
|
|
111
|
+
return 'summary'
|
|
112
|
+
|
|
113
|
+
return 'unknown'
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def is_binary_content(content: bytes) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
Check if content appears to be binary.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
content: Content bytes to check
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True if content appears binary
|
|
125
|
+
"""
|
|
126
|
+
# Check for null bytes
|
|
127
|
+
if b'\x00' in content:
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
# Check for high ratio of non-printable characters
|
|
131
|
+
if len(content) == 0:
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
text_chars = bytearray({7, 8, 9, 10, 12, 13, 27} | set(range(0x20, 0x100)) - {0x7f})
|
|
135
|
+
non_text = sum(1 for byte in content if byte not in text_chars)
|
|
136
|
+
|
|
137
|
+
return non_text / len(content) > 0.30
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def sanitize_filename(filename: str) -> str:
|
|
141
|
+
"""
|
|
142
|
+
Sanitize a filename for safe use.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
filename: Original filename
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Sanitized filename
|
|
149
|
+
"""
|
|
150
|
+
# Remove or replace unsafe characters
|
|
151
|
+
unsafe_chars = '<>:"/\\|?*'
|
|
152
|
+
for char in unsafe_chars:
|
|
153
|
+
filename = filename.replace(char, '_')
|
|
154
|
+
|
|
155
|
+
# Remove leading/trailing whitespace and dots
|
|
156
|
+
filename = filename.strip(' .')
|
|
157
|
+
|
|
158
|
+
# Ensure not empty
|
|
159
|
+
if not filename:
|
|
160
|
+
filename = 'unnamed'
|
|
161
|
+
|
|
162
|
+
return filename
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def generate_session_id() -> str:
|
|
166
|
+
"""
|
|
167
|
+
Generate a unique session ID.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Session ID string
|
|
171
|
+
"""
|
|
172
|
+
from datetime import datetime
|
|
173
|
+
import uuid
|
|
174
|
+
|
|
175
|
+
timestamp = datetime.utcnow().strftime('%Y%m%d-%H%M%S')
|
|
176
|
+
short_uuid = str(uuid.uuid4())[:8]
|
|
177
|
+
|
|
178
|
+
return f"session-{timestamp}-{short_uuid}"
|