memhook 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/bin/memhook.d.ts +16 -0
- package/dist/bin/memhook.d.ts.map +1 -0
- package/dist/bin/memhook.js +122 -0
- package/dist/bin/memhook.js.map +1 -0
- package/dist/src/cache.d.ts +30 -0
- package/dist/src/cache.d.ts.map +1 -0
- package/dist/src/cache.js +80 -0
- package/dist/src/cache.js.map +1 -0
- package/dist/src/catalog.d.ts +20 -0
- package/dist/src/catalog.d.ts.map +1 -0
- package/dist/src/catalog.js +152 -0
- package/dist/src/catalog.js.map +1 -0
- package/dist/src/config.d.ts +60 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +172 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/configFile.d.ts +54 -0
- package/dist/src/configFile.d.ts.map +1 -0
- package/dist/src/configFile.js +51 -0
- package/dist/src/configFile.js.map +1 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/preFilter.d.ts +16 -0
- package/dist/src/preFilter.d.ts.map +1 -0
- package/dist/src/preFilter.js +40 -0
- package/dist/src/preFilter.js.map +1 -0
- package/dist/src/providers/anthropic.d.ts +33 -0
- package/dist/src/providers/anthropic.d.ts.map +1 -0
- package/dist/src/providers/anthropic.js +98 -0
- package/dist/src/providers/anthropic.js.map +1 -0
- package/dist/src/providers/factory.d.ts +15 -0
- package/dist/src/providers/factory.d.ts.map +1 -0
- package/dist/src/providers/factory.js +37 -0
- package/dist/src/providers/factory.js.map +1 -0
- package/dist/src/providers/http.d.ts +34 -0
- package/dist/src/providers/http.d.ts.map +1 -0
- package/dist/src/providers/http.js +60 -0
- package/dist/src/providers/http.js.map +1 -0
- package/dist/src/providers/ollama.d.ts +30 -0
- package/dist/src/providers/ollama.d.ts.map +1 -0
- package/dist/src/providers/ollama.js +89 -0
- package/dist/src/providers/ollama.js.map +1 -0
- package/dist/src/providers/openai.d.ts +31 -0
- package/dist/src/providers/openai.d.ts.map +1 -0
- package/dist/src/providers/openai.js +94 -0
- package/dist/src/providers/openai.js.map +1 -0
- package/dist/src/providers/types.d.ts +48 -0
- package/dist/src/providers/types.d.ts.map +1 -0
- package/dist/src/providers/types.js +18 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/router.d.ts +32 -0
- package/dist/src/router.d.ts.map +1 -0
- package/dist/src/router.js +342 -0
- package/dist/src/router.js.map +1 -0
- package/dist/src/version.d.ts +13 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js +13 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +88 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to memhook are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0](https://github.com/utilia-ai-wox/memhook/compare/v0.1.0-preview.0...v0.2.0) (2026-06-02)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **providers:** add multi-provider support and optional YAML config ([97920e4](https://github.com/utilia-ai-wox/memhook/commit/97920e4dc5680dd403336b0a9c44a021fe193de3))
|
|
14
|
+
* **providers:** add OpenAI and Ollama providers + optional YAML config ([8bc5555](https://github.com/utilia-ai-wox/memhook/commit/8bc5555ad73130267984b0d18660a630fdb409d3))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **config:** clamp numeric knobs and widen boolean env vocabulary ([0ad7c6f](https://github.com/utilia-ai-wox/memhook/commit/0ad7c6f13ada7c33933fd8ef70fdf20d35e1f882))
|
|
20
|
+
* **config:** clamp numeric knobs and widen boolean env vocabulary ([ea386c0](https://github.com/utilia-ai-wox/memhook/commit/ea386c0247fc8746908635d0362cff3f01b34be2))
|
|
21
|
+
* point package exports at the compiled dist/src layout ([970ee71](https://github.com/utilia-ai-wox/memhook/commit/970ee711685baaaf63967afb8c5d696a9688486b))
|
|
22
|
+
* point package exports at the compiled dist/src layout ([57e1ff4](https://github.com/utilia-ai-wox/memhook/commit/57e1ff469e4eeb25cbca568f9a390cac690c759a))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
|
|
27
|
+
* document v0.2 multi-provider and YAML config ([63e6d1b](https://github.com/utilia-ai-wox/memhook/commit/63e6d1b709f2f405b1f18c18958fe28979d57702))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Chore
|
|
31
|
+
|
|
32
|
+
* graduate release line to clean 0.2.0 ([f0a07e5](https://github.com/utilia-ai-wox/memhook/commit/f0a07e51fe5792df4efd7af9d59452d97603b675))
|
|
33
|
+
|
|
34
|
+
## [Unreleased]
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **OpenAI provider** (`MEMHOOK_PROVIDER=openai`) — Chat Completions API,
|
|
39
|
+
`Authorization: Bearer`, catalog as the leading system message so OpenAI's
|
|
40
|
+
automatic prompt caching can engage. Default model `gpt-4o-mini`.
|
|
41
|
+
- **Ollama local provider** (`MEMHOOK_PROVIDER=ollama`) — native `/api/chat`
|
|
42
|
+
endpoint, no API key, `stream:false` + `format:"json"`. Default model
|
|
43
|
+
`llama3.1`, with a 30s default timeout to absorb cold model loads.
|
|
44
|
+
- **YAML config file** (`config.yaml`) — optional, opt-in, read from
|
|
45
|
+
`$MEMHOOK_CONFIG` or `~/.config/memhook/config.yaml`. Precedence is
|
|
46
|
+
env var > YAML > default. A missing or malformed file is ignored
|
|
47
|
+
(fail-soft to defaults). See `config.example.yaml`.
|
|
48
|
+
- `src/providers/factory.ts` — `createProvider()` selects the adapter from
|
|
49
|
+
`config.provider.type` with compile-time exhaustiveness.
|
|
50
|
+
- `src/providers/http.ts` — single shared `postJsonWithRetry` transport
|
|
51
|
+
(timeout + single retry) used by all providers.
|
|
52
|
+
- `MEMHOOK_PROVIDER` and `MEMHOOK_CONFIG` env vars; per-provider defaults for
|
|
53
|
+
model / API-key env var / timeout.
|
|
54
|
+
- Hardening pre-publish (CI, CHANGELOG, CONTRIBUTING, `.env.example`)
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- The provider interface is now provider-agnostic: Anthropic-specific
|
|
59
|
+
`betaHeaders` and `cacheControlTtl` moved off the shared `SelectionRequest`
|
|
60
|
+
into `AnthropicProviderOptions`. `ProviderConfig.apiKey` is now optional
|
|
61
|
+
(local providers need none).
|
|
62
|
+
- Cache key now includes the provider identity (`type:model`) so switching
|
|
63
|
+
provider or model never serves a selection made by a different model.
|
|
64
|
+
- The two hardcoded version strings (`config.ts`, `bin/memhook.ts`) are
|
|
65
|
+
centralised in `src/version.ts`.
|
|
66
|
+
|
|
67
|
+
### Note
|
|
68
|
+
|
|
69
|
+
- Adding `openai` / `ollama` introduces opt-in outbound calls to
|
|
70
|
+
`api.openai.com` / `localhost:11434`. The default remains Anthropic-only;
|
|
71
|
+
`api.anthropic.com` is still the sole endpoint for an unconfigured user. No
|
|
72
|
+
telemetry, no phone-home.
|
|
73
|
+
- First runtime dependency: `yaml` (zero sub-dependencies).
|
|
74
|
+
|
|
75
|
+
## [0.1.0-preview.0] — 2026-05-28
|
|
76
|
+
|
|
77
|
+
Initial public preview.
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- `src/router.ts` — UserPromptSubmit hook entry point with cap-A1 projection
|
|
82
|
+
fix (skip a file pre-injection if its content would push the cumulated
|
|
83
|
+
injection past `maxAdditionalChars`, while always allowing at least one).
|
|
84
|
+
- `src/catalog.ts` — catalog builder with Q4 title-only reduction for
|
|
85
|
+
non-CWD zones (~50% size cut on a typical 3-repo layout).
|
|
86
|
+
- `src/cache.ts` — local LRU cache keyed on
|
|
87
|
+
`sha256(prompt + catalog_mtime + cwd + script_version)`. Stored as
|
|
88
|
+
per-key JSON files. 60-min TTL by default, 7-day eviction floor.
|
|
89
|
+
- `src/preFilter.ts` — trivial-prompt pre-filter loaded from
|
|
90
|
+
`~/.config/memhook/trivial-words.txt` with a sensible default list.
|
|
91
|
+
- `src/providers/anthropic.ts` — provider implementation for Anthropic
|
|
92
|
+
Messages API. Uses `ephemeral` `1h` cache control on the system prompt
|
|
93
|
+
(GA in 2026, no beta header) so the catalog sits in cache (10× cheaper
|
|
94
|
+
writes amortised across the hour).
|
|
95
|
+
- `src/config.ts` — env-driven config loader. No YAML in v0.1; deferred to
|
|
96
|
+
v0.2.
|
|
97
|
+
- `bin/memhook.ts` — CLI with `run`, `build-catalog`, `version`, `help`.
|
|
98
|
+
- JSONL observability log at `~/.claude/logs/memhook.log` including
|
|
99
|
+
`additional_size_chars` + `additional_size_tokens_est` so users can audit
|
|
100
|
+
the actual size of the injected `additionalContext` over time.
|
|
101
|
+
- 18 unit tests covering the router pipeline, pre-filter normalisation, and
|
|
102
|
+
cache key derivation / TTL / eviction.
|
|
103
|
+
|
|
104
|
+
[Unreleased]: https://github.com/utilia-ai-wox/memhook/compare/v0.1.0-preview.0...HEAD
|
|
105
|
+
[0.1.0-preview.0]: https://github.com/utilia-ai-wox/memhook/releases/tag/v0.1.0-preview.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# memhook
|
|
2
|
+
|
|
3
|
+
> Semantic memory router for Claude Code — picks the relevant feedbacks &
|
|
4
|
+
> rules for each prompt via Haiku, injects them as `additionalContext`.
|
|
5
|
+
|
|
6
|
+
**Status**: `v0.1.0-preview` — extracted from a private hook used daily across
|
|
7
|
+
3 large repos. API surface and naming may shift before `v1.0.0`.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
Claude Code's `~/.claude/` directory accumulates a growing set of
|
|
12
|
+
`feedback_*.md` (behavioural corrections) and `rule_*.md` (project doctrine)
|
|
13
|
+
files. Loading all of them on every prompt is wasteful (10–14k tokens of
|
|
14
|
+
catalog overhead, most of it irrelevant to the current question).
|
|
15
|
+
|
|
16
|
+
memhook uses **Haiku 4.5** as a cheap router: each user prompt is matched
|
|
17
|
+
against a one-line catalog of all available memory files, and only the 0–5
|
|
18
|
+
most relevant ones are read and injected into `additionalContext`. The rest
|
|
19
|
+
sit on disk, invisible to Claude until they matter.
|
|
20
|
+
|
|
21
|
+
## How it works
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
UserPromptSubmit hook
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
┌────────────────────────────────────────┐
|
|
28
|
+
│ 1. Pre-filter trivial prompts │ ── "ok" / "merci" → skip LLM
|
|
29
|
+
│ 2. Check local LRU cache │ ── identical prompt < 60min → hit
|
|
30
|
+
│ 3. Call Haiku with catalog as system │ ── ephemeral 1h cache control
|
|
31
|
+
│ 4. Parse JSON array of basenames │ ── ["feedback_X.md", "rule_Y.md"]
|
|
32
|
+
│ 5. Read files, cap by token budget │ ── max 9.5k chars or 5 files
|
|
33
|
+
│ 6. Emit additionalContext │
|
|
34
|
+
└────────────────────────────────────────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g memhook # not yet published — see "From source" below
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### From source (preview)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/utilia-ai-wox/memhook.git
|
|
47
|
+
cd memhook
|
|
48
|
+
bun install
|
|
49
|
+
bun run build
|
|
50
|
+
npm link
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Setup
|
|
54
|
+
|
|
55
|
+
1. **Set your API key**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export ANTHROPIC_API_KEY=sk-ant-…
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. **Build the initial catalog**
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
memhook build-catalog
|
|
65
|
+
# → ~/.claude/cache/memory-catalog.txt
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
3. **Wire the hooks** in `~/.claude/settings.json`:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"hooks": {
|
|
73
|
+
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "memhook run" }] }],
|
|
74
|
+
"SessionStart": [
|
|
75
|
+
{
|
|
76
|
+
"hooks": [{ "type": "command", "command": "memhook build-catalog" }]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
Every knob is an env var, and (since `v0.2`) optionally a YAML file.
|
|
86
|
+
Precedence per key is **env var > YAML file > built-in default**, so an
|
|
87
|
+
env-var-only setup behaves exactly as before. Sensible defaults work for most
|
|
88
|
+
users.
|
|
89
|
+
|
|
90
|
+
| Variable | Default | Purpose |
|
|
91
|
+
| -------------------------------- | ------------------------------- | -------------------------------------- |
|
|
92
|
+
| `MEMHOOK_ENABLED` | `true` | Master toggle |
|
|
93
|
+
| `MEMHOOK_PROVIDER` | `anthropic` | `anthropic` \| `openai` \| `ollama` |
|
|
94
|
+
| `MEMHOOK_MODEL` | per-provider | Model id (provider default if unset) |
|
|
95
|
+
| `MEMHOOK_API_KEY_ENV` | per-provider | Name of env var holding the API key |
|
|
96
|
+
| `MEMHOOK_BASE_URL` | per-provider | Override the provider API endpoint |
|
|
97
|
+
| `MEMHOOK_CONFIG` | `~/.config/memhook/config.yaml` | Path to the optional YAML config file |
|
|
98
|
+
| `MEMHOOK_MAX_FILES` | `5` | Hard cap on injected files |
|
|
99
|
+
| `MEMHOOK_MAX_ADDITIONAL_CHARS` | `9500` | Soft cap on injected chars (≈2.4k tok) |
|
|
100
|
+
| `MEMHOOK_MAX_OUTPUT_TOKENS` | `200` | Model output cap for the selection |
|
|
101
|
+
| `MEMHOOK_TIMEOUT_MS` | `8000` (`30000` for ollama) | Per-request timeout |
|
|
102
|
+
| `MEMHOOK_DISABLE_CACHE=true` | _(off)_ | Skip local LRU cache |
|
|
103
|
+
| `MEMHOOK_DISABLE_PREFILTER=true` | _(off)_ | Skip trivial-prompt skip |
|
|
104
|
+
| `MEMHOOK_DEBUG=true` | _(off)_ | Print errors to stderr |
|
|
105
|
+
|
|
106
|
+
### YAML config (optional)
|
|
107
|
+
|
|
108
|
+
memhook works with **no config file at all**. If you prefer YAML, copy
|
|
109
|
+
[`config.example.yaml`](config.example.yaml) to `~/.config/memhook/config.yaml`
|
|
110
|
+
(or point `MEMHOOK_CONFIG` at it). A missing or malformed file is ignored
|
|
111
|
+
silently — memhook falls back to env vars and defaults, never blocking your
|
|
112
|
+
prompt.
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
provider:
|
|
116
|
+
type: openai
|
|
117
|
+
# model + apiKeyEnv default to gpt-4o-mini + OPENAI_API_KEY
|
|
118
|
+
selection:
|
|
119
|
+
maxFiles: 5
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Providers
|
|
123
|
+
|
|
124
|
+
The default provider is **Anthropic** — with no `MEMHOOK_PROVIDER` set, the
|
|
125
|
+
only outbound call memhook ever makes is to `api.anthropic.com`, using your own
|
|
126
|
+
key. Selecting another provider is **opt-in** and changes which endpoint is
|
|
127
|
+
contacted. memhook never phones home and has no telemetry; "provider" means the
|
|
128
|
+
LLM endpoint _you_ choose to route through.
|
|
129
|
+
|
|
130
|
+
| Provider | `MEMHOOK_PROVIDER` | Default model | API key | Endpoint |
|
|
131
|
+
| --------- | ------------------ | ------------------ | ------------------- | --------------------------------------- |
|
|
132
|
+
| Anthropic | `anthropic` | `claude-haiku-4-5` | `ANTHROPIC_API_KEY` | `api.anthropic.com` |
|
|
133
|
+
| OpenAI | `openai` | `gpt-4o-mini` | `OPENAI_API_KEY` | `api.openai.com` |
|
|
134
|
+
| Ollama | `ollama` | `llama3.1` | _none_ (local) | `http://localhost:11434` (configurable) |
|
|
135
|
+
|
|
136
|
+
- **OpenAI** — set `MEMHOOK_PROVIDER=openai` and `OPENAI_API_KEY`. Uses the
|
|
137
|
+
Chat Completions API; the catalog rides as the leading system message so
|
|
138
|
+
OpenAI's automatic prompt caching can engage on a large catalog.
|
|
139
|
+
- **Ollama** — set `MEMHOOK_PROVIDER=ollama` and make sure the model is pulled
|
|
140
|
+
(`ollama pull llama3.1`) with the daemon running. No API key required. Hits
|
|
141
|
+
the native `/api/chat` endpoint with `stream:false`; the timeout defaults to
|
|
142
|
+
30s to absorb cold model loads.
|
|
143
|
+
|
|
144
|
+
## Observability
|
|
145
|
+
|
|
146
|
+
Every invocation appends one JSON line to `~/.claude/logs/memhook.log`:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"ts": "2026-05-28T08:41:18Z",
|
|
151
|
+
"prompt_preview": "fix the OPAQUE wire format drift…",
|
|
152
|
+
"selected": ["opaque-interop-rust-ts.md"],
|
|
153
|
+
"latency_ms": 1727,
|
|
154
|
+
"tokens_in": 12,
|
|
155
|
+
"tokens_out": 28,
|
|
156
|
+
"cache_create": 0,
|
|
157
|
+
"cache_read": 13398,
|
|
158
|
+
"additional_size_chars": 20225,
|
|
159
|
+
"additional_size_tokens_est": 5056,
|
|
160
|
+
"status": "ok"
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Useful one-liner to inspect the last 7 days:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
jq -c 'select((.ts | fromdateiso8601) > (now - 7*86400)) | .status' \
|
|
168
|
+
~/.claude/logs/memhook.log | sort | uniq -c
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Status values
|
|
172
|
+
|
|
173
|
+
| `status` | Meaning |
|
|
174
|
+
| ---------------------- | ----------------------------------------------------- |
|
|
175
|
+
| `ok` | Files injected from a fresh model selection |
|
|
176
|
+
| `cache_hit` | Files injected from local LRU cache |
|
|
177
|
+
| `pre_filter_skip` | Trivial prompt, LLM call skipped |
|
|
178
|
+
| `empty_selection` | The model returned `[]` (no memory needed) |
|
|
179
|
+
| `all_unfound` | The model returned basenames that don't exist on disk |
|
|
180
|
+
| `no_catalog` | Catalog missing — run `memhook build-catalog` |
|
|
181
|
+
| `no_api_key` | API key env var not set (not needed for ollama) |
|
|
182
|
+
| `provider_init_failed` | Provider couldn't be constructed (bad config) |
|
|
183
|
+
| `api_no_response` | Network error or timeout |
|
|
184
|
+
| `api_no_content` | API returned 200 but no text |
|
|
185
|
+
| `parse_invalid` | Response wasn't a valid JSON array |
|
|
186
|
+
|
|
187
|
+
## Fail-soft
|
|
188
|
+
|
|
189
|
+
memhook never blocks Claude Code. On any error — missing key, network
|
|
190
|
+
timeout, malformed JSON, broken filesystem — it emits an empty
|
|
191
|
+
`additionalContext` and logs the status. Your prompt always reaches the
|
|
192
|
+
model, just without injected memories for that turn.
|
|
193
|
+
|
|
194
|
+
## Roadmap
|
|
195
|
+
|
|
196
|
+
- `v0.2` ✅ — YAML config file, OpenAI provider, Ollama local provider
|
|
197
|
+
- `v0.3` — TUI live monitor (`memhook tail`)
|
|
198
|
+
- `v0.4` — Companion skills (`/wrap`, `/curate`, `/relay`)
|
|
199
|
+
- `v0.5` — Auto-bootstrap (`memhook init` detects empty memory dirs)
|
|
200
|
+
- `v1.0` — Cross-platform validated, published to npm
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT © wox
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* memhook CLI — three commands shipped in v0.1 preview:
|
|
4
|
+
*
|
|
5
|
+
* memhook run Read stdin (Claude Code hook JSON), write hook output
|
|
6
|
+
* memhook build-catalog Rebuild ~/.claude/cache/memory-catalog.txt
|
|
7
|
+
* memhook version Print package version
|
|
8
|
+
*
|
|
9
|
+
* Designed to be wired into ~/.claude/settings.json hooks:
|
|
10
|
+
* "UserPromptSubmit": [{ "hooks": [{ "type": "command",
|
|
11
|
+
* "command": "memhook run" }] }]
|
|
12
|
+
* "SessionStart": [{ "hooks": [{ "type": "command",
|
|
13
|
+
* "command": "memhook build-catalog" }] }]
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=memhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memhook.d.ts","sourceRoot":"","sources":["../../bin/memhook.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* memhook CLI — three commands shipped in v0.1 preview:
|
|
4
|
+
*
|
|
5
|
+
* memhook run Read stdin (Claude Code hook JSON), write hook output
|
|
6
|
+
* memhook build-catalog Rebuild ~/.claude/cache/memory-catalog.txt
|
|
7
|
+
* memhook version Print package version
|
|
8
|
+
*
|
|
9
|
+
* Designed to be wired into ~/.claude/settings.json hooks:
|
|
10
|
+
* "UserPromptSubmit": [{ "hooks": [{ "type": "command",
|
|
11
|
+
* "command": "memhook run" }] }]
|
|
12
|
+
* "SessionStart": [{ "hooks": [{ "type": "command",
|
|
13
|
+
* "command": "memhook build-catalog" }] }]
|
|
14
|
+
*/
|
|
15
|
+
import { route } from "../src/router.js";
|
|
16
|
+
import { buildCatalog } from "../src/catalog.js";
|
|
17
|
+
import { loadConfig } from "../src/config.js";
|
|
18
|
+
import { MEMHOOK_VERSION as VERSION } from "../src/version.js";
|
|
19
|
+
async function main() {
|
|
20
|
+
const cmd = process.argv[2] ?? "help";
|
|
21
|
+
switch (cmd) {
|
|
22
|
+
case "run":
|
|
23
|
+
await cmdRun();
|
|
24
|
+
break;
|
|
25
|
+
case "build-catalog":
|
|
26
|
+
cmdBuildCatalog();
|
|
27
|
+
break;
|
|
28
|
+
case "version":
|
|
29
|
+
case "--version":
|
|
30
|
+
case "-v":
|
|
31
|
+
console.log(VERSION);
|
|
32
|
+
break;
|
|
33
|
+
case "help":
|
|
34
|
+
case "--help":
|
|
35
|
+
case "-h":
|
|
36
|
+
printHelp();
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
console.error(`memhook: unknown command "${cmd}"`);
|
|
40
|
+
printHelp();
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function cmdRun() {
|
|
45
|
+
const stdin = await readStdin();
|
|
46
|
+
let output;
|
|
47
|
+
try {
|
|
48
|
+
output = await route(stdin);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
// Fail-soft: emit empty additionalContext, never block the user prompt
|
|
52
|
+
output = {
|
|
53
|
+
hookSpecificOutput: {
|
|
54
|
+
hookEventName: "UserPromptSubmit",
|
|
55
|
+
additionalContext: "",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
if (process.env["MEMHOOK_DEBUG"] === "true") {
|
|
59
|
+
process.stderr.write(`memhook run error: ${String(err)}\n`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
process.stdout.write(JSON.stringify(output) + "\n");
|
|
63
|
+
}
|
|
64
|
+
function cmdBuildCatalog() {
|
|
65
|
+
const config = loadConfig();
|
|
66
|
+
const result = buildCatalog({
|
|
67
|
+
cwd: process.cwd(),
|
|
68
|
+
outputPath: config.catalog.path,
|
|
69
|
+
});
|
|
70
|
+
process.stderr.write(`[memhook build-catalog] ${config.catalog.path} — ${result.lines}L, ${result.bytes}B\n`);
|
|
71
|
+
}
|
|
72
|
+
function readStdin() {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
let data = "";
|
|
75
|
+
process.stdin.setEncoding("utf8");
|
|
76
|
+
process.stdin.on("data", (chunk) => (data += chunk));
|
|
77
|
+
process.stdin.on("end", () => resolve(data));
|
|
78
|
+
process.stdin.on("error", reject);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function printHelp() {
|
|
82
|
+
console.log(`memhook ${VERSION}
|
|
83
|
+
|
|
84
|
+
USAGE
|
|
85
|
+
memhook <command>
|
|
86
|
+
|
|
87
|
+
COMMANDS
|
|
88
|
+
run Read Claude Code hook JSON from stdin, emit additionalContext
|
|
89
|
+
build-catalog Rebuild the memory catalog at $MEMHOOK_CATALOG_PATH
|
|
90
|
+
version Print version
|
|
91
|
+
help Show this message
|
|
92
|
+
|
|
93
|
+
ENV VARS
|
|
94
|
+
MEMHOOK_ENABLED toggle (default: true)
|
|
95
|
+
MEMHOOK_PROVIDER anthropic | openai | ollama (default: anthropic)
|
|
96
|
+
MEMHOOK_MODEL model id (per-provider default if unset)
|
|
97
|
+
MEMHOOK_API_KEY_ENV env var name holding the API key
|
|
98
|
+
(anthropic: ANTHROPIC_API_KEY, openai: OPENAI_API_KEY,
|
|
99
|
+
ollama: none required)
|
|
100
|
+
MEMHOOK_BASE_URL override the provider API endpoint
|
|
101
|
+
MEMHOOK_CONFIG path to a YAML config file
|
|
102
|
+
(default: ~/.config/memhook/config.yaml)
|
|
103
|
+
MEMHOOK_MAX_FILES file-count cap (default: 5)
|
|
104
|
+
MEMHOOK_MAX_ADDITIONAL_CHARS injection size cap (default: 9500)
|
|
105
|
+
MEMHOOK_MAX_OUTPUT_TOKENS model output cap (default: 200)
|
|
106
|
+
MEMHOOK_TIMEOUT_MS request timeout (default: 8000; ollama: 30000)
|
|
107
|
+
MEMHOOK_DISABLE_CACHE=true skip local LRU cache
|
|
108
|
+
MEMHOOK_DISABLE_PREFILTER=true skip trivial-prompt skip
|
|
109
|
+
MEMHOOK_DEBUG=true print errors to stderr (default: silent fail-soft)
|
|
110
|
+
|
|
111
|
+
PROVIDERS
|
|
112
|
+
Default is Anthropic (only api.anthropic.com is contacted). Selecting
|
|
113
|
+
openai or ollama is opt-in and changes the outbound endpoint to
|
|
114
|
+
api.openai.com or your local Ollama (http://localhost:11434) respectively.
|
|
115
|
+
Per-key precedence is env var > YAML config > built-in default.
|
|
116
|
+
`);
|
|
117
|
+
}
|
|
118
|
+
main().catch((err) => {
|
|
119
|
+
process.stderr.write(`memhook fatal: ${String(err)}\n`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=memhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memhook.js","sourceRoot":"","sources":["../../bin/memhook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local LRU cache for memhook selections.
|
|
3
|
+
*
|
|
4
|
+
* Key = sha256(prompt + catalog_mtime + cwd + script_version + provider). Bump
|
|
5
|
+
* any of these and the entry is automatically invalidated — `provider`
|
|
6
|
+
* (type + model) is included so switching provider/model never serves a stale
|
|
7
|
+
* selection made by a different model.
|
|
8
|
+
*
|
|
9
|
+
* Storage: one JSON file per key under config.cache.dir. TTL enforced via
|
|
10
|
+
* filesystem mtime check.
|
|
11
|
+
*/
|
|
12
|
+
export interface CacheKeyInput {
|
|
13
|
+
prompt: string;
|
|
14
|
+
catalogMtimeMs: number;
|
|
15
|
+
cwd: string;
|
|
16
|
+
scriptVersion: string;
|
|
17
|
+
/** Provider identity, e.g. "anthropic:claude-haiku-4-5". */
|
|
18
|
+
provider: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class LocalCache {
|
|
21
|
+
private readonly dir;
|
|
22
|
+
private readonly ttlMin;
|
|
23
|
+
private readonly evictionDays;
|
|
24
|
+
constructor(dir: string, ttlMin: number, evictionDays: number);
|
|
25
|
+
key(input: CacheKeyInput): string;
|
|
26
|
+
get(key: string): string | null;
|
|
27
|
+
put(key: string, value: string): void;
|
|
28
|
+
evictStale(): number;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAFZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM;IAKvC,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM;IAKjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiB/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKrC,UAAU,IAAI,MAAM;CAwBrB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local LRU cache for memhook selections.
|
|
3
|
+
*
|
|
4
|
+
* Key = sha256(prompt + catalog_mtime + cwd + script_version + provider). Bump
|
|
5
|
+
* any of these and the entry is automatically invalidated — `provider`
|
|
6
|
+
* (type + model) is included so switching provider/model never serves a stale
|
|
7
|
+
* selection made by a different model.
|
|
8
|
+
*
|
|
9
|
+
* Storage: one JSON file per key under config.cache.dir. TTL enforced via
|
|
10
|
+
* filesystem mtime check.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
export class LocalCache {
|
|
16
|
+
dir;
|
|
17
|
+
ttlMin;
|
|
18
|
+
evictionDays;
|
|
19
|
+
constructor(dir, ttlMin, evictionDays) {
|
|
20
|
+
this.dir = dir;
|
|
21
|
+
this.ttlMin = ttlMin;
|
|
22
|
+
this.evictionDays = evictionDays;
|
|
23
|
+
mkdirSync(this.dir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
key(input) {
|
|
26
|
+
const raw = `${input.prompt}|${input.catalogMtimeMs}|${input.cwd}|${input.scriptVersion}|${input.provider}`;
|
|
27
|
+
return createHash("sha256").update(raw).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
get(key) {
|
|
30
|
+
const file = join(this.dir, `${key}.json`);
|
|
31
|
+
let stat;
|
|
32
|
+
try {
|
|
33
|
+
stat = statSync(file);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
39
|
+
if (ageMs > this.ttlMin * 60_000)
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return readFileSync(file, "utf8");
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
put(key, value) {
|
|
49
|
+
const file = join(this.dir, `${key}.json`);
|
|
50
|
+
writeFileSync(file, value, "utf8");
|
|
51
|
+
}
|
|
52
|
+
evictStale() {
|
|
53
|
+
let removed = 0;
|
|
54
|
+
const cutoffMs = Date.now() - this.evictionDays * 86_400_000;
|
|
55
|
+
let entries = [];
|
|
56
|
+
try {
|
|
57
|
+
entries = readdirSync(this.dir);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
for (const name of entries) {
|
|
63
|
+
if (!name.endsWith(".json"))
|
|
64
|
+
continue;
|
|
65
|
+
const file = join(this.dir, name);
|
|
66
|
+
try {
|
|
67
|
+
const stat = statSync(file);
|
|
68
|
+
if (stat.mtimeMs < cutoffMs) {
|
|
69
|
+
unlinkSync(file);
|
|
70
|
+
removed++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// ignore
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return removed;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memhook catalog builder — TS port of build-memory-catalog.sh.
|
|
3
|
+
*
|
|
4
|
+
* Discovers feedbacks & projects in `~/.claude/projects/* /memory/`, global
|
|
5
|
+
* rules in `~/.claude/rules/`, and project rules in `<cwd>/.claude/rules/`.
|
|
6
|
+
*
|
|
7
|
+
* Phase 0.5 Q4: title-only for non-CWD zones (~50% catalog size reduction).
|
|
8
|
+
* The CWD zone gets full `basename: description`; others list just basenames.
|
|
9
|
+
*/
|
|
10
|
+
export interface CatalogBuildOptions {
|
|
11
|
+
cwd: string;
|
|
12
|
+
projectsRoot?: string;
|
|
13
|
+
globalRulesDir?: string;
|
|
14
|
+
outputPath: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildCatalog(opts: CatalogBuildOptions): {
|
|
17
|
+
lines: number;
|
|
18
|
+
bytes: number;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CA2BA"}
|