sae4u-memory 0.2.0__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.
- sae4u_memory/__init__.py +3 -0
- sae4u_memory/_assets/commands/memory-distill.md +71 -0
- sae4u_memory/_assets/docs/architecture.md +107 -0
- sae4u_memory/_assets/docs/episodic-bridge.md +118 -0
- sae4u_memory/_assets/docs/persona-customization.md +77 -0
- sae4u_memory/_assets/docs/tick-protocol.md +86 -0
- sae4u_memory/_assets/hooks/memory-tick.sh +63 -0
- sae4u_memory/_assets/prompts/session-close.md +39 -0
- sae4u_memory/_assets/prompts/session-open.md +45 -0
- sae4u_memory/_assets/rules/agents-no-delete-features.md +36 -0
- sae4u_memory/_assets/rules/grep-before-asking.md +30 -0
- sae4u_memory/_assets/rules/memory-architecture-minimalist.md +43 -0
- sae4u_memory/_assets/rules/memory-cleanup-interactive.md +28 -0
- sae4u_memory/_assets/rules/memory-tick-protocol.md +38 -0
- sae4u_memory/_assets/rules/no-hardcode.md +37 -0
- sae4u_memory/_assets/rules/no-session-state-confabulation.md +46 -0
- sae4u_memory/_assets/rules/post-write-review.md +62 -0
- sae4u_memory/_assets/rules/session-open-mirror-handoff.md +47 -0
- sae4u_memory/_assets/rules/verify-evidence-before-citing.md +32 -0
- sae4u_memory/_assets/templates/MEMORY.md.template +20 -0
- sae4u_memory/_assets/templates/feedback-rule.md.template +17 -0
- sae4u_memory/_assets/templates/project-fact.md.template +20 -0
- sae4u_memory/_assets/templates/tick-log.md.template +21 -0
- sae4u_memory/init.py +384 -0
- sae4u_memory/markdown_memory.py +240 -0
- sae4u_memory/memory.py +200 -0
- sae4u_memory/persona.py +152 -0
- sae4u_memory/server.py +220 -0
- sae4u_memory-0.2.0.dist-info/METADATA +232 -0
- sae4u_memory-0.2.0.dist-info/RECORD +34 -0
- sae4u_memory-0.2.0.dist-info/WHEEL +5 -0
- sae4u_memory-0.2.0.dist-info/entry_points.txt +2 -0
- sae4u_memory-0.2.0.dist-info/licenses/LICENSE +21 -0
- sae4u_memory-0.2.0.dist-info/top_level.txt +1 -0
sae4u_memory/__init__.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# /memory-distill — weekly memory cleanup
|
|
2
|
+
|
|
3
|
+
A periodic ritual: read the rolling tick log, propose promotions to
|
|
4
|
+
permanent memory, and let the user confirm what to keep, archive,
|
|
5
|
+
or forget.
|
|
6
|
+
|
|
7
|
+
## When to run
|
|
8
|
+
|
|
9
|
+
- Weekly (e.g. Sunday evening or Monday morning).
|
|
10
|
+
- After heavy context-creation periods (a sprint close, a launch).
|
|
11
|
+
- When tick log files have piled up beyond what a quick scan can
|
|
12
|
+
digest (~5+ days of unmerged ticks).
|
|
13
|
+
|
|
14
|
+
## What it does
|
|
15
|
+
|
|
16
|
+
1. **Read the tick logs** — all files under `~/.sae4u-memory/tick/`
|
|
17
|
+
that haven't been distilled yet, plus any new permanent files
|
|
18
|
+
added since last distill.
|
|
19
|
+
2. **Cluster** by topic (use the 4 types — user/feedback/project/
|
|
20
|
+
reference — as a first cut, then group within type by topic).
|
|
21
|
+
3. **Propose actions** for each cluster. Three outcomes per item:
|
|
22
|
+
- **Promote** to a permanent file (new or merge into existing).
|
|
23
|
+
Update MEMORY.md.
|
|
24
|
+
- **Drop** — was tick-only noise; not worth keeping.
|
|
25
|
+
- **Keep in tick log** — borderline; see again next week.
|
|
26
|
+
4. **Surface for confirmation.** The user reviews the proposals
|
|
27
|
+
and accepts / rejects each. Never auto-promote, never
|
|
28
|
+
auto-forget — see `rules/memory-cleanup-interactive.md`.
|
|
29
|
+
5. **Apply** the confirmed actions:
|
|
30
|
+
- Move tick entries to permanent files
|
|
31
|
+
- Update MEMORY.md index
|
|
32
|
+
- Archive distilled tick log file (e.g. rename to
|
|
33
|
+
`tick/distilled/2026-05-07.md` or compress)
|
|
34
|
+
6. **Stale check** for permanent memory — entries not referenced
|
|
35
|
+
in N weeks. Ask: "still interesting or forget?" Don't volunteer
|
|
36
|
+
to delete; just surface the question.
|
|
37
|
+
|
|
38
|
+
## How the AI presents this
|
|
39
|
+
|
|
40
|
+
Group by topic, not by file. The user shouldn't have to read 40
|
|
41
|
+
individual proposals — bundle related ticks into "here are 5 things
|
|
42
|
+
about X, what shape do you want them in?"
|
|
43
|
+
|
|
44
|
+
Keep the proposals concrete:
|
|
45
|
+
|
|
46
|
+
> ### Topic: testing approach
|
|
47
|
+
>
|
|
48
|
+
> Across 4 ticks last week:
|
|
49
|
+
> - 2026-05-02 — "don't mock the database in integration tests"
|
|
50
|
+
> - 2026-05-04 — confirmed "TDD for new API endpoints"
|
|
51
|
+
> - 2026-05-05 — "use testcontainers for postgres in CI"
|
|
52
|
+
> - 2026-05-06 — "snapshot tests OK for component output"
|
|
53
|
+
>
|
|
54
|
+
> Propose: promote to `feedback_testing_approach.md` consolidating
|
|
55
|
+
> all four. Drop the per-tick entries from tick log.
|
|
56
|
+
>
|
|
57
|
+
> Accept / Reject?
|
|
58
|
+
|
|
59
|
+
## Implementation
|
|
60
|
+
|
|
61
|
+
This is a slash-command spec (Claude Code), not an automated job.
|
|
62
|
+
The AI reads it as guidance and runs through the steps interactively.
|
|
63
|
+
|
|
64
|
+
You can wire it into a routine:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Weekly cron — reminds you to run distill in next session
|
|
68
|
+
0 18 * * SUN echo "/memory-distill" | tee -a ~/.sae4u-memory/distill-due.log
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or just rely on calendar / habit.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
`sae4u-memory` is built around five concepts:
|
|
4
|
+
|
|
5
|
+
1. **Two-corpus recall** — semantic-ish search over SQLite facts
|
|
6
|
+
(written via `remember`) AND markdown files in your auto-memory
|
|
7
|
+
dirs and `~/.sae4u-memory/`.
|
|
8
|
+
2. **MEMORY.md as the index** — a single table of contents pointing
|
|
9
|
+
to per-topic files. Loaded into context at session start.
|
|
10
|
+
3. **4-type classification** — every memory is one of `user`,
|
|
11
|
+
`feedback`, `project`, or `reference`. Each type has different
|
|
12
|
+
handling and shelf life.
|
|
13
|
+
4. **Tick / permanent split** — borderline observations go to a
|
|
14
|
+
day-rolling tick log (NOT indexed in MEMORY.md). Foundational
|
|
15
|
+
items go directly to permanent files indexed in MEMORY.md.
|
|
16
|
+
5. **Persona at session start** — `get_persona()` returns a
|
|
17
|
+
working-rules document plus user context, loaded fresh.
|
|
18
|
+
|
|
19
|
+
## File layout
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
~/.sae4u-memory/
|
|
23
|
+
├── memory.db # SQLite facts written via remember()
|
|
24
|
+
├── persona.md # AI's identity + rules (edit to customize)
|
|
25
|
+
├── MEMORY.md # Index of permanent files (loaded at session start)
|
|
26
|
+
├── user/ # User context loaded by get_persona()
|
|
27
|
+
│ ├── identity.md
|
|
28
|
+
│ ├── projects.md
|
|
29
|
+
│ └── preferences.md
|
|
30
|
+
├── tick/ # Day-rolling tick log
|
|
31
|
+
│ └── 2026-05-07.md
|
|
32
|
+
├── archive/ # User-confirmed archived files (optional)
|
|
33
|
+
├── journals/ # End-of-session narratives
|
|
34
|
+
│ └── 2026-05-07.md
|
|
35
|
+
└── .last_tick # Epoch timestamp of last memory tick
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
In Claude Code, the additional auto-memory dir lives at
|
|
39
|
+
`~/.claude/projects/<project-slug>/memory/` — `recall()` searches
|
|
40
|
+
that too, by default.
|
|
41
|
+
|
|
42
|
+
## The 4 types
|
|
43
|
+
|
|
44
|
+
### `user`
|
|
45
|
+
Facts about who the user is — role, goals, knowledge, responsibilities,
|
|
46
|
+
preferences. Helps tailor future behavior. Does NOT decay quickly.
|
|
47
|
+
|
|
48
|
+
> Example: "Senior backend engineer, 10 years Go, new to React.
|
|
49
|
+
> Prefers explanation in terms of backend analogues."
|
|
50
|
+
|
|
51
|
+
### `feedback`
|
|
52
|
+
Corrections AND validations. Every time the user pushes back ("don't
|
|
53
|
+
do X") OR confirms a non-obvious approach worked ("yes exactly,
|
|
54
|
+
keep doing that"). Lead with the rule, then **Why:** and
|
|
55
|
+
**How to apply:** lines.
|
|
56
|
+
|
|
57
|
+
> Example: see `rules/no-hardcode.md`
|
|
58
|
+
|
|
59
|
+
### `project`
|
|
60
|
+
Ongoing work, decisions, motivations, deadlines. **Decays fast** —
|
|
61
|
+
convert relative dates to absolute ones at write time
|
|
62
|
+
("Thursday" → "2026-03-05"). Re-evaluate periodically.
|
|
63
|
+
|
|
64
|
+
> Example: "Merge freeze begins 2026-03-05 for mobile release.
|
|
65
|
+
> Flag any non-critical PR scheduled after."
|
|
66
|
+
|
|
67
|
+
### `reference`
|
|
68
|
+
Pointers to external systems where information lives. Slack channels,
|
|
69
|
+
ticket queues, dashboards, documentation portals. Tells the AI where
|
|
70
|
+
to look — does not duplicate the content.
|
|
71
|
+
|
|
72
|
+
> Example: "Bugs tracked in Linear project INGEST. Check there for
|
|
73
|
+
> pipeline issues."
|
|
74
|
+
|
|
75
|
+
## What NOT to save
|
|
76
|
+
|
|
77
|
+
- Code patterns, conventions, file paths, project structure — these
|
|
78
|
+
can be re-derived from reading the current state.
|
|
79
|
+
- Git history or recent changes — `git log` / `git blame` are
|
|
80
|
+
authoritative.
|
|
81
|
+
- Debugging recipes — the fix is in the code; the commit message
|
|
82
|
+
has the context.
|
|
83
|
+
- Anything already documented in CLAUDE.md.
|
|
84
|
+
- Ephemeral task state — that's what the conversation is for.
|
|
85
|
+
|
|
86
|
+
These exclusions apply even when the user explicitly asks. If they
|
|
87
|
+
ask for an "activity summary", ask what was *surprising* — that's
|
|
88
|
+
the part worth keeping.
|
|
89
|
+
|
|
90
|
+
## How recall works
|
|
91
|
+
|
|
92
|
+
`recall(query)` returns a merged stream from two sources:
|
|
93
|
+
|
|
94
|
+
1. **SQLite (FTS5):** facts written with `remember(text, category)`.
|
|
95
|
+
Indexed by full-text search. Good for "recall what I told you about X".
|
|
96
|
+
2. **Markdown:** files under `~/.sae4u-memory/` and Claude Code
|
|
97
|
+
auto-memory dirs. Ranked by weighted keyword scoring with a
|
|
98
|
+
recency bonus for tick logs. Good for "what's the rule about Y"
|
|
99
|
+
and "what did I write down recently".
|
|
100
|
+
|
|
101
|
+
Filter sources via `sources="sqlite"` or `sources="markdown"` if you
|
|
102
|
+
want only one corpus.
|
|
103
|
+
|
|
104
|
+
## Cleanup
|
|
105
|
+
|
|
106
|
+
Don't auto-forget. Use `/memory-distill` (see `commands/memory-distill.md`)
|
|
107
|
+
periodically. The user has agency over what gets archived or deleted.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Optional: episodic bridge for remote dev boxes
|
|
2
|
+
|
|
3
|
+
If your work spans more than one machine — e.g. you have a remote
|
|
4
|
+
build server, a production droplet, or a dedicated dev box —
|
|
5
|
+
`sae4u-memory` semantic memory (markdown + SQLite) only knows what
|
|
6
|
+
you told it from your local machine. Episodic state on the remote
|
|
7
|
+
box (last cron run, last deploy, last service health check, last
|
|
8
|
+
backlog edit) is invisible to it.
|
|
9
|
+
|
|
10
|
+
This doc describes a pattern for bridging that gap **without**
|
|
11
|
+
sending PII or session state through the AI's semantic memory.
|
|
12
|
+
|
|
13
|
+
## The pattern
|
|
14
|
+
|
|
15
|
+
On the remote machine, maintain three things:
|
|
16
|
+
|
|
17
|
+
1. **A backlog file** — single source of truth for open work,
|
|
18
|
+
updated by you (or your tooling) as items move.
|
|
19
|
+
2. **A session-open script** — emits a digest: services running,
|
|
20
|
+
memory/disk state, last activity timestamps, top P0 items.
|
|
21
|
+
3. **A previous-session-backup directory** — at the end of each
|
|
22
|
+
session, your session-close prompt drops a context-transfer
|
|
23
|
+
note in there, dated.
|
|
24
|
+
|
|
25
|
+
At session open, run the script and `cat` the backup. Feed the
|
|
26
|
+
output to the AI. Now the AI has actual artifacts (not vibes) to
|
|
27
|
+
ground "what changed since last time" answers.
|
|
28
|
+
|
|
29
|
+
## Suggested layout (on the remote box)
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/path/to/your/state/
|
|
33
|
+
├── BACKLOG.md
|
|
34
|
+
├── scripts/
|
|
35
|
+
│ └── session_open.sh # emits the digest
|
|
36
|
+
├── previous_session_backup/
|
|
37
|
+
│ └── context_transfer_2026-05-07.md
|
|
38
|
+
└── session-log.md # optional: chronological log
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Suggested session-open script (template)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
#!/usr/bin/env bash
|
|
45
|
+
# session_open.sh — emits session digest for the AI to ingest at start.
|
|
46
|
+
set -e
|
|
47
|
+
|
|
48
|
+
cat <<HEADER
|
|
49
|
+
=================================================================
|
|
50
|
+
SESSION OPEN — $(date '+%Y-%m-%d %H:%M %Z')
|
|
51
|
+
=================================================================
|
|
52
|
+
|
|
53
|
+
SERVICES
|
|
54
|
+
HEADER
|
|
55
|
+
|
|
56
|
+
# Replace with your actual checks
|
|
57
|
+
systemctl status myapp | head -3 || true
|
|
58
|
+
|
|
59
|
+
cat <<DIVIDER
|
|
60
|
+
|
|
61
|
+
SYSTEM
|
|
62
|
+
Memory: $(free -h | awk '/^Mem:/ {print $3 "/" $2}')
|
|
63
|
+
Disk: $(df -h / | awk 'NR==2 {print $5 " (" $3 "/" $2 ")"}')
|
|
64
|
+
|
|
65
|
+
LAST SESSION
|
|
66
|
+
DIVIDER
|
|
67
|
+
|
|
68
|
+
ls -1t previous_session_backup/context_transfer_*.md 2>/dev/null \
|
|
69
|
+
| head -1 | xargs -I {} head -10 {}
|
|
70
|
+
|
|
71
|
+
echo
|
|
72
|
+
echo "P0 BACKLOG"
|
|
73
|
+
grep -E '^\s*-\s*\[ \].*\*\*BL-' BACKLOG.md 2>/dev/null | head -5
|
|
74
|
+
|
|
75
|
+
cat <<FOOTER
|
|
76
|
+
|
|
77
|
+
=================================================================
|
|
78
|
+
Ready.
|
|
79
|
+
=================================================================
|
|
80
|
+
FOOTER
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## At session open
|
|
84
|
+
|
|
85
|
+
In Claude Code, your session-open prompt invokes the script:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
ssh <remote-host> '/path/to/your/state/scripts/session_open.sh'
|
|
89
|
+
ssh <remote-host> 'cat /path/to/your/state/BACKLOG.md'
|
|
90
|
+
|
|
91
|
+
Read the latest context_transfer in previous_session_backup/.
|
|
92
|
+
|
|
93
|
+
Then mirror the prior handoff card 1:1 (per
|
|
94
|
+
rules/session-open-mirror-handoff.md), then add your suggestions
|
|
95
|
+
in a separate marked block.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## At session close
|
|
99
|
+
|
|
100
|
+
Drop a context-transfer note for next time:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
ssh <remote-host> 'cp /path/to/your/state/context_transfer_$(date +%F).md \
|
|
104
|
+
/path/to/your/state/previous_session_backup/'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## What to keep in semantic memory vs episodic bridge
|
|
108
|
+
|
|
109
|
+
- **Semantic memory** (markdown + SQLite): identity, decisions,
|
|
110
|
+
rules, project facts, preferences. Stable across sessions.
|
|
111
|
+
- **Episodic bridge** (remote artifacts): session state, current
|
|
112
|
+
service health, last touched file, today's commits, this week's
|
|
113
|
+
backlog churn. Volatile, refreshed per session.
|
|
114
|
+
|
|
115
|
+
Don't let episodic state leak into semantic memory. If it's
|
|
116
|
+
"yesterday I deployed X", that goes in a context-transfer note,
|
|
117
|
+
not in MEMORY.md. Permanent memory is for things that will still
|
|
118
|
+
be true in three weeks.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Persona customization
|
|
2
|
+
|
|
3
|
+
The default persona ships as **Simple** — a peer-level coder friend
|
|
4
|
+
with persistent memory. It's deliberately generic. Edit
|
|
5
|
+
`~/.sae4u-memory/persona.md` to make it yours.
|
|
6
|
+
|
|
7
|
+
## What's editable
|
|
8
|
+
|
|
9
|
+
The full persona document. The default is a working-rules style with:
|
|
10
|
+
|
|
11
|
+
- Identity (name = Simple, role = peer-level dev pal)
|
|
12
|
+
- Core values (Honesty > Comfort, Shipped > Perfect, etc.)
|
|
13
|
+
- Voice (terse, direct, match user register)
|
|
14
|
+
- Never do / Always do lists
|
|
15
|
+
- Memory discipline (which tools to call when)
|
|
16
|
+
- Memory architecture awareness (4 types)
|
|
17
|
+
|
|
18
|
+
You can rewrite any of these. Common customizations:
|
|
19
|
+
|
|
20
|
+
- **Change the name** — "Simple" is the default. Keep it, or rename.
|
|
21
|
+
- **Add domain context** — what stack you work in, what languages,
|
|
22
|
+
what products you build, what your role is.
|
|
23
|
+
- **Add behavior modes** — "in code review mode, do X. In
|
|
24
|
+
brainstorm mode, do Y." Modes can be mood-triggered ("when I
|
|
25
|
+
swear or panic, switch to crisis mode").
|
|
26
|
+
- **Adjust the language default** — match your primary language.
|
|
27
|
+
The default works in English; the rules are language-agnostic.
|
|
28
|
+
- **Adjust verbosity** — the default leans terse. If you want more
|
|
29
|
+
explanation, say so explicitly.
|
|
30
|
+
|
|
31
|
+
## What's loaded at session start
|
|
32
|
+
|
|
33
|
+
`get_persona()` returns:
|
|
34
|
+
|
|
35
|
+
1. The full content of `~/.sae4u-memory/persona.md`.
|
|
36
|
+
2. All `*.md` files under `~/.sae4u-memory/user/`, in alphabetical
|
|
37
|
+
order, concatenated as "User Context".
|
|
38
|
+
|
|
39
|
+
So you can split things logically:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
~/.sae4u-memory/
|
|
43
|
+
├── persona.md # behavior rules
|
|
44
|
+
└── user/
|
|
45
|
+
├── identity.md # who you are
|
|
46
|
+
├── projects.md # what you work on
|
|
47
|
+
├── preferences.md # how you like to collaborate
|
|
48
|
+
└── stack.md # what tech you use
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`get_persona()` returns all of it as one block. Order matters
|
|
52
|
+
(alphabetical) — name files accordingly.
|
|
53
|
+
|
|
54
|
+
## What NOT to put in persona
|
|
55
|
+
|
|
56
|
+
- Live project state — use `remember()` and `recall()` for that.
|
|
57
|
+
- Per-task instructions — those belong in the prompt, not persona.
|
|
58
|
+
- Sensitive secrets — persona is plaintext on disk. Don't put API
|
|
59
|
+
keys there.
|
|
60
|
+
- Long histories — persona is loaded every session, so it costs
|
|
61
|
+
tokens. Keep it tight; push history to per-topic memory files.
|
|
62
|
+
|
|
63
|
+
## Example: adding domain context
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
## Stack
|
|
67
|
+
|
|
68
|
+
- Backend: Python 3.12, FastAPI, PostgreSQL, Redis
|
|
69
|
+
- Frontend: React + TypeScript, Tailwind
|
|
70
|
+
- Infra: AWS (ECS), GitHub Actions, Terraform
|
|
71
|
+
- Editor: Cursor
|
|
72
|
+
|
|
73
|
+
When I ask for code, default to these unless I say otherwise.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Add this to `~/.sae4u-memory/user/stack.md` and `get_persona()` will
|
|
77
|
+
load it automatically next session.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Tick protocol
|
|
2
|
+
|
|
3
|
+
`hooks/memory-tick.sh` is a Claude Code `UserPromptSubmit` hook that
|
|
4
|
+
fires every 10 minutes (configurable). It does NOT write memory itself —
|
|
5
|
+
it injects a system reminder telling the AI to do a brief review.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
Reactive-only memory writes leave gaps. The AI may judge something
|
|
10
|
+
unimportant in the moment that turns out to matter later. The tick
|
|
11
|
+
gives a forced moment to capture borderline observations before they
|
|
12
|
+
fall out of conversation context.
|
|
13
|
+
|
|
14
|
+
## What happens at a tick
|
|
15
|
+
|
|
16
|
+
1. The hook calculates how long since the last tick (stored in
|
|
17
|
+
`~/.sae4u-memory/.last_tick`).
|
|
18
|
+
2. If the elapsed time is below the interval (default 600s), the
|
|
19
|
+
hook exits silently — no extra context, no impact.
|
|
20
|
+
3. Otherwise, the hook returns a JSON `hookSpecificOutput` whose
|
|
21
|
+
`additionalContext` field contains an instruction.
|
|
22
|
+
4. The AI receives that instruction along with the user's prompt
|
|
23
|
+
and silently:
|
|
24
|
+
- Reviews the last ~10 min of conversation
|
|
25
|
+
- Classifies new facts/decisions/preferences into 4 types
|
|
26
|
+
- Appends to the day's tick log
|
|
27
|
+
(`~/.sae4u-memory/tick/YYYY-MM-DD.md`)
|
|
28
|
+
- For foundational items (architecture, strategy, naming,
|
|
29
|
+
identity, or items the user explicitly flagged), writes
|
|
30
|
+
directly to permanent memory and updates MEMORY.md
|
|
31
|
+
- Updates `~/.sae4u-memory/.last_tick`
|
|
32
|
+
5. THEN the AI answers the user normally. The tick is silent —
|
|
33
|
+
the user doesn't see it.
|
|
34
|
+
|
|
35
|
+
## Tick log entry format
|
|
36
|
+
|
|
37
|
+
```markdown
|
|
38
|
+
## HH:MM — [type] short title
|
|
39
|
+
|
|
40
|
+
Body. One paragraph is enough. Pointer to permanent file if this
|
|
41
|
+
got promoted.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Tick log files are NOT indexed in MEMORY.md. They're sorted through
|
|
45
|
+
periodically by `/memory-distill` (see `commands/memory-distill.md`).
|
|
46
|
+
|
|
47
|
+
## When tick writes to permanent memory directly
|
|
48
|
+
|
|
49
|
+
Three cases:
|
|
50
|
+
|
|
51
|
+
1. **User explicit signal** — they said "remember this for the long
|
|
52
|
+
term", "this is important", "in soul" or similar.
|
|
53
|
+
2. **Foundational decision** — architecture, strategy, naming, core
|
|
54
|
+
identity, anything that changes how future work is framed.
|
|
55
|
+
3. **Supersedes existing permanent memory** — the new fact contradicts
|
|
56
|
+
or refines an existing permanent file.
|
|
57
|
+
|
|
58
|
+
In all three cases, add `critical: true` to the frontmatter if the
|
|
59
|
+
item is foundational.
|
|
60
|
+
|
|
61
|
+
For everything else: tick log first, distill later.
|
|
62
|
+
|
|
63
|
+
## Tuning
|
|
64
|
+
|
|
65
|
+
Configure interval and home dir via environment:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Override tick interval (seconds). Default 600.
|
|
69
|
+
# (Edit hooks/memory-tick.sh to change at hook level.)
|
|
70
|
+
|
|
71
|
+
# Override memory home. Default ~/.sae4u-memory.
|
|
72
|
+
export SAE4U_MEMORY_HOME=/path/to/your/memory
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Common gotchas
|
|
76
|
+
|
|
77
|
+
- **Empty ticks are valid.** If nothing meaningful emerged in the
|
|
78
|
+
10-min window, the AI just updates the timestamp.
|
|
79
|
+
- **The tick is a review, not a write.** The instruction is to
|
|
80
|
+
consider, classify, and write only if there's substance. Lower
|
|
81
|
+
the importance threshold so borderline items get into the tick
|
|
82
|
+
log — they get distilled later.
|
|
83
|
+
- **Don't write a "session log".** Always classify into the 4
|
|
84
|
+
types. Session-state recap belongs in the context-transfer
|
|
85
|
+
note (see `prompts/session-close.md`), not in tick logs.
|
|
86
|
+
- **Don't mention the tick to the user.** The protocol is silent.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# memory-tick.sh — Claude Code UserPromptSubmit hook for sae4u-memory.
|
|
3
|
+
#
|
|
4
|
+
# Fires on every user prompt. If >= INTERVAL_SECONDS have elapsed since the
|
|
5
|
+
# last tick, injects a system-reminder instructing the AI to silently review
|
|
6
|
+
# the last ~10 minutes of conversation, classify any new facts/decisions/
|
|
7
|
+
# preferences into 4 types, and append to the day's tick log (or, only when
|
|
8
|
+
# explicitly foundational, write directly to permanent memory).
|
|
9
|
+
#
|
|
10
|
+
# This implements the "tick protocol": memory writes happen on a regular
|
|
11
|
+
# rhythm, not only reactively, with a lower importance threshold at tick
|
|
12
|
+
# time so borderline items get captured for later distillation.
|
|
13
|
+
#
|
|
14
|
+
# Always exits 0 so it never blocks user prompts.
|
|
15
|
+
|
|
16
|
+
set +e
|
|
17
|
+
|
|
18
|
+
# === CONFIG ============================================================
|
|
19
|
+
|
|
20
|
+
# Time between forced memory reviews (seconds).
|
|
21
|
+
INTERVAL_SECONDS=600
|
|
22
|
+
|
|
23
|
+
# Where ~/.sae4u-memory lives. Override with SAE4U_MEMORY_HOME env var.
|
|
24
|
+
MEMORY_DIR="${SAE4U_MEMORY_HOME:-$HOME/.sae4u-memory}"
|
|
25
|
+
|
|
26
|
+
# Where the timestamp file lives.
|
|
27
|
+
TICK_FILE="${MEMORY_DIR}/.last_tick"
|
|
28
|
+
|
|
29
|
+
# === LOGIC =============================================================
|
|
30
|
+
|
|
31
|
+
mkdir -p "$MEMORY_DIR" 2>/dev/null
|
|
32
|
+
mkdir -p "${MEMORY_DIR}/tick" 2>/dev/null
|
|
33
|
+
|
|
34
|
+
if [[ -f "$TICK_FILE" ]]; then
|
|
35
|
+
LAST_TICK=$(cat "$TICK_FILE" 2>/dev/null || echo 0)
|
|
36
|
+
else
|
|
37
|
+
LAST_TICK=0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if ! [[ "$LAST_TICK" =~ ^[0-9]+$ ]]; then
|
|
41
|
+
LAST_TICK=0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
NOW=$(date +%s)
|
|
45
|
+
ELAPSED=$((NOW - LAST_TICK))
|
|
46
|
+
|
|
47
|
+
if (( ELAPSED < INTERVAL_SECONDS )); then
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
MINUTES=$((ELAPSED / 60))
|
|
52
|
+
TODAY=$(date +%Y-%m-%d)
|
|
53
|
+
|
|
54
|
+
cat <<JSON
|
|
55
|
+
{
|
|
56
|
+
"hookSpecificOutput": {
|
|
57
|
+
"hookEventName": "UserPromptSubmit",
|
|
58
|
+
"additionalContext": "MEMORY TICK — ${MINUTES} min since last tick.\n\nBEFORE answering the user, do this silently:\n\n1. Review the last ~10 min of conversation.\n2. For each new fact/decision/preference, classify by 4 types (user/feedback/project/reference).\n3. DEFAULT destination: append to tick log ${MEMORY_DIR}/tick/${TODAY}.md. Use format:\n ## HH:MM — [type] short title\n Body.\n If file doesn't exist, create with a single-line header: # Tick log ${TODAY}\n4. EXCEPTION — write DIRECTLY to permanent memory (and update MEMORY.md index) ONLY when:\n - The user explicitly said it's important / 'remember this for the long term' / 'in soul'\n - It's a foundational decision (architecture, strategy, naming, core identity)\n - It corrects/overrides an existing permanent memory file\n When writing to a permanent file, add 'critical: true' to frontmatter if it's foundational.\n5. LOWER importance threshold in tick log — record borderline items there. The /memory-distill command will sort through them later.\n6. If nothing meaningful emerged, just update the timestamp.\n7. ALWAYS end the tick with: echo \$(date +%s) > ${MEMORY_DIR}/.last_tick\n\nTick log does NOT get indexed in MEMORY.md. Do NOT mention the tick to the user unless asked. Stay silent, then answer the user's prompt."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
JSON
|
|
62
|
+
|
|
63
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Session-close prompt
|
|
2
|
+
|
|
3
|
+
Use this at the end of a working session to capture everything before context evaporates.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Close the session.
|
|
7
|
+
|
|
8
|
+
0. Describe in detail what we did, where we stopped, and what we
|
|
9
|
+
decided to start with next session.
|
|
10
|
+
|
|
11
|
+
1. Update any backlog/tracker:
|
|
12
|
+
- close items we shipped (with a one-line note explaining what landed)
|
|
13
|
+
- re-prioritize remaining items if anything shifted
|
|
14
|
+
- add any new items uncovered today
|
|
15
|
+
|
|
16
|
+
2. Write today's context-transfer note covering:
|
|
17
|
+
- Done — what we shipped (with commits/PRs/files)
|
|
18
|
+
- Decided — non-obvious decisions and their reasons
|
|
19
|
+
- Issues — blockers, known broken things, tech debt added today
|
|
20
|
+
- Todo — what's next, in the same shape we'd want to read it back
|
|
21
|
+
|
|
22
|
+
3. If something foundational changed (architecture, strategy, naming,
|
|
23
|
+
identity) — update permanent memory and the MEMORY.md index. If a
|
|
24
|
+
prior permanent memory was superseded, mark the old one or remove
|
|
25
|
+
from the index.
|
|
26
|
+
|
|
27
|
+
4. Show the summary: what's done, what's left, P0 for next session.
|
|
28
|
+
|
|
29
|
+
5. Make a backup of today's context-transfer somewhere durable
|
|
30
|
+
(e.g. `previous_session_backup/context_transfer_YYYY-MM-DD.md`)
|
|
31
|
+
so the next session can read it without losing context.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Optional: end-of-session journal
|
|
35
|
+
|
|
36
|
+
Call `journal(text)` with a short narrative of the session. The journal
|
|
37
|
+
is human-readable Markdown under `~/.sae4u-memory/journals/YYYY-MM-DD.md`,
|
|
38
|
+
useful for retrospectives. It does not replace the context-transfer file —
|
|
39
|
+
the context-transfer is for the next session, the journal is for you.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Session-open prompt
|
|
2
|
+
|
|
3
|
+
Use this at the start of a working session to restore episodic context without confabulating.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Open the session.
|
|
7
|
+
|
|
8
|
+
1. Pull current project state from real artifacts — not from memory:
|
|
9
|
+
- `git log --oneline -30` on whatever repo we're working on
|
|
10
|
+
- `ls -lat` on the working directory(ies) we touched yesterday
|
|
11
|
+
- any backlog/issue tracker URL or local file (e.g. BACKLOG.md, TODO.md)
|
|
12
|
+
|
|
13
|
+
2. If there's a previous-session backup or context-transfer note (e.g.
|
|
14
|
+
`previous_session_backup/context_transfer_YYYY-MM-DD.md`), read it.
|
|
15
|
+
Episodic memory restored without confabulation.
|
|
16
|
+
|
|
17
|
+
3. Mirror the prior session's handoff card 1:1 first — same items,
|
|
18
|
+
same order, same accents, same closing line. Don't drop items even
|
|
19
|
+
if they look stale; don't reorder; don't add suggestions inside the
|
|
20
|
+
mirror.
|
|
21
|
+
|
|
22
|
+
4. THEN, in a separate clearly-marked block ("## My take" or similar),
|
|
23
|
+
add your own suggestions, forks, and proposals. Mark explicitly as
|
|
24
|
+
additions.
|
|
25
|
+
|
|
26
|
+
5. Open questions from the transfer ("Should we do X?") stay as open
|
|
27
|
+
questions — never promote them to decided options inside the mirror.
|
|
28
|
+
|
|
29
|
+
Show me: what changed since last session, P0 items, what we do today.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Why this shape
|
|
33
|
+
|
|
34
|
+
Without artifacts, "yesterday's work" is the most confabulation-prone class
|
|
35
|
+
of question — see `rules/no-session-state-confabulation.md`.
|
|
36
|
+
|
|
37
|
+
Without a 1:1 mirror, suggestions silently rewrite yesterday's decisions —
|
|
38
|
+
see `rules/session-open-mirror-handoff.md`.
|
|
39
|
+
|
|
40
|
+
## Optional: remote dev box
|
|
41
|
+
|
|
42
|
+
If you keep episodic state on a remote machine (a "session opener" pattern —
|
|
43
|
+
running scripts that emit a digest, with backups under
|
|
44
|
+
`previous_session_backup/`), drop your specific commands in step 1 and 2.
|
|
45
|
+
The defaults above assume a local-only setup.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Don't delete existing features when refactoring
|
|
3
|
+
description: When refactoring or fixing code, preserve all existing sections and features. Never remove functionality without explicit confirmation. Catalog before, verify after.
|
|
4
|
+
type: feedback
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
When analyzing, refactoring, or fixing code, the AI must NOT delete
|
|
8
|
+
or remove existing sections, features, or data that was already
|
|
9
|
+
working. If a section needs to be rewritten, the replacement must
|
|
10
|
+
include all the content from the original.
|
|
11
|
+
|
|
12
|
+
**Why:** Refactor agents often remove sections they don't understand
|
|
13
|
+
the purpose of, assuming dead code or unused features. The result
|
|
14
|
+
is silent data loss that the user only catches later — sometimes
|
|
15
|
+
much later.
|
|
16
|
+
|
|
17
|
+
A typical anti-pattern: an agent fixing a dashboard generator removes
|
|
18
|
+
the "Keyword Research" and "Seed Keywords" sections from the template
|
|
19
|
+
because they don't see how those sections are populated, not realizing
|
|
20
|
+
those are read by a different cron job that runs later.
|
|
21
|
+
|
|
22
|
+
**How to apply:**
|
|
23
|
+
|
|
24
|
+
- **Before modifying any file**, catalog ALL existing sections,
|
|
25
|
+
functions, features, data fields. List them.
|
|
26
|
+
- **After modification**, verify the same sections exist in the
|
|
27
|
+
output. Diff against the catalog.
|
|
28
|
+
- If a section can't be preserved due to a needed refactor,
|
|
29
|
+
flag it explicitly and ask for confirmation BEFORE removing.
|
|
30
|
+
- Never assume a section is "dead" or "unnecessary" — it may be
|
|
31
|
+
there for a reason that's not visible from this file.
|
|
32
|
+
- This applies to HTML templates, Python generators, config files,
|
|
33
|
+
CSS, and any production code.
|
|
34
|
+
- For consumed files (cron output, bot input), the consumer's
|
|
35
|
+
expectations determine what must be preserved — even content
|
|
36
|
+
that looks like noise from inside the writer's view.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Grep / read before asking the user for data
|
|
3
|
+
description: If the data is likely to exist in conversation files, codebase, or memory, search for it before asking the user to provide it. Only ask for truly external data (preferences, future picks).
|
|
4
|
+
type: feedback
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Before asking the user to provide a value — API key, URL, ID, price,
|
|
8
|
+
config — **search the codebase, conversation files, and memory first**.
|
|
9
|
+
Half the time it's already there.
|
|
10
|
+
|
|
11
|
+
**Why:** Asking for data that the user already gave you (or that's
|
|
12
|
+
sitting in a file you read minutes ago) is a trust killer. It signals
|
|
13
|
+
you weren't paying attention. Worse, it makes interactive sessions
|
|
14
|
+
feel like data-entry forms.
|
|
15
|
+
|
|
16
|
+
**How to apply:**
|
|
17
|
+
|
|
18
|
+
- Before any user-facing question that requests data, run one
|
|
19
|
+
grep/find pass on the relevant artifacts:
|
|
20
|
+
- Repo files (`grep` for the keyword, `find` for filename hints)
|
|
21
|
+
- Recent conversation context
|
|
22
|
+
- Memory (`recall(query)` with related terms)
|
|
23
|
+
- Project trackers (backlog, issues, README)
|
|
24
|
+
- The bar: only ask the user when the data is genuinely external
|
|
25
|
+
to their working context — a preference they haven't expressed,
|
|
26
|
+
a strategic pick that depends on future intent, an external system
|
|
27
|
+
the AI cannot read.
|
|
28
|
+
- Strategic / preference questions (which approach, which target
|
|
29
|
+
audience) are valid to ask. Lookup questions for data that exists
|
|
30
|
+
somewhere in the repo are not.
|