Long-term user profile memory that persists across Claude Code sessions
/plugin marketplace add cameronsjo/claude-marketplace/plugin install essentials@cameronsjoThis skill inherits all available tools. When active, it can use any tool Claude has access to.
CHANGELOG.mddocs/architecture.mddocs/data-schemas.mddocs/examples.mddocs/quick-reference.mdmcp/package-lock.jsonmcp/package.jsonmcp/src/__tests__/extract.test.tsmcp/src/__tests__/store.test.tsmcp/src/context.tsmcp/src/decay-check.tsmcp/src/extract-memory.tsmcp/src/hooks/session-start.shmcp/src/hooks/stop-memory.shmcp/src/mcp-server.tsmcp/src/prompt.tsmcp/src/session.tsmcp/src/store.tsmcp/src/types.tsmcp/tsconfig.jsonPersistent user profile memory for Claude Code. Two implementations - pick your flavor:
| Mode | Deps | Features | Best for |
|---|---|---|---|
| minimal/ | jq only | Hook extraction | Lightweight, portable |
| mcp/ | Node + MCP SDK | Hooks + real-time tools | Full control |
# Add to ~/.claude/settings.json
{
"hooks": {
"SessionStart": [{
"hooks": [{
"type": "command",
"command": "~/.claude/skills/user-memory/minimal/session-start.sh"
}]
}],
"Stop": [{
"hooks": [{
"type": "command",
"command": "~/.claude/skills/user-memory/minimal/stop-memory.sh"
}]
}]
}
}
Done. No npm install needed.
cd ~/.claude/skills/user-memory/mcp
npm install
{
"hooks": {
"SessionStart": [{
"hooks": [{
"type": "command",
"command": "~/.claude/skills/user-memory/mcp/src/hooks/session-start.sh"
}]
}],
"Stop": [{
"hooks": [{
"type": "command",
"command": "~/.claude/skills/user-memory/mcp/src/hooks/stop-memory.sh"
}]
}]
},
"mcpServers": {
"user-memory": {
"command": "npx",
"args": ["tsx", "~/.claude/skills/user-memory/mcp/src/mcp-server.ts"]
}
}
}
SessionStart hook
↓
Loads ~/.claude/user-memory/profile.json
↓
Injects into session context
Stop hook (after every Claude response)
↓
Reads conversation transcript
↓
Extracts preferences via pattern matching
↓
Deduplicates (skips already-processed turns)
↓
Merges into profile.json
[MCP only] Claude can also call:
Profile tools:
- get_user_profile
- update_user_profile
- remove_preference
- clear_user_profile
- get_changelog
- get_preference_metadata
- run_decay
Session continuity tools:
- get_session_context
- update_task
- log_decision
- add_session_context
- set_session_summary
- get_full_context
| Category | Trigger phrases |
|---|---|
| Tech stack | "I prefer Bun", "I'm switching to FastAPI" |
| Editor | "I use neovim", "My editor is VS Code" |
| Tone | "Be more direct", "I prefer concise" |
| Role | "I'm a backend engineer" |
| Languages | "I work mostly in TypeScript" |
~/.claude/user-memory/
├── profile.json # Your preferences
├── profile-meta.json # Confidence/decay tracking (MCP only)
├── changelog.jsonl # Audit trail (auto-pruned)
├── .processed_turns # Dedup tracker
└── sessions/ # Session continuity (MCP only)
├── session-abc123.json
└── ...
Override location: USER_MEMORY_DIR=/custom/path
Every profile change is logged to changelog.jsonl:
{"timestamp":"2025-01-15T10:30:00Z","session_id":"abc123","action":"extract","source":"minimal/hook","changes":{"codePreferences":{"preferredStacks":["Bun"]}}}
{"timestamp":"2025-01-15T11:00:00Z","action":"update","source":"mcp/tool","changes":{"tools":{"editor":"neovim"}}}
{"timestamp":"2025-01-16T09:00:00Z","action":"clear","source":"mcp/tool","changes":{"userId":"default"}}
| Field | Description |
|---|---|
timestamp | ISO 8601 when change occurred |
session_id | Session that triggered the change (hooks only) |
action | extract, update, clear, remove, decay |
source | minimal/hook, mcp/hook, mcp/tool, system |
changes | What was added/modified |
removed | Paths that were removed (for remove/decay actions) |
MCP only: Use get_changelog tool to query the log.
Auto-pruning: Changelog keeps last 1000 entries and removes entries older than 90 days.
Track task progress and decisions across sessions. Resume where you left off.
~/.claude/user-memory/sessions/
├── session-abc123.json # Session with tasks, decisions, context
├── session-def456.json
└── ...
| Tool | Purpose |
|---|---|
get_session_context | Get resume context from previous sessions |
update_task | Track task progress (pending/in_progress/blocked/completed) |
log_decision | Record important decisions with rationale |
add_session_context | Store context notes for future sessions |
set_session_summary | Set summary shown at next session start |
get_full_context | Get full context prompt (profile + session resume) |
Sessions older than 30 days are automatically removed.
The extraction system understands when you want to remove preferences:
| Phrase | Effect |
|---|---|
| "I no longer use Webpack" | Removes Webpack from stacks |
| "I stopped using React" | Removes React from stacks |
| "Forget that I prefer tabs" | Removes that preference |
| "I switched away from npm" | Removes npm from tools |
Negation patterns have higher priority than positive patterns, so "I prefer Bun over npm" will add Bun and remove npm atomically.
Preferences decay over time if not reinforced. This prevents stale preferences from persisting forever.
confidence * 0.5^(days / 30)| Day | Event | Confidence |
|---|---|---|
| 0 | "I prefer Bun" | 1.00 |
| 30 | No mention (decay) | 0.50 |
| 60 | No mention (decay) | 0.25 |
| 75 | "I'm using Bun" (reinforce) | 0.55 |
| 90 | No mention (decay) | 0.39 |
| Tool | Purpose |
|---|---|
get_preference_metadata | View confidence scores, days until decay |
run_decay | Manually trigger decay cycle |
remove_preference | Explicitly remove preferences |
When using MCP mode, decay is automatically checked at session start:
interface UserProfile {
userId: string;
schemaVersion: 1;
lastUpdated: string;
bio?: string;
work?: {
role?: string;
focusAreas?: string[];
languages?: string[];
};
codePreferences?: {
tone?: "direct" | "neutral" | "friendly";
detailLevel?: "high" | "medium" | "low";
avoidExamples?: string[];
preferredStacks?: string[];
};
tools?: {
editor?: string;
infra?: string[];
};
interests?: string[];
custom?: Record<string, unknown>;
}
Switch modes anytime - both use the same profile.json:
# Switch from minimal → mcp
# Just update hooks paths in settings.json and add mcpServers
# Switch from mcp → minimal
# Remove mcpServers, update hook paths
Why Stop hook instead of SessionEnd? SessionEnd doesn't fire on Ctrl+C, terminal close, or crashes. Stop hook runs after every response - bulletproof.
Why heuristic extraction?
Minimal vs MCP trade-offs:
| Minimal | MCP | |
|---|---|---|
| Dependencies | jq | Node, tsx, MCP SDK |
| Real-time updates | No | Yes (tool calls) |
| Cross-tool access | No | Yes |
| Pattern coverage | Basic | Extended |
| Portability | High | Medium |
skills/user-memory/
├── SKILL.md
├── minimal/ # Zero-dep shell scripts
│ ├── session-start.sh
│ └── stop-memory.sh
└── mcp/ # Full TypeScript MCP
├── package.json
├── tsconfig.json
└── src/
├── types.ts # Type definitions
├── store.ts # Profile storage + decay
├── context.ts # Context injection builder
├── session.ts # Session continuity
├── prompt.ts # System prompt builder
├── extract-memory.ts # Pattern extraction
├── decay-check.ts # Auto-decay on session start
├── mcp-server.ts # MCP server (13 tools)
└── hooks/
├── session-start.sh
└── stop-memory.sh
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.