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.
Files changed (67) hide show
  1. agmem-0.1.1.dist-info/METADATA +656 -0
  2. agmem-0.1.1.dist-info/RECORD +67 -0
  3. agmem-0.1.1.dist-info/WHEEL +5 -0
  4. agmem-0.1.1.dist-info/entry_points.txt +2 -0
  5. agmem-0.1.1.dist-info/licenses/LICENSE +21 -0
  6. agmem-0.1.1.dist-info/top_level.txt +1 -0
  7. memvcs/__init__.py +9 -0
  8. memvcs/cli.py +178 -0
  9. memvcs/commands/__init__.py +23 -0
  10. memvcs/commands/add.py +258 -0
  11. memvcs/commands/base.py +23 -0
  12. memvcs/commands/blame.py +169 -0
  13. memvcs/commands/branch.py +110 -0
  14. memvcs/commands/checkout.py +101 -0
  15. memvcs/commands/clean.py +76 -0
  16. memvcs/commands/clone.py +91 -0
  17. memvcs/commands/commit.py +174 -0
  18. memvcs/commands/daemon.py +267 -0
  19. memvcs/commands/diff.py +157 -0
  20. memvcs/commands/fsck.py +203 -0
  21. memvcs/commands/garden.py +107 -0
  22. memvcs/commands/graph.py +151 -0
  23. memvcs/commands/init.py +61 -0
  24. memvcs/commands/log.py +103 -0
  25. memvcs/commands/mcp.py +59 -0
  26. memvcs/commands/merge.py +88 -0
  27. memvcs/commands/pull.py +65 -0
  28. memvcs/commands/push.py +143 -0
  29. memvcs/commands/reflog.py +52 -0
  30. memvcs/commands/remote.py +51 -0
  31. memvcs/commands/reset.py +98 -0
  32. memvcs/commands/search.py +163 -0
  33. memvcs/commands/serve.py +54 -0
  34. memvcs/commands/show.py +125 -0
  35. memvcs/commands/stash.py +97 -0
  36. memvcs/commands/status.py +112 -0
  37. memvcs/commands/tag.py +117 -0
  38. memvcs/commands/test.py +132 -0
  39. memvcs/commands/tree.py +156 -0
  40. memvcs/core/__init__.py +21 -0
  41. memvcs/core/config_loader.py +245 -0
  42. memvcs/core/constants.py +12 -0
  43. memvcs/core/diff.py +380 -0
  44. memvcs/core/gardener.py +466 -0
  45. memvcs/core/hooks.py +151 -0
  46. memvcs/core/knowledge_graph.py +381 -0
  47. memvcs/core/merge.py +474 -0
  48. memvcs/core/objects.py +323 -0
  49. memvcs/core/pii_scanner.py +343 -0
  50. memvcs/core/refs.py +447 -0
  51. memvcs/core/remote.py +278 -0
  52. memvcs/core/repository.py +522 -0
  53. memvcs/core/schema.py +414 -0
  54. memvcs/core/staging.py +227 -0
  55. memvcs/core/storage/__init__.py +72 -0
  56. memvcs/core/storage/base.py +359 -0
  57. memvcs/core/storage/gcs.py +308 -0
  58. memvcs/core/storage/local.py +182 -0
  59. memvcs/core/storage/s3.py +369 -0
  60. memvcs/core/test_runner.py +371 -0
  61. memvcs/core/vector_store.py +313 -0
  62. memvcs/integrations/__init__.py +5 -0
  63. memvcs/integrations/mcp_server.py +267 -0
  64. memvcs/integrations/web_ui/__init__.py +1 -0
  65. memvcs/integrations/web_ui/server.py +352 -0
  66. memvcs/utils/__init__.py +9 -0
  67. memvcs/utils/helpers.py +178 -0
@@ -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}"