hmem-config

View and change hmem memory settings, hooks, sync, and checkpoint configuration. Use this skill whenever the user types /hmem-config, asks to change memory settings, adjust parameters, tune bulk-read behavior, configure auto-checkpoints, manage hmem-sync, or troubleshoot memory-related issues. Also trigger when the user asks things like 'how often does auto-save fire', 'why is my context so large', 'change checkpoint to auto', 'how many tokens does startup cost', or 'set up sync'.

hmem-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:

{
  "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

ParameterDefaultPurpose
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.
maxDepth5Tree depth (1–5). Most users need 5. Lower values save storage but lose granularity.
defaultReadLimit100Max entries per bulk read. Lower = faster startup, higher = more complete overview.
maxTitleChars50Auto-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.
accessCountTopN5Entries 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:

ParameterDefaultPurpose
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.
checkpointInterval20Exchanges 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.
recentOEntries10How 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.
contextTokenThreshold100000When 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.

{
  "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
      }
    }
  }
}
FieldTypeDescription
sections[].namestringL2 section title. Used for matching during auto-reconcile (case-insensitive).
sections[].loadDepth0-40=skip, 1=title only, 2=+L3 titles, 3=+L3 body, 4=full subtree
sections[].defaultChildrenstring[]L3 nodes created by create_project. Optional.
createLinkedObooleanAuto-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:

ParameterDefaultPurpose
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.

ParameterDefaultPurpose
bulkReadV2.topNewestCount5Newest entries expanded. Increase if you want more recent context at startup.
bulkReadV2.topAccessCount3Most-accessed entries expanded (time-weighted: access_count / log2(age_days + 2)).
bulkReadV2.topObsoleteCount3Obsolete entries kept visible — "biggest mistakes" are still worth seeing.
bulkReadV2.topSubnodeCount3Entries 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

ParameterRangeGuidance
maxCharsPerLevel[0] (L1)60–300Below 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–100000Raw data storage. Higher allows more verbatim content but L5 is rarely loaded.
maxDepth2–53 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.
checkpointInterval0–10020 is a good balance. Lower = more frequent saves (more Haiku cost). 0 = disabled.
recentOEntries0–2010 is the sweet spot. With checkpoint summaries, load_project shows summary + recent exchanges only — much more compact than raw exchange dumps.
contextTokenThreshold0–500000100k is recommended for most models. Increase for 1M-context models.

Common recipes

"I want auto-checkpoints":

{ "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:

{
  "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 hmem-sync status

Run this check as part of every /hmem-config invocation.

If installed (which hmem-sync): run npx hmem-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 hmem-sync enables zero-knowledge encrypted cross-device sync (AES-256-GCM, server sees only opaque blobs), and offer to install it:

npm install -g hmem-sync
npx hmem-sync connect

Sync troubleshooting

ProblemFix
"Config not found"Run npx hmem-sync connect
401 Token verification failedPassphrase has special chars — set HMEM_SYNC_PASSPHRASE in .mcp.json env
0 entries after pullHMEM_PATH filename must match between devices
Updatenpm update -g hmem-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):

{
  "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/hmem-mcp/dist/cli.js hook-startup",
            "shell": "powershell"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/hmem-mcp/dist/cli.js log-exchange",
            "shell": "powershell"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/hmem-mcp/dist/cli.js context-inject",
            "shell": "powershell"
          }
        ]
      }
    ]
  },
  "statusLine": {
    "type": "command",
    "command": "node C:/Users/<you>/AppData/Roaming/npm/node_modules/hmem-mcp/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:

SymptomCauseFix
UserPromptSubmit hook error (no stacktrace)Inline VAR=value in command + cmd.exe parses it as a command nameMove env vars to env block, remove inline prefix
bash.exe: *** fatal error - add_item ... errno 1Git Bash MSYS2 runtime crashing at startupAdd "shell": "powershell" to every hook command
Hooks silently do nothing (no errors)Wrong shell interpreting the command, or project not active for session loggingVerify "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.