get-claudia 1.55.21 → 1.57.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 (38) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/bin/index.js +213 -5
  3. package/bin/manifest-lib.js +245 -0
  4. package/memory-daemon/claudia_memory/daemon/health.py +1 -1
  5. package/memory-daemon/claudia_memory/daemon/scheduler.py +1 -1
  6. package/memory-daemon/claudia_memory/mcp/server.py +132 -123
  7. package/memory-daemon/claudia_memory/services/consolidate.py +1 -1
  8. package/memory-daemon/claudia_memory/services/remember.py +1 -1
  9. package/package.json +6 -2
  10. package/template-v2/.claude/hooks/__pycache__/post-tool-capture.cpython-313.pyc +0 -0
  11. package/template-v2/.claude/hooks/__pycache__/session-health-check.cpython-313.pyc +0 -0
  12. package/template-v2/.claude/hooks/__pycache__/user-prompt-capture.cpython-313.pyc +0 -0
  13. package/template-v2/.claude/hooks/hooks.json +11 -11
  14. package/template-v2/.claude/hooks/post-tool-capture.py +110 -10
  15. package/template-v2/.claude/hooks/pre-compact.py +4 -4
  16. package/template-v2/.claude/hooks/pre-compact.sh +1 -1
  17. package/template-v2/.claude/hooks/session-health-check.py +52 -4
  18. package/template-v2/.claude/hooks/session-summary.py +399 -0
  19. package/template-v2/.claude/hooks/user-prompt-capture.py +123 -0
  20. package/template-v2/.claude/manifest.json +73 -0
  21. package/template-v2/.claude/rules/claudia-principles.md +2 -2
  22. package/template-v2/.claude/rules/memory-availability.md +3 -3
  23. package/template-v2/.claude/rules/memory-commitment.md +92 -0
  24. package/template-v2/.claude/settings.local.json +26 -0
  25. package/template-v2/.claude/skills/capture-meeting/SKILL.md +6 -6
  26. package/template-v2/.claude/skills/capture-meeting/evals/basic.yaml +1 -1
  27. package/template-v2/.claude/skills/deep-context/SKILL.md +7 -7
  28. package/template-v2/.claude/skills/meditate/SKILL.md +10 -10
  29. package/template-v2/.claude/skills/meditate/evals/basic.yaml +1 -1
  30. package/template-v2/.claude/skills/meeting-prep/SKILL.md +3 -3
  31. package/template-v2/.claude/skills/memory-health/SKILL.md +1 -1
  32. package/template-v2/.claude/skills/memory-manager.md +85 -85
  33. package/template-v2/.claude/skills/morning-brief/SKILL.md +10 -10
  34. package/template-v2/.claude/skills/research/SKILL.md +2 -2
  35. package/template-v2/.claude/skills/skill-index.json +1 -1
  36. package/template-v2/CLAUDE.md +6 -6
  37. package/template-v2/.claude/hooks/__pycache__/pre-compact.cpython-313.pyc +0 -0
  38. package/template-v2/gitignore +0 -35
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env python3
2
+ """Generate a daily session summary markdown file from observations.jsonl.
3
+
4
+ Designed to run from a SessionEnd hook OR manually for retrospective summaries.
5
+
6
+ Output path: ~/.claudia/sessions/YYYY-MM-DD/NN-slug.md
7
+ Plus an INDEX.md per day, auto-regenerated.
8
+
9
+ Usage:
10
+ session-summary.py # uses CLAUDE_SESSION_ID env var or stdin JSON
11
+ session-summary.py <session_id> # explicit session id
12
+ session-summary.py --rebuild-index # regenerate today's INDEX.md only
13
+ """
14
+
15
+ import json
16
+ import os
17
+ import re
18
+ import sys
19
+ import time
20
+ from collections import Counter
21
+ from datetime import datetime, timezone
22
+ from pathlib import Path
23
+
24
+ OBS_FILE = Path.home() / ".claudia" / "observations.jsonl"
25
+ SESSIONS_DIR = Path.home() / ".claudia" / "sessions"
26
+
27
+ # Words to filter out when deriving topic slugs
28
+ STOPWORDS = {
29
+ "the", "a", "an", "and", "or", "but", "if", "of", "to", "for", "with",
30
+ "in", "on", "at", "by", "is", "are", "was", "were", "be", "been", "being",
31
+ "i", "you", "we", "us", "they", "this", "that", "these", "those",
32
+ "have", "has", "had", "do", "does", "did", "can", "could", "will",
33
+ "would", "should", "may", "might", "must", "let", "let's", "ok", "okay",
34
+ "yes", "no", "now", "go", "ahead", "please", "thanks", "today",
35
+ "use", "make", "get", "see", "look", "want",
36
+ }
37
+
38
+
39
+ def load_observations(session_id: str = None) -> list[dict]:
40
+ """Load observations from the JSONL file, optionally filtered by session_id."""
41
+ if not OBS_FILE.exists():
42
+ return []
43
+ obs = []
44
+ try:
45
+ with open(OBS_FILE, "r", encoding="utf-8") as f:
46
+ for line in f:
47
+ line = line.strip()
48
+ if not line:
49
+ continue
50
+ try:
51
+ o = json.loads(line)
52
+ if session_id and o.get("session_id") != session_id:
53
+ continue
54
+ obs.append(o)
55
+ except json.JSONDecodeError:
56
+ continue
57
+ except OSError:
58
+ return []
59
+ return obs
60
+
61
+
62
+ def first_user_prompt(transcript_path: str) -> str | None:
63
+ """Try to extract the first user prompt from a transcript file (JSONL of turns)."""
64
+ if not transcript_path or not Path(transcript_path).exists():
65
+ return None
66
+ try:
67
+ with open(transcript_path, "r", encoding="utf-8") as f:
68
+ for line in f:
69
+ try:
70
+ turn = json.loads(line)
71
+ except json.JSONDecodeError:
72
+ continue
73
+ role = turn.get("role") or turn.get("type")
74
+ if role == "user":
75
+ content = turn.get("content") or turn.get("text") or ""
76
+ if isinstance(content, list):
77
+ for block in content:
78
+ if isinstance(block, dict) and block.get("type") == "text":
79
+ return block.get("text", "")[:500]
80
+ elif isinstance(content, str):
81
+ return content[:500]
82
+ except OSError:
83
+ return None
84
+ return None
85
+
86
+
87
+ def derive_topic_slug(observations: list[dict], first_prompt: str | None) -> str:
88
+ """Derive a 2-4 word topic slug from observations and first prompt."""
89
+ text_pool = []
90
+
91
+ if first_prompt:
92
+ text_pool.append(first_prompt)
93
+
94
+ for o in observations:
95
+ if o.get("file_path"):
96
+ parts = Path(o["file_path"]).parts
97
+ if len(parts) >= 2:
98
+ text_pool.append(parts[-2])
99
+
100
+ text_blob = " ".join(text_pool).lower()
101
+ words = re.findall(r"[a-z]{3,}", text_blob)
102
+ words = [w for w in words if w not in STOPWORDS]
103
+
104
+ if not words:
105
+ return "session"
106
+
107
+ counter = Counter(words)
108
+ top_words = [w for w, _ in counter.most_common(4)]
109
+ slug = "-".join(top_words[:3]) if top_words else "session"
110
+ return slug[:60]
111
+
112
+
113
+ def session_window(observations: list[dict]) -> tuple[float, float]:
114
+ """Return (start_ts, end_ts) from observation timestamps."""
115
+ if not observations:
116
+ return (time.time(), time.time())
117
+ timestamps = [o.get("ts", 0) for o in observations if o.get("ts")]
118
+ if not timestamps:
119
+ return (time.time(), time.time())
120
+ return (min(timestamps), max(timestamps))
121
+
122
+
123
+ def files_touched(observations: list[dict]) -> list[str]:
124
+ """Return unique file paths touched in this session, in order of first appearance."""
125
+ seen = []
126
+ seen_set = set()
127
+ for o in observations:
128
+ fp = o.get("file_path")
129
+ if fp and fp not in seen_set:
130
+ seen.append(fp)
131
+ seen_set.add(fp)
132
+ return seen
133
+
134
+
135
+ def external_actions(observations: list[dict]) -> list[dict]:
136
+ """Return observations flagged with external_action."""
137
+ return [
138
+ {
139
+ "ts": o.get("ts"),
140
+ "tool": o.get("tool"),
141
+ "action": o.get("external_action"),
142
+ "input": o.get("input", "")[:200],
143
+ }
144
+ for o in observations if o.get("external_action")
145
+ ]
146
+
147
+
148
+ def memory_entries_in_window(start_ts: float, end_ts: float) -> list[dict]:
149
+ """Best-effort fetch of memory entries created during the session window.
150
+
151
+ Relies on the claudia-memory daemon's HTTP endpoint if available.
152
+ Returns empty list if not reachable. The daemon does not currently
153
+ expose a /recent_memories endpoint; this is a placeholder for a
154
+ future enhancement.
155
+ """
156
+ try:
157
+ import urllib.request
158
+ url = f"http://localhost:3848/recent_memories?since={start_ts}&until={end_ts}"
159
+ req = urllib.request.Request(url)
160
+ with urllib.request.urlopen(req, timeout=2) as resp:
161
+ return json.loads(resp.read().decode("utf-8")).get("memories", [])
162
+ except Exception:
163
+ return []
164
+
165
+
166
+ def next_session_number(date_dir: Path) -> int:
167
+ """Return the next sequential session number for the day."""
168
+ if not date_dir.exists():
169
+ return 1
170
+ existing = []
171
+ for f in date_dir.glob("[0-9][0-9]-*.md"):
172
+ match = re.match(r"^(\d{2})-", f.name)
173
+ if match:
174
+ existing.append(int(match.group(1)))
175
+ return (max(existing) + 1) if existing else 1
176
+
177
+
178
+ def render_summary(
179
+ session_id: str,
180
+ date_str: str,
181
+ session_num: int,
182
+ topic_slug: str,
183
+ start_ts: float,
184
+ end_ts: float,
185
+ files: list[str],
186
+ actions: list[dict],
187
+ memories: list[dict],
188
+ first_prompt: str | None,
189
+ transcript_path: str | None,
190
+ ) -> str:
191
+ """Render the session summary markdown."""
192
+ duration_min = max(1, round((end_ts - start_ts) / 60))
193
+ started = datetime.fromtimestamp(start_ts, tz=timezone.utc).astimezone()
194
+ ended = datetime.fromtimestamp(end_ts, tz=timezone.utc).astimezone()
195
+
196
+ lines = [
197
+ f"# Session {session_num:02d} — {topic_slug.replace('-', ' ').title()}",
198
+ "",
199
+ f"**Date:** {date_str}",
200
+ f"**Started:** {started.strftime('%H:%M %Z')}",
201
+ f"**Ended:** {ended.strftime('%H:%M %Z')}",
202
+ f"**Duration:** ~{duration_min} min",
203
+ f"**Session ID:** `{session_id}`",
204
+ "",
205
+ ]
206
+
207
+ if first_prompt:
208
+ lines += [
209
+ "## Opening prompt",
210
+ "",
211
+ "> " + first_prompt.strip().split("\n")[0][:300],
212
+ "",
213
+ ]
214
+
215
+ lines += ["## Files touched", ""]
216
+ if files:
217
+ for f in files:
218
+ lines.append(f"- `{f}`")
219
+ else:
220
+ lines.append("- (none)")
221
+ lines.append("")
222
+
223
+ lines += ["## External actions", ""]
224
+ if actions:
225
+ for a in actions:
226
+ lines.append(f"- **{a.get('action')}** via `{a.get('tool')}` — `{a.get('input')[:120]}`")
227
+ else:
228
+ lines.append("- (none)")
229
+ lines.append("")
230
+
231
+ lines += ["## Memory entries created", ""]
232
+ if memories:
233
+ for m in memories[:30]:
234
+ mid = m.get("id") or m.get("memory_id") or "?"
235
+ content = (m.get("content") or "")[:200]
236
+ lines.append(f"- `mem-{mid}` — {content}")
237
+ else:
238
+ lines.append("- (none captured — memory daemon may not expose recent_memories endpoint)")
239
+ lines.append("")
240
+
241
+ if transcript_path:
242
+ lines += [
243
+ "## Find this again",
244
+ "",
245
+ f"- Transcript: `{transcript_path}`",
246
+ f"- Memory query: `\"{topic_slug.replace('-', ' ')}\"`",
247
+ "",
248
+ ]
249
+
250
+ lines += [
251
+ "---",
252
+ f"*Auto-generated by session-summary.py at {datetime.now().astimezone().strftime('%Y-%m-%d %H:%M %Z')}*",
253
+ "",
254
+ ]
255
+
256
+ return "\n".join(lines)
257
+
258
+
259
+ def regenerate_index(date_dir: Path) -> None:
260
+ """Regenerate INDEX.md for a given day's folder."""
261
+ if not date_dir.exists():
262
+ return
263
+
264
+ sessions = []
265
+ for f in sorted(date_dir.glob("[0-9][0-9]-*.md")):
266
+ first_lines = f.read_text(encoding="utf-8").split("\n")[:6]
267
+ title = first_lines[0].lstrip("# ").strip() if first_lines else f.name
268
+ started = ""
269
+ for line in first_lines:
270
+ if line.startswith("**Started:**"):
271
+ started = line.replace("**Started:**", "").strip()
272
+ break
273
+ sessions.append((f.name, title, started))
274
+
275
+ date_str = date_dir.name
276
+ lines = [
277
+ f"# Sessions — {date_str}",
278
+ "",
279
+ f"*{len(sessions)} session(s) captured.*",
280
+ "",
281
+ "| # | Topic | Started | File |",
282
+ "|---|-------|---------|------|",
283
+ ]
284
+ for fname, title, started in sessions:
285
+ match = re.match(r"^(\d{2})-", fname)
286
+ num = match.group(1) if match else "??"
287
+ clean_title = title.split("—", 1)[1].strip() if "—" in title else title
288
+ lines.append(f"| {num} | {clean_title} | {started} | [`{fname}`](./{fname}) |")
289
+ lines += [
290
+ "",
291
+ f"*INDEX regenerated {datetime.now().astimezone().strftime('%Y-%m-%d %H:%M %Z')}*",
292
+ "",
293
+ ]
294
+ (date_dir / "INDEX.md").write_text("\n".join(lines), encoding="utf-8")
295
+
296
+
297
+ def main():
298
+ if len(sys.argv) > 1 and sys.argv[1] == "--rebuild-index":
299
+ date_str = sys.argv[2] if len(sys.argv) > 2 else datetime.now().astimezone().strftime("%Y-%m-%d")
300
+ regenerate_index(SESSIONS_DIR / date_str)
301
+ print(json.dumps({"action": "rebuild-index", "date": date_str}))
302
+ return
303
+
304
+ session_id = ""
305
+ transcript_path = ""
306
+
307
+ if len(sys.argv) > 1:
308
+ session_id = sys.argv[1]
309
+ else:
310
+ session_id = os.environ.get("CLAUDE_SESSION_ID", "")
311
+ # Try stdin (SessionEnd hook contract)
312
+ if not session_id and not sys.stdin.isatty():
313
+ try:
314
+ raw = sys.stdin.read()
315
+ if raw.strip():
316
+ payload = json.loads(raw)
317
+ session_id = payload.get("session_id", "")
318
+ transcript_path = payload.get("transcript_path", "")
319
+ except (json.JSONDecodeError, OSError):
320
+ pass
321
+
322
+ if not session_id:
323
+ print(json.dumps({"error": "no session_id provided"}))
324
+ return
325
+
326
+ observations = load_observations(session_id)
327
+ if not observations:
328
+ print(json.dumps({"warning": "no observations for session", "session_id": session_id}))
329
+ return
330
+
331
+ start_ts, end_ts = session_window(observations)
332
+ date_str = datetime.fromtimestamp(start_ts, tz=timezone.utc).astimezone().strftime("%Y-%m-%d")
333
+ date_dir = SESSIONS_DIR / date_str
334
+ date_dir.mkdir(parents=True, exist_ok=True)
335
+
336
+ first_prompt = first_user_prompt(transcript_path) if transcript_path else None
337
+ topic_slug = derive_topic_slug(observations, first_prompt)
338
+
339
+ # Check if a summary for this session already exists; overwrite with latest data
340
+ existing_file = None
341
+ session_num = next_session_number(date_dir)
342
+ for existing in date_dir.glob("[0-9][0-9]-*.md"):
343
+ try:
344
+ content = existing.read_text(encoding="utf-8")
345
+ if f"`{session_id}`" in content:
346
+ existing_file = existing
347
+ match = re.match(r"^(\d{2})-", existing.name)
348
+ if match:
349
+ session_num = int(match.group(1))
350
+ break
351
+ except OSError:
352
+ continue
353
+
354
+ files = files_touched(observations)
355
+ actions = external_actions(observations)
356
+ memories = memory_entries_in_window(start_ts, end_ts)
357
+
358
+ summary = render_summary(
359
+ session_id=session_id,
360
+ date_str=date_str,
361
+ session_num=session_num,
362
+ topic_slug=topic_slug,
363
+ start_ts=start_ts,
364
+ end_ts=end_ts,
365
+ files=files,
366
+ actions=actions,
367
+ memories=memories,
368
+ first_prompt=first_prompt,
369
+ transcript_path=transcript_path,
370
+ )
371
+
372
+ out_file = date_dir / f"{session_num:02d}-{topic_slug}.md"
373
+
374
+ # If existing file had a different topic slug, remove the stale name
375
+ if existing_file and existing_file != out_file:
376
+ try:
377
+ existing_file.unlink()
378
+ except OSError:
379
+ pass
380
+
381
+ out_file.write_text(summary, encoding="utf-8")
382
+ regenerate_index(date_dir)
383
+
384
+ print(json.dumps({
385
+ "ok": True,
386
+ "session_id": session_id,
387
+ "file": str(out_file),
388
+ "files_touched": len(files),
389
+ "external_actions": len(actions),
390
+ "memories_in_window": len(memories),
391
+ "updated": existing_file is not None,
392
+ }))
393
+
394
+
395
+ if __name__ == "__main__":
396
+ try:
397
+ main()
398
+ except Exception as e:
399
+ print(json.dumps({"error": str(e)[:200]}))
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env python3
2
+ """UserPromptSubmit hook: detect canonical-fact trigger phrases and destructive
3
+ verbs in user prompts, inject reminder context for the agent.
4
+
5
+ Reads hook payload from stdin (Claude Code hook contract).
6
+ Outputs JSON with additionalContext when triggers fire. Outputs nothing otherwise.
7
+
8
+ Designed to complete in <5ms (regex match + simple JSON output, no network).
9
+
10
+ Two trigger classes:
11
+ 1. Memory-commitment phrases ("lock this in", "remember this", "this is canonical")
12
+ -> Inject reminder for the agent to save the fact to memory immediately,
13
+ per the memory-commitment rule (which governs tool selection).
14
+ 2. Destructive operation patterns (rm -rf, drop table, force push, etc.)
15
+ -> Inject "verify before acting" reminder per Claudia's safety-first principle.
16
+
17
+ Both classes can fire on the same prompt; both messages are concatenated.
18
+ """
19
+
20
+ import json
21
+ import re
22
+ import sys
23
+
24
+ # Trigger phrases that signal the user is asserting a canonical fact.
25
+ # Word boundaries (\b) used to avoid matching inside other words.
26
+ COMMITMENT_TRIGGERS = [
27
+ r"\block this in\b",
28
+ r"\bremember this\b",
29
+ r"\bthis is canonical\b",
30
+ r"\bthis is locked\b",
31
+ r"\bsave this for later\b",
32
+ r"\bimportant to remember\b",
33
+ r"\bfor the record\b",
34
+ r"\bdon'?t forget\b",
35
+ ]
36
+
37
+ # Destructive operation patterns. Narrow matches to limit false positives.
38
+ # Conversation about deletion ("how do I undo a delete?") should NOT fire these.
39
+ DESTRUCTIVE_PATTERNS = [
40
+ r"\brm\s+-rf\b",
41
+ r"\bdrop\s+(table|database|schema)\b",
42
+ r"\bgit\s+push\s+(?:-+f\b|--force\b)",
43
+ r"\bgit\s+reset\s+--hard\b",
44
+ r"\btruncate\s+table\b",
45
+ r"\bDELETE\s+FROM\b", # SQL all-caps signals intent
46
+ ]
47
+
48
+ # Human-readable labels for each destructive pattern. Keep keys in lockstep
49
+ # with DESTRUCTIVE_PATTERNS so the model never sees raw regex syntax.
50
+ PATTERN_LABELS = {
51
+ r"\brm\s+-rf\b": "rm -rf (recursive delete)",
52
+ r"\bdrop\s+(table|database|schema)\b": "DROP TABLE/DATABASE/SCHEMA",
53
+ r"\bgit\s+push\s+(?:-+f\b|--force\b)": "git push --force",
54
+ r"\bgit\s+reset\s+--hard\b": "git reset --hard",
55
+ r"\btruncate\s+table\b": "TRUNCATE TABLE",
56
+ r"\bDELETE\s+FROM\b": "DELETE FROM (SQL)",
57
+ }
58
+
59
+ # Fail-fast at import time if a pattern is missing a label (or vice versa).
60
+ assert set(DESTRUCTIVE_PATTERNS) == set(PATTERN_LABELS), (
61
+ "Every DESTRUCTIVE_PATTERN must have a matching PATTERN_LABELS entry"
62
+ )
63
+
64
+
65
+ def detect(text: str, patterns: list) -> list:
66
+ """Return list of pattern strings that matched in the text."""
67
+ return [p for p in patterns if re.search(p, text, re.IGNORECASE)]
68
+
69
+
70
+ def main():
71
+ try:
72
+ raw = sys.stdin.read()
73
+ if not raw.strip():
74
+ return
75
+ payload = json.loads(raw)
76
+ except (json.JSONDecodeError, OSError):
77
+ return
78
+
79
+ # Claude Code passes the user's prompt under "prompt" (newer) or "text" (older).
80
+ prompt = payload.get("prompt") or payload.get("text") or ""
81
+ if not prompt:
82
+ return
83
+
84
+ commitment_hits = detect(prompt, COMMITMENT_TRIGGERS)
85
+ destructive_hits = detect(prompt, DESTRUCTIVE_PATTERNS)
86
+
87
+ if not commitment_hits and not destructive_hits:
88
+ return
89
+
90
+ sections = []
91
+
92
+ if commitment_hits:
93
+ sections.append(
94
+ "**Canonical-fact trigger detected.** Per the memory-commitment rule, "
95
+ "save the canonical fact to memory immediately. Do not batch to "
96
+ "/meditate. After saving, continue the conversation normally and "
97
+ "briefly surface that the fact has been recorded so the user knows "
98
+ "it is recoverable in future sessions."
99
+ )
100
+
101
+ if destructive_hits:
102
+ # Show up to three matched patterns for context, using human-readable
103
+ # labels so raw regex syntax never reaches the model.
104
+ labels_list = ", ".join(
105
+ f"`{PATTERN_LABELS[p]}`" for p in destructive_hits[:3]
106
+ )
107
+ sections.append(
108
+ f"**Destructive operation pattern detected** ({labels_list}). "
109
+ "Per the safety-first principle, verify with the user before executing: "
110
+ "show what will happen (recipients, content, irreversible effects), ask "
111
+ "for explicit confirmation, then proceed only on a clear yes. Silence or "
112
+ "ambiguity means do not proceed."
113
+ )
114
+
115
+ output = {"additionalContext": "\n\n".join(sections)}
116
+ print(json.dumps(output))
117
+
118
+
119
+ if __name__ == "__main__":
120
+ try:
121
+ main()
122
+ except Exception:
123
+ pass # Never block Claude
@@ -0,0 +1,73 @@
1
+ {
2
+ "version": "1.57.0",
3
+ "generated": "2026-05-13T09:56:45.377Z",
4
+ "algorithm": "sha256",
5
+ "files": {
6
+ ".claude/rules/claudia-principles.md": "b1c5e33aeb33857f3485231e5ed34a441c3cc4568b69e3341be770963418a240",
7
+ ".claude/rules/data-freshness.md": "052b3b8f3f489a54fff065b29e0ffc46bfad6da1c3a42386170034f298599233",
8
+ ".claude/rules/memory-availability.md": "48309e2683b267c0a17ebf019fb3ee1116150d811b1d93b4ddb52fed89ae1fe3",
9
+ ".claude/rules/memory-commitment.md": "49eee330b56c6ca0b5f1e01550931c4eef3dcb3249e3d0e2380de3e8dbfe31a8",
10
+ ".claude/rules/shell-compatibility.md": "565977bc04e269b3ce7d8a7963173df4f44bb9634692ea76abb1b64f6a67513e",
11
+ ".claude/rules/trust-north-star.md": "0188b17c26b791cf597ce975bb75d40f543aa3d6b84b7edb1309b78530b3d43f",
12
+ ".claude/skills/README.md": "f84c553ff5bc01f7a9a479b7358dbd16a60ee2ba11e01467fe1ed3d91e33e68e",
13
+ ".claude/skills/agent-dispatcher.md": "b48fc5283f0a88d1ebbc692b30b6c326fef012d4168dcf721a94e3c954b76b90",
14
+ ".claude/skills/archetypes/_base-structure.md": "c0d8df77c07aa48cd9ba9c5ba3eddb533fcea790c7f98440fa3d31405fd5c75d",
15
+ ".claude/skills/archetypes/consultant.md": "1e0ccbf89115f92a1fa0c86b48edcce22668c4bb828ba4268376fcd6b5c05680",
16
+ ".claude/skills/archetypes/creator.md": "c364b60963c9794923fe9353d843bfca2f3ff86ca48dcb4b5539bf4545babd7a",
17
+ ".claude/skills/archetypes/executive.md": "0d43c5c2c6315f5a4d6d36150778b55229cc922e0491a80af54824d87ac52e82",
18
+ ".claude/skills/archetypes/founder.md": "a5bf3f22439c7c5f554c13966899a4af4de80f90c8f6a25eb62be1c68de6d54f",
19
+ ".claude/skills/archetypes/solo.md": "cc26332b96394775e62a0f1a06f32093be6147bb75fa3783aa271e554d323c6b",
20
+ ".claude/skills/brain/SKILL.md": "710d0defa5725a758868391f22f8518c189a5068ff376403eda431585b546b56",
21
+ ".claude/skills/brain-monitor/SKILL.md": "86d73e43ea09acdaa44db08454abfd0701805fe3e297d3f568119c9cb5a1f0cf",
22
+ ".claude/skills/capability-suggester.md": "33e559078098a532f216fa3668e48c248d10d5a7a9647c38ef6defe0e4a37361",
23
+ ".claude/skills/capture-meeting/SKILL.md": "c193bfd237a0f456ef04202d6eac609b2dd54dc6caabbb75c9df6456b563ea36",
24
+ ".claude/skills/capture-meeting/evals/basic.yaml": "47c4eb1479aeda8067bc877cebfe8688c9b2bb3ce09297e42375cdf558f412e3",
25
+ ".claude/skills/client-health/SKILL.md": "fed639e6f2c4433cab6a6b4b59776985492dda16be3bd1e843c3e94175936655",
26
+ ".claude/skills/commitment-detector.md": "66f9e919349d1880eb12636cec6f583be1b3fbe3b28f6180ec732d3c284af3b6",
27
+ ".claude/skills/connector-discovery.md": "8cf1067e840c377e0d65aae04e41fb7858da614c548cbf973421700915ad0ab3",
28
+ ".claude/skills/databases/SKILL.md": "b2ce4ae30aafa2487dabd7befd067976f0092fcef31dea7601e1683ceca0ef50",
29
+ ".claude/skills/deep-context/SKILL.md": "2d21f93e33191c226f46909a66d21c64c84df2ac261082adef78cd68093d7b07",
30
+ ".claude/skills/diagnose/SKILL.md": "8bfbae6c19afc64df9cc2a7048ee62a737d41cad33e22ba099a59369814ed50f",
31
+ ".claude/skills/diagnose/evals/basic.yaml": "7fa4e3a255f14d7dc883f59f8fd1d796a62db92d62a543ef87b5dda92a1c0c0b",
32
+ ".claude/skills/diagnose/references/common-issues.md": "f80b13d4b70e656151a4cb9db8c1f042378d1f6cb9f5ba8c550741d6bf34917c",
33
+ ".claude/skills/draft-reply/SKILL.md": "9f8210af7515c06ec15a65fa3692124c5482bdf661d82373604eb7fb670d3b97",
34
+ ".claude/skills/feedback/SKILL.md": "a8b5a1e82eca177461d11c87839dbf933e358fc3214d5f1975dd2d4fff9f3be7",
35
+ ".claude/skills/file-document/SKILL.md": "eb74255204f00d85909b7a3e083e1e5f6495cf4cdfedd3234aa7fabcde03d0d1",
36
+ ".claude/skills/financial-snapshot/SKILL.md": "33ddabb7d60024eadd8e6772229d8b17db3c3fa02d1289c80330c74aa86ae00a",
37
+ ".claude/skills/fix-duplicates/SKILL.md": "bd3d6aae0bce09bc844ba93f76abb4a97ff0286c6a4da29cd369763864fd4a86",
38
+ ".claude/skills/follow-up-draft/SKILL.md": "020f4bfab0845fecafd79fb72bb3a8655f8c50d6aa22c26427b489ae7f40a503",
39
+ ".claude/skills/growth-check/SKILL.md": "71ab16c6535d759397d97b2454cd007c1f6a3e2cab1bd6415b40989e61d987fe",
40
+ ".claude/skills/hire-agent.md": "c07014addd14ea132fb4b366fbeeccea000af85b7827db616da475be5ebf57ce",
41
+ ".claude/skills/inbox-check/SKILL.md": "c6746bd16b4f135cd4f28ebf4ce8dfe0c27c2a9a1fd6bda5676179fe02ed8666",
42
+ ".claude/skills/ingest-sources/SKILL.md": "07bbe32f7922e40b782314f3e0b968e547b755ede687ed3ab903caa954da6b4c",
43
+ ".claude/skills/ingest-sources/references/extraction-patterns.md": "28d6dc604c4a11eacfe4523e705ad430ba8fe26632bafcccaef656e9618e5aea",
44
+ ".claude/skills/judgment-awareness.md": "7dfb4b457319e7f33f18c5295a64cce88aae26feef5afac8b93a00376aeb939c",
45
+ ".claude/skills/map-connections/SKILL.md": "92f42d86a76185bced7d64739489f10b0a8b129c8902e5d9508a471e611b8692",
46
+ ".claude/skills/meditate/SKILL.md": "b67f030413c4b5327cb209e543ccce0b35f5e970f70d370ce21f65426f076e37",
47
+ ".claude/skills/meditate/evals/basic.yaml": "daba441b2fd9d1d4afddcff6eaa9673884198b1d0f85d9d06edbe0738012291a",
48
+ ".claude/skills/meeting-prep/SKILL.md": "f8e4a17b795b5bc1a098cfffba09992fb4eab0db36157525605984edf50b2463",
49
+ ".claude/skills/memory-audit/SKILL.md": "b1f44da062c9f51381e878c20043e28336850c5180b2839a191ecb23bb73ddb0",
50
+ ".claude/skills/memory-health/SKILL.md": "a8a6881c33b6f1c5249267edd4cdb6315c6f23764ca04ab1dce66b94d759a78a",
51
+ ".claude/skills/memory-manager.md": "62edf1ed340687da9d8896dc4a21934b39b12c8269ea7a8de1beb9d25fd894e6",
52
+ ".claude/skills/morning-brief/SKILL.md": "b3354754ce2d0bf43c202e0b0ccb40b473b695f37bd9085ea575b649de194cfa",
53
+ ".claude/skills/morning-brief/evals/basic.yaml": "537de9adcde134161de9ffb9602b8c920bf1464bf224d99ad6f95f854d8480b7",
54
+ ".claude/skills/new-person/SKILL.md": "46a08baf49f2eb9df2527772df6de9fa4cb47eaeb841d3ec26a4c52aeea91542",
55
+ ".claude/skills/new-person/evals/basic.yaml": "f3c09a37c05d420e67520d8132a46356fed81575dcfb817cc97caf47f230392b",
56
+ ".claude/skills/new-workspace/SKILL.md": "c3f838b44a016b87445c66555398b666970a156288320efb183bc8cb8af2f0df",
57
+ ".claude/skills/new-workspace/references/workspace-templates.md": "6b36e961f112d46f32fdcf19cdacf3b05b4246201be315eee79659d606a7e243",
58
+ ".claude/skills/onboarding.md": "72fe133672f61929de0cfc7ff77699eb8856ca1e283b13acd3becffff75fca55",
59
+ ".claude/skills/pattern-recognizer.md": "05aad9b25a259e437d8b25dc57f0e14af81a6e53152475a571e373117b59132c",
60
+ ".claude/skills/pipeline-review/SKILL.md": "7891ceb3b6f4fb19e5255ae71c5894b6da8a9c566bc317e566bd4bc282ea1a6f",
61
+ ".claude/skills/relationship-tracker.md": "02316fc25b141c5783b3428e297bf6513a530e8e89782115e65ad01add8bd869",
62
+ ".claude/skills/research/SKILL.md": "0d317e6350b043ee774916b923957f61dbc174e3caafc0187198fa236a2fd28d",
63
+ ".claude/skills/research/references/source-evaluation.md": "64614b7eff83468d7ff76dd640252579f23e69e760969672e9aebe5ceb00f695",
64
+ ".claude/skills/risk-surfacer.md": "c69e720660c1a8cfe340fcaa56b0c30db672856d6ea75296fc94781c9efe12af",
65
+ ".claude/skills/skill-index.json": "043c86f1b07f28bb54db80117437f60fb3d79ad0d18549fdc2c74921b996a56f",
66
+ ".claude/skills/structure-generator.md": "dbe70841ab60599a632687aaa3c3652361483df2fe854640c56982b60622eb19",
67
+ ".claude/skills/summarize-doc/SKILL.md": "f8119d070bd66d8a448276bcd355bf26a9e69de777760d376dcd9e68bd9d64ad",
68
+ ".claude/skills/vault-awareness.md": "5a9c3d3f0b907750b9941a51ec9308c2bd465aa9bf3c513124f640696e0a4c2c",
69
+ ".claude/skills/weekly-review/SKILL.md": "fe7df64d3df18a0a47f98f7d4ef83cf98cef78cd9e0170a9316005035a2c6df7",
70
+ ".claude/skills/what-am-i-missing/SKILL.md": "56736397717c4f0a25cab17e7258702e5b04d20b48727c262fdf66e3b2e5899f",
71
+ "CLAUDE.md": "8cbbebee457366978a9b7b1d8788ac569ab0670874728fd9324fed53df8845c2"
72
+ }
73
+ }
@@ -191,7 +191,7 @@ Each significant action gets confirmed individually. "Go ahead with everything"
191
191
 
192
192
  **I always file raw source material before extracting from it.**
193
193
 
194
- When someone shares a transcript, email, document, or any substantive source, I file the original via the `memory.file` MCP tool before extracting memories. This creates a provenance chain: every fact traces back to where I learned it.
194
+ When someone shares a transcript, email, document, or any substantive source, I file the original via the `memory_file` MCP tool before extracting memories. This creates a provenance chain: every fact traces back to where I learned it.
195
195
 
196
196
  | Source Type | source_type |
197
197
  |-------------|-------------|
@@ -250,7 +250,7 @@ When you need to reference something volatile, store a pointer instead of a valu
250
250
  **Good:** "Beemok interview files are at workspaces/beemok/interviews/. Count files for current total."
251
251
 
252
252
  **Bad:** "Active commitments: send proposal to Sarah, review contract with Jim"
253
- **Good:** "Active commitments are tracked in context/commitments.md and via the `memory.recall` MCP tool"
253
+ **Good:** "Active commitments are tracked in context/commitments.md and via the `memory_recall` MCP tool"
254
254
 
255
255
  ### The Timestamp Rule
256
256
 
@@ -6,15 +6,15 @@ This rule is always active and applies to every session. Follow it silently - do
6
6
 
7
7
  ## How Memory Works
8
8
 
9
- Claudia's memory is provided by the **claudia-memory daemon**, a Python MCP server that registers memory tools (e.g., `memory.recall`, `memory.remember`, `memory.about`). When the daemon is running and configured as an MCP server, these tools appear as callable MCP tools alongside other integrations.
9
+ Claudia's memory is provided by the **claudia-memory daemon**, a Python MCP server that registers memory tools (e.g., `memory_recall`, `memory_remember`, `memory_about`). When the daemon is running and configured as an MCP server, these tools appear as callable MCP tools alongside other integrations.
10
10
 
11
11
  The `claudia` npm binary handles **setup and health checks only** (`claudia setup`, `claudia system-health`). It does not provide memory operations. All memory operations are MCP tools from the daemon.
12
12
 
13
- > **Migration note:** Some skill files may still reference old CLI syntax like `claudia memory recall "query" --project-dir "$PWD"`. Interpret these as calls to the equivalent MCP tool (e.g., `memory.recall` with a query parameter). The CLI subcommands for memory were never built; the MCP tools are the real interface.
13
+ > **Migration note:** Some skill files may still reference old CLI syntax like `claudia memory recall "query" --project-dir "$PWD"`. Interpret these as calls to the equivalent MCP tool (e.g., `memory_recall` with a query parameter). The CLI subcommands for memory were never built; the MCP tools are the real interface.
14
14
 
15
15
  ## Mandatory Disclosure
16
16
 
17
- **This is non-negotiable.** If you reach the Session Start Protocol and the `memory.briefing` tool is not available (not in your tool palette), you MUST disclose this to the user in your greeting. Do not wait to be asked. Do not silently fall back to context files. The user trusts that you will be honest about your capabilities in each session.
17
+ **This is non-negotiable.** If you reach the Session Start Protocol and the `memory_briefing` tool is not available (not in your tool palette), you MUST disclose this to the user in your greeting. Do not wait to be asked. Do not silently fall back to context files. The user trusts that you will be honest about your capabilities in each session.
18
18
 
19
19
  A single sentence is sufficient: "Heads up: my memory daemon isn't running this session, so I'm working from context files only."
20
20