agents-memory 0.42__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.
Files changed (36) hide show
  1. agents_memory-0.42/LICENSE +21 -0
  2. agents_memory-0.42/PKG-INFO +148 -0
  3. agents_memory-0.42/README.md +137 -0
  4. agents_memory-0.42/pyproject.toml +21 -0
  5. agents_memory-0.42/setup.cfg +4 -0
  6. agents_memory-0.42/src/agent_memory/__init__.py +18 -0
  7. agents_memory-0.42/src/agent_memory/__main__.py +92 -0
  8. agents_memory-0.42/src/agent_memory/cli_help.py +166 -0
  9. agents_memory-0.42/src/agent_memory/consolidate.py +22 -0
  10. agents_memory-0.42/src/agent_memory/extract_openai.py +102 -0
  11. agents_memory-0.42/src/agent_memory/ingest.py +110 -0
  12. agents_memory-0.42/src/agent_memory/ingest_catalog.py +346 -0
  13. agents_memory-0.42/src/agent_memory/ingest_chats.py +14 -0
  14. agents_memory-0.42/src/agent_memory/ingest_common.py +170 -0
  15. agents_memory-0.42/src/agent_memory/ingest_config.py +245 -0
  16. agents_memory-0.42/src/agent_memory/ingest_extractors.py +374 -0
  17. agents_memory-0.42/src/agent_memory/inventory.py +101 -0
  18. agents_memory-0.42/src/agent_memory/mcp_server.py +313 -0
  19. agents_memory-0.42/src/agent_memory/store.py +2211 -0
  20. agents_memory-0.42/src/agent_memory/sync.py +67 -0
  21. agents_memory-0.42/src/agents_memory.egg-info/PKG-INFO +148 -0
  22. agents_memory-0.42/src/agents_memory.egg-info/SOURCES.txt +34 -0
  23. agents_memory-0.42/src/agents_memory.egg-info/dependency_links.txt +1 -0
  24. agents_memory-0.42/src/agents_memory.egg-info/entry_points.txt +2 -0
  25. agents_memory-0.42/src/agents_memory.egg-info/requires.txt +1 -0
  26. agents_memory-0.42/src/agents_memory.egg-info/top_level.txt +1 -0
  27. agents_memory-0.42/tests/test_auto_triggering.py +163 -0
  28. agents_memory-0.42/tests/test_cli_comprehensive.py +97 -0
  29. agents_memory-0.42/tests/test_cli_help.py +52 -0
  30. agents_memory-0.42/tests/test_distill_benchmark.py +369 -0
  31. agents_memory-0.42/tests/test_extract_filters.py +125 -0
  32. agents_memory-0.42/tests/test_extract_openai.py +39 -0
  33. agents_memory-0.42/tests/test_ingest.py +103 -0
  34. agents_memory-0.42/tests/test_ingest_pipeline.py +114 -0
  35. agents_memory-0.42/tests/test_mcp_server.py +215 -0
  36. agents_memory-0.42/tests/test_store.py +402 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Felix Kempf, Fabian Schindler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: agents-memory
3
+ Version: 0.42
4
+ Summary: Local markdown memory for coding agents — ABI in abi/, Python MCP reference implementation
5
+ Author: Felix Kempf, Fabian Schindler
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: mcp>=1.0.0
10
+ Dynamic: license-file
11
+
12
+ # agent-memory
13
+
14
+ <p align="left">
15
+ <a href="https://github.com/Lolaplex/agent-memory/releases"><img src="https://img.shields.io/badge/version-0.42-blue.svg?style=flat-square" alt="Version 0.42"></a>
16
+ <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Standard-orange.svg?style=flat-square" alt="MCP"></a>
17
+ <a href="https://python.org"><img src="https://img.shields.io/badge/Python-3.10+-3776AB.svg?style=flat-square&logo=python&logoColor=white" alt="Python 3.10+"></a>
18
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
19
+ </p>
20
+
21
+ **Local markdown memory & cross-agent context engine for AI coding assistants.**
22
+ One persistent identity, shared across **Claude Code**, **Cursor**, **Antigravity**, and **Zed**.
23
+
24
+ ---
25
+
26
+ ## Why agent-memory?
27
+
28
+ Without a shared memory layout, **every AI tool lives in its own silo**:
29
+ - Each vendor locks chat history in proprietary local databases or remote clouds.
30
+ - Switching IDEs or agents means starting from scratch and repeating preferences.
31
+ - Vector DBs and RAG solutions add infrastructure complexity, drift out of date, and are not human-editable.
32
+
33
+ `agent-memory` solves this with a **pure Markdown-first architecture**:
34
+ - **Local & Offline:** Your identity and repo memory live in plain files (`~/.agents/memory/` and `<repo>/.agents/memory/`).
35
+ - **Human-Readable & Git-Friendly:** Edit with any text editor, diff with git, commit when you want.
36
+ - **Universal MCP Server:** Exposes memory tools (`search_memory`, `add_memory`, `get_project_memories`, `distill_batch`) to all modern agents.
37
+ - **Autonomous Ingest & Distillation:** Extracts durable rules and architecture decisions from session logs (OpenAI, Claude, Cursor, Copilot, Antigravity, Pi).
38
+
39
+ ---
40
+
41
+ ## Architecture & Flow
42
+
43
+ ```text
44
+ ┌─────────────────────────────────────────────────────────────┐
45
+ │ SESSION INGEST │
46
+ │ OpenAI Exports · Claude JSONL · Cursor · Antigravity · Pi │
47
+ └──────────────────────────────┬──────────────────────────────┘
48
+ │ ingest catalog / extract
49
+
50
+ ┌─────────────────────────────────────────────────────────────┐
51
+ │ STAGING INBOX │
52
+ │ Raw captured bullets & noise-filtered facts │
53
+ └──────────────────────────────┬──────────────────────────────┘
54
+ │ distill_batch / skill
55
+
56
+ ┌─────────────────────────────────────────────────────────────┐
57
+ │ LOCAL MEMORY STORE │
58
+ │ ~/.agents/memory/USER.md ~/.agents/memory/PROJECTS.md │
59
+ │ ~/.agents/memory/concepts/ <repo>/.agents/memory/ │
60
+ └──────────────┬───────────────────────────────┬──────────────┘
61
+ │ │
62
+ ▼ ▼
63
+ ┌─────────────────────────────┐ ┌─────────────────────────────┐
64
+ │ IDE INJECTION │ │ MCP SERVER & CLERK │
65
+ │ Cursor Rules · Zed Context │ │ search_memory · add_memory │
66
+ │ Antigravity AGENTS.md │ │ Universal Tool Integration │
67
+ └─────────────────────────────┘ └─────────────────────────────┘
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Quickstart
73
+
74
+ ### 1. Installation
75
+
76
+ ```bash
77
+ # Clone the repository
78
+ git clone https://github.com/Lolaplex/agent-memory.git
79
+ cd agent-memory
80
+
81
+ # Install in editable mode
82
+ python -m pip install -e .
83
+ ```
84
+
85
+ ### 2. Initialize & Sync
86
+
87
+ ```bash
88
+ # Scaffold initial directories and wire into host IDEs / MCP configs
89
+ agent-memory sync --init
90
+
91
+ # Audit and register local repositories
92
+ agent-memory inventory
93
+ ```
94
+
95
+ Fill in `~/.agents/memory/USER.md` with your profile and stack preferences. Reload your agent to connect to the MCP server.
96
+
97
+ ---
98
+
99
+ ## CLI Reference
100
+
101
+ | Command | Purpose |
102
+ |---------|---------|
103
+ | `agent-memory sync` | Updates canonical `AGENTS.md`, host rules, and MCP registrations |
104
+ | `agent-memory sync --init` | First-time scaffolding and host discovery |
105
+ | `agent-memory inventory` | Discovers unregistered local repositories across workspace roots |
106
+ | `agent-memory inventory --register SLUG PATH ROLE STACK` | Registers a new repository |
107
+ | `agent-memory ingest catalog` | Indexes local chat transcripts across all supported providers |
108
+ | `agent-memory ingest extract` | Runs heuristic filters to extract durable facts into staging |
109
+ | `agent-memory consolidate` | Ensures no private state leaked into working repository |
110
+
111
+ ---
112
+
113
+ ## Supported Ecosystem
114
+
115
+ - **Claude Code:** Bound via symlink / canonical `AGENTS.md` and MCP server.
116
+ - **Google Antigravity:** Integrated via `.gemini/config` rules and `agent-memory` MCP.
117
+ - **Cursor:** Automatically injects rules and configures `.cursor/mcp.json`.
118
+ - **Zed:** Configures `context_servers` and mirrors assistant skills.
119
+ - **VS Code / Copilot:** Ingests session history from local state databases.
120
+
121
+ ---
122
+
123
+ ## Open ABI Specification
124
+
125
+ The formal, implementation-agnostic layout specification lives in [`abi/`](abi/):
126
+
127
+ - [`abi/WHY.md`](abi/WHY.md) — Architecture decisions & why Markdown wins over RAG.
128
+ - [`abi/LAYOUT.md`](abi/LAYOUT.md) — Directory taxonomy and path contracts.
129
+ - [`abi/KINDS.md`](abi/KINDS.md) — Typed memory taxonomy (`concepts`, `facts`, `decisions`).
130
+ - [`abi/MCP.md`](abi/MCP.md) — Tool definitions and request/response specifications.
131
+ - [`abi/INGEST.md`](abi/INGEST.md) — Catalog, extract, and distillation pipeline.
132
+ - [`abi/INJECTION.md`](abi/INJECTION.md) — Host rule injection mechanisms.
133
+
134
+ ---
135
+
136
+ ## Testing & Verification
137
+
138
+ Run the comprehensive test suite and distillation benchmark:
139
+
140
+ ```bash
141
+ python tests/run_all_tests.py
142
+ ```
143
+
144
+ ---
145
+
146
+ ## License
147
+
148
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,137 @@
1
+ # agent-memory
2
+
3
+ <p align="left">
4
+ <a href="https://github.com/Lolaplex/agent-memory/releases"><img src="https://img.shields.io/badge/version-0.42-blue.svg?style=flat-square" alt="Version 0.42"></a>
5
+ <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Standard-orange.svg?style=flat-square" alt="MCP"></a>
6
+ <a href="https://python.org"><img src="https://img.shields.io/badge/Python-3.10+-3776AB.svg?style=flat-square&logo=python&logoColor=white" alt="Python 3.10+"></a>
7
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
8
+ </p>
9
+
10
+ **Local markdown memory & cross-agent context engine for AI coding assistants.**
11
+ One persistent identity, shared across **Claude Code**, **Cursor**, **Antigravity**, and **Zed**.
12
+
13
+ ---
14
+
15
+ ## Why agent-memory?
16
+
17
+ Without a shared memory layout, **every AI tool lives in its own silo**:
18
+ - Each vendor locks chat history in proprietary local databases or remote clouds.
19
+ - Switching IDEs or agents means starting from scratch and repeating preferences.
20
+ - Vector DBs and RAG solutions add infrastructure complexity, drift out of date, and are not human-editable.
21
+
22
+ `agent-memory` solves this with a **pure Markdown-first architecture**:
23
+ - **Local & Offline:** Your identity and repo memory live in plain files (`~/.agents/memory/` and `<repo>/.agents/memory/`).
24
+ - **Human-Readable & Git-Friendly:** Edit with any text editor, diff with git, commit when you want.
25
+ - **Universal MCP Server:** Exposes memory tools (`search_memory`, `add_memory`, `get_project_memories`, `distill_batch`) to all modern agents.
26
+ - **Autonomous Ingest & Distillation:** Extracts durable rules and architecture decisions from session logs (OpenAI, Claude, Cursor, Copilot, Antigravity, Pi).
27
+
28
+ ---
29
+
30
+ ## Architecture & Flow
31
+
32
+ ```text
33
+ ┌─────────────────────────────────────────────────────────────┐
34
+ │ SESSION INGEST │
35
+ │ OpenAI Exports · Claude JSONL · Cursor · Antigravity · Pi │
36
+ └──────────────────────────────┬──────────────────────────────┘
37
+ │ ingest catalog / extract
38
+
39
+ ┌─────────────────────────────────────────────────────────────┐
40
+ │ STAGING INBOX │
41
+ │ Raw captured bullets & noise-filtered facts │
42
+ └──────────────────────────────┬──────────────────────────────┘
43
+ │ distill_batch / skill
44
+
45
+ ┌─────────────────────────────────────────────────────────────┐
46
+ │ LOCAL MEMORY STORE │
47
+ │ ~/.agents/memory/USER.md ~/.agents/memory/PROJECTS.md │
48
+ │ ~/.agents/memory/concepts/ <repo>/.agents/memory/ │
49
+ └──────────────┬───────────────────────────────┬──────────────┘
50
+ │ │
51
+ ▼ ▼
52
+ ┌─────────────────────────────┐ ┌─────────────────────────────┐
53
+ │ IDE INJECTION │ │ MCP SERVER & CLERK │
54
+ │ Cursor Rules · Zed Context │ │ search_memory · add_memory │
55
+ │ Antigravity AGENTS.md │ │ Universal Tool Integration │
56
+ └─────────────────────────────┘ └─────────────────────────────┘
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Quickstart
62
+
63
+ ### 1. Installation
64
+
65
+ ```bash
66
+ # Clone the repository
67
+ git clone https://github.com/Lolaplex/agent-memory.git
68
+ cd agent-memory
69
+
70
+ # Install in editable mode
71
+ python -m pip install -e .
72
+ ```
73
+
74
+ ### 2. Initialize & Sync
75
+
76
+ ```bash
77
+ # Scaffold initial directories and wire into host IDEs / MCP configs
78
+ agent-memory sync --init
79
+
80
+ # Audit and register local repositories
81
+ agent-memory inventory
82
+ ```
83
+
84
+ Fill in `~/.agents/memory/USER.md` with your profile and stack preferences. Reload your agent to connect to the MCP server.
85
+
86
+ ---
87
+
88
+ ## CLI Reference
89
+
90
+ | Command | Purpose |
91
+ |---------|---------|
92
+ | `agent-memory sync` | Updates canonical `AGENTS.md`, host rules, and MCP registrations |
93
+ | `agent-memory sync --init` | First-time scaffolding and host discovery |
94
+ | `agent-memory inventory` | Discovers unregistered local repositories across workspace roots |
95
+ | `agent-memory inventory --register SLUG PATH ROLE STACK` | Registers a new repository |
96
+ | `agent-memory ingest catalog` | Indexes local chat transcripts across all supported providers |
97
+ | `agent-memory ingest extract` | Runs heuristic filters to extract durable facts into staging |
98
+ | `agent-memory consolidate` | Ensures no private state leaked into working repository |
99
+
100
+ ---
101
+
102
+ ## Supported Ecosystem
103
+
104
+ - **Claude Code:** Bound via symlink / canonical `AGENTS.md` and MCP server.
105
+ - **Google Antigravity:** Integrated via `.gemini/config` rules and `agent-memory` MCP.
106
+ - **Cursor:** Automatically injects rules and configures `.cursor/mcp.json`.
107
+ - **Zed:** Configures `context_servers` and mirrors assistant skills.
108
+ - **VS Code / Copilot:** Ingests session history from local state databases.
109
+
110
+ ---
111
+
112
+ ## Open ABI Specification
113
+
114
+ The formal, implementation-agnostic layout specification lives in [`abi/`](abi/):
115
+
116
+ - [`abi/WHY.md`](abi/WHY.md) — Architecture decisions & why Markdown wins over RAG.
117
+ - [`abi/LAYOUT.md`](abi/LAYOUT.md) — Directory taxonomy and path contracts.
118
+ - [`abi/KINDS.md`](abi/KINDS.md) — Typed memory taxonomy (`concepts`, `facts`, `decisions`).
119
+ - [`abi/MCP.md`](abi/MCP.md) — Tool definitions and request/response specifications.
120
+ - [`abi/INGEST.md`](abi/INGEST.md) — Catalog, extract, and distillation pipeline.
121
+ - [`abi/INJECTION.md`](abi/INJECTION.md) — Host rule injection mechanisms.
122
+
123
+ ---
124
+
125
+ ## Testing & Verification
126
+
127
+ Run the comprehensive test suite and distillation benchmark:
128
+
129
+ ```bash
130
+ python tests/run_all_tests.py
131
+ ```
132
+
133
+ ---
134
+
135
+ ## License
136
+
137
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agents-memory"
7
+ version = "0.42"
8
+ description = "Local markdown memory for coding agents — ABI in abi/, Python MCP reference implementation"
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Felix Kempf" },
12
+ { name = "Fabian Schindler" },
13
+ ]
14
+ requires-python = ">=3.10"
15
+ dependencies = ["mcp>=1.0.0"]
16
+
17
+ [project.scripts]
18
+ agent-memory = "agent_memory.__main__:main"
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ """Local markdown memory — reference implementation of the agent-memory ABI."""
2
+ from __future__ import annotations
3
+
4
+ from pathlib import Path
5
+
6
+ __all__ = ["ROOT", "__version__"]
7
+
8
+
9
+ def _engine_root() -> Path:
10
+ here = Path(__file__).resolve().parent
11
+ for path in [here, *here.parents]:
12
+ if (path / "abi" / "VERSION").is_file():
13
+ return path
14
+ return here.parents[2]
15
+
16
+
17
+ ROOT = _engine_root()
18
+ __version__ = (ROOT / "abi" / "VERSION").read_text(encoding="utf-8").strip()
@@ -0,0 +1,92 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ # Bootstrap src/ on sys.path if invoked without editable install
5
+ _SRC_DIR = str(Path(__file__).resolve().parent.parent)
6
+ if _SRC_DIR not in sys.path:
7
+ sys.path.insert(0, _SRC_DIR)
8
+
9
+ if hasattr(sys.stdout, "reconfigure"):
10
+ try:
11
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
12
+ except Exception:
13
+ pass
14
+
15
+ USAGE = """Usage: python -m agent_memory COMMAND [args]
16
+
17
+ Commands:
18
+ sync Rewrite always-on injection
19
+ inventory Disk vs PROJECTS.md
20
+ ingest Catalog / extract pipeline
21
+ consolidate Move clone leaks into ~/.agents/memory
22
+ extract-openai Filter Open AI GDPR export into staging
23
+ distill Inspect staging inbox for distillation
24
+ mcp stdio MCP server
25
+ help-json Machine-readable CLI + injection spec
26
+ """
27
+
28
+
29
+ def main(argv: list[str] | None = None) -> int:
30
+ args = list(argv if argv is not None else sys.argv[1:])
31
+ if not args or args[0] in ("-h", "--help"):
32
+ print(USAGE, end="")
33
+ return 0 if args else 2
34
+ if args[0] in ("--help-json", "help-json"):
35
+ from .cli_help import main as help_main
36
+
37
+ return help_main(["--help-json"])
38
+ cmd, rest = args[0], args[1:]
39
+ if cmd == "sync":
40
+ from .sync import main as run
41
+
42
+ return run(rest)
43
+ if cmd == "inventory":
44
+ from .inventory import main as run
45
+
46
+ return run(rest)
47
+ if cmd == "ingest":
48
+ from .ingest import main as run
49
+
50
+ return run(rest)
51
+ if cmd == "distill":
52
+ from .store import get_staging_inbox
53
+
54
+ inbox = get_staging_inbox(limit=15)
55
+ if inbox["total"] == 0:
56
+ print("Staging inbox is empty (all caught up).")
57
+ return 0
58
+ print(f"Staging inbox: {inbox['total']} bullets ({inbox['shown']} shown)")
59
+ for group in inbox["groups"]:
60
+ label = group.get("source") or group.get("file")
61
+ extra = ""
62
+ if group.get("truncated"):
63
+ extra = f" (showing {group['count']} of {group['count']}+)"
64
+ print(f"\n## {label}{extra}")
65
+ for item in group["bullets"]:
66
+ title = item.get("title") or ""
67
+ prefix = f"[{title}] " if title else ""
68
+ print(f"- {prefix}{item.get('text') or item.get('bullet')}")
69
+ print("\nTo distill, tell your Agent: 'run memory-distill' or use the memory-distill skill.")
70
+ return 0
71
+ if cmd in ("ingest-chats", "ingest_chats"):
72
+ from .ingest_chats import main as run
73
+
74
+ return run()
75
+ if cmd == "consolidate":
76
+ from .consolidate import main as run
77
+
78
+ return run()
79
+ if cmd in ("extract-openai", "extract_openai"):
80
+ from .extract_openai import main as run
81
+
82
+ return run(rest)
83
+ if cmd in ("mcp", "mcp-server", "mcp_server"):
84
+ from .mcp_server import main as run
85
+
86
+ return run()
87
+ print(f"unknown command: {cmd}\n{USAGE}", file=sys.stderr)
88
+ return 2
89
+
90
+
91
+ if __name__ == "__main__":
92
+ raise SystemExit(main())
@@ -0,0 +1,166 @@
1
+ """Machine-readable CLI specs derived from argparse (source of truth)."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from typing import Any, List
8
+
9
+ from . import __version__ as ABI_VERSION
10
+
11
+
12
+ def _json_safe(value: Any) -> Any:
13
+ if value is None or isinstance(value, (bool, int, float, str)):
14
+ return value
15
+ if isinstance(value, (list, tuple)):
16
+ return [_json_safe(v) for v in value]
17
+ return str(value)
18
+
19
+
20
+ def _action_spec(action: argparse.Action) -> dict[str, Any] | None:
21
+ if action.help is argparse.SUPPRESS:
22
+ return None
23
+ if action.option_strings and action.option_strings[0] in ("-h", "--help"):
24
+ return None
25
+ if action.dest in ("help", "func"):
26
+ return None
27
+ if isinstance(action, argparse._SubParsersAction):
28
+ return None
29
+ flags = list(action.option_strings)
30
+ is_option = bool(flags)
31
+ is_flag = action.nargs == 0 or isinstance(action, argparse._StoreTrueAction)
32
+ spec: dict[str, Any] = {
33
+ "name": action.metavar or action.dest,
34
+ "dest": action.dest,
35
+ "help": action.help or "",
36
+ "required": bool(action.required),
37
+ }
38
+ if is_option:
39
+ spec["flags"] = flags
40
+ spec["kind"] = "flag" if is_flag else "option"
41
+ else:
42
+ spec["kind"] = "argument"
43
+ if action.nargs is not None:
44
+ spec["nargs"] = str(action.nargs)
45
+ default = action.default
46
+ if default is not argparse.SUPPRESS and default is not None:
47
+ spec["default"] = _json_safe(default)
48
+ if action.choices:
49
+ spec["choices"] = [_json_safe(c) for c in action.choices]
50
+ return spec
51
+
52
+
53
+ def cli_spec(parser: argparse.ArgumentParser, *, name: str, description: str = "") -> dict[str, Any]:
54
+ options: List[dict[str, Any]] = []
55
+ arguments: List[dict[str, Any]] = []
56
+ for action in parser._actions:
57
+ item = _action_spec(action)
58
+ if not item:
59
+ continue
60
+ if item["kind"] == "argument":
61
+ arguments.append(item)
62
+ else:
63
+ options.append(item)
64
+ return {
65
+ "name": name,
66
+ "abi_version": ABI_VERSION,
67
+ "description": description or parser.description or "",
68
+ "usage": parser.format_usage().strip(),
69
+ "options": options,
70
+ "arguments": arguments,
71
+ }
72
+
73
+
74
+ def injection_spec() -> dict[str, Any]:
75
+ return {
76
+ "scan_skips": [".agents", ".cursor", ".git", "node_modules"],
77
+ "no_empty_folders": True,
78
+ "generated_on_sync": [
79
+ {
80
+ "path": "~/.agents/AGENTS.md",
81
+ "from": ["~/.agents/memory/USER.md", "~/.agents/memory/PROJECTS.md"],
82
+ "edit": "Edit USER.md / PROJECTS.md, then re-run python -m agent_memory sync",
83
+ },
84
+ {
85
+ "path": "~/.agents/CLAUDE.md",
86
+ "bind": "~/.agents/AGENTS.md",
87
+ "edit": "Edit AGENTS.md only",
88
+ },
89
+ {
90
+ "path": "~/.agents/rules/<agent_rule_name>.mdc",
91
+ "hosts": ["~/.cursor/rules/<agent_rule_name>.mdc (bound on sync)"],
92
+ "config": "scan.json agent_rule_name (default user-rules.mdc)",
93
+ },
94
+ {
95
+ "path": "<repo>/.agents/AGENTS.md + CLAUDE.md",
96
+ "when": "registered project with path on disk",
97
+ "marker": "<!-- agent-memory-sync -->",
98
+ "note": "No <repo>/.cursor/ — scan skips .cursor; no empty memory subfolders",
99
+ },
100
+ {
101
+ "path": "<repo>/.agents/memory/README.md + staging/captured.md",
102
+ "when": "register_project",
103
+ },
104
+ {
105
+ "path": "~/.agents/skills/memory-sync/SKILL.md",
106
+ "also": ["host skill dirs per INSTALL.md"],
107
+ },
108
+ ],
109
+ "merged_on_init": [
110
+ {"path": "~/.cursor/mcp.json", "key": "mcpServers.agent-memory"},
111
+ {"path": "Zed settings.json", "key": "context_servers"},
112
+ ],
113
+ "copied_not_generated": [
114
+ {"path": "~/.agents/memory/LAYOUT.md", "source": "abi/LAYOUT.md in engine clone"},
115
+ ],
116
+ "marker": "<!-- agent-memory-sync -->",
117
+ }
118
+
119
+
120
+ def full_spec() -> dict[str, Any]:
121
+ from .sync import build_parser as sync_parser
122
+ from .inventory import build_parser as inventory_parser
123
+
124
+ return {
125
+ "name": "agent-memory",
126
+ "abi_version": ABI_VERSION,
127
+ "scripts": {
128
+ "sync": cli_spec(sync_parser(), name="sync", description="Rewrite always-on injection for your Agent."),
129
+ "inventory": cli_spec(
130
+ inventory_parser(),
131
+ name="inventory",
132
+ description="Compare scan.json roots to PROJECTS.md.",
133
+ ),
134
+ },
135
+ "scripts_no_flags": {
136
+ "ingest": "Serial ingest: catalog (references) -> extract (staging) -> distill via MCP add_memory.",
137
+ "consolidate": "Move live markdown leaked into the engine clone into ~/.agents/memory.",
138
+ "extract-openai": "Thin wrapper: ingest extract for Open AI GDPR export (openai-export; --out = legacy JSON).",
139
+ "mcp": "MCP stdio server. Tools: see abi/MCP.md.",
140
+ },
141
+ "injection": injection_spec(),
142
+ "discover": "python -m agent_memory --help-json | python -m agent_memory sync --help-json | python -m agent_memory inventory --help-json",
143
+ }
144
+
145
+
146
+ def emit_help_json(argv: list[str], parser: argparse.ArgumentParser, *, name: str, description: str = "") -> None:
147
+ payload = cli_spec(parser, name=name, description=description)
148
+ print(json.dumps(payload, indent=2, ensure_ascii=False))
149
+
150
+
151
+ def main(argv: list[str] | None = None) -> int:
152
+ if hasattr(sys.stdout, "reconfigure"):
153
+ try:
154
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
155
+ except Exception:
156
+ pass
157
+ args = list(argv if argv is not None else sys.argv[1:])
158
+ if "--help-json" in args:
159
+ print(json.dumps(full_spec(), indent=2, ensure_ascii=False))
160
+ return 0
161
+ print("Usage: python -m agent_memory --help-json", file=sys.stderr)
162
+ return 2
163
+
164
+
165
+ if __name__ == "__main__":
166
+ raise SystemExit(main())
@@ -0,0 +1,22 @@
1
+ """Move live markdown out of the engine clone into ~/.agents/memory."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+
6
+ from .store import consolidate_repo_leaks, ensure_memory_layout
7
+
8
+
9
+ def main() -> int:
10
+ ensure_memory_layout()
11
+ moved = consolidate_repo_leaks()
12
+ if not moved:
13
+ print("nothing to consolidate")
14
+ return 0
15
+ print(f"consolidated {len(moved)} paths:")
16
+ for line in moved:
17
+ print(f" {line}")
18
+ return 0
19
+
20
+
21
+ if __name__ == "__main__":
22
+ sys.exit(main())