capture · git:20260703.bcd56e8 · 2026-07-03 · sha256 4f058f1a65e97555

capture git:20260703.bcd56e8A

Immutable. This exact content is served forever at /api/v1/blob/4f058f1a65e97555.

---
name: capture
description: >
  Save a memory to Wenlan in flow. Active capture verb — use proactively
  when the user states a preference, makes a decision, corrects you, or
  shares a durable fact. Invoked as `/capture <content>`.
argument-hint: "<content>"
allowed-tools: ["mcp__plugin_wenlan_wenlan__capture", "mcp__plugin_wenlan_wenlan__recall", "mcp__plugin_wenlan_wenlan__create_entity", "mcp__plugin_wenlan_wenlan__create_relation", "mcp__plugin_wenlan_wenlan__accept_revision", "mcp__plugin_wenlan_wenlan__dismiss_revision", "Bash"]
---

# /capture

Capture a single memory in the moment. Active verb: agent captures the
moment of insight, like a photograph.

## Argument parsing

The `/capture` skill accepts one optional inline token of the form
`space:<name>` anywhere in the argument string. Extract it before
treating the rest as content:

    raw_args="<the full argument string passed to /capture>"
    space_arg="$(printf '%s\n' "$raw_args" | grep -oE 'space:[A-Za-z0-9_-]+' | head -1 | cut -d: -f2)"
    content="$(printf '%s\n' "$raw_args" | sed -E 's/[[:space:]]*space:[A-Za-z0-9_-]+[[:space:]]*/ /g' | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"

If `space_arg` is non-empty, pass it to the resolver as `--arg "$space_arg"`.

## Resolve the active space

Call the bundled resolver:

    resolved="$("$CLAUDE_PLUGIN_ROOT/bin/resolve-space.sh" --cwd "$PWD" \
        ${space_arg:+--arg "$space_arg"} 2>/dev/null)"
    space="$(printf '%s\n' "$resolved" | cut -f1)"
    source_layer="$(printf '%s\n' "$resolved" | cut -f2)"

Pass `space="$space"` to the `capture` MCP tool only when `space` is
non-empty. Before every capture, also print:

    Resolved space: <space> (from <source-layer>)

If `space` is empty, print:

    Resolved space: none (unscoped)

Unknown spaces are not auto-created. Register a new space first with
`wenlan spaces add <space>`, or omit `space` to store uncategorized.

## How to invoke

Call the `wenlan` MCP server's `capture` tool with the user's content as a
complete, self-contained statement. Attach `topic` from cwd or the
conversation — don't make the user type it.

```
capture(content="<args, written as a full sentence with WHY>",
        memory_type="<picked from the 6 types>",
        entity="<primary entity name, if any>",
        space=<resolved if non-empty>)
```

### `memory_type` — agent picks one of 6

The daemon classifies when a local model or API key is configured. In
local memory mode it does not, so the agent picks the type from the content itself. Use this
mapping:

| Type | Use for |
|---|---|
| `identity` | Durable facts about the user (role, company, language preference) |
| `preference` | "I prefer X because Y" — a habit, a correction, a stylistic choice |
| `decision` | "Going with A over B because C" — a specific choice with rationale |
| `lesson` | Root cause found, workaround discovered, technical insight earned |
| `gotcha` | Sharp edge, surprising behavior, a thing to watch out for |
| `fact` | Durable info about people, projects, tools — anchor to `entity` when possible |

If two types fit, pick the one closest to *why the memory matters*. A
decision *also* implies a preference, but `decision` is more specific.

### `entity` — extract the anchor

Pick the single most important named thing in the content: a person,
project, tool, place. Use the exact name. Example: "Alice prefers TDD
because…" → `entity="Alice"`. If the content has no named anchor,
omit `entity`.

### `topic` / `space` inference

- cwd inside a repo → repo name (e.g. `~/Repos/wenlan/...` → `"wenlan"`).
- Outside any repo → most recent topic from the conversation, or omit.
- Pass `space` only when scope is known; if uncertain, run `list_spaces`
  later (post-PR-C) or omit.

### Multiple entities or relations

The MCP `capture` tool takes a single primary `entity`. For additional
entities or relations, use the dedicated MCP tools. If the content
names more than one entity, capture the memory first, then for each
additional entity:

```
create_entity(name="<entity>", entity_type="<person|project|tool|place>")
```

For a relation between two entities:

```
create_relation(from_entity="<a>", to_entity="<b>", relation_type="<verb>")
```

Skip these calls when the daemon has an LLM — its post-ingest enrichment
covers extraction.

## What to capture

- Decisions: "Going with approach A because B"
- Preferences: "Prefers TDD because catches regressions early"
- Corrections: "Actually it's C, not D"
- Identity / project facts: "Works on Wenlan, a local memory daemon for AI tools"

## What NOT to capture

- System prompts, boot logs, heartbeats
- Transient task state ("currently working on...")
- Tool output, command results, architecture dumps
- Single-word acknowledgments
- Things the user can trivially re-derive (file paths, recent git history)
- Agent operating rules — "always X" / "never Y" directives about how the
  agent should behave. Those belong in CLAUDE.md / AGENTS.md / MEMORY.md (the
  obey tier), not Wenlan. Capture the user's *preference* ("prefers TDD
  because…"), not the agent-facing *rule* ("always run TDD first").

## Atomic ideas

One capture = one idea. "Prefers TDD" and "Uses pytest" are two captures, not
one.

## When to use

- User explicitly says "remember this", "save that", "capture this".
- User states a durable preference / decision / correction proactively (no
  ask required — that's the floor, not the trigger).

## When NOT to use

- End of session bulk store → use `/handoff` (multi-item batch).
- Pulling memories back out → use `/recall`.

## Post-capture contradiction signal

After `capture` returns, check `response.triggered_revisions` and `response.auto_superseded`.

### auto_superseded (no action needed)

If `auto_superseded` is non-empty, the daemon already resolved the contradiction. Surface it as informational:

```
Note: auto-superseded mem_X. Wenlan replaced a prior protected memory because
trust=high and similarity > 0.9. No action needed.
```

No accept/dismiss call required. The revision was applied automatically.

### triggered_revisions (human review needed)

If `triggered_revisions` is non-empty (and `auto_superseded` is empty), render an inline block to the user:

```
Stored mem_new.

This capture topic-matches a protected memory now flagged for revision:
  - mem_target_abc

Action: accept (replace original content) | dismiss (drop the revision) | leave (decide later)
```

Inline verb map:

- accept: `accept_revision(target_source_id="mem_target_abc")`
- dismiss: `dismiss_revision(target_source_id="mem_target_abc")`
- leave: no call; surfaces again in next `/brief`

Both fields can technically be non-empty in a single response (multiple protected matches), but in practice only one fires per capture: `auto_superseded` fires when trust=full and similarity > 0.9, `triggered_revisions` fires otherwise.

If neither field is non-empty, the capture stored cleanly with no conflicts.