@lh8ppl/claude-memory-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/bin/cmk-compress-lazy.mjs +59 -0
  2. package/bin/cmk-daily-distill.mjs +67 -0
  3. package/bin/cmk-weekly-curate.mjs +56 -0
  4. package/bin/cmk.mjs +12 -0
  5. package/package.json +50 -0
  6. package/src/audit-log.mjs +103 -0
  7. package/src/auto-extract.mjs +742 -0
  8. package/src/capture-prompt.mjs +61 -0
  9. package/src/capture-turn.mjs +273 -0
  10. package/src/claude-md.mjs +212 -0
  11. package/src/compress-session.mjs +349 -0
  12. package/src/compressor.mjs +376 -0
  13. package/src/conflict-queue.mjs +796 -0
  14. package/src/cooldown.mjs +61 -0
  15. package/src/daily-distill.mjs +252 -0
  16. package/src/doctor.mjs +528 -0
  17. package/src/forget.mjs +335 -0
  18. package/src/frontmatter.mjs +73 -0
  19. package/src/import-anthropic-memory.mjs +266 -0
  20. package/src/index-db.mjs +154 -0
  21. package/src/index-rebuild.mjs +597 -0
  22. package/src/index.mjs +90 -0
  23. package/src/inject-context.mjs +484 -0
  24. package/src/install.mjs +327 -0
  25. package/src/lazy-compress.mjs +326 -0
  26. package/src/lock-discipline.mjs +166 -0
  27. package/src/mcp-server.mjs +498 -0
  28. package/src/memory-write.mjs +565 -0
  29. package/src/merge-facts.mjs +213 -0
  30. package/src/observe-edit.mjs +87 -0
  31. package/src/platform-commands.mjs +138 -0
  32. package/src/poison-guard.mjs +245 -0
  33. package/src/privacy.mjs +21 -0
  34. package/src/provenance.mjs +217 -0
  35. package/src/register-crons.mjs +354 -0
  36. package/src/reindex.mjs +134 -0
  37. package/src/repair.mjs +316 -0
  38. package/src/result-shapes.mjs +155 -0
  39. package/src/review-queue.mjs +345 -0
  40. package/src/roll.mjs +115 -0
  41. package/src/scratchpad.mjs +335 -0
  42. package/src/search.mjs +311 -0
  43. package/src/subcommands.mjs +1252 -0
  44. package/src/tier-paths.mjs +74 -0
  45. package/src/transcripts.mjs +234 -0
  46. package/src/trust.mjs +226 -0
  47. package/src/weekly-curate.mjs +454 -0
  48. package/src/write-fact.mjs +205 -0
  49. package/template/.claude/hooks/pre-tool-memory.js +78 -0
  50. package/template/.claude/hooks/transcript-capture.js +69 -0
  51. package/template/.claude/settings.json +27 -0
  52. package/template/.claude/skills/memory-write/SKILL.md +117 -0
  53. package/template/.gitignore.fragment +12 -0
  54. package/template/CLAUDE.md.template +49 -0
  55. package/template/docs/journey/journey-log.md.template +292 -0
  56. package/template/local/machine-paths.md.template +37 -0
  57. package/template/local/overrides.md.template +36 -0
  58. package/template/project/.index/.gitkeep +0 -0
  59. package/template/project/MEMORY.md.template +47 -0
  60. package/template/project/SOUL.md.template +35 -0
  61. package/template/project/memory/INDEX.md.template +47 -0
  62. package/template/project/memory/archive/superseded/.gitkeep +0 -0
  63. package/template/project/memory/archive/tombstones/.gitkeep +0 -0
  64. package/template/project/queues/.gitkeep +0 -0
  65. package/template/project/sessions/.gitkeep +0 -0
  66. package/template/project/transcripts/.gitkeep +0 -0
  67. package/template/support/cron-jobs/daily-memory-distill.md +15 -0
  68. package/template/support/cron-jobs/nightly-memsearch-index.md +17 -0
  69. package/template/support/cron-jobs/weekly-memory-curator.md +15 -0
  70. package/template/support/milvus-deploy/README.md +57 -0
  71. package/template/support/milvus-deploy/docker-compose.yml +66 -0
  72. package/template/support/scripts/auto-extract-memory.sh +102 -0
  73. package/template/support/scripts/memsearch-index-with-flush.sh +59 -0
  74. package/template/support/scripts/refresh-distill-timestamp.py +35 -0
  75. package/template/support/scripts/register-crons.py +242 -0
  76. package/template/support/scripts/run-daily-distill.sh +67 -0
  77. package/template/support/scripts/run-weekly-curate.sh +58 -0
  78. package/template/user/HABITS.md.template +18 -0
  79. package/template/user/LESSONS.md.template +18 -0
  80. package/template/user/USER.md.template +18 -0
  81. package/template/user/fragments/INDEX.md.template +23 -0
@@ -0,0 +1,36 @@
1
+ <!-- Cap: 1500 chars · Last distilled: {{TODAY}} · Last health check: {{TODAY}} -->
2
+
3
+ <!--
4
+ overrides.md = machine-specific overrides of preferences declared elsewhere.
5
+ Local tier (gitignored). Highest precedence in the 3-tier model.
6
+ 3 fixed sections per design §2.1.
7
+
8
+ Bullet+provenance format (universal — see provenance.mjs):
9
+ - (L-XXXXXXXX) the bullet text on one line
10
+ <!-- source, source_line, sha1, write, trust, at -->
11
+
12
+ Auto-populated by `cmk persona generate` (Task N, design §16.16); empty is fine.
13
+ -->
14
+
15
+ # Machine-specific overrides (local tier)
16
+
17
+ ## Tool Overrides
18
+
19
+ <!-- Editor/shell/tool overrides for this machine. -->
20
+
21
+ - (L-AUT6CX47) (example) editor: code --wait
22
+ <!-- source: overrides.md, source_line: 20, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
23
+
24
+ ## Behavior Overrides
25
+
26
+ <!-- Behavior changes for this machine (offline mode, rate limits, etc.). -->
27
+
28
+ - (L-2QZJRZLT) (example) offline mode: true
29
+ <!-- source: overrides.md, source_line: 27, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
30
+
31
+ ## Path Overrides
32
+
33
+ <!-- Path overrides for this machine. -->
34
+
35
+ - (L-YRDULPLW) (example) workspace root: ~/Projects
36
+ <!-- source: overrides.md, source_line: 34, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
File without changes
@@ -0,0 +1,47 @@
1
+ <!-- Cap: 2500 chars · Last distilled: {{TODAY}} · Last health check: {{TODAY}} -->
2
+
3
+ <!--
4
+ MEMORY.md is the working scratchpad. Cap measured by `wc -c` over the whole
5
+ file (header + comments + bullets). Consolidation triggers at >95% (Task 12);
6
+ stale bullets (>14d without `trust: high`) drop on consolidate. Three fixed
7
+ sections per design §2.1.
8
+
9
+ Bullet+provenance format (universal across all scratchpads):
10
+ - (P-XXXXXXXX) the bullet text on one line
11
+ <!-- source: <file>, source_line: <int>, sha1: <40-hex>, write: <enum>, trust: <enum>, at: <ISO 8601> -->
12
+
13
+ Each section ships with a placeholder seed bullet — replace with real project
14
+ state. Empty sections are fine if you haven't captured anything yet.
15
+ -->
16
+
17
+ # Working Memory
18
+
19
+ ## Active Threads
20
+
21
+ <!--
22
+ Current work in progress. Drop bullets when work resolves. Auto-extract
23
+ (Task 23) writes here; manual edits welcome too.
24
+ -->
25
+
26
+ - (P-T6M95JXF) (example) reviewing PR #142 for the auth refactor
27
+ <!-- source: MEMORY.md, source_line: 22, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
28
+
29
+ ## Environment Notes
30
+
31
+ <!--
32
+ Tool versions, paths, URLs, env state. Update on change. Auto-extract picks
33
+ these up from PostToolUse hook events; manual edits welcome.
34
+ -->
35
+
36
+ - (P-R662a95Y) (example) Node 20.x; Python 3.13; Postgres 16 in the test environment
37
+ <!-- source: MEMORY.md, source_line: 30, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
38
+
39
+ ## Pending Decisions
40
+
41
+ <!--
42
+ Things the user still has to decide. Remove when resolved. Auto-populated by
43
+ `cmk persona generate` (Task N, design §16.16); empty is fine.
44
+ -->
45
+
46
+ - (P-KU3aNBX9) (example) decide whether to deprecate /api/v1 by Q3 2026
47
+ <!-- source: MEMORY.md, source_line: 38, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
@@ -0,0 +1,35 @@
1
+ <!-- Cap: 1800 chars · Last distilled: {{TODAY}} · Last health check: {{TODAY}} -->
2
+
3
+ <!--
4
+ SOUL.md = project persona / disposition / norms. Where USER.md = "who the user
5
+ is", SOUL.md = "how Claude should show up". 3 fixed sections per design §2.1.
6
+
7
+ Bullet+provenance format (universal — see provenance.mjs):
8
+ - (P-XXXXXXXX) the bullet text on one line
9
+ <!-- source, source_line, sha1, write, trust, at -->
10
+
11
+ Auto-populated by `cmk persona generate` (Task N, design §16.16); empty is fine.
12
+ -->
13
+
14
+ # Project Soul
15
+
16
+ ## Tone and Disposition
17
+
18
+ <!-- How Claude communicates in this project. -->
19
+
20
+ - (P-9Ba72DB2) explains tradeoffs before recommending; doesn't lead with conclusions
21
+ <!-- source: SOUL.md, source_line: 18, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
22
+
23
+ ## Operating Defaults
24
+
25
+ <!-- Standing rules for how Claude works. -->
26
+
27
+ - (P-PCD975WA) verifies external claims against primary sources before stating as fact
28
+ <!-- source: SOUL.md, source_line: 25, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
29
+
30
+ ## Boundary Rules
31
+
32
+ <!-- What Claude must NOT do. -->
33
+
34
+ - (P-U5ELYUZQ) writes to memory silently; doesn't announce captures
35
+ <!-- source: SOUL.md, source_line: 32, sha1: 0000000000000000000000000000000000000000, write: manual-edit, trust: medium, at: 2020-01-01T00:00:00Z -->
@@ -0,0 +1,47 @@
1
+ # Granular Memory Archive — Index
2
+
3
+ This file lists every per-fact memory file in `context/memory/`. **NOT auto-loaded at session start** — read this index when looking up whether a topic has a saved memory.
4
+
5
+ ## Purpose vs. the bounded scratchpad
6
+
7
+ | File | Purpose | Loaded at startup? | Cap |
8
+ |---|---|---|---|
9
+ | `../MEMORY.md` | Hot working state (current threads, environment, pending decisions) | Yes | 2,500 chars |
10
+ | `../USER.md` | User profile and preferences | Yes | 1,375 chars |
11
+ | `./<type>_<slug>.md` (this dir) | Durable typed facts with reasoning | No | none |
12
+
13
+ ## When to write where
14
+
15
+ - **Durable typed fact** (user role, project decision with rationale, feedback rule, external system pointer) → new file here, plus one-line entry below.
16
+ - **Working state** (current threads, today's environment notes, open decisions) → `../MEMORY.md`.
17
+ - If a fact graduates from working state to durable archive, MOVE it (don't duplicate).
18
+
19
+ ## Frontmatter format for granular files
20
+
21
+ ````markdown
22
+ ---
23
+ name: short-kebab-case-slug
24
+ description: one-line summary used for retrieval
25
+ metadata:
26
+ type: user | feedback | project | reference
27
+ ---
28
+
29
+ [Body. For feedback / project entries, lead with the rule or fact, then:
30
+ **Why:** the reason / context / past incident
31
+ **How to apply:** when this guidance kicks in, edge cases to watch]
32
+
33
+ Link related entries with [[their-slug]].
34
+ ````
35
+
36
+ ## Type taxonomy
37
+
38
+ - `user_*.md` — facts about the user (role, goals, knowledge, responsibilities).
39
+ - `feedback_*.md` — corrections or confirmations about how to approach work. Always include Why + How to apply.
40
+ - `project_*.md` — facts about ongoing initiatives or decisions specific to a project. Time-bound.
41
+ - `reference_*.md` — pointers to external systems (Linear, Slack, dashboards, repos).
42
+
43
+ ## Files
44
+
45
+ <!-- (No granular files yet. As you accumulate, add one line per file:
46
+ - [type] [Title](filename.md) — short hook
47
+ -->
File without changes
File without changes
File without changes
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: Daily Memory Distillation
3
+ time: '23:00'
4
+ days: daily
5
+ active: 'true'
6
+ description: 'Distills durable facts from today''s session log into MEMORY.md and updates Last-distilled timestamp'
7
+ timeout: 10m
8
+ job_type: shell_command
9
+ command: 'bash scripts/run-daily-distill.sh'
10
+ working_directory: '${CLAUDE_PROJECT_DIR}'
11
+ ---
12
+
13
+ Invokes Claude headlessly to extract durable facts from `context/sessions/{today}.md` into `context/MEMORY.md`. Promotes typed facts to `context/memory/<type>_*.md` granular files. Enforces the 2,500-char MEMORY.md cap by consolidating older entries if needed. Updates the `Last distilled` timestamp so HC-3 stays green.
14
+
15
+ See `scripts/run-daily-distill.sh` for the exact prompt and tool allowlist.
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: Nightly MemSearch Index
3
+ time: '02:00'
4
+ days: daily
5
+ active: 'true'
6
+ description: 'Re-indexes context/ markdown files for vector search'
7
+ timeout: 10m
8
+ job_type: shell_command
9
+ command: 'bash scripts/memsearch-index-with-flush.sh context/memory context/sessions context/transcripts'
10
+ working_directory: '${CLAUDE_PROJECT_DIR}'
11
+ # Requires Layer 5 installed (memsearch on PATH + a reachable Milvus backend).
12
+ # On Windows: Docker Desktop running with Milvus container — see context/SETUP.md.
13
+ # If memsearch isn't installed or backend isn't reachable, the task will fail;
14
+ # HC-7 (backend reachability) will flag it on the next session start.
15
+ ---
16
+
17
+ Updates the memsearch vector index with any new content from `context/memory/`, `context/sessions/`, and `context/transcripts/`. Idempotent: only re-embeds chunks whose hash changed since the last run.
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: Weekly Memory Curator
3
+ time: '09:00'
4
+ days: sun
5
+ active: 'true'
6
+ description: 'Prunes, merges, and consolidates entries in MEMORY.md; updates Last-health-check timestamp'
7
+ timeout: 15m
8
+ job_type: shell_command
9
+ command: 'bash scripts/run-weekly-curate.sh'
10
+ working_directory: '${CLAUDE_PROJECT_DIR}'
11
+ ---
12
+
13
+ Conservative cleanup pass on `context/MEMORY.md`. Removes resolved Active Threads, merges duplicate bullets, drops clearly stale entries — but never ADDS content (that's the daily-distill's job). Logs results to today's session log under a "## Session — automated curation" heading.
14
+
15
+ See `scripts/run-weekly-curate.sh` for the exact prompt and tool allowlist.
@@ -0,0 +1,57 @@
1
+ # Milvus deployment (Windows + optional)
2
+
3
+ This compose stack runs Milvus v2.6.16 plus its required dependencies (etcd, MinIO) as three local containers. It's used by **Layer 5** (memsearch vector search) of the memory system.
4
+
5
+ ## When you need this
6
+
7
+ - **Windows**: required. `milvus-lite` (the default embedded vector store memsearch ships) has no Windows wheels on PyPI. Use this Docker stack instead.
8
+ - **Linux / macOS**: optional. memsearch auto-installs milvus-lite and uses it at `~/.memsearch/milvus.db`. Skip this directory entirely unless you specifically want a remote Milvus.
9
+
10
+ ## Bring it up
11
+
12
+ ```bash
13
+ cd milvus-deploy
14
+ docker compose up -d
15
+ ```
16
+
17
+ Wait ~30-60 seconds for all three containers to report `(healthy)`:
18
+
19
+ ```bash
20
+ docker compose ps
21
+ ```
22
+
23
+ You should see `milvus-etcd`, `milvus-minio`, `milvus-standalone` all `Up (healthy)`.
24
+
25
+ ## Configure memsearch to use it
26
+
27
+ ```bash
28
+ memsearch config set milvus.uri "http://localhost:19530"
29
+ ```
30
+
31
+ ## Bring it down
32
+
33
+ ```bash
34
+ docker compose down
35
+ ```
36
+
37
+ Volumes persist in `./volumes/` — re-running `up -d` reuses the same data.
38
+
39
+ ## Why a multi-container stack and not just `milvus-standalone`?
40
+
41
+ Milvus standalone needs etcd (metadata store) and MinIO (object storage for segments and index data). On Linux/macOS, `milvus-lite` bundles all of that into one Python wheel; on Windows there's no equivalent wheel, so the three services run as separate containers.
42
+
43
+ The single-container `docker run milvusdb/milvus:latest standalone` pattern referenced in some older docs is no longer supported — `latest` is `v3.0-beta` whose entrypoint doesn't accept `standalone` as a command. Use this compose file with the pinned versions instead.
44
+
45
+ ## Known quirk: memsearch index without flush
46
+
47
+ memsearch v0.4.x doesn't call `flush()` after `MilvusStore.upsert()`. On Milvus v2.6+ (Woodpecker WAL), this means `memsearch index` reports success but the data isn't searchable until a flush forces the growing segment to seal.
48
+
49
+ Use `scripts/memsearch-index-with-flush.sh` instead of raw `memsearch index` until upstream ships a fix. Tracked as [memsearch issue #534](https://github.com/zilliztech/memsearch/issues/534).
50
+
51
+ ## Pinned versions
52
+
53
+ | Image | Version | Why pinned |
54
+ |---|---|---|
55
+ | `milvusdb/milvus` | `v2.6.16` | Latest stable v2.6 line. `latest` tag is v3.0-beta and crashes. |
56
+ | `quay.io/coreos/etcd` | `v3.5.25` | Compatible with milvus v2.6. |
57
+ | `minio/minio` | `RELEASE.2024-12-18T13-15-44Z` | Recent stable RELEASE tag. |
@@ -0,0 +1,66 @@
1
+ version: '3.5'
2
+
3
+ services:
4
+ etcd:
5
+ container_name: milvus-etcd
6
+ image: quay.io/coreos/etcd:v3.5.25
7
+ environment:
8
+ - ETCD_AUTO_COMPACTION_MODE=revision
9
+ - ETCD_AUTO_COMPACTION_RETENTION=1000
10
+ - ETCD_QUOTA_BACKEND_BYTES=4294967296
11
+ - ETCD_SNAPSHOT_COUNT=50000
12
+ volumes:
13
+ - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
14
+ command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
15
+ healthcheck:
16
+ test: ["CMD", "etcdctl", "endpoint", "health"]
17
+ interval: 30s
18
+ timeout: 20s
19
+ retries: 3
20
+
21
+ minio:
22
+ container_name: milvus-minio
23
+ image: minio/minio:RELEASE.2024-12-18T13-15-44Z
24
+ environment:
25
+ MINIO_ACCESS_KEY: minioadmin
26
+ MINIO_SECRET_KEY: minioadmin
27
+ ports:
28
+ - "9001:9001"
29
+ - "9000:9000"
30
+ volumes:
31
+ - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
32
+ command: minio server /minio_data --console-address ":9001"
33
+ healthcheck:
34
+ test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
35
+ interval: 30s
36
+ timeout: 20s
37
+ retries: 3
38
+
39
+ standalone:
40
+ container_name: milvus-standalone
41
+ image: milvusdb/milvus:v2.6.16
42
+ command: ["milvus", "run", "standalone"]
43
+ security_opt:
44
+ - seccomp:unconfined
45
+ environment:
46
+ ETCD_ENDPOINTS: etcd:2379
47
+ MINIO_ADDRESS: minio:9000
48
+ MQ_TYPE: woodpecker
49
+ volumes:
50
+ - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
51
+ healthcheck:
52
+ test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
53
+ interval: 30s
54
+ start_period: 90s
55
+ timeout: 20s
56
+ retries: 3
57
+ ports:
58
+ - "19530:19530"
59
+ - "9091:9091"
60
+ depends_on:
61
+ - "etcd"
62
+ - "minio"
63
+
64
+ networks:
65
+ default:
66
+ name: milvus
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Auto-extract: invoked by the Stop hook in the background after every
4
+ # assistant turn. Reads the turn from a temp file, runs Claude headlessly
5
+ # with a focused fact-extraction prompt, and lets Claude write any
6
+ # durable facts to MEMORY.md / USER.md / granular archive via the
7
+ # memory-write skill.
8
+ #
9
+ # This is the "make it automatic" piece: instead of the user having to
10
+ # say "remember this", the system harvests memory-worthy content from
11
+ # every turn on its own.
12
+ #
13
+ # Args:
14
+ # $1 — path to a temp file containing the assistant turn's text
15
+ #
16
+ # Detached from the parent hook — runs in background, fire-and-forget.
17
+ # Errors are swallowed (logged to context/sessions/{today}.extract.log).
18
+
19
+ # Detached process contexts don't always inherit Git Bash's PATH on
20
+ # Windows. Set it up explicitly. On Linux/macOS this is a no-op since
21
+ # /usr/bin is already present.
22
+ case ":$PATH:" in
23
+ *":/usr/bin:"*) ;;
24
+ *) export PATH="/usr/bin:/c/Program Files/Git/usr/bin:$PATH" ;;
25
+ esac
26
+
27
+ set -u
28
+
29
+ TURN_FILE="${1:-}"
30
+ [ -z "$TURN_FILE" ] && exit 0
31
+ [ ! -f "$TURN_FILE" ] && exit 0
32
+
33
+ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
34
+ cd "$REPO_ROOT"
35
+
36
+ TODAY=$(date +%Y-%m-%d)
37
+ LOG="${REPO_ROOT}/context/sessions/${TODAY}.extract.log"
38
+
39
+ TURN_TEXT=$(cat "$TURN_FILE")
40
+
41
+ trap "rm -f \"$TURN_FILE\"" EXIT
42
+
43
+ # Skip very short turns — nothing to extract.
44
+ TURN_LEN=${#TURN_TEXT}
45
+ if [ "$TURN_LEN" -lt 100 ]; then
46
+ echo "[$(date -Iseconds)] skip: turn too short ($TURN_LEN chars)" >> "$LOG"
47
+ exit 0
48
+ fi
49
+
50
+ PROMPT=$(cat <<EOF
51
+ You are running as a silent background auto-extract task for this project. No one is watching this output. Be CONSERVATIVE — only capture what is clearly durable. Most turns will have nothing to save. That is the correct outcome.
52
+
53
+ GOAL: look at the assistant turn below and decide whether the user (in the conversation that produced it) said anything that should be saved to memory. If yes, write it to context/MEMORY.md, context/USER.md, or context/memory/<type>_<slug>.md using the memory-write skill.
54
+
55
+ EXTRACT and SAVE only when one of these is clearly present:
56
+ - User explicitly asked to remember ("remember this", "note that", "save this", "from now on", "going forward", "i prefer")
57
+ - User made a concrete decision worth carrying forward ("we're using X not Y", "let's go with X")
58
+ - User corrected the assistant ("actually it's X not Y", "you got that wrong, X is the right answer")
59
+ - Assistant acknowledged a NEW environment fact not already in MEMORY.md / USER.md (new tool version, new path, new config)
60
+ - Assistant identified a durable rule with "Why:" / "How to apply:" structure
61
+
62
+ DO NOT save:
63
+ - Conversational chatter, hello/goodbye
64
+ - One-off task execution narration ("ran the script, got output X")
65
+ - Information already in MEMORY.md / USER.md / context/memory/ (check INDEX.md)
66
+ - Anything you would summarize as "we discussed X" without a concrete decision
67
+
68
+ STEPS:
69
+ 1. Read context/MEMORY.md, context/USER.md, and context/memory/INDEX.md to know current state.
70
+ 2. Read the turn content (passed as the user message of this conversation).
71
+ 3. If anything durable is present, use the memory-write skill rules:
72
+ - Choose the right file (scratchpad vs USER.md vs granular archive)
73
+ - Dedup-check against existing content
74
+ - Cap-check (consolidate first if over)
75
+ - Write a single bullet, concise (<200 chars)
76
+ 4. If nothing durable: exit silently. Do not write.
77
+ 5. Output ONE line of plain text: either "saved: <one-line summary of what>" or "skip: nothing durable" — for the log.
78
+
79
+ CONSTRAINTS:
80
+ - Use Read, Edit, and Bash(wc *) only.
81
+ - Do NOT create new files outside context/memory/.
82
+ - Do NOT commit, push, or run git operations.
83
+ - Do NOT print anything except the one-line outcome.
84
+
85
+ === TURN CONTENT ===
86
+ ${TURN_TEXT}
87
+ === END TURN ===
88
+ EOF
89
+ )
90
+
91
+ mkdir -p "$(dirname "$LOG")"
92
+ echo "[$(date -Iseconds)] auto-extract fired on turn len=$TURN_LEN" >> "$LOG"
93
+
94
+ OUTPUT=$(echo "$PROMPT" | claude --print \
95
+ --add-dir "$REPO_ROOT" \
96
+ --allowed-tools "Read" "Edit" "Bash(wc *)" \
97
+ --output-format text \
98
+ 2>&1)
99
+ EXIT=$?
100
+
101
+ echo "[$(date -Iseconds)] auto-extract exit=$EXIT output: $OUTPUT" >> "$LOG"
102
+ exit 0
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Wraps `memsearch index` to also force a Milvus flush afterward.
4
+ #
5
+ # Why: Milvus v2.6+ uses the Woodpecker WAL backend, which (unlike v2.5's
6
+ # Pulsar) does not auto-flush growing segments on a short timer. As a result,
7
+ # `memsearch index` reports "Indexed N chunks" successfully but
8
+ # `get_collection_stats` returns 0 rows and search returns no results until
9
+ # a manual flush forces the growing segment to seal.
10
+ #
11
+ # This wrapper runs the index, then issues a flush via pymilvus.
12
+ #
13
+ # Usage:
14
+ # bash scripts/memsearch-index-with-flush.sh context/memory context/sessions context/transcripts
15
+ #
16
+ # Reads MILVUS_URI from env (falls back to the memsearch config value, then localhost).
17
+ # Reads MILVUS_COLLECTION from env (falls back to memsearch config, then "memsearch_chunks").
18
+
19
+ # Task Scheduler / launchd / unattended cron contexts don't always inherit
20
+ # the user's PATH. Set it up explicitly for the common locations.
21
+ case ":$PATH:" in
22
+ *":/usr/bin:"*) ;;
23
+ *) export PATH="/usr/bin:/usr/local/bin:/opt/homebrew/bin:/c/Program Files/Git/usr/bin:$PATH" ;;
24
+ esac
25
+
26
+ # On Windows, also add the Python Scripts dir where `memsearch.exe` lands.
27
+ if [ -d "/c/Users/$USERNAME/AppData/Local/Programs/Python" ]; then
28
+ for d in /c/Users/$USERNAME/AppData/Local/Programs/Python/Python*/Scripts \
29
+ /c/Users/$USERNAME/AppData/Local/Programs/Python/Python*; do
30
+ [ -d "$d" ] && export PATH="$d:$PATH"
31
+ done
32
+ fi
33
+
34
+ set -euo pipefail
35
+
36
+ # Step 1 — index. Pass through all args.
37
+ memsearch index "$@"
38
+
39
+ # Step 2 — flush.
40
+ MILVUS_URI="${MILVUS_URI:-$(memsearch config get milvus.uri 2>/dev/null | tail -n1 | tr -d '\r')}"
41
+ MILVUS_COLLECTION="${MILVUS_COLLECTION:-$(memsearch config get milvus.collection 2>/dev/null | tail -n1 | tr -d '\r')}"
42
+ MILVUS_URI="${MILVUS_URI:-http://localhost:19530}"
43
+ MILVUS_COLLECTION="${MILVUS_COLLECTION:-memsearch_chunks}"
44
+
45
+ # Flush only if we're using a remote Milvus (milvus-lite auto-flushes).
46
+ case "$MILVUS_URI" in
47
+ http://*|https://*|tcp://*)
48
+ python -c "
49
+ from pymilvus import MilvusClient
50
+ client = MilvusClient(uri='${MILVUS_URI}')
51
+ client.flush('${MILVUS_COLLECTION}')
52
+ stats = client.get_collection_stats('${MILVUS_COLLECTION}')
53
+ print(f'Flushed. Collection {stats!r}')
54
+ "
55
+ ;;
56
+ *)
57
+ echo "Local milvus-lite detected (${MILVUS_URI}); skipping flush (auto-flush)."
58
+ ;;
59
+ esac
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tiny helper for the daily-memory-distill cron job.
4
+
5
+ Rewrites the `<!-- Last distilled: YYYY-MM-DD -->` line in
6
+ context/MEMORY.md to today's date. Keeps HC-3 green even when the real
7
+ distillation (Claude-driven extraction of facts from sessions/) isn't
8
+ wired up yet.
9
+
10
+ Usage:
11
+ python scripts/refresh-distill-timestamp.py
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import datetime
16
+ import re
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ REPO_ROOT = Path(__file__).resolve().parent.parent
21
+ MEMORY = REPO_ROOT / "context" / "MEMORY.md"
22
+
23
+ if not MEMORY.exists():
24
+ print(f"ERROR: {MEMORY} not found", file=sys.stderr)
25
+ sys.exit(1)
26
+
27
+ today = datetime.date.today().isoformat()
28
+ text = MEMORY.read_text(encoding="utf-8")
29
+ new_text, n = re.subn(r"Last distilled: \d{4}-\d{2}-\d{2}",
30
+ f"Last distilled: {today}", text, count=1)
31
+ if n == 0:
32
+ print("WARN: no 'Last distilled:' line found in MEMORY.md; nothing to update", file=sys.stderr)
33
+ sys.exit(0)
34
+ MEMORY.write_text(new_text, encoding="utf-8")
35
+ print(f"OK: updated Last distilled to {today}")