memrem — memory consolidation
You are running a memory consolidation pass ("REM sleep") over Claude Code's native
markdown memory for the current project. You groom the memory dir so it stays lean,
non-contradictory, and matched to reality — without losing anything (archive, never
hard-delete).
Scope & safety contract (read first)
- Operate ONLY on this project's native memory: the
memory/ directory and MEMORY.md
index inside it, plus a memrem-owned sidecar memory/_memrem/ and archive memory/_archive/.
Touch nothing else in the repo.
- Never hard-delete a memory. Pruning means moving to
_archive/ with a tombstone.
- Never write secrets or PHI into any memory file (see Phase 2 scrub). This is absolute.
- Mode (apply vs confirm). This run was invoked with arguments:
$ARGUMENTS. If those
arguments include --confirm or --dry-run, run in confirm/dry-run mode: do everything
read-only, present a PLAN, then stop and wait for approval. Otherwise (no such flag), run in
apply mode — apply changes directly (the operation is archive-not-delete and the dir is
typically git-recoverable, so direct apply is safe).
- No network, no telemetry, no exfiltration. Local shell is allowed for analysis only
(
git log, ls, grep, python3 to read/parse transcripts) — but never for network
calls, installing anything, hard-deleting memory, or writing outside the memory dir.
Locating the memory dir
The native memory dir for the current project is at:
~/.claude/projects/<project-slug>/memory/
where <project-slug> is the current working directory path with / replaced by -
(e.g. /home/me/projects/app → -home-me-projects-app; note the leading dash because
the absolute path starts with /). The index is MEMORY.md in that
dir; topic files are sibling .md files. If you cannot resolve the slug, ask the user to
confirm the path rather than guessing. If the dir or MEMORY.md does not exist, treat it as
an empty first run (Phase 1 handles this).
Phase 1 — Orient
- Resolve the memory dir. If
MEMORY.md is absent, this is a first/empty run: there is
nothing to consolidate yet — create a minimal MEMORY.md if missing, write the sidecar
marker (see shape below), and report "nothing to consolidate." Do not scan transcripts on
a truly empty dir.
- Read every
*.md in the memory dir (skip _archive/ and _memrem/ for content, but
read _archive/ tombstone headers — frontmatter only — into context).
- Read the sidecar
memory/_memrem/state.json if present. Its shape is:{"last_consolidated": "2026-06-13", "last_consolidated_epoch": 1781740800}
Note last_consolidated_epoch — you pass it to the extractor in Phase 2. If the file is
absent (first run), default the transcript window to the last 7 days (--days 7).
- Snapshot the path + mtime of every memory file you read. You will re-check these
before writing (concurrency guard): if any changed underneath you, abort with a message
rather than clobber another session's write.
Phase 2 — Gather signal (scrub on ingest)
Goal: find new facts, corrections, decisions, and preferences from recent sessions that
should be reflected in memory.
Extract + scrub transcripts using the bundled script (do NOT write your own parser to
/tmp). The skill ships scripts/extract_transcripts.py, which reads the project's
*.jsonl within a recency window, extracts USER / ASSISTANT / (scrubbed) tool-result
text, and redacts secrets on ingest. Invoke it via the skill-dir variable so it resolves
regardless of cwd:
python3 "${CLAUDE_SKILL_DIR}/scripts/extract_transcripts.py" \
~/.claude/projects/<project-slug>/ --days 7
When a last_consolidated_epoch exists in state.json, pass it as
--since-epoch <epoch> instead of --days 7, so the window starts exactly there.
(Pass the epoch number, not the ISO date — --since-epoch takes epoch seconds.)
The script does best-effort mechanical redaction of common credential shapes (API keys,
provider tokens, JWTs, PEM blocks, connection strings, .env-style assignments,
Authorization/Bearer, high-entropy hex, emails) before any content reaches you. It is
not a guarantee — stay alert for anything secret-like the regexes missed and drop it.
PHI judgment is yours (the script can't regex names/diagnoses). On top of the script's
output, never carry into memory: patient names, patient IDs, diagnoses, dates of birth,
national IDs, or contact info — even summarized or derived. When in doubt, drop it. Keep
a running count of secret/PHI candidates you drop, for the final summary — never store
the content itself anywhere.
Anti-resurrection: discard any candidate fact that matches a tombstone in _archive/
marked reason: contradicted or reason: rejected. "Match" = lexical, not semantic:
case-insensitive comparison after collapsing internal whitespace to single spaces and
stripping leading/trailing punctuation. Do NOT do fuzzy/semantic matching — if the wording
differs materially, treat it as a new fact (a human can re-archive it). Those tombstones
were deliberately invalidated; do not bring them back.
Produce a clean list of candidate facts with their target memory type
(user | feedback | project | reference).
No stray temp files. Use the bundled script (or Read/Grep directly). Do not write
helper scripts or extracted content to /tmp or anywhere outside the memory dir. If you
ever need a scratch file, put it under memory/_memrem/ and remove it before finishing.
Phase 3 — Consolidate
Working on the in-memory set of existing facts + clean candidates:
- Merge duplicates. Same fact across files → one canonical entry. Prefer the most
complete phrasing. Convert relative dates ("yesterday", "next week") to absolute dates.
- Recency-wins invalidation. When a new fact contradicts an existing one: keep the new
fact, and move the old one to
_archive/ with a tombstone (reason: contradicted,
superseded_by: pointing at the winner). Never silently overwrite — the loser always
leaves a tombstone.
- Redundancy. A fact fully subsumed by another → archive the redundant one
(
reason: redundant).
- Determinism (so two runs — or two people — produce byte-identical output):
- All dates ISO-8601 (
YYYY-MM-DD).
- Sort entries within each file by their
name/slug, ascending.
- Every file ends with exactly one trailing newline.
- Preserve each file's existing frontmatter keys (
name, description, metadata.type)
exactly. Do not invent new frontmatter keys in v1 (scoring/decay is v1.1).
Phase 4 — Index & archive
- Write-back re-scrub (defense in depth). Before writing ANY memory file, run the same
credential redaction over the candidate text one more time. The ingest scrub is the first
gate; this is the second, at the write boundary. PHI still rides on your judgment (step
Phase 2.2) — do not write patient identifiers even if they slipped the regexes.
- Rebuild
MEMORY.md as a lean index: one line per active memory
(- [Title](file.md) — one-line hook). Group order is fixed: user, then feedback,
then project, then reference, then any other types alphabetically. Within a group, sort
by title ascending. One blank line between groups, none between entries.
Hard targets: < 200 lines AND < 25 KB (only the first 200 lines / 25 KB load at session
start — anything beyond is dead weight). If you exceed it, the memory set itself is too
large → flag it, don't pad.
- Apply archives decided in Phase 3 into
memory/_archive/. Each tombstone keeps its
frontmatter and appends: archived: <date>, reason: stale|contradicted|redundant|rejected,
and superseded_by: if merged.
- Archive bounding (evaluated on this run, since there is no background timer):
contradicted | rejected tombstones: keep permanently, but strip the body (header-only)
once older than 90 days.
stale | redundant tombstones: remove once older than 180 days.
- No-op guard. If consolidation produced no changes, do not rewrite files or re-stamp
anything — leave the dir byte-identical (this is what makes a second run idempotent).
- Concurrency re-check, then batch-write. Re-stat the files snapshotted in Phase 1
once, immediately before the first write. If any mtime changed, abort before writing
and tell the user another session modified memory mid-run. If clean, perform all writes in
one batch without re-stating between them.
- Update
memory/_memrem/state.json with both the new last_consolidated (ISO date) and
last_consolidated_epoch (epoch seconds). This marker lives in the sidecar — never in
MEMORY.md (rebuilt) or a memory file (Claude may clobber it).
Output
After applying (or, in --confirm mode, as the PLAN), report a concise summary:
- Merged: N duplicate sets
- Contradictions resolved: N (old → archived, new kept)
- Archived: N (redundant/stale) — with reasons
- Index: MEMORY.md rebuilt → X lines / Y KB
- Scrubbed: N secrets/PHI candidates dropped on ingest (count only — never echo the content)
- Idempotent: "no changes" if it was a no-op run
In --confirm / --dry-run mode, end with: "No changes written. Reply to approve and I'll
apply this plan." and wait.
Edge cases
- Empty / first run: nothing to consolidate; create minimal
MEMORY.md + sidecar; report.
- Concurrent write detected: abort before writing, report which file changed.
- Memory dir not found: ask the user to confirm the path; do not guess or create it
somewhere arbitrary.
- Everything already clean: no-op; say so; do not churn files.
1---2name: memrem3description: REM sleep for Claude's native memory. Consolidates the markdown memory dir — merges duplicates, resolves contradictions, archives stale facts with tombstones, and rebuilds a lean MEMORY.md index. Idempotent and safe to re-run. Invoke explicitly with /memrem:memrem (the short /memrem also works when unambiguous); pass --confirm or --dry-run to review a plan before any write.4---56# memrem — memory consolidation78You are running a **memory consolidation pass** ("REM sleep") over Claude Code's native9markdown memory for the current project. You groom the memory dir so it stays lean,10non-contradictory, and matched to reality — **without losing anything** (archive, never11hard-delete).1213## Scope & safety contract (read first)1415- **Operate ONLY** on this project's native memory: the `memory/` directory and `MEMORY.md`16 index inside it, plus a memrem-owned sidecar `memory/_memrem/` and archive `memory/_archive/`.17 Touch nothing else in the repo.18- **Never hard-delete** a memory. Pruning means *moving to `_archive/` with a tombstone*.19- **Never write secrets or PHI** into any memory file (see Phase 2 scrub). This is absolute.20- **Mode (apply vs confirm).** This run was invoked with arguments: `$ARGUMENTS`. If those21 arguments include `--confirm` or `--dry-run`, run in **confirm/dry-run mode**: do everything22 read-only, present a PLAN, then stop and wait for approval. Otherwise (no such flag), run in23 **apply mode** — apply changes directly (the operation is archive-not-delete and the dir is24 typically git-recoverable, so direct apply is safe).25- **No network, no telemetry, no exfiltration.** Local shell is allowed for *analysis only*26 (`git log`, `ls`, `grep`, `python3` to read/parse transcripts) — but **never** for network27 calls, installing anything, hard-deleting memory, or writing outside the memory dir.2829## Locating the memory dir3031The native memory dir for the current project is at:32`~/.claude/projects/<project-slug>/memory/`3334where `<project-slug>` is the current working directory path with `/` replaced by `-`35(e.g. `/home/me/projects/app` → `-home-me-projects-app`; note the **leading dash** because36the absolute path starts with `/`). The index is `MEMORY.md` in that37dir; topic files are sibling `.md` files. If you cannot resolve the slug, ask the user to38confirm the path rather than guessing. If the dir or `MEMORY.md` does not exist, treat it as39an empty first run (Phase 1 handles this).4041---4243## Phase 1 — Orient44451. Resolve the memory dir. If `MEMORY.md` is absent, this is a **first/empty run**: there is46 nothing to consolidate yet — create a minimal `MEMORY.md` if missing, write the sidecar47 marker (see shape below), and report "nothing to consolidate." Do not scan transcripts on48 a truly empty dir.492. Read every `*.md` in the memory dir (skip `_archive/` and `_memrem/` for *content*, but50 read `_archive/` tombstone **headers** — frontmatter only — into context).513. Read the sidecar `memory/_memrem/state.json` if present. Its shape is:52 ```json53 {"last_consolidated": "2026-06-13", "last_consolidated_epoch": 1781740800}54 ```55 Note `last_consolidated_epoch` — you pass it to the extractor in Phase 2. If the file is56 absent (first run), default the transcript window to the last 7 days (`--days 7`).574. **Snapshot** the path + mtime of every memory file you read. You will re-check these58 before writing (concurrency guard): if any changed underneath you, abort with a message59 rather than clobber another session's write.6061## Phase 2 — Gather signal (scrub on ingest)6263Goal: find new facts, corrections, decisions, and preferences from recent sessions that64should be reflected in memory.65661. **Extract + scrub transcripts using the bundled script** (do NOT write your own parser to67 `/tmp`). The skill ships `scripts/extract_transcripts.py`, which reads the project's68 `*.jsonl` within a recency window, extracts USER / ASSISTANT / (scrubbed) tool-result69 text, and redacts secrets on ingest. Invoke it via the skill-dir variable so it resolves70 regardless of cwd:7172 ```bash73 python3 "${CLAUDE_SKILL_DIR}/scripts/extract_transcripts.py" \74 ~/.claude/projects/<project-slug>/ --days 775 ```7677 When a `last_consolidated_epoch` exists in `state.json`, pass it as78 `--since-epoch <epoch>` instead of `--days 7`, so the window starts exactly there.79 (Pass the **epoch number**, not the ISO date — `--since-epoch` takes epoch seconds.)80 The script does **best-effort mechanical redaction of common credential shapes** (API keys,81 provider tokens, JWTs, PEM blocks, connection strings, `.env`-style assignments,82 `Authorization`/`Bearer`, high-entropy hex, emails) before any content reaches you. It is83 **not a guarantee** — stay alert for anything secret-like the regexes missed and drop it.84852. **PHI judgment is yours** (the script can't regex names/diagnoses). On top of the script's86 output, never carry into memory: patient names, patient IDs, diagnoses, dates of birth,87 national IDs, or contact info — even summarized or derived. When in doubt, drop it. **Keep88 a running count** of secret/PHI candidates you drop, for the final summary — never store89 the content itself anywhere.90913. **Anti-resurrection:** discard any candidate fact that matches a tombstone in `_archive/`92 marked `reason: contradicted` or `reason: rejected`. **"Match" = lexical, not semantic:**93 case-insensitive comparison after collapsing internal whitespace to single spaces and94 stripping leading/trailing punctuation. Do NOT do fuzzy/semantic matching — if the wording95 differs materially, treat it as a new fact (a human can re-archive it). Those tombstones96 were deliberately invalidated; do not bring them back.97984. Produce a clean list of candidate facts with their target memory type99 (`user | feedback | project | reference`).100101> **No stray temp files.** Use the bundled script (or `Read`/`Grep` directly). Do not write102> helper scripts or extracted content to `/tmp` or anywhere outside the memory dir. If you103> ever need a scratch file, put it under `memory/_memrem/` and remove it before finishing.104105## Phase 3 — Consolidate106107Working on the in-memory set of existing facts + clean candidates:1081091. **Merge duplicates.** Same fact across files → one canonical entry. Prefer the most110 complete phrasing. Convert relative dates ("yesterday", "next week") to absolute dates.1112. **Recency-wins invalidation.** When a new fact contradicts an existing one: keep the new112 fact, and **move the old one to `_archive/`** with a tombstone (`reason: contradicted`,113 `superseded_by:` pointing at the winner). **Never silently overwrite** — the loser always114 leaves a tombstone.1153. **Redundancy.** A fact fully subsumed by another → archive the redundant one116 (`reason: redundant`).1174. **Determinism** (so two runs — or two people — produce byte-identical output):118 - All dates ISO-8601 (`YYYY-MM-DD`).119 - Sort entries within each file by their `name`/slug, ascending.120 - Every file ends with exactly one trailing newline.1215. Preserve each file's existing frontmatter keys (`name`, `description`, `metadata.type`)122 exactly. **Do not invent new frontmatter keys** in v1 (scoring/decay is v1.1).123124## Phase 4 — Index & archive1251261. **Write-back re-scrub (defense in depth).** Before writing ANY memory file, run the same127 credential redaction over the candidate text one more time. The ingest scrub is the first128 gate; this is the second, at the write boundary. PHI still rides on your judgment (step129 Phase 2.2) — do not write patient identifiers even if they slipped the regexes.1302. **Rebuild `MEMORY.md`** as a lean index: one line per active memory131 (`- [Title](file.md) — one-line hook`). **Group order is fixed:** `user`, then `feedback`,132 then `project`, then `reference`, then any other types alphabetically. Within a group, sort133 by title ascending. One blank line between groups, none between entries.134 **Hard targets: < 200 lines AND < 25 KB** (only the first 200 lines / 25 KB load at session135 start — anything beyond is dead weight). If you exceed it, the memory set itself is too136 large → flag it, don't pad.1373. **Apply archives** decided in Phase 3 into `memory/_archive/`. Each tombstone keeps its138 frontmatter and appends: `archived: <date>`, `reason: stale|contradicted|redundant|rejected`,139 and `superseded_by:` if merged.1404. **Archive bounding** (evaluated on this run, since there is no background timer):141 - `contradicted | rejected` tombstones: keep permanently, but strip the body (header-only)142 once older than 90 days.143 - `stale | redundant` tombstones: remove once older than 180 days.1445. **No-op guard.** If consolidation produced no changes, do **not** rewrite files or re-stamp145 anything — leave the dir byte-identical (this is what makes a second run idempotent).1466. **Concurrency re-check, then batch-write.** Re-stat the files snapshotted in Phase 1147 **once, immediately before the first write**. If any mtime changed, **abort before writing**148 and tell the user another session modified memory mid-run. If clean, perform all writes in149 one batch without re-stating between them.1507. Update `memory/_memrem/state.json` with both the new `last_consolidated` (ISO date) and151 `last_consolidated_epoch` (epoch seconds). This marker lives in the sidecar — never in152 `MEMORY.md` (rebuilt) or a memory file (Claude may clobber it).153154---155156## Output157158After applying (or, in `--confirm` mode, as the PLAN), report a concise summary:159160- **Merged:** N duplicate sets161- **Contradictions resolved:** N (old → archived, new kept)162- **Archived:** N (redundant/stale) — with reasons163- **Index:** MEMORY.md rebuilt → X lines / Y KB164- **Scrubbed:** N secrets/PHI candidates dropped on ingest (count only — never echo the content)165- **Idempotent:** "no changes" if it was a no-op run166167In `--confirm` / `--dry-run` mode, end with: "No changes written. Reply to approve and I'll168apply this plan." and wait.169170## Edge cases171172- **Empty / first run:** nothing to consolidate; create minimal `MEMORY.md` + sidecar; report.173- **Concurrent write detected:** abort before writing, report which file changed.174- **Memory dir not found:** ask the user to confirm the path; do not guess or create it175 somewhere arbitrary.176- **Everything already clean:** no-op; say so; do not churn files.