@kaged/plugin-memory-markdown
v0.1.2
Published
Reference project plugin for agent memory — markdown-based entry storage with keyword scoring, recency boost, tag filtering, and auto-facts aggregation
Readme
影 @kaged/plugin-memory-markdown
shadow ops for your [memory]
Markdown-file-backed agent memory for kaged — every entry a plain markdown file with YAML frontmatter, searchable by keyword + recency + tag scoring. The canonical reference plugin: if you're building a kaged project plugin, start here.
What it does
Zero-credentials, file-based memory for kaged agents. Every memory entry is a markdown file with YAML frontmatter. The operator can cat, grep, git diff, or hand-edit any file.
retain— store a memory entry (auto-appends short facts tofacts.md)recall— keyword + recency + tag scoring search across entriesforget— delete a memory entry by ID (idempotent)list_tags— list all unique tags across entrieson_session_start— inject recent memories into the agent's contexton_session_idle— auto-save session transcripton_compact— preserve compacted messages on disk, inject relevant context
This is the reference floor — the simplest credible memory backend. The ceiling is @kaged/plugin-memory-hindsight (vector-graph-temporal search via the Hindsight API).
Why it's the reference plugin
This plugin demonstrates every manifest surface a project plugin can use:
| Surface | What this plugin does |
|---|---|
| Manifest (kaged-plugin.yaml) | Full metadata, sandbox capabilities, env vars |
| Roles | observer (hooks into session lifecycle) |
| Hooks | All 3 lifecycle hooks: on_session_start, on_session_idle, on_compact |
| Tools | 4 tools: retain, recall, forget, list_tags |
| Config schema | 14 project-side fields (scoring weights, limits, headers, isolation) |
| System config schema | 1 operator-local secret (encryption_key) — demonstrates the project/system split |
| Knobs (7 types) | path, enum, boolean, int_range, text, range, multiline |
| JSON-RPC server | Stdio transport, handshake, method dispatch, context extraction |
When kaged adds new plugin features, this plugin gets updated to showcase them. If a feature doesn't apply to a memory plugin (e.g. model_alias knobs), it's noted in the spec with an explanation of why it's omitted.
Quick start
Declare in project DSL
# .kaged/project.yaml
plugins:
memory:
package: "@kaged/plugin-memory-markdown"That's it. No API keys, no external services. Memory files land in ~/.local/share/kaged/memory/ by default.
Operator config (optional)
Override in local.toml:
[plugins."@kaged/plugin-memory-markdown".system_config]
encryption_key = "..." # v0 placeholder — demonstrates system_configConfigure per-project (optional)
# .kaged/project.yaml
plugins:
memory:
package: "@kaged/plugin-memory-markdown"
config:
isolation: project # share memory across agents
store: "project:/memory" # store inside project dir (committable)
tags: [project-x] # default tags on every retain
score_tag_exact: 5 # heavier tag matching
custom_inject_header: "Context from past sessions:"All config fields have sensible defaults — the plugin works with zero configuration.
Architecture
src/
config.ts MarkdownConfig, MarkdownSystemConfig, RuntimeConfig, mergeConfig()
handlers.ts Tool + hook handlers composing URI → directory → I/O
server.ts JSON-RPC 2.0 stdio server, method dispatch
main.ts Process entry point (stdin line reader)
io.ts File I/O: write, read, recall, delete, tags
frontmatter.ts YAML frontmatter build/parse round-trip
score.ts Configurable keyword + recency + tag scoring
storage.ts Store directory resolution (per-agent vs per-project)
uri.ts URI scheme resolution (config:/, project:/)
id.ts Snowflake ID generation
readme.ts README template for store root
index.ts Barrel exportsConfig reference
Project-side (committed to git)
| Field | Type | Default | Description |
|---|---|---|---|
| isolation | "agent" | "project" | "agent" | Per-agent or per-project memory scope |
| store | string (URI) | "config:/memory" | Where memory files live (config:/ or project:/) |
| tags | string[] | [] | Default tags applied to every retain |
| retain_on_session_idle | boolean | true | Auto-save transcript when session goes idle |
| inject_on_session_start_max_entries | integer | 10 | Max entries to inject at session start |
| max_content_bytes | integer | 102400 | Max retain content size (100 KB) |
| fact_max_length | integer | 200 | Content shorter than this gets appended to facts.md |
| inject_tag_hint | boolean | true | Include known-tags hint in session-start injection |
| score_recency_24h | integer | 2 | Recency boost for entries < 24h old |
| score_recency_7d | integer | 1 | Recency boost for entries < 7d old |
| score_tag_exact | integer | 3 | Boost for exact tag-token matches |
| debug | boolean | false | Verbose stderr logging |
| store_description | string | "" | Human-readable store description (in README) |
| custom_inject_header | string | "Known about this project:" | Override the injection header |
System-side (operator-local, never committed)
| Field | Type | Description |
|---|---|---|
| encryption_key | string | v0 placeholder — demonstrates system_config_schema usage |
Testing
bun test # 186 tests, 317 assertions
tsc --noEmit # typecheckOn-disk layout
<store>/
README.md # auto-generated, preserved if operator edits
facts.md # curated bullet list of short facts
<snowflake-id>.md # individual memory entries
transcripts/
<session-id>.md # full session transcript (upsert)
<session-id>-compact-<id>.md # preserved compaction chunks
primary/ # per-agent dir when isolation: agent
primary.subagents.researcher/ # subagent dirs when isolation: agentEvery file is plain markdown with YAML frontmatter. The operator can read, edit, diff, or commit any of it.
License
MIT © the kaged project
[kaged] · kaged.dev · sanctioned edge, sacred code
