Skip to content
This repository was archived by the owner on Jan 2, 2026. It is now read-only.

Commit 9a6ba56

Browse files
zircoteclaude
andcommitted
fix(plugin): make hooks and commands self-contained for cross-project use
- Add sys.path bootstrap to all hook entry points (posttooluse.py, precompact.py, sessionstart.py, stop.py, userpromptsubmit.py) to find plugin bundled src directory via __file__ resolution - Update all command files (capture.md, recall.md, search.md, status.md, sync.md) to use uv run --directory pointing to plugin root This ensures the plugin uses its own pyproject.toml and dependencies rather than the current project's environment - Use CLAUDE_PLUGIN_ROOT env var if available, fall back to known plugin cache location for compatibility This fixes the issue where running plugin commands in other projects would fail with ModuleNotFoundError or trigger unnecessary project builds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 3ad7651 commit 9a6ba56

11 files changed

Lines changed: 117 additions & 18 deletions

File tree

commands/capture.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ If `$ARGUMENTS` is empty or very short (< 10 characters):
4343
Use Bash to invoke the Python library:
4444

4545
```bash
46-
uv run python3 -c "
46+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
47+
uv run --directory "$PLUGIN_ROOT" python3 -c "
4748
from git_notes_memory import get_capture_service
4849
4950
capture = get_capture_service()

commands/recall.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ If query is empty:
3434
Use Bash to invoke the Python library:
3535

3636
```bash
37-
uv run python3 -c "
37+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
38+
uv run --directory "$PLUGIN_ROOT" python3 -c "
3839
from git_notes_memory import get_recall_service
3940
4041
recall = get_recall_service()

commands/search.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ Use Bash to invoke the Python library:
3232

3333
**Semantic Search** (default - vector similarity):
3434
```bash
35-
uv run python3 -c "
35+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
36+
uv run --directory "$PLUGIN_ROOT" python3 -c "
3637
from git_notes_memory import get_recall_service
3738
3839
recall = get_recall_service()
@@ -58,7 +59,8 @@ else:
5859

5960
**Text Search** (keyword/FTS matching):
6061
```bash
61-
uv run python3 -c "
62+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
63+
uv run --directory "$PLUGIN_ROOT" python3 -c "
6264
from git_notes_memory import get_recall_service
6365
6466
recall = get_recall_service()

commands/status.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ Check if `--verbose` flag is present.
2222

2323
**Basic Status**:
2424
```bash
25-
uv run python3 -c "
25+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
26+
uv run --directory "$PLUGIN_ROOT" python3 -c "
2627
from git_notes_memory import get_sync_service
2728
from git_notes_memory.index import IndexService
2829
from git_notes_memory.config import get_embedding_model, get_index_path, get_data_path
@@ -61,11 +62,12 @@ print(f'| Data Directory | {get_data_path()} |')
6162

6263
**Verbose Status**:
6364
```bash
64-
uv run python3 -c "
65+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
66+
uv run --directory "$PLUGIN_ROOT" python3 -c "
67+
import subprocess
6568
from git_notes_memory import get_sync_service
6669
from git_notes_memory.index import IndexService
6770
from git_notes_memory.config import get_embedding_model, get_index_path, get_data_path, NAMESPACES
68-
import subprocess
6971
7072
sync = get_sync_service()
7173
index_path = get_index_path()

commands/sync.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ Use Bash to invoke the Python library based on mode:
2626

2727
**Incremental Sync** (default):
2828
```bash
29-
uv run python3 -c "
30-
from git_notes_memory import get_sync_service
29+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
30+
uv run --directory "$PLUGIN_ROOT" python3 -c "
3131
import time
32+
from git_notes_memory import get_sync_service
3233
3334
sync = get_sync_service()
3435
start = time.time()
@@ -45,9 +46,10 @@ print(f'| Duration | {duration:.2f}s |')
4546

4647
**Full Reindex**:
4748
```bash
48-
uv run python3 -c "
49-
from git_notes_memory import get_sync_service
49+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
50+
uv run --directory "$PLUGIN_ROOT" python3 -c "
5051
import time
52+
from git_notes_memory import get_sync_service
5153
5254
sync = get_sync_service()
5355
start = time.time()
@@ -64,7 +66,8 @@ print(f'| Duration | {duration:.2f}s |')
6466

6567
**Verify Consistency**:
6668
```bash
67-
uv run python3 -c "
69+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
70+
uv run --directory "$PLUGIN_ROOT" python3 -c "
6871
from git_notes_memory import get_sync_service
6972
7073
sync = get_sync_service()
@@ -87,7 +90,8 @@ else:
8790

8891
**Repair**:
8992
```bash
90-
uv run python3 -c "
93+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
94+
uv run --directory "$PLUGIN_ROOT" python3 -c "
9195
from git_notes_memory import get_sync_service
9296
9397
sync = get_sync_service()
@@ -113,7 +117,8 @@ else:
113117

114118
If `--dry-run` is specified, show what would happen without making changes:
115119
```bash
116-
uv run python3 -c "
120+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
121+
uv run --directory "$PLUGIN_ROOT" python3 -c "
117122
from git_notes_memory import get_sync_service
118123
119124
sync = get_sync_service()

hooks/posttooluse.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
from __future__ import annotations
1818

1919
import sys
20+
from pathlib import Path
21+
22+
# Bootstrap: Add plugin's src directory to sys.path for self-contained execution
23+
_plugin_root = Path(__file__).resolve().parent.parent
24+
_src_path = _plugin_root / "src"
25+
if _src_path.exists() and str(_src_path) not in sys.path:
26+
sys.path.insert(0, str(_src_path))
2027

2128

2229
def main() -> None:

hooks/precompact.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
from __future__ import annotations
2121

2222
import sys
23+
from pathlib import Path
24+
25+
# Bootstrap: Add plugin's src directory to sys.path for self-contained execution
26+
_plugin_root = Path(__file__).resolve().parent.parent
27+
_src_path = _plugin_root / "src"
28+
if _src_path.exists() and str(_src_path) not in sys.path:
29+
sys.path.insert(0, str(_src_path))
2330

2431

2532
def main() -> None:

hooks/sessionstart.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@
1414
from __future__ import annotations
1515

1616
import json
17-
import os
1817
import sys
1918
from pathlib import Path
2019

20+
# Bootstrap: Add plugin's src directory to sys.path for self-contained execution
21+
_plugin_root = Path(__file__).resolve().parent.parent
22+
_src_path = _plugin_root / "src"
23+
if _src_path.exists() and str(_src_path) not in sys.path:
24+
sys.path.insert(0, str(_src_path))
25+
2126

2227
def get_memory_status() -> dict:
2328
"""Get current memory system status.
@@ -106,11 +111,11 @@ def build_context_message(status: dict) -> str:
106111

107112
def main() -> None:
108113
"""Main hook entry point."""
109-
# Read hook input from stdin
114+
# Read hook input from stdin (not used but must be consumed)
110115
try:
111-
input_data = json.load(sys.stdin)
116+
_input_data = json.load(sys.stdin)
112117
except (json.JSONDecodeError, ValueError):
113-
input_data = {}
118+
_input_data = {} # noqa: F841
114119

115120
# Get memory system status
116121
status = get_memory_status()

hooks/stop.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121

2222
import json
2323
import sys
24+
from pathlib import Path
25+
26+
# Bootstrap: Add plugin's src directory to sys.path for self-contained execution
27+
_plugin_root = Path(__file__).resolve().parent.parent
28+
_src_path = _plugin_root / "src"
29+
if _src_path.exists() and str(_src_path) not in sys.path:
30+
sys.path.insert(0, str(_src_path))
2431

2532

2633
def main() -> None:

hooks/userpromptsubmit.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
import json
1818
import re
1919
import sys
20+
from pathlib import Path
21+
22+
# Bootstrap: Add plugin's src directory to sys.path for self-contained execution
23+
_plugin_root = Path(__file__).resolve().parent.parent
24+
_src_path = _plugin_root / "src"
25+
if _src_path.exists() and str(_src_path) not in sys.path:
26+
sys.path.insert(0, str(_src_path))
2027

2128

2229
def should_capture(prompt: str) -> tuple[bool, str]:

0 commit comments

Comments
 (0)