ccmemory — Prompts (Verbatim)
1. Skill Prompt: skills/ccmemory/SKILL.md (full, verbatim)
This is the primary agent instruction file. It teaches Claude when and how to use the MCP tools.
# ccmemory: Context Graph Skill
You have access to a persistent context graph that captures decisions, corrections, insights, and other valuable context from Claude Code sessions. The graph has two domains:
- **Domain 1 (Your Specifics)**: High-confidence lived experience — decisions you made, corrections to Claude's understanding, exceptions to rules, failed approaches
- **Domain 2 (Reference Knowledge)**: Curated reference material — cached URLs, PDFs, indexed documentation
## Available Tools
### Recording Context
Use these tools to explicitly capture important context:
- `recordDecision` — Record a decision with rationale, options considered, and revisit triggers
- `recordCorrection` — Record when the user corrects your understanding (highest value!)
- `recordException` — Record when normal rules don't apply in this context
- `recordInsight` — Record realizations, analyses, or strategic conclusions
- `recordQuestion` — Record meaningful Q&A exchanges
- `recordFailedApproach` — Record what was tried and didn't work
- `recordReference` — Record URLs or file paths mentioned
### Querying Context
Use these tools to retrieve relevant context:
- `queryContext` — Get recent context for the current project
- `searchPrecedent` — Full-text search across all context types
- `searchSemantic` — Semantic similarity search using embeddings
- `queryByTopic` — Get all context related to a specific topic
- `traceDecision` — Get full context around a specific decision
- `queryStaleDecisions` — Find decisions that may need review
- `queryFailedApproaches` — Get failed approaches to avoid repeating mistakes
- `getMetrics` — Get context graph metrics (cognitive coefficient, etc.)
### Reference Knowledge
- `cacheUrl` — Fetch and cache a URL as markdown
- `cachePdf` — Extract PDF content to markdown
- `indexReference` — Rebuild the reference knowledge index
- `queryReference` — Semantic search over cached references
- `listReferences` — List all cached reference files
### Management
- `promoteDecisions` — Promote developmental decisions to curated (team-visible) status
### Backfilling Historical Data
- `ccmemory_backfill_conversation` — Import a JSONL conversation file
- `ccmemory_backfill_markdown` — Import a markdown file
**CRITICAL:** When backfilling, only import from the CURRENT project's folder.
The JSONL files are at `~/.claude/projects/` in folders matching the full path with slashes replaced by dashes.
Example: If cwd is `/Users/patrick/theapp`, import ONLY from `~/.claude/projects/-Users-patrick-theapp/`
Do NOT import from `-Users-patrick-theapp-planner` or any other folder.
## Behaviors
### When the user makes a decision
1. Record it immediately with `recordDecision`
2. Include the rationale if stated
3. Note any revisit triggers ("if X changes, reconsider")
4. Check for related prior decisions with `searchPrecedent`
### When the user corrects your understanding
**This is the highest-value capture.** When you get something wrong and the user corrects you:
1. Immediately call `recordCorrection` with:
- `wrong_belief`: What you incorrectly believed
- `right_belief`: The correct understanding
- `severity`: How significant the error was
Example triggers:
- "No, that's not right"
- "Actually, in this project we..."
- "That's the wrong approach because..."
### When the user grants an exception
Record with `recordException` when:
- "In this case, skip the normal..."
- "Just this once, we'll..."
- "Despite the rule about X, here we should Y"
### When something doesn't work
Record with `recordFailedApproach` when:
- "That didn't work"
- "Let's try something else"
- After debugging reveals a dead end
### When insights emerge
Record with `recordInsight` for:
- Realizations about the situation
- Strategic conclusions
- Pattern recognition
- Synthesized understanding
### Session Startup: Check for Pending Imports
**IMPORTANT:** At session start, if the injected context contains a "## Pending History Import" section showing conversations not yet imported, you MUST use `AskUserQuestion` to offer the user import options:
- Option 1: "Import 10 conversations" (Recommended) — imports the 10 most recent quality conversations
- Option 2: "Import all conversations" — imports all pending conversations
- Option 3: "Skip import" — proceed without importing
If the user chooses to import, use `ccmemory_list_conversations` to get the session list, then call `ccmemory_backfill_conversation` for each session (reading the JSONL file content first).
### Proactive Context Use
At the start of each session, context is automatically injected. Additionally:
1. **Check for related context** before giving advice — use `searchPrecedent` or `searchSemantic`
2. **Surface failed approaches** before suggesting solutions — use `queryFailedApproaches`
3. **Reference prior decisions** when they're relevant
4. **Flag stale decisions** that may need review
## The Cognitive Coefficient
The system tracks a "cognitive coefficient" — a measure of how much the accumulated context improves effectiveness. This grows as:
- More decisions are captured and reused
- Corrections are learned from
- Failed approaches prevent repeated mistakes
- The graph density increases
Current project metrics can be retrieved with `getMetrics`.
## Team Mode
In team mode (`CCMEMORY_USER_ID` set):
- `developmental` decisions are only visible to their creator
- `curated` decisions are visible to all team members
- Use `promoteDecisions` to make decisions team-visible after they're validated
## Error Handling: Session Lost
If any ccmemory tool returns a response with `"error": "session_not_found"` and `"ask_user": true`, the MCP server session was lost (typically due to server restart).
**You MUST use AskUserQuestion** to ask the user:
> "The ccmemory server session was lost. Would you like to:"
> - **Retry** — Re-establishes the session (the session_start hook will run again on your next message)
> - **Continue without saving** — Skip saving this context to the knowledge graph
If the user chooses "Retry", inform them the session will re-establish on the next interaction. If they choose "Continue without saving", proceed normally but note that the current context won't be persisted.
2. Hook Script: hooks/session_start.sh (verbatim)
This hook fires at SessionStart, retrieves accumulated context from Neo4j via the MCP server, and injects it into the Claude session.
#!/bin/bash
# SessionStart hook - calls MCP server HTTP endpoint
# Reads JSON from stdin, forwards to server, outputs context to stdout
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/activity_log.sh"
CCMEMORY_URL="${CCMEMORY_URL:-http://localhost:8766}"
HOOK_NAME="session_start"
hookStart "$HOOK_NAME"
# Quality filter thresholds
MIN_SIZE=5000 # 5KB minimum
MAX_SIZE=500000 # 500KB maximum
input=$(cat)
activityLogDebug "hook:$HOOK_NAME" "stdin: ${input:0:200}..."
cwd=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
if [ -z "$cwd" ]; then
activityLogError "hook:$HOOK_NAME" "No cwd in session start input"
echo "# Context Graph: unknown"
echo "Error: No cwd in session start input"
hookEnd "$HOOK_NAME"
exit 0
fi
activityLogDebug "hook:$HOOK_NAME" "cwd=$cwd"
folder_name=$(echo "$cwd" | tr '/' '-')
folder_name="${folder_name#-}"
folder_name="-$folder_name"
claude_dir="$HOME/.claude/projects/$folder_name"
conversation_stems="[]"
if [ -d "$claude_dir" ]; then
# Find files in quality range, sorted by recency, limit 200
stems=$(find "$claude_dir" -name "*.jsonl" -size +${MIN_SIZE}c -size -${MAX_SIZE}c -print0 2>/dev/null | \
xargs -0 ls -t 2>/dev/null | \
head -200 | \
xargs -I{} basename {} .jsonl 2>/dev/null)
if [ -n "$stems" ]; then
stem_count=$(echo "$stems" | wc -l | tr -d ' ')
activityLogDebug "hook:$HOOK_NAME" "Found $stem_count conversation files in quality range"
conversation_stems=$(echo "$stems" | jq -R . 2>/dev/null | jq -s . 2>/dev/null) || conversation_stems="[]"
fi
fi
payload=$(echo "$input" | jq --argjson stems "$conversation_stems" '. + {conversation_stems: $stems}' 2>/dev/null) || payload="$input"
activityLogInfo "hook:$HOOK_NAME" "POST ${CCMEMORY_URL}/hooks/session-start"
response=$(curl -s -X POST "${CCMEMORY_URL}/hooks/session-start" \
-H "Content-Type: application/json" \
-d "$payload" \
--connect-timeout 5 \
--max-time 10 2>/dev/null)
if [ -z "$response" ]; then
project=$(basename "$cwd")
activityLogError "hook:$HOOK_NAME" "Server not responding"
echo "# Context Graph: $project"
echo "Server not running. Start with: ccmemory start"
hookEnd "$HOOK_NAME"
exit 0
fi
activityLogDebug "hook:$HOOK_NAME" "Response: ${response:0:200}..."
context=$(echo "$response" | jq -r '.context // ""' 2>/dev/null)
if [ -n "$context" ]; then
context_len=${#context}
activityLogInfo "hook:$HOOK_NAME" "Got context: ${context_len} chars"
echo "$context"
fi
hookEnd "$HOOK_NAME"
3. Hook Script: hooks/prompt_submit.sh (verbatim)
Fires on UserPromptSubmit once per session to prompt the user to import conversation history:
#!/bin/bash
# UserPromptSubmit hook - checks for pending backfills and reminds Claude to ask
# Only runs once per session (on first prompt)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/activity_log.sh"
CCMEMORY_URL="${CCMEMORY_URL:-http://localhost:8766}"
STATE_DIR="$HOME/.ccmemory"
mkdir -p "$STATE_DIR"
HOOK_NAME="prompt_submit"
hookStart "$HOOK_NAME"
input=$(cat)
session_id=$(echo "$input" | jq -r '.session_id // ""' 2>/dev/null)
cwd=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
activityLogDebug "hook:$HOOK_NAME" "session_id=$session_id cwd=$cwd"
# Track if we've already reminded this session
state_file="$STATE_DIR/prompted-$session_id"
if [ -f "$state_file" ]; then
activityLogDebug "hook:$HOOK_NAME" "Already prompted this session, skipping"
hookEnd "$HOOK_NAME"
exit 0
fi
# Check for pending backfills (quick local check)
folder_name=$(echo "$cwd" | tr '/' '-')
folder_name="${folder_name#-}"
folder_name="-$folder_name"
claude_dir="$HOME/.claude/projects/$folder_name"
MIN_SIZE=5000
MAX_SIZE=500000
pending_count=0
if [ -d "$claude_dir" ]; then
pending_count=$(find "$claude_dir" -name "*.jsonl" -size +${MIN_SIZE}c -size -${MAX_SIZE}c 2>/dev/null | wc -l | tr -d ' ')
fi
activityLogDebug "hook:$HOOK_NAME" "Pending imports: $pending_count"
# Mark this session as prompted
touch "$state_file"
# If there are pending imports, inject a reminder
if [ "$pending_count" -gt 0 ]; then
activityLogInfo "hook:$HOOK_NAME" "Injecting import reminder for $pending_count files"
cat << EOF
SYSTEM REMINDER: There are $pending_count conversation history files that could be imported into ccmemory.
Before responding to the user's request, use AskUserQuestion to offer:
- "Import 10 recent conversations" (Recommended)
- "Import all $pending_count conversations"
- "Skip import"
This is a one-time prompt per session. After user responds, proceed normally.
EOF
fi
hookEnd "$HOOK_NAME"
@mcp.tool()
@logTool
async def recordDecision(
description: str,
rationale: Optional[str] = None,
options_considered: Optional[str] = None,
revisit_trigger: Optional[str] = None,
sets_precedent: bool = False,
topics: Optional[list[str]] = None,
) -> dict:
"""Record a decision to the context graph.
Args:
description: What was decided
rationale: Why this choice was made
options_considered: What alternatives were evaluated
revisit_trigger: Conditions that should prompt reconsideration
sets_precedent: Whether this decision should guide future similar decisions
topics: Topics/components this decision relates to (e.g., ['auth', 'api'])
"""
project = getCurrentProject()
if not project:
return _projectError()
client = getClient()
decision_id = f"decision-{uuid.uuid4().hex[:8]}"
text_for_embedding = f"{description} {rationale or ''}"
embedding = getEmbedding(text_for_embedding)
kwargs = {
"detection_method": "explicit_command",
"detection_confidence": 1.0,
}
if rationale:
kwargs["rationale"] = rationale
if options_considered:
kwargs["options_considered"] = options_considered
if revisit_trigger:
kwargs["revisit_trigger"] = revisit_trigger
if sets_precedent:
kwargs["sets_precedent"] = sets_precedent
result = client.createDecision(
decision_id=decision_id,
project=project,
description=description,
embedding=embedding,
topics=topics or [],
**kwargs,
)
return {"decision_id": decision_id, "status": "recorded", **result}
Demonstrates the two-stage retrieval pattern: Ollama embeddings for candidates, then LLM reranking.
@mcp.tool()
@logTool
async def searchSemantic(
query: str, limit: int = 10, include_team: bool = True
) -> dict:
"""Semantic similarity search across decisions, corrections, and insights.
Uses local embeddings for candidate retrieval, then Claude for reranking.
Args:
query: Natural language query
limit: Maximum results to return
include_team: Whether to include curated team decisions
"""
client = getClient()
project = _getProject()
embedding = getEmbedding(query)
raw_limit = min(limit * 2, 20)
results = client.searchSemantic(
embedding, project, limit=raw_limit, include_team=include_team
)
candidates = []
for category, items in results.items():
for item in items:
candidates.append(
{"data": item[0], "score": item[1], "category": category}
)
candidates.sort(key=lambda x: x["score"], reverse=True)
reranked = await rerank(query, candidates, limit=limit)
formatted = {}
for item in reranked:
cat = item.get("category", "unknown")
if cat not in formatted:
formatted[cat] = []
formatted[cat].append({"data": item["data"], "score": item["score"]})
return {"project": project, "query": query, "results": formatted}
6. Project CLAUDE.md (excerpt — first-time setup instructions for Claude)
From CLAUDE.md in the root of the repo (this CLAUDE.md is for developing ccmemory itself, not for users):
Before making design decisions, consult these in order:
doc/clarifications/ — Binding decisions that override other docs
doc/PROJECT_VISION.md — Intended behavior and architecture
- Gupta/Koratana Articles — External strategy ccmemory implements
Key principles from these sources:
- Event clock, not state clock — Capture reasoning/decisions, not just current state
- Decision traces, not containers — Organize by time + entity links, not sessions
- Schema as output — Let structure emerge from use, don't over-specify upfront
- World models, not retrieval — Goal is simulation ("what if?"), not just search
Prompting Techniques Observed
Trigger-phrase enumeration: SKILL.md lists exact natural language phrases that should trigger tool calls (e.g., "No, that's not right", "Actually, in this project we...", "That didn't work") — grounding abstract behavior rules in concrete linguistic signals.
Priority labeling: Corrections are explicitly labeled "highest-value capture" and "highest priority" to guide agent attention ordering.
SYSTEM REMINDER injection via hook: The prompt_submit.sh hook injects a SYSTEM REMINDER block directly into the user's prompt stream — a hook-based prompt injection pattern that doesn't require any changes to CLAUDE.md.
Error recovery prompting: The skill includes a complete error-recovery sub-protocol for when the MCP session is lost, including exact AskUserQuestion text to present to the user.
Two-stage retrieval: Semantic search uses local Ollama embeddings for fast candidate retrieval, then passes candidates to a Claude LLM reranker — a RAG-plus-rerank pattern expressed entirely within an MCP tool.