Skip to content

fix(claude-code): centralize working memory hook#231

Merged
wey-gu merged 5 commits into
mainfrom
fix/claude-read-hook-helper
May 11, 2026
Merged

fix(claude-code): centralize working memory hook#231
wey-gu merged 5 commits into
mainfrom
fix/claude-read-hook-helper

Conversation

@wey-gu

@wey-gu wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member

Summary

  • Extract Claude Code SessionStart/compact Working Memory loading into scripts/nmem-hook-read.sh so both hook matchers share the same behavior.
  • Preserve the existing fallback order: project-space nmem wm read, default-space nmem wm read, then ~/ai-now/memory.md.
  • Bump the Claude Code plugin metadata to 0.7.9 and add regression coverage for space resolution and fallback paths.

Verification

  • uv run --with pytest pytest nowledge-mem-claude-code-plugin/tests/test_nmem_hook_read.py nowledge-mem-claude-code-plugin/tests/test_nmem_hook_save.py tests/plugin_e2e/test_key_plugins_e2e.py -q
  • uv run --with pytest pytest tests/plugin_e2e -q
  • uv run --with pytest pytest nowledge-mem-hermes/tests -q
  • node nowledge-mem-codex-plugin/scripts/validate-plugin.mjs
  • sh -n nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh
  • git diff --check

Note

Low Risk
Low risk refactor of Claude Code hook wiring plus added tests; main risk is regressions in shell hook execution and fallback behavior across environments (git/non-git, missing CLAUDE_PLUGIN_ROOT, Windows nmem.cmd).

Overview
Claude Code’s SessionStart and compact Working Memory injection is refactored to call a new shared script (scripts/nmem-hook-read.sh) instead of duplicating a long inline shell pipeline in hooks.json, while preserving the same fallback order (space-aware wm read → default wm read$HOME/ai-now/memory.md).

Adds focused regression tests for space resolution (git --git-common-dir and NMEM_SPACE override), fallback behavior when space/default WM is empty or nmem is missing, and Windows nmem.cmd invocation/escaping; updates plugin registry/manifest versions to 0.7.9 and asserts in E2E static checks that hooks no longer embed wm read directly and that file fallback works without CLAUDE_PLUGIN_ROOT.

Reviewed by Cursor Bugbot for commit 3d3baa8. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Chores

    • Bumped version to 0.7.9
  • Tests

    • Added test coverage for working memory read hook behavior, including git-space resolution, environment variable overrides, and fallback scenarios

Review Change Stack

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

bugbot review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6feee2d25b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh Outdated
@nowledge-co nowledge-co deleted a comment from coderabbitai Bot May 11, 2026
@wey-gu wey-gu requested a review from Copilot May 11, 2026 16:21
@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

bugbot review

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The pull request extracts the working-memory read logic from inline hook commands into a standalone shell script (nmem-hook-read.sh) with cross-platform support, space resolution, and fallback handling. Version bumps to 0.7.9 are applied to the integration and plugin manifests. Comprehensive tests validate space derivation, overrides, fallbacks, and Windows invocation. The e2e contract is updated to verify the new script.

Changes

Hook Read Script Extraction

Layer / File(s) Summary
Version Manifest Updates
integrations.json, nowledge-mem-claude-code-plugin/.claude-plugin/plugin.json
Integration and plugin manifests bumped from 0.7.8 to 0.7.9.
Hook Read Script Implementation
nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh
New shell script handles working-memory injection with Windows cmd wrapper, space resolution via NMEM_SPACE or derived from git common-dir, JSON parsing via Python, and fallback to ~/ai-now/memory.md.
Hook Configuration Wiring
nowledge-mem-claude-code-plugin/hooks/hooks.json
SessionStart (startup|resume|clear) and compact hooks delegate memory-read logic to nmem-hook-read.sh instead of inline commands.
Hook Read Script Tests
nowledge-mem-claude-code-plugin/tests/test_nmem_hook_read.py
Five tests validate git-derived space selection, NMEM_SPACE override, default-space fallback when project space is empty, local memory.md fallback without nmem, and Windows cmd.exe invocation with stub executables.
Documentation & E2E Contract
nowledge-mem-claude-code-plugin/CHANGELOG.md, tests/plugin_e2e/test_key_plugins_e2e.py
Changelog documents the extraction and regression coverage; e2e test extends Claude plugin contract to require nmem-hook-read.sh and nmem-hook-save.py presence and verify absence of inline "wm read".

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • nowledge-co/community#224: Continues space-aware SessionStart/compact hook changes, extracting inline read logic into the new nmem-hook-read.sh script with tests.
  • nowledge-co/community#115: Earlier PR that modified Claude plugin hook logic for nmem invocation and fallback handling.
  • nowledge-co/community#82: Prior modification to Claude Code plugin hook configuration and version manifest.

🐰 A script hops out of the shell,
With spaces and fallbacks that work quite well;
Windows and Unix, no more duplication,
Hook logic blooms in extraction!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(claude-code): centralize working memory hook' accurately reflects the main change: refactoring duplicated working memory loading logic into a shared centralized script.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/claude-read-hook-helper

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes Claude Code “Working Memory” injection by moving the duplicated nmem wm read shell pipeline out of hooks.json into a shared script (scripts/nmem-hook-read.sh), while preserving the existing fallback order (project space → default space → ~/ai-now/memory.md). It also bumps the Claude Code plugin version to 0.7.9 and adds regression/static-contract tests to ensure the new wiring stays in place.

Changes:

  • Replace inline wm read pipelines in Claude Code SessionStart hooks with a shared nmem-hook-read.sh script.
  • Add unit + e2e static-contract coverage to validate hook wiring, space resolution, and fallback behavior.
  • Bump Claude Code plugin metadata/version references to 0.7.9 and document in the changelog.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/plugin_e2e/test_key_plugins_e2e.py Adds static assertions that Claude hooks reference the new read script and no longer embed wm read.
nowledge-mem-claude-code-plugin/tests/test_nmem_hook_read.py New regression tests for space resolution and fallback order in the read hook script.
nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh New shared implementation of the Working Memory read/fallback logic (including Windows nmem.cmd handling).
nowledge-mem-claude-code-plugin/hooks/hooks.json Updates SessionStart hook commands to call the shared read script.
nowledge-mem-claude-code-plugin/CHANGELOG.md Documents the refactor and added coverage for version 0.7.9.
nowledge-mem-claude-code-plugin/.claude-plugin/plugin.json Bumps plugin version to 0.7.9.
integrations.json Updates the Claude Code integration version to 0.7.9.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +118 to +122
result = _run_hook(
tmp_path,
cwd=tmp_path,
env={"PATH": "/bin:/usr/bin", "NMEM_SPACE": ""},
)
@wey-gu wey-gu requested a review from Copilot May 11, 2026 16:26
@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

bugbot review

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh (1)

6-12: 💤 Low value

Consider more robust argument escaping for the Windows wrapper.

The argument quoting in the nmem() function may not handle edge cases correctly when arguments contain embedded quotes, backslashes, or cmd.exe metacharacters. While nmem arguments are typically simple, a space name like project"2024 could break the command string passed to cmd.exe.

Consider escaping arguments more defensively, or document the limitation.

♻️ More robust quoting approach
-    nmem() {
-      q=""
-      for a in "$@"; do
-        q="$q \"$a\""
-      done
-      cmd.exe /s /c "\"nmem.cmd\"$q"
-    }
+    nmem() {
+      # Escape each argument for cmd.exe by doubling quotes
+      local args=""
+      for a in "$@"; do
+        # Replace " with "" for cmd.exe escaping
+        local escaped="${a//\"/\"\"}"
+        args="$args \"$escaped\""
+      done
+      cmd.exe /s /c "\"nmem.cmd\"$args"
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh` around lines 6 -
12, The nmem() wrapper builds a command string without escaping backslashes or
embedded double-quotes, which can break cmd.exe invocation for args like
project"2024; update nmem to defensively escape each argument before adding it
to q (e.g., for each a: first escape backslashes then escape double quotes via
parameter expansion esc=${a//\\/\\\\}; esc=${esc//\"/\\\"}; then append q="$q
\"$esc\""), continue to wrap each escaped arg in quotes and pass the final
string to cmd.exe /s /c "\"nmem.cmd\"$q"; reference: function name nmem in the
diff.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh`:
- Around line 6-12: The nmem() wrapper builds a command string without escaping
backslashes or embedded double-quotes, which can break cmd.exe invocation for
args like project"2024; update nmem to defensively escape each argument before
adding it to q (e.g., for each a: first escape backslashes then escape double
quotes via parameter expansion esc=${a//\\/\\\\}; esc=${esc//\"/\\\"}; then
append q="$q \"$esc\""), continue to wrap each escaped arg in quotes and pass
the final string to cmd.exe /s /c "\"nmem.cmd\"$q"; reference: function name
nmem in the diff.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 66d5c732-28dd-41cd-b9a8-6670b851b9b0

📥 Commits

Reviewing files that changed from the base of the PR and between e3406bd and f437502.

📒 Files selected for processing (7)
  • integrations.json
  • nowledge-mem-claude-code-plugin/.claude-plugin/plugin.json
  • nowledge-mem-claude-code-plugin/CHANGELOG.md
  • nowledge-mem-claude-code-plugin/hooks/hooks.json
  • nowledge-mem-claude-code-plugin/scripts/nmem-hook-read.sh
  • nowledge-mem-claude-code-plugin/tests/test_nmem_hook_read.py
  • tests/plugin_e2e/test_key_plugins_e2e.py

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

bugbot review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

Addressed the actionable review comments in the latest head:

  • Codex WSL bridge concern: nmem.cmd is invoked by command name through cmd.exe, not by POSIX /mnt/c/... path, with regression coverage.
  • Copilot fallback-test concern: the no-nmem test now uses an isolated PATH containing only cat, so the fallback branch is deterministic.
  • CodeRabbit quote-escaping nit: the nmem.cmd bridge now escapes backslashes and embedded double quotes before building the cmd.exe command string, with quote-containing NMEM_SPACE coverage.

Final-head validation passed locally: focused hook E2E, Claude live E2E, plugin static gate, Codex plugin validator, Hermes tests, sh -n, and git diff --check.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread nowledge-mem-claude-code-plugin/hooks/hooks.json Outdated
@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@wey-gu

wey-gu commented May 11, 2026

Copy link
Copy Markdown
Member Author

bugbot review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d3baa8e5e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{
"type": "command",
"command": "command -v nmem >/dev/null 2>&1 || { command -v nmem.cmd >/dev/null 2>&1 && nmem(){ local q=''; for a in \"$@\"; do q=\"$q \\\"$a\\\"\"; done; cmd.exe /s /c \"\\\"nmem.cmd\\\"$q\"; }; }; PY=\"$(command -v python3 || command -v python || true)\"; SPACE=\"${NMEM_SPACE:-$(basename \"$(cd \"$(dirname \"$(git rev-parse --git-common-dir 2>/dev/null)\")\" 2>/dev/null && pwd)\" 2>/dev/null | tr '[:upper:]' '[:lower:]')}\"; { [ -n \"$PY\" ] && [ -n \"$SPACE\" ] && nmem --json wm read --space \"$SPACE\" 2>/dev/null | \"$PY\" -c \"import sys,json;d=json.load(sys.stdin);c=d.get('content','');print(c) if d.get('exists') and c else sys.exit(1)\" 2>/dev/null; } || { [ -n \"$PY\" ] && nmem --json wm read 2>/dev/null | \"$PY\" -c \"import sys,json;d=json.load(sys.stdin);c=d.get('content','');print(c) if c else sys.exit(1)\" 2>/dev/null; } || cat ~/ai-now/memory.md 2>/dev/null || true"
"command": "SCRIPT=\"${CLAUDE_PLUGIN_ROOT:-}/scripts/nmem-hook-read.sh\"; { [ -f \"$SCRIPT\" ] && sh \"$SCRIPT\"; } || cat \"$HOME/ai-now/memory.md\" 2>/dev/null || true"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep nmem lookup when plugin root is unset

The new SessionStart hook command now only runs WM loading through scripts/nmem-hook-read.sh when that file path exists, and otherwise falls straight to ~/ai-now/memory.md. If CLAUDE_PLUGIN_ROOT is unset/empty (which can happen in direct hook execution), SCRIPT becomes /scripts/nmem-hook-read.sh, so the hook never attempts nmem --json wm read even when nmem is installed; users lose project/default working-memory injection unless the fallback file exists. The previous inline command still performed nmem wm read in this scenario, so this is a behavior regression.

Useful? React with 👍 / 👎.

@wey-gu wey-gu merged commit 0a620df into main May 11, 2026
2 checks passed
@wey-gu wey-gu deleted the fix/claude-read-hook-helper branch May 11, 2026 16:52

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3d3baa8. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants