o9k-config
View and change hmem memory settings, hooks, sync, and checkpoints. Use on /o9k-config, or when the user asks why context is huge, how to change auto-save, how often checkpoints fire, or how to set up sync.
Best use case
o9k-config is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
View and change hmem memory settings, hooks, sync, and checkpoints. Use on /o9k-config, or when the user asks why context is huge, how to change auto-save, how often checkpoints fire, or how to set up sync.
Teams using o9k-config 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
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/o9k-config/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How o9k-config Compares
| Feature / Agent | o9k-config | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
View and change hmem memory settings, hooks, sync, and checkpoints. Use on /o9k-config, or when the user asks why context is huge, how to change auto-save, how often checkpoints fire, or how to set up sync.
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
# o9k-config — View and Change Settings
This skill guides you through reading, explaining, and updating hmem's configuration. The config controls how memory is stored, displayed, checkpointed, and synced across devices.
## Locate and read the config
The config lives at `hmem.config.json` in the same directory as your .hmem file. Located at `~/.hmem/hmem.config.json` (in the same directory as your .hmem file).
Read the file directly — don't ask the user where it is. If it doesn't exist, offer to create one (only non-default values need to be specified).
The config uses a unified format with a `"memory"` block and an optional `"sync"` block:
```json
{
"memory": { ... },
"sync": { ... }
}
```
## Show current settings
Present a table of current values vs. defaults. Only highlight values that differ from defaults — the user cares about what they've customized, not the full list.
### Core parameters
| Parameter | Default | Purpose |
|-----------|---------|---------|
| `maxCharsPerLevel` | [200, 2500, 10000, 25000, 50000] | Character limits per tree level [L1–L5]. L1 is always loaded at startup, so keeping it short saves tokens across every session. L5 is raw data, rarely accessed. |
| `maxDepth` | 5 | Tree depth (1–5). Most users need 5. Lower values save storage but lose granularity. |
| `defaultReadLimit` | 100 | Max entries per bulk read. Lower = faster startup, higher = more complete overview. |
| `maxTitleChars` | 50 | Auto-extracted title length. Only applies to entries without explicit body separation — entries with a blank-line body use the first line as title verbatim. Titles are navigation labels — too short truncates meaning, too long wastes space. |
| `accessCountTopN` | 5 | Entries with highest access count get [★] and auto-expand in bulk reads. These are "organic favorites" — the things the agent keeps coming back to. |
### Checkpoint and session parameters (v5+)
These control the automatic knowledge extraction pipeline:
| Parameter | Default | Purpose |
|-----------|---------|---------|
| `checkpointMode` | `"remind"` | **`"auto"`** spawns a Haiku subagent in the background every N exchanges — it reads the conversation, extracts lessons/errors/decisions, and writes them via MCP tools. The main agent is never interrupted. **`"remind"`** injects a prompt asking the main agent to save manually — simpler but interrupts flow. |
| `checkpointInterval` | 20 | Exchanges between checkpoints. Counted in the active O-entry, not per session — so 10 messages on your laptop + 10 on your server = checkpoint fires at 20. Set to 0 to disable. |
| `recentOEntries` | 10 | How many recent session logs to show when loading a project. All entries include full user/agent exchanges (L4/L5), not just titles. Higher = more context but more tokens at project load. |
| `contextTokenThreshold` | 100000 | When cumulative hmem output exceeds this, the agent is told to flush context and /clear. Prevents runaway token usage in long sessions. Set to 0 to disable. |
### Entry schemas (v6.3.0+)
Define per-prefix section schemas that control `create_project` structure and `load_project` rendering depth. When a schema is defined, it replaces the hardcoded R0009 sections and the `loadProjectExpand` settings.
```json
{
"memory": {
"schemas": {
"P": {
"sections": [
{ "name": "Overview", "loadDepth": 3, "defaultChildren": ["Current state", "Goals", "Environment"] },
{ "name": "Codebase", "loadDepth": 1 },
{ "name": "Protocol", "loadDepth": 0 },
{ "name": "Next Steps", "loadDepth": 3 }
],
"createLinkedO": true
}
}
}
}
```
| Field | Type | Description |
|-------|------|-------------|
| `sections[].name` | string | L2 section title. Used for matching during auto-reconcile (case-insensitive). |
| `sections[].loadDepth` | 0-4 | 0=skip, 1=title only, 2=+L3 titles, 3=+L3 body, 4=full subtree |
| `sections[].defaultChildren` | string[] | L3 nodes created by `create_project`. Optional. |
| `createLinkedO` | boolean | Auto-create matching O-entry on `create_project`. Default: false. |
**Auto-reconcile:** On every `load_project`, missing schema sections are automatically added as empty L2 nodes. Extra sections not in the schema are kept (loaded at depth 1).
**No schema defined:** Falls back to hardcoded R0009 behavior and `loadProjectExpand` settings.
### load_project display (legacy, pre-v6.3.0)
Only used when no `schemas` entry exists for the prefix. Controls which P-entry sections are expanded:
| Parameter | Default | Purpose |
|-----------|---------|---------|
| `loadProjectExpand.withBody` | `[1]` | L2 section seq numbers where L3 children show title + body content. Default: `.1 Overview` — shows full architecture/state/goals detail. |
| `loadProjectExpand.withChildren` | `[6, 8]` | L2 section seq numbers where all L3 children are listed as titles. Default: `.6 Bugs`, `.8 Open Tasks` — all items visible at a glance. |
Sections not in either list show L3 titles only in compact mode.
### Bulk-read tuning
The bulk-read algorithm decides which entries get expanded (full L2 detail) vs. compressed (title only). Most users don't need to touch these — the defaults work well up to ~500 entries.
| Parameter | Default | Purpose |
|-----------|---------|---------|
| `bulkReadV2.topNewestCount` | 5 | Newest entries expanded. Increase if you want more recent context at startup. |
| `bulkReadV2.topAccessCount` | 3 | Most-accessed entries expanded (time-weighted: `access_count / log2(age_days + 2)`). |
| `bulkReadV2.topObsoleteCount` | 3 | Obsolete entries kept visible — "biggest mistakes" are still worth seeing. |
| `bulkReadV2.topSubnodeCount` | 3 | Entries with most children expanded. These tend to be the most detailed/important. |
### Prefixes
Default: P, L, T, E, D, M, S, N, H, R, O, I. Custom prefixes are merged with defaults — they don't replace them. Each prefix can have a custom description used as group header in bulk reads.
## Help the user make changes
For each parameter the user wants to change:
1. **Explain the tradeoff** in plain language — what gets better, what gets worse
2. **Show the recommended range** (see below)
3. **Validate** before writing — numbers must be positive, arrays must be valid JSON
### Recommended ranges
| Parameter | Range | Guidance |
|-----------|-------|----------|
| `maxCharsPerLevel[0]` (L1) | 60–300 | Below 60 is too terse for useful summaries. Above 300 wastes tokens on every bulk read — L1 is loaded at every session start. |
| `maxCharsPerLevel[4]` (L5) | 1000–100000 | Raw data storage. Higher allows more verbatim content but L5 is rarely loaded. |
| `maxDepth` | 2–5 | 3 suffices for simple setups. 5 for multi-agent or complex projects. |
| `checkpointMode` | `"auto"` or `"remind"` | Recommend `"auto"` — it's non-disruptive and produces better results because Haiku has MCP access to check for duplicates. Auto-checkpoints also write rolling summaries (`[CP]` nodes) and tag skill-dialog exchanges for filtering. |
| `checkpointInterval` | 0–100 | 20 is a good balance. Lower = more frequent saves (more Haiku cost). 0 = disabled. |
| `recentOEntries` | 0–20 | 10 is the sweet spot. With checkpoint summaries, `load_project` shows summary + recent exchanges only — much more compact than raw exchange dumps. |
| `contextTokenThreshold` | 0–500000 | 100k is recommended for most models. Increase for 1M-context models. |
### Common recipes
**"I want auto-checkpoints":**
```json
{ "memory": { "checkpointMode": "auto", "checkpointInterval": 20 } }
```
**"Startup is too slow / uses too many tokens":**
Reduce `recentOEntries` (e.g., 5), `bulkReadV2.topNewestCount` (e.g., 3), or `maxCharsPerLevel[0]` (e.g., 150).
**"I have 500+ entries and bulk reads are noisy":**
Increase `bulkReadV2.topAccessCount` and decrease `topNewestCount` — favor proven entries over new ones.
## Write the updated config
Write `hmem.config.json` with only non-default values. The config uses a `"memory"` wrapper:
```json
{
"memory": {
"checkpointMode": "auto"
},
"sync": { ... }
}
```
After writing, tell the user:
- Which values changed
- Changes take effect **immediately** — no restart needed
- `maxCharsPerLevel` only affects new entries (existing entries are not reformatted)
## Check o9k-sync status
Run this check as part of every /o9k-config invocation.
**If installed** (`which o9k-sync`): run `npx o9k-sync status` and show server URL, user ID, last push/pull timestamps, and whether `HMEM_SYNC_PASSPHRASE` is set in `.mcp.json` (needed for auto-sync).
**If not installed**: explain that o9k-sync enables zero-knowledge encrypted cross-device sync (AES-256-GCM, server sees only opaque blobs), and offer to install it:
```bash
npm install -g o9k-sync
npx o9k-sync connect
```
### Sync troubleshooting
| Problem | Fix |
|---------|-----|
| "Config not found" | Run `npx o9k-sync connect` |
| 401 Token verification failed | Passphrase has special chars — set `HMEM_SYNC_PASSPHRASE` in .mcp.json env |
| 0 entries after pull | `HMEM_PATH` filename must match between devices |
| Update | `npm update -g o9k-sync` (always global, never inside a project) |
## Hook configuration on Windows (REQUIRED)
On Windows, hook execution is fragile out of the box. Two issues bite every new user:
**1. Git Bash routing** — On systems with Git for Windows installed, Claude Code may route hook commands through Git Bash (`bash.exe`). Its MSYS2 runtime crashes transiently with `add_item ("\??\C:\Program Files\Git", "/", ...) failed, errno 1` during cygheap init, killing the hook **before the command is even parsed**. Symptom: `UserPromptSubmit hook error` or `Stop hook error` with a bash.exe stacktrace.
**2. Unix inline env-var syntax** — Commands like `HMEM_PATH=C:/... node ...` work in bash but break in cmd.exe and PowerShell. Symptom: `"HMEM_PATH" is not recognized as a command`.
**The fix for Windows users (apply to every hook + statusLine):**
```json
{
"env": {
"HMEM_PATH": "C:/Users/<you>/.hmem/Agents/<AGENT>/<AGENT>.hmem"
},
"hooks": {
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/its-over-9k/dist/cli.js hook-startup",
"shell": "powershell"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/its-over-9k/dist/cli.js log-exchange",
"shell": "powershell"
}
]
}
],
"SessionStart": [
{
"matcher": "clear",
"hooks": [
{
"type": "command",
"command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/its-over-9k/dist/cli.js context-inject",
"shell": "powershell"
}
]
}
]
},
"statusLine": {
"type": "command",
"command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/its-over-9k/dist/cli.js statusline",
"shell": "powershell"
}
}
```
Two things to notice:
- `"shell": "powershell"` on **every** hook command and on statusLine — forces native PowerShell, bypasses Git Bash entirely.
- `HMEM_PATH` lives in the top-level `env` block, **not** inline in the command. Claude Code inherits the env block to every hook subprocess, regardless of shell.
**Never use inline env-var syntax in hook commands on Windows.** `VAR=value command` is bash-only syntax and will silently break under cmd.exe or PowerShell.
**Troubleshooting matrix:**
| Symptom | Cause | Fix |
|---------|-------|-----|
| `UserPromptSubmit hook error` (no stacktrace) | Inline `VAR=value` in command + cmd.exe parses it as a command name | Move env vars to `env` block, remove inline prefix |
| `bash.exe: *** fatal error - add_item ... errno 1` | Git Bash MSYS2 runtime crashing at startup | Add `"shell": "powershell"` to every hook command |
| Hooks silently do nothing (no errors) | Wrong shell interpreting the command, or project not active for session logging | Verify `"shell": "powershell"`, call `load_project(id="P00XX")` every session |
**Note on `load_project` per-session:** The `active` flag on a P-entry persists in the database, but the "currently active project for session logging" is a per-session attribute. After every Claude Code restart, the agent must call `load_project(id="P00XX")` again, or exchanges will be logged to O0000 (no-project fallback) instead of the project's O-entry. Consider adding this to your project briefing or session-start routine.
## Hermes Agent Hook Configuration
For Hermes Agent (not Claude Code), hmem integration is configured via
`~/.hermes/config.yaml` hooks and the `hermes-hooks/` scripts shipped
in the its-over-9k repo.
### Required hooks
```yaml
hooks:
pre_llm_call:
- command: "~/.hermes/agent-hooks/o9k-startup.sh"
timeout: 10
post_llm_call:
- command: "~/.hermes/agent-hooks/o9k-log-exchange.sh"
timeout: 10
on_session_end:
- command: "/bin/bash -c 'exec hmem checkpoint'"
timeout: 120
hooks_auto_accept: true
```
### Hook scripts
| Script | Type | Purpose |
|--------|------|---------|
| `o9k-startup.sh` | pre_llm_call | 🟢/🔴 Sync-Status, Projekt-Liste, session_id-Cache für Statusline |
| `o9k-log-exchange.sh` | post_llm_call | Exchange-Logging an aktiven O-Entry via `hmem log-exchange` |
| `hmem-statusline.sh` | Statusbar | Device \| Projekt → O-Node \| Checkpoint-Counter |
### Statusline (CC-Style in Hermes CLI)
To show hmem info in Hermes' status bar (like Claude Code's statusline):
1. Apply the CLI patch:
```bash
cd ~/.hermes/hermes-agent
git apply ~/projects/hmem/hermes-hooks/hermes-cli-hmem-statusline.patch
```
2. Restart Hermes. The status bar will show:
```
Strato Server | P0048 its-over-9k → O0048.118 | 3/5 | ⚕ model | ...
```
The statusline requires `hmem statusline` to be installed and the
`o9k-startup.sh` hook to be active (for session_id caching).
### Deployment
On update or fresh install:
```bash
cp ~/projects/hmem/hermes-hooks/*.sh ~/.hermes/agent-hooks/
chmod +x ~/.hermes/agent-hooks/*.sh
```
### Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Statusleiste zeigt kein hmem | CLI-Patch nicht angewandt | `git apply hermes-cli-hmem-statusline.patch` |
| O-Node fehlt in Statusbar | session_id-Cache nicht geschrieben | o9k-startup.sh hook prüfen |
| 🔴 Sync offline | `hmem sync status` schlägt fehl | `hmem sync status` manuell testen |
| Hook-Scripts werden nicht ausgeführt | hooks_auto_accept: false | Setze `hooks_auto_accept: true` in config.yaml |