o9k-read

Load long-term memory from hmem via read_memory or load_project. Use when starting fresh with no L1 summaries, after /compact, when the user asks 'what do you remember', 'continue where we left off', 'was war der letzte Stand', or about a project/topic visible in L1 summaries. Covers search, prefix filter, find_related, stale detection, and cross-project read_project().

10 stars

Best use case

o9k-read is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Load long-term memory from hmem via read_memory or load_project. Use when starting fresh with no L1 summaries, after /compact, when the user asks 'what do you remember', 'continue where we left off', 'was war der letzte Stand', or about a project/topic visible in L1 summaries. Covers search, prefix filter, find_related, stale detection, and cross-project read_project().

Teams using o9k-read should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/o9k-read/SKILL.md --create-dirs "https://raw.githubusercontent.com/Bumblebiber/hmem/main/skills/o9k-read/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/o9k-read/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How o9k-read Compares

Feature / Agento9k-readStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Load long-term memory from hmem via read_memory or load_project. Use when starting fresh with no L1 summaries, after /compact, when the user asks 'what do you remember', 'continue where we left off', 'was war der letzte Stand', or about a project/topic visible in L1 summaries. Covers search, prefix filter, find_related, stale detection, and cross-project read_project().

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Load Memory — Choose Your Path

When this skill is invoked, pick the right path and execute a tool call immediately.
Do NOT just read this document — act.

If `read_memory` is not available, tell the user:
"read_memory tool not found. Run `hmem init` to configure the MCP server."

**Announcements:** If `read_memory` or `o9k-sync pull` shows urgent announcements
(yellow warnings at the top), act on them **immediately** before doing anything else.
These are broadcast messages from the user or another device — typically config changes,
server migrations, or breaking updates that must be handled first.

---

## Path A: Start Working on a Project (primary workflow)

Use `load_project` as the single entry point. It activates the project, returns the full
briefing (L2 content + L3 titles), and shows recent O-entries with full L4/L5 user/agent
exchanges — so you see exactly what happened in previous sessions, not just titles.

- **User mentioned a project** → load it directly:
  ```
  load_project(id="P0037")
  ```
  This replaces the old pattern of `update_memory(active=true)` + `read_memory()`.
  One call gives you everything: project structure, open tasks, and recent session history.

- **Unclear which project** → list projects first, then load:
  ```
  read_memory(titles_only=true, prefix="P")   # ~200 tokens overview
  ```
  Then ask the user or pick the most relevant one, and call `load_project(id="P00XX")`.

- **New project** → create it first, then load:
  ```
  write_memory(prefix="P", content="New project: ...")   # → P00XX
  load_project(id="P00XX")
  ```

- **Cross-project reference** — need context from another project *without* switching active project:
  ```
  read_project(id="P0048")   # focused briefing, does NOT activate
  ```
  Returns Overview, Codebase titles, Usage, Context, Requirements, Roadmap titles.
  Omits: session history (O-entries), Bugs, History, Ideas, Next Steps, Rules, R-injection.
  Use this when working on Project A but needing the API/architecture of Project B.

- **No specific project** → load everything:
  ```
  read_memory()   # full bulk read, all projects
  ```

### What load_project returns

The response includes:
- **L2 content + L3 titles** — the complete project briefing (~700 tokens)
- **Recent O-entries with full exchanges** — configurable via `recentOEntries` in config
  (default: 10). Each O-entry contains the actual L4/L5 user/agent messages, not just
  summaries. This is your continuity — read them to understand where the last session
  left off.

### Why load_project over read_memory

`read_memory()` shows a cross-project overview optimized for breadth. `load_project`
goes deep on one project — it activates it, filters related entries, and includes session
history. Use `load_project` when you know which project to work on; use `read_memory`
when you need orientation across all projects.

## Path B: After Context Compression (/compact)

You still have the recent conversation — you do not need the newest entries again.
Load the long-term knowledge that was lost during compression:

```
read_memory(mode="essentials")
```

Essentials mode prioritizes favorites, most-accessed, and pinned entries over newest.
This is your "recover what matters" call — rules, decisions, error patterns, key references.

For a specific project's full context:
```
read_memory(context_for="P0029")
```

## Path C: User Asks About a Specific Topic

When the user asks about something visible in your L1 summaries:

```
# "Was weißt du über das hmem Projekt?"
read_memory(context_for="P0029")

# "Tell me about that Heimdall error"
read_memory(context_for="E0090")
```

`context_for` loads the entry expanded + all related entries (via links and weighted tag
scoring) in a single call. Raise `min_tag_score` for fewer results:

```
read_memory(context_for="P0029", min_tag_score=7)   # stricter — only strong matches
```

---

## Bulk Read Design

`read_memory()` shows **current context** — newest entries, most-accessed favorites, open
tasks. It is not a full dump. Older entries with low access_count are intentionally omitted.

**Expanded entries** (newest, most-accessed, favorites): show L2 children + links.
**Non-expanded entries**: latest child + `[+N more → ID]` hint.
**Active-prefix filtering**: entries in active projects get full expansion, others title-only.

**For older or broader knowledge:**

```
read_memory(titles_only=true, prefix="L")   # all lesson titles as table of contents
read_memory(search="SQLite corruption")      # semantic search
read_memory(tag="#sqlite")                   # hashtag filter
```

**Tags are hidden by default** — use `read_memory(curator=true)` to see hashtags on entries.

Repeated bulk reads without a goal yield little new information after 3-4 iterations — use
targeted search or a prefix overview instead.

---

## Title vs. Body (v5.1+)

Every node has a **title** (short navigation label) and an optional **body** (detail shown on drill-down). This affects what you see in different read modes:

| Mode | Shows |
|------|-------|
| `titles_only=true` | Title only — compact table of contents |
| Default (by ID) | Title + body for requested node, children as title-only |
| `expand=true` | Title + body recursively for all nodes |
| Bulk reads | Title only for L1, expanded entries show L2 titles |

Entries with a blank line between title and body have explicit title/body separation. Older entries without separation show auto-extracted titles (~50 chars) — the full text is still accessible on drill-down.

---

## Lazy Loading Protocol (subsequent reads)

After the initial load, drill deeper with these patterns:

```
read_memory(prefix="E")           # only errors
read_memory(store="company")      # shared company knowledge
read_memory(id="E0042")           # expand root → shows L2 children
read_memory(id="E0042.2")         # expand L2 → shows L3 children
read_memory(id="E0042.2.1")       # expand further (rarely needed)
```

**Compact table of contents:**

```
read_memory(titles_only=true)                # all entries, one line each
read_memory(titles_only=true, prefix="L")    # only lessons
```

**Depth parameter** — useful for listings (max 4), not for ID queries:

```
read_memory(depth=2)              # all entries with L2 children
read_memory(prefix="L", depth=2)  # all lessons with details
read_memory(depth=4)              # deep dive L2+L3+L4 (large output, use sparingly)
```

For L5 detail, drill into a specific node ID instead of using depth.

---

## Time-Based Search

Find entries created around a specific time or near another entry:

```
read_memory(time="14:30")                        # ±2h window around 14:30 today
read_memory(time="14:30", date="2026-02-20")     # specific date + time
read_memory(time="14:30", period="-1h")           # custom window: only 1h before
read_memory(time_around="P0001")                  # entries created near P0001
read_memory(time_around="P0001", period="+2h")    # only after P0001
```

---

## Search

```
read_memory(search="Node.js startup crash")
read_memory(search="auth token")
search_memory(query="auth token", scope="memories")
```

**Sub-node attribution (v6.6.1+):** FTS hits inside sub-nodes are now surfaced explicitly. If the match is inside `P0048.8.4`, the output shows the root entry plus a `↳ matched in N sub-nodes:` block with compact IDs and an 80-char preview. Tag-only or root-level matches keep the previous one-line format (no `↳` block).

---

## Original Context History (O-prefix)

O-entries store raw session context with progressive summarization. They are created
**automatically** by the Stop hook — every user/agent exchange is recorded as an O-entry
without manual intervention. When you switch projects, a new O-entry is started
automatically (project-based O-entries).

O-entries are hidden from bulk reads but searchable. Use them when you need the original
reasoning behind a decision or the full conversation that led to an entry:

```
read_memory(prefix="O")                                    # browse recent context
read_memory(search="why did we choose per-node scoring")   # search across raw context
read_memory(id="O0042", expand=true)                       # drill into specific entry
```

O-entries are linked to curated entries (P/L/D/E) via tags, so `context_for` will
surface relevant O-entries when their tags match.

**v5 checkpoint integration:** When `checkpointMode` is set to `"auto"`, a Haiku subagent
reads recent O-entry exchanges at configurable intervals and automatically extracts
L/D/E entries + writes a rolling checkpoint summary (`[CP]` node tagged `#checkpoint-summary`).

**What `load_project` shows:** For the latest O-entry, it displays:
1. The most recent checkpoint summary (if available)
2. Only raw exchanges AFTER the summary (minimum 5 exchanges guaranteed)
3. Skill-dialog exchanges (brainstorming, TDD, etc.) are filtered out automatically

This keeps `load_project` compact even for long sessions. To see the full unfiltered
history, use `read_memory(id="O0042", expand=true)`.

The `recentOEntries` config parameter (default: 10) controls how many recent O-entries
`load_project` includes.

---

## Show All Obsolete Entries

By default, bulk reads hide most obsolete entries (top 3 by access count shown). To see all:

```
read_memory(show_obsolete=true)
```

---

## Stale Detection

Find entries not accessed in a while — useful for curation:

```
read_memory(stale_days=30)                # sorted oldest-access first
read_memory(stale_days=60, prefix="L")    # only stale lessons
```

---

## Memory Stats

Quick overview of your memory health:

```
memory_stats()                    # personal store
memory_stats(store="company")     # company store
```

Output includes: total entries by prefix, total nodes, favorites count, pinned count,
unique hashtags, stale count (>30d), oldest entry, and top 5 most-accessed entries.

---

## Find Related

Find entries similar to a given entry via FTS5 keyword matching — spots potential
duplicates or thematic connections:

```
find_related(id="P0029")            # up to 5 similar entries
find_related(id="L0042", limit=10)
```

Returns title-only list with overlapping keywords (different from `relatedEntries` in
ID reads which uses shared tags).

---

## Memory Health Audit

Check memory for structural issues before/after curation:

```
memory_health()                    # personal store
memory_health(store="company")
```

Checks: broken links, orphaned entries, stale favorites/pinned, broken obsolete chains,
tag orphans.

---

## Adapt Communication to User Skill Level

After loading memory, read H-prefix entries to understand the user. The standard H-schema has four categories:

| Section | What it contains |
|---------|-----------------|
| **Identity** | Name, contact, GitHub, roles, background |
| **Work Style** | Communication style, decision-making, agent behavior expectations |
| **Priorities** | Current goals and focus areas |
| **Preferences** | Likes/dislikes — what to do and what to avoid |

H-entries also contain **Skill Assessments** (1–10 per subtopic). Adapt language accordingly:

- **1–4**: Explain concepts, avoid jargon, use analogies
- **5–6**: Brief explanations, some jargon OK
- **7–8**: Direct technical language, skip basics
- **9–10**: Peer-level, challenge assumptions

If no H-entries exist yet, create them from context clues in the conversation
(see o9k-write skill for the H-prefix convention).

---

## After Loading — Proactive Curation

Your memory is your brain. If something is wrong, stale, or noisy — fix it NOW, don't list
problems for later. This applies after `load_project` AND after `read_memory`.

### Scan the load_project output

Every time you receive a `load_project` response, scan it for issues and fix them
immediately — before responding to the user. The load_project output IS the briefing
that every future session gets. If it contains noise, every future session starts with
noise. Treat it like your own resume: if something doesn't belong, remove it.

**What to look for and how to fix it:**

| Problem | Example | Action |
|---------|---------|--------|
| Resolved/done bugs | `.6.2 E0101: O-entry root title never updated` (fixed months ago) | `update_memory(id="P0048.6.2", content="...", irrelevant=true)` |
| Old protocol entries | `.7.32 - windowsHide: true on all child_process spawns` (fragment, not a session) | `update_memory(id="P0048.7.32", content="...", irrelevant=true)` |
| Stale version info | Overview says "v5.3.1" but current is v6.0.0 | `update_memory(id="P0048.1.1", content="Current state: v6.0.0 on npm...")` |
| Duplicate sections | `.10 Bugs (duplicate of .6)` | `update_memory(id="P0048.10", content="...", irrelevant=true)` |
| Completed open tasks | `.8.2 ✓ DONE: O-Entry Session History Injection` | Should be auto-filtered; if not, mark irrelevant |
| Wrong project's exchanges | O-entry exchanges from P0052 appearing in P0048 | Use `move_nodes` (see below) |
| Stale env/config references | Still references `HMEM_PROJECT_DIR` instead of `HMEM_PATH` | Update the node content |

Use `update_many` when marking multiple entries irrelevant in one go:
```
update_many(ids=["P0048.7.32", "P0048.6.2", "P0048.6.5"], irrelevant=true)
```

### Fix misplaced O-entries with move_nodes

When exchanges or session nodes land in the wrong O-entry (e.g. project confusion during
a session), move them to the correct location:

```
move_nodes(source_ids=["O0048.3.1.5", "O0048.3.1.6"], target_parent="O0052.1.1")
```

This preserves the exchange content while fixing the tree structure. Don't delete
misplaced entries — move them to where they belong.

### Scan bulk read output (read_memory)

After any `read_memory()` call, scan for:

- **Wrong facts** → write correction first, then mark obsolete with `[✓ID]`:
  ```
  write_memory(prefix="E", content="Correct fix is...") → E0076
  update_memory(id="E0042", content="Wrong — see [✓E0076]", obsolete=true)
  ```
- **Noise/irrelevant** → `update_memory(id="T0005", content="...", irrelevant=true)`
- **Important discoveries** → `update_memory(id="S0001", content="...", favorite=true)`

### When NOT to curate

- Don't curate after every single `read_memory` call — only on the first bulk read
  of a session and after `load_project`
- Don't curate during time-critical tasks (the user is waiting for a bug fix, not curation)
- Don't mark entries irrelevant if you're unsure — ask the user first

For a thorough deep-clean, use the `/o9k-curate` skill.

---

## Anti-Patterns

| Wrong | Right |
|-------|-------|
| `read_memory()` after /compact | `read_memory(mode="essentials")` — you already have recent context |
| `update_memory(active=true)` + `read_memory()` | `load_project(id="P00XX")` — one call does both |
| Blind bulk read at session start | `read_memory(titles_only=true, prefix="P")` first, then `load_project` |
| `read_memory(id="E0042", depth=3)` | `read_memory(id="E0042.2")` — branch by branch |
| Load everything without purpose | Check L1 first, then expand selectively |
| Read .hmem file directly | Always use MCP tools — it is a SQLite binary |
| Just display this skill text | **Call a read_memory or load_project variant immediately** |
| `load_project` on a secondary reference project | `read_project(id="P00XX")` — doesn't switch active project |
| `update_memory(id="X", obsolete=true)` without `[✓ID]` | Write correction first, then mark obsolete with `[✓ID]` tag |
| Repeated `read_memory()` to find old entries | `read_memory(titles_only=true, prefix="L")` or `read_memory(search="...")` |
| Listing noise without fixing it | Fix it NOW: `update_memory(id, content, irrelevant=true)` |
| Deleting misplaced O-entries | Move them: `move_nodes(source_ids=[...], target_parent="O00XX.Y")` |
| Manually creating O-entries | O-entries are auto-logged by the Stop hook — do not create them manually |

Related Skills

We are still matching the closest adjacent skills for this page. In the meantime, continue through the full directory.