agmem 0.1.1__py3-none-any.whl → 0.1.3__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 (100) hide show
  1. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/METADATA +157 -16
  2. agmem-0.1.3.dist-info/RECORD +105 -0
  3. memvcs/__init__.py +1 -1
  4. memvcs/cli.py +45 -31
  5. memvcs/commands/__init__.py +9 -9
  6. memvcs/commands/add.py +83 -76
  7. memvcs/commands/audit.py +59 -0
  8. memvcs/commands/blame.py +46 -53
  9. memvcs/commands/branch.py +13 -33
  10. memvcs/commands/checkout.py +27 -32
  11. memvcs/commands/clean.py +18 -23
  12. memvcs/commands/clone.py +11 -1
  13. memvcs/commands/commit.py +40 -39
  14. memvcs/commands/daemon.py +109 -76
  15. memvcs/commands/decay.py +77 -0
  16. memvcs/commands/diff.py +56 -57
  17. memvcs/commands/distill.py +90 -0
  18. memvcs/commands/federated.py +53 -0
  19. memvcs/commands/fsck.py +86 -61
  20. memvcs/commands/garden.py +40 -35
  21. memvcs/commands/gc.py +51 -0
  22. memvcs/commands/graph.py +41 -48
  23. memvcs/commands/init.py +16 -24
  24. memvcs/commands/log.py +25 -40
  25. memvcs/commands/merge.py +69 -27
  26. memvcs/commands/pack.py +129 -0
  27. memvcs/commands/prove.py +66 -0
  28. memvcs/commands/pull.py +31 -1
  29. memvcs/commands/push.py +4 -2
  30. memvcs/commands/recall.py +145 -0
  31. memvcs/commands/reflog.py +13 -22
  32. memvcs/commands/remote.py +1 -0
  33. memvcs/commands/repair.py +66 -0
  34. memvcs/commands/reset.py +23 -33
  35. memvcs/commands/resolve.py +130 -0
  36. memvcs/commands/resurrect.py +82 -0
  37. memvcs/commands/search.py +3 -4
  38. memvcs/commands/serve.py +2 -1
  39. memvcs/commands/show.py +66 -36
  40. memvcs/commands/stash.py +34 -34
  41. memvcs/commands/status.py +27 -35
  42. memvcs/commands/tag.py +23 -47
  43. memvcs/commands/test.py +30 -44
  44. memvcs/commands/timeline.py +111 -0
  45. memvcs/commands/tree.py +26 -27
  46. memvcs/commands/verify.py +110 -0
  47. memvcs/commands/when.py +115 -0
  48. memvcs/core/access_index.py +167 -0
  49. memvcs/core/audit.py +124 -0
  50. memvcs/core/config_loader.py +3 -1
  51. memvcs/core/consistency.py +214 -0
  52. memvcs/core/crypto_verify.py +280 -0
  53. memvcs/core/decay.py +185 -0
  54. memvcs/core/diff.py +158 -143
  55. memvcs/core/distiller.py +277 -0
  56. memvcs/core/encryption.py +169 -0
  57. memvcs/core/federated.py +86 -0
  58. memvcs/core/gardener.py +176 -145
  59. memvcs/core/hooks.py +48 -14
  60. memvcs/core/ipfs_remote.py +39 -0
  61. memvcs/core/knowledge_graph.py +135 -138
  62. memvcs/core/llm/__init__.py +10 -0
  63. memvcs/core/llm/anthropic_provider.py +50 -0
  64. memvcs/core/llm/base.py +27 -0
  65. memvcs/core/llm/factory.py +30 -0
  66. memvcs/core/llm/openai_provider.py +36 -0
  67. memvcs/core/merge.py +260 -170
  68. memvcs/core/objects.py +110 -101
  69. memvcs/core/pack.py +92 -0
  70. memvcs/core/pii_scanner.py +147 -146
  71. memvcs/core/privacy_budget.py +63 -0
  72. memvcs/core/refs.py +132 -115
  73. memvcs/core/remote.py +38 -0
  74. memvcs/core/repository.py +254 -164
  75. memvcs/core/schema.py +155 -113
  76. memvcs/core/staging.py +60 -65
  77. memvcs/core/storage/__init__.py +20 -18
  78. memvcs/core/storage/base.py +74 -70
  79. memvcs/core/storage/gcs.py +70 -68
  80. memvcs/core/storage/local.py +42 -40
  81. memvcs/core/storage/s3.py +105 -110
  82. memvcs/core/temporal_index.py +121 -0
  83. memvcs/core/test_runner.py +101 -93
  84. memvcs/core/trust.py +103 -0
  85. memvcs/core/vector_store.py +56 -36
  86. memvcs/core/zk_proofs.py +26 -0
  87. memvcs/integrations/mcp_server.py +1 -3
  88. memvcs/integrations/web_ui/server.py +25 -26
  89. memvcs/retrieval/__init__.py +22 -0
  90. memvcs/retrieval/base.py +54 -0
  91. memvcs/retrieval/pack.py +128 -0
  92. memvcs/retrieval/recaller.py +105 -0
  93. memvcs/retrieval/strategies.py +314 -0
  94. memvcs/utils/__init__.py +3 -3
  95. memvcs/utils/helpers.py +52 -52
  96. agmem-0.1.1.dist-info/RECORD +0 -67
  97. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/WHEEL +0 -0
  98. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/entry_points.txt +0 -0
  99. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/licenses/LICENSE +0 -0
  100. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/top_level.txt +0 -0
@@ -10,27 +10,18 @@ from ..core.repository import Repository
10
10
 
11
11
  class CheckoutCommand:
12
12
  """Switch branches or restore working tree files."""
13
-
14
- name = 'checkout'
15
- help = 'Switch branches or restore working tree files'
16
-
13
+
14
+ name = "checkout"
15
+ help = "Switch branches or restore working tree files"
16
+
17
17
  @staticmethod
18
18
  def add_arguments(parser: argparse.ArgumentParser):
19
+ parser.add_argument("ref", help="Branch, tag, or commit to checkout")
20
+ parser.add_argument("-b", action="store_true", help="Create and checkout a new branch")
19
21
  parser.add_argument(
20
- 'ref',
21
- help='Branch, tag, or commit to checkout'
22
+ "--force", "-f", action="store_true", help="Force checkout (discard local changes)"
22
23
  )
23
- parser.add_argument(
24
- '-b',
25
- action='store_true',
26
- help='Create and checkout a new branch'
27
- )
28
- parser.add_argument(
29
- '--force', '-f',
30
- action='store_true',
31
- help='Force checkout (discard local changes)'
32
- )
33
-
24
+
34
25
  @staticmethod
35
26
  def execute(args) -> int:
36
27
  repo, code = require_repo()
@@ -40,24 +31,24 @@ class CheckoutCommand:
40
31
  # Create and checkout new branch
41
32
  if args.b:
42
33
  branch_name = args.ref
43
-
34
+
44
35
  # Check if branch already exists
45
36
  if repo.refs.branch_exists(branch_name):
46
37
  print(f"Error: A branch named '{branch_name}' already exists.")
47
38
  return 1
48
-
39
+
49
40
  # Get current HEAD commit
50
41
  head = repo.refs.get_head()
51
- if head['type'] == 'branch':
52
- current_commit = repo.refs.get_branch_commit(head['value'])
42
+ if head["type"] == "branch":
43
+ current_commit = repo.refs.get_branch_commit(head["value"])
53
44
  else:
54
- current_commit = head['value']
55
-
45
+ current_commit = head["value"]
46
+
56
47
  # Create branch
57
48
  if not repo.refs.create_branch(branch_name, current_commit):
58
49
  print(f"Error: Could not create branch '{branch_name}'")
59
50
  return 1
60
-
51
+
61
52
  # Switch to new branch
62
53
  try:
63
54
  repo.refs.set_head_branch(branch_name)
@@ -66,16 +57,16 @@ class CheckoutCommand:
66
57
  except Exception as e:
67
58
  print(f"Error switching to branch: {e}")
68
59
  return 1
69
-
60
+
70
61
  # Regular checkout
71
62
  ref = args.ref
72
-
63
+
73
64
  # Check if it's a branch
74
65
  is_branch = repo.refs.branch_exists(ref)
75
-
66
+
76
67
  try:
77
68
  commit_hash = repo.checkout(ref, force=args.force)
78
-
69
+
79
70
  if is_branch:
80
71
  print(f"Switched to branch '{ref}'")
81
72
  else:
@@ -83,16 +74,20 @@ class CheckoutCommand:
83
74
  if repo.refs.tag_exists(ref):
84
75
  print(f"Note: checking out '{ref}'.")
85
76
  print()
86
- print("You are in 'detached HEAD' state. You can look around, make experimental")
87
- print("changes and commit them, and you can discard any commits you make in this")
77
+ print(
78
+ "You are in 'detached HEAD' state. You can look around, make experimental"
79
+ )
80
+ print(
81
+ "changes and commit them, and you can discard any commits you make in this"
82
+ )
88
83
  print("state without impacting any branches by switching back to a branch.")
89
84
  else:
90
85
  print(f"Note: checking out '{commit_hash[:8]}'.")
91
86
  print()
92
87
  print("You are in 'detached HEAD' state.")
93
-
88
+
94
89
  return 0
95
-
90
+
96
91
  except ValueError as e:
97
92
  print(f"Error: {e}")
98
93
  return 1
memvcs/commands/clean.py CHANGED
@@ -12,52 +12,46 @@ from ..core.repository import Repository
12
12
 
13
13
  class CleanCommand:
14
14
  """Remove untracked files from working directory."""
15
-
16
- name = 'clean'
17
- help = 'Remove untracked files from working directory'
18
-
15
+
16
+ name = "clean"
17
+ help = "Remove untracked files from working directory"
18
+
19
19
  @staticmethod
20
20
  def add_arguments(parser: argparse.ArgumentParser):
21
21
  parser.add_argument(
22
- '-n', '--dry-run',
23
- action='store_true',
24
- help='Show what would be removed without removing'
25
- )
26
- parser.add_argument(
27
- '-f', '--force',
28
- action='store_true',
29
- help='Required to actually remove files'
22
+ "-n",
23
+ "--dry-run",
24
+ action="store_true",
25
+ help="Show what would be removed without removing",
30
26
  )
31
27
  parser.add_argument(
32
- '-d',
33
- action='store_true',
34
- help='Remove untracked directories too'
28
+ "-f", "--force", action="store_true", help="Required to actually remove files"
35
29
  )
36
-
30
+ parser.add_argument("-d", action="store_true", help="Remove untracked directories too")
31
+
37
32
  @staticmethod
38
33
  def execute(args) -> int:
39
34
  repo, code = require_repo()
40
35
  if code != 0:
41
36
  return code
42
37
 
43
-
44
38
  status = repo.get_status()
45
- untracked = status.get('untracked', [])
46
-
39
+ untracked = status.get("untracked", [])
40
+
47
41
  if not untracked:
48
42
  print("Nothing to clean.")
49
43
  return 0
50
-
44
+
51
45
  if args.dry_run:
52
46
  print("Would remove:")
53
47
  for p in untracked:
54
48
  print(f" {p}")
55
49
  return 0
56
-
50
+
57
51
  if not args.force:
58
52
  print("Use -f to force removal of untracked files.")
59
53
  return 1
60
-
54
+
61
55
  removed = 0
62
56
  for rel_path in untracked:
63
57
  full_path = repo.current_dir / rel_path
@@ -68,9 +62,10 @@ class CleanCommand:
68
62
  print(f"Removed {rel_path}")
69
63
  elif args.d and full_path.is_dir():
70
64
  import shutil
65
+
71
66
  shutil.rmtree(full_path)
72
67
  removed += 1
73
68
  print(f"Removed {rel_path}/")
74
-
69
+
75
70
  print(f"Removed {removed} file(s)")
76
71
  return 0
memvcs/commands/clone.py CHANGED
@@ -80,12 +80,22 @@ class CloneCommand:
80
80
 
81
81
  # Set remote origin to source
82
82
  import json
83
+
83
84
  config_file = target / ".mem" / "config.json"
84
85
  config = json.loads(config_file.read_text()) if config_file.exists() else {}
85
86
  if "remotes" not in config:
86
87
  config["remotes"] = {}
87
- config["remotes"]["origin"] = {"url": url if url.startswith("file://") else f"file://{remote_path}"}
88
+ config["remotes"]["origin"] = {
89
+ "url": url if url.startswith("file://") else f"file://{remote_path}"
90
+ }
88
91
  config_file.write_text(json.dumps(config, indent=2))
89
92
 
93
+ # Copy remote's public key to .mem/keys/remotes/origin.pub for trust store
94
+ remote_keys = remote_mem / "keys" / "public.pem"
95
+ if remote_keys.exists():
96
+ keys_remotes = target / ".mem" / "keys" / "remotes"
97
+ keys_remotes.mkdir(parents=True, exist_ok=True)
98
+ shutil.copy2(remote_keys, keys_remotes / "origin.pub")
99
+
90
100
  print(f"Cloned into {target}")
91
101
  return 0
memvcs/commands/commit.py CHANGED
@@ -7,88 +7,82 @@ from datetime import datetime
7
7
 
8
8
  from ..commands.base import require_repo
9
9
  from ..core.schema import SchemaValidator
10
- from ..core.hooks import run_pre_commit_hooks
10
+ from ..core.hooks import run_pre_commit_hooks, compute_suggested_importance
11
11
 
12
12
 
13
13
  class CommitCommand:
14
14
  """Create a commit from staged changes."""
15
-
16
- name = 'commit'
17
- help = 'Save staged changes as a memory snapshot'
18
-
15
+
16
+ name = "commit"
17
+ help = "Save staged changes as a memory snapshot"
18
+
19
19
  @staticmethod
20
20
  def add_arguments(parser: argparse.ArgumentParser):
21
21
  parser.add_argument(
22
- '-m', '--message',
23
- required=True,
24
- help='Commit message describing the changes'
25
- )
26
- parser.add_argument(
27
- '--author',
28
- help='Override default author'
22
+ "-m", "--message", required=True, help="Commit message describing the changes"
29
23
  )
24
+ parser.add_argument("--author", help="Override default author")
30
25
  parser.add_argument(
31
- '--no-verify',
32
- action='store_true',
33
- help='Skip pre-commit hooks and schema validation'
26
+ "--no-verify", action="store_true", help="Skip pre-commit hooks and schema validation"
34
27
  )
28
+ parser.add_argument("--strict", action="store_true", help="Treat schema warnings as errors")
35
29
  parser.add_argument(
36
- '--strict',
37
- action='store_true',
38
- help='Treat schema warnings as errors'
30
+ "--run-tests", action="store_true", help="Run memory tests before committing"
39
31
  )
40
32
  parser.add_argument(
41
- '--run-tests',
42
- action='store_true',
43
- help='Run memory tests before committing'
33
+ "--importance",
34
+ type=float,
35
+ metavar="SCORE",
36
+ help="Importance score 0.0-1.0 for recall/decay weighting",
44
37
  )
45
-
38
+
46
39
  @staticmethod
47
40
  def _get_blob_hash(file_info) -> str:
48
41
  """Get blob hash from StagedFile or dict (for hooks that pass either)."""
49
- if hasattr(file_info, 'blob_hash'):
42
+ if hasattr(file_info, "blob_hash"):
50
43
  return file_info.blob_hash
51
44
  if isinstance(file_info, dict):
52
- return file_info.get('blob_hash') or file_info.get('hash') or ''
53
- return ''
45
+ return file_info.get("blob_hash") or file_info.get("hash") or ""
46
+ return ""
54
47
 
55
48
  @staticmethod
56
49
  def _validate_staged_files(repo, staged: dict, strict: bool) -> tuple:
57
50
  """
58
51
  Validate staged files for schema compliance.
59
-
52
+
60
53
  Returns:
61
54
  Tuple of (success, validation_results)
62
55
  """
63
56
  validation_results = {}
64
57
  has_errors = False
65
-
58
+
66
59
  for filepath, file_info in staged.items():
67
60
  blob_hash = CommitCommand._get_blob_hash(file_info)
68
61
  if not blob_hash:
69
62
  continue
70
-
63
+
71
64
  # Read content from object store
72
65
  from ..core.objects import Blob
66
+
73
67
  blob = Blob.load(repo.object_store, blob_hash)
74
68
  if not blob:
75
69
  continue
76
-
70
+
77
71
  try:
78
- content = blob.content.decode('utf-8')
72
+ content = blob.content.decode("utf-8")
79
73
  except UnicodeDecodeError:
80
74
  # Skip binary files
81
75
  continue
82
-
76
+
83
77
  # Validate the file
84
78
  result = SchemaValidator.validate(content, filepath, strict=strict)
85
79
  validation_results[filepath] = result
86
-
80
+
87
81
  if not result.valid:
88
82
  has_errors = True
89
-
83
+
90
84
  return not has_errors, validation_results
91
-
85
+
92
86
  @staticmethod
93
87
  def _print_validation_results(results: dict) -> None:
94
88
  """Print validation results in a readable format."""
@@ -126,6 +120,7 @@ class CommitCommand:
126
120
  """Run memory tests if available. Returns 1 on failure, 0 on success or skip."""
127
121
  try:
128
122
  from ..core.test_runner import TestRunner
123
+
129
124
  test_runner = TestRunner(repo)
130
125
  test_result = test_runner.run_all()
131
126
  if not test_result.passed:
@@ -156,13 +151,19 @@ class CommitCommand:
156
151
  if args.run_tests:
157
152
  if CommitCommand._run_memory_tests(repo) != 0:
158
153
  return 1
159
- metadata = {
160
- 'files_changed': len(staged),
161
- 'timestamp': datetime.utcnow().isoformat() + 'Z'
162
- }
154
+ metadata = {"files_changed": len(staged), "timestamp": datetime.utcnow().isoformat() + "Z"}
155
+ # Importance scoring: explicit --importance or auto from heuristics
156
+ if args.importance is not None:
157
+ if not (0.0 <= args.importance <= 1.0):
158
+ print("Error: --importance must be between 0.0 and 1.0")
159
+ return 1
160
+ metadata["importance"] = args.importance
161
+ else:
162
+ suggested = compute_suggested_importance(repo, staged, args.message, metadata)
163
+ metadata["importance"] = suggested
163
164
  if args.author:
164
165
  config = repo.get_config()
165
- config['author']['name'] = args.author
166
+ config["author"]["name"] = args.author
166
167
  repo.set_config(config)
167
168
  try:
168
169
  commit_hash = repo.commit(args.message, metadata)