memd 0.3.0__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.
- memd-0.3.0/LICENSE +21 -0
- memd-0.3.0/PKG-INFO +353 -0
- memd-0.3.0/README.md +327 -0
- memd-0.3.0/memd/__init__.py +113 -0
- memd-0.3.0/memd/cli.py +196 -0
- memd-0.3.0/memd/config.py +164 -0
- memd-0.3.0/memd/curator.py +251 -0
- memd-0.3.0/memd/digest.py +378 -0
- memd-0.3.0/memd/errors.py +28 -0
- memd-0.3.0/memd/hooks.py +117 -0
- memd-0.3.0/memd/inbox.py +80 -0
- memd-0.3.0/memd/memory.py +491 -0
- memd-0.3.0/memd/sweep.py +201 -0
- memd-0.3.0/memd/sync.py +195 -0
- memd-0.3.0/memd.egg-info/PKG-INFO +353 -0
- memd-0.3.0/memd.egg-info/SOURCES.txt +33 -0
- memd-0.3.0/memd.egg-info/dependency_links.txt +1 -0
- memd-0.3.0/memd.egg-info/entry_points.txt +2 -0
- memd-0.3.0/memd.egg-info/top_level.txt +1 -0
- memd-0.3.0/pyproject.toml +44 -0
- memd-0.3.0/setup.cfg +4 -0
- memd-0.3.0/tests/test_antigravity.py +136 -0
- memd-0.3.0/tests/test_brief.py +219 -0
- memd-0.3.0/tests/test_config.py +71 -0
- memd-0.3.0/tests/test_core.py +166 -0
- memd-0.3.0/tests/test_curation_quality.py +184 -0
- memd-0.3.0/tests/test_curator.py +123 -0
- memd-0.3.0/tests/test_cursors.py +114 -0
- memd-0.3.0/tests/test_digest.py +99 -0
- memd-0.3.0/tests/test_inbox.py +137 -0
- memd-0.3.0/tests/test_memory_repo.py +132 -0
- memd-0.3.0/tests/test_redact.py +116 -0
- memd-0.3.0/tests/test_sweep.py +120 -0
- memd-0.3.0/tests/test_sync_e2e.py +96 -0
- memd-0.3.0/tests/test_validate_budget.py +132 -0
memd-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lowcache
|
|
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.
|
memd-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memd
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Agent-driven project-memory curator
|
|
5
|
+
Author: lowcache
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lowcache/memd
|
|
8
|
+
Project-URL: Repository, https://github.com/lowcache/memd
|
|
9
|
+
Project-URL: Issues, https://github.com/lowcache/memd/issues
|
|
10
|
+
Keywords: ai,agents,memory,context,claude,llm,curator
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# memd — Agent-Driven Project Memory Curator
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="assets/memd.png" alt="memd logo" />
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
[](https://github.com/lowcache/memd)
|
|
34
|
+
[](#installation)
|
|
35
|
+
[](LICENSE)
|
|
36
|
+
[](#nix-installation)
|
|
37
|
+
|
|
38
|
+
`memd` gives your AI coding tools a memory that survives between sessions. It runs quietly in the background, reads the session logs your CLIs leave behind (`claude-code`, `antigravity-cli`, custom agents), and boils them down into a few small, git-versioned markdown files under `./.memory/` — the stuff worth remembering, minus the noise.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Why memd?
|
|
43
|
+
|
|
44
|
+
Every AI coding session ends the same way: the context window fills up, the session closes, and everything the model figured out — the port that service actually runs on, why you picked SQLite, the bug it hit last Tuesday — is gone. Next session it asks the same questions and steps on the same rakes.
|
|
45
|
+
|
|
46
|
+
`memd` fixes that by keeping a small set of memory files up to date automatically:
|
|
47
|
+
* **Remembers what matters:** current system state, active decisions, and open todos, in plain markdown you can read yourself.
|
|
48
|
+
* **Skips what doesn't:** conversational back-and-forth and tool spam get dropped; only durable facts survive.
|
|
49
|
+
* **Works with a crowd:** a lock-safe inbox lets any agent, script, or human hand a note to the curator without stepping on anyone else.
|
|
50
|
+
* **Keeps receipts:** memory changes are committed to git, so you can see how your project's context evolved over time.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
Sessions end (or a timer fires), memd collects whatever is new — transcripts, databases, inbox notes — scrubs it for secrets, and hands it to a small "curator" model that rewrites the memory files. Python code, not the model, gets the final say on what's allowed to change.
|
|
57
|
+
|
|
58
|
+
```mermaid
|
|
59
|
+
graph TD
|
|
60
|
+
subgraph Clients / CLI Sessions
|
|
61
|
+
CC[claude-code] -->|hooks / SessionEnd| MH[memd hook]
|
|
62
|
+
AG[antigravity-cli] -->|SQLite DB/protobuf| DB[(conversations/*.db)]
|
|
63
|
+
SA[Swarm/Agents] -->|Inbox Protocol v1.0| IN[(.memory/inbox/*)]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
subgraph memd Core
|
|
67
|
+
MH -->|Trigger| MS[memd sync]
|
|
68
|
+
T[systemd sweep timer] -->|memd sweep| MW[Sweep Worker ThreadPool]
|
|
69
|
+
MW -->|Sync Project| MS
|
|
70
|
+
DB -.->|Parsed by| MS
|
|
71
|
+
IN -.->|Ingested by| MS
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
subgraph Distillation & Curators
|
|
75
|
+
MS -->|Assemble Prompt & Digest| PR[Redaction & De-duplication]
|
|
76
|
+
PR -->|Prompt / Stdin| CB{Curator Backend}
|
|
77
|
+
CB -->|Headless claude -p / Custom LLM| MS
|
|
78
|
+
MS -->|Validate Outputs & Budgeting| VAL[Hard Invariants Enforcer]
|
|
79
|
+
VAL -->|Write & Commit| MC[(.memory/ files)]
|
|
80
|
+
VAL -->|Prune Overflow| ARC[(archive/YYYY-MM.md)]
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## The Four Memory Files
|
|
87
|
+
|
|
88
|
+
Everything lands in four markdown files under `./.memory/`:
|
|
89
|
+
|
|
90
|
+
| File | What's in it | House rules |
|
|
91
|
+
| :--- | :--- | :--- |
|
|
92
|
+
| `state.md` | What's true *right now*: ports, directory layout, running services, active workarounds. | Present tense only. When a fact goes stale it gets replaced — no history kept here. |
|
|
93
|
+
| `decisions.md` | What was decided, why, and what that rules out. | Each entry has to actually constrain future work, not just describe the past. |
|
|
94
|
+
| `todo.md` | What's still open: tasks, roadmap items, things to verify. | Finished or abandoned items move to the archive. |
|
|
95
|
+
| `mistakes.md` | What went wrong before, and how to not do it again. | Append-only. Each entry: symptom, root cause, and the rule that prevents a repeat. |
|
|
96
|
+
|
|
97
|
+
*Anything that overflows the size budgets gets moved to `./.memory/archive/YYYY-MM.md` instead of deleted.*
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Teaching Your Agents to Use the Memory
|
|
102
|
+
|
|
103
|
+
Keeping the memory curated is only half the job. The models working in your codebase won't read `.memory/` unless something tells them it exists and how to behave around it. The rules boil down to three:
|
|
104
|
+
|
|
105
|
+
1. **Read first.** Check `state.md`, `decisions.md`, `mistakes.md`, and `todo.md` (or run `memd brief` for a short digest) before doing real work.
|
|
106
|
+
2. **Don't edit the memory files.** The curator owns them and rewrites them on every pass — hand-edits just get overwritten.
|
|
107
|
+
3. **Send new facts through the inbox.** `memd note -m "..."`, or drop a file in `.memory/inbox/`. One fact per note.
|
|
108
|
+
|
|
109
|
+
You don't have to write those instructions yourself. [`contrib/agents/`](contrib/agents/) ships ready-made instruction files for the major tools — drop the right one into your project and you're done:
|
|
110
|
+
|
|
111
|
+
| Tool | File | Goes in |
|
|
112
|
+
| :--- | :--- | :--- |
|
|
113
|
+
| Codex, Cursor, Gemini CLI, Zed, and anything else on the [agents.md](https://agents.md) standard | `AGENTS.md` | project root |
|
|
114
|
+
| Claude Code (knows about memd's hooks) | `CLAUDE.md` | project root |
|
|
115
|
+
| Gemini / antigravity-cli | `GEMINI.md` | project root |
|
|
116
|
+
| GitHub Copilot | `copilot-instructions.md` | `.github/` |
|
|
117
|
+
| Cursor (rules format) | `memd.mdc` | `.cursor/rules/` |
|
|
118
|
+
|
|
119
|
+
Already have a `CLAUDE.md` or `AGENTS.md`? Append the memd section instead of overwriting — the files are written to compose. Details in [`contrib/agents/README.md`](contrib/agents/README.md).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Guardrails Enforced by Code (Not the LLM)
|
|
124
|
+
|
|
125
|
+
The curator model is useful but not trusted. The rules that keep memory intact live in plain Python, so a confused (or prompt-injected) model can't wreck the store:
|
|
126
|
+
|
|
127
|
+
1. **Frontmatter required:** Every memory file must carry valid YAML frontmatter (`type`, `project`, `last_updated`, `status`), or the write is rejected.
|
|
128
|
+
2. **Mistakes are forever:** `mistakes.md` is append-only. The curator can add entries but never edit or delete old ones.
|
|
129
|
+
3. **Shrink guard:** A distill that would delete more than 60% of a file gets rejected — unless the removed sections were explicitly moved to the archive.
|
|
130
|
+
4. **Size budgets:** Each file has a character cap (set in `config.json`). Overflow gets moved to `./.memory/archive/YYYY-MM.md`, oldest sections first.
|
|
131
|
+
5. **No races:** Per-project file locking (`flock`) keeps concurrent sweeps, hooks, and agents from corrupting each other's writes.
|
|
132
|
+
6. **Nothing skipped:** memd tracks how far it has read into each transcript and database, and only advances that marker after a successful distill and write — a failed run just gets retried from the same spot.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Where the Raw Material Comes From
|
|
137
|
+
|
|
138
|
+
`memd` pulls from several sources before each distill:
|
|
139
|
+
|
|
140
|
+
### 1. `claude-code` Hooks
|
|
141
|
+
`memd` plugs into the `claude-code` CLI through lifecycle hooks in `~/.claude/settings.json` (wired up for you by `memd install-hooks`):
|
|
142
|
+
* **Session start:** the memory brief gets injected into the assistant's starting context, so it begins the session already knowing the project.
|
|
143
|
+
* **Session end / pre-compact:** a background `memd sync` digests the transcript of the session that just finished.
|
|
144
|
+
|
|
145
|
+
### 2. `antigravity-cli` Database Parsing
|
|
146
|
+
For `antigravity-cli`, `memd` reads the SQLite conversation databases in `~/.gemini/antigravity-cli/conversations/*.db` directly:
|
|
147
|
+
* Pulls out the useful steps — user inputs, assistant responses, tool actions, errors.
|
|
148
|
+
* Figures out which project a conversation belongs to by counting workspace-path mentions in the payloads (index kept in `~/.local/state/memd/ag_index.json`).
|
|
149
|
+
|
|
150
|
+
### 3. The Inbox Protocol (v1.0)
|
|
151
|
+
The inbox is how everything else — another agent, an MCP tool, a CI script, you with a text editor — hands a fact to the curator without touching the memory files directly. Drop a markdown note in the inbox; the next sweep folds it into memory and deletes the note.
|
|
152
|
+
|
|
153
|
+
* **Project Inbox:** `<project-root>/.memory/inbox/` (feeds local project files)
|
|
154
|
+
* **Global Inbox:** `<global_root>/.memory/inbox/` (default `~/.memory/inbox/`, feeds system-wide/cross-project files)
|
|
155
|
+
|
|
156
|
+
#### Rules for Writers
|
|
157
|
+
The inbox has many writers and one reader that deletes what it consumes, so a sloppy writer can lose data for everyone. Every writer must publish atomically:
|
|
158
|
+
1. **Stage Outside the Inbox:** Write the markdown note to a temporary file in the *parent* `.memory/` directory (e.g. `.remember-*.tmp`).
|
|
159
|
+
2. **fsync the File:** Durably sync the data to disk (`flush` + `fsync`).
|
|
160
|
+
3. **Atomic Publish:** Publish to the `inbox/` directory via `rename(2)` or `link(2)` (`os.replace` or `os.link`).
|
|
161
|
+
4. **fsync the Inbox Directory:** Ensure the parent directory structure is updated.
|
|
162
|
+
5. **Collision-Proof Names:** Name files using microsecond-resolution timestamp + PID (e.g., `20260711T123000123456-9876.md`).
|
|
163
|
+
6. **Write-Once:** Never modify or reuse a published note.
|
|
164
|
+
|
|
165
|
+
For complete writer/reader guidelines, see [INBOX-PROTOCOL.md](INBOX-PROTOCOL.md).
|
|
166
|
+
|
|
167
|
+
### 4. Extra Custom Sources
|
|
168
|
+
Additional text transcript sources (e.g. custom JSONL logs) can be registered per-project under the `projects.<path>.extra_sources` array in `config.json`.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Configuration (`config.json`)
|
|
173
|
+
|
|
174
|
+
Configuration is stored in `$XDG_CONFIG_HOME/memd/config.json` (defaults to `~/.config/memd/config.json`).
|
|
175
|
+
|
|
176
|
+
### Key Reference & Defaults
|
|
177
|
+
|
|
178
|
+
| Option | Type | Default | Description |
|
|
179
|
+
| :--- | :--- | :--- | :--- |
|
|
180
|
+
| `claude_bin` | `string` | `"claude"` | Name or path of the `claude-code` binary. |
|
|
181
|
+
| `curator_cmd` | `array` | `[]` | Override list of arguments to invoke a custom curator backend (e.g., `["nix", "run", ".#", "--", "status"]`). Substitutes `{model}`. If empty, falls back to the headless `claude_bin` command. |
|
|
182
|
+
| `antigravity_dir` | `string` | `"~/.gemini/antigravity-cli"` | Directory containing native SQLite database conversations. |
|
|
183
|
+
| `model_small` | `string` | `"haiku"` | Model used for small/routine transcript distills. |
|
|
184
|
+
| `model_large` | `string` | `"sonnet"` | Model used for large/complex session distills. |
|
|
185
|
+
| `escalate_chars` | `integer` | `15000` | Character count above which digests are run with `model_large`. |
|
|
186
|
+
| `digest_cap_chars` | `integer` | `60000` | Maximum length of transcript digest fed to the LLM. |
|
|
187
|
+
| `quiet_seconds` | `integer` | `600` | Skip digesting transcripts modified within this cooldown period. |
|
|
188
|
+
| `sweep_jobs` | `integer` | `4` | Number of worker threads for parallel sweeps. |
|
|
189
|
+
| `auto_scaffold` | `boolean` | `true` | Auto-create `.memory/` structure inside detected git repositories. |
|
|
190
|
+
| `git_commit` | `boolean` | `true` | Automatically run `git commit` on memory files after successful distill. |
|
|
191
|
+
| `budgets` | `object` | See below | Dictionary specifying character size caps for active memory files before archiving. |
|
|
192
|
+
| `REDACT_EXTRA_PATTERNS` | `array` | `[]` | List of raw regex patterns for custom credential redaction. |
|
|
193
|
+
| `exclude` | `array` | `[]` | List of absolute project paths to ignore during sweeps. |
|
|
194
|
+
| `projects` | `object` | `{}` | Mapping of absolute paths to project definitions: `{"<path>": {"name": "memd", "extra_sources": ["*.jsonl"]}}`. |
|
|
195
|
+
| `global_root` | `string` | `HOME` | Path to the global fallback project root. |
|
|
196
|
+
| `global_brief_chars` | `integer` | `800` | Max character excerpt of global `state.md` injected into a project's brief. |
|
|
197
|
+
|
|
198
|
+
#### Default File Budgets
|
|
199
|
+
```json
|
|
200
|
+
"budgets": {
|
|
201
|
+
"state.md": 10000,
|
|
202
|
+
"decisions.md": 12000,
|
|
203
|
+
"todo.md": 10000,
|
|
204
|
+
"mistakes.md": 22000
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Built-In Credential Redaction
|
|
211
|
+
|
|
212
|
+
Session transcripts have a bad habit of containing API keys. Before anything reaches the curator model, `memd` scrubs it with a set of regex filters, so a token that leaked into a transcript doesn't leak again into a prompt (or into a git-committed memory file).
|
|
213
|
+
|
|
214
|
+
13 rules are built in:
|
|
215
|
+
* `google_oauth` (Google OAuth2 tokens, `ya29.` prefix)
|
|
216
|
+
* `github_pat` (Classic and fine-grained GitHub tokens)
|
|
217
|
+
* `anthropic_key` (Anthropic API keys, `sk-ant-` prefix)
|
|
218
|
+
* `openai_key` (OpenAI API keys, `sk-` / `sk-proj-` prefix)
|
|
219
|
+
* `aws_access` (AWS Access Key IDs, `AKIA` prefix)
|
|
220
|
+
* `slack_token` (Slack bot/user/workspace tokens, `xox` prefix)
|
|
221
|
+
* `gitlab_token` (GitLab PATs, `glpat-` prefix)
|
|
222
|
+
* `npm_token` (npm access tokens, `npm_` prefix)
|
|
223
|
+
* `jwt` (JSON Web Tokens starting with `eyJ`)
|
|
224
|
+
* `json_token_field` (JSON keys containing `access_token`, `refresh_token`, etc.)
|
|
225
|
+
* `bearer_header` (HTTP Authorization headers with Bearer tokens)
|
|
226
|
+
* `env_credential` (UPPERCASE credentials assigned in `.env` or shell exports)
|
|
227
|
+
* `ssh_private_key` (PEM blocks starting with `-----BEGIN ... PRIVATE KEY-----`)
|
|
228
|
+
|
|
229
|
+
> [!NOTE]
|
|
230
|
+
> Azure storage/service keys do not have a distinct prefix and cannot be reliably matched without high false-positive rates. Use `REDACT_EXTRA_PATTERNS` to configure rules matching your specific Azure keys if needed.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## CLI Command Quick Reference
|
|
235
|
+
|
|
236
|
+
| Command | Usage | Exit Codes & Notes |
|
|
237
|
+
| :--- | :--- | :--- |
|
|
238
|
+
| `memd init [path]` | Scaffold `.memory/` & register a project. Supports `--name <name>` and `--global` (sets up the fallback root). | `0` (Success), `2` (Config error) |
|
|
239
|
+
| `memd sync` | Distill new session content into memory. Supports `--project <path>`, `--transcript <path>`, `--trigger <name>`, and `--dry-run`. | `0` (Success/No-op), `3` (Curator/distill failure) |
|
|
240
|
+
| `memd sweep` | Periodic sweep to catch up all projects, ingest inbox files, and detect new git projects. Supports `--jobs <N>`. | `0` (Success), `1` (Any worker failed) |
|
|
241
|
+
| `memd brief [path]` | Print the session-start brief (context injection). Supports `--max-chars <N>` and `--topic <keyword>`. | Prints brief text to stdout. |
|
|
242
|
+
| `memd status` | Display registry, backlog bytes, and last distill summaries. | Prints status details. |
|
|
243
|
+
| `memd install-hooks` | Idempotently wire hooks into `~/.claude/settings.json`. | Configures hooks. |
|
|
244
|
+
| `memd note` | Append a collision-safe note to the project or global inbox. Supports `-m "<message>"` and `--global`. | `0` (Success), `2` (Config error) |
|
|
245
|
+
| `memd exclude <path>`| Exclude a path from automatic project discovery. | Registers path to exclude list. |
|
|
246
|
+
| `memd hook <event>` | Invoked by `claude-code` CLI lifecycle events. | `session-start`, `session-end`, `pre-compact` |
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Installation
|
|
251
|
+
|
|
252
|
+
### pip Installation
|
|
253
|
+
|
|
254
|
+
Requires Python **3.10+**. `memd` has zero runtime dependencies (standard library only).
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# From PyPI
|
|
258
|
+
pip install memd
|
|
259
|
+
|
|
260
|
+
# With pipx (recommended for CLI tools — isolated, no system-Python friction)
|
|
261
|
+
pipx install memd
|
|
262
|
+
|
|
263
|
+
# Directly from the repository, no PyPI needed
|
|
264
|
+
pip install git+https://github.com/lowcache/memd
|
|
265
|
+
|
|
266
|
+
# From a local clone (add -e for editable/development mode)
|
|
267
|
+
pip install .
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
> **PEP 668 note:** distributions that mark the system Python as externally managed (NixOS, Debian 12+, Arch, Fedora 38+) reject bare `pip install` outside a virtual environment. Use `pipx`, or a venv: `python3 -m venv ~/.venvs/memd && ~/.venvs/memd/bin/pip install memd`.
|
|
271
|
+
|
|
272
|
+
### Nix Installation
|
|
273
|
+
|
|
274
|
+
`memd` is packaged as a Nix flake. You can run or install it directly:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Run ad-hoc from the flake
|
|
278
|
+
nix run github:lowcache/memd -- status
|
|
279
|
+
|
|
280
|
+
# Install to user profile
|
|
281
|
+
nix profile install github:lowcache/memd
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Declarative Setup via Home Manager
|
|
285
|
+
Import the module and enable the periodic sweep timer:
|
|
286
|
+
|
|
287
|
+
```nix
|
|
288
|
+
{ inputs, pkgs, ... }: {
|
|
289
|
+
imports = [ inputs.memd.homeManagerModules.default ];
|
|
290
|
+
|
|
291
|
+
services.memd = {
|
|
292
|
+
enable = true;
|
|
293
|
+
installClaudeHooks = true; # Idempotently configure ~/.claude/settings.json hooks
|
|
294
|
+
|
|
295
|
+
sweep = {
|
|
296
|
+
enable = true; # Periodically execute `memd sweep`
|
|
297
|
+
interval = "30min"; # Timer interval (default: "30min")
|
|
298
|
+
onBoot = "5min"; # Delay after boot before running the first sweep (default: "5min")
|
|
299
|
+
randomizedDelay = "2min"; # Randomized delay jitter (default: "2min")
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### Standalone systemd User Service & Timer
|
|
306
|
+
|
|
307
|
+
For non-Nix environments, a standalone systemd user service and timer can be set up using files in the `contrib/` directory:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Copy units to the user systemd directory
|
|
311
|
+
cp contrib/memd-sweep.* ~/.config/systemd/user/
|
|
312
|
+
|
|
313
|
+
# Reload the systemd user manager
|
|
314
|
+
systemctl --user daemon-reload
|
|
315
|
+
|
|
316
|
+
# Enable and start the timer
|
|
317
|
+
systemctl --user enable --now memd-sweep.timer
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Refer to [contrib/README.md](contrib/README.md) for detailed customization and uninstallation instructions.
|
|
321
|
+
|
|
322
|
+
### Manual Installation (no pip, no Nix)
|
|
323
|
+
|
|
324
|
+
`memd` is compatible with **Python 3.10 to 3.13** and uses standard library packages exclusively, so a plain clone runs as-is:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
git clone https://github.com/lowcache/memd && cd memd
|
|
328
|
+
./memd.py --help
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Symlink the shim onto your `PATH` for a live-updating install: `ln -s "$PWD/memd.py" ~/.local/bin/memd`.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Testing & Verification
|
|
336
|
+
|
|
337
|
+
166 automated tests cover the parts that would hurt if they broke: XDG isolation, cursors, redaction, database parsing, atomic inbox publishing (including an 8-writer concurrency stress test), retry policies, brief budgeting, memory-repo decoupling + worktree sharing, and curation quality. Verified on Python 3.10 through 3.13.
|
|
338
|
+
|
|
339
|
+
To run tests in a Nix-isolated environment:
|
|
340
|
+
```bash
|
|
341
|
+
nix flake check
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Or run via `pytest` locally:
|
|
345
|
+
```bash
|
|
346
|
+
python -m pytest tests/ -q
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Changelog
|
|
352
|
+
|
|
353
|
+
- **0.3.0**: Each project's `.memory/` now gets its own standalone Git repository, decoupling memory history from code branches. Linked Git worktrees automatically share one physical `.memory` store via symlinks (never overwriting unsaved memory), and a migration helper untracks `.memory` from the parent repo on init.
|