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
@@ -2613,7 +2613,7 @@ class ConsolidateService:
2613
2613
  f"Possible duplicate entities: '{candidate['entity_1']['name']}' "
2614
2614
  f"and '{candidate['entity_2']['name']}' "
2615
2615
  f"({candidate['similarity']:.0%} similar via {candidate['method']}). "
2616
- f"Consider merging with memory.merge_entities."
2616
+ f"Consider merging with memory_merge_entities."
2617
2617
  )
2618
2618
  # Check for existing dedupe prediction
2619
2619
  existing = self.db.execute(
@@ -1345,7 +1345,7 @@ class RememberService:
1345
1345
  # Validate episode exists before any DB operations
1346
1346
  episode = self.db.get_one("episodes", where="id = ?", where_params=(episode_id,))
1347
1347
  if not episode:
1348
- result["error"] = f"Episode {episode_id} not found. Call memory.buffer_turn first to create an episode."
1348
+ result["error"] = f"Episode {episode_id} not found. Call memory_buffer_turn first to create an episode."
1349
1349
  logger.warning(f"end_session called with non-existent episode_id={episode_id}")
1350
1350
  return result
1351
1351
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-claudia",
3
- "version": "1.55.21",
3
+ "version": "1.57.0",
4
4
  "description": "An AI assistant who learns how you work.",
5
5
  "keywords": [
6
6
  "claudia",
@@ -39,7 +39,11 @@
39
39
  ],
40
40
  "dependencies": {},
41
41
  "devDependencies": {},
42
- "scripts": {},
42
+ "scripts": {
43
+ "generate-manifest": "node scripts/generate-manifest.js",
44
+ "test": "node --test test/",
45
+ "prepublishOnly": "node scripts/generate-manifest.js"
46
+ },
43
47
  "engines": {
44
48
  "node": ">=18.0.0"
45
49
  },
@@ -15,13 +15,13 @@
15
15
  "on_missing_me": "trigger_onboarding",
16
16
  "memory_verification": {
17
17
  "step_1_check_cli": "BEFORE anything else, verify the `claudia` CLI is available by running `claudia system-health --project-dir \"$PWD\"` via Bash tool. This checks that the claudia-memory daemon is configured and responsive. If the command fails or is not found: (1) Do NOT fall back to plugin:episodic-memory, mcp__plugin_episodic-memory_*, or any other memory search tool as a substitute — these give inferior, unrelated results and mask the real problem. (2) Tell the user: 'My memory tools are not available. The claudia-memory daemon may not be running or may not be configured as an MCP server. Check that the daemon is listed in your .mcp.json and that it starts without errors. Your context files are preserved.' (3) Continue with context files only (context/me.md, etc.) until fixed.",
18
- "step_2_load_context": "Call the `memory.session_context` MCP tool to get a formatted context block with recent memories, commitments, and unsummarized session alerts. If the MCP tool is not available, the daemon may not be properly configured. Suggest: 'The claudia-memory daemon may need to be configured in .mcp.json. Run claudia system-health for diagnostics.'",
18
+ "step_2_load_context": "Call the `memory_session_context` MCP tool to get a formatted context block with recent memories, commitments, and unsummarized session alerts. If the MCP tool is not available, the daemon may not be properly configured. Suggest: 'The claudia-memory daemon may need to be configured in .mcp.json. Run claudia system-health for diagnostics.'",
19
19
  "fallback": "If memory MCP tools are not available: (1) Do NOT use plugin:episodic-memory or any other memory search tool as a substitute. (2) Tell the user: 'My memory tools are not available. The claudia-memory daemon may not be configured as an MCP server.' (3) Read context files directly (context/me.md, context/commitments.md, context/learnings.md, context/patterns.md, context/waiting.md) as the fallback. Make clear to the user they are in degraded mode with no semantic search or cross-session learning."
20
20
  },
21
21
  "enhanced_memory": {
22
- "context_load": "Call the `memory.session_context` MCP tool to get a formatted context block with recent memories, commitments, and unsummarized session alerts.",
23
- "catch_up": "If unsummarized sessions are reported in the context block, review buffered turns and call the `memory.end_session` MCP tool with a retroactive narrative and structured extractions for each.",
24
- "recall_context": "Use the loaded context for greeting. For deeper search on specific topics, call the `memory.recall` MCP tool with the relevant query."
22
+ "context_load": "Call the `memory_session_context` MCP tool to get a formatted context block with recent memories, commitments, and unsummarized session alerts.",
23
+ "catch_up": "If unsummarized sessions are reported in the context block, review buffered turns and call the `memory_end_session` MCP tool with a retroactive narrative and structured extractions for each.",
24
+ "recall_context": "Use the loaded context for greeting. For deeper search on specific topics, call the `memory_recall` MCP tool with the relevant query."
25
25
  },
26
26
  "proactive_surfacing": {
27
27
  "description": "Surface urgent items naturally in the greeting. Keep it to a brief mention, not a full report.",
@@ -30,12 +30,12 @@
30
30
  {
31
31
  "type": "overdue_commitments",
32
32
  "description": "Commitments older than 7 days without resolution",
33
- "query_hint": "Call the `memory.recall` MCP tool with query about overdue commitments and check dates"
33
+ "query_hint": "Call the `memory_recall` MCP tool with query about overdue commitments and check dates"
34
34
  },
35
35
  {
36
36
  "type": "cooling_relationships",
37
37
  "description": "Important people not mentioned in >30 days",
38
- "query_hint": "Call the `memory.search_entities` MCP tool with type: person and check last_mentioned dates"
38
+ "query_hint": "Call the `memory_search_entities` MCP tool with type: person and check last_mentioned dates"
39
39
  }
40
40
  ],
41
41
  "examples": [
@@ -58,7 +58,7 @@
58
58
  ],
59
59
  "save_mode": "merge",
60
60
  "enhanced_memory": {
61
- "summarize": "Call the `memory.end_session` MCP tool with a narrative summary and structured extractions from the session's buffered turns. The narrative should enhance stored information with tone, context, unresolved threads, and nuance that structured fields cannot capture."
61
+ "summarize": "Call the `memory_end_session` MCP tool with a narrative summary and structured extractions from the session's buffered turns. The narrative should enhance stored information with tone, context, unresolved threads, and nuance that structured fields cannot capture."
62
62
  }
63
63
  }
64
64
  },
@@ -75,10 +75,10 @@
75
75
  "notes": {
76
76
  "implementation": "These hooks define intended behaviors. Claude Code will execute them as behavioral guidelines rather than traditional script execution.",
77
77
  "memory_verification": "CRITICAL: At session start, verify memory availability by running `claudia system-health --project-dir \"$PWD\"` BEFORE proceeding. If memory MCP tools are not available, do NOT use plugin:episodic-memory as a substitute. Tell the user the claudia-memory daemon may need to be configured. Fall back to context files only (context/me.md, etc.) in the meantime.",
78
- "session_start": "At session start: (1) Verify claudia system-health passes, (2) Call the `memory.session_context` MCP tool, (3) Read context files, (4) Check for unsummarized sessions. If me.md is missing, trigger onboarding.",
79
- "session_end": "Before session ends, Claudia should generate a session summary via the `memory.end_session` MCP tool (enhanced memory) or update context files (markdown fallback). The summary includes both a free-form narrative and structured extractions.",
78
+ "session_start": "At session start: (1) Verify claudia system-health passes, (2) Call the `memory_session_context` MCP tool, (3) Read context files, (4) Check for unsummarized sessions. If me.md is missing, trigger onboarding.",
79
+ "session_end": "Before session ends, Claudia should generate a session summary via the `memory_end_session` MCP tool (enhanced memory) or update context files (markdown fallback). The summary includes both a free-form narrative and structured extractions.",
80
80
  "first_run": "The absence of context/me.md signals a first-run scenario requiring onboarding.",
81
- "turn_buffering": "During sessions with enhanced memory, Claudia buffers each meaningful conversation turn via the `memory.buffer_turn` MCP tool. This ensures nothing is lost even if the session ends abruptly. The buffered turns are summarized at session end or caught up at next session start.",
82
- "source_filing": "CRITICAL: When processing transcripts, emails, or documents, ALWAYS call the `memory.file` MCP tool BEFORE extracting information. The Source Preservation principle (#12) is non-negotiable. Raw sources must be filed for provenance."
81
+ "turn_buffering": "During sessions with enhanced memory, Claudia buffers each meaningful conversation turn via the `memory_buffer_turn` MCP tool. This ensures nothing is lost even if the session ends abruptly. The buffered turns are summarized at session end or caught up at next session start.",
82
+ "source_filing": "CRITICAL: When processing transcripts, emails, or documents, ALWAYS call the `memory_file` MCP tool BEFORE extracting information. The Source Preservation principle (#12) is non-negotiable. Raw sources must be filed for provenance."
83
83
  }
84
84
  }
@@ -1,37 +1,137 @@
1
1
  #!/usr/bin/env python3
2
2
  """PostToolUse hook: passively captures tool invocations to a JSONL file.
3
3
 
4
+ Reads hook input from stdin (Claude Code's actual contract), not env vars.
4
5
  Writes observations to ~/.claudia/observations.jsonl for later ingestion
5
- by the memory daemon. Designed to complete in ~1ms (file append only).
6
+ by the memory daemon and by the session-summary generator.
7
+
8
+ Designed to complete in <50ms (file append only, no network).
9
+
10
+ Captured per-tool-call:
11
+ - tool name + truncated input/output
12
+ - file paths touched (for Write/Edit/MultiEdit/NotebookEdit)
13
+ - external actions (git push, gh repo create, vercel deploy, etc.)
14
+ - session_id for linking observations to a session
6
15
  """
7
16
 
8
17
  import json
9
- import os
18
+ import re
10
19
  import sys
11
20
  import time
12
21
  from pathlib import Path
13
22
 
14
- SKIP_PREFIXES = ("memory.", "mcp__plugin_episodic", "cognitive.")
15
- SKIP_NAMES = {"Read", "Glob", "Grep", "LS", "ListMcpResourcesTool", "ReadMcpResourceTool"}
23
+ # Skip noisy or read-only tools that don't need observation
24
+ SKIP_PREFIXES = ("memory_", "memory.", "mcp__plugin_episodic", "cognitive.")
25
+ SKIP_NAMES = {
26
+ "Read", "Glob", "Grep", "LS", "TodoRead",
27
+ "ListMcpResourcesTool", "ReadMcpResourceTool",
28
+ "TaskList", "TaskGet", "TaskOutput", "ToolSearch",
29
+ }
30
+
31
+ # Bash command patterns that signal an "external action" worth flagging in
32
+ # the daily summary. Anchored so the candidate command must appear at the
33
+ # start of the line or after a shell separator (`;`, `&&`, `|`, `\n`, `(`),
34
+ # with optional transparent prefixes (`sudo`, `nohup`, `time`, `env VAR=`).
35
+ # This rejects echo'd test JSON ("git push for testing") and prefixed
36
+ # look-alikes ("git pushd", "ungit push") while still firing on real
37
+ # invocations including chained ones (`cd foo && git push`).
38
+ _BASH_CMD_ANCHOR = (
39
+ r"(?:^|[;&|\n(])\s*"
40
+ r"(?:sudo\s+|nohup\s+|time\s+|env\s+\w+=\S+\s+)*"
41
+ )
42
+ EXTERNAL_ACTION_PATTERNS: list[tuple[str, re.Pattern]] = [
43
+ ("git push", re.compile(_BASH_CMD_ANCHOR + r"git\s+push\b")),
44
+ ("gh repo create", re.compile(_BASH_CMD_ANCHOR + r"gh\s+repo\s+create\b")),
45
+ ("gh pr create", re.compile(_BASH_CMD_ANCHOR + r"gh\s+pr\s+create\b")),
46
+ ("gh release", re.compile(_BASH_CMD_ANCHOR + r"gh\s+release\b")),
47
+ ("vercel --prod", re.compile(_BASH_CMD_ANCHOR + r"vercel\s+(?:[^\n]*\s)?--prod\b")),
48
+ ("vercel deploy", re.compile(_BASH_CMD_ANCHOR + r"vercel\s+deploy\b")),
49
+ ("netlify deploy", re.compile(_BASH_CMD_ANCHOR + r"netlify\s+deploy\b")),
50
+ ("supabase db push", re.compile(_BASH_CMD_ANCHOR + r"supabase\s+db\s+push\b")),
51
+ ("npx supabase", re.compile(_BASH_CMD_ANCHOR + r"npx\s+supabase\b")),
52
+ ]
53
+
54
+
55
+ def is_external_action(tool_name: str, tool_input) -> str | None:
56
+ """Return a short label if this tool call looks like an external action."""
57
+ if tool_name == "Bash":
58
+ cmd = (tool_input or {}).get("command", "") if tool_input else ""
59
+ if not cmd:
60
+ return None
61
+ for label, regex in EXTERNAL_ACTION_PATTERNS:
62
+ if regex.search(cmd):
63
+ return label
64
+ elif tool_name in {
65
+ "mcp__claudia-private-email__claudia_email_send",
66
+ "mcp__gmail__send_email", "mcp__gmail__draft_email",
67
+ }:
68
+ return "email send/draft"
69
+ elif tool_name in {
70
+ "mcp__google-calendar__create_event",
71
+ "mcp__google-calendar__update_event",
72
+ "mcp__google-calendar__delete_event",
73
+ }:
74
+ return "calendar event"
75
+ elif tool_name.startswith("mcp__claude_ai_Slack__slack_send"):
76
+ return "slack send"
77
+ return None
78
+
79
+
80
+ def extract_file_path(tool_name: str, tool_input: dict) -> str | None:
81
+ """For Write/Edit/MultiEdit/NotebookEdit, return the file path being modified."""
82
+ if tool_name in {"Write", "Edit", "MultiEdit", "NotebookEdit"}:
83
+ return (tool_input or {}).get("file_path")
84
+ return None
16
85
 
17
86
 
18
87
  def main():
19
- tool_name = os.environ.get("CLAUDE_TOOL_NAME", "")
88
+ # Claude Code passes hook payload as JSON on stdin.
89
+ try:
90
+ raw = sys.stdin.read()
91
+ if not raw.strip():
92
+ return
93
+ payload = json.loads(raw)
94
+ except (json.JSONDecodeError, OSError):
95
+ return
96
+
97
+ tool_name = payload.get("tool_name", "")
20
98
  if not tool_name:
21
99
  return
22
100
 
23
101
  if any(tool_name.startswith(p) for p in SKIP_PREFIXES) or tool_name in SKIP_NAMES:
24
102
  return
25
103
 
26
- tool_input = os.environ.get("CLAUDE_TOOL_INPUT", "")[:200]
27
- tool_output = os.environ.get("CLAUDE_TOOL_OUTPUT", "")[:200]
104
+ tool_input = payload.get("tool_input") or {}
105
+ tool_response = payload.get("tool_response") or {}
106
+ session_id = payload.get("session_id", "")
107
+
108
+ input_summary = json.dumps(tool_input)[:300] if tool_input else ""
109
+ output_summary = ""
110
+ if isinstance(tool_response, dict):
111
+ for key in ("stdout", "output", "result", "content"):
112
+ if key in tool_response:
113
+ val = tool_response[key]
114
+ output_summary = str(val)[:300] if val else ""
115
+ break
116
+ if not output_summary:
117
+ output_summary = json.dumps(tool_response)[:300]
118
+ else:
119
+ output_summary = str(tool_response)[:300]
120
+
121
+ file_path = extract_file_path(tool_name, tool_input)
122
+ external_action = is_external_action(tool_name, tool_input)
28
123
 
29
124
  observation = {
30
- "tool": tool_name,
31
- "input": tool_input,
32
- "output": tool_output,
33
125
  "ts": time.time(),
126
+ "session_id": session_id,
127
+ "tool": tool_name,
128
+ "input": input_summary,
129
+ "output": output_summary,
34
130
  }
131
+ if file_path:
132
+ observation["file_path"] = file_path
133
+ if external_action:
134
+ observation["external_action"] = external_action
35
135
 
36
136
  obs_file = Path.home() / ".claudia" / "observations.jsonl"
37
137
  obs_file.parent.mkdir(parents=True, exist_ok=True)
@@ -12,10 +12,10 @@ print(json.dumps({
12
12
  "additionalContext": (
13
13
  "Context compaction advisory: If important information was discussed recently, "
14
14
  "ensure it has been stored via MCP tools. Check: (1) Commitments: call the "
15
- "memory.remember MCP tool for any promises not yet stored. (2) People: call the "
16
- "memory.entity MCP tool for anyone discussed in detail. (3) Relationships: call the "
17
- "memory.relate MCP tool for connections mentioned. (4) Buffer: call the "
18
- "memory.buffer_turn MCP tool with a summary if recent exchanges were not buffered. "
15
+ "memory_remember MCP tool for any promises not yet stored. (2) People: call the "
16
+ "memory_entity MCP tool for anyone discussed in detail. (3) Relationships: call the "
17
+ "memory_relate MCP tool for connections mentioned. (4) Buffer: call the "
18
+ "memory_buffer_turn MCP tool with a summary if recent exchanges were not buffered. "
19
19
  "With 1M context, compaction is less frequent, but proactive capture remains good practice."
20
20
  )
21
21
  }))
@@ -6,7 +6,7 @@
6
6
  # Inject advisory into compacted context
7
7
  cat <<EOF
8
8
  {
9
- "additionalContext": "Context compaction advisory: If important information was discussed recently, ensure it has been stored via MCP tools. Check: (1) Commitments: call the memory.remember MCP tool for any promises not yet stored. (2) People: call the memory.entity MCP tool for anyone discussed in detail. (3) Relationships: call the memory.relate MCP tool for connections mentioned. (4) Buffer: call the memory.buffer_turn MCP tool with a summary if recent exchanges were not buffered. With 1M context, compaction is less frequent, but proactive capture remains good practice."
9
+ "additionalContext": "Context compaction advisory: If important information was discussed recently, ensure it has been stored via MCP tools. Check: (1) Commitments: call the memory_remember MCP tool for any promises not yet stored. (2) People: call the memory_entity MCP tool for anyone discussed in detail. (3) Relationships: call the memory_relate MCP tool for connections mentioned. (4) Buffer: call the memory_buffer_turn MCP tool with a summary if recent exchanges were not buffered. With 1M context, compaction is less frequent, but proactive capture remains good practice."
10
10
  }
11
11
  EOF
12
12
  exit 0
@@ -24,6 +24,50 @@ def _fetch_briefing():
24
24
  return None
25
25
 
26
26
 
27
+ def _recent_sessions_summary(max_days: int = 3) -> str:
28
+ """Return a compact recap of the last N days of session summaries.
29
+
30
+ Reads ~/.claudia/sessions/YYYY-MM-DD/INDEX.md files. Returns a short
31
+ multi-day digest showing what was worked on. Bounded to keep startup fast.
32
+
33
+ Empty string when no session history exists yet (fresh installs).
34
+ """
35
+ sessions_dir = Path.home() / ".claudia" / "sessions"
36
+ if not sessions_dir.exists():
37
+ return ""
38
+
39
+ date_folders = sorted(
40
+ [d for d in sessions_dir.iterdir() if d.is_dir() and d.name[:4].isdigit()],
41
+ reverse=True,
42
+ )[:max_days]
43
+
44
+ if not date_folders:
45
+ return ""
46
+
47
+ lines = []
48
+ for date_dir in date_folders:
49
+ summaries = sorted(date_dir.glob("[0-9][0-9]-*.md"))
50
+ if not summaries:
51
+ continue
52
+ topics = []
53
+ for f in summaries:
54
+ try:
55
+ first_line = f.read_text(encoding="utf-8").split("\n", 1)[0]
56
+ topic = first_line.lstrip("#").strip()
57
+ if "—" in topic:
58
+ topic = topic.split("—", 1)[1].strip()
59
+ topics.append(topic)
60
+ except OSError:
61
+ continue
62
+ if topics:
63
+ lines.append(f" {date_dir.name}: {' · '.join(topics[:5])}")
64
+
65
+ if not lines:
66
+ return ""
67
+
68
+ return "Recent sessions (from ~/.claudia/sessions/):\n" + "\n".join(lines)
69
+
70
+
27
71
  def _run_claudia(*args):
28
72
  """Run claudia CLI and return parsed JSON output, or None on failure."""
29
73
  claudia_bin = shutil.which("claudia")
@@ -65,11 +109,15 @@ def check_health():
65
109
 
66
110
  summary = " ".join(parts) if parts else "System ready."
67
111
  briefing = _fetch_briefing()
112
+ recent = _recent_sessions_summary()
113
+
114
+ sections = [f"Memory system healthy. {summary}"]
115
+ if recent:
116
+ sections.append("--- Recent Sessions ---\n" + recent)
68
117
  if briefing:
69
- output = f"Memory system healthy. {summary}\n\n--- Session Briefing ---\n{briefing}"
70
- else:
71
- output = f"Memory system healthy. {summary}"
72
- print(json.dumps({"additionalContext": output}))
118
+ sections.append("--- Session Briefing ---\n" + briefing)
119
+
120
+ print(json.dumps({"additionalContext": "\n\n".join(sections)}))
73
121
  return
74
122
 
75
123
  # CLI not available -- provide fallback guidance