Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "atomicmemory",
"owner": {
"name": "AtomicMemory",
"url": "https://atomicmem.ai"
},
"plugins": [
{
"name": "claude-code",
"source": "./plugins/claude-code",
"description": "Persistent semantic memory for Claude Code — user preferences, project context, prior decisions, and codebase facts that survive across sessions.",
"version": "0.1.0",
"category": "productivity",
"homepage": "https://docs.atomicmemory.ai/integrations/coding-agents/claude-code",
"license": "MIT"
}
]
}
50 changes: 8 additions & 42 deletions plugins/claude-code/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,15 @@
"command": "npx",
"args": ["-y", "@atomicmemory/mcp-server"],
"env": {
"ATOMICMEMORY_API_URL": "${config.apiUrl}",
"ATOMICMEMORY_API_KEY": "${config.apiKey}",
"ATOMICMEMORY_PROVIDER": "${config.provider}",
"ATOMICMEMORY_SCOPE_USER": "${config.scope.user}",
"ATOMICMEMORY_SCOPE_AGENT": "${config.scope.agent}",
"ATOMICMEMORY_SCOPE_NAMESPACE": "${config.scope.namespace}",
"ATOMICMEMORY_SCOPE_THREAD": "${config.scope.thread}"
"ATOMICMEMORY_API_URL": "${ATOMICMEMORY_API_URL}",
"ATOMICMEMORY_API_KEY": "${ATOMICMEMORY_API_KEY}",
"ATOMICMEMORY_PROVIDER": "${ATOMICMEMORY_PROVIDER:-atomicmemory}",
"ATOMICMEMORY_SCOPE_USER": "${ATOMICMEMORY_SCOPE_USER}",
"ATOMICMEMORY_SCOPE_AGENT": "${ATOMICMEMORY_SCOPE_AGENT:-}",
"ATOMICMEMORY_SCOPE_NAMESPACE": "${ATOMICMEMORY_SCOPE_NAMESPACE:-}",
"ATOMICMEMORY_SCOPE_THREAD": "${ATOMICMEMORY_SCOPE_THREAD:-}"
}
}
},
"skills": ["./skills/atomicmemory"],
"configSchema": {
"type": "object",
"required": ["apiUrl", "apiKey", "scope"],
"additionalProperties": false,
"properties": {
"apiUrl": {
"type": "string",
"format": "uri",
"description": "AtomicMemory core URL."
},
"apiKey": {
"type": "string",
"description": "API key — stored in Claude Code's credentials vault, not rendered in logs."
},
"provider": {
"type": "string",
"enum": ["atomicmemory", "mem0"],
"default": "atomicmemory",
"description": "Provider name dispatched through the SDK's MemoryProvider model."
},
"scope": {
"type": "object",
"required": ["user"],
"additionalProperties": false,
"description": "At least `user` must be set — memory operations throw without any scope.",
"properties": {
"user": { "type": "string" },
"agent": { "type": "string" },
"namespace": { "type": "string" },
"thread": { "type": "string" }
}
}
}
}
"skills": ["./skills/atomicmemory"]
}
81 changes: 67 additions & 14 deletions plugins/claude-code/README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,87 @@
# AtomicMemory for Claude Code

Persistent semantic memory that survives across Claude Code sessions.
Persistent semantic memory that survives across Claude Code sessions. Ships the MCP server spec, lifecycle hooks for automatic memory capture, and a skill that teaches Claude when to call the memory tools.

## Install

### 1. Install dependencies

The hook scripts are bash and depend on `jq` (for safe JSON input/output parsing) and `curl` (for `on_user_prompt.sh`'s direct HTTP search).

```bash
# macOS
brew install jq

# Debian/Ubuntu
sudo apt-get install -y jq
```

### 2. Export shell env vars

Both the MCP server and the lifecycle hook scripts read their config from the shell environment. Export these in `~/.zshrc` / `~/.bashrc` before launching Claude Code:

```bash
claude plugin install atomicmemory/claude-code
claude plugin config atomicmemory/claude-code \
--set apiUrl=https://memory.yourco.com \
--set apiKey=$ATOMICMEMORY_API_KEY \
--set scope.user=$USER \
--set scope.namespace=<repo-or-project>
export ATOMICMEMORY_API_URL="https://memory.yourco.com"
export ATOMICMEMORY_API_KEY="am_live_…"
export ATOMICMEMORY_SCOPE_USER="$USER"
# Optional:
# export ATOMICMEMORY_SCOPE_NAMESPACE="<repo-or-project>"
# export ATOMICMEMORY_SCOPE_AGENT="claude-code"
# export ATOMICMEMORY_SCOPE_THREAD="<thread-id>"
# export ATOMICMEMORY_PROVIDER="atomicmemory" # or "mem0"
```

`scope.user` is required. `scope.namespace` / `scope.agent` / `scope.thread` are optional.
`ATOMICMEMORY_SCOPE_USER` is required. All `_SCOPE_*` / `_PROVIDER` vars are optional and safe to omit.

See the [full documentation](https://docs.atomicmemory.ai/integrations/coding-agents/claude-code) for details, the MCP tool reference, and how to point the plugin at alternative MemoryProvider backends.
- `_API_URL` / `_API_KEY` / `_SCOPE_USER` — needed by **both** the MCP server (for `memory_search` / `memory_ingest` / `memory_package` tool calls) and `on_user_prompt.sh`'s direct HTTP search. If absent, `on_user_prompt.sh` no-ops (the user's prompt still goes through).
- `_SCOPE_NAMESPACE` — used by both, as a per-project isolation boundary.
- `_SCOPE_AGENT` / `_SCOPE_THREAD` — forwarded to the MCP server only; the hook scripts do not read them.

If `jq` or `curl` are missing, all hooks degrade to no-ops rather than failing.

### 3. Install the plugin

```bash
# Register the marketplace (one-time)
claude plugin marketplace add atomicmemory/atomicmemory-integrations

# Install the plugin
claude plugin install claude-code@atomicmemory
```

## What's in this directory

```
plugins/claude-code/
├── .claude-plugin/
│ └── plugin.json # plugin manifest (MCP server + configSchema)
└── skills/
└── atomicmemory/
└── SKILL.md # when/how the agent should call memory tools
│ └── plugin.json # plugin manifest
├── hooks/
│ └── hooks.json # Claude Code lifecycle hook registrations
├── scripts/ # lifecycle hook scripts
│ ├── block_memory_write.sh
│ ├── on_pre_compact.sh
│ ├── on_session_start.sh
│ ├── on_stop.sh
│ ├── on_task_completed.sh
│ └── on_user_prompt.sh
├── skills/
│ └── atomicmemory/
│ └── SKILL.md # when/how the agent should call memory tools
└── README.md
```

The plugin spawns [`@atomicmemory/mcp-server`](../../packages/mcp-server) via `npx`. It ships no runtime code of its own — all memory semantics live in the shared server.
The plugin spawns [`@atomicmemory/mcp-server`](../../packages/mcp-server) via `npx`. All memory semantics live in the shared server; the scripts here are thin — mostly inject guidance for the agent, with `on_user_prompt.sh` hitting the core's `/v1/memories/search/fast` endpoint directly for lower latency on every prompt.

## Lifecycle hooks

| Hook | What it does |
|---|---|
| `SessionStart` | Injects a bootstrap prompt telling Claude to call `memory_search` early. Different prompt for `startup` / `resume` / `compact`. |
| `UserPromptSubmit` | Searches memory for the current prompt via HTTP and injects matching memories as context. Skipped for prompts &lt; 20 chars or missing env. |
| `PreCompact` | Prompts Claude to ingest a full session-state summary before context compaction collapses it. |
| `Stop` | Prompts Claude to store any durable learnings from the finished turn. Guards against infinite loops via `stop_hook_active`. |
| `TaskCompleted` | Prompts Claude to extract task-specific learnings. |
| `PreToolUse` (Write\|Edit) | Blocks writes to `MEMORY.md` and adjacent memory-file paths — redirects agents to `memory_ingest`. |

## License

Expand Down
72 changes: 72 additions & 0 deletions plugins/claude-code/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|compact",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_session_start.sh",
"statusMessage": "Loading AtomicMemory context..."
}
]
}
],
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/block_memory_write.sh"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_pre_compact.sh",
"statusMessage": "Prompting session-state save..."
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_stop.sh",
"timeout": 10
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_user_prompt.sh",
"statusMessage": "Searching AtomicMemory...",
"timeout": 5
}
]
}
],
"TaskCompleted": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_task_completed.sh",
"timeout": 10
}
]
}
]
}
}
37 changes: 37 additions & 0 deletions plugins/claude-code/scripts/block_memory_write.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Hook: PreToolUse (matcher: Write|Edit)
#
# Blocks writes to MEMORY.md and adjacent auto-memory files so the
# agent uses the `memory_ingest` MCP tool as the single source of
# truth for durable memory.
#
# Input: JSON on stdin with tool_name, tool_input
# Output: stderr message (exit 2 = block)
#
# Exit codes:
# 0 = allow the tool call
# 2 = block the tool call (stderr is shown to Claude as feedback)

# Use -uo (not -euo): a failure inside the jq pipeline should
# degrade to "allow", not surface as a non-2 error to Claude Code.
set -uo pipefail

command -v jq >/dev/null 2>&1 || exit 0

INPUT=$(cat)

FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""' 2>/dev/null || echo "")

if [ -z "$FILE_PATH" ]; then
exit 0
fi

case "$FILE_PATH" in
MEMORY.md|*/MEMORY.md|*/memory/*.md|*/.claude/*/memory/*)
echo "BLOCKED: Do not write to $FILE_PATH. Use the \`memory_ingest\` MCP tool instead — this project uses AtomicMemory for all durable memory, so file-based notes will drift from the semantic store." >&2
exit 2
;;
*)
exit 0
;;
esac
69 changes: 69 additions & 0 deletions plugins/claude-code/scripts/on_pre_compact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# Hook: PreCompact
#
# Last chance to capture the full conversation before compaction
# collapses it. Prompts Claude to ingest a session summary while
# it still has the full context.
#
# Input: JSON on stdin (trigger + transcript_path).
# Output: JSON — {"decision":"block","reason":"..."}
# NOTE: Plain stdout on PreCompact is debug-log only. The
# JSON decision-form's context-injection semantics for
# PreCompact are not documented — this is best-effort. If
# Claude Code treats `block` as "skip compaction" without
# surfacing the reason to the model, this hook becomes a
# no-op rather than a bug.
#
# No direct API call here — we rely on Claude's MCP tool use so the
# content reflects what Claude knows, not what scripts can grep.

set -uo pipefail

command -v jq >/dev/null 2>&1 || exit 0

jq -Rs '{decision: "block", reason: .}' <<'REASON_EOF' 2>/dev/null || exit 0
## CRITICAL: Pre-Compaction Session Summary

Context compaction is about to happen. You are about to lose most of your conversation history. You MUST store a comprehensive session summary NOW using the `memory_ingest` MCP tool.

### Step 1: Store session summary

Call `memory_ingest` with `mode: "text"` and a thorough summary covering ALL of the following:

```
## Session Summary (Pre-Compaction)

### User goal
[What the user originally asked for and their intent]

### Accomplished
[Numbered list of tasks completed, features built, bugs fixed]

### Key decisions
[Architectural choices, design decisions, trade-offs discussed]

### Files touched
[Important file paths with what changed in each and why]

### Current state
[What is in progress RIGHT NOW — the task you were in the middle of,
any pending items, blockers, or concrete next steps]

### Important context
[User preferences observed, coding patterns, anything that would help
the post-compaction agent continue without asking redundant questions]
```

### Step 2: Store any unstored learnings

If there are learnings from this session you have not stored yet, call `memory_ingest` for each:

- Failed approaches (mode: text, prefix with "Anti-pattern:")
- Successful strategies (prefix with "Strategy:")
- Architectural decisions (prefix with "Decision:")
- User preferences (prefix with "Preference:")

Do this NOW. The quality of this summary determines whether you can continue the user task after compaction.
REASON_EOF

exit 0
Loading