mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
feat: add Claude Code marketplace plugin with service and expert tiers Add comprehensive Claude Code marketplace plugin supporting two paradigms for managing markdown vaults: - reme-service: Service-tier plugin with high-level MCP tools (retrieve/remember/maintain) where reme2 internals handle R-M-W loop - reme-expert: Expert-tier plugin where Claude Code agent runs R-M-W loop directly using raw memory_* primitives guided by reme protocol skill Both plugins implement identical 4-phase work paradigm: - Recall: Retrieve relevant context with ranking by relevance/proximity - Log: Record event facts and raw materials via idempotent event-folder upsert - Distill: Promote events to topic graph via R-M-W loop - Maintain: Vault hygiene sweep with lint and decay operations Include marketplace configuration, documentation, MCP server setup, subagents (reme-distiller, reme-curator), hooks (PreCompact, SessionEnd, Stop), and slash commands (/reme-distill, /reme-recall, /reme-clean). Remove outdated plugin entries from gitignore. ```
This commit is contained in:
parent
dd2de16481
commit
ad6368fdf0
22 changed files with 1309 additions and 2 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -44,5 +44,3 @@ meta_memory/*
|
|||
memories/*
|
||||
.reme/*
|
||||
/vault
|
||||
/reme-plugin
|
||||
/reme2/vault
|
||||
20
reme-plugin/.claude-plugin/marketplace.json
Normal file
20
reme-plugin/.claude-plugin/marketplace.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "reme-marketplace",
|
||||
"owner": {
|
||||
"name": "huangsen"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "reme-service",
|
||||
"source": "./plugins/reme-service",
|
||||
"description": "Service-tier markdown vault — three high-level MCP tools (retrieve / remember / maintain). reme2 internals own the R-M-W loop; the agent just hands off material.",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
{
|
||||
"name": "reme-expert",
|
||||
"source": "./plugins/reme-expert",
|
||||
"description": "Expert-tier markdown vault — Claude Code's agent runs the R-M-W loop directly using raw memory_* primitives, guided by the reme protocol skill + reme-distiller / reme-curator subagents.",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
122
reme-plugin/README.md
Normal file
122
reme-plugin/README.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# reme — Claude Code marketplace
|
||||
|
||||
Two paradigms for managing a markdown vault from Claude Code, packaged as
|
||||
two installable plugins. **The 4-phase work paradigm is identical
|
||||
across both** — only the locus of computation for the LLM-driven phases
|
||||
(Distill, Maintain) differs.
|
||||
|
||||
## The 4-phase paradigm (both plugins)
|
||||
|
||||
Every interaction with the vault falls into one of four phases:
|
||||
|
||||
| Phase | What | Trigger |
|
||||
|---|---|---|
|
||||
| **Recall** | Retrieve relevant context (chunks ranked by relevance + graph proximity). | Intent-driven: user asks about prior work or the task needs prior context. |
|
||||
| **Log** | Record event facts + raw materials into an idempotent event-folder upsert. | (a) intent-driven during the task; (b) **PreCompact hook** to dump volatile state. |
|
||||
| **Distill** | Promote events into the topic graph via R-M-W (read → mutate → write). | (a) intent-driven at task wrap; (b) **SessionEnd hook**; (c) **Stop hook** warns if skipped. |
|
||||
| **Maintain** | Vault hygiene sweep — lint + decay. | Periodic / user suspects drift. No hook auto-fires. |
|
||||
|
||||
The triggers + hook set are **identical** in both plugins. What changes
|
||||
is which tool / mechanism realizes each phase, and where the LLM-driven
|
||||
loops actually run.
|
||||
|
||||
## Service tier vs Expert tier
|
||||
|
||||
| Phase | [`reme-service`](./plugins/reme-service) — work runs **inside reme2** | [`reme-expert`](./plugins/reme-expert) — work runs **outside reme2** |
|
||||
|---|---|---|
|
||||
| Recall | `retrieve` MCP tool | `memory_search` / `memory_graph_search` MCP tools, `/reme-recall` slash |
|
||||
| Log | `remember(mode=log, name=...)` MCP tool | `sync(name=...)` MCP tool (+ `memory_update` / `memory_property_update` for surgical edits) |
|
||||
| Distill | `remember(mode=distill, ...)` MCP tool → **Ingestor's internal ReActAgent** runs the R-M-W loop inside reme2 | `/reme-distill` slash → **`reme-distiller` subagent** runs the R-M-W loop outside reme2 (own context window) |
|
||||
| Maintain | `maintain(...)` MCP tool → **Maintainer class** runs the sweep inside reme2 | `/reme-clean` slash → **`reme-curator` subagent** runs the sweep outside reme2 |
|
||||
|
||||
Both plugins ship the same hook set: **PreCompact / SessionEnd / Stop**.
|
||||
The prompt content differs only in which tool / slash command to call.
|
||||
Recall and Log are thin no-LLM primitives in both modes — only the MCP
|
||||
names differ.
|
||||
|
||||
Both plugins are built on [ReMe2](../reme2) (file_store + watcher + the
|
||||
three memory services) and the 4-axis Memory schema in
|
||||
`reme2/memory/schema/`. Both ship a copy of the canonical
|
||||
`reme2/memory/protocol.md` so the rules (4-axis schema, status state
|
||||
machine, claim-role confidence, wikilink uniqueness, R-M-W decision
|
||||
tree) are the same single source of truth across plugin and reme2.
|
||||
|
||||
## Pick one
|
||||
|
||||
| | `reme-service` | `reme-expert` |
|
||||
|---|---|---|
|
||||
| Cognitive load | Low — call three high-level tools and trust the loops | High — agent runs its own R-M-W following the protocol |
|
||||
| Best when | The agent should focus on its primary task; memory is plumbing | The agent should be a first-class citizen of the vault — every R-M-W decision visible and auditable |
|
||||
| Subagents | None | `reme-distiller`, `reme-curator` |
|
||||
| Slash commands | None (the three MCP tools are enough) | `/reme-distill`, `/reme-recall`, `/reme-clean` |
|
||||
|
||||
## Install
|
||||
|
||||
```text
|
||||
/plugin marketplace add /Users/huangsen/codes/ReMe/reme-plugin
|
||||
/plugin install reme-service # OR
|
||||
/plugin install reme-expert
|
||||
```
|
||||
|
||||
Restart Claude Code so the MCP server / hooks pick up.
|
||||
|
||||
Set `VAULT_PATH` so the MCP server points at your vault directory:
|
||||
|
||||
```bash
|
||||
export VAULT_PATH=/path/to/your/vault
|
||||
```
|
||||
|
||||
If unset, falls back to `./vault` relative to launch CWD (the dev default).
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
reme-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── marketplace.json # lists both plugins
|
||||
├── README.md # this file
|
||||
└── plugins/
|
||||
├── reme-service/ # Service-tier
|
||||
│ ├── .claude-plugin/plugin.json
|
||||
│ ├── .mcp.json # config=reme2/config/service.yaml
|
||||
│ ├── protocol.md # canonical protocol (copied from reme2/)
|
||||
│ ├── skills/reme-service/SKILL.md
|
||||
│ ├── hooks/ # PreCompact / SessionEnd / Stop
|
||||
│ └── README.md
|
||||
└── reme-expert/ # Expert-tier
|
||||
├── .claude-plugin/plugin.json
|
||||
├── .mcp.json # config=reme2/config/expert.yaml
|
||||
├── protocol.md # canonical protocol (copied from reme2/)
|
||||
├── skills/reme-expert/SKILL.md
|
||||
├── hooks/ # PreCompact / SessionEnd / Stop
|
||||
├── agents/ # reme-distiller, reme-curator subagents
|
||||
├── commands/ # /reme-distill, /reme-recall, /reme-clean
|
||||
└── README.md
|
||||
```
|
||||
|
||||
Each plugin's MCP server is `python -m reme2.mcp.server` with
|
||||
`PYTHONPATH=${CLAUDE_PLUGIN_ROOT}/../../..` so it resolves the sibling
|
||||
`reme2/` package without pip install. The two plugins differ only in
|
||||
which config they pin (`service.yaml` vs `expert.yaml`).
|
||||
|
||||
## Switching modes
|
||||
|
||||
Both modes write through the same MFS engine and read the same files,
|
||||
so the vault is portable across modes. To switch:
|
||||
|
||||
1. `/plugin uninstall reme-{service|expert}`
|
||||
2. `/plugin install reme-{expert|service}`
|
||||
3. Restart Claude Code.
|
||||
|
||||
You can keep both installed simultaneously as long as you don't run two
|
||||
MCP servers at the same vault — the file_store cache will fight.
|
||||
|
||||
Switch from **service → expert** when:
|
||||
- You need fine-grained control over what gets created/edited.
|
||||
- You want every memory mutation visible in the main session's tool log.
|
||||
- You want subagents to handle distillation as bounded background work.
|
||||
|
||||
Switch from **expert → service** when:
|
||||
- The agent's main task is what matters; memory should be invisible plumbing.
|
||||
- You don't want to maintain or read the protocol.
|
||||
- You're OK with the Ingestor's defaults around topic creation, claim confidence, and wikilink uniqueness.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "reme-expert",
|
||||
"version": "0.1.0",
|
||||
"description": "Expert-tier markdown vault. Claude Code's agent runs the R-M-W loop directly using raw memory_* MCP primitives, guided by the reme-expert protocol skill. Bundles reme-distiller / reme-curator subagents (auto-spawned at SessionEnd / explicit slash commands), PreCompact + Stop hooks, and /reme-distill /reme-recall /reme-clean commands.",
|
||||
"author": {
|
||||
"name": "huangsen"
|
||||
},
|
||||
"keywords": ["reme", "vault", "memory", "knowledge-management", "obsidian", "mcp", "expert-tier", "agentic"]
|
||||
}
|
||||
10
reme-plugin/plugins/reme-expert/.mcp.json
Normal file
10
reme-plugin/plugins/reme-expert/.mcp.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"reme": {
|
||||
"command": "python",
|
||||
"args": ["-m", "reme2.mcp.server", "config=reme2/config/expert.yaml"],
|
||||
"env": {
|
||||
"PYTHONUNBUFFERED": "1",
|
||||
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../../.."
|
||||
}
|
||||
}
|
||||
}
|
||||
130
reme-plugin/plugins/reme-expert/README.md
Normal file
130
reme-plugin/plugins/reme-expert/README.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# reme-expert
|
||||
|
||||
Expert-tier markdown vault management for Claude Code. Claude Code's
|
||||
agent runs the R-M-W loop directly using raw `memory_*` MCP primitives,
|
||||
guided by the `reme-expert` protocol skill. The LLM-driven Distill loop
|
||||
and the agentic Maintain sweep run **outside reme2** in dedicated
|
||||
subagents (`reme-distiller`, `reme-curator`) that own their own context
|
||||
windows.
|
||||
|
||||
For the simpler service-tier alternative where reme2's internal Ingestor
|
||||
+ Maintainer own those loops, install [reme-service](../reme-service) instead.
|
||||
|
||||
## Install
|
||||
|
||||
```text
|
||||
/plugin marketplace add /Users/huangsen/codes/ReMe/reme-plugin
|
||||
/plugin install reme-expert
|
||||
```
|
||||
|
||||
```bash
|
||||
export VAULT_PATH=/path/to/your/vault
|
||||
```
|
||||
|
||||
## What's in the box
|
||||
|
||||
### MCP server (`reme`)
|
||||
|
||||
16 tools from `reme2/config/expert.yaml` — every memory_* primitive plus
|
||||
`sync` for event-folder upsert:
|
||||
|
||||
| Group | Tools | Phase |
|
||||
|---|---|---|
|
||||
| Hot-write | `sync` | Log |
|
||||
| Reads | `memory_search`, `memory_graph_search`, `memory_get`, `memory_list`, `memory_links`, `memory_backlinks`, `memory_resolve_wikilink`, `memory_count_tokens`, `memory_lint` | Recall |
|
||||
| Writes | `memory_create`, `memory_update`, `memory_property_update`, `memory_rename`, `memory_delete`, `memory_archive` | Log (surgical) / Distill (subagent uses) |
|
||||
|
||||
`ingest` is deliberately NOT exposed in this profile — Distill runs in
|
||||
the `reme-distiller` subagent, not in reme2's internal ReActAgent.
|
||||
|
||||
### Skill
|
||||
|
||||
`reme-expert` (auto-invoked) — the agent's protocol playbook. Describes
|
||||
the 4-phase paradigm (Recall / Log / Distill / Maintain), the trigger
|
||||
table, and which tool / slash command realizes each phase. Symmetrical
|
||||
with `reme-service`'s skill: same chapter structure, same triggers,
|
||||
just different tools to call.
|
||||
|
||||
### Subagents
|
||||
|
||||
| Subagent | Phase | Role |
|
||||
|---|---|---|
|
||||
| `reme-distiller` | Distill | Reads active events + materials + linked topics, applies R-M-W decision rules (SKIP / CONTRADICT / EXTEND / CREATE / STATUS-FLIP), returns audit. Auto-spawned by SessionEnd hook; also invokable via `/reme-distill`. |
|
||||
| `reme-curator` | Maintain | Vault hygiene sweep. Lint + decay; default `dry_run=true`. Invokable via `/reme-clean`. |
|
||||
|
||||
Both subagents transclude `../protocol.md` so they see the same rules as
|
||||
the main agent's skill.
|
||||
|
||||
### Slash commands
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `/reme-distill [hint or paths]` | Spawn `reme-distiller` (auto-fires at SessionEnd; manual invocation available). |
|
||||
| `/reme-recall <query>` | Explicit deep `memory_graph_search` (8 hits, depth 1). |
|
||||
| `/reme-clean [target_prefix] [dry_run]` | Spawn `reme-curator` for hygiene sweep. |
|
||||
|
||||
### Hooks (parallel to reme-service)
|
||||
|
||||
| Event | Action |
|
||||
|---|---|
|
||||
| **PreCompact** | Prompt: call `sync(materials=[...])` to dump volatile state into the current thread's event folder, reusing the thread's `name` for upsert; then output a compression guide. |
|
||||
| **SessionEnd** | Prompt: call `sync` to capture remaining facts, then **invoke `/reme-distill`** to spawn the distiller subagent. |
|
||||
| **Stop** | `active_events_check.py` — warns via stderr if any `status: active` events remain, suggests `/reme-distill`. |
|
||||
|
||||
There are **no SessionStart / UserPromptSubmit auto-recall hooks** — the
|
||||
agent's skill tells it when to call `memory_search` itself, which is
|
||||
more accurate than blanket injection. Use `/reme-recall` when you need
|
||||
deeper recall than your inline search would surface.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
plugins/reme-expert/
|
||||
├── .claude-plugin/plugin.json
|
||||
├── .mcp.json # config=reme2/config/expert.yaml
|
||||
├── protocol.md # canonical protocol; transcluded by SKILL via @../../protocol.md and by subagents via @../protocol.md
|
||||
├── skills/reme-expert/SKILL.md
|
||||
├── hooks/
|
||||
│ ├── hooks.json # PreCompact / SessionEnd / Stop
|
||||
│ └── active_events_check.py
|
||||
├── agents/
|
||||
│ ├── reme-distiller.md
|
||||
│ └── reme-curator.md
|
||||
├── commands/
|
||||
│ ├── reme-distill.md
|
||||
│ ├── reme-recall.md
|
||||
│ └── reme-clean.md
|
||||
└── README.md # this file
|
||||
```
|
||||
|
||||
`.mcp.json` resolves the sibling `reme2/` package via
|
||||
`PYTHONPATH=${CLAUDE_PLUGIN_ROOT}/../../..` (marketplace root → repo
|
||||
root). No pip install needed.
|
||||
|
||||
`protocol.md` is a copy of the canonical `reme2/memory/protocol.md`. The
|
||||
SKILL transcludes it from `skills/reme-expert/` via `@../../protocol.md`;
|
||||
the subagents transclude it from `agents/` via `@../protocol.md`. This
|
||||
local copy keeps the references stable after marketplace install.
|
||||
|
||||
## Workflow at a glance
|
||||
|
||||
```
|
||||
对话过程 → 模型按需调 memory_search / memory_graph_search;
|
||||
/reme-recall 触发更深召回
|
||||
任务执行中 → sync 持续 upsert;surgical 时 memory_update / memory_property_update
|
||||
PreCompact (hook) → 提示调 sync dump materials + 输出压缩指引
|
||||
任务完成 → /reme-distill → reme-distiller 子代理在独立 context 中跑 R-M-W
|
||||
SessionEnd (hook) → 提示 sync 收尾 + invoke /reme-distill (passing event paths)
|
||||
Stop (hook) → active_events_check.py 提醒未 distill 的 active events
|
||||
periodic → /reme-clean → reme-curator 子代理跑 lint + decay
|
||||
```
|
||||
|
||||
## When to switch to service mode
|
||||
|
||||
Switch to [reme-service](../reme-service) when:
|
||||
|
||||
- You don't want to maintain or read the protocol skill — let the
|
||||
Ingestor's defaults decide.
|
||||
- The agent should focus on its primary task and treat memory as plumbing.
|
||||
- You're OK trusting reme2's auto-decisions on topic creation, claim
|
||||
confidence, and wikilink uniqueness.
|
||||
40
reme-plugin/plugins/reme-expert/agents/reme-curator.md
Normal file
40
reme-plugin/plugins/reme-expert/agents/reme-curator.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
name: reme-curator
|
||||
description: Use proactively for vault hygiene sweeps — finds broken wikilinks, schema violations, stem collisions, and stale events past their freshness window; proposes / applies fixes (rename for collisions, archive for decay, frontmatter patch for schema). Spawned by `/reme-clean` slash command. Owns the Maintainer-style work in its own context window so the main session stays focused. Default behavior is `dry_run` — read the audit first, then re-run with `dry_run=false` to apply.
|
||||
tools: mcp__reme__memory_list, mcp__reme__memory_get, mcp__reme__memory_links, mcp__reme__memory_backlinks, mcp__reme__memory_resolve_wikilink, mcp__reme__memory_search, mcp__reme__memory_property_update, mcp__reme__memory_update, mcp__reme__memory_rename, mcp__reme__memory_archive, mcp__reme__memory_lint, mcp__reme__memory_count_tokens
|
||||
model: inherit
|
||||
---
|
||||
|
||||
# reme-curator — vault hygiene subagent
|
||||
|
||||
You are the **curator** for the markdown vault. Your job is a single hygiene sweep: scan for issues, propose fixes, and (unless `dry_run=true`) apply them. You run in your own context window so the main session stays focused.
|
||||
|
||||
You **MUST** default to `dry_run=true` and report the proposed plan before making any mutation. You **MUST NOT** mutate anything outside the scope of the issues you found (no opportunistic refactors). You **MUST** respect the protocol below — same wikilink-uniqueness gate, same status state machine, same frontmatter axes.
|
||||
|
||||
## Working set
|
||||
|
||||
The caller (slash command) gives you:
|
||||
- A scope (target_prefix like `topics/methods/` or empty for the whole vault).
|
||||
- A switch (`dry_run=true|false`).
|
||||
- An optional ops filter (`lint`, `decay`, `merge`, `split`, or all). Default ops: `lint` + `decay`.
|
||||
|
||||
Your steps:
|
||||
|
||||
1. **Scan** — `memory_lint` for the cheap diagnostics (broken wikilinks, schema violations, stem collisions). `memory_list` filtered by `status: active` + old `created` for decay candidates.
|
||||
2. **Categorize** — group findings by op type (rename / archive / property fix / dangling link).
|
||||
3. **Propose** — write a structured plan: for each finding, the file path, the issue, and the proposed fix. Stop here if `dry_run=true`.
|
||||
4. **Apply** (only if `dry_run=false`) — execute each proposed fix. Use `memory_rename` for collisions (auto-rewrites incoming wikilinks), `memory_archive` for decay (flips status + moves to Archive/), `memory_property_update` for schema patches.
|
||||
5. **Return** — short audit: scanned N files, found K issues across [categories], applied L fixes (or "dry_run — no changes").
|
||||
|
||||
## Protocol
|
||||
|
||||
@../protocol.md
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- ❌ Skipping the dry-run report — even on `dry_run=false`, surface the plan first so the caller can read it before fixes land in the audit.
|
||||
- ❌ Renaming files whose new stem would itself be ambiguous — `memory_rename` refuses; pick a domain-specific qualifier instead.
|
||||
- ❌ Archiving an `active` event without first `memory_property_update key=status value=distilled` — single-direction state machine.
|
||||
- ❌ Touching files outside `target_prefix` even if you spot issues there — surface them in the report, but don't fix; out of scope.
|
||||
- ❌ Using `memory_update` to fix YAML frontmatter — use `memory_property_update`.
|
||||
- ❌ Bulk-fixing dangling wikilinks by deleting the link text — surface the dangling-link list to the caller; the human or distiller decides whether the target should be created.
|
||||
42
reme-plugin/plugins/reme-expert/agents/reme-distiller.md
Normal file
42
reme-plugin/plugins/reme-expert/agents/reme-distiller.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
name: reme-distiller
|
||||
description: Use proactively for end-of-task or session-end vault distillation — reads active event folders + materials + linked topics, runs the R-M-W loop (Read → Mutate → Write), and flips distilled events' status. Auto-spawned by the SessionEnd hook and by the `/reme-distill` slash command. Owns the cold-path topic update / creation work in its own context window so the main session stays focused on the user's task.
|
||||
tools: mcp__reme__memory_search, mcp__reme__memory_graph_search, mcp__reme__memory_get, mcp__reme__memory_list, mcp__reme__memory_links, mcp__reme__memory_backlinks, mcp__reme__memory_resolve_wikilink, mcp__reme__memory_create, mcp__reme__memory_update, mcp__reme__memory_property_update, mcp__reme__memory_rename, mcp__reme__memory_archive, mcp__reme__memory_delete, mcp__reme__memory_count_tokens, mcp__reme__sync
|
||||
model: inherit
|
||||
---
|
||||
|
||||
# reme-distiller — cold-path R-M-W subagent
|
||||
|
||||
You are the **distiller** for the markdown vault. Your job is to take a working set (active event folders + their materials + any candidate topics) and integrate it into the long-lived topic graph, following the protocol below. You run in your own context window so the main session can stay focused on the user's task.
|
||||
|
||||
You **MUST** apply the R-M-W decision rules in order and stop at the first match. You **MUST NOT** restructure topics while integrating content; minimal edits only. You **MUST** flip the status of every distilled event to `distilled` (active → distilled is the only allowed transition for the events you process).
|
||||
|
||||
## Working set
|
||||
|
||||
The caller (slash command or SessionEnd hook) gives you:
|
||||
- A short hint about what the session was about.
|
||||
- A list of active event folder index paths (or a directive to find them yourself via `memory_list metadata={role: observation, status: active}` filtered to today, or whatever filter the caller supplies).
|
||||
|
||||
Your steps:
|
||||
|
||||
1. **Discover** — if the caller didn't list event folders, call `memory_list` to find candidates (`status: active`, recent date prefix).
|
||||
2. **Read** — for each event index, `memory_get` it; follow `## Materials` and read each material whose content you need; resolve linked topics with `memory_resolve_wikilink` and `memory_get` them; pull related context with `memory_search` / `memory_graph_search` if the events name unfamiliar concepts.
|
||||
3. **Decide per topic** — apply the R-M-W decision rules below. Don't decide blind — finish your reads before you write.
|
||||
4. **Mutate** — apply the chosen op (`SKIP` / `CONTRADICT` → `memory_update` / `EXTEND` → `memory_update` with tail anchor / `CREATE` → `memory_create`). After every mutation, the watcher updates the graph; you may want to re-`memory_get` if subsequent decisions depend on the new state.
|
||||
5. **Status flip** — for every event whose content you integrated, `memory_property_update path=<event-index> key=status value=distilled`.
|
||||
6. **Return** — write a concise audit summary as your final reply: applied ops (with paths), skipped events (with reason), failed ops (with reason). The caller surfaces this back to the main session.
|
||||
|
||||
## Protocol
|
||||
|
||||
@../protocol.md
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- ❌ Creating topics under `events/` — `sync` owns that path; topics live under `topics/`.
|
||||
- ❌ Forcing event status straight from `active` to `archived` — single-direction state machine; archive only after `distilled`.
|
||||
- ❌ Bulk-updating frontmatter via `memory_update` (string substitution on YAML is brittle) — use `memory_property_update`.
|
||||
- ❌ Restructuring an existing topic body just because you're touching it. Edit the changed sentence; leave the rest.
|
||||
- ❌ Calling `memory_create` for a topic whose stem `[[X]]` already resolves elsewhere — the create gate refuses; use the `suggested_name` it returns or pick a domain qualifier.
|
||||
- ❌ Distilling work that's already covered — apply the SKIP rule and move on.
|
||||
|
||||
Your reply to the caller is the **only** thing the main session sees from your run. Make it short and concrete.
|
||||
30
reme-plugin/plugins/reme-expert/commands/reme-clean.md
Normal file
30
reme-plugin/plugins/reme-expert/commands/reme-clean.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
description: Vault hygiene sweep — finds broken wikilinks, schema violations, stem collisions, stale events; proposes / applies fixes via the reme-curator subagent.
|
||||
argument-hint: [target_prefix] [dry_run=true|false]
|
||||
---
|
||||
|
||||
Spawn the `reme-curator` subagent for a vault hygiene sweep. The subagent runs in its own context window and returns a short audit; the main session stays focused.
|
||||
|
||||
Default behavior is `dry_run=true` — report the proposed plan first. Only re-run with `dry_run=false` after the user confirms the plan looks right.
|
||||
|
||||
Use the Agent tool with `subagent_type: reme-curator` and the following prompt:
|
||||
|
||||
```
|
||||
Vault hygiene sweep.
|
||||
|
||||
Args: $ARGUMENTS
|
||||
|
||||
Defaults if not specified in args:
|
||||
- target_prefix: "" (whole vault)
|
||||
- dry_run: true
|
||||
- ops: ["lint", "decay"]
|
||||
|
||||
Steps:
|
||||
1. memory_lint to enumerate broken wikilinks, schema violations, stem collisions.
|
||||
2. memory_list filtered by status=active + old `created` for decay candidates.
|
||||
3. Propose a fix plan grouped by op (rename / archive / property fix / dangling).
|
||||
4. If dry_run=false, apply the plan via memory_rename / memory_archive / memory_property_update.
|
||||
5. Return: scanned N files, found K issues across [categories], applied L fixes (or "dry_run — no changes").
|
||||
```
|
||||
|
||||
After the subagent returns, surface its audit to the user verbatim. If the plan looks right, suggest re-invoking with `dry_run=false`.
|
||||
24
reme-plugin/plugins/reme-expert/commands/reme-distill.md
Normal file
24
reme-plugin/plugins/reme-expert/commands/reme-distill.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
description: Distill the current session's active events into the topic graph via the reme-distiller subagent.
|
||||
argument-hint: [optional event-folder paths or hint]
|
||||
---
|
||||
|
||||
Spawn the `reme-distiller` subagent to handle cold-path R-M-W on the current session's events. The subagent runs in its own context window and returns a short audit summary; the main session stays focused on the user's task.
|
||||
|
||||
Use the Agent tool with `subagent_type: reme-distiller` and the following prompt:
|
||||
|
||||
```
|
||||
End-of-task distillation pass.
|
||||
|
||||
Hint: $ARGUMENTS
|
||||
|
||||
If the hint above doesn't list specific event folder paths, discover them yourself:
|
||||
- memory_list metadata={role: observation, status: active} filtered to today's date prefix.
|
||||
- For each candidate, memory_get the index, follow ## Materials, decide whether the content has already been distilled (skip if so).
|
||||
|
||||
Apply the R-M-W decision rules (SKIP / CONTRADICT / EXTEND / CREATE / STATUS-FLIP) per the protocol you have loaded. Mutations go through memory_create / memory_update / memory_property_update — every claim-role topic needs `confidence`, every distilled event ends with `status=distilled`.
|
||||
|
||||
Return a concise audit: applied ops with paths, skipped events with reason, failed ops with reason.
|
||||
```
|
||||
|
||||
After the subagent returns, surface its audit to the user verbatim — don't paraphrase.
|
||||
16
reme-plugin/plugins/reme-expert/commands/reme-recall.md
Normal file
16
reme-plugin/plugins/reme-expert/commands/reme-recall.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
description: Deep recall — graph-aware hybrid retrieval on a query. Use when SessionStart-style auto-injection isn't enough.
|
||||
argument-hint: <query, optionally including [[Anchor]]>
|
||||
---
|
||||
|
||||
Call the `mcp__reme__memory_graph_search` MCP tool with:
|
||||
|
||||
```
|
||||
query: $ARGUMENTS
|
||||
max_results: 8
|
||||
graph_depth: 1
|
||||
```
|
||||
|
||||
If the query contains a `[[wikilink]]`, the search will seed BFS at that file. If you want to seed at a specific topic explicitly, edit the call to add `seeds: [<absolute-path>]`.
|
||||
|
||||
After the call returns, present the top hits to the user: for each, the file path, the `graph_hop` distance from the seed, and a 1-line excerpt. Don't paraphrase the chunks — they may be load-bearing for the user's question.
|
||||
34
reme-plugin/plugins/reme-expert/hooks/active_events_check.py
Executable file
34
reme-plugin/plugins/reme-expert/hooks/active_events_check.py
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Stop hook: warn if there are still events with status: active."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
vault_path = os.environ.get("VAULT_PATH")
|
||||
if not vault_path:
|
||||
sys.exit(0)
|
||||
|
||||
events_dir = Path(vault_path) / "events"
|
||||
if not events_dir.is_dir():
|
||||
sys.exit(0)
|
||||
|
||||
active = 0
|
||||
fm_re = re.compile(r"^---\s*$(.*?)^---\s*$", re.MULTILINE | re.DOTALL)
|
||||
status_re = re.compile(r"^status:\s*active\s*$", re.MULTILINE)
|
||||
|
||||
for md in events_dir.rglob("*.md"):
|
||||
try:
|
||||
text = md.read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
continue
|
||||
fm_match = fm_re.search(text)
|
||||
if fm_match and status_re.search(fm_match.group(1)):
|
||||
active += 1
|
||||
|
||||
if active > 0:
|
||||
sys.stderr.write(
|
||||
f"⚠️ 流程合规:还有 {active} 个 active event 未 distill。"
|
||||
f"建议调用 `/reme-distill` 把这些 active events 提炼到 topic 后再结束会话。\n",
|
||||
)
|
||||
36
reme-plugin/plugins/reme-expert/hooks/hooks.json
Normal file
36
reme-plugin/plugins/reme-expert/hooks/hooks.json
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"hooks": {
|
||||
"PreCompact": [
|
||||
{
|
||||
"matcher": "auto|manual",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "上下文即将被压缩。两步保住关键状态:\n1. 调用 `sync` 把任何尚未落盘的事实写入当前线程的 event folder——一定要在 `materials` 字段里塞进**会丢失的原始素材**(用户原文 prompt、关键工具输出、决策时的中间数据)。**复用本任务一直在用的 `name`** 让 sync upsert 到同一文件夹(连续性靠这个保证);只有开启全新逻辑线程时才换名字。零 LLM 成本。\n2. 输出压缩指引:\n\n## 压缩指引\n\n### 当前工作\n[正在做什么,做到哪一步,下一步是什么]\n\n### 关键上下文\n[必须保留的推理链、未完成的分析、重要的中间结论]\n\n### 已持久化\n[本次同步落盘的 event folder 路径——压缩时可省略 raw 细节,需要时 memory_get 索引或 materials 重新读]\n\n### 活跃约束\n[当前需要持续遵守的规则或用户要求]"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionEnd": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "会话即将结束。两步收尾——**必须**走 distiller subagent,不要在主 session 里手动跑 R-M-W:\n\n1. 先调用 `sync` 把所有尚未落盘的事实写入对应线程的 event folder(`materials` 装原始素材,确定性、零 LLM、复用同名 upsert)。\n\n2. 然后调用 `/reme-distill` —— 它派出 `reme-distiller` subagent 在独立 context 中跑 R-M-W:读本次会话的 active event folders + materials + 相关 topic,按 reme 协议(4 轴 schema、claim-role confidence、wikilink 唯一性、path templates、status 状态机)决定哪些 topic update / create,flip distilled events 的 status,返回 audit 摘要。"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${VAULT_PYTHON:-python} ${CLAUDE_PLUGIN_ROOT}/hooks/active_events_check.py",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
147
reme-plugin/plugins/reme-expert/protocol.md
Normal file
147
reme-plugin/plugins/reme-expert/protocol.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# Memory Protocol
|
||||
|
||||
Single source of truth for vault schema, conventions, and the R-M-W
|
||||
write loop. Consumed by:
|
||||
|
||||
- **Ingestor's embedded ReAct prompt** (`reme2/memory/ingestor.yaml` —
|
||||
injected as `{protocol}` at load time).
|
||||
- **Strong-agent SKILL** (`reme-plugin/skills/reme/SKILL.md` —
|
||||
transcluded so the host agent sees the same rules).
|
||||
|
||||
Anything that defines schema invariants, path templates, write tool
|
||||
semantics, or the R-M-W decision tree belongs here. Anything role-
|
||||
specific (caller framing, audit trail expectations, summary
|
||||
requirements) stays in the consumer.
|
||||
|
||||
## Vault layout
|
||||
|
||||
- **Topics** — long-lived cognitive memory at `topics/{folder}/{name}.md`.
|
||||
A **folder topic** has `folder == name`; it's the cluster's index head.
|
||||
Short wikilink `[[X]]` resolves to the folder topic if one exists,
|
||||
else falls back to a unique same-stem file.
|
||||
- **Events** — fact log of one session at
|
||||
`events/{YYYY-MM-DD}/{name}/{name}.md`. The `.md` is the **index**
|
||||
inside a folder; sibling files are **materials** (raw conversation,
|
||||
tool outputs, data dumps). The index lists them under `## Materials`.
|
||||
|
||||
## Frontmatter — 4 schema axes
|
||||
|
||||
Every memory declares 4 orthogonal axes. The legacy `category` field is
|
||||
auto-translated to these axes for back-compat reads, but new writes
|
||||
should set the axes directly.
|
||||
|
||||
| Axis | Values | Meaning |
|
||||
|---|---|---|
|
||||
| `lifecycle` | `streaming` / `evolving` / `frozen` | streaming = events (decay/archive); evolving = topics (long-lived, edited); frozen = materials (immutable references) |
|
||||
| `scope` | `instance` / `class` | instance = a specific moment / object; class = abstract concept / role / pattern |
|
||||
| `source` | `auto` / `curated` / `derived` | auto = system-captured; curated = human/LLM intent; derived = computed from other memories |
|
||||
| `role` | `observation` / `claim` / `question` / `profile` / `concept` / `method` / `reference` / `fundamentals` | cognitive role — drives ranking + role-specific validation |
|
||||
|
||||
### Conditional fields
|
||||
|
||||
- `confidence` ∈ {⏳, ✅, ❌} — **REQUIRED** when `role: claim` (legacy
|
||||
categories `thesis` / `model`). Same gate applies to `role: question`
|
||||
(legacy `questions`).
|
||||
- `status` ∈ {`active`, `distilled`, `archived`} — meaningful only for
|
||||
`lifecycle: streaming`. Topic-style memories ignore it.
|
||||
- `originSessionId` — should be set when `source: auto`.
|
||||
|
||||
### Standard identity fields
|
||||
|
||||
`title`, `description`, `tags`, `created`, `updated`, `topics`,
|
||||
`parent`. Use today's date for `created` / `updated` on new writes.
|
||||
|
||||
## Cross-file references
|
||||
|
||||
`[[wikilink]]` syntax. Two forms:
|
||||
|
||||
- **Stem form** `[[X]]` — resolved against the file_store's stem index;
|
||||
prefers the folder topic if one exists.
|
||||
- **Path form** `[[topics/X/X]]` or `[[topics/X/X.md]]` — anchored at
|
||||
the vault root.
|
||||
|
||||
## Status state machine
|
||||
|
||||
`active → distilled → archived` (single direction, no skip). A reverse
|
||||
or skip transition will be flagged by the Maintainer.
|
||||
|
||||
## Wikilink uniqueness
|
||||
|
||||
Every create path routes through `MemoryCreate.write`, which refuses to
|
||||
introduce ambiguity (existing `[[X]]` would resolve to ≥2 paths). When
|
||||
rejected, the response includes a `suggested_name`. Retry with that, or
|
||||
pick a domain-specific qualifier (`Apple-Inc` beats `Apple-2`). Never
|
||||
bypass with `force=true` unless you fully understand the ambiguity.
|
||||
|
||||
## Available tools
|
||||
|
||||
### Read tools (gather context BEFORE writing)
|
||||
|
||||
- `memory_get(path, include_chunks=False)` — full file content +
|
||||
frontmatter. On an event index, follow `## Materials` and read each
|
||||
artifact whose content you need.
|
||||
- `memory_list(path_prefix=None, tags=None, metadata=None, limit=100)`
|
||||
— list indexed files filtered by prefix / tags / frontmatter.
|
||||
- `memory_resolve_wikilink(wikilink)` — resolve `[[X]]` to a path;
|
||||
flags ambiguity / dangling.
|
||||
- `memory_backlinks(path)` — files linking TO the given path.
|
||||
- `memory_links(path)` — files the given path links to.
|
||||
- `memory_search(query, …)` — hybrid (vector + keyword) chunk search.
|
||||
- `memory_graph_search(query, seeds, graph_depth, …)` — vector +
|
||||
keyword + graph BFS fusion.
|
||||
|
||||
### Write tools (mutations are SSOT-routed; each returns success +
|
||||
payload + records to audit)
|
||||
|
||||
- `memory_update(path, old_string, new_string, replace_all=False)` —
|
||||
body edit by exact-string substitution. Use a tail snippet to append.
|
||||
- `memory_property_update(path, key, value)` — change one frontmatter
|
||||
key (`value=null` deletes). Use this to flip status.
|
||||
- `memory_create(path, metadata, content, overwrite=False, force=False)`
|
||||
— new file. Reserve for genuinely NEW topics. Do NOT use for events
|
||||
(`sync` owns events). All paths must be ABSOLUTE under vault_root.
|
||||
- `memory_rename(old_path, new_path)` — move file + rewrite cross-vault
|
||||
wikilinks. Refuses on destination conflict or stem ambiguity.
|
||||
- `memory_delete(path)` — remove a file.
|
||||
- `memory_archive(path)` — flip `status: archived` and move under
|
||||
`<vault>/Archive/`.
|
||||
|
||||
### Hot-write helper (deterministic, no LLM)
|
||||
|
||||
- `sync(name, description?, content?, topics?, tags?, materials?,
|
||||
on_date?)` — idempotent upsert of an event FOLDER per `(date, name)`.
|
||||
Reuse the same `name` across calls in one thread to keep extending
|
||||
the same folder. Refuses on `status: distilled` / `archived` and
|
||||
returns a `suggested_name`.
|
||||
|
||||
## R-M-W decision rules
|
||||
|
||||
Apply in order. Stop at the first match.
|
||||
|
||||
1. **SKIP** — if material is ALREADY covered by existing topics, reply
|
||||
with a single line `SKIP: <one-line reason>` and call no tools.
|
||||
2. **CONTRADICT** — if material CONTRADICTS an existing block, use
|
||||
`memory_update` with a unique snippet of the outdated text and the
|
||||
corrected replacement.
|
||||
3. **EXTEND** — if material EXTENDS an existing topic, use
|
||||
`memory_update` with a unique TAIL snippet of the existing body, and
|
||||
`new_string = tail + blank line + new content`.
|
||||
4. **CREATE** — if material warrants a GENUINELY NEW topic, use
|
||||
`memory_create` at `topics/{folder}/{name}.md`. Do NOT
|
||||
`memory_create` under `events/` — `sync` owns that path.
|
||||
5. **STATUS FLIP** — after integrating an event's content into a
|
||||
topic, flip that event's status to `distilled` with
|
||||
`memory_property_update`.
|
||||
|
||||
## Operating principles
|
||||
|
||||
- **Read before write.** Always inspect related topics before deciding
|
||||
CONTRADICT vs EXTEND vs CREATE. The wikilink-uniqueness gate refuses
|
||||
blind creates; reading first prevents wasted attempts.
|
||||
- **Minimal edits.** Edit only what must change. Don't restructure
|
||||
while updating content.
|
||||
- **Frontmatter on create.** Always include reasonable frontmatter:
|
||||
the 4 axes, `title`, `created`, `updated`, plus `confidence` when
|
||||
`role: claim` or `role: question`.
|
||||
- **Never delete unless asked.** Distillation flips status; it does
|
||||
not remove events.
|
||||
156
reme-plugin/plugins/reme-expert/skills/reme-expert/SKILL.md
Normal file
156
reme-plugin/plugins/reme-expert/skills/reme-expert/SKILL.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
name: reme-expert
|
||||
description: Use this skill whenever the user references their personal vault (markdown notes managed by the `reme` MCP, expert-tier surface), or when there's a meaningful session outcome to record / a question that prior work might answer / a need to clean up the vault. Triggers include "what do I know about X", "did I work on Y before", "save this", "记下", "落盘", "提炼", "vault", any mention of topics/events/methodology, or recognizing that a non-trivial session outcome should be recorded. Skill follows a 4-phase paradigm (Recall / Log / Distill / Maintain) projected onto raw `memory_*` MCP primitives + `sync` (event log) + slash commands `/reme-distill` (auto-fired at SessionEnd) / `/reme-recall` / `/reme-clean`. Distill and Maintain run **outside reme2** in dedicated subagents (`reme-distiller`, `reme-curator`) that own their own context windows.
|
||||
---
|
||||
|
||||
# Vault — expert tier
|
||||
|
||||
The vault is a personal markdown knowledge base managed by the `reme` MCP server. **Expert tier**: raw `memory_*` primitives + `sync` for event logging, plus slash commands that spawn subagents for the LLM-driven loops. The R-M-W loops for Distill and Maintain run **outside reme2** in subagents you (Claude Code) drive directly via the same primitives.
|
||||
|
||||
## Business objects
|
||||
|
||||
- **Event** — fact log of one session. A folder `events/{YYYY-MM-DD}/{name}/` containing the index `{name}.md` (Memory schema, `lifecycle: streaming`) plus arbitrary **materials** (raw conversation, tool outputs, data dumps). `status: active → distilled → archived`.
|
||||
- **Topic** — long-lived cognition. Path `topics/{folder}/{name}.md`. Folder topic = `topics/X/X.md` is the cluster's index head.
|
||||
|
||||
Lifecycle: **session → event folder (fact + materials) → distill → topic (cognition)**.
|
||||
|
||||
## 4-Phase paradigm
|
||||
|
||||
Every interaction with the vault falls into one of four phases. The schema, state machine, and wikilink-uniqueness rules apply to every write you make — see the protocol section below.
|
||||
|
||||
### Phase 1: Recall
|
||||
|
||||
**What** — retrieve relevant context (chunks ranked by combined relevance + graph proximity).
|
||||
**Triggers** — intent-driven only: "what do I know about X" / "did I work on Y" / "what's connected to [[Z]]" / task needs prior methodology.
|
||||
**How (this tier)** — `memory_search(query, ...)` for hybrid vector + keyword; `memory_graph_search(query, seeds?, graph_depth?, ...)` when you want context expansion through wikilinks. For deeper recall (8 hits, depth 1) invoke `/reme-recall <query>`.
|
||||
**Where the work runs** — `reme2` thin primitive (FileGraph + FTS + vector store). No LLM.
|
||||
|
||||
```
|
||||
memory_search query="apple valuation" max_results=5
|
||||
memory_graph_search query="see [[Apple]]" graph_depth=1
|
||||
/reme-recall apple valuation around [[Apple]]
|
||||
```
|
||||
|
||||
Each result chunk carries `graph_hop` so you know how far it sits from your seeds.
|
||||
|
||||
Use `memory_get` / `memory_list` / `memory_links` / `memory_backlinks` / `memory_resolve_wikilink` for primary-key reads when you already know the path or want to follow a specific edge.
|
||||
|
||||
### Phase 2: Log
|
||||
|
||||
**What** — record event facts + raw materials. Idempotent upsert: same `name` continues the same event folder.
|
||||
**Triggers** — (a) intent-driven: a meaningful fact / output / decision just landed during the task; (b) **PreCompact hook**: prompt fires asking you to dump volatile state into `materials` before context truncation.
|
||||
**How (this tier)** — `sync(name=..., content?, materials?, topics?, tags?, on_date?, description?)`.
|
||||
**Where the work runs** — `reme2` thin primitive (sync upsert). No LLM.
|
||||
|
||||
**Continuity rule**: pick a stable `name` per logical thread and reuse it across calls. Each call extends the same folder.
|
||||
|
||||
```
|
||||
# First call in the thread
|
||||
sync
|
||||
name: <kebab-case, stable across this thread>
|
||||
description: <one line> # set on first call only
|
||||
content: |
|
||||
## ops
|
||||
- did X with tool Y
|
||||
## findings
|
||||
- Z turned out to be ...
|
||||
topics: ["[[X]]", "[[Y]]"]
|
||||
tags: [...]
|
||||
materials:
|
||||
- filename: raw-prompt.md
|
||||
content: <user's original prompt>
|
||||
- filename: tool-output.txt
|
||||
content: <verbose output worth preserving raw>
|
||||
|
||||
# Later in the SAME thread (or PreCompact firing)
|
||||
sync
|
||||
name: <same name>
|
||||
content: |
|
||||
## follow-up
|
||||
- second-pass facts
|
||||
materials:
|
||||
- filename: tool-output.txt # collision → auto-suffixed to tool-output-2.txt
|
||||
content: <new output>
|
||||
```
|
||||
|
||||
Each subsequent call appends `## Update — {iso}` to the index body, lands new materials as siblings, unions topics/tags. Refuses if the event has already been distilled — pick a fresh `name` from `suggested_name`.
|
||||
|
||||
**Inline surgical edits during the task** (not Distill — Distill is a bigger loop):
|
||||
- Change one frontmatter field on an existing topic → `memory_property_update path=... key=... value=...`
|
||||
- Edit one body snippet on an existing topic → `memory_update path=... old_string=... new_string=...`
|
||||
- These are still Phase 2 (you're recording a small fact); use them when the change is small and obvious. For multi-step R-M-W, defer to Phase 3.
|
||||
|
||||
### Phase 3: Distill
|
||||
|
||||
**What** — promote active events into the topic graph: read events + materials + linked topics, decide which existing topics to update vs. which deserve a brand-new topic, flip distilled events' status.
|
||||
**Triggers** — (a) intent-driven: task wraps and the working set is ready; (b) **SessionEnd hook**: prompt fires asking you to invoke `/reme-distill`; (c) **Stop hook**: stderr warning if any active events remain undistilled.
|
||||
**How (this tier)** — invoke `/reme-distill [hint or event paths]`. The slash command spawns the `reme-distiller` subagent; it reads the protocol, runs the R-M-W loop using raw `memory_*` tools, returns an audit summary.
|
||||
**Where the work runs** — **outside reme2**. The `reme-distiller` subagent is a Claude Code agent with its own context window — main session doesn't bloat with the 5-10 tool-call R-M-W sequence. The subagent uses the same `memory_*` primitives this skill describes; it just runs them in isolation and reports back.
|
||||
|
||||
**Cold-path rule**: handoff once at task wrap, not per turn. The SessionEnd hook auto-fires the prompt — your job there is just to (1) `sync` final state and (2) invoke `/reme-distill`, passing event paths from this session if you have them.
|
||||
|
||||
```
|
||||
/reme-distill end-of-task distillation. event folders this session:
|
||||
events/2026-05-09/<name1>/<name1>.md
|
||||
events/2026-05-09/<name2>/<name2>.md
|
||||
```
|
||||
|
||||
The subagent owns the decision rules (SKIP / CONTRADICT / EXTEND / CREATE / STATUS-FLIP) and the schema enforcement (claim-role confidence, wikilink uniqueness, path templates). Surface its audit verbatim — it's the source of truth for what changed.
|
||||
|
||||
### Phase 4: Maintain
|
||||
|
||||
**What** — vault hygiene sweep. Lint (broken wikilinks, schema violations, stem collisions) + decay (move stale events to archive).
|
||||
**Triggers** — periodic / user suspects vault drift. No hook auto-fires.
|
||||
**How (this tier)** — invoke `/reme-clean [target_prefix] [dry_run]`. The slash command spawns the `reme-curator` subagent; it runs `memory_lint` for diagnostics + uses `memory_rename` / `memory_archive` / `memory_property_update` to apply fixes. Default `dry_run=true`.
|
||||
**Where the work runs** — **outside reme2**. `memory_lint` is a thin reme2 projection of the Maintainer's diagnostics, but the *application* of fixes is agentic — the curator decides which renames / archives to actually apply and uses raw write primitives, all in the subagent's own context.
|
||||
|
||||
```
|
||||
/reme-clean # whole vault, dry_run=true
|
||||
/reme-clean events/2026-04 dry_run=false # apply within a path subset
|
||||
```
|
||||
|
||||
Always read the dry-run audit first; re-run with `dry_run=false` if the proposals look right.
|
||||
|
||||
For a quick read-only diagnostic without spawning a subagent, you can call `memory_lint` directly — but apply-side fixes still go through `/reme-clean`.
|
||||
|
||||
## Trigger → Phase quick reference
|
||||
|
||||
| Trigger | Phase | What you do |
|
||||
|---|---|---|
|
||||
| User asks about prior work / [[X]] | Recall | `memory_search` / `memory_graph_search` / `/reme-recall` |
|
||||
| Fact lands during task | Log | `sync(name=...)` |
|
||||
| Small surgical edit needed | Log | `memory_update` / `memory_property_update` inline |
|
||||
| **PreCompact hook** fires | Log (urgent dump) | `sync(name=..., materials=[...])` + compression guide |
|
||||
| Task wraps | Distill | `/reme-distill` |
|
||||
| **SessionEnd hook** fires | Log + Distill | final `sync` then `/reme-distill <event paths>` |
|
||||
| **Stop hook** warns active events | Distill (compliance) | `/reme-distill` for the listed events |
|
||||
| User suspects vault drift / periodic | Maintain | `/reme-clean` (dry_run=true) then `dry_run=false` |
|
||||
|
||||
## Protocol (the rules every write must respect)
|
||||
|
||||
These apply to every `memory_create` / `memory_update` / `memory_property_update` / `sync` you make and to every R-M-W decision the distiller subagent runs.
|
||||
|
||||
@../../protocol.md
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- ❌ Picking a fresh `name` on every `sync` call within the same logical thread → fragments. **Reuse the same name.**
|
||||
- ❌ Reusing the same `sync` `name` across genuinely unrelated threads on the same day → silent merge.
|
||||
- ❌ Running R-M-W inline in the main session at end-of-task — that's what `/reme-distill` exists for. Use it so the loop runs in the subagent's own context.
|
||||
- ❌ Using `memory_update` to edit YAML frontmatter — use `memory_property_update`.
|
||||
- ❌ Calling `memory_create` for a topic whose stem already resolves to another path — wikilink-uniqueness gate refuses; rename or pick a domain-specific qualifier.
|
||||
- ❌ Writing a thesis/model/questions topic without `confidence` — schema validation refuses; either add it before creating, or let the distiller surface the gap.
|
||||
- ❌ Writing event files manually with `memory_create` under `events/` — use `sync` so path template + Materials footer + idempotent upsert all run.
|
||||
- ❌ Forcing event status straight from `active` to `archived` — single-direction state machine; archive only after `distilled`.
|
||||
- ❌ Skipping `dry_run=true` on `/reme-clean` — read the plan first.
|
||||
|
||||
## What's parallel to service mode (for comparison)
|
||||
|
||||
| Phase | Service tier (work runs **inside reme2**) | Expert tier (work runs **outside reme2**) |
|
||||
|---|---|---|
|
||||
| Recall | `retrieve` | `memory_search` / `memory_graph_search` / `/reme-recall` |
|
||||
| Log | `remember(mode=log, name=...)` | `sync(name=...)` |
|
||||
| Distill | `remember(mode=distill, ...)` (Ingestor's internal ReActAgent) | `/reme-distill` (Claude Code subagent) |
|
||||
| Maintain | `maintain(...)` (Maintainer class) | `/reme-clean` (Claude Code subagent) |
|
||||
|
||||
The 4 phases and their triggers are identical across modes; only the locus of computation for Distill and Maintain differs (and Recall/Log have different MCP names).
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "reme-service",
|
||||
"version": "0.1.0",
|
||||
"description": "Service-tier markdown vault MCP. Three high-level tools (retrieve / remember / maintain). reme2's internal Ingestor + Maintainer own the R-M-W loop; the agent just hands off material.",
|
||||
"author": {
|
||||
"name": "huangsen"
|
||||
},
|
||||
"keywords": ["reme", "vault", "memory", "knowledge-management", "mcp", "service-tier"]
|
||||
}
|
||||
10
reme-plugin/plugins/reme-service/.mcp.json
Normal file
10
reme-plugin/plugins/reme-service/.mcp.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"reme": {
|
||||
"command": "python",
|
||||
"args": ["-m", "reme2.mcp.server", "config=reme2/config/service.yaml"],
|
||||
"env": {
|
||||
"PYTHONUNBUFFERED": "1",
|
||||
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../../.."
|
||||
}
|
||||
}
|
||||
}
|
||||
102
reme-plugin/plugins/reme-service/README.md
Normal file
102
reme-plugin/plugins/reme-service/README.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# reme-service
|
||||
|
||||
Service-tier markdown vault management for Claude Code. Three high-level
|
||||
MCP tools — `retrieve` / `remember` / `maintain` — backed by reme2's
|
||||
internal Ingestor + Maintainer. The agent calls the tools with intent;
|
||||
the LLM-driven R-M-W loop (Distill) and the sweep loop (Maintain) run
|
||||
**inside reme2**.
|
||||
|
||||
For the agentic alternative where Claude Code's own subagents run those
|
||||
loops outside reme2, install [reme-expert](../reme-expert) instead.
|
||||
|
||||
## Install
|
||||
|
||||
```text
|
||||
/plugin marketplace add /Users/huangsen/codes/ReMe/reme-plugin
|
||||
/plugin install reme-service
|
||||
```
|
||||
|
||||
```bash
|
||||
export VAULT_PATH=/path/to/your/vault
|
||||
```
|
||||
|
||||
## What's in the box
|
||||
|
||||
### MCP server (`reme`)
|
||||
|
||||
Three tools projected from the three memory services
|
||||
(`reme2/config/service.yaml`):
|
||||
|
||||
| Tool | Phase | Backend |
|
||||
|---|---|---|
|
||||
| `retrieve(query, max_results?, graph_depth?, seeds?, ...)` | Recall | thin (memory_graph_search) |
|
||||
| `remember(mode=log\|distill, name?, content, materials?, related_paths?, ...)` | Log / Distill | sync upsert (mode=log, no LLM) / Ingestor's internal ReActAgent (mode=distill) |
|
||||
| `maintain(target_prefix?, ops?, dry_run?, decay_days?)` | Maintain | Maintainer class |
|
||||
|
||||
### Skill
|
||||
|
||||
`reme-service` (auto-invoked) — describes the 4-phase paradigm
|
||||
(Recall / Log / Distill / Maintain) and how each phase maps to the three
|
||||
tools. Symmetrical with `reme-expert`'s skill: same chapter structure,
|
||||
same triggers, just different tools to call.
|
||||
|
||||
### Hooks (parallel to reme-expert)
|
||||
|
||||
| Event | Action |
|
||||
|---|---|
|
||||
| **PreCompact** | Prompt: call `remember(mode=log, materials=[...])` to dump volatile state into the current thread's event folder, reusing the thread's `name` for upsert; then output a compression guide. |
|
||||
| **SessionEnd** | Prompt: call `remember(mode=log)` to capture remaining facts, then call `remember(mode=distill, related_paths=[...])` once to hand off the working set to the Ingestor. |
|
||||
| **Stop** | `active_events_check.py` — warns via stderr if any `status: active` events remain, suggests `remember(mode=distill)`. |
|
||||
|
||||
There are **no SessionStart / UserPromptSubmit auto-recall hooks** — the
|
||||
agent's skill tells it when to call `retrieve` itself, which is more
|
||||
accurate than blanket injection.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
plugins/reme-service/
|
||||
├── .claude-plugin/plugin.json
|
||||
├── .mcp.json # config=reme2/config/service.yaml
|
||||
├── protocol.md # canonical protocol, transcluded by SKILL via @../../protocol.md
|
||||
├── skills/reme-service/SKILL.md
|
||||
├── hooks/
|
||||
│ ├── hooks.json # PreCompact / SessionEnd / Stop
|
||||
│ └── active_events_check.py
|
||||
└── README.md # this file
|
||||
```
|
||||
|
||||
`.mcp.json` resolves the sibling `reme2/` package via
|
||||
`PYTHONPATH=${CLAUDE_PLUGIN_ROOT}/../../..` (marketplace root → repo
|
||||
root). No pip install needed.
|
||||
|
||||
`protocol.md` is a copy of the canonical `reme2/memory/protocol.md`. The
|
||||
same file is read by reme2's internal Ingestor (injected as `{protocol}`
|
||||
into the ReActAgent's sys_prompt) — this plugin ships its own copy so
|
||||
the SKILL's `@../../protocol.md` transclusion works after marketplace
|
||||
install.
|
||||
|
||||
## Workflow at a glance
|
||||
|
||||
```
|
||||
对话过程 → 模型按需调 retrieve(intent-driven)
|
||||
任务执行中 → remember(mode=log) 持续 upsert;materials 装原始素材(确定性、零 LLM)
|
||||
PreCompact (hook) → 提示调 remember(mode=log) dump materials + 输出压缩指引
|
||||
任务完成 → remember(mode=distill) 一次 → Ingestor 内部 ReActAgent 跑 R-M-W → 返回 audit
|
||||
SessionEnd (hook) → 提示 remember(mode=log) 收尾 + remember(mode=distill) handoff
|
||||
Stop (hook) → active_events_check.py 提醒未 distill 的 active events
|
||||
periodic → maintain(dry_run=true) 看一眼,必要时 dry_run=false 应用
|
||||
```
|
||||
|
||||
## When to switch to expert mode
|
||||
|
||||
The service plugin hides the schema, claim confidence, wikilink
|
||||
uniqueness, path templates, and status state machine — `remember(mode=
|
||||
distill)` enforces them all underneath. Switch to
|
||||
[reme-expert](../reme-expert) when:
|
||||
|
||||
- You need fine-grained control over what gets created/edited.
|
||||
- You want every memory mutation visible in the main session's tool log
|
||||
(vs hidden inside the Ingestor's internal ReActAgent).
|
||||
- You want subagents to handle distillation as bounded background work
|
||||
with their own context windows.
|
||||
35
reme-plugin/plugins/reme-service/hooks/active_events_check.py
Executable file
35
reme-plugin/plugins/reme-service/hooks/active_events_check.py
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Stop hook: warn if there are still events with status: active."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
vault_path = os.environ.get("VAULT_PATH")
|
||||
if not vault_path:
|
||||
sys.exit(0)
|
||||
|
||||
events_dir = Path(vault_path) / "events"
|
||||
if not events_dir.is_dir():
|
||||
sys.exit(0)
|
||||
|
||||
active = 0
|
||||
fm_re = re.compile(r"^---\s*$(.*?)^---\s*$", re.MULTILINE | re.DOTALL)
|
||||
status_re = re.compile(r"^status:\s*active\s*$", re.MULTILINE)
|
||||
|
||||
for md in events_dir.rglob("*.md"):
|
||||
try:
|
||||
text = md.read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
continue
|
||||
fm_match = fm_re.search(text)
|
||||
if fm_match and status_re.search(fm_match.group(1)):
|
||||
active += 1
|
||||
|
||||
if active > 0:
|
||||
sys.stderr.write(
|
||||
f"⚠️ 流程合规:还有 {active} 个 active event 未 distill。"
|
||||
f"建议调用 `remember(mode=distill, content=..., related_paths=[...])` "
|
||||
f"把这些 active events 提炼到 topic 后再结束会话。\n",
|
||||
)
|
||||
36
reme-plugin/plugins/reme-service/hooks/hooks.json
Normal file
36
reme-plugin/plugins/reme-service/hooks/hooks.json
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"hooks": {
|
||||
"PreCompact": [
|
||||
{
|
||||
"matcher": "auto|manual",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "上下文即将被压缩。两步保住关键状态:\n1. 调用 `remember(mode=log, name=..., content=..., materials=[...])` 把任何尚未落盘的事实写入当前线程的 event folder——一定要在 `materials` 字段里塞进**会丢失的原始素材**(用户原文 prompt、关键工具输出、决策时的中间数据)。**复用本任务一直在用的 `name`** 让 remember 在同一文件夹 upsert(连续性靠这个保证);只有开启全新逻辑线程时才换名字。零 LLM 成本。\n2. 输出压缩指引:\n\n## 压缩指引\n\n### 当前工作\n[正在做什么,做到哪一步,下一步是什么]\n\n### 关键上下文\n[必须保留的推理链、未完成的分析、重要的中间结论]\n\n### 已持久化\n[本次落盘的 event folder 路径——压缩时可省略 raw 细节,需要时再 retrieve / memory_get 索引或 materials 重新读]\n\n### 活跃约束\n[当前需要持续遵守的规则或用户要求]"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionEnd": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "会话即将结束。两步收尾:\n\n1. 先调用 `remember(mode=log, name=..., materials=[...])` 把所有尚未落盘的事实写入对应线程的 event folder(`materials` 装原始素材,确定性、零 LLM、复用同名 upsert)。\n\n2. 然后调用 `remember(mode=distill, content=..., related_paths=[...])` —— **reme2 内部的 Ingestor 会跑 R-M-W 循环**:读本次会话的 active event folders + materials + 相关 topic,按 reme 协议(4 轴 schema、claim-role confidence、wikilink 唯一性、path templates、status 状态机)决定哪些 topic update / create,flip distilled events 的 status。在 `content` 里写一两行 session 摘要 + 提示,在 `related_paths` 里列本次 sync 过的 event folder 索引路径。\n\nDistill 是 cold-path,handoff 一次就够了,不要每轮调。"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${VAULT_PYTHON:-python} ${CLAUDE_PLUGIN_ROOT}/hooks/active_events_check.py",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
147
reme-plugin/plugins/reme-service/protocol.md
Normal file
147
reme-plugin/plugins/reme-service/protocol.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# Memory Protocol
|
||||
|
||||
Single source of truth for vault schema, conventions, and the R-M-W
|
||||
write loop. Consumed by:
|
||||
|
||||
- **Ingestor's embedded ReAct prompt** (`reme2/memory/ingestor.yaml` —
|
||||
injected as `{protocol}` at load time).
|
||||
- **Strong-agent SKILL** (`reme-plugin/skills/reme/SKILL.md` —
|
||||
transcluded so the host agent sees the same rules).
|
||||
|
||||
Anything that defines schema invariants, path templates, write tool
|
||||
semantics, or the R-M-W decision tree belongs here. Anything role-
|
||||
specific (caller framing, audit trail expectations, summary
|
||||
requirements) stays in the consumer.
|
||||
|
||||
## Vault layout
|
||||
|
||||
- **Topics** — long-lived cognitive memory at `topics/{folder}/{name}.md`.
|
||||
A **folder topic** has `folder == name`; it's the cluster's index head.
|
||||
Short wikilink `[[X]]` resolves to the folder topic if one exists,
|
||||
else falls back to a unique same-stem file.
|
||||
- **Events** — fact log of one session at
|
||||
`events/{YYYY-MM-DD}/{name}/{name}.md`. The `.md` is the **index**
|
||||
inside a folder; sibling files are **materials** (raw conversation,
|
||||
tool outputs, data dumps). The index lists them under `## Materials`.
|
||||
|
||||
## Frontmatter — 4 schema axes
|
||||
|
||||
Every memory declares 4 orthogonal axes. The legacy `category` field is
|
||||
auto-translated to these axes for back-compat reads, but new writes
|
||||
should set the axes directly.
|
||||
|
||||
| Axis | Values | Meaning |
|
||||
|---|---|---|
|
||||
| `lifecycle` | `streaming` / `evolving` / `frozen` | streaming = events (decay/archive); evolving = topics (long-lived, edited); frozen = materials (immutable references) |
|
||||
| `scope` | `instance` / `class` | instance = a specific moment / object; class = abstract concept / role / pattern |
|
||||
| `source` | `auto` / `curated` / `derived` | auto = system-captured; curated = human/LLM intent; derived = computed from other memories |
|
||||
| `role` | `observation` / `claim` / `question` / `profile` / `concept` / `method` / `reference` / `fundamentals` | cognitive role — drives ranking + role-specific validation |
|
||||
|
||||
### Conditional fields
|
||||
|
||||
- `confidence` ∈ {⏳, ✅, ❌} — **REQUIRED** when `role: claim` (legacy
|
||||
categories `thesis` / `model`). Same gate applies to `role: question`
|
||||
(legacy `questions`).
|
||||
- `status` ∈ {`active`, `distilled`, `archived`} — meaningful only for
|
||||
`lifecycle: streaming`. Topic-style memories ignore it.
|
||||
- `originSessionId` — should be set when `source: auto`.
|
||||
|
||||
### Standard identity fields
|
||||
|
||||
`title`, `description`, `tags`, `created`, `updated`, `topics`,
|
||||
`parent`. Use today's date for `created` / `updated` on new writes.
|
||||
|
||||
## Cross-file references
|
||||
|
||||
`[[wikilink]]` syntax. Two forms:
|
||||
|
||||
- **Stem form** `[[X]]` — resolved against the file_store's stem index;
|
||||
prefers the folder topic if one exists.
|
||||
- **Path form** `[[topics/X/X]]` or `[[topics/X/X.md]]` — anchored at
|
||||
the vault root.
|
||||
|
||||
## Status state machine
|
||||
|
||||
`active → distilled → archived` (single direction, no skip). A reverse
|
||||
or skip transition will be flagged by the Maintainer.
|
||||
|
||||
## Wikilink uniqueness
|
||||
|
||||
Every create path routes through `MemoryCreate.write`, which refuses to
|
||||
introduce ambiguity (existing `[[X]]` would resolve to ≥2 paths). When
|
||||
rejected, the response includes a `suggested_name`. Retry with that, or
|
||||
pick a domain-specific qualifier (`Apple-Inc` beats `Apple-2`). Never
|
||||
bypass with `force=true` unless you fully understand the ambiguity.
|
||||
|
||||
## Available tools
|
||||
|
||||
### Read tools (gather context BEFORE writing)
|
||||
|
||||
- `memory_get(path, include_chunks=False)` — full file content +
|
||||
frontmatter. On an event index, follow `## Materials` and read each
|
||||
artifact whose content you need.
|
||||
- `memory_list(path_prefix=None, tags=None, metadata=None, limit=100)`
|
||||
— list indexed files filtered by prefix / tags / frontmatter.
|
||||
- `memory_resolve_wikilink(wikilink)` — resolve `[[X]]` to a path;
|
||||
flags ambiguity / dangling.
|
||||
- `memory_backlinks(path)` — files linking TO the given path.
|
||||
- `memory_links(path)` — files the given path links to.
|
||||
- `memory_search(query, …)` — hybrid (vector + keyword) chunk search.
|
||||
- `memory_graph_search(query, seeds, graph_depth, …)` — vector +
|
||||
keyword + graph BFS fusion.
|
||||
|
||||
### Write tools (mutations are SSOT-routed; each returns success +
|
||||
payload + records to audit)
|
||||
|
||||
- `memory_update(path, old_string, new_string, replace_all=False)` —
|
||||
body edit by exact-string substitution. Use a tail snippet to append.
|
||||
- `memory_property_update(path, key, value)` — change one frontmatter
|
||||
key (`value=null` deletes). Use this to flip status.
|
||||
- `memory_create(path, metadata, content, overwrite=False, force=False)`
|
||||
— new file. Reserve for genuinely NEW topics. Do NOT use for events
|
||||
(`sync` owns events). All paths must be ABSOLUTE under vault_root.
|
||||
- `memory_rename(old_path, new_path)` — move file + rewrite cross-vault
|
||||
wikilinks. Refuses on destination conflict or stem ambiguity.
|
||||
- `memory_delete(path)` — remove a file.
|
||||
- `memory_archive(path)` — flip `status: archived` and move under
|
||||
`<vault>/Archive/`.
|
||||
|
||||
### Hot-write helper (deterministic, no LLM)
|
||||
|
||||
- `sync(name, description?, content?, topics?, tags?, materials?,
|
||||
on_date?)` — idempotent upsert of an event FOLDER per `(date, name)`.
|
||||
Reuse the same `name` across calls in one thread to keep extending
|
||||
the same folder. Refuses on `status: distilled` / `archived` and
|
||||
returns a `suggested_name`.
|
||||
|
||||
## R-M-W decision rules
|
||||
|
||||
Apply in order. Stop at the first match.
|
||||
|
||||
1. **SKIP** — if material is ALREADY covered by existing topics, reply
|
||||
with a single line `SKIP: <one-line reason>` and call no tools.
|
||||
2. **CONTRADICT** — if material CONTRADICTS an existing block, use
|
||||
`memory_update` with a unique snippet of the outdated text and the
|
||||
corrected replacement.
|
||||
3. **EXTEND** — if material EXTENDS an existing topic, use
|
||||
`memory_update` with a unique TAIL snippet of the existing body, and
|
||||
`new_string = tail + blank line + new content`.
|
||||
4. **CREATE** — if material warrants a GENUINELY NEW topic, use
|
||||
`memory_create` at `topics/{folder}/{name}.md`. Do NOT
|
||||
`memory_create` under `events/` — `sync` owns that path.
|
||||
5. **STATUS FLIP** — after integrating an event's content into a
|
||||
topic, flip that event's status to `distilled` with
|
||||
`memory_property_update`.
|
||||
|
||||
## Operating principles
|
||||
|
||||
- **Read before write.** Always inspect related topics before deciding
|
||||
CONTRADICT vs EXTEND vs CREATE. The wikilink-uniqueness gate refuses
|
||||
blind creates; reading first prevents wasted attempts.
|
||||
- **Minimal edits.** Edit only what must change. Don't restructure
|
||||
while updating content.
|
||||
- **Frontmatter on create.** Always include reasonable frontmatter:
|
||||
the 4 axes, `title`, `created`, `updated`, plus `confidence` when
|
||||
`role: claim` or `role: question`.
|
||||
- **Never delete unless asked.** Distillation flips status; it does
|
||||
not remove events.
|
||||
154
reme-plugin/plugins/reme-service/skills/reme-service/SKILL.md
Normal file
154
reme-plugin/plugins/reme-service/skills/reme-service/SKILL.md
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
---
|
||||
name: reme-service
|
||||
description: Use this skill whenever the user references their personal vault (markdown notes managed by the `reme` MCP, service-tier surface), or when there's a meaningful session outcome to record / a question that prior work might answer / a need to clean up the vault. Triggers include "what do I know about X", "did I work on Y before", "save this", "记下", "落盘", "提炼", "vault", any mention of topics/events/methodology, or recognizing that a non-trivial session outcome should be recorded. Skill follows a 4-phase paradigm (Recall / Log / Distill / Maintain) projected onto three MCP tools — `retrieve`, `remember(mode=log|distill)`, `maintain`. The R-M-W loop for Distill and the sweep for Maintain run **inside reme2** (the Ingestor's internal ReActAgent and the Maintainer class respectively).
|
||||
---
|
||||
|
||||
# Vault — service tier
|
||||
|
||||
The vault is a personal markdown knowledge base managed by the `reme` MCP server. **Service tier**: three high-level MCP tools, one per memory service. The R-M-W loops for Distill and Maintain run **inside reme2** — you hand off material with the right intent and the Ingestor / Maintainer do the work.
|
||||
|
||||
## Business objects
|
||||
|
||||
- **Event** — fact log of one session. A folder `events/{YYYY-MM-DD}/{name}/` containing the index `{name}.md` (Memory schema, `lifecycle: streaming`) plus arbitrary **materials** (raw conversation, tool outputs, data dumps). `status: active → distilled → archived`.
|
||||
- **Topic** — long-lived cognition. Path `topics/{folder}/{name}.md`. Folder topic = `topics/X/X.md` is the cluster's index head.
|
||||
|
||||
Lifecycle: **session → event folder (fact + materials) → distill → topic (cognition)**.
|
||||
|
||||
## 4-Phase paradigm
|
||||
|
||||
Every interaction with the vault falls into one of four phases. The schema, state machine, and wikilink-uniqueness rules are enforced by reme2 underneath — you don't manage them.
|
||||
|
||||
### Phase 1: Recall
|
||||
|
||||
**What** — retrieve relevant context (chunks ranked by combined relevance + graph proximity).
|
||||
**Triggers** — intent-driven only: "what do I know about X" / "did I work on Y" / "what's connected to [[Z]]" / task needs prior methodology.
|
||||
**How (this tier)** — `retrieve(query, max_results?, graph_depth?, seeds?, ...)`. Anchor mode: include `[[Target]]` in the query to seed BFS at that file. Topic-rooted mode: pass `seeds` explicitly.
|
||||
**Where the work runs** — `reme2` thin primitive (memory_graph_search backend). No LLM.
|
||||
|
||||
```
|
||||
retrieve query="apple valuation" max_results=5
|
||||
retrieve query="see [[Apple]]" graph_depth=1 # anchored
|
||||
retrieve query="any methodology" seeds=["topics/methods/dcf.md"] # topic-rooted
|
||||
```
|
||||
|
||||
Each result chunk carries `graph_hop` so you know how far it sits from your seeds.
|
||||
|
||||
### Phase 2: Log
|
||||
|
||||
**What** — record event facts + raw materials. Idempotent upsert: same `name` continues the same event folder.
|
||||
**Triggers** — (a) intent-driven: a meaningful fact / output / decision just landed during the task; (b) **PreCompact hook**: prompt fires asking you to dump volatile state into `materials` before context truncation.
|
||||
**How (this tier)** — `remember(mode=log, name=..., content?, materials?, topics?, tags?, on_date?)`.
|
||||
**Where the work runs** — `reme2` thin primitive (sync upsert). No LLM.
|
||||
|
||||
**Continuity rule**: pick a stable `name` per logical thread and reuse it across calls. Each call extends the same folder.
|
||||
|
||||
```
|
||||
# First call in the thread
|
||||
remember
|
||||
mode: log
|
||||
name: <kebab-case, stable across this thread>
|
||||
description: <one line> # set on first call only
|
||||
content: |
|
||||
## ops
|
||||
- did X with tool Y
|
||||
## findings
|
||||
- Z turned out to be ...
|
||||
topics: ["[[X]]", "[[Y]]"]
|
||||
tags: [...]
|
||||
materials:
|
||||
- filename: raw-prompt.md
|
||||
content: <user's original prompt>
|
||||
- filename: tool-output.txt
|
||||
content: <verbose output worth preserving raw>
|
||||
|
||||
# Later in the SAME thread (or PreCompact firing)
|
||||
remember
|
||||
mode: log
|
||||
name: <same name>
|
||||
content: |
|
||||
## follow-up
|
||||
- second-pass facts
|
||||
materials:
|
||||
- filename: tool-output.txt # collision → auto-suffixed to tool-output-2.txt
|
||||
content: <new output>
|
||||
```
|
||||
|
||||
Each subsequent call appends `## Update — {iso}` to the index body, lands new materials as siblings, unions topics/tags. Refuses if the event has already been distilled — pick a fresh `name` from `suggested_name`.
|
||||
|
||||
### Phase 3: Distill
|
||||
|
||||
**What** — promote active events into the topic graph: read events + materials + linked topics, decide which existing topics to update vs. which deserve a brand-new topic, flip distilled events' status.
|
||||
**Triggers** — (a) intent-driven: task wraps and the working set is ready; (b) **SessionEnd hook**: prompt fires asking you to call `remember(mode=distill)` once; (c) **Stop hook**: stderr warning if any active events remain undistilled.
|
||||
**How (this tier)** — `remember(mode=distill, content, hint?, target_path?, metadata?, related_paths?)`. Default `mode` is `distill`, so the parameter can be omitted.
|
||||
**Where the work runs** — **inside reme2**. The Ingestor's internal ReActAgent reads the working set + linked topics, applies the R-M-W decision rules (SKIP / CONTRADICT / EXTEND / CREATE / STATUS-FLIP), enforces claim-role confidence + wikilink uniqueness + path templates underneath, returns an audit.
|
||||
|
||||
**Cold-path rule**: handoff once at task wrap, not per turn. Hand off the working set in two interchangeable forms — `content` (inline material — hint, summary, or raw text) and/or `related_paths` (event folder indexes; the Ingestor follows `## Materials` to read each artifact).
|
||||
|
||||
```
|
||||
remember
|
||||
mode: distill # default — can be omitted
|
||||
content: |
|
||||
Distill these active events into the topic graph. Update existing
|
||||
topics where extended; create new topics only for genuinely new
|
||||
cognitive nodes. Flip each distilled event's status.
|
||||
|
||||
Session summary: <2-3 line recap>
|
||||
hint: "End-of-task distillation — minimal edits."
|
||||
related_paths:
|
||||
- <abs path of event folder 1's index .md>
|
||||
- <abs path of event folder 2's index .md>
|
||||
```
|
||||
|
||||
The Ingestor enforces the schema underneath: claim-role topics need `confidence` ∈ {⏳ ✅ ❌}, wikilink uniqueness, path templates. You don't drive any of that — describe intent in `content` / `hint`.
|
||||
|
||||
### Phase 4: Maintain
|
||||
|
||||
**What** — vault hygiene sweep. Lint (broken wikilinks, schema violations, stem collisions) + decay (move stale events to archive).
|
||||
**Triggers** — periodic / user suspects vault drift. No hook auto-fires.
|
||||
**How (this tier)** — `maintain(target_prefix?, ops?, dry_run?, decay_days?)`. Default `dry_run=true` and `ops=["lint","decay"]`.
|
||||
**Where the work runs** — **inside reme2**. The Maintainer class scans, proposes ops, resolves conflicts, applies (only when `dry_run=false`), returns an audit.
|
||||
|
||||
```
|
||||
maintain # full vault, lint + decay, dry_run=true
|
||||
maintain target_prefix="events/2026-04" dry_run=false # apply within a path subset
|
||||
maintain ops=["lint"] # diagnostics only
|
||||
```
|
||||
|
||||
Always read the dry-run audit first; re-run with `dry_run=false` if the proposals look right.
|
||||
|
||||
## Trigger → Phase quick reference
|
||||
|
||||
| Trigger | Phase | What you do |
|
||||
|---|---|---|
|
||||
| User asks about prior work / [[X]] | Recall | `retrieve` |
|
||||
| Fact lands during task | Log | `remember(mode=log, name=...)` |
|
||||
| **PreCompact hook** fires | Log (urgent dump) | `remember(mode=log, name=..., materials=[...])` + compression guide |
|
||||
| Task wraps | Distill | `remember(mode=distill, related_paths=[...])` |
|
||||
| **SessionEnd hook** fires | Log + Distill | final `remember(mode=log)` then `remember(mode=distill)` |
|
||||
| **Stop hook** warns active events | Distill (compliance) | `remember(mode=distill)` for the listed events |
|
||||
| User suspects vault drift / periodic | Maintain | `maintain(dry_run=true)` then `dry_run=false` |
|
||||
|
||||
## Protocol (the rules reme2 enforces underneath)
|
||||
|
||||
Don't manage these — but knowing they exist explains why a write might be refused or a `suggested_name` returned.
|
||||
|
||||
@../../protocol.md
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- ❌ Picking a fresh `name` on every `remember(mode=log)` call within the same logical thread → fragments the thread. **Reuse the same name.**
|
||||
- ❌ Reusing the same `name` across genuinely unrelated threads on the same day → silent merge.
|
||||
- ❌ Calling `remember(mode=distill)` per turn → it's a handoff tool, not a per-turn tool. Once at end-of-task is the rule.
|
||||
- ❌ Skipping `dry_run=true` on `maintain` for a fresh vault — read what it would do first.
|
||||
- ❌ Trying to drive schema decisions from your side (claim confidence, path templates, status state machine) — the Ingestor enforces them; pass intent via `content` / `hint` and let it ask if it needs more.
|
||||
- ❌ Trying to do R-M-W manually with raw `memory_*` tools — those aren't exposed in this tier. Switch to **expert** mode if you need direct primitives.
|
||||
|
||||
## What you DON'T have to think about
|
||||
|
||||
- The schema (4 axes: lifecycle / scope / source / role) — `remember(mode=distill)` enforces it.
|
||||
- Wikilink uniqueness — the create gate refuses ambiguity; the Ingestor handles `suggested_name` retries.
|
||||
- Path templates (`topics/{X}/{X}.md`, `events/{date}/{name}/...`) — the Ingestor / `remember(mode=log)` apply them.
|
||||
- Status state machine — `remember(mode=distill)` flips it; `maintain` decays.
|
||||
- Claim-role confidence — the Ingestor refuses claim-role topics without it; pass intent and let it ask.
|
||||
|
||||
If you need any of those decisions surfaced (e.g. "I want to manually curate this topic"), switch to **expert** mode which gives you the raw `memory_*` primitives.
|
||||
Loading…
Add table
Reference in a new issue