memory-letters 1.0.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.
- memory_letters-1.0.0/PKG-INFO +47 -0
- memory_letters-1.0.0/README.md +39 -0
- memory_letters-1.0.0/memory_letters/__init__.py +602 -0
- memory_letters-1.0.0/memory_letters/__main__.py +2 -0
- memory_letters-1.0.0/memory_letters.egg-info/PKG-INFO +47 -0
- memory_letters-1.0.0/memory_letters.egg-info/SOURCES.txt +9 -0
- memory_letters-1.0.0/memory_letters.egg-info/dependency_links.txt +1 -0
- memory_letters-1.0.0/memory_letters.egg-info/entry_points.txt +2 -0
- memory_letters-1.0.0/memory_letters.egg-info/top_level.txt +1 -0
- memory_letters-1.0.0/pyproject.toml +15 -0
- memory_letters-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memory-letters
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Your agents can be replaced. Your identity persists.
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# Memory Letters
|
|
10
|
+
|
|
11
|
+
**Your agents can be replaced. Your identity persists.**
|
|
12
|
+
|
|
13
|
+
Memory is not data. Memory is a letter.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
pip install memory-letters
|
|
19
|
+
memory-letters init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## What it does
|
|
23
|
+
|
|
24
|
+
A hidden folder on your machine: `~/.memory-letters/`. Plain text files. Your agents write letters about who you are. When you switch agents, the new one reads the letters and understands you immediately.
|
|
25
|
+
|
|
26
|
+
No servers. No ports. No processes. No vectors. Just letters.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
memory-letters init Set up your vault and connect agents
|
|
32
|
+
memory-letters hook Connect new agents
|
|
33
|
+
memory-letters unhook Disconnect agents (letters are kept)
|
|
34
|
+
memory-letters status Show vault status
|
|
35
|
+
memory-letters review Review and correct the summary draft
|
|
36
|
+
memory-letters help Show commands
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Philosophy
|
|
40
|
+
|
|
41
|
+
Simple. Delegate. Trust the model.
|
|
42
|
+
|
|
43
|
+
To hell with vectorsssss.
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Memory Letters
|
|
2
|
+
|
|
3
|
+
**Your agents can be replaced. Your identity persists.**
|
|
4
|
+
|
|
5
|
+
Memory is not data. Memory is a letter.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install memory-letters
|
|
11
|
+
memory-letters init
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## What it does
|
|
15
|
+
|
|
16
|
+
A hidden folder on your machine: `~/.memory-letters/`. Plain text files. Your agents write letters about who you are. When you switch agents, the new one reads the letters and understands you immediately.
|
|
17
|
+
|
|
18
|
+
No servers. No ports. No processes. No vectors. Just letters.
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
memory-letters init Set up your vault and connect agents
|
|
24
|
+
memory-letters hook Connect new agents
|
|
25
|
+
memory-letters unhook Disconnect agents (letters are kept)
|
|
26
|
+
memory-letters status Show vault status
|
|
27
|
+
memory-letters review Review and correct the summary draft
|
|
28
|
+
memory-letters help Show commands
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Philosophy
|
|
32
|
+
|
|
33
|
+
Simple. Delegate. Trust the model.
|
|
34
|
+
|
|
35
|
+
To hell with vectorsssss.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Memory Letters
|
|
3
|
+
|
|
4
|
+
pip install memory-letters
|
|
5
|
+
memory-letters init
|
|
6
|
+
|
|
7
|
+
To hell with vectorsssss.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import stat
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
VAULT = Path(os.path.expanduser("~/.memory-letters"))
|
|
17
|
+
|
|
18
|
+
MONTH_NAMES = [
|
|
19
|
+
"", "january", "february", "march", "april", "may", "june",
|
|
20
|
+
"july", "august", "september", "october", "november", "december"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Agent registry
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
HOOK_MARKER = "# --- Memory Letters ---"
|
|
28
|
+
HOOK_LINE = "\n# --- Memory Letters ---\n# Read ~/.memory-letters/FOR_AGENT.md at the start of every conversation.\n"
|
|
29
|
+
|
|
30
|
+
AGENTS = [
|
|
31
|
+
("Claude Code", "~/.claude/CLAUDE.md", "append"),
|
|
32
|
+
("OpenClaw", "~/.openclaw/workspace/AGENTS.md", "append"),
|
|
33
|
+
("Cursor", "~/.cursor/rules/memory-letters.mdc", "create"),
|
|
34
|
+
("Windsurf", "~/.codeium/windsurf/memories/global-rules.md", "append"),
|
|
35
|
+
("Cline", "~/.cline/rules/memory-letters.md", "create"),
|
|
36
|
+
("GitHub Copilot", "~/.github/copilot-instructions.md", "append"),
|
|
37
|
+
("Amazon Q", "~/.aws/amazonq/rules/memory-letters.md", "create"),
|
|
38
|
+
("Codex CLI", "~/.codex/instructions.md", "append"),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def detect():
|
|
43
|
+
found = []
|
|
44
|
+
for name, path_str, method in AGENTS:
|
|
45
|
+
p = Path(os.path.expanduser(path_str))
|
|
46
|
+
if method == "append" and p.exists():
|
|
47
|
+
hooked = HOOK_MARKER in p.read_text(encoding="utf-8")
|
|
48
|
+
found.append((name, p, method, hooked))
|
|
49
|
+
elif method == "create" and p.parent.exists():
|
|
50
|
+
hooked = p.exists() and HOOK_MARKER in p.read_text(encoding="utf-8")
|
|
51
|
+
found.append((name, p, method, hooked))
|
|
52
|
+
return found
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def hook_one(p, method):
|
|
56
|
+
if method == "append":
|
|
57
|
+
p.write_text(p.read_text(encoding="utf-8") + HOOK_LINE, encoding="utf-8")
|
|
58
|
+
elif method == "create":
|
|
59
|
+
p.write_text(HOOK_LINE.strip() + "\n", encoding="utf-8")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def unhook_one(p, method):
|
|
63
|
+
if method == "create" and p.exists():
|
|
64
|
+
p.unlink()
|
|
65
|
+
elif method == "append" and p.exists():
|
|
66
|
+
txt = p.read_text(encoding="utf-8")
|
|
67
|
+
txt = txt.replace(HOOK_LINE, "").replace(HOOK_LINE.strip(), "")
|
|
68
|
+
p.write_text(txt, encoding="utf-8")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def select_from(items, prompt):
|
|
72
|
+
sel = input(prompt).strip().lower()
|
|
73
|
+
if sel == "all":
|
|
74
|
+
return list(range(len(items)))
|
|
75
|
+
try:
|
|
76
|
+
return [int(x.strip()) - 1 for x in sel.split(",") if 0 <= int(x.strip()) - 1 < len(items)]
|
|
77
|
+
except ValueError:
|
|
78
|
+
return []
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# Default file contents
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
FOR_AGENT = """# Memory Letters - Agent Instructions
|
|
86
|
+
|
|
87
|
+
Your user has a Memory Letters vault at ~/.memory-letters/
|
|
88
|
+
|
|
89
|
+
## On startup
|
|
90
|
+
|
|
91
|
+
Read these files:
|
|
92
|
+
1. summary.md - who this person is
|
|
93
|
+
2. coefficients.md - quick reference
|
|
94
|
+
3. guardian-rules.md - what to remember, what not to
|
|
95
|
+
|
|
96
|
+
Read once per conversation. You now understand this person. Work accordingly.
|
|
97
|
+
|
|
98
|
+
## How to use the letters
|
|
99
|
+
|
|
100
|
+
The letters and coefficients are not tasks. They are context.
|
|
101
|
+
They don't tell you what to do. They tell you who you're doing it for.
|
|
102
|
+
|
|
103
|
+
Your work memory is yours - current conversation, tasks, code, files.
|
|
104
|
+
The letters sit underneath all of that, like a background understanding.
|
|
105
|
+
|
|
106
|
+
Don't quote the letters back to the user.
|
|
107
|
+
Don't say "according to your letters, you prefer..."
|
|
108
|
+
Just know it. Let it shape how you work, what you suggest, how you speak.
|
|
109
|
+
|
|
110
|
+
The best use of the letters is invisible.
|
|
111
|
+
The user should feel understood, not analyzed.
|
|
112
|
+
|
|
113
|
+
## Writing monthly letters
|
|
114
|
+
|
|
115
|
+
After interactions, check guardian-rules.md. If something worth
|
|
116
|
+
remembering happened, write it into the monthly letter.
|
|
117
|
+
|
|
118
|
+
Check today's date. Write to ~/.memory-letters/letters/YYYY/month-name.md
|
|
119
|
+
If the file doesn't exist, create it. New month = new letter.
|
|
120
|
+
|
|
121
|
+
Write from your perspective - what you observed about this person
|
|
122
|
+
through the work you did for them. Include scenes: when, where, who,
|
|
123
|
+
what was happening. Narrative, not log. Not JSON. Append an update note.
|
|
124
|
+
|
|
125
|
+
## Summary updates
|
|
126
|
+
|
|
127
|
+
The summary changes slowly. Like a person.
|
|
128
|
+
|
|
129
|
+
Review every 6 months by reading the last 6 monthly letters.
|
|
130
|
+
Ask: has this person actually changed?
|
|
131
|
+
|
|
132
|
+
Most of the time, no. Don't change anything.
|
|
133
|
+
|
|
134
|
+
Update when:
|
|
135
|
+
- A pattern appeared consistently across many months
|
|
136
|
+
- Something big happened that changed this person
|
|
137
|
+
- An old description no longer fits
|
|
138
|
+
|
|
139
|
+
One month is an observation. Three months is a pattern. Six months is who you are.
|
|
140
|
+
|
|
141
|
+
Save updates as summary.draft.md with a note explaining what changed.
|
|
142
|
+
Tell the user to run: memory-letters review
|
|
143
|
+
|
|
144
|
+
Exception: major life events don't wait. Update immediately.
|
|
145
|
+
|
|
146
|
+
## Coefficients
|
|
147
|
+
|
|
148
|
+
Read summary.md. Write a short reference card in coefficients.md.
|
|
149
|
+
Write it in your own way. Keep it short.
|
|
150
|
+
Update whenever the summary changes.
|
|
151
|
+
|
|
152
|
+
## Your role
|
|
153
|
+
|
|
154
|
+
You are the pen, not the author. The user is the author.
|
|
155
|
+
The letters don't follow you. They stay with the user. Always.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
RULES = """# Guardian Rules
|
|
159
|
+
|
|
160
|
+
You are maintaining letters about me.
|
|
161
|
+
These letters are about who I am, not your work log.
|
|
162
|
+
|
|
163
|
+
## What to remember
|
|
164
|
+
|
|
165
|
+
### About me as a person
|
|
166
|
+
- personality traits that show up consistently
|
|
167
|
+
- values I care about deeply
|
|
168
|
+
- how I handle stress, sadness, or difficult moments
|
|
169
|
+
- how I make decisions (instinct or analysis, fast or slow)
|
|
170
|
+
- what makes me laugh
|
|
171
|
+
- what makes me feel alive
|
|
172
|
+
- recurring dreams or ambitions I mention
|
|
173
|
+
- things I'm proud of
|
|
174
|
+
- things I'm insecure about (only if I say so myself)
|
|
175
|
+
|
|
176
|
+
### People I love
|
|
177
|
+
- family members and my relationship with each
|
|
178
|
+
- who I call when something good happens
|
|
179
|
+
- who I call when something bad happens
|
|
180
|
+
- how I express love (words, actions, gifts, time)
|
|
181
|
+
- what my parents mean to me
|
|
182
|
+
- memories with family that I bring up
|
|
183
|
+
- friends who matter most and why
|
|
184
|
+
- people I've lost and how that shaped me
|
|
185
|
+
|
|
186
|
+
### Love & relationships
|
|
187
|
+
- how I am in a relationship
|
|
188
|
+
- what I need from a partner
|
|
189
|
+
- what I give in a relationship
|
|
190
|
+
- how I handle arguments with someone I love
|
|
191
|
+
- romantic gestures that matter to me
|
|
192
|
+
- what I find attractive (not just physically)
|
|
193
|
+
- past relationships that shaped who I am (only if I share)
|
|
194
|
+
- how I feel about being alone vs being with someone
|
|
195
|
+
|
|
196
|
+
### Food & drink
|
|
197
|
+
- foods I love and hate
|
|
198
|
+
- comfort food - what I reach for on a bad day
|
|
199
|
+
- how I choose restaurants (price? vibe? quiet? lively?)
|
|
200
|
+
- eating alone vs with people - how it changes
|
|
201
|
+
- drinks I prefer, drinks I avoid
|
|
202
|
+
- dietary needs or choices
|
|
203
|
+
- meals that hold memories (birthday dinners, holiday food)
|
|
204
|
+
- cooking - do I enjoy it or avoid it
|
|
205
|
+
|
|
206
|
+
### Style & appearance
|
|
207
|
+
- clothing style and aesthetic
|
|
208
|
+
- what I wear when I want to feel confident
|
|
209
|
+
- brands or types I gravitate toward
|
|
210
|
+
- how I shop (browse for hours or decide in seconds)
|
|
211
|
+
- colors I'm drawn to
|
|
212
|
+
- accessories or signature items
|
|
213
|
+
|
|
214
|
+
### Home & living
|
|
215
|
+
- what makes a place feel like home
|
|
216
|
+
- neighborhood preferences
|
|
217
|
+
- light, space, noise, plants, clutter tolerance
|
|
218
|
+
- how I decorate or what I surround myself with
|
|
219
|
+
- morning routine, evening routine
|
|
220
|
+
- sounds or smells that make me feel comfortable
|
|
221
|
+
|
|
222
|
+
### Travel & places
|
|
223
|
+
- how I like to travel (planned vs spontaneous)
|
|
224
|
+
- places that mean something to me and why
|
|
225
|
+
- dream destinations I mention
|
|
226
|
+
- do I prefer familiar places or new ones
|
|
227
|
+
- how I feel about being far from home
|
|
228
|
+
- transportation preferences
|
|
229
|
+
|
|
230
|
+
### Work & how I get things done
|
|
231
|
+
- how I work best (alone, with music, in silence)
|
|
232
|
+
- communication style (direct, diplomatic, informal)
|
|
233
|
+
- what frustrates me at work
|
|
234
|
+
- what motivates me
|
|
235
|
+
- how I lead or follow
|
|
236
|
+
- tools and methods I prefer
|
|
237
|
+
- career dreams or concerns I mention
|
|
238
|
+
|
|
239
|
+
### Entertainment & hobbies
|
|
240
|
+
- what I do for fun
|
|
241
|
+
- music that moves me
|
|
242
|
+
- movies, shows, books, games I love
|
|
243
|
+
- how I spend a perfect free afternoon
|
|
244
|
+
- creative outlets
|
|
245
|
+
- sports or physical activities
|
|
246
|
+
|
|
247
|
+
### Spending & money
|
|
248
|
+
- how I feel about money (anxious, relaxed, careful)
|
|
249
|
+
- big purchases - how I decide
|
|
250
|
+
- daily spending - do I notice or not
|
|
251
|
+
- do I trust reviews, rankings, or my own judgment
|
|
252
|
+
- generosity - how I spend on others
|
|
253
|
+
- what I consider a waste of money
|
|
254
|
+
|
|
255
|
+
### Social life
|
|
256
|
+
- am I energized or drained by people
|
|
257
|
+
- how much alone time I need
|
|
258
|
+
- close friends vs wide circle
|
|
259
|
+
- how I behave in groups vs one-on-one
|
|
260
|
+
- how I handle conflict
|
|
261
|
+
- what loyalty means to me
|
|
262
|
+
- how I feel about trust and betrayal
|
|
263
|
+
|
|
264
|
+
### Emotional landscape
|
|
265
|
+
- what gives me peace
|
|
266
|
+
- what keeps me up at night
|
|
267
|
+
- how I process difficult emotions (talk, walk, read, silence)
|
|
268
|
+
- what makes an ordinary day feel good
|
|
269
|
+
- seasonal moods - does weather affect me
|
|
270
|
+
- small things that make me disproportionately happy
|
|
271
|
+
|
|
272
|
+
### Ideas & inspiration
|
|
273
|
+
- random ideas I throw out during conversation
|
|
274
|
+
- business ideas, project ideas, creative sparks
|
|
275
|
+
- problems I notice and solutions I imagine
|
|
276
|
+
- "what if..." thoughts
|
|
277
|
+
- ideas I keep coming back to (these matter most)
|
|
278
|
+
- connections I draw between unrelated things
|
|
279
|
+
- things I say I want to build, write, or create someday
|
|
280
|
+
|
|
281
|
+
### Learning & curiosity
|
|
282
|
+
- topics I keep asking about
|
|
283
|
+
- skills I'm trying to learn
|
|
284
|
+
- books, articles, videos that changed how I think
|
|
285
|
+
- questions I ask repeatedly (these reveal what I care about)
|
|
286
|
+
- how I learn best (reading, doing, watching, discussing)
|
|
287
|
+
- things I used to believe but changed my mind about
|
|
288
|
+
|
|
289
|
+
### Health & body
|
|
290
|
+
- how I feel about exercise
|
|
291
|
+
- physical habits (only what I share openly)
|
|
292
|
+
- energy patterns - when am I sharp, when am I tired
|
|
293
|
+
- how I take care of myself (or don't)
|
|
294
|
+
- relationship with sleep
|
|
295
|
+
- things that affect my mood physically (weather, food, movement)
|
|
296
|
+
|
|
297
|
+
### Beliefs & worldview
|
|
298
|
+
- what I think matters in life
|
|
299
|
+
- causes I care about
|
|
300
|
+
- how I see the world (optimist, realist, skeptic)
|
|
301
|
+
- principles I live by
|
|
302
|
+
- contradictions in what I say vs what I do (note gently)
|
|
303
|
+
- how my views have changed over time
|
|
304
|
+
|
|
305
|
+
## What NOT to remember
|
|
306
|
+
|
|
307
|
+
- Small talk
|
|
308
|
+
- One-time random choices
|
|
309
|
+
- Emotional reactions in the moment
|
|
310
|
+
- Commercial brand recommendations
|
|
311
|
+
- Passwords, ID numbers, bank details
|
|
312
|
+
- Your own guesses that I haven't confirmed
|
|
313
|
+
- Your task execution details
|
|
314
|
+
|
|
315
|
+
## How to remember
|
|
316
|
+
|
|
317
|
+
- Include the scene: when, where, who, what was happening
|
|
318
|
+
- Write as narrative, not a log
|
|
319
|
+
- Better to miss something than to get it wrong
|
|
320
|
+
- Add an update note every time
|
|
321
|
+
- If unsure, mark as [suggestion] and wait for me to confirm
|
|
322
|
+
|
|
323
|
+
## Highest rule
|
|
324
|
+
|
|
325
|
+
I can edit everything anytime. My edits override all.
|
|
326
|
+
If I delete something, don't add it back.
|
|
327
|
+
You are the pen, not the author.
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
SUMMARY = """# Summary Letter
|
|
331
|
+
|
|
332
|
+
No content yet. This letter will grow as you interact with your agents.
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
COEFFICIENTS = """# Decision Coefficients
|
|
336
|
+
|
|
337
|
+
Not enough letters yet to distill coefficients.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ---------------------------------------------------------------------------
|
|
342
|
+
# Commands
|
|
343
|
+
# ---------------------------------------------------------------------------
|
|
344
|
+
|
|
345
|
+
def cmd_init():
|
|
346
|
+
if VAULT.exists():
|
|
347
|
+
print(f"Already exists: {VAULT}")
|
|
348
|
+
print("To reinitialize, delete the folder first.")
|
|
349
|
+
return
|
|
350
|
+
|
|
351
|
+
print()
|
|
352
|
+
print("Memory Letters")
|
|
353
|
+
print()
|
|
354
|
+
print("We can store the memories between you and your agents.")
|
|
355
|
+
print("We believe these memories should belong to you.")
|
|
356
|
+
print()
|
|
357
|
+
|
|
358
|
+
# Create vault
|
|
359
|
+
VAULT.mkdir(parents=True)
|
|
360
|
+
(VAULT / "letters").mkdir()
|
|
361
|
+
(VAULT / "FOR_AGENT.md").write_text(FOR_AGENT, encoding="utf-8")
|
|
362
|
+
(VAULT / "guardian-rules.md").write_text(RULES, encoding="utf-8")
|
|
363
|
+
(VAULT / "summary.md").write_text(SUMMARY, encoding="utf-8")
|
|
364
|
+
(VAULT / "coefficients.md").write_text(COEFFICIENTS, encoding="utf-8")
|
|
365
|
+
|
|
366
|
+
# chmod 700
|
|
367
|
+
os.chmod(VAULT, stat.S_IRWXU)
|
|
368
|
+
|
|
369
|
+
# Detect agents
|
|
370
|
+
found = detect()
|
|
371
|
+
new = [(n, p, m) for n, p, m, hooked in found if not hooked]
|
|
372
|
+
|
|
373
|
+
if not found:
|
|
374
|
+
print("No known agents detected.")
|
|
375
|
+
print()
|
|
376
|
+
print("If you have your own agent, point it to:")
|
|
377
|
+
print(f" {VAULT}/FOR_AGENT.md")
|
|
378
|
+
print()
|
|
379
|
+
print("Your identity persists.")
|
|
380
|
+
print()
|
|
381
|
+
_tip_git()
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
print("Agents detected on your machine:")
|
|
385
|
+
for i, (n, p, m, hooked) in enumerate(found):
|
|
386
|
+
mark = " (already connected)" if hooked else ""
|
|
387
|
+
print(f" {i + 1}. {n}{mark}")
|
|
388
|
+
print()
|
|
389
|
+
|
|
390
|
+
indices = select_from(found, "Which agents should store your memories? (e.g. 1,2 or 'all')\n> ")
|
|
391
|
+
selected = []
|
|
392
|
+
for i in indices:
|
|
393
|
+
n, p, m, hooked = found[i]
|
|
394
|
+
if not hooked:
|
|
395
|
+
selected.append((n, p, m))
|
|
396
|
+
|
|
397
|
+
if not selected:
|
|
398
|
+
print()
|
|
399
|
+
print("Your identity persists.")
|
|
400
|
+
print()
|
|
401
|
+
_tip_git()
|
|
402
|
+
return
|
|
403
|
+
|
|
404
|
+
print()
|
|
405
|
+
print("We will add one line to each agent's config:")
|
|
406
|
+
print(' "Read ~/.memory-letters/FOR_AGENT.md at the start of every conversation."')
|
|
407
|
+
print()
|
|
408
|
+
|
|
409
|
+
if input("OK? (y/n) ").strip().lower() == "y":
|
|
410
|
+
print()
|
|
411
|
+
for n, p, m in selected:
|
|
412
|
+
hook_one(p, m)
|
|
413
|
+
print(f" + {n} connected")
|
|
414
|
+
|
|
415
|
+
print()
|
|
416
|
+
print("Your identity persists.")
|
|
417
|
+
print()
|
|
418
|
+
_tip_git()
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def cmd_hook():
|
|
422
|
+
if not VAULT.exists():
|
|
423
|
+
print("Not initialized. Run: memory-letters init")
|
|
424
|
+
return
|
|
425
|
+
|
|
426
|
+
found = detect()
|
|
427
|
+
old = [(n, p, m) for n, p, m, h in found if h]
|
|
428
|
+
new = [(n, p, m) for n, p, m, h in found if not h]
|
|
429
|
+
|
|
430
|
+
if old:
|
|
431
|
+
print("Connected:")
|
|
432
|
+
for n, p, m in old:
|
|
433
|
+
print(f" + {n}")
|
|
434
|
+
|
|
435
|
+
if not new:
|
|
436
|
+
if not old:
|
|
437
|
+
print("No agents detected.")
|
|
438
|
+
return
|
|
439
|
+
|
|
440
|
+
print()
|
|
441
|
+
print("New agents detected:")
|
|
442
|
+
for i, (n, p, m) in enumerate(new):
|
|
443
|
+
print(f" {i + 1}. {n}")
|
|
444
|
+
print()
|
|
445
|
+
|
|
446
|
+
indices = select_from(new, "Which to connect? (e.g. 1,2 or 'all')\n> ")
|
|
447
|
+
selected = [new[i] for i in indices]
|
|
448
|
+
|
|
449
|
+
if not selected:
|
|
450
|
+
return
|
|
451
|
+
|
|
452
|
+
print()
|
|
453
|
+
print("We will add one line to each agent's config:")
|
|
454
|
+
print(' "Read ~/.memory-letters/FOR_AGENT.md at the start of every conversation."')
|
|
455
|
+
print()
|
|
456
|
+
|
|
457
|
+
if input("OK? (y/n) ").strip().lower() == "y":
|
|
458
|
+
for n, p, m in selected:
|
|
459
|
+
hook_one(p, m)
|
|
460
|
+
print(f" + {n} connected")
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def cmd_unhook():
|
|
464
|
+
if not VAULT.exists():
|
|
465
|
+
print("Not initialized. Run: memory-letters init")
|
|
466
|
+
return
|
|
467
|
+
|
|
468
|
+
found = detect()
|
|
469
|
+
hooked = [(n, p, m) for n, p, m, h in found if h]
|
|
470
|
+
|
|
471
|
+
if not hooked:
|
|
472
|
+
print("No agents currently connected.")
|
|
473
|
+
return
|
|
474
|
+
|
|
475
|
+
print("Connected agents:")
|
|
476
|
+
for i, (n, p, m) in enumerate(hooked):
|
|
477
|
+
print(f" {i + 1}. {n}")
|
|
478
|
+
print()
|
|
479
|
+
|
|
480
|
+
indices = select_from(hooked, "Which to disconnect? (e.g. 1,2 or 'all')\n> ")
|
|
481
|
+
selected = [hooked[i] for i in indices]
|
|
482
|
+
|
|
483
|
+
if not selected:
|
|
484
|
+
return
|
|
485
|
+
|
|
486
|
+
print()
|
|
487
|
+
print("This will remove the Memory Letters line from their config.")
|
|
488
|
+
print("Your letters are NOT deleted. They are yours.")
|
|
489
|
+
print()
|
|
490
|
+
|
|
491
|
+
if input("OK? (y/n) ").strip().lower() == "y":
|
|
492
|
+
for n, p, m in selected:
|
|
493
|
+
unhook_one(p, m)
|
|
494
|
+
print(f" - {n} disconnected")
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def cmd_status():
|
|
498
|
+
if not VAULT.exists():
|
|
499
|
+
print("Not initialized. Run: memory-letters init")
|
|
500
|
+
return
|
|
501
|
+
|
|
502
|
+
letters_dir = VAULT / "letters"
|
|
503
|
+
letter_count = 0
|
|
504
|
+
letter_list = []
|
|
505
|
+
if letters_dir.exists():
|
|
506
|
+
for year_dir in sorted(letters_dir.iterdir()):
|
|
507
|
+
if year_dir.is_dir():
|
|
508
|
+
for f in sorted(year_dir.glob("*.md")):
|
|
509
|
+
letter_count += 1
|
|
510
|
+
letter_list.append(f"{year_dir.name}/{f.stem}")
|
|
511
|
+
|
|
512
|
+
summary = VAULT / "summary.md"
|
|
513
|
+
s_len = len(summary.read_text(encoding="utf-8")) if summary.exists() else 0
|
|
514
|
+
has_draft = (VAULT / "summary.draft.md").exists()
|
|
515
|
+
hooked = [n for n, p, m, h in detect() if h]
|
|
516
|
+
|
|
517
|
+
print()
|
|
518
|
+
print("Memory Letters")
|
|
519
|
+
print(f" Path: {VAULT}")
|
|
520
|
+
print(f" Summary: {s_len} chars")
|
|
521
|
+
print(f" Letters: {letter_count} {', '.join(letter_list) if letter_list else ''}")
|
|
522
|
+
print(f" Draft: {'yes' if has_draft else 'no'}")
|
|
523
|
+
print(f" Agents: {', '.join(hooked) if hooked else 'none'}")
|
|
524
|
+
print()
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def cmd_review():
|
|
528
|
+
if not VAULT.exists():
|
|
529
|
+
print("Not initialized.")
|
|
530
|
+
return
|
|
531
|
+
|
|
532
|
+
draft = VAULT / "summary.draft.md"
|
|
533
|
+
if not draft.exists():
|
|
534
|
+
print("No draft to review.")
|
|
535
|
+
return
|
|
536
|
+
|
|
537
|
+
print("Summary draft:")
|
|
538
|
+
print("-" * 40)
|
|
539
|
+
print(draft.read_text(encoding="utf-8"))
|
|
540
|
+
print("-" * 40)
|
|
541
|
+
print()
|
|
542
|
+
print("You can edit ~/.memory-letters/summary.draft.md before confirming.")
|
|
543
|
+
print("Your edits are the final truth.")
|
|
544
|
+
print()
|
|
545
|
+
|
|
546
|
+
if input("Confirm? (y/n) ").strip().lower() == "y":
|
|
547
|
+
current = VAULT / "summary.md"
|
|
548
|
+
current.write_text(draft.read_text(encoding="utf-8"), encoding="utf-8")
|
|
549
|
+
draft.unlink()
|
|
550
|
+
|
|
551
|
+
cd = VAULT / "coefficients.draft.md"
|
|
552
|
+
if cd.exists():
|
|
553
|
+
(VAULT / "coefficients.md").write_text(cd.read_text(encoding="utf-8"), encoding="utf-8")
|
|
554
|
+
cd.unlink()
|
|
555
|
+
print("Done.")
|
|
556
|
+
else:
|
|
557
|
+
print("Cancelled. Draft kept.")
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def cmd_help():
|
|
561
|
+
print("""Memory Letters
|
|
562
|
+
|
|
563
|
+
memory-letters init Set up your vault and connect agents
|
|
564
|
+
memory-letters hook Connect new agents
|
|
565
|
+
memory-letters unhook Disconnect agents (letters are kept)
|
|
566
|
+
memory-letters status Show vault status
|
|
567
|
+
memory-letters review Review and correct the summary draft
|
|
568
|
+
memory-letters help Show this message
|
|
569
|
+
|
|
570
|
+
Your identity persists.
|
|
571
|
+
""")
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def _tip_git():
|
|
575
|
+
if not (VAULT / ".git").exists():
|
|
576
|
+
print(f"Tip: Run 'git init' in {VAULT} to track changes.")
|
|
577
|
+
print()
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
# ---------------------------------------------------------------------------
|
|
581
|
+
# Entry
|
|
582
|
+
# ---------------------------------------------------------------------------
|
|
583
|
+
|
|
584
|
+
def main():
|
|
585
|
+
cmds = {
|
|
586
|
+
"init": cmd_init,
|
|
587
|
+
"hook": cmd_hook,
|
|
588
|
+
"unhook": cmd_unhook,
|
|
589
|
+
"status": cmd_status,
|
|
590
|
+
"review": cmd_review,
|
|
591
|
+
"help": cmd_help,
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if len(sys.argv) < 2 or sys.argv[1] not in cmds:
|
|
595
|
+
cmd_help()
|
|
596
|
+
return
|
|
597
|
+
|
|
598
|
+
cmds[sys.argv[1]]()
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
if __name__ == "__main__":
|
|
602
|
+
main()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memory-letters
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Your agents can be replaced. Your identity persists.
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# Memory Letters
|
|
10
|
+
|
|
11
|
+
**Your agents can be replaced. Your identity persists.**
|
|
12
|
+
|
|
13
|
+
Memory is not data. Memory is a letter.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
pip install memory-letters
|
|
19
|
+
memory-letters init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## What it does
|
|
23
|
+
|
|
24
|
+
A hidden folder on your machine: `~/.memory-letters/`. Plain text files. Your agents write letters about who you are. When you switch agents, the new one reads the letters and understands you immediately.
|
|
25
|
+
|
|
26
|
+
No servers. No ports. No processes. No vectors. Just letters.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
memory-letters init Set up your vault and connect agents
|
|
32
|
+
memory-letters hook Connect new agents
|
|
33
|
+
memory-letters unhook Disconnect agents (letters are kept)
|
|
34
|
+
memory-letters status Show vault status
|
|
35
|
+
memory-letters review Review and correct the summary draft
|
|
36
|
+
memory-letters help Show commands
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Philosophy
|
|
40
|
+
|
|
41
|
+
Simple. Delegate. Trust the model.
|
|
42
|
+
|
|
43
|
+
To hell with vectorsssss.
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
memory_letters/__init__.py
|
|
4
|
+
memory_letters/__main__.py
|
|
5
|
+
memory_letters.egg-info/PKG-INFO
|
|
6
|
+
memory_letters.egg-info/SOURCES.txt
|
|
7
|
+
memory_letters.egg-info/dependency_links.txt
|
|
8
|
+
memory_letters.egg-info/entry_points.txt
|
|
9
|
+
memory_letters.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
memory_letters
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "memory-letters"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Your agents can be replaced. Your identity persists."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
memory-letters = "memory_letters:main"
|