# https://github.com/rickbassham/commonplace 项目说明书

生成时间：2026-05-18 04:25:02 UTC

## 目录

- [Introduction to Commonplace](#introduction)
- [Quick Start Guide](#quickstart)
- [System Architecture](#system-architecture)
- [Project Structure](#project-structure)
- [Memory Data Model](#memory-data-model)
- [Memory Types Taxonomy](#memory-types-taxonomy)
- [Memory Store](#memory-store)
- [Embedding System](#embedding-system)
- [Semantic Search](#semantic-search)
- [Graph Features](#graph-features)
- [MCP Tool Reference](#mcp-tool-reference)
- [Server Handlers](#server-handlers)

<a id='introduction'></a>

## Introduction to Commonplace

### 相关页面

相关主题：[Quick Start Guide](#quickstart), [System Architecture](#system-architecture), [Memory Data Model](#memory-data-model)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)
- [CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)
- [src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [src/store/atomic-write.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/atomic-write.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [CHANGELOG.md](https://github.com/rickbassham/commonplace/blob/main/CHANGELOG.md)
- [CONTRIBUTING.md](https://github.com/rickbassham/commonplace/blob/main/CONTRIBUTING.md)
</details>

# Introduction to Commonplace

Commonplace is a local-first personal knowledge management system that functions as an MCP (Model Context Protocol) server. It enables AI agents to persistently store, retrieve, and reason about notes using semantic search, all without requiring external API calls or cloud services. 资料来源：[README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)

## Overview

Commonplace operates as a flat directory of Markdown files with YAML frontmatter. Each note is a plain `.md` file, and embeddings are stored in `.embedding` sidecar files generated locally using `transformers.js`. This architecture ensures that:

- **Notes are human-readable and portable** — since memories are plain Markdown files, they can be edited, backed up, or version-controlled using standard tools.
- **Embeddings are derived, not the source of truth** — `.embedding` files are regenerable from `.md` files at any time and may be deleted or rebuilt without data loss. 资料来源：[CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)

## Architecture

```mermaid
graph TD
    subgraph "MCP Client Layer"
        A["Claude Code / AI Agent"]
    end
    
    subgraph "MCP Server Layer"
        B["commonplace MCP Server<br/>stdio interface"]
    end
    
    subgraph "Store Layer"
        C["MemoryStore<br/>src/store/memory-store.ts"]
        D["MemoryGraph<br/>Graph relationships"]
        E["Embedder<br/>transformers.js + bge-base-en-v1.5"]
    end
    
    subgraph "Filesystem Layer"
        F["COMMONPLACE_USER_DIR<br/>~/.commonplace"]
        G["COMMONPLACE_PROJECT_DIR<br/>.commonplace"]
    end
    
    A -->|"MCP Protocol"| B
    B --> C
    C --> D
    C --> E
    C --> F
    C --> G
    
    F -.->|"user scope"| C
    G -.->|"project scope"| C
```

### Key Components

| Component | File | Responsibility |
|-----------|------|-----------------|
| MCP Server | `src/server/handlers.ts` | Handles MCP protocol communication, registers tools |
| MemoryStore | `src/store/memory-store.ts` | Manages CRUD operations, scanning, searching |
| MemoryGraph | `src/store/memory-store.ts` | In-memory graph for relations and mentions |
| Embedder | `src/store/embedder.ts` | Local embedding generation via transformers.js |
| Memory I/O | `src/store/memory.ts` | Parses/serializes `.md` files with YAML frontmatter |
| Atomic Write | `src/store/atomic-write.ts` | Safe file writes to prevent corruption |
| Mentions | `src/store/mentions.ts` | Extracts `[[name]]` references from body content |

## Memory Model

### Memory Types

Memories are classified into a four-element taxonomy defined at 资料来源：[README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)

| Type | Purpose | Example |
|------|---------|---------|
| `user` | Personal rules, preferences, identity facts about the human operator | Coding style preferences, work schedule |
| `feedback` | Corrections and lessons learned from prior agent behavior | "Don't shrink scope unilaterally" |
| `project` | Per-project context like architecture notes, repo conventions | API conventions, decision records |
| `reference` | Durable, neutral knowledge: API shapes, formulas, citations | Library documentation, formulas |

### Memory File Format

A memory is a Markdown file with YAML frontmatter:

```yaml
---
name: feedback_scope
description: Don't shrink scope unilaterally
type: feedback
relations:
  - to: scope_management
    type: builds-on
supersedes:
  - old_scope_note
---
<Body content here. Can contain [[other_memory_name]] mentions.>
```

**Frontmatter Fields:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Memory identifier matching `^[a-z0-9_]+$` |
| `description` | string | Yes | Brief summary of the memory |
| `type` | enum | Yes | One of: `user`, `feedback`, `project`, `reference` |
| `relations` | array | No | List of `{to, type}` edges to other memories |
| `supersedes` | array | No | List of memory names this memory replaces |

The `contentSha` is computed from `${type}\n${name}\n${description}\n${body}` — graph fields do not affect the hash, so adding or removing edges does not invalidate embeddings. 资料来源：[src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

### Relation Types

Graph edges support the following relation types 资料来源：[src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts):

| Relation Type | Description |
|---------------|-------------|
| `builds-on` | This memory extends or depends on another |
| `related-to` | Tangentially connected memories |
| `contradicts` | This memory opposes another |
| `child-of` | This memory is a sub-component of another |
| `mentions` | Body contains `[[name]]` reference |

## MCP Tools

Commonplace exposes the following tools via MCP protocol 资料来源：[CHANGELOG.md](https://github.com/rickbassham/commonplace/blob/main/CHANGELOG.md):

### memory_save

Saves a memory as a Markdown file with YAML frontmatter and generates an embedding sidecar. Refuses to overwrite existing entries.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | Yes | Memory name, must match `^[a-z0-9_]+$` |
| `type` | enum | Yes | One of the four memory types |
| `description` | string | Yes | Brief description |
| `body` | string | Yes | Full memory content |
| `scope` | enum | No | `'user'` (default) or `'project'` |

### memory_list

Returns all memories of a given type.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | enum | Yes | Filter by memory type |
| `scope` | enum | No | `'user'`, `'project'`, or both (default) |

### memory_delete

Removes a memory and its embedding sidecar.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | Yes | Memory name to delete |
| `scope` | enum | No | `'user'` (default) or `'project'` |

### memory_search

Performs semantic search across memories using cosine similarity.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `query` | string | Yes | Natural language query |
| `type` | enum | No | Filter by memory type |
| `limit` | number | No | Maximum results (default: 5) |
| `scope` | enum | No | `'user'`, `'project'`, or both (default) |

### memory_link

Creates a directed edge between two memories.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `from` | string | Yes | Source memory name |
| `to` | string | Yes | Target memory name |
| `type` | enum | Yes | One of: `builds-on`, `related-to`, `contradicts`, `child-of` |
| `scope` | enum | No | `'user'` (default) or `'project'` |

### memory_unlink

Removes an edge between two memories.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `from` | string | Yes | Source memory name |
| `to` | string | Yes | Target memory name |
| `type` | enum | No | Specific edge type to remove; omit to remove all |

## CLI Commands

The `commonplace` CLI provides additional management functions 资料来源：[src/cli/migrate.ts](https://github.com/rickbassham/commonplace/blob/main/src/cli/migrate.ts)

### migrate

The `migrate` command handles embedding sidecar management and migration between systems.

```
commonplace migrate <dir> [--dry-run] [--prune-dangling]
```

**Functions:**

1. **Embeds** any `.md` whose `.embedding` sidecar is missing
2. **Re-embeds** any `.md` whose sidecar is stale (contentSha, modelId, or dim mismatch)
3. **Removes** orphaned `.embedding` files whose matching `.md` is gone
4. **Prunes** dangling edges when `--prune-dangling` is specified

### Import from Claude Code

```
commonplace migrate --from claude-code        # import memories
commonplace migrate --from claude-code --dry-run  # preview import
```

Import copies compatible `.md` files from Claude Code's auto-memory locations (`~/.claude/projects/*/memory/*.md`) into `COMMONPLACE_USER_DIR`.

**Conflict Policy:** Skip and report — if a memory already exists, the source is skipped without overwrite. Use `--dry-run` to preview conflicts.

## Scopes and Directory Structure

Commonplace supports two independent scopes 资料来源：[src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts):

```mermaid
graph LR
    A["~/.commonplace<br/>COMMONPLACE_USER_DIR"] <--> B["Cross-project memories<br/>user scope"]
    C[".commonplace<br/>COMMONPLACE_PROJECT_DIR"] <--> D["Project-specific memories<br/>project scope"]
```

| Scope | Directory | Contents |
|-------|-----------|----------|
| `user` | `~/.commonplace/` | Cross-project memories (preferences, general feedback) |
| `project` | `.commonplace/` | Per-project memories (conventions, decisions) |

When `scope` is omitted on tool calls, reads merge across both stores while writes default to `user`.

## Search Behavior

Search results include 资料来源：[src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts):

| Field | Description |
|-------|-------------|
| `name` | Memory identifier |
| `type` | Memory type |
| `description` | Brief summary |
| `body` | Full body content (never truncated) |
| `score` | Cosine similarity, rounded to 3 decimals |
| `relations` | Outgoing graph edges from this memory |

**Expand Types:** By default, search enriches results with `builds-on` and `related-to` neighbors. Pass `expandTypes: ['supersedes']` to include superseded entries explicitly.

**Supersedes Handling:** Entries listed in another memory's `supersedes` field are excluded by default from search results. Pass `excludeSuperseded: false` to include them.

## [[name]] Mentions

Body mentions are extracted using a regex tokenizer 资料来源：[src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts):

- Only `<name>` tokens matching `^[a-z0-9_]+$` are recognized
- Mentions are added as directed edges in the MemoryGraph
- Extraction is controlled by `COMMONPLACE_EXTRACT_MENTIONS` env var (default: on)

Mentions are **not**:
- Rendered as wiki-style links
- Aware of code fences or inline code blocks (intentional)
- Surfaced through MCP tool responses (DAR-929/DAR-932)

## Installation and Setup

After installing Commonplace:

```bash
npm install -g commonplace
# or
pnpm add -g commonplace
```

Configure Claude Code to use the MCP server by adding to your settings. After restarting Claude Code sessions, the four memory tools become available. 资料来源：[README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)

## Workflow Summary

```mermaid
graph TD
    A["Agent starts task"] --> B["memory_search or memory_list"]
    B --> C{"Results found?"}
    C -->|Yes| D["Use memory content"]
    C -->|No| E["Gather new information"]
    E --> F["memory_save<br/>with appropriate type"]
    F --> G["Embedding generated<br/>and stored"]
    D --> H["Complete task"]
    G --> H
```

## Contributing

This project follows a squash-merge workflow with the following requirements 资料来源：[CONTRIBUTING.md](https://github.com/rickbassham/commonplace/blob/main/CONTRIBUTING.md):

1. Branch from `main` with descriptive names
2. No direct pushes to `main` — all changes via PR
3. CI must pass: `make typecheck`, `make lint`, `make build`, `make test`
4. All PR conversations must be resolved before merge
5. Squash-merge only to keep history linear

The project dogfoods Commonplace for its own memory — developers are encouraged to use `mcp__commonplace__memory_save` for cross-project and project-specific lessons. 资料来源：[CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)

---

<a id='quickstart'></a>

## Quick Start Guide

### 相关页面

相关主题：[Introduction to Commonplace](#introduction), [MCP Tool Reference](#mcp-tool-reference)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)
- [CONTRIBUTING.md](https://github.com/rickbassham/commonplace/blob/main/CONTRIBUTING.md)
- [CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)
- [src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts)
- [src/cli/migrate.ts](https://github.com/rickbassham/commonplace/blob/main/src/cli/migrate.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
</details>

# Quick Start Guide

Welcome to **Commonplace** — a persistent, embeddable memory system for AI agents that operates on plain markdown files with YAML frontmatter.

## Prerequisites

Before getting started, ensure your environment meets the following requirements:

| Requirement | Version | Notes |
|-------------|---------|-------|
| Node.js | 20 or 22 | CI runs on both versions |
| pnpm | latest | Package manager for this project |
| Claude Code | compatible | For MCP tool integration |

资料来源：[CONTRIBUTING.md:1-20]()

## Installation

### From Source

Clone the repository and install dependencies:

```bash
git clone https://github.com/rickbassham/commonplace.git
cd commonplace
pnpm install
```

### Build the Project

```bash
make build
```

This compiles the TypeScript source and prepares the CLI binary.

资料来源：[CONTRIBUTING.md:22-30]()

## Core Concepts

### Memory Types

Every memory in Commonplace has a `type` field that categorizes its content:

| Type | Purpose | Example |
|------|---------|---------|
| `user` | Personal rules, preferences, identity facts | `preferred_code_style`, `keyboard_shortcuts` |
| `feedback` | Corrections and lessons learned | `dont_shrink_scope_unilaterally`, `verify_api_response` |
| `project` | Per-project context, architecture, conventions | `monorepo_structure`, `db_schema_notes` |
| `reference` | Durable neutral knowledge, API docs | `openapi_spec_location`, `rate_limit_formulas` |

资料来源：[README.md:1-20]()

### Memory Scopes

Commonplace supports two parallel memory stores:

```mermaid
graph TD
    A[Claude Code Session] --> B{Scope Selection}
    B -->|user| C[User Store]
    B -->|project| D[Project Store]
    C --> E[~/.commonplace/memory]
    D --> F[COMMONPLACE_PROJECT_DIR<br/>or<br/>.commonplace/memory]
    
    style C fill:#e1f5fe
    style D fill:#fff3e0
```

| Scope | Location | Use Case |
|-------|----------|----------|
| `user` | `~/.commonplace/memory` (default) | Cross-project memories, personal preferences |
| `project` | Project root `.commonplace/memory` | Repository-specific context |

资料来源：[README.md:45-70]()

### Memory File Structure

Each memory is stored as a markdown file with YAML frontmatter:

```yaml
---
name: my_memory_name
description: Brief description of this memory
type: feedback
relations:
  - to: other_memory
    type: builds-on
supersedes:
  - old_memory_name
---
Body content goes here. Can contain [[wiki-links]] to other memories.
```

资料来源：[src/store/memory.ts:35-55]()

## CLI Commands

### Migrate Command

The `commonplace migrate` command manages your memory directory:

```bash
# Scan and embed all memories
commonplace migrate <dir>

# Preview changes without writing
commonplace migrate <dir> --dry-run

# Remove dangling references
commonplace migrate <dir> --prune-dangling
```

#### Migration Workflow

```mermaid
graph TD
    A[commonplace migrate <dir>] --> B{Orphan Detection}
    B -->|Sidecar exists<br/>no .md file| C[Clean up orphan]
    B -->|.md exists<br/>no sidecar| D[Embed memory]
    B -->|Sidecar stale| E[Re-embed memory]
    B -->|Both fresh| F[Mark unchanged]
    
    C --> G[Report: orphaned]
    D --> H[Report: embedded]
    E --> I[Report: re-embedded]
    F --> J[Report: loaded]
    
    style G fill:#c8e6c9
    style H fill:#c8e6c9
    style I fill:#c8e6c9
    style J fill:#c8e6c9
```

资料来源：[src/cli/migrate.ts:50-85]()

### Import from Claude Code

Import memories from Claude Code's auto-memory directories:

```bash
# Detect candidates
commonplace migrate --from claude-code

# Perform import
commonplace migrate --from claude-code --auto
```

The import:
1. Scans `~/.claude/projects/*/memory/*.md` for candidates
2. Copies compatible files to your user store (skips if target exists)
3. Runs embed pass on newly imported files

资料来源：[src/cli/migrate.ts:120-180]()

## MCP Tool Reference

After installation, four tools become available in Claude Code:

### memory_save

Save a new memory:

```json
{
  "name": "my_memory",
  "type": "project",
  "body": "Detailed content here..."
}
```

### memory_search

Search memories by semantic similarity:

```json
{
  "query": "scope handshake",
  "limit": 3,
  "type": "feedback"
}
```

### memory_list

List all memories, optionally filtered:

```json
{
  "type": "reference",
  "includeSuperseded": false
}
```

### memory_link / memory_unlink

Create or remove relationships between memories:

```json
// memory_link
{
  "from": "architecture_overview",
  "to": "feedback_scope",
  "type": "builds-on"
}
```

资料来源：[README.md:100-200]()

## Search and Expansion

### One-Hop Expansion

When searching, you can expand results to include connected memories:

```mermaid
graph LR
    A[Query] --> B[Direct Hits]
    B --> C{expand: 'one-hop'}
    C -->|Enabled| D[Outbound Edges]
    D --> E[Neighbors]
    C -->|Disabled| F[Direct Only]
    
    E -.->|via: {source, edge}| B
```

| Parameter | Values | Default | Description |
|-----------|--------|---------|-------------|
| `expand` | `none`, `one-hop` | `none` | Expansion mode |
| `expandTypes` | edge types array | `['builds-on', 'related-to']` | Which edges to follow |
| `expandLimit` | integer | `2` | Max neighbors per hit |

资料来源：[README.md:180-220]()

## Development Workflow

### Make Targets

| Target | Purpose |
|--------|---------|
| `make typecheck` | Run TypeScript type checking |
| `make lint` | Run ESLint |
| `make build` | Compile TypeScript to JavaScript |
| `make test` | Run test suite |
| `make audit` | Security audit (non-blocking) |

### Branch Strategy

1. Branch from `main` with a descriptive name
2. Open a PR against `main`
3. Ensure CI passes before merge
4. Squash-merge to keep history linear

资料来源：[CONTRIBUTING.md:5-35]()

## Configuration

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `COMMONPLACE_USER_DIR` | `~/.commonplace/memory` | User memory store location |
| `COMMONPLACE_PROJECT_DIR` | (project root) | Project memory store override |
| `COMMONPLACE_EXTRACT_MENTIONS` | `'true'` | Enable `[[name]]` extraction |

资料来源：[src/store/mentions.ts:10-25]()

## Troubleshooting

### Embedding Sidecars

The `.embedding` sidecar files are derived and regenerable. If embeddings seem stale:

```bash
# Rebuild all embeddings
commonplace migrate <dir>
```

### Dangling References

If memories reference names that no longer exist:

```bash
# Preview dangling removal
commonplace migrate <dir> --dry-run --prune-dangling

# Execute cleanup
commonplace migrate <dir> --prune-dangling
```

资料来源：[src/cli/migrate.ts:60-80]()

## See Also

- [Memory Store Architecture](../architecture/memory-store.md) — Detailed internal design
- [MCP Integration](../integration/mcp-tools.md) — Tool contracts and responses
- [Contributing Guide](../contributing/CONTRIBUTING.md) — Development standards

---

<a id='system-architecture'></a>

## System Architecture

### 相关页面

相关主题：[Project Structure](#project-structure), [Embedding System](#embedding-system), [Memory Store](#memory-store)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/atomic-write.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/atomic-write.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)

Note: `src/embedder/index.ts`, `src/server/index.ts`, and `src/server/server.ts` were referenced but not included in the provided context.

</details>

# System Architecture

## Overview

Commonplace is a persistent memory system for AI agents that stores contextual knowledge as markdown files with YAML frontmatter. The system provides semantic search across memories using embeddings, a graph-based relationship model for linking related memories, and a dual-store architecture supporting both user-level and project-level contexts.

The architecture is designed around the principle that **markdown files are the source of truth** — `.embedding` sidecar files are derived and regenerable at any time. 资料来源：[CLAUDE.md]()

## Core Components

| Component | File | Purpose |
|-----------|------|---------|
| MemoryStore | `src/store/memory-store.ts` | Central store managing in-memory index, file I/O, search, and graph edges |
| Memory I/O | `src/store/memory.ts` | YAML frontmatter parsing, serialization, content hashing |
| Atomic Write | `src/store/atomic-write.ts` | Safe file writes via temp-file + rename pattern |
| Mentions Extractor | `src/store/mentions.ts` | Extracts `[[name]]` references from memory bodies |
| MCP Server | `src/server/handlers.ts` | Tool handlers exposing memory operations via Model Context Protocol |
| CLI Entry | `src/index.ts` | Command-line interface dispatcher |

## Memory Model

### Data Structure

Each memory is a markdown file with the following structure:

```yaml
---
name: memory_name
description: Brief description
type: user | feedback | project | reference
relations:           # DAR-925, optional
  - to: other_name
    type: builds-on
supersedes:          # DAR-925, optional
  - old_name
---
<body content>
```

The `contentSha` is computed from the canonical form `${type}\n${name}\n${description}\n${body}` — intentionally excluding graph fields (`relations`, `supersedes`) so adding or removing edges does not invalidate embeddings. 资料来源：[src/store/memory.ts:30-35]()

### Memory Types

| Type | Purpose |
|------|---------|
| `user` | Personal rules, preferences, identity facts about the human operator |
| `feedback` | Corrections and lessons learned from prior agent behaviour |
| `project` | Per-project context (architecture notes, repo conventions) |
| `reference` | Durable, neutral knowledge (API shapes, formulas, citations) |

资料来源：[README.md]()

## Dual-Store Architecture

Commonplace loads up to two memory stores side by side:

```mermaid
graph TD
    subgraph User_Store["User Store (always loaded)"]
        U[~/.commonplace/memory]
    end
    
    subgraph Project_Store["Project Store (conditional)"]
        P[.commonplace/memory in project root]
    end
    
    MS[MemoryStore instances]
    Search[Search merges across both stores]
    
    U --> MS
    P --> MS
    MS --> Search
```

### Store Detection Priority

1. `COMMONPLACE_PROJECT_DIR` env var (explicit override, always wins)
2. MCP `roots/list` response for project root detection
3. Current working directory traversal

资料来源：[README.md]()

### Scope Behavior

- **Reads** merge hits across both stores by descending score
- **Writes** default to `user` scope when `scope` argument is omitted
- Search results carry a `scope: 'user' | 'project'` tag identifying the source store

## MemoryStore Architecture

### Core Responsibilities

The `MemoryStore` class is the central orchestrator responsible for:

1. **Indexing** — maintaining an in-memory array of memory entries
2. **File I/O** — reading/writing `.md` files with YAML frontmatter
3. **Embedding** — coordinating with the embedder for semantic search
4. **Graph edges** — managing `relations[]` and `supersedes[]` arrays
5. **Caching** — detecting directory changes to avoid redundant rescans

资料来源：[src/store/memory-store.ts]()

### Scan and Cache Invalidation

The `scan()` method rebuilds the in-memory index by walking the directory. Sidecar freshness is determined by:

```mermaid
graph TD
    A[Check .embedding exists] --> B{Decode success?}
    B -->|No| C[Re-embed]
    B -->|Yes| D{modelId matches?}
    D -->|No| C
    D -->|Yes| E{dim matches?}
    E -->|No| C
    E -->|Yes| F{contentSha matches?}
    F -->|No| C
    F -->|Yes| G[Sidecar is fresh, reuse]
    
    C --> H[Write new .embedding sidecar]
    G --> I[Use existing sidecar]
```

Reuse criteria per memory:
- `.embedding` file exists and decodes (magic + version + length)
- `decoded.modelId === embedder.modelId`
- `decoded.dim === embedder.dim`
- `decoded.contentSha === contentSha(memoryAsRead)`

资料来源：[src/store/memory-store.ts:60-75]()

### Orphan Cleanup (DAR-918)

After the per-`.md` pass, `scan()` walks the directory again and removes any `.embedding` file whose matching `<name>.md` is missing. This prevents dead weight accumulation from deleted memories.

```mermaid
graph LR
    A[Scan directory] --> B[Find all .md files]
    A --> C[Find all .embedding files]
    B --> D[Build .md set]
    C --> E[For each .embedding]
    E --> F{basename.md exists?}
    F -->|No| G[Delete orphan sidecar]
    F -->|Yes| H[Keep sidecar]
```

## Graph System (DAR-925)

### Edge Types

| Type | Storage Location | Behavior |
|------|------------------|----------|
| `related-to` | `relations[]` | Deduped by `(to, type)` |
| `builds-on` | `relations[]` | Deduped by `(to, type)` |
| `contradicts` | `relations[]` | Deduped by `(to, type)` |
| `child-of` | `relations[]` | Deduped by `(to, type)` |
| `supersedes` | `supersedes[]` | Deduped by name |
| `mentions` | Auto-extracted | From `[[name]]` body tokens |

资料来源：[src/server/handlers.ts:20-30]()

### Edge Operations

The `MemoryStore` provides two primary graph mutation methods:

**`linkEdge(from, to, type)`**
- Validates: no self-edges, both memories loaded, no duplicate edges
- Writes source `.md` through `atomicWrite`
- Updates in-memory entry in place
- Calls `MemoryGraph.addEdge()` incrementally

资料来源：[src/store/memory-store.ts:linkEdge-method]()

**`unlinkEdge(from, to, type?)`**
- Removes matching entry from `relations[]` or `supersedes[]`
- When `type` omitted, removes ALL edges from → to
- No-op if edge doesn't exist

### Default Expand Types

When searching with graph expansion, the default edge types are `['builds-on', 'related-to']`. Other types require explicit opt-in:

```typescript
export const DEFAULT_EXPAND_TYPES: readonly EdgeType[] = ['builds-on', 'related-to'] as const;
```

资料来源：[src/server/handlers.ts:15-20]()

## Atomic Write System (DAR-923)

### Pattern

The atomic write uses a tmpfile + rename sequence:

1. Generate tmp name: `<basename>.<8-hex-chars>.tmp`
2. Write data to tmp file in same directory
3. Rename tmp → target (atomic on same filesystem)

### Safety Guards

```mermaid
graph TD
    A[atomicWrite called] --> B{Same filesystem?}
    B -->|No| C[Throw: cross-fs rename not atomic]
    B -->|Yes| D[Write to tmp file]
    D --> E[Rename tmp → target]
    E --> F[Release tmp file handle]
    G[On error] --> H[Cleanup tmp file]
```

**Cross-filesystem guard**: The implementation stats both the target directory and tmpfile directory, requiring matching `dev` (device number). 资料来源：[src/store/atomic-write.ts]()

### Advisory Locking

Memory operations acquire a proper-lockfile lock before writing:

- Lock target: `<dir>/.locks/<name>.lock`
- `stale: 5000` — orphaned locks from crashed processes are reclaimed after 5 seconds
- `realpath: false` — works even when target file doesn't exist yet
- `ELOCKED` error translated to: `MemoryStore: lock for memory '<name>' is busy`

资料来源：[src/store/memory-store.ts:acquireNameLock]()

## Embedding Pipeline

### Mentions Extraction (DAR-927)

The `[[name]]` tokenizer extracts unique mention targets from memory bodies:

```typescript
export const extractMentions = (body: string): string[]
```

- Only extracts tokens matching `^[a-z0-9_]+$`
- Returns unique names in first-occurrence order
- Gated by `COMMONPLACE_EXTRACT_MENTIONS` env var (default: on)

资料来源：[src/store/mentions.ts]()

### Embedding Flow

```mermaid
graph TD
    A[memory_save or scan] --> B[extractMentions from body]
    B --> C[MemoryGraph.addMentionsEdge for each]
    D[Save/write memory] --> E[embedder.embed description + body]
    E --> F[Write .embedding sidecar]
    G[Search query] --> H[embedder.embed query]
    H --> I[Compare vectors with store]
    I --> J[Return ranked results]
```

## CLI Architecture

### Command Dispatch

The CLI entry point (`src/index.ts`) dispatches to subcommands:

| Command | Handler | Purpose |
|---------|---------|---------|
| `commonplace migrate` | `migrateMain` | Embed missing/stale sidecars, import from Claude Code |
| `commonplace graph` | `graphMain` | Graph operations (DAR-933) |

```mermaid
graph TD
    A[commonplace CLI] --> B{First arg}
    B -->|graph| C[graphMain]
    B -->|migrate| D[migrateMain]
    B -->|unknown| E[Error: unknown subcommand]
    
    C --> F[embedderFactory]
    D --> F
    F --> G[MemoryStore.scan]
```

The embedder is constructed via a lazy factory to avoid model loading until needed. 资料来源：[src/index.ts]()

### Migration Modes

**Scan/Embed Pass**:
1. Load all `.md` files
2. For each, check if `.embedding` sidecar is fresh
3. Re-embed stale or missing sidecars
4. Remove orphan sidecars (`.embedding` with no matching `.md`)

**Import from Claude Code**:
1. Detect `~/.claude/projects/*/memory/*.md` files
2. Copy non-colliding files to `COMMONPLACE_USER_DIR`
3. Run scan/embed pass on imported files

资料来源：[src/cli/migrate.ts]()

## MCP Server Integration

The MCP handlers expose memory operations as tools:

| Tool | Scope | Description |
|------|-------|-------------|
| `memory_save` | user/project | Save memory with auto-embed |
| `memory_search` | user/project | Semantic search with optional graph expansion |
| `memory_list` | user/project | List memories by type filter |
| `memory_read` | user/project | Retrieve single memory |
| `memory_delete` | user/project | Delete memory and sidecar |
| `memory_link` | user/project | Create graph edge |
| `memory_unlink` | user/project | Remove graph edge |
| `memory_path` | user/project | Get filesystem path for memory |

资料来源：[README.md](), [src/server/handlers.ts]()

### Search Result Structure

```typescript
export interface MemorySearchMatch {
  name: string;
  type: MemoryType;
  description: string;
  body: string;        // Full body, never truncated
  score: number;       // Cosine similarity, 3 decimals
  relations: Relation[];
}
```

The body is returned verbatim per DAR-920 ac-3 — no truncation, summarization, or transformation. 资料来源：[src/server/handlers.ts:35-50]()

## Data Flow Summary

```mermaid
graph TD
    subgraph External["External Consumers"]
        MCP[MCP Tools]
        CLI[CLI Commands]
    end
    
    subgraph Core["MemoryStore Core"]
        Index[in-memory index]
        Graph[MemoryGraph]
        Embedder[Embedder]
    end
    
    subgraph Persistence["Persistence Layer"]
        Files[.md files]
        Sidecars[.embedding sidecars]
        Locks[.locks/ directory]
    end
    
    MCP --> Core
    CLI --> Core
    Core --> Index
    Core --> Graph
    Core --> Embedder
    Index --> Files
    Index --> Sidecars
    Core --> Locks
```

## Key Design Decisions

| Decision | Rationale |
|----------|------------|
| Markdown as source of truth | Human-readable, version-control friendly, no lock-in |
| SHA-256 content hash | Deterministic identity for embedding invalidation |
| Graph fields excluded from sha | Adding/removing edges doesn't invalidate embeddings |
| Dual-store architecture | Separates personal context from project-specific knowledge |
| Atomic writes + advisory locks | Safe concurrent access without database |
| Lazy embedder factory | Avoid expensive model loading on non-embed subcommands |

资料来源：[src/store/memory.ts:contentSha](), [CLAUDE.md]()

---

<a id='project-structure'></a>

## Project Structure

### 相关页面

相关主题：[System Architecture](#system-architecture), [Memory Store](#memory-store)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/cli/migrate.ts](https://github.com/rickbassham/commonplace/blob/main/src/cli/migrate.ts)
- [src/store/atomic-write.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/atomic-write.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [CONTRIBUTING.md](https://github.com/rickbassham/commonplace/blob/main/CONTRIBUTING.md)
- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)
</details>

# Project Structure

## Overview

Commonplace is a personal knowledge management system designed for AI agents operating via Claude Code. It provides persistent memory storage through markdown files with YAML frontmatter, vector embeddings for semantic search, and a graph structure for tracking relationships between memories.

The project is structured as a Node.js CLI application with an MCP (Model Context Protocol) server component. 资料来源：[src/index.ts:1-25]()

## Directory Layout

```
commonplace/
├── src/
│   ├── index.ts              # CLI entry point and dispatcher
│   ├── bin/
│   │   ├── boot.ts           # Boot process for MCP server
│   │   └── commonplace-mcp.ts # MCP server stdio interface
│   ├── cli/
│   │   ├── migrate.ts         # Migration command implementation
│   │   └── graph.ts           # Graph query command
│   ├── server/
│   │   └── server.ts          # MCP protocol server
│   ├── store/
│   │   ├── memory-store.ts    # Core memory management
│   │   ├── memory.ts          # Memory I/O and serialization
│   │   ├── atomic-write.ts    # Atomic file operations
│   │   └── mentions.ts        # [[name]] mention extraction
│   └── embedder/
│       └── index.ts           # Vector embedding generation
├── package.json
└── tsconfig.json
```

## Entry Points

The project declares two bin entries in `package.json`:

| Bin Name | Target | Purpose |
|----------|--------|---------|
| `commonplace` | `dist/index.js` | Human-readable CLI output for migrate and graph commands |
| `commonplace-mcp` | `dist/bin/commonplace-mcp.js` | Stdio MCP server for AI tool integration |

资料来源：[src/index.ts:25-40]()

The deliberate separation ensures the MCP server's JSON-RPC framing is never polluted by CLI output. 资料来源：[src/index.ts:42-45]()

## CLI Dispatcher

The main CLI entry point (`src/index.ts`) acts as a subcommand router. It supports two primary command paths:

1. **`commonplace migrate`** — Detect known external memory sources and import memories
2. **`commonplace migrate <dir>`** — Rebuild sidecars for an existing memory directory
3. **`commonplace graph`** — Query memory relationship graph 资料来源：[src/index.ts:54-77]()

```mermaid
graph TD
    A[commonplace CLI] --> B{Subcommand?}
    B -->|graph| C[graphMain]
    B -->|migrate| D{Args?}
    D -->|detect mode| E[detectImportSources]
    D -->|import mode| F[runImportFromClaudeCode]
    D -->|rebuild mode| G[migrateMain]
```

The dispatcher uses lazy embedder instantiation — the `Embedder` (which triggers model loading) is only constructed when migrateMain decides to run after argument parsing succeeds. 资料来源：[src/index.ts:63-69]()

## Core Modules

### MemoryStore (`src/store/memory-store.ts`)

The `MemoryStore` class is the central orchestration component for memory management. It maintains:

- An in-memory index of all loaded memories
- A directory watcher for detecting changes
- An optional `MemoryGraph` for relationship tracking
- A lazy embedder factory for generating vector embeddings

Key responsibilities include scanning directories, managing embedding sidecars, handling atomic writes, and processing graph edges. 资料来源：[src/store/memory-store.ts:1-50]()

The store implements a lazy-scanning pattern where the directory is scanned on first access, and subsequent operations use the cached in-memory index with mtime-based invalidation. 资料来源：[src/store/memory-store.ts:150-180]()

### Memory I/O (`src/store/memory.ts`)

The `memory.ts` module handles the serialization and deserialization of individual memory files. Each memory consists of:

**YAML Frontmatter:**
```yaml
---
name: feedback_scope
description: Don't shrink scope unilaterally
type: feedback
relations:
  - to: other_name
    type: builds-on
supersedes:
  - old_name
---
<body>
```

**Content Hashing:**
The module exports `contentSha()` which computes a SHA-256 digest of the canonical form: `${type}\n${name}\n${description}\n${body}`. This hash is used to detect content changes without embedding sidecars. 资料来源：[src/store/memory.ts:45-55]()

The hash deliberately excludes `relations` and `supersedes` fields — graph edges must not invalidate embeddings. 资料来源：[src/store/memory.ts:56-62]()

### Atomic Write (`src/store/atomic-write.ts`)

The `atomicWrite()` function provides safe file writes using a temp-file-and-rename pattern:

1. Generate a random tmp file name with 16 hex chars of entropy
2. Verify source and destination are on the same filesystem
3. Write data to tmp file
4. Rename tmp file to target path 资料来源：[src/store/atomic-write.ts:1-20]()

The same-filesystem guard prevents cross-filesystem atomic rename failures which would break the atomicity guarantee. 资料来源：[src/store/atomic-write.ts:21-27]()

### Mention Extraction (`src/store/mentions.ts`)

The `mentions.ts` module extracts `[[name]]` wiki-style references from memory bodies. It provides a pure tokenizer that:

- Returns unique mention targets in first-occurrence order
- Validates names against `^[a-z0-9_]+$` (same pattern as memory filenames)
- Is gated by `COMMONPLACE_EXTRACT_MENTIONS` env var (defaults to on)

The extracted mentions are forwarded to `MemoryGraph.addMentionsEdge` during `scan()` and `save()` operations. 资料来源：[src/store/mentions.ts:1-30]()

## Memory Scopes

Commonplace supports two memory stores that can be loaded simultaneously:

| Scope | Location | Purpose |
|-------|----------|---------|
| User | `~/.commonplace/memory` (default) | Personal rules, preferences, hard feedback |
| Project | `<project-root>/.commonplace/memory` | Per-project context, architecture notes |

Detection priority:
1. `COMMONPLACE_PROJECT_DIR` env var (explicit override)
2. MCP `roots/list` protocol detection
3. Current working directory scan 资料来源：[README.md:120-145]()

Search operations merge hits across both stores by descending score, with each match tagged with its originating scope. 资料来源：[README.md:140-145]()

## Embedding Pipeline

Embeddings are generated using transformers.js and stored as binary sidecar files (`.embedding`). The sidecar format includes:

- Magic bytes and version header
- Model ID and dimensions
- Content SHA for staleness detection
- Binary vector data 资料来源：[src/store/memory-store.ts:200-230]()

Sidecar freshness is validated by checking:
1. File exists and decodes correctly
2. `modelId` matches current embedder
3. `dim` matches current embedder dimensions
4. `contentSha` matches computed hash of current file 资料来源：[src/store/memory-store.ts:200-220]()

## Migration CLI (`src/cli/migrate.ts`)

The migrate command performs one-shot maintenance on memory directories:

```mermaid
graph LR
    A[Scan .md files] --> B{Embedding check}
    B -->|Missing| C[Generate embedding]
    B -->|Stale| D[Re-generate embedding]
    B -->|Fresh| E[Use cached]
    C --> F[Write .embedding sidecar]
    D --> F
    E --> G[Orphan cleanup]
    F --> G
    G --> H[Optional: prune dangling edges]
```

Operations include:
- **Embed new files** — Generate `.embedding` for new memories
- **Re-embed stale files** — Regenerate when content or model changes
- **Orphan cleanup** — Remove `.embedding` files with no matching `.md`
- **Dangling edge pruning** — Remove `relations`/`supersedes` pointing to non-existent memories 资料来源：[src/cli/migrate.ts:1-40]()

## MCP Server Architecture

The MCP server component provides AI tool access to memory operations:

| Tool | Purpose |
|------|---------|
| `memory_save` | Create/update memories with YAML frontmatter |
| `memory_search` | Semantic vector search across stores |
| `memory_list` | List memories by type/scope |
| `memory_path` | Find paths between memories via graph traversal |

The server uses stdio for communication, keeping its output channel separate from the human-readable CLI to prevent JSON-RPC framing pollution. 资料来源：[src/index.ts:42-45]()

## Workflow Summary

```mermaid
graph TD
    subgraph "CLI Path"
        A1[commonplace migrate] --> A2[Argument parsing]
        A2 --> A3[Lazy embedder creation]
        A3 --> A4[scan/detect/import]
    end
    
    subgraph "MCP Path"
        B1[commonplace-mcp] --> B2[Stdio server loop]
        B2 --> B3[Tool request]
        B3 --> B4[MemoryStore operations]
        B4 --> B5[JSON-RPC response]
    end
    
    subgraph "Shared Core"
        C1[MemoryStore] --> C2[memory.ts I/O]
        C1 --> C3[atomic-write.ts]
        C1 --> C4[MemoryGraph]
    end
    
    A4 --> C1
    B4 --> C1
```

## Memory Types

The system uses a four-element type taxonomy:

| Type | Description |
|------|-------------|
| `user` | Personal rules, preferences, identity facts |
| `feedback` | Corrections and lessons from prior agent behavior |
| `project` | Per-project context, architecture, conventions |
| `reference` | Durable neutral knowledge, API shapes, citations |

These types are enforced in YAML frontmatter and validated by the serialization layer. 资料来源：[README.md:45-65]()

## Build and CI

The project uses pnpm with the following make targets for CI validation:

| Target | Purpose |
|--------|---------|
| `make typecheck` | TypeScript type checking |
| `make lint` | Code linting |
| `make build` | Compile TypeScript |
| `make test` | Run test suite |
| `make audit` | Security audit (non-blocking for v0.1) |

All targets must pass on both Node 20 and Node 22 before PR merge. 资料来源：[CONTRIBUTING.md:20-30]()

---

<a id='memory-data-model'></a>

## Memory Data Model

### 相关页面

相关主题：[Memory Types Taxonomy](#memory-types-taxonomy), [Memory Store](#memory-store), [Embedding System](#embedding-system)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [src/server/tools.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/tools.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)
</details>

# Memory Data Model

The Memory Data Model is the core abstraction in Commonplace—a local-first knowledge management system that persists memories as plain Markdown files with typed YAML frontmatter. Each memory represents a discrete unit of knowledge, preference, or context that the agent can recall, search, and link to other memories.

## Overview

Memories are the atomic units of the Commonplace system. They are stored as `.md` files on disk where each file contains:

1. **YAML frontmatter** carrying typed metadata
2. **Markdown body** containing the actual knowledge content

The design philosophy treats the markdown file as the source of truth. Derived artifacts (such as `.embedding` sidecars) can be regenerated from the markdown at any time. 资料来源：[CLAUDE.md](https://github.com/rickbassham/commonplace/blob/main/CLAUDE.md)

```mermaid
graph LR
    A[Memory Input] --> B[YAML Frontmatter]
    A --> C[Markdown Body]
    B --> D[.md File]
    C --> D
    D --> E[.embedding Sidecar]
    D --> F[Search Index]
    D --> G[Graph Edges]
```

## Memory Types

Commonplace defines a four-element taxonomy for categorizing memories. Every memory must declare its type in the frontmatter. 资料来源：[README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)

| Type | Description | Use Case |
|------|-------------|----------|
| `user` | Personal rules, preferences, and identity facts about the human operator | Storing user preferences, communication style, work habits |
| `feedback` | Corrections and lessons learned from prior agent behavior | Persistent course-corrections, things that didn't work |
| `project` | Per-project context | Architecture notes, repo conventions, decisions that bind to one codebase |
| `reference` | Durable, neutral knowledge | API shapes, formulas, citations, lookup-by-meaning content |

### Type Constraints

The `type` field is mandatory and validated against the allowed set. Attempting to save a memory with an invalid type throws an error:

```typescript
if (!isMemoryType(memory.type)) {
  throw new Error(
    `memory.type must be one of ${MEMORY_TYPES.join(', ')}; got ${JSON.stringify(memory.type)}`,
  );
}
```

资料来源：[src/store/memory.ts:parseMemory](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

## Memory Structure

### TypeScript Interface

```typescript
interface Memory {
  name: string;
  description: string;
  type: MemoryType;
  body: string;
  relations: Relation[];
  supersedes: string[];
}
```

### Frontmatter Schema

Each memory file follows this YAML frontmatter structure:

```yaml
---
name: feedback_scope
description: Don't shrink scope unilaterally
type: feedback
relations:
  - to: other_memory_name
    type: builds-on
supersedes:
  - old_memory_name
---
<markdown body content>
```

资料来源：[src/store/memory.ts:serializeMemory](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

### Field Specifications

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| `name` | string | Yes | Must match `^[a-z0-9_]+$`; becomes the filename stem |
| `description` | string | Yes | Short human-readable summary |
| `type` | enum | Yes | One of: `user`, `feedback`, `project`, `reference` |
| `body` | string | Yes | Arbitrary markdown content |
| `relations` | Relation[] | No | Defaults to `[]`; typed graph edges |
| `supersedes` | string[] | No | Defaults to `[]`; names of superseded memories |

## Name Validation

Memory names must conform to the pattern `^[a-z0-9_]+$` (lowercase alphanumeric characters and underscores only). This restriction ensures:

- Names are filesystem-safe (no path separators)
- Names work consistently across storage backends
- Mention extraction (`[[name]]`) has predictable matching

```typescript
export const NAME_PATTERN = /^[a-z0-9_]+$/;

const validateName = (raw: unknown, ctx: string): string => {
  if (typeof raw !== 'string') {
    throw new Error(`${ctx} must be a non-empty string; got ${JSON.stringify(raw)}`);
  }
  if (!NAME_PATTERN.test(raw)) {
    throw new Error(
      `${ctx} must match ${NAME_PATTERN}; got ${JSON.stringify(raw)}`,
    );
  }
  return raw;
};
```

资料来源：[src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

## Relation Model (DAR-925)

The `relations` field enables explicit typed edges between memories, forming a graph structure. 资料来源：[src/store/memory.ts:parseRelations](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

### Relation Types

| Type | Semantics | Default in Search? |
|------|-----------|-------------------|
| `builds-on` | This memory extends or depends on another | Yes |
| `related-to` | Topic or context overlap with another memory | Yes |
| `contradicts` | This memory contradicts another | No |
| `child-of` | Hierarchical parent relationship | No |
| `mentions` | Body content references another memory | No |
| `supersedes` | This memory replaces another (via `supersedes` field) | No |

The default expand types for search results are `['builds-on', 'related-to']`. These are the edge types most likely to surface useful neighbors the agent did not explicitly request. 资料来源：[src/server/handlers.ts:DEFAULT_EXPAND_TYPES](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)

### Relation Validation

Relations are validated during parse and write operations:

- Each entry must be a mapping with `to` and `type` keys
- The `to` value must be a valid memory name
- The `type` value must be one of the allowed relation types
- Duplicate `(to, type)` pairs are deduplicated preserving first-occurrence order
- Self-referential relations (`to === memory.name`) are rejected

```typescript
const parseRelation = (entry: unknown, ctx: string): Relation => {
  if (!isPlainObject(entry)) {
    throw new Error(`${ctx} must be a mapping with \`to\` and \`type\``);
  }
  if (!('to' in entry)) {
    throw new Error(`${ctx} is missing required key \`to\``);
  }
  if (!('type' in entry)) {
    throw new Error(`${ctx} is missing required key \`type\``);
  }
  const to = validateName(entry.to, `${ctx}.to`);
  const type = entry.type;
  if (!isRelationType(type)) {
    throw new Error(`${ctx}.type must be one of ${RELATION_TYPES.join(', ')}`);
  }
  return { to, type };
};
```

资料来源：[src/store/memory.ts:parseRelation](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

## Content SHA (Canonicalization)

The `contentSha` function computes a SHA-256 hex digest that serves as a canonical identifier for memory content. This digest is used to determine when embeddings need regeneration.

### Canonical Form

The SHA is computed over the concatenation of:

```
${type}\n${name}\n${description}\n${body}
```

### Critical Design Decision

**Graph fields (`relations` and `supersedes`) do NOT participate in the SHA computation.** Adding or removing graph edges does not invalidate the corresponding embedding sidecar. This means:

- Embeddings are based on semantic content only
- Graph operations are lightweight and don't trigger re-embedding
- The `contentSha` remains stable across graph modifications

```typescript
export const contentSha = (memory: Memory): string =>
  createHash('sha256')
    .update(`${memory.type}\n${memory.name}\n${memory.description}\n${body}`, 'utf8')
    .digest('hex');
```

资料来源：[src/store/memory.ts:contentSha](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

## Serialization and Parsing

### Serialization (`serializeMemory`)

The `serializeMemory` function converts an in-memory `Memory` object to its canonical UTF-8 markdown representation:

1. Validates the memory type
2. Parses and deduplicates relations and supersedes
3. Rejects self-referential relations
4. Serializes YAML frontmatter followed by the body

```typescript
export const serializeMemory = (memory: Memory): string => {
  if (!isMemoryType(memory.type)) {
    throw new Error(
      `memory.type must be one of ${MEMORY_TYPES.join(', ')}; got ${JSON.stringify(memory.type)}`,
    );
  }
  const relations = parseRelations(memory.relations);
  const supersedes = parseSupersedes(memory.supersedes);
  // ... validation checks ...
  const fmObject = {
    name: memory.name,
    description: memory.description,
    type: memory.type,
    ...(relations.length > 0 ? { relations } : {}),
    ...(supersedes.length > 0 ? { supersedes } : {}),
  };
  const fmYaml = stringifyYaml(fmObject);
  return `---\n${fmYaml}---\n${memory.body}`;
};
```

资料来源：[src/store/memory.ts:serializeMemory](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

### Parsing (`parseMemory`)

The `parseMemory` function reads a markdown file and extracts the typed memory object:

1. Splits on `\n---\n` to separate frontmatter from body
2. Parses YAML frontmatter using `parseYaml`
3. Validates required fields (`name`, `description`, `type`)
4. Parses optional `relations` and `supersedes` arrays
5. Rejects unknown frontmatter fields

```typescript
export const parseMemory = (raw: string): Memory => {
  // Splits on the first `---` boundary
  const fmEnd = raw.indexOf('\n---', 4);
  const fmRaw = raw.slice(4, fmEnd);
  const body = raw.slice(fmEnd + 4).replace(/^\n/, '');
  const fmObject = parseYaml(fmRaw);
  // ... field extraction and validation ...
  return { name, description, type: type as MemoryType, body, relations, supersedes };
};
```

资料来源：[src/store/memory.ts:parseMemory](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)

## Memory Store Architecture

The `MemoryStore` class manages a directory of memory files and their associated sidecars. 资料来源：[src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)

```mermaid
graph TD
    subgraph MemoryStore
        A[Directory] --> B[scan]
        A --> C[save]
        A --> D[delete]
        A --> E[linkEdge]
        A --> F[unlinkEdge]
    end
    B --> G[Memory Entry]
    C --> H[serializeMemory]
    C --> I[atomicWrite]
    E --> J[parseMemory]
    E --> K[serializeMemory]
    G --> L[contentSha]
    G --> M[Embedding Sidecar]
```

### Key Operations

| Operation | Description |
|-----------|-------------|
| `scan()` | Discovers all `.md` files, validates/reuses/regenerates `.embedding` sidecars, removes orphans |
| `save()` | Creates new memory file with atomic write; refuses to overwrite |
| `delete()` | Removes memory file and sidecar |
| `linkEdge()` | Adds a typed relation to a memory |
| `unlinkEdge()` | Removes a typed relation from a memory |

### Orphan Cleanup (DAR-918)

After scanning, `scan()` walks the directory again and removes any `.embedding` sidecar whose matching `<name>.md` is missing. This prevents silent accumulation of dead-weight sidecar files. 资料来源：[src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)

## Mention Extraction (DAR-927)

The `mentions` module extracts `[[name]]` style references from memory body content. 资料来源：[src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)

### Extraction Rules

- Only `<name>` tokens matching `^[a-z0-9_]+$` are recognized
- Other patterns are silently ignored
- Mentions are forwarded to `MemoryGraph.addMentionsEdge`
- Extraction is gated by `COMMONPLACE_EXTRACT_MENTIONS` env var (default: on)

### Out of Scope

- Wiki-style backlink rendering or autocomplete
- Code-fence or inline-code awareness (mentions in code blocks are extracted like any other)
- Surfacing mentions through MCP tool responses

## Multi-Store Design

Commonplace supports two memory stores with different scopes: 资料来源：[src/server/tools.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/tools.ts)

| Scope | Location | Purpose |
|-------|----------|---------|
| `user` | `COMMONPLACE_USER_DIR` (default: `~/.commonplace`) | Cross-project memories |
| `project` | Detected project root | Per-project memories |

### Scope Behavior

- **Reads** merge across both stores when `scope` is omitted
- **Writes** default to `user` when `scope` is omitted
- **Project store** auto-creates on first `memory_save({ scope: 'project' })`
- **Deletions** require explicit `scope` when name exists in both stores

## MCP Tool Integration

The four core memory tools interact with the data model as follows: 资料来源：[src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)

### memory_save

Creates a new memory file. Validates name, type, and description against the data model constraints.

### memory_list

Returns frontmatter-only entries (name, type, description, scope) without body content.

### memory_search

Returns matches with full body content. Each match includes:
- `name`, `type`, `description`, `body`
- `score` (cosine similarity, 3 decimals)
- `relations` (outgoing authored edges)

### memory_delete

Removes memory and sidecar. Requires `scope` when name exists in both stores.

### memory_link / memory_unlink

Manipulate explicit typed edges without modifying the memory body or triggering re-embedding.

## Write Safety

Memory writes implement several safety guarantees: 资料来源：[src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)

1. **No Overwrite** - `save()` refuses to overwrite existing entries; callers must `delete()` first
2. **Atomic Writes** - Uses temp-file + rename pattern (DAR-923)
3. **Advisory Locking** - Per-name locks prevent racing writers across processes
4. **Lazy Directory Creation** - Parent directory created recursively on first save to project scope

## Sidecar Format

Each memory may have an associated `.embedding` sidecar file containing:

- Magic number for format detection
- Version byte
- Model ID
- Dimensions
- `contentSha` for cache validation
- Binary embedding vector

The sidecar is derived from the `.md` file and regenerated when:
- The sidecar is missing or corrupt
- The `modelId` or `dim` doesn't match the current embedder
- The `contentSha` doesn't match the current memory content

资料来源：[src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)

## Summary

The Memory Data Model provides:

| Aspect | Implementation |
|--------|---------------|
| **Storage** | Plain `.md` files with YAML frontmatter |
| **Metadata** | Name, type, description, relations, supersedes |
| **Content** | Arbitrary markdown body |
| **Identification** | SHA-256 over type, name, description, body |
| **Taxonomy** | Four types: user, feedback, project, reference |
| **Graph** | Typed relations with six edge types |
| **Mentions** | `[[name]]` extraction from body content |
| **Stores** | User-scope and project-scope directories |
| **Safety** | No-overwrite contract, atomic writes, advisory locks |

---

<a id='memory-types-taxonomy'></a>

## Memory Types Taxonomy

### 相关页面

相关主题：[Memory Data Model](#memory-data-model), [Memory Store](#memory-store), [Semantic Search](#semantic-search)

<details>
<summary>Source Files Used</summary>

以下源码文件用于生成本页说明：

- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [src/server/tools.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/tools.ts)
- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)
</details>

# Memory Types Taxonomy

## Overview

The Memory Types Taxonomy is a four-element classification system that categorizes memories stored in Commonplace. Each memory carries a `type` field that determines its scope, purpose, and how it should be used by the AI agent. This taxonomy enables organized knowledge management across personal rules, learned corrections, project-specific context, and reference materials.

Memory types serve as the primary organizational primitive for the entire system. They appear in YAML frontmatter of markdown files, are validated at write time, and are used to filter search results and graph traversals. Source: [README.md]()

## The Four Memory Types

| Type | Purpose | Scope | Typical Content |
|------|---------|-------|-----------------|
| `user` | Personal rules, preferences, and identity facts about the human operator | Cross-project | Operating preferences, name, role, interaction style |
| `feedback` | Corrections and lessons learned from prior agent behavior | Cross-project | Persistent course-corrections, mistake patterns |
| `project` | Per-project context specific to one codebase | Single project | Architecture notes, repo conventions, decisions |
| `reference` | Durable, neutral knowledge | Cross-project | API shapes, formulas, citations, lookup knowledge |

Source: [README.md]()

### User Type

The `user` type captures information about the human operating the AI agent. This includes personal rules, preferences, identity facts, and operational context that should persist across all projects. Memories of this type represent the agent's understanding of who it is working with and how that person prefers to be addressed or assisted.

Example frontmatter:
```yaml
---
name: user_prefers_typescript
description: Prefers TypeScript over JavaScript for new projects
type: user
---
```

### Feedback Type

The `feedback` type stores corrections and lessons learned from prior agent behavior. When an agent makes a mistake or receives guidance, the correction is persisted as a feedback memory to prevent recurrence. These memories represent persistent course-corrections that shape future interactions.

### Project Type

The `project` type contains information that is specific to a single codebase or repository. Unlike `user` and `feedback` memories which are shared across all projects, `project` memories are isolated to a particular working directory. This type is used for architecture notes, conventions unique to the codebase, and decisions that only apply within that project.

### Reference Type

The `reference` type holds durable, neutral knowledge that can be looked up by meaning. This includes API documentation, formulas, citations, and any factual information that should persist independently of specific projects or interactions.

## Memory File Structure

Each memory is stored as a markdown file with YAML frontmatter. The frontmatter defines the memory's metadata while the body contains the actual content.

```yaml
---
name: feedback_scope
description: Don't shrink scope unilaterally
type: feedback
relations:
  - to: project_planning_rules
    type: builds-on
supersedes:
  - old_scope_rules
---
<markdown body content>
```

Source: [src/store/memory.ts:1-50]()

## Type Validation

The system enforces strict validation on the `type` field. During save operations, the type is validated against the allowed set. An invalid type results in an error that lists all permitted values:

```
memory.type must be one of user, feedback, project, reference; got "invalid_type"
```

Source: [src/store/memory.ts:100-105]()

### Validation Code

```typescript
export const isMemoryType = (v: unknown): v is MemoryType =>
  typeof v === 'string' && (MEMORY_TYPES as readonly string[]).includes(v);
```

Source: [src/store/memory.ts:60-62]()

## Scope and Store Architecture

The taxonomy is complemented by a two-tier store architecture that separates memories by scope:

```mermaid
graph TD
    A[Memory Store] --> B[User Store]
    A --> C[Project Store]
    B --> D[~/.commonplace/memories/]
    C --> E[.claude/memories/]
```

Both stores accept the same four memory types, but the `project` store is isolated to project-specific memories while the `user` store holds cross-project memories of any type.

### Scope Parameter

When saving memories, the `scope` argument determines which store receives the data:

| Scope Value | Target Store | Default |
|-------------|--------------|---------|
| `user` | User store (`~/.commonplace/memories/`) | Yes |
| `project` | Project store (`.claude/memories/`) | No |

Source: [src/server/tools.ts:50-60]()

## Type Usage in API Tools

### memory_save

The `memory_save` tool requires the `type` parameter:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Memory identifier matching `^[a-z0-9_]+$` |
| `type` | enum | Yes | One of: `user`, `feedback`, `project`, `reference` |
| `description` | string | Yes | Short human description |
| `body` | string | Yes | Markdown body content |
| `scope` | enum | No | `user` (default) or `project` |

Source: [src/server/tools.ts:30-55]()

### memory_list

The `memory_list` tool returns memories with their type information:

```typescript
interface MemoryListResult {
  memories: Array<{
    name: string;
    type: MemoryType;
    description: string;
    scope: Scope;
  }>;
}
```

Source: [src/server/handlers.ts:80-90]()

## Type Filtering in Search

When searching memories, the system can filter by type. The default search behavior includes all types, but callers can specify a subset:

```typescript
interface MemorySearchParams {
  query: string;
  types?: MemoryType[];  // Filter by memory types
  scope?: Scope;         // Filter by user/project store
}
```

Source: [src/server/handlers.ts:100-110]()

## Graph Relations by Type

Graph edges between memories are typed, and the default expansion types in graph queries prioritize certain memory types:

```typescript
export const DEFAULT_EXPAND_TYPES: readonly EdgeType[] = 
  ['builds-on', 'related-to'] as const;
```

Source: [src/server/handlers.ts:15-20]()

## Mention Extraction by Type

The `[[name]]` mention extraction system operates on memory bodies regardless of type. All four memory types support body-mention extraction when the environment variable `COMMONPLACE_EXTRACT_MENTIONS` is set (default: enabled).

Source: [src/store/mentions.ts:20-30]()

## Import and Migration

The migration system can import memories from Claude Code's auto-memory directories. During import, memories are classified and saved to the appropriate store:

```mermaid
graph LR
    A[Claude Code<br/>auto-memory] --> B[Detect Type]
    B --> C{Source Type}
    C -->|project memory| D[Project Store]
    C -->|other| E[User Store]
```

Source: [src/cli/migrate.ts:30-60]()

## Best Practices

1. **Use `user` for personal context** - Store facts about the human operator that should persist across all projects.

2. **Use `feedback` for corrections** - Record agent mistakes and guidance to improve future responses.

3. **Use `project` for codebase-specific knowledge** - Keep architecture and convention notes isolated to their relevant repository.

4. **Use `reference` for reusable knowledge** - Store API documentation, formulas, and factual information that can be looked up by meaning.

5. **Validate before saving** - Ensure the type field is valid to avoid runtime errors.

## Related Features

- [Graph Relations](graph-relations) - Edges between memories
- [Mention Extraction](mentions) - `[[name]]` syntax support
- [Search API](search) - Querying memories by type
- [Migration Tools](migration) - Importing from external sources

---

<a id='memory-store'></a>

## Memory Store

### 相关页面

相关主题：[Memory Data Model](#memory-data-model), [Embedding System](#embedding-system), [Semantic Search](#semantic-search)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/atomic-write.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/atomic-write.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [src/store/graph.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/graph.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [src/server/tools.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/tools.ts)
- [CHANGELOG.md](https://github.com/rickbassham/commonplace/blob/main/CHANGELOG.md)
</details>

# Memory Store

The Memory Store is the core persistence layer of the Commonplace system. It manages the lifecycle of memory entries—markdown files with YAML frontmatter and binary embedding sidecars—providing atomic writes, concurrent access control, semantic search, and graph-based relationship tracking.

## Overview

The Memory Store implements a local-first, file-based storage system where each memory is persisted as a `.md` file alongside a derived `.embedding` sidecar. It supports two independent stores (`user` and `project`) and exposes both low-level filesystem operations and high-level search/list functionality through an internal API consumed by the MCP server layer.

**Key characteristics:**

| Characteristic | Description |
|----------------|-------------|
| Storage model | Local filesystem with markdown + sidecar files |
| Concurrency | Per-memory advisory locking via `proper-lockfile` |
| Atomicity | Write-through-temp-and-rename pattern |
| Search | Offline semantic search using `transformers.js` + `bge-base-en-v1.5` |
| Graph | In-memory adjacency list for typed relationships |
| Mentions | `[[name]]` extraction from body content |

资料来源：[memory-store.ts:1-50]()

## Architecture

```mermaid
graph TD
    subgraph "Memory Store Layer"
        MS[MemoryStore]
        G[MemoryGraph]
        ATOMIC[atomicWrite]
        LOCK[acquireNameLock]
    end
    
    subgraph "Persistence Layer"
        FS[FileSystem]
        MD[*.md Files]
        EMB[*.embedding Sidecars]
    end
    
    subgraph "Integration"
        HANDLERS[MCP Handlers]
        SEARCH[Search Handler]
    end
    
    MS --> G
    MS --> ATOMIC
    MS --> LOCK
    ATOMIC --> FS
    LOCK --> FS
    MS --> MD
    MS --> EMB
    HANDLER_FACTORIES[Handler Factories] --> MS
    SEARCH --> MS
```

The `MemoryStore` class owns the directory-scanning, file I/O, embedding caching, and mtime-based invalidation logic. It delegates locking to `acquireNameLock`, writes to `atomicWrite`, and maintains an optional `MemoryGraph` instance for relationship tracking.

资料来源：[memory-store.ts:50-120]()

## Memory Data Model

### Memory Structure

A memory consists of:

1. **YAML Frontmatter**: Structured metadata
2. **Markdown Body**: Arbitrary content containing `[[name]]` mention syntax

```typescript
interface Memory {
  name: string;           // Filename stem, matches ^[a-z0-9_]+$
  description: string;    // Short human description
  type: MemoryType;       // 'user' | 'feedback' | 'project' | 'reference'
  body: string;           // Markdown content
  relations: Relation[];  // Typed graph edges (DAR-925)
  supersedes: string[];   // Names of superseded memories
}

interface Relation {
  to: string;             // Target memory name
  type: RelationType;     // 'builds-on' | 'related-to' | 'contradicts' | 'child-of'
}
```

资料来源：[memory.ts:1-80]()

### File Format

Memory files use canonical markdown with YAML frontmatter:

```markdown
---
name: feedback_scope
description: Don't shrink scope unilaterally
type: feedback
relations:
  - to: project_context
    type: builds-on
supersedes:
  - old_feedback
---
<body content here>
```

The frontmatter is canonicalized by `stringifyYaml` to ensure round-trip idempotence. The file is the source of truth; sidecars are derived.

资料来源：[memory.ts:80-120]()

### Content Hashing

The `contentSha` function computes a deterministic SHA-256 digest used for sidecar validation:

```typescript
export const contentSha = (memory: Memory): string =>
  createHash('sha256')
    .update(`${memory.type}\n${memory.name}\n${memory.description}\n${memory.body}`, 'utf8')
    .digest('hex');
```

**Important**: The sha covers only the v0.1 baseline frontmatter (`type`, `name`, `description`) plus the body. Graph fields (`relations`, `supersedes`) are excluded to prevent embedding invalidation when edges change.

资料来源：[memory.ts:140-155]()

## Core Operations

### Initialization

```typescript
class MemoryStore {
  constructor(
    dir: string,
    embedder: Embedder,
    graph?: MemoryGraph,
    lockOptions?: LockOptions
  )
}
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `dir` | `string` | Path to the memory directory |
| `embedder` | `Embedder` | Embedding pipeline (model ID, dimension, encode function) |
| `graph` | `MemoryGraph?` | Optional graph instance for relationship tracking |
| `lockOptions` | `LockOptions?` | Lock staleness and retry configuration |

The constructor stores the embedder reference for lazy re-embedding and initializes in-memory state. The directory is created lazily on first save.

资料来源：[memory-store.ts:50-80]()

### Scan Operation

The `scan()` method rebuilds the in-memory index from disk:

```typescript
async scan(opts?: ScanOptions): Promise<ScanResult>
```

**Behavior:**
1. Walk the directory for `*.md` files
2. For each file, check if the matching `*.embedding` sidecar is reusable based on:
   - Magic + version + length checks
   - `modelId` match
   - Dimension match
   - `contentSha` match
3. Re-embed and rewrite sidecar when stale
4. Remove orphan sidecars (files with no matching `.md`)

| Sidecar Check | Fresh If... |
|---------------|-------------|
| File exists | Yes |
| Decodes correctly | Yes |
| `modelId` matches | Yes |
| `dim` matches | Yes |
| `contentSha` matches | Yes |

资料来源：[memory-store.ts:150-250]()

### Save Operation

```typescript
async save(name: string, memory: Memory): Promise<void>
```

**Workflow:**

```mermaid
graph TD
    START[save called] --> CHECK_EXISTS{existsSync<br/>${name}.md?}
    CHECK_EXISTS -->|No| MKDIR[mkdir -p dir]
    CHECK_EXISTS -->|Yes| ERROR[Error: exists]
    MKDIR --> LOCK[acquireNameLock]
    LOCK --> RE_CHECK{existsSync<br/>${name}.md?}
    RE_CHECK -->|Yes| RELEASE[Release lock]
    RE_CHECK -->|No| SERIALIZE[serializeMemory]
    SERIALIZE --> EMBED[embed body]
    EMBED --> ATOMIC_WRITE_MD[atomicWrite<br/>${name}.md]
    ATOMIC_WRITE_MD --> ATOMIC_WRITE_EMB[atomicWrite<br/>${name}.embedding]
    ATOMIC_WRITE_EMB --> UPDATE_INDEX[Update in-memory index]
    UPDATE_INDEX --> RELEASE
    RELEASE --> DONE[Return]
    ERROR --> ABORT[Throw]
```

**Key behaviors:**
- Refuses to overwrite existing entries (delete first, then save)
- Lazily creates directory on first save
- Holds per-name advisory lock for entire write operation
- Uses atomic write for both `.md` and `.embedding`

资料来源：[memory-store.ts:280-380]()

### Search Operation

```typescript
async search(query: string, opts?: SearchOptions): Promise<MemorySearchResult>
```

**Search options:**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `type` | `MemoryType?` | undefined | Filter by memory type |
| `limit` | `number` | 20 | Maximum results |
| `expandTypes` | `EdgeType[]` | `['builds-on', 'related-to']` | Edge types to include |
| `minScore` | `number` | 0 | Minimum similarity score |

The search computes query embeddings via the embedder, performs brute-force top-k cosine similarity against all loaded memories, and enriches results with outgoing graph edges. Superseded memories are excluded by default.

资料来源：[memory-store.ts:380-450](), [handlers.ts:80-120]()

### Delete Operation

```typescript
async delete(name: string, opts?: DeleteOptions): Promise<void>
```

```mermaid
graph TD
    START[delete called] --> LOCK[acquireNameLock]
    LOCK --> UNLINK_MD[unlinkSync<br/>${name}.md]
    UNLINK_MD --> UNLINK_EMB[unlinkSync<br/>${name}.embedding]
    UNLINK_EMB --> REMOVE_INDEX[Remove from in-memory index]
    REMOVE_INDEX --> GRAPH_REMOVE[graph.remove<br/>if exists]
    GRAPH_REMOVE --> RELEASE[Release lock]
    RELEASE --> DONE
```

资料来源：[memory-store.ts:450-500]()

### List Operation

```typescript
async list(opts?: ListOptions): Promise<MemoryListResult>
```

Returns frontmatter-only entries (name, type, description, scope) without body content. Filters can be applied by type.

资料来源：[memory-store.ts:500-550](), [handlers.ts:150-180]()

## Atomic Write Mechanism

The `atomicWrite` helper implements the write-through-temp-and-rename pattern:

```typescript
export const atomicWrite = async (
  target: string,
  data: Buffer | Uint8Array
): Promise<void>
```

**Algorithm:**

```mermaid
graph TD
    START[atomicWrite] --> TMP[Generate tmp filename]
    TMP --> STAT_TMP[stat tmp parent]
    STAT_TMP --> CHECK_FS{same filesystem?}
    CHECK_FS -->|No| ERROR[Error: cross-fs]
    CHECK_FS -->|Yes| WRITE[writeFileSync tmp]
    WRITE --> FSYNC[fsync tmp file]
    FSYNC --> RENAME[renameSync tmp → target]
    RENAME --> DONE
```

**Safety guarantees:**
- Same-filesystem check via `dev` comparison prevents cross-fs rename
- Random 16-char hex suffix prevents collision
- `fsync` before rename ensures durability
- Failure during write leaves original file untouched

资料来源：[atomic-write.ts:1-60]()

## Concurrency Control

### Per-Memory Locking

The `acquireNameLock` function uses `proper-lockfile` for advisory locking:

```typescript
const acquireNameLock = async (
  dir: string,
  name: string
): Promise<() => Promise<void>>
```

**Configuration:**

| Option | Value | Purpose |
|--------|-------|---------|
| `stale` | 5000ms | Reclaim orphaned locks from crashed holders |
| `realpath` | `false` | Works before file exists |
| `retries` | configurable | Retry on transient lock acquisition failure |

**Lock target pattern:** `${dir}/.locks/${name}.lock`

The lock is held for the entire save operation (both `.md` and `.embedding` writes), serializing concurrent writers on the same memory name across processes.

资料来源：[memory-store.ts:120-170]()

## Graph Integration

The `MemoryStore` optionally owns a `MemoryGraph` instance for tracking relationships:

```typescript
class MemoryGraph {
  add(memory: GraphMemory): void
  addEdge(edge: Edge): void
  remove(name: string): void
  getOutbound(name: string): Edge[]
  getInbound(name: string): Edge[]
  detectDangling(): Edge[]
  rebuild(memories: GraphMemory[], mentions: Mention[]): void
}
```

**Edge types:**
| Type | Default Included | Description |
|------|------------------|--------------|
| `builds-on` | Yes | Forward dependency |
| `related-to` | Yes | General association |
| `mentions` | No | Body `[[name]]` extraction |
| `supersedes` | No | Replacement relationship |
| `contradicts` | No | Contradictory memory |
| `child-of` | No | Hierarchical relationship |

资料来源：[graph.ts:1-80](), [handlers.ts:40-60]()

## Mention Extraction

The `extractMentions` function parses `[[name]]` tokens from body content:

```typescript
export const extractMentions(body: string): string[]
```

**Rules:**
- Matches `<name>` tokens with pattern `^[a-z0-9_]+$`
- Returns unique set in first-occurrence order
- Extraction can be disabled via `COMMONPLACE_EXTRACT_MENTIONS=false`

The MemoryStore calls `extractMentions` during `scan()` and `save()`, forwarding results to `MemoryGraph.addMentionsEdge`.

资料来源：[mentions.ts:1-50]()

## MCP Tool Integration

The MemoryStore is consumed by MCP handler factories in `handlers.ts`:

| Handler Factory | MCP Tool | Operation |
|-----------------|----------|-----------|
| `createMemorySaveHandler` | `memory_save` | Save new memory |
| `createMemoryListHandler` | `memory_list` | List memories |
| `createMemoryDeleteHandler` | `memory_delete` | Delete memory |
| `createMemorySearchHandler` | `memory_search` | Semantic search |
| `createMemoryLinkHandler` | `memory_link` | Add graph edge |
| `createMemoryUnlinkHandler` | `memory_unlink` | Remove graph edge |

All handlers accept an optional `scope: 'user' | 'project'` argument that selects which store to operate on.

资料来源：[handlers.ts:180-300](), [tools.ts:1-100]()

## Configuration

### Directory Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `COMMONPLACE_USER_DIR` | `~/.config/commonplace` | User store directory |
| `COMMONPLACE_PROJECT_DIR` | (auto-detected) | Project store directory |

### Feature Flags

| Variable | Default | Description |
|----------|---------|-------------|
| `COMMONPLACE_EXTRACT_MENTIONS` | `'true'` | Enable `[[name]]` extraction |

资料来源：[memory-store.ts:1-50](), [mentions.ts:1-30]()

## Error Handling

| Error Condition | Message | Recovery |
|-----------------|---------|----------|
| Duplicate save | `MemoryStore.save: a memory file already exists at ${path}` | Delete first, then save |
| Lock busy | `MemoryStore: lock for memory \`${name}\` is busy` | Retry after other writer releases |
| Cross-filesystem atomic | `atomicWrite: refusing to rename across filesystems` | Check filesystem layout |
| Invalid memory type | `memory.type must be one of ${MEMORY_TYPES}` | Validate before save |
| Self-reference relation | `memory relation target cannot be self` | Remove invalid relation |

资料来源：[memory-store.ts:280-350](), [memory.ts:180-220](), [atomic-write.ts:30-40]()

## Sidecar Format

Embedding sidecars are binary files with the following structure:

```
[magic: 4 bytes][version: 1 byte][dim: 4 bytes LE][vector: dim * 4 bytes]
```

| Field | Size | Description |
|-------|------|-------------|
| magic | 4 bytes | Magic bytes for file identification |
| version | 1 byte | Format version number |
| dim | 4 bytes | Vector dimension (little-endian) |
| vector | dim × 4 bytes | Float32 embedding values |

资料来源：[memory-store.ts:200-250]()
</details>

---

<a id='embedding-system'></a>

## Embedding System

### 相关页面

相关主题：[Memory Store](#memory-store), [Semantic Search](#semantic-search)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/embedder/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/embedder/index.ts)
- [src/store/sidecar.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/sidecar.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/atomic-write.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/atomic-write.ts)
- [src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts)
</details>

# Embedding System

The embedding system in Commonplace transforms markdown memory content into vector representations stored as sidecar files, enabling semantic search across the memory store. It is designed to treat the `.md` source as the single source of truth, with embeddings as derived, regenerable artifacts.

## Architecture Overview

The embedding pipeline consists of three primary layers:

1. **Memory Layer** (`src/store/memory.ts`) — parses and serializes markdown with YAML frontmatter, computes canonical content hashes
2. **Store Layer** (`src/store/memory-store.ts`) — orchestrates the scan/embed lifecycle, validates sidecar freshness, manages orphan cleanup
3. **Embedder Layer** (`src/embedder/index.ts`) — generates vector embeddings via transformers.js

```mermaid
graph TD
    A[Markdown File] --> B[readMemory]
    B --> C[contentSha Computation]
    C --> D[Load Sidecar]
    D --> E{Sidecar Fresh?}
    E -->|Yes| F[Skip Embedding]
    E -->|No| G[Embedder.embed]
    G --> H[Serialize Sidecar]
    H --> I[atomicWrite]
    I --> J[.embedding Sidecar]
    C -.-> K[body text + frontmatter]
    F --> L[Use Existing Sidecar]
```

## Content Hash (contentSha)

The canonical content hash is computed over a specific slice of the memory's data structure, deliberately excluding graph-related fields to prevent embedding invalidation when relationships change.

### Canonical Hash Inputs

| Field | Included | Rationale |
|-------|----------|-----------|
| `type` | ✅ Yes | Memory type affects semantic context |
| `name` | ✅ Yes | Name is part of semantic identity |
| `description` | ✅ Yes | Description is semantically meaningful |
| `body` | ✅ Yes | Full body content |
| `relations` | ❌ No | Graph edges are derived, not source |
| `supersedes` | ❌ No | Graph edges are derived, not source |

资料来源：[src/store/memory.ts:contentSha function]()

The hash formula is:
```
sha256("${type}\n${name}\n${description}\n${body}")
```

This produces a 64-character lowercase hex string that uniquely identifies the semantic content of a memory.

### SHA Scope Justification

The graph fields (`relations`, `supersedes`) are intentionally excluded per DAR-925 design decisions. Adding or removing graph edges must not invalidate existing embeddings because:

- Embeddings are derived from semantic content, not graph topology
- Forcing re-embedding on graph edits would create unnecessary API calls
- The sidecar is explicitly marked as derived, not authoritative

## Sidecar Format

Embeddings are stored in `.embedding` sidecar files alongside their corresponding `.md` files. The sidecar encodes sufficient metadata to validate freshness without re-reading the markdown.

### Sidecar Schema

| Field | Type | Purpose |
|-------|------|---------|
| `modelId` | string | Identifies the embedding model used |
| `dim` | number | Embedding vector dimensionality |
| `contentSha` | string | Hash of canonical content |
| `embedding` | number[] | The vector data |

资料来源：[src/store/sidecar.ts]()

### Sidecar File Naming

Sidecar files follow the naming convention `<name>.embedding` where `<name>` matches the corresponding memory file `<name>.md`. This 1:1 correspondence enables orphan detection.

## Scan Lifecycle

The `MemoryStore.scan()` method implements the complete embed lifecycle, checking each memory's sidecar for freshness and triggering regeneration when needed.

### Freshness Criteria

A sidecar is considered **fresh** (reusable) when ALL of the following hold:

1. The `.embedding` file exists
2. It decodes successfully (magic + version + length checks pass)
3. `decoded.modelId === embedder.modelId`
4. `decoded.dim === embedder.dim`
5. `decoded.contentSha === contentSha(memoryAsRead)`

资料来源：[src/store/memory-store.ts:scan method]()

If ANY criterion fails, the memory is re-embedded and the sidecar rewritten.

### Scan Process

```mermaid
graph TD
    A[Start Scan] --> B[Read all .md files]
    B --> C{For each memory}
    C --> D{No sidecar?}
    D -->|Yes| E[Trigger embed]
    D -->|No| F{Decode passes?}
    F -->|No| E
    F -->|Yes| G{modelId match?}
    G -->|No| E
    G -->|Yes| H{dim match?}
    H -->|No| E
    H -->|Yes| I{contentSha match?}
    I -->|Yes| J[Skip - use existing]
    I -->|No| E
    C --> K[Orphan Cleanup]
    K --> L[Remove orphaned .embedding files]
    E --> M[Write new sidecar]
    J --> C
    M --> C
```

### Orphan Cleanup

After processing all memories, scan performs a second directory walk to detect and remove orphan sidecars — `.embedding` files whose matching `.md` no longer exists. Orphans are reported via the `ScanResult.orphaned` field.

资料来源：[src/store/memory-store.ts:orphan cleanup]()

## Atomic Write Strategy

Sidecar writes use an atomic write-temp-rename pattern to prevent corruption from partial writes.

### Write Sequence

1. Generate random tmp filename: `<basename>.<8-hex-chars>.tmp`
2. Write data to tmp file
3. Verify source and target are on same filesystem (`dev` match)
4. Rename tmp file to target (atomic on same filesystem)

资料来源：[src/store/atomic-write.ts]()

### Cross-Filesystem Guard

```typescript
if (targetDirStat.dev !== tmpDirStat.dev) {
  throw new Error("refusing to rename across filesystems");
}
```

This guard ensures rename operations remain atomic, as rename(2) is not atomic across filesystem boundaries.

## Integration Points

### CLI Entry Point

The main CLI (`src/index.ts`) wires the embedder factory into the migrate command:

```typescript
embedderFactory: () => new Embedder(resolveModelId(process.env)),
```

The lazy factory ensures the transformers.js model is only loaded when an actual embed call occurs, not during argument parsing.

### Migrate Command

The `commonplace migrate <dir>` command exposes scan functionality as a standalone tool:

- Re-embeds any `.md` whose sidecar is missing or stale
- Cleans up orphaned sidecars
- Optionally prunes dangling graph edges (--prune-dangling flag)

资料来源：[src/cli/migrate.ts:migrateScan]()

## Configuration

### Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `COMMONPLACE_EMBEDDING_MODEL` | model-specific | Embedding model identifier |

The `resolveModelId()` function reads the environment to determine which embedding model to instantiate.

### Embedder Initialization

The `Embedder` class is instantiated with the resolved model ID. Model loading (transformers.js) occurs on first `embed()` call, not on construction, enabling lazy initialization.

## Memory Types and Embedding

All four memory types are embeddable with identical treatment:

| Type | Description | Embedding Behavior |
|------|-------------|-------------------|
| `user` | Personal rules and preferences | Standard |
| `feedback` | Lessons from prior behavior | Standard |
| `project` | Per-project context | Standard |
| `reference` | Durable knowledge | Standard |

Memory type is included in the content hash, so type changes trigger re-embedding.

## Summary

The embedding system provides:

- **Semantic search** via vector embeddings
- **Source-of-truth isolation** with SHA-scoped content hashes
- **Automatic regeneration** when content changes
- **Orphan cleanup** for stale sidecars
- **Atomic writes** for corruption prevention
- **Model portability** validation through metadata checks

All embedding behavior is derived from markdown content; the `.embedding` files are regenerable artifacts that can be deleted and rebuilt via the migrate command at any time.

---

<a id='semantic-search'></a>

## Semantic Search

### 相关页面

相关主题：[Embedding System](#embedding-system), [Graph Features](#graph-features), [MCP Tool Reference](#mcp-tool-reference)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/graph.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/graph.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
</details>

# Semantic Search

Semantic search enables finding memories by meaning rather than exact keyword matching. The system embeds memory content into high-dimensional vectors and uses cosine similarity to rank results against a query.

## Overview

The semantic search system consists of three main layers:

1. **Embedding Pipeline** — Converts text into dense vectors using a local transformer model
2. **Vector Storage** — Binary `.embedding` sidecar files generated from markdown content
3. **Search Engine** — Cosine similarity ranking across one or both memory stores

Memories are stored as markdown files with YAML frontmatter. Each memory has a corresponding `.embedding` sidecar containing its pre-computed vector representation. The embedding is derived from the canonical memory content and cached until the content changes (detected via SHA-256 hash).

资料来源：[src/store/memory.ts:1-50]()

## Architecture

```mermaid
graph TD
    subgraph "CLI / MCP Layer"
        H[handlers.ts]
    end
    
    subgraph "Store Layer"
        MS[MemoryStore]
        G[MemoryGraph]
    end
    
    subgraph "I/O Layer"
        FS[File System]
    end
    
    subgraph "Embedding Layer"
        E[Embedder]
        TFM[transformers.js]
        BGE[bge-base-en-v1.5]
    end
    
    H --> MS
    MS --> E
    E --> TFM
    TFM --> BGE
    MS <--> G
    MS <--> FS
    G -.-> MS
    
    style E fill:#e1f5fe
    style BGE fill:#fff3e0
```

### Component Responsibilities

| Component | File | Role |
|-----------|------|------|
| `MemoryStore` | memory-store.ts | Manages scanning, embedding, and search operations |
| `Embedder` | embedder.ts | Wraps transformers.js for local inference |
| `MemoryGraph` | graph.ts | Tracks relations and enriches search results |
| `handlers.ts` | handlers.ts | MCP tool handler for memory_search |

资料来源：[src/store/memory-store.ts:1-100]()

## Embedding Pipeline

### Model Configuration

The system uses `bge-base-en-v1.5` from the BGE (BAAI General Embedding) family. This model produces 768-dimensional dense vectors optimized for semantic similarity tasks.

```typescript
// Resolved from environment: EMBEDDING_MODEL_ID
const DEFAULT_MODEL_ID = 'Xenova/bge-base-en-v1.5';
```

The embedder runs entirely offline via `@huggingface/transformers` (transformers.js), requiring no external API calls or internet connectivity after the initial model load.

资料来源：[src/store/memory-store.ts:100-150]()

### Embedding Derivation

Embeddings are derived from the **canonical memory content**, defined as:

```
${type}\n${name}\n${description}\n${body}
```

The SHA-256 hash of this canonical string is computed and compared against stored hashes to detect content changes. This ensures embeddings are regenerated only when necessary.

```typescript
export const contentSha = (memory: Memory): string =>
  createHash('sha256')
    .update(`${memory.type}\n${memory.name}\n${description}\n${body}`, 'utf8')
    .digest('hex');
```

**Important**: Graph fields (`relations`, `supersedes`) do NOT affect the canonical content or embedding. Adding or removing graph edges does not invalidate the embedding sidecar.

资料来源：[src/store/memory.ts:80-100]()

### Sidecar Format

Each memory generates a binary `.embedding` sidecar file in the same directory as the `.md` file:

```
~/.commonplace/memory/
├── feedback_scope.md
├── feedback_scope.embedding  ← binary float32 vectors
└── architecture.md
    └── architecture.embedding
```

The sidecar is **derived data** — it can be deleted and regenerated at any time from the markdown source.

资料来源：[CLAUDE.md:1-30]()

## Search Workflow

```mermaid
sequenceDiagram
    participant C as MCP Client
    participant H as memory_search Handler
    participant MS as MemoryStore
    participant E as Embedder
    participant FS as File System
    
    C->>H: memory_search({ query, k?, scope? })
    H->>MS: search(query, k, opts)
    
    Note over MS: Load user store<br/>+ project store if present
    
    MS->>E: embed(query)
    E->>E: Load model (lazy)
    E-->>MS: queryVector[768]
    
    MS->>FS: Load all .embedding sidecars
    FS-->>MS: Float32Array vectors
    
    loop Per-memory
        MS->>MS: cosineSimilarity(queryVector, memoryVector)
    end
    
    MS->>G: Filter supersedes, enrich relations
    G-->>MS: Augmented matches
    
    MS-->>H: MemorySearchResult[]
    H-->>C: JSON response
```

### Search Flow Details

1. **Query embedding** — The search query is embedded using the same model as memory content
2. **Vector loading** — All `.embedding` sidecars are loaded into memory (lazy, cached)
3. **Similarity computation** — Brute-force cosine similarity between query vector and all memory vectors
4. **Result filtering** — Superseded memories are excluded by default
5. **Graph enrichment** — Results include outgoing relations from the frontmatter

资料来源：[src/store/memory-store.ts:200-300]()

## Memory Search API

### MCP Tool: `memory_search`

Input schema:

| Argument | Type | Required | Default | Description |
|----------|------|----------|---------|-------------|
| `query` | string | Yes | — | Natural language search query |
| `k` | integer | No | 5 | Maximum number of results to return |
| `type` | MemoryType | No | — | Filter by memory type (user/feedback/project/reference) |
| `includeSuperseded` | boolean | No | false | Include memories superseded by others |
| `expandTypes` | string[] | No | ['builds-on', 'related-to'] | Edge types to include in results |
| `scope` | 'user' \| 'project' | No | merged | Which store to search |

### Response Schema

```typescript
interface MemorySearchResult {
  query: string;
  matches: MemorySearchMatch[];
  scope: 'user' | 'project' | 'merged';
}

interface MemorySearchMatch {
  name: string;
  type: MemoryType;
  description: string;
  body: string;       // Full body verbatim (never truncated)
  score: number;      // Cosine similarity, 3 decimals
  relations: Relation[];  // Outgoing edges from frontmatter
  scope: 'user' | 'project';
}
```

**Note**: The body is never truncated, summarized, or transformed. The caller receives exactly what was persisted.

资料来源：[src/server/handlers.ts:50-120]()

### Default Expand Types

Only `builds-on` and `related-to` are included by default. Other edge types require explicit opt-in:

```typescript
export const DEFAULT_EXPAND_TYPES: readonly EdgeType[] = 
  ['builds-on', 'related-to'] as const;
```

This prevents surprising the agent with `mentions`, `supersedes`, `contradicts`, or `child-of` edges unless requested.

资料来源：[src/server/handlers.ts:15-25]()

## Memory Stores and Scoping

### Dual-Store Architecture

The system supports two independent memory stores:

| Store | Location | Purpose |
|-------|----------|---------|
| User Store | `~/.commonplace/memory` (default) | Personal rules, preferences, feedback |
| Project Store | `<project-root>/.commonplace/memory` | Per-project context |

### Detection Priority

Project store selection follows this priority:

1. `COMMONPLACE_PROJECT_DIR` env var (explicit override)
2. MCP `roots/list` response
3. Current working directory

If none apply, only the user store is loaded.

### Merged Search

When `scope` is omitted, search merges results from both stores by descending score. Each match carries a `scope` tag identifying its source.

资料来源：[src/server/handlers.ts:150-200]()

## Scan and Re-embedding

### Trigger Conditions

The store performs a lazy rescan when:

1. Directory mtime advances past the baseline (new files detected)
2. Memory is saved (triggers immediate re-embedding)

```typescript
// MemoryStore.scan() behavior:
// - Skips if dir mtime hasn't changed (lastScanMtimeMs baseline)
// - Regenerates embedding for any memory with changed sha
// - Stores new dir mtime as baseline after scan
```

### Lazy Re-embedding

On save, the system:
1. Computes `contentSha` for the new content
2. Compares against stored hash
3. Regenerates embedding only if hash differs

This avoids redundant model inference for graph-only changes (relation additions/removals).

资料来源：[src/store/memory-store.ts:250-280]()

## Content-Based Mention Extraction

While not part of the vector search pipeline, `[[name]]` mentions in body text create graph edges that enrich search results:

```typescript
// Mention extraction (DAR-927)
// Scanned during save/scan, not during search
// Creates 'mentions' edges in MemoryGraph
```

Mention extraction is controlled by `COMMONPLACE_EXTRACT_MENTIONS` env var (default: enabled). Regex pattern matches `<name>` tokens against `^[a-z0-9_]+$`.

资料来源：[src/store/mentions.ts:1-50]()

## Related Features

| Feature | Description |
|---------|-------------|
| [Memory List](memory-list) | List memories without semantic scoring |
| [Memory Graph](memory-graph) | Graph traversal and path finding |
| [Memory Link/Unlink](memory-link) | Manual edge creation |
| [Migrate](cli-migrate) | Import from Claude Code auto-memory |

## Configuration Reference

| Variable | Default | Description |
|----------|---------|-------------|
| `EMBEDDING_MODEL_ID` | `Xenova/bge-base-en-v1.5` | HuggingFace model identifier |
| `COMMONPLACE_USER_DIR` | `~/.commonplace/memory` | User store root |
| `COMMONPLACE_PROJECT_DIR` | — | Project store override |
| `COMMONPLACE_EXTRACT_MENTIONS` | `true` | Enable `[[name]]` extraction |
| `COMMONPLACE_EMBED_BATCH_SIZE` | — | Batch size for embedding |

---

<a id='graph-features'></a>

## Graph Features

### 相关页面

相关主题：[Semantic Search](#semantic-search), [MCP Tool Reference](#mcp-tool-reference)

<details>
<summary>Relevant Source Files</summary>

以下源码文件用于生成本页说明：

- [src/store/graph.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/graph.ts)
- [src/store/mentions.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/mentions.ts)
- [src/cli/graph.ts](https://github.com/rickbassham/commonplace/blob/main/src/cli/graph.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [src/server/tools.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/tools.ts)
</details>

# Graph Features

## Overview

The Graph Features system provides a network-based view of memories, enabling relationships between notes to be tracked, queried, and visualized. Rather than treating each memory as an isolated entity, the graph layer exposes connections through authored `relations` in frontmatter, `supersedes` declarations, and automatic `[[name]]` mention extraction from body content.

The graph is not a separate data store—it derives from the same markdown files that form the memory corpus. Each `.md` file in a memory store may declare outgoing edges via its YAML frontmatter, and the in-memory `MemoryGraph` maintains an adjacency list for efficient traversal.

资料来源：[src/store/graph.ts:1-30]()

## Architecture

```mermaid
graph TD
    subgraph "Memory Sources"
        UserStore["User Memory Store<br/>(~/.commonplace)"]
        ProjectStore["Project Memory Store<br/>(./.commonplace)"]
    end
    
    subgraph "Graph Layer"
        MemoryGraph["MemoryGraph"]
        AdjacencyList["Adjacency List<br/>(Map&lt;name, Edge[]&gt;)"]
    end
    
    subgraph "Edge Sources"
        Relations["relations[]<br/>frontmatter field"]
        Supersedes["supersedes[]<br/>frontmatter field"]
        Mentions["[[name]] mentions<br/>body extraction"]
    end
    
    UserStore --> MemoryGraph
    ProjectStore --> MemoryGraph
    MemoryGraph --> AdjacencyList
    Relations --> MemoryGraph
    Supersedes --> MemoryGraph
    Mentions --> MemoryGraph
```

### Core Components

| Component | File | Purpose |
|-----------|------|---------|
| `MemoryGraph` | `src/store/graph.ts` | In-memory graph with adjacency list, edge management, and traversal |
| `Edge` / `DanglingEdge` | `src/store/graph.ts` | Data models for directed edges |
| `MemoryStore` | `src/store/memory-store.ts` | Persists graph edges via frontmatter, maintains in-memory sync |
| `mentions.ts` | `src/store/mentions.ts` | Extracts `[[name]]` tokens from body content |
| `handlers.ts` | `src/server/handlers.ts` | MCP tool handlers for graph operations |
| `graph.ts` CLI | `src/cli/graph.ts` | Command-line graph visualization |

资料来源：[src/store/graph.ts:25-45]()

## Edge Types

The graph supports a union of edge types drawn from three sources:

| Edge Type | Source | Description |
|-----------|--------|-------------|
| `related-to` | `relations[]` frontmatter | General-purpose association between memories |
| `builds-on` | `relations[]` frontmatter | Memory that extends or depends on another |
| `contradicts` | `relations[]` frontmatter | Memory that opposes or negates another |
| `child-of` | `relations[]` frontmatter | Hierarchical parent-child relationship |
| `supersedes` | `supersedes[]` frontmatter | Memory that replaces or renders another obsolete |
| `mentions` | Body `[[name]]` extraction | Memory references another by name in its body |

资料来源：[src/store/graph.ts:15-25]()

### Frontmatter Declaration

Memories declare outgoing edges in YAML frontmatter:

```yaml
---
name: feedback_scope
description: Don't shrink scope unilaterally
type: feedback
relations:
  - to: scope_contract
    type: builds-on
  - to: legacy_approach
    type: supersedes
---
<memory body content>
```

资料来源：[src/store/memory.ts:1-30]()

## MemoryGraph Module

The `MemoryGraph` class maintains an in-memory adjacency list and exposes methods for adding, removing, and querying edges.

### Data Model

```typescript
// src/store/graph.ts
export interface Edge {
  from: string;
  to: string;
  type: EdgeType;
}

export interface DanglingEdge {
  from: string;
  to: string;
  type: string;  // widened to string for extensibility
}
```

资料来源：[src/store/graph.ts:30-45]()

### Graph Construction

The graph is built during store initialization by iterating all memories and emitting edges for each source:

1. For each memory, emit one edge per `relations[]` entry
2. Emit one `supersedes` edge per `supersedes[]` entry
3. Emit one `mentions` edge per `[[name]]` token in body (if extraction enabled)

```mermaid
graph LR
    A["scan()"] --> B["For each memory"]
    B --> C["Parse relations[]"]
    B --> D["Parse supersedes[]"]
    B --> E["Extract [[name]] mentions"]
    C --> F["addEdge()"]
    D --> F
    E --> F
```

资料来源：[src/store/graph.ts:50-80]()

## Mentions Extraction

The `[[name]]` mention extractor (DAR-927) parses body content for wiki-style name references and surfaces them as graph edges.

### Tokenizer Rules

- Extracts tokens matching the pattern `[[name]]` where `name` conforms to `^[a-z0-9_]+$`
- Names must match the same validation rule used for memory filenames
- Tokens not matching this pattern are silently ignored
- Code fences and inline code are **not** excluded (per contract, the tokenizer operates purely on regex over body content)

### Configuration

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `COMMONPLACE_EXTRACT_MENTIONS` | `'true'` | Set to `'false'` to disable mention extraction |

资料来源：[src/store/mentions.ts:1-45]()

## MCP Tools

The MCP server exposes graph operations as JSON-RPC tools.

### memory_graph

Traverses the graph starting from a named memory and returns the neighborhood.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `name` | string | required | Root memory name |
| `depth` | integer | `1` | Maximum edge traversal depth |
| `types` | string[] | authored + supersedes | Edge types to follow |
| `direction` | `'in' \| 'out' \| 'both'` | `'both'` | Traversal direction |
| `scope` | `'user' \| 'project'` | auto | Memory scope filter |

资料来源：[src/server/tools.ts:1-50]()

### memory_link

Creates a directed edge between two memories.

| Parameter | Type | Description |
|-----------|------|-------------|
| `from` | string | Source memory name |
| `to` | string | Target memory name |
| `type` | RelationType | Edge type (one of the four authored types) |

资料来源：[src/store/memory-store.ts:80-120]()

### memory_unlink

Removes one or more edges from a memory.

| Parameter | Type | Description |
|-----------|------|-------------|
| `from` | string | Source memory name |
| `to` | string | Target memory name |
| `type` | RelationType (optional) | Specific edge type to remove; if omitted, removes all edges to target |

资料来源：[src/store/memory-store.ts:120-160]()

## CLI Integration

The `commonplace graph` command provides command-line graph visualization (DAR-933).

### Usage

```bash
commonplace graph <name> [options]
```

### Options

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--depth, -d` | integer | `1` | Traversal depth |
| `--types, -t` | string | all except mentions | Comma-separated edge types |
| `--direction, -D` | `in\|out\|both` | `both` | Traversal direction |
| `--format, -f` | `mermaid\|json\|dot` | `mermaid` | Output format |
| `--scope, -s` | `user\|project\|both` | `both` | Memory scope |

资料来源：[src/cli/graph.ts:1-80]()

### Output Formats

#### Mermaid (default)

```mermaid
flowchart LR
  memory_a["memory_a (feedback)"]
  memory_b["memory_b (project)"]
  memory_a -- "builds-on" --> memory_b
```

#### JSON

Returns the `MemoryGraphResult` structure verbatim for piping to external tools:

```json
{
  "root": "memory_a",
  "nodes": [...],
  "edges": [...]
}
```

#### DOT

Graphviz DOT format for archival or large-graph workflows.

资料来源：[src/cli/graph.ts:80-150]()

## Dangling Edge Detection

The graph detects edges whose `to` target does not correspond to any existing memory. These "dangling edges" are identified during graph construction and can be pruned.

### Detection

| Step | Description |
|------|-------------|
| 1 | Build adjacency list from all memories |
| 2 | Collect all known memory names |
| 3 | For each edge, verify `to` exists in name set |
| 4 | Flag edges where `to` is absent |

### Pruning

The `migrate prune` command removes dangling edges:

```bash
commonplace migrate --prune [--dry-run]
```

Results show per-memory counts and total edges removed.

资料来源：[src/store/graph.ts:80-120]()

## Out of Scope

The following features are explicitly deferred to future iterations:

| Feature | Issue | Notes |
|---------|-------|-------|
| Verifying referenced names exist on disk | DAR-926 | Dangling detection exists; validation does not |
| In-memory adjacency list builder | DAR-926 | Graph derives from store, not built independently |
| `[[name]]` backlink rendering | DAR-927 | AC excludes wiki-style rendering |
| Centrality / PageRank | DAR-931 | Future ranking metrics |
| Traversal / path queries | DAR-932 | Neighborhood walk only for v0.1 |

资料来源：[src/store/graph.ts:10-20]()

## Summary

The Graph Features system treats the memory corpus as a navigable network. Edges originate from three sources—authored `relations`, explicit `supersedes` declarations, and automatic `[[name]]` mention extraction—and are consolidated into a unified in-memory adjacency list. The MCP server exposes CRUD operations on edges, the CLI provides visualization, and the architecture intentionally keeps the graph derived rather than authoritative, ensuring it never diverges from the underlying markdown files.

---

<a id='mcp-tool-reference'></a>

## MCP Tool Reference

### 相关页面

相关主题：[Server Handlers](#server-handlers), [Semantic Search](#semantic-search), [Graph Features](#graph-features)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/server/tools.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/tools.ts)
- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [README.md](https://github.com/rickbassham/commonplace/blob/main/README.md)
</details>

# MCP Tool Reference

The Model Context Protocol (MCP) server exposes a set of tools that enable external clients—primarily Claude Code—to interact with the commonplace book. These tools provide CRUD operations for memories, semantic search, and graph management. The MCP stdio server acts as the bridge between the local embedding pipeline and any MCP-compatible client.

资料来源：[src/server/handlers.ts:1-25]()

## Architecture Overview

The MCP server is implemented in `src/server/` and wired to the memory store layer. Tool requests arrive via stdio JSON-RPC, are validated in handler functions, dispatched to the `MemoryStore`, and returned as JSON-serializable responses wrapped in MCP content blocks.

```mermaid
graph TD
    A[MCP Client<br/>Claude Code] --> B[stdio JSON-RPC]
    B --> C[server.ts<br/>CallToolRequest Dispatcher]
    C --> D[handlers.ts<br/>Tool Handlers]
    D --> E[MemoryStore]
    E --> F[memory.ts<br/>YAML + Embedding I/O]
    E --> G[graph.ts<br/>In-memory Graph]
    F --> H[.md Files<br/>+ .embedding Sidecars]
```

资料来源：[src/server/handlers.ts:17-23]()

## Tool Categories

The MCP server exposes two categories of tools:

| Category | Purpose | Tools |
|----------|---------|-------|
| **Memory CRUD** | Create, read, delete, and search memories | `memory_save`, `memory_list`, `memory_delete`, `memory_search` |
| **Graph Management** | Manage relationships between memories | `memory_link`, `memory_unlink` |

资料来源：[README.md:1-50]()

## Scope Model

Every tool accepts an optional `scope` argument that selects which memory store to address:

| Scope | Description |
|-------|-------------|
| `user` | Cross-project memories stored in `COMMONPLACE_USER_DIR` |
| `project` | Project-specific memories stored in `COMMONPLACE_PROJECT_DIR` |

When `scope` is omitted, reads merge across both stores and writes default to `user`.

资料来源：[src/server/handlers.ts:30-35]()

## Memory Types

All memory tools operate on a four-element taxonomy defined in the store layer:

| Type | Purpose |
|------|---------|
| `user` | Personal rules, preferences, and identity facts about the human operator |
| `feedback` | Corrections and lessons learned from prior agent behaviour; persistent course-corrections |
| `project` | Per-project context like architecture notes, repo conventions, and decisions |
| `reference` | Durable, neutral knowledge: API shapes, formulas, citations |

资料来源：[src/store/memory.ts:1-30]()

## Memory CRUD Tools

### memory_save

Save a memory as a markdown file with YAML frontmatter and a derived embedding sidecar. Refuses to overwrite an existing entry; the contract is delete + save.

**Input Schema:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | Yes | Memory name. Must match `^[a-z0-9_]+$`. Becomes the filename stem. |
| `type` | `user \| feedback \| project \| reference` | Yes | One of the four memory types. |
| `description` | string | Yes | Short description used in search result summaries. |
| `body` | string | Yes | Markdown content of the memory. |
| `scope` | `'user' \| 'project'` | No | Which store to write to. Defaults to `'user'`. |

资料来源：[README.md:50-80]()

**Validation:** Arguments are validated manually at handler entry. The handler checks `name` against `validateName`, `type` against `MEMORY_TYPES`, and passes store errors through unchanged.

资料来源：[src/server/handlers.ts:40-55]()

### memory_list

List all memories, optionally filtered by type.

**Input Schema:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | `user \| feedback \| project \| reference` | No | Filter to memories of this type only. |
| `scope` | `'user' \| 'project'` | No | Which store to query. Defaults to merging both stores. |

**Response:** Returns an array of memory entries with `name`, `type`, `description`, and `body` fields.

资料来源：[src/server/handlers.ts:60-75]()

### memory_delete

Delete a memory by name from the specified store.

**Input Schema:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | Yes | Name of the memory to delete. |
| `scope` | `'user' \| 'project'` | No | Which store to delete from. Defaults to `'user'`. |

资料来源：[src/server/handlers.ts:80-95]()

### memory_search

Semantic search across all memories using local embeddings.

**Input Schema:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `query` | string | Yes | Natural language search query. |
| `type` | `user \| feedback \| project \| reference` | No | Filter to memories of this type only. |
| `limit` | number | No | Maximum results to return. Defaults to `DEFAULT_SEARCH_LIMIT`. |
| `scope` | `'user' \| 'project'` | No | Which store to search. Defaults to merging both stores. |

资料来源：[src/server/handlers.ts:100-130]()

**Response Enrichment:** Search results include relations from connected memories and exclude notes marked as superseded. This is handled by `MemoryGraph` integration.

资料来源：[src/store/memory-store.ts:1-50]()

## Graph Management Tools

### memory_link

Create a directed edge between two memories, establishing a typed relationship.

**Input Schema:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `from` | string | Yes | Source memory name. |
| `to` | string | Yes | Target memory name. |
| `type` | `RelationType \| 'supersedes'` | Yes | Relationship type. |
| `scope` | `'user' \| 'project'` | No | Which store contains both memories. |

**Relation Types:**

| Type | Description |
|------|-------------|
| `builds-on` | The source memory builds upon the target |
| `implements` | The source memory implements the target |
| `relates-to` | General relationship between memories |
| `supersedes` | The source memory supersedes the target |

**Behavior:**

- Throws if the edge already exists (no duplicate edges).
- Updates the `relations` array in the source memory's YAML frontmatter.
- Mutates the in-memory entry in place for consistency.
- Refreshes the directory mtime baseline to avoid wasteful rescans.

资料来源：[src/store/memory-store.ts:200-280]()

```mermaid
graph LR
    A[memory: feedback_scope] -->|builds-on| B[memory: scope_management]
    A -->|supersedes| C[memory: old_feedback_v1]
```

### memory_unlink

Remove a directed edge between two memories.

**Input Schema:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `from` | string | Yes | Source memory name. |
| `to` | string | Yes | Target memory name. |
| `type` | `RelationType \| 'supersedes'` | No | Specific edge type to remove. If omitted, removes ALL edges from `from` to `to`. |

**Behavior:**

- No-op when the requested edge does not exist (no atomic write, no graph mutation).
- Returns `{ ..., note: '<reason>' }` for friendly messaging when edges don't exist.
- When edges are removed, writes the source `.md` through `atomicWrite`, updates the in-memory entry in place, and calls `MemoryGraph.removeEdge`.

资料来源：[src/store/memory-store.ts:300-400]()

## Validation Strategy

Tool argument validation is deliberately manual rather than schema-library-based:

- Zero new dependencies for validation logic.
- Rejection messages are tailored to name the offending field.
- Store layer errors are passed through unchanged to preserve context.

资料来源：[src/server/handlers.ts:15-20]()

## Response Format

All tool responses are JSON-serializable shapes wrapped in a single text content block by the MCP server's `CallToolRequest` dispatcher in `server.ts`. The dispatcher handles both the CRUD tools and graph tools uniformly.

资料来源：[src/server/handlers.ts:20-23]()

## Environment Variables

The MCP server respects the following environment variables:

| Variable | Default | Description |
|----------|---------|-------------|
| `COMMONPLACE_USER_DIR` | `~/.commonplace` | Directory for user-scoped memories |
| `COMMONPLACE_PROJECT_DIR` | `<cwd>/.commonplace` | Directory for project-scoped memories |
| `COMMONPLACE_EXTRACT_MENTIONS` | `'true'` | Enable `[[name]]` mention extraction from body content |

资料来源：[src/store/mentions.ts:1-30]()

## MCP Server Binary

The MCP server is exposed as the `commonplace-mcp` binary, separate from the CLI's `commonplace` command. This separation ensures the MCP stdio framing channel is never polluted by CLI output.

资料来源：[src/index.ts:1-30]()

## See Also

- [Memory Store Architecture](./memory-store-architecture.md) — In-depth details on embedding, sidecars, and graph integration
- [CLI Reference](./cli-reference.md) — The `commonplace` CLI for migration and graph commands
- [Memory File Format](./memory-file-format.md) — YAML frontmatter schema and content hashing

---

<a id='server-handlers'></a>

## Server Handlers

### 相关页面

相关主题：[MCP Tool Reference](#mcp-tool-reference), [System Architecture](#system-architecture)

<details>
<summary>Related Source Files</summary>

以下源码文件用于生成本页说明：

- [src/server/handlers.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/handlers.ts)
- [src/server/server.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/server.ts)
- [src/server/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/server/index.ts)
- [src/store/memory-store.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory-store.ts)
- [src/store/memory.ts](https://github.com/rickbassham/commonplace/blob/main/src/store/memory.ts)
- [src/index.ts](https://github.com/rickbassham/commonplace/blob/main/src/index.ts)
</details>

# Server Handlers

## Overview

Server Handlers are the core request-processing layer in the Commonplace MCP server. They implement the MCP (Model Context Protocol) tool handlers that bridge the MCP client interface to the underlying `MemoryStore` persistence layer. Each handler is a factory function that produces an MCP-compatible request handler with consistent validation, error handling, and response shaping.

The handler system is designed around:

- **Factory pattern**: Each handler is created via a factory function (e.g., `createMemorySaveHandler`, `createMemoryListHandler`) that encapsulates store access and validation logic.
- **Typed interfaces**: All inputs and outputs use TypeScript interfaces exported from `handlers.ts` for compile-time safety.
- **Scope awareness**: Handlers support both `user` and `project` scopes, allowing memories to be stored in different directories.
- **Graph integration**: Handlers can optionally work with the `MemoryGraph` to enrich responses with relation data.

## Architecture

### Request Flow

```mermaid
graph TD
    A[MCP Request] --> B[Server dispatch]
    B --> C{Handler Factory}
    C --> D[createMemorySaveHandler]
    C --> E[createMemoryListHandler]
    C --> F[createMemorySearchHandler]
    C --> G[createMemoryDeleteHandler]
    C --> H[createMemoryLinkHandler]
    C --> I[createMemoryUnlinkHandler]
    D --> J[MemoryStore]
    E --> J
    F --> J
    G --> J
    H --> J
    I --> J
    J --> K[MemoryGraph]
    J --> L[File System]
    K --> M[Relation Enrichment]
    L --> N[.md + .embedding files]
    M --> O[MCP Response]
    N --> O
```

### Handler Factory Pattern

Each handler follows the factory pattern documented in `handlers.ts`:

```typescript
export const createMemorySaveHandler = (opts: CreateHandlerOptions) => {
  const { userStore, projectStore } = resolveStores(opts);
  return async (request: MemorySaveRequest): Promise<MemorySaveResult> => {
    // Validation and processing
  };
};
```

资料来源：[src/server/handlers.ts:1-50]()

## Handler Types

### MemorySaveHandler

**Purpose**: Creates or updates a memory note with YAML frontmatter and markdown body.

**Factory**: `createMemorySaveHandler(options: CreateHandlerOptions)`

**Request Interface**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | Yes | Memory identifier matching `^[a-z0-9_]+$` |
| `type` | `MemoryType` | Yes | One of: `user`, `feedback`, `project`, `reference` |
| `description` | `string` | Yes | Brief description for search relevance |
| `body` | `string` | Yes | Markdown content |
| `scope` | `Scope` | No | `user` (default) or `project` |

**Response Interface**:

```typescript
export interface MemorySaveResult {
  saved: {
    name: string;
    type: MemoryType;
    description: string;
  };
  path: string;
  /** Which store the memory was written to (DAR-924) */
  scope: Scope;
}
```

资料来源：[src/server/handlers.ts:80-95]()

**Validation**:
- Name must match `NAME_PATTERN` (`^[a-z0-9_]+$`)
- Type must be one of `MEMORY_TYPES`
- Scope is validated via `validateScope()` helper
- Duplicate relations are deduplicated on write

资料来源：[src/store/memory.ts:1-50]()

### MemoryListHandler

**Purpose**: Returns all memories from a store, optionally filtered by type and scope.

**Factory**: `createMemoryListHandler(options: CreateHandlerOptions)`

**Request Interface**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `MemoryType` | No | Filter by memory type |
| `scope` | `Scope` | No | Filter by scope (`user`/`project`) |

**Response Interface**:

```typescript
export interface MemoryListResult {
  memories: Array<{
    name: string;
    type: MemoryType;
    description: string;
    /** Which store this entry came from */
    scope: Scope;
  }>;
}
```

资料来源：[src/server/handlers.ts:97-108]()

### MemorySearchHandler

**Purpose**: Performs semantic search across memory embeddings using cosine similarity.

**Factory**: `createMemorySearchHandler(options: CreateHandlerOptions)`

**Request Interface**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `query` | `string` | Yes | Natural language search query |
| `limit` | `number` | No | Max results (default: 10) |
| `type` | `MemoryType` | No | Filter by type |
| `scope` | `Scope` | No | Filter by scope |
| `expand` | `boolean` | No | Include related memories (default: true) |
| `expandTypes` | `EdgeType[]` | No | Which relation types to expand |

**Response Interface**:

```typescript
export interface MemorySearchMatch {
  name: string;
  type: MemoryType;
  description: string;
  body: string;          // Full body, never truncated
  score: number;         // Cosine similarity, 3 decimals
  relations: Relation[]; // Outgoing graph edges
}
```

资料来源：[src/server/handlers.ts:35-60]()

**Expand Types Configuration**:

| Type | Default | Description |
|------|---------|-------------|
| `builds-on` | Yes | Included by default |
| `related-to` | Yes | Included by default |
| `mentions` | No | Requires opt-in |
| `supersedes` | No | Requires opt-in |
| `contradicts` | No | Requires opt-in |
| `child-of` | No | Requires opt-in |

```typescript
export const DEFAULT_EXPAND_TYPES: readonly EdgeType[] = ['builds-on', 'related-to'] as const;
```

资料来源：[src/server/handlers.ts:20-30]()

**Response Enrichment**:

When `expand` is enabled, the handler enriches results by:
1. Fetching direct matches from `MemoryStore.search()`
2. For each match, query `MemoryGraph` for outgoing edges matching `expandTypes`
3. Add expanded memories to response (marked with `expanded: true`)
4. Exclude superseded memories unless explicitly requested

### MemoryDeleteHandler

**Purpose**: Removes a memory from the store by name.

**Factory**: `createMemoryDeleteHandler(options: CreateHandlerOptions)`

**Request Interface**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | Yes | Memory name to delete |
| `scope` | `Scope` | No | Scope hint for store resolution |

**Response Interface**:

```typescript
export interface MemoryDeleteResult {
  deleted: string;
  scope: Scope;
}
```

### MemoryLinkHandler

**Purpose**: Creates a directed edge between two memories in the graph.

**Factory**: `createMemoryLinkHandler(options: CreateHandlerOptions)`

**Request Interface**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `from` | `string` | Yes | Source memory name |
| `to` | `string` | Yes | Target memory name |
| `type` | `RelationType` | Yes | Edge type |
| `scope` | `Scope` | No | Scope for operation |

**Relation Types**:

| Type | Description |
|------|-------------|
| `builds-on` | Memory extends or depends on another |
| `related-to` | Memory has loose association |
| `contradicts` | Memory refutes another |
| `child-of` | Memory is subordinate to another |

**Internal Behavior**:
The handler delegates to `MemoryStore.linkEdge()` which:
1. Validates both memory names exist
2. Adds the relation to the source memory's frontmatter
3. If type is `supersedes`, also adds to `supersedes` list
4. Writes updated markdown via atomic write
5. Updates the in-memory `MemoryGraph`

资料来源：[src/store/memory-store.ts:1-50]()

### MemoryUnlinkHandler

**Purpose**: Removes a directed edge between two memories.

**Factory**: `createMemoryUnlinkHandler(options: CreateHandlerOptions)`

**Request Interface**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `from` | `string` | Yes | Source memory name |
| `to` | `string` | Yes | Target memory name |
| `type` | `RelationType \| 'supersedes'` | No | Specific edge type to remove. Omit to remove all edges to target |

**Behavior**:
- No-op when the requested edge does not exist
- When `type` is omitted, removes ALL edges from `from` -> `to`
- Returns `{ relations, supersedes, note }` describing the new state

资料来源：[src/store/memory-store.ts:100-150]()

## Store Resolution

Handlers use the `resolveStores` helper to map request scopes to actual store instances:

```typescript
const resolveStores = (opts: { store?: MemoryStore; userStore?: MemoryStore; projectStore?: MemoryStore }) => {
  const userStore = opts.userStore ?? opts.store;
  if (userStore === undefined) {
    throw new Error(`${toolName}: handler factory requires a userStore`);
  }
  return { userStore, projectStore: opts.projectStore };
};
```

资料来源：[src/server/handlers.ts:65-75]()

## Scope System

The scope system allows memories to be stored in different locations:

| Scope | Directory | Use Case |
|-------|-----------|----------|
| `user` | `COMMONPLACE_USER_DIR` | Cross-project knowledge |
| `project` | `COMMONPLACE_PROJECT_DIR` | Project-specific facts |

**Validation**:

```typescript
const SCOPES = ['user', 'project'] as const;
type Scope = typeof SCOPES[number];

const isScope = (v: unknown): v is Scope =>
  typeof v === 'string' && (SCOPES as readonly string[]).includes(v);
```

资料来源：[src/server/handlers.ts:55-70]()

## Server Initialization

The handlers are wired into the MCP server in `server.ts`:

```mermaid
graph LR
    A[MCP Server] --> B[registerTools]
    B --> C[MemorySaveHandler]
    B --> D[MemoryListHandler]
    B --> E[MemorySearchHandler]
    B --> F[MemoryDeleteHandler]
    B --> G[MemoryLinkHandler]
    B --> H[MemoryUnlinkHandler]
```

The main entry point (`src/index.ts`) dispatches to the server module:

```typescript
if (argv[0] === 'graph') {
  const result = await graphMain({ argv, ... });
  return result.exitCode;
}
```

资料来源：[src/index.ts:1-30]()

## Error Handling

All handlers follow consistent error handling patterns:

| Error Type | Cause | Response |
|------------|-------|----------|
| Validation error | Invalid input format | Throws with descriptive message |
| Not found | Memory doesn't exist | Handled by store methods |
| Duplicate edge | Edge already exists | Handled by `linkEdge` |
| Store unavailable | Store not initialized | Factory throws on construction |

## Integration with MemoryStore

Handlers delegate to `MemoryStore` methods for all persistence operations:

| Handler | Store Method |
|---------|--------------|
| MemorySaveHandler | `MemoryStore.save()` |
| MemoryListHandler | `MemoryStore.all()` |
| MemorySearchHandler | `MemoryStore.search()` |
| MemoryDeleteHandler | `MemoryStore.delete()` |
| MemoryLinkHandler | `MemoryStore.linkEdge()` |
| MemoryUnlinkHandler | `MemoryStore.unlinkEdge()` |

资料来源：[src/store/memory-store.ts:1-200]()

## Mention Extraction

When a memory is saved, the body is scanned for `[[name]]` wiki-link syntax:

```mermaid
graph TD
    A[Memory Save] --> B[Extract mentions]
    B --> C{NAME_PATTERN test}
    C -->|pass| D[Add to MemoryGraph]
    C -->|fail| E[Skip]
    D --> F[edgesPruned count]
```

This extraction is controlled by the `COMMONPLACE_EXTRACT_MENTIONS` environment variable (default: enabled).

资料来源：[src/store/mentions.ts:1-60]()

## Configuration

Handlers are configured via environment variables managed in DAR-913:

| Variable | Default | Effect |
|----------|---------|--------|
| `COMMONPLACE_USER_DIR` | `~/.commonplace` | User memory directory |
| `COMMONPLACE_PROJECT_DIR` | `./.commonplace` | Project memory directory |
| `COMMONPLACE_MODEL_ID` | `Xenova/bge-base-en-v1.5` | Embedding model |
| `COMMONPLACE_EXTRACT_MENTIONS` | `true` | Enable `[[name]]` extraction |

## Related Documentation

- **Memory Store**: `src/store/memory-store.ts` - Persistence layer
- **Memory Types**: `src/store/memory.ts` - Type definitions
- **Atomic Write**: `src/store/atomic-write.ts` - Safe file operations
- **Mention Extraction**: `src/store/mentions.ts` - `[[name]]` parsing
- **CLI Entry**: `src/index.ts` - Command dispatch

---

---

## Doramagic 踩坑日志

项目：rickbassham/commonplace

摘要：发现 8 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：能力坑 - 能力判断依赖假设。

## 1. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 建议检查：将假设转成下游验证清单。
- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。
- 证据：capability.assumptions | github_repo:1232879661 | https://github.com/rickbassham/commonplace | README/documentation is current enough for a first validation pass.

## 2. 运行坑 · 运行可能依赖外部服务

- 严重度：medium
- 证据强度：source_linked
- 发现：项目说明出现 external service/cloud/webhook/database 等运行依赖关键词。
- 对用户的影响：本地安装成功不等于能力可用，外部服务不可用会阻断体验。
- 建议检查：确认是否有离线 demo、mock 数据或可替代服务。
- 防护动作：外部服务依赖未明确时，不把本地安装成功等同于能力可用。
- 证据：packet_text.keyword_scan | github_repo:1232879661 | https://github.com/rickbassham/commonplace | matched external service / cloud / webhook / database keyword

## 3. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。
- 证据：evidence.maintainer_signals | github_repo:1232879661 | https://github.com/rickbassham/commonplace | last_activity_observed missing

## 4. 安全/权限坑 · 下游验证发现风险项

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：下游已经要求复核，不能在页面中弱化。
- 建议检查：进入安全/权限治理复核队列。
- 防护动作：下游风险存在时必须保持 review/recommendation 降级。
- 证据：downstream_validation.risk_items | github_repo:1232879661 | https://github.com/rickbassham/commonplace | no_demo; severity=medium

## 5. 安全/权限坑 · 存在安全注意事项

- 严重度：medium
- 证据强度：source_linked
- 发现：No sandbox install has been executed yet; downstream must verify before user use.
- 对用户的影响：用户安装前需要知道权限边界和敏感操作。
- 建议检查：转成明确权限清单和安全审查提示。
- 防护动作：安全注意事项必须面向用户前置展示。
- 证据：risks.safety_notes | github_repo:1232879661 | https://github.com/rickbassham/commonplace | No sandbox install has been executed yet; downstream must verify before user use.

## 6. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 建议检查：把风险写入边界卡，并确认是否需要人工复核。
- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。
- 证据：risks.scoring_risks | github_repo:1232879661 | https://github.com/rickbassham/commonplace | no_demo; severity=medium

## 7. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。
- 防护动作：issue/PR 响应未知时，必须提示维护风险。
- 证据：evidence.maintainer_signals | github_repo:1232879661 | https://github.com/rickbassham/commonplace | issue_or_pr_quality=unknown

## 8. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。
- 证据：evidence.maintainer_signals | github_repo:1232879661 | https://github.com/rickbassham/commonplace | release_recency=unknown

<!-- canonical_name: rickbassham/commonplace; human_manual_source: deepwiki_human_wiki -->
