Compare commits
277 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b214a59a1 | |||
| 77579669f2 | |||
| 4e67393d27 | |||
| cb0933a908 | |||
| af95461a70 | |||
| 79b3a61ac8 | |||
| a9e3b659d3 | |||
| af9584a174 | |||
| 63827c9dcb | |||
| 809175612c | |||
| 06d9ef24f1 | |||
| f37a1fd6dc | |||
| badcd0e0be | |||
| 22683f6910 | |||
| 7ffa1b06ee | |||
| 418e38ee46 | |||
| 6ac5507e4e | |||
| 9bcef1774d | |||
| edecfdb5bc | |||
| 74670c00a6 | |||
| bd9b02f364 | |||
| 05b615c858 | |||
| e13562e4cb | |||
| c7f7f87321 | |||
| 11532a36fb | |||
| 121f673328 | |||
| f5b69df11a | |||
| 7cc27d45a4 | |||
| 9c20f4142c | |||
| 7b7a92e35a | |||
| 33ab7ba747 | |||
| 1b9f601c41 | |||
| b2ddf59db4 | |||
| a0d737ba51 | |||
| db207807cb | |||
| 1d090b33f5 | |||
| 700aae8a31 | |||
| fbdff0b178 | |||
| 018549c3c7 | |||
| 63e8755be5 | |||
| c8aaa79ab6 | |||
| 194e44f52a | |||
| efd47a9586 | |||
| f1ecf5bc68 | |||
| 719079581a | |||
| f8d8de53e8 | |||
| baa37eba07 | |||
| 8933343433 | |||
| e4e1d3fb92 | |||
| dac989c697 | |||
| 5969d670d0 | |||
| 39990f2818 | |||
| 8dfcb5e612 | |||
| 4d91053f8c | |||
| ff503d08a7 | |||
| 0ee8c00c5f | |||
| d4e122a1f8 | |||
| 5508194189 | |||
| ded11645e3 | |||
| 7afbba4c3f | |||
| bf439043cf | |||
| 24ce746b91 | |||
| 98920bd860 | |||
| fbcbdfca62 | |||
| 6ae8890092 | |||
| c113e23001 | |||
| b58a61c0b3 | |||
| 936080d710 | |||
| 10675f7125 | |||
| 39e4aa1976 | |||
| 5dffb1ebb0 | |||
| 56df2c45e5 | |||
| 4c719d776a | |||
| da1d2cd36a | |||
| 7ed1e576b2 | |||
| 8030c44af4 | |||
| 2f935a56b9 | |||
| 830d7a2b23 | |||
| 5cbe0a38ca | |||
| c477ded1b5 | |||
| 3040ea8a97 | |||
| 9ad640a5c4 | |||
| 109fb1b506 | |||
| 55e13047e4 | |||
| 75a0f2e981 | |||
| d935229711 | |||
| 91e1d5baad | |||
| 8de8afbab8 | |||
| 75df2e52c1 | |||
| 40e699209f | |||
| 256ebcead0 | |||
| 44bfb74932 | |||
| 97c84a3963 | |||
| b493c2d6b0 | |||
| 4ea03b0597 | |||
| 7b08c94bdb | |||
| 92f7c7dfd9 | |||
| 358215a92f | |||
| 867513dc79 | |||
| 583a76b150 | |||
| 2a62a94b09 | |||
| 05d65ed797 | |||
| 1e04f82fe2 | |||
| 596f0ad16c | |||
| 296bf195ce | |||
| ba0793127c | |||
| 06711cfb20 | |||
| 8628e4024d | |||
| 1b912020ea | |||
| 3093f80196 | |||
| 6b67b3df63 | |||
| e9fee163a9 | |||
| e24ad77ab5 | |||
| c56e0ae7f8 | |||
| ed9ffe76a7 | |||
| 0622ebf534 | |||
| 63b0105d05 | |||
| 3ad53733e8 | |||
| 403913a0fc | |||
| 9f2a237aaf | |||
| b967772897 | |||
| e19617a065 | |||
| 9bd56c993c | |||
| f62b5d248c | |||
| 711f5455df | |||
| e36efff00d | |||
| 486bae633b | |||
| 228a006953 | |||
| 2d40afe7ef | |||
| ff55b6e4e0 | |||
| e2de295c41 | |||
| ea38601564 | |||
| 9fd32ad44b | |||
| dd753184db | |||
| e8097caed7 | |||
| eb7e37dcf1 | |||
| f24bba21e9 | |||
| 1a1297c12a | |||
| af308ea5c8 | |||
| 6382d6f9c7 | |||
| 1ae6c5708c | |||
| 93bad99d79 | |||
| 9c68a3ba3d | |||
| 117710d449 | |||
| b07130acc6 | |||
| bb96092d74 | |||
| 9907df1db8 | |||
| ab3d4ca865 | |||
| e424d85a93 | |||
| e5a133b3da | |||
| 15e9473533 | |||
| 20333e6214 | |||
| 5d1ee20076 | |||
| 576d407c00 | |||
| 3eb0154d58 | |||
| 80663fff97 | |||
| 27aa98c269 | |||
| 9789a1969c | |||
| d333c7dc08 | |||
| edfc1a403f | |||
| ad37629f0a | |||
| 7be19b20cd | |||
| 5b2eb9f599 | |||
| ede75b1c5b | |||
| cdb0e823aa | |||
| a35190b7bf | |||
| 4b180d8d66 | |||
| 61615ddc0f | |||
| 0ecb387f58 | |||
| 3840a1734d | |||
| ca990e8d53 | |||
| c9010c5c58 | |||
| 2ea716a017 | |||
| 4e4ad32f97 | |||
| faa1360f33 | |||
| 64dc50f3fb | |||
| 1ac35b5d56 | |||
| 1dd456a6ca | |||
| be01694383 | |||
| b8b88d998c | |||
| bf4d9421e0 | |||
| 67f9d631bd | |||
| 311d62cc02 | |||
| 46a75c4d98 | |||
| fececc4e51 | |||
| d0b4c7ee59 | |||
| e6af8d207a | |||
| 75b9930ee4 | |||
| 86b1d7fad9 | |||
| 2aab998b62 | |||
| 6b22017721 | |||
| 34aef1aa18 | |||
| d00762d1f7 | |||
| 400f08c20d | |||
| 18c7f1cd76 | |||
| 74506dd4e6 | |||
| 0f72356cb1 | |||
| 121fb24705 | |||
| 867c658f4f | |||
| 0704355a7f | |||
| 6930a38997 | |||
| e218dc5947 | |||
| 7e1c13c6c2 | |||
| e87701228d | |||
| d6751e0f07 | |||
| b04a041c2d | |||
| 8218fcb17b | |||
| 86bdb036bd | |||
| eefc2eaf0f | |||
| ebed56674e | |||
| ce4cab0a23 | |||
| 9913820366 | |||
| 640053d727 | |||
| aaf00829d3 | |||
| 094f5ab1b4 | |||
| a0380fe1f7 | |||
| 9864410eb8 | |||
| 450b2135b6 | |||
| a58a22cc6a | |||
| 0c9b8826c9 | |||
| 74f6b75db2 | |||
| b8821f5ea3 | |||
| 8f1a260d96 | |||
| a69613b4e0 | |||
| e7cae825bd | |||
| 486570d2b8 | |||
| ce576db0dc | |||
| 53f6f57420 | |||
| 814d2f6c03 | |||
| 006ff40175 | |||
| aedee33ca9 | |||
| 6ee6e21eb5 | |||
| add5d62cec | |||
| fa604849fe | |||
| 34004904e7 | |||
| 4df9f61347 | |||
| 14ca7cf7d6 | |||
| 57a60c1309 | |||
| bef825c0d8 | |||
| 93354e7a3e | |||
| f173a32fa3 | |||
| 7566b8c650 | |||
| 2eaef1f586 | |||
| 1341e93fca | |||
| 06864b0199 | |||
| a16b25275e | |||
| abffce6424 | |||
| c948a7778b | |||
| bd1fe5995f | |||
| 6791069bca | |||
| 3e6add90de | |||
| d3331d1e22 | |||
| bd619229b2 | |||
| 182097ef1c | |||
| 0b7ecedcd7 | |||
| da01e4bba0 | |||
| 7c3bfadd5e | |||
| a8bb625513 | |||
| bab8f554bd | |||
| c1b5b2a783 | |||
| 67651669a1 | |||
| ae454cfc01 | |||
| fa218b0d71 | |||
| c29d91a9c4 | |||
| e6ae017609 | |||
| 901cff909e | |||
| 5c8e2dcfcc | |||
| 47dec9cf4d | |||
| 3d40b45fd1 | |||
| 05323c9db5 | |||
| c314946204 | |||
| 7eb9f4051c | |||
| 550183bab2 | |||
| 92c4d245c4 | |||
| a2ab45a461 | |||
| 1d68e299dc | |||
| b987789754 |
@@ -1,8 +1,6 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Oct 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.3",
|
||||
"version": "10.0.2",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 21, 2025
|
||||
|
||||
**test-analysis-report.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31743 | 10:36 PM | 🔵 | PR #412 proposes mode system with inheritance and multilingual support | ~523 |
|
||||
|
||||
### Dec 22, 2025
|
||||
|
||||
**test-analysis-report.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31865 | 6:56 PM | ✅ | 開発ドキュメントのクリーンアップをコミット | ~150 |
|
||||
| #31864 | " | ✅ | 計画ドキュメントと分析ファイルの削除 | ~142 |
|
||||
| #31859 | 6:55 PM | ✅ | 計画ドキュメントファイルの削除 | ~109 |
|
||||
| #31855 | 6:53 PM | ✅ | テストスイートの大規模削除とテスト分析レポートの追加 | ~197 |
|
||||
|
||||
### Dec 25, 2025
|
||||
|
||||
**settings.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32602 | 8:42 PM | 🔵 | Identified potential settings configuration files | ~224 |
|
||||
</claude-mem-context>
|
||||
@@ -1,34 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Oct 25, 2025
|
||||
|
||||
**changelog.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2491 | 10:59 PM | 🟣 | Added changelog viewer slash command | ~290 |
|
||||
|
||||
**terminal-shortcut.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2480 | 6:32 PM | 🟣 | Cross-platform terminal shortcut command for claude-mem CLI | ~459 |
|
||||
|
||||
**setup-alias.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2479 | 6:29 PM | 🟣 | Shell Alias Setup Slash Command | ~423 |
|
||||
|
||||
**version-bump.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2358 | 1:07 PM | 🟣 | Created version-bump slash command for automated version updates | ~361 |
|
||||
|
||||
### Jan 1, 2026
|
||||
|
||||
**anti-pattern-czar.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35638 | 11:17 PM | 🟣 | Anti-Pattern Czar Custom Command Created | ~583 |
|
||||
</claude-mem-context>
|
||||
@@ -1,7 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,176 @@
|
||||
# Bugfix Plan: Observer Sessions Authentication Failure
|
||||
|
||||
## Problem Summary
|
||||
|
||||
Observer sessions fail with "Invalid API key · Please run /login" because the `CLAUDE_CONFIG_DIR` environment variable is being set to an isolated directory (`~/.claude-mem/observer-config/`) that lacks authentication credentials.
|
||||
|
||||
## Root Cause
|
||||
|
||||
**File:** `src/services/worker/ProcessRegistry.ts` (lines 207-211)
|
||||
|
||||
```typescript
|
||||
const isolatedEnv = {
|
||||
...spawnOptions.env,
|
||||
CLAUDE_CONFIG_DIR: OBSERVER_CONFIG_DIR // <-- This isolates auth credentials!
|
||||
};
|
||||
```
|
||||
|
||||
This was added in Issue #832 to prevent observer sessions from polluting the `claude --resume` list. However, it also isolates the authentication credentials, breaking the SDK's ability to authenticate with the Anthropic API.
|
||||
|
||||
## Evidence
|
||||
|
||||
1. Running Claude with alternate config dir reproduces the error:
|
||||
```bash
|
||||
CLAUDE_CONFIG_DIR=/tmp/test-claude claude --print "hello"
|
||||
# Output: Invalid API key · Please run /login
|
||||
```
|
||||
|
||||
2. The observer config directory exists but only has cached feature flags, no authentication:
|
||||
- `~/.claude-mem/observer-config/.claude.json` - feature flags only
|
||||
- No credentials copied from main `~/.claude/` directory
|
||||
|
||||
## Solution
|
||||
|
||||
The fix must allow authentication while still isolating session history. Claude Code stores different data types in `CLAUDE_CONFIG_DIR`:
|
||||
- Authentication credentials (needed)
|
||||
- Session history/resume list (should be isolated)
|
||||
- Feature flags and settings (can be shared or isolated)
|
||||
|
||||
**Approach:** Do NOT override `CLAUDE_CONFIG_DIR`. Instead, find an alternative solution for Issue #832.
|
||||
|
||||
### Alternative Approaches for Session Isolation
|
||||
|
||||
1. **Use `--no-resume` flag** (if SDK supports it) - Prevent observer sessions from being resumable
|
||||
2. **Accept pollution** - Observer sessions in resume list may be acceptable tradeoff
|
||||
3. **Post-hoc cleanup** - Clean up observer session entries from history after completion
|
||||
4. **SDK parameter** - Check if SDK has a session isolation option that doesn't affect auth
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Documentation Discovery
|
||||
|
||||
### Objective
|
||||
Understand SDK options for session isolation without breaking authentication.
|
||||
|
||||
### Tasks
|
||||
1. Read SDK documentation/source for:
|
||||
- Available `query()` options
|
||||
- Session isolation mechanisms
|
||||
- Authentication handling
|
||||
|
||||
2. Read Issue #832 context:
|
||||
- What was the original problem?
|
||||
- How bad was the pollution?
|
||||
- Are there alternative solutions mentioned?
|
||||
|
||||
### Verification
|
||||
- [ ] List all `query()` options available
|
||||
- [ ] Identify if `--no-resume` or equivalent exists
|
||||
- [ ] Document the tradeoffs
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Fix Authentication
|
||||
|
||||
### Objective
|
||||
Remove the `CLAUDE_CONFIG_DIR` override to restore authentication.
|
||||
|
||||
### File to Modify
|
||||
`src/services/worker/ProcessRegistry.ts`
|
||||
|
||||
### Change
|
||||
Remove lines 207-211 that override `CLAUDE_CONFIG_DIR`:
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
const isolatedEnv = {
|
||||
...spawnOptions.env,
|
||||
CLAUDE_CONFIG_DIR: OBSERVER_CONFIG_DIR
|
||||
};
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
const isolatedEnv = {
|
||||
...spawnOptions.env
|
||||
// CLAUDE_CONFIG_DIR removed - observer sessions need access to auth credentials
|
||||
// Session isolation addressed via [alternative approach]
|
||||
};
|
||||
```
|
||||
|
||||
### Verification
|
||||
- [ ] Build succeeds: `npm run build`
|
||||
- [ ] Observer sessions authenticate successfully
|
||||
- [ ] Observations are saved to database
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Address Session Isolation (Issue #832)
|
||||
|
||||
### Objective
|
||||
Find alternative solution to prevent observer sessions from polluting `claude --resume` list.
|
||||
|
||||
### Options to Evaluate
|
||||
|
||||
1. **Option A: Accept the tradeoff**
|
||||
- Observer sessions appear in resume list but users can ignore them
|
||||
- No code changes needed beyond Phase 1
|
||||
|
||||
2. **Option B: Use isSynthetic flag**
|
||||
- If SDK has a flag to mark sessions as non-resumable, use it
|
||||
- Requires SDK documentation review
|
||||
|
||||
3. **Option C: Post-processing cleanup**
|
||||
- After session ends, remove observer entries from history
|
||||
- More complex, may have race conditions
|
||||
|
||||
### Decision Point
|
||||
After Phase 0 documentation review, choose the appropriate option.
|
||||
|
||||
### Verification
|
||||
- [ ] Chosen approach documented
|
||||
- [ ] If code changes made, tests pass
|
||||
- [ ] Observer sessions either isolated OR tradeoff accepted
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Testing
|
||||
|
||||
### Manual Tests
|
||||
1. Start a new Claude Code session with the plugin
|
||||
2. Verify observations are being saved (check logs)
|
||||
3. Check that no "Invalid API key" errors appear
|
||||
4. Verify `claude --resume` behavior (acceptable level of observer entries)
|
||||
|
||||
### Verification Checklist
|
||||
- [ ] `npm run build` succeeds
|
||||
- [ ] Worker service starts without errors
|
||||
- [ ] Observations save to database
|
||||
- [ ] No authentication errors in logs
|
||||
- [ ] Issue #832 regression acceptable or addressed
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
1. **DO NOT** add `ANTHROPIC_API_KEY` to environment - authentication is handled by Claude Code's built-in credential management
|
||||
2. **DO NOT** copy credential files to observer config dir - credentials may be in keychain or other secure storage
|
||||
3. **DO NOT** try to "fix" authentication by adding API key loading - that creates Issue #588 (unexpected API charges)
|
||||
|
||||
---
|
||||
|
||||
## Files Involved
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/services/worker/ProcessRegistry.ts` | Contains the problematic `CLAUDE_CONFIG_DIR` override |
|
||||
| `src/shared/paths.ts` | Defines `OBSERVER_CONFIG_DIR` constant |
|
||||
| `src/services/worker/SDKAgent.ts` | Uses `createPidCapturingSpawn` which sets the env |
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**Low Risk:** Removing the `CLAUDE_CONFIG_DIR` override is a simple, targeted change.
|
||||
|
||||
**Regression Risk (Issue #832):** Observer sessions may appear in `claude --resume` list again. This is a cosmetic issue vs. complete authentication failure, so the tradeoff favors removing the override.
|
||||
@@ -0,0 +1,314 @@
|
||||
# Plan: Cleanup worker-service.ts Unjustified Logic
|
||||
|
||||
**Created:** 2026-01-13
|
||||
**Source:** `docs/reports/nonsense-logic.md`
|
||||
**Target:** `src/services/worker-service.ts` (813 lines)
|
||||
**Goal:** Address 23 identified issues, prioritizing safe deletions first
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Documentation Discovery (COMPLETED)
|
||||
|
||||
### Evidence Gathered
|
||||
|
||||
**Exit Code Strategy (CLAUDE.md:44-54):**
|
||||
```
|
||||
- Exit 0: Success or graceful shutdown (Windows Terminal closes tabs)
|
||||
- Exit 1: Non-blocking error
|
||||
- Exit 2: Blocking error
|
||||
Philosophy: Exit 0 prevents Windows Terminal tab accumulation
|
||||
```
|
||||
|
||||
**Signal Handler Pattern (ProcessManager.ts:294-317):**
|
||||
- Uses mutable reference object `isShuttingDownRef`
|
||||
- Factory function `createSignalHandler()` returns handler with embedded state
|
||||
- Current implementation has 3-hop indirection
|
||||
|
||||
**MCP Client Pattern (worker-service.ts:157-160, ChromaSync.ts:124-136):**
|
||||
```typescript
|
||||
this.mcpClient = new Client({
|
||||
name: 'worker-search-proxy',
|
||||
version: '1.0.0'
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**Verification Results:**
|
||||
- `runInteractiveSetup` (lines 439-639): **NEVER CALLED** - grep shows only definition
|
||||
- `import * as fs from 'fs'` (line 13): **UNUSED** - no `fs.` usage found
|
||||
- `import { spawn } from 'child_process'` (line 14): **UNUSED** - no `spawn(` calls
|
||||
- `homedir` (line 15): Only used in `runInteractiveSetup` (dead code)
|
||||
- `processPendingQueues` default `= 10`: Never used, all callers pass explicit args
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Safe Deletions (Dead Code & Unused Imports)
|
||||
|
||||
### 1.1 Delete `runInteractiveSetup` Function
|
||||
|
||||
**What:** Delete lines 435-639 (~201 lines)
|
||||
**Why:** Function is defined but never called. Setup happens via `handleCursorCommand()`.
|
||||
**Evidence:** `grep -n "runInteractiveSetup" src/services/worker-service.ts` returns only definition
|
||||
|
||||
**Pattern to follow:** N/A - straight deletion
|
||||
|
||||
**Steps:**
|
||||
1. Read worker-service.ts lines 435-650
|
||||
2. Delete the entire function including section comment (lines 435-639)
|
||||
3. Run `npm run build` to verify no compile errors
|
||||
|
||||
**Verification:**
|
||||
- `grep "runInteractiveSetup" src/` returns nothing
|
||||
- Build succeeds
|
||||
|
||||
### 1.2 Remove Unused Imports
|
||||
|
||||
**What:** Delete lines 13, 14, 17
|
||||
|
||||
**Current (delete these):**
|
||||
```typescript
|
||||
import * as fs from 'fs'; // Line 13 - UNUSED
|
||||
import { spawn } from 'child_process'; // Line 14 - UNUSED
|
||||
import * as readline from 'readline'; // Line 17 - Only in dead code
|
||||
```
|
||||
|
||||
**Keep:**
|
||||
```typescript
|
||||
import { homedir } from 'os'; // Line 15 - DELETE (only in dead code)
|
||||
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs'; // Line 16 - CHECK USAGE
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. After deleting `runInteractiveSetup`, grep for remaining usages:
|
||||
- `grep "homedir" src/services/worker-service.ts`
|
||||
- `grep "readline" src/services/worker-service.ts`
|
||||
- `grep "detectClaudeCode\|findCursorHooksDir\|installCursorHooks\|configureCursorMcp" src/services/worker-service.ts`
|
||||
2. Delete imports with zero usages
|
||||
3. Run `npm run build`
|
||||
|
||||
**Verification:**
|
||||
- No TypeScript unused import warnings
|
||||
- Build succeeds
|
||||
|
||||
### 1.3 Clean Up Cursor Integration Imports
|
||||
|
||||
After deleting `runInteractiveSetup`, some CursorHooksInstaller imports become unused:
|
||||
- `detectClaudeCode` - only in runInteractiveSetup
|
||||
- `findCursorHooksDir` - only in runInteractiveSetup
|
||||
- `installCursorHooks` - only in runInteractiveSetup
|
||||
- `configureCursorMcp` - only in runInteractiveSetup
|
||||
|
||||
**Steps:**
|
||||
1. Grep each import after dead code removal
|
||||
2. Remove any that are now unused
|
||||
3. Keep `updateCursorContextForProject` (re-exported) and `handleCursorCommand` (used in main)
|
||||
|
||||
**Verification:**
|
||||
- `grep "detectClaudeCode\|findCursorHooksDir\|installCursorHooks\|configureCursorMcp" src/services/worker-service.ts` returns nothing
|
||||
- Build succeeds
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Low-Risk Simplifications
|
||||
|
||||
### 2.1 Remove Unused Default Parameter
|
||||
|
||||
**What:** Line 350 - `async processPendingQueues(sessionLimit: number = 10)`
|
||||
**Why:** Default never used. All callers pass explicit args (50 in startup, dynamic in HTTP)
|
||||
|
||||
**Change from:**
|
||||
```typescript
|
||||
async processPendingQueues(sessionLimit: number = 10): Promise<{...}>
|
||||
```
|
||||
|
||||
**Change to:**
|
||||
```typescript
|
||||
async processPendingQueues(sessionLimit: number): Promise<{...}>
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- Build succeeds
|
||||
- All call sites provide explicit values
|
||||
|
||||
### 2.2 Simplify onRestart Callback
|
||||
|
||||
**Location:** Lines 395-396 (approximate, find exact)
|
||||
**Issue:** `onShutdown` and `onRestart` both call `this.shutdown()`
|
||||
|
||||
**Find pattern:**
|
||||
```typescript
|
||||
onShutdown: () => this.shutdown(),
|
||||
onRestart: () => this.shutdown()
|
||||
```
|
||||
|
||||
**Options:**
|
||||
1. **Keep as-is** if restart semantically differs from shutdown (future-proofing)
|
||||
2. **Add comment** explaining intentional parity
|
||||
3. **Remove onRestart** if never used differently
|
||||
|
||||
**Investigation needed:** Grep for `onRestart` usage in Server.ts to understand contract
|
||||
|
||||
**Steps:**
|
||||
1. Grep `onRestart` in `src/services/server/`
|
||||
2. If Server.ts treats them identically, add clarifying comment
|
||||
3. If different, document why both map to shutdown
|
||||
|
||||
### 2.3 Fix Over-Commented Lines (Sample Only)
|
||||
|
||||
**Strategy:** Do NOT strip all comments. Only remove comments that describe obvious code.
|
||||
|
||||
**Anti-pattern (remove):**
|
||||
```typescript
|
||||
// WHAT: Imports centralized logging utility with structured output
|
||||
// WHY: All worker logs go through this for consistent formatting
|
||||
import { logger } from '../utils/logger.js';
|
||||
```
|
||||
|
||||
**Pattern to follow:** Remove WHAT/WHY on simple imports. Keep architectural comments.
|
||||
|
||||
**Scope:** Sample 5-10 obvious comment removals to demonstrate approach, not exhaustive
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Medium-Risk Improvements
|
||||
|
||||
### 3.1 Simplify Signal Handler Pattern
|
||||
|
||||
**Current (worker-service.ts:180-192 + ProcessManager.ts:294-317):**
|
||||
```typescript
|
||||
// 3-hop indirection with mutable reference
|
||||
const shutdownRef = { value: this.isShuttingDown };
|
||||
const handler = createSignalHandler(() => this.shutdown(), shutdownRef);
|
||||
process.on('SIGTERM', () => {
|
||||
this.isShuttingDown = shutdownRef.value; // Sync back
|
||||
handler('SIGTERM');
|
||||
});
|
||||
```
|
||||
|
||||
**Simplified approach:**
|
||||
```typescript
|
||||
private registerSignalHandlers(): void {
|
||||
const handler = async (signal: string) => {
|
||||
if (this.isShuttingDown) {
|
||||
logger.warn('SYSTEM', `Received ${signal} but shutdown already in progress`);
|
||||
return;
|
||||
}
|
||||
this.isShuttingDown = true;
|
||||
logger.info('SYSTEM', `Received ${signal}, shutting down...`);
|
||||
try {
|
||||
await this.shutdown();
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
logger.error('SYSTEM', 'Error during shutdown', {}, error as Error);
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
process.on('SIGTERM', () => handler('SIGTERM'));
|
||||
process.on('SIGINT', () => handler('SIGINT'));
|
||||
}
|
||||
```
|
||||
|
||||
**Decision needed:** Does `createSignalHandler` serve other callers? If yes, keep factory but simplify worker usage.
|
||||
|
||||
**Steps:**
|
||||
1. Grep `createSignalHandler` usage across codebase
|
||||
2. If only worker-service uses it, inline and simplify
|
||||
3. If shared, simplify worker's usage while keeping factory
|
||||
|
||||
### 3.2 Unify Dual Initialization Tracking
|
||||
|
||||
**Current (lines 111, 129-130):**
|
||||
```typescript
|
||||
private initializationCompleteFlag: boolean = false;
|
||||
private initializationComplete: Promise<void>;
|
||||
```
|
||||
|
||||
**Recommendation:** Keep both but add clarifying comments:
|
||||
- Promise: For async waiters (HTTP handlers)
|
||||
- Flag: For sync checks (status endpoints)
|
||||
|
||||
**Alternative:** Use Promise with inspection pattern:
|
||||
```typescript
|
||||
private initializationComplete = false;
|
||||
private initializationPromise: Promise<void>;
|
||||
// Flag derived from promise state via finally() callback
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Add documentation comment explaining dual tracking purpose
|
||||
2. Consider if flag can be derived from promise state instead
|
||||
|
||||
### 3.3 Reduce 5-Minute Timeout
|
||||
|
||||
**Location:** Lines 464-478 (approximate)
|
||||
**Current:** `const timeoutMs = 300000; // 5 minutes`
|
||||
**Recommendation:** Reduce to 30-60 seconds for HTTP handler, keep 5min for MCP init
|
||||
|
||||
**Caution:** MCP initialization can legitimately be slow (ChromaDB, model loading). May need different timeouts per use case.
|
||||
|
||||
**Steps:**
|
||||
1. Find exact line for context inject timeout
|
||||
2. Verify this is separate from MCP init timeout
|
||||
3. Reduce HTTP handler timeout to 30-60 seconds
|
||||
4. Keep MCP init timeout at 5 minutes
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Deferred / Low Priority
|
||||
|
||||
These items are noted but NOT part of this cleanup:
|
||||
|
||||
| Issue | Reason to Defer |
|
||||
|-------|-----------------|
|
||||
| Exit code 0 always | Documented Windows Terminal workaround - intentional |
|
||||
| Re-export for circular import | Works correctly, architectural fix is separate work |
|
||||
| Fallback agent verification | Behavioral change, needs feature design |
|
||||
| MCP version hardcoding | Low impact, separate version management issue |
|
||||
| Empty capabilities | Documentation issue only |
|
||||
| Unsafe `as Error` casts | Common TS pattern, low risk |
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Verification
|
||||
|
||||
### 5.1 Build Verification
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
Expected: No errors
|
||||
|
||||
### 5.2 Test Suite
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
Expected: All tests pass
|
||||
|
||||
### 5.3 Grep for Anti-patterns
|
||||
```bash
|
||||
# Verify dead code removed
|
||||
grep -r "runInteractiveSetup" src/
|
||||
|
||||
# Verify unused imports removed
|
||||
grep "import \* as fs from 'fs'" src/services/worker-service.ts
|
||||
grep "import { spawn }" src/services/worker-service.ts
|
||||
```
|
||||
Expected: No matches
|
||||
|
||||
### 5.4 Runtime Check
|
||||
```bash
|
||||
npm run build-and-sync
|
||||
# Start worker and verify basic operation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Phase | Items | Estimated Reduction |
|
||||
|-------|-------|---------------------|
|
||||
| Phase 1 | Dead code + unused imports | ~210 lines |
|
||||
| Phase 2 | Low-risk simplifications | ~5 lines + clarity |
|
||||
| Phase 3 | Medium-risk improvements | ~30 lines |
|
||||
| Total | | ~245 lines (~30% reduction) |
|
||||
|
||||
**Execution Order:** Phase 1 → Phase 2 → Phase 3 → Phase 5 (verification after each)
|
||||
@@ -0,0 +1,266 @@
|
||||
# Plan: Fix Empty CLAUDE.md File Generation
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Currently the CLAUDE.md generator creates files with wasteful content:
|
||||
1. **Empty files with "No recent activity"** - Files are created even when there are zero observations for a folder
|
||||
2. **Redundant HTML comment** - "<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->" is unnecessary since the `<claude-mem-context>` tag already conveys this information
|
||||
|
||||
These issues create noisy, wasteful context that loads automatically and provides no value.
|
||||
|
||||
## Phase 0: Documentation Discovery
|
||||
|
||||
### Allowed APIs (from code analysis)
|
||||
- `formatTimelineForClaudeMd(timelineText: string): string` - src/utils/claude-md-utils.ts:139
|
||||
- `formatObservationsForClaudeMd(observations, folderPath): string` - scripts/regenerate-claude-md.ts:238
|
||||
- `writeClaudeMdToFolder(folderPath, newContent): void` - src/utils/claude-md-utils.ts:94
|
||||
- `updateFolderClaudeMdFiles(filePaths, project, port, projectRoot): Promise<void>` - src/utils/claude-md-utils.ts:257
|
||||
- `replaceTaggedContent(existingContent, newContent): string` - src/utils/claude-md-utils.ts:64
|
||||
|
||||
### Key Locations
|
||||
| File | Lines | Purpose |
|
||||
|------|-------|---------|
|
||||
| `src/utils/claude-md-utils.ts` | 139-235 | Main formatting function |
|
||||
| `src/utils/claude-md-utils.ts` | 143 | HTML comment generation |
|
||||
| `src/utils/claude-md-utils.ts` | 209-211 | "No recent activity" handling |
|
||||
| `src/utils/claude-md-utils.ts` | 322-323 | Write decision point |
|
||||
| `scripts/regenerate-claude-md.ts` | 238-286 | Regeneration script formatting |
|
||||
| `scripts/regenerate-claude-md.ts` | 242 | HTML comment generation (duplicate) |
|
||||
| `scripts/regenerate-claude-md.ts` | 245-247 | "No recent activity" handling |
|
||||
| `scripts/regenerate-claude-md.ts` | 452-453 | Write decision point |
|
||||
| `tests/utils/claude-md-utils.test.ts` | 96-109 | Tests for "No recent activity" behavior |
|
||||
|
||||
### Anti-patterns to avoid
|
||||
- Do NOT add new configuration options for this behavior - just fix it
|
||||
- Do NOT add logging for skipped files (unnecessary noise)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Modify formatTimelineForClaudeMd to Return Empty on No Observations
|
||||
|
||||
### Task 1.1: Update formatTimelineForClaudeMd return behavior
|
||||
**File:** `src/utils/claude-md-utils.ts`
|
||||
**Lines:** 139-235
|
||||
|
||||
**Changes:**
|
||||
1. Remove HTML comment line at line 143
|
||||
2. Change the empty observations case (lines 209-211) to return an empty string instead of "No recent activity"
|
||||
|
||||
**Before (lines 141-144):**
|
||||
```typescript
|
||||
lines.push('# Recent Activity');
|
||||
lines.push('');
|
||||
lines.push('<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->');
|
||||
lines.push('');
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
lines.push('# Recent Activity');
|
||||
lines.push('');
|
||||
```
|
||||
|
||||
**Before (lines 209-212):**
|
||||
```typescript
|
||||
if (observations.length === 0) {
|
||||
lines.push('*No recent activity*');
|
||||
return lines.join('\n');
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
if (observations.length === 0) {
|
||||
return '';
|
||||
}
|
||||
```
|
||||
|
||||
### Verification
|
||||
- Run `bun test tests/utils/claude-md-utils.test.ts`
|
||||
- Tests at lines 96-109 will FAIL (expected - they test for "No recent activity")
|
||||
- Update tests to expect empty string for empty input
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Update updateFolderClaudeMdFiles to Skip Empty Content
|
||||
|
||||
### Task 2.1: Add empty content check before writing
|
||||
**File:** `src/utils/claude-md-utils.ts`
|
||||
**Lines:** 322-323
|
||||
|
||||
**Changes:**
|
||||
After formatting, check if result is empty and skip writing if so.
|
||||
|
||||
**Before (lines 321-325):**
|
||||
```typescript
|
||||
const formatted = formatTimelineForClaudeMd(result.content[0].text);
|
||||
writeClaudeMdToFolder(folderPath, formatted);
|
||||
|
||||
logger.debug('FOLDER_INDEX', 'Updated CLAUDE.md', { folderPath });
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
const formatted = formatTimelineForClaudeMd(result.content[0].text);
|
||||
if (!formatted) {
|
||||
logger.debug('FOLDER_INDEX', 'No observations for folder, skipping', { folderPath });
|
||||
continue;
|
||||
}
|
||||
writeClaudeMdToFolder(folderPath, formatted);
|
||||
|
||||
logger.debug('FOLDER_INDEX', 'Updated CLAUDE.md', { folderPath });
|
||||
```
|
||||
|
||||
### Verification
|
||||
- Grep for files containing "No recent activity": should find none after running
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Update Regeneration Script
|
||||
|
||||
### Task 3.1: Remove HTML comment from formatObservationsForClaudeMd
|
||||
**File:** `scripts/regenerate-claude-md.ts`
|
||||
**Lines:** 238-286
|
||||
|
||||
**Changes:**
|
||||
1. Remove HTML comment line at line 242
|
||||
2. Change empty observations case (lines 245-247) to return empty string
|
||||
|
||||
**Before (lines 240-244):**
|
||||
```typescript
|
||||
lines.push('# Recent Activity');
|
||||
lines.push('');
|
||||
lines.push('<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->');
|
||||
lines.push('');
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
lines.push('# Recent Activity');
|
||||
lines.push('');
|
||||
```
|
||||
|
||||
**Before (lines 245-248):**
|
||||
```typescript
|
||||
if (observations.length === 0) {
|
||||
lines.push('*No recent activity*');
|
||||
return lines.join('\n');
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
if (observations.length === 0) {
|
||||
return '';
|
||||
}
|
||||
```
|
||||
|
||||
### Task 3.2: Update regenerateFolder to handle empty formatted content
|
||||
**File:** `scripts/regenerate-claude-md.ts`
|
||||
**Lines:** 432-459
|
||||
|
||||
The script already skips folders with no observations (lines 443-444), so this change is already compatible. The `formatObservationsForClaudeMd` returning empty string doesn't change behavior since observations are checked before calling it.
|
||||
|
||||
### Verification
|
||||
- Run `bun scripts/regenerate-claude-md.ts --dry-run` in the project
|
||||
- Should NOT show any folders with 0 observations
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Update Tests
|
||||
|
||||
### Task 4.1: Update tests for new empty behavior
|
||||
**File:** `tests/utils/claude-md-utils.test.ts`
|
||||
**Lines:** 96-109
|
||||
|
||||
**Changes:**
|
||||
Update the two tests that expect "No recent activity" to expect empty string instead.
|
||||
|
||||
**Before (lines 96-101):**
|
||||
```typescript
|
||||
it('should return "No recent activity" for empty input', () => {
|
||||
const result = formatTimelineForClaudeMd('');
|
||||
|
||||
expect(result).toContain('# Recent Activity');
|
||||
expect(result).toContain('*No recent activity*');
|
||||
});
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
it('should return empty string for empty input', () => {
|
||||
const result = formatTimelineForClaudeMd('');
|
||||
|
||||
expect(result).toBe('');
|
||||
});
|
||||
```
|
||||
|
||||
**Before (lines 103-109):**
|
||||
```typescript
|
||||
it('should return "No recent activity" when no table rows exist', () => {
|
||||
const input = 'Just some plain text without table rows';
|
||||
|
||||
const result = formatTimelineForClaudeMd(input);
|
||||
|
||||
expect(result).toContain('*No recent activity*');
|
||||
});
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
it('should return empty string when no table rows exist', () => {
|
||||
const input = 'Just some plain text without table rows';
|
||||
|
||||
const result = formatTimelineForClaudeMd(input);
|
||||
|
||||
expect(result).toBe('');
|
||||
});
|
||||
```
|
||||
|
||||
### Task 4.2: Remove HTML comment assertions from any other tests
|
||||
Search for tests that assert on "auto-generated" comment and update accordingly.
|
||||
|
||||
### Verification
|
||||
- Run full test suite: `bun test`
|
||||
- All tests should pass
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Cleanup Existing Empty Files
|
||||
|
||||
### Task 5.1: Run cleanup to remove existing empty CLAUDE.md files
|
||||
**Command:**
|
||||
```bash
|
||||
bun scripts/regenerate-claude-md.ts --clean
|
||||
```
|
||||
|
||||
This will:
|
||||
- Find all CLAUDE.md files with `<claude-mem-context>` tags
|
||||
- Strip the tagged section
|
||||
- Delete files that become empty after stripping
|
||||
- Preserve files that have user content outside the tags
|
||||
|
||||
### Verification
|
||||
- `grep -r "No recent activity" . --include="CLAUDE.md"` should return no results
|
||||
- `grep -r "auto-generated by claude-mem" . --include="CLAUDE.md"` should return no results
|
||||
|
||||
---
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `src/utils/claude-md-utils.ts:143` | Remove HTML comment line |
|
||||
| `src/utils/claude-md-utils.ts:209-211` | Return empty string instead of "No recent activity" |
|
||||
| `src/utils/claude-md-utils.ts:322` | Skip writing if formatted content is empty |
|
||||
| `scripts/regenerate-claude-md.ts:242` | Remove HTML comment line |
|
||||
| `scripts/regenerate-claude-md.ts:245-247` | Return empty string instead of "No recent activity" |
|
||||
| `tests/utils/claude-md-utils.test.ts:96-109` | Update tests to expect empty string |
|
||||
|
||||
## Final Verification Checklist
|
||||
- [ ] `bun test` passes
|
||||
- [ ] No "No recent activity" CLAUDE.md files exist
|
||||
- [ ] No "auto-generated" comments in CLAUDE.md files
|
||||
- [ ] Build succeeds: `npm run build-and-sync`
|
||||
- [ ] New observations correctly generate CLAUDE.md files with content
|
||||
- [ ] Folders without observations get no CLAUDE.md file created
|
||||
@@ -0,0 +1,356 @@
|
||||
# Execution Plan: Intentional Patterns Validation Actions
|
||||
|
||||
**Created:** 2026-01-13
|
||||
**Source:** `docs/reports/intentional-patterns-validation.md`
|
||||
**Target:** `src/services/worker-service.ts` and related files
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Documentation Discovery (COMPLETED)
|
||||
|
||||
### Evidence Gathered
|
||||
|
||||
**Files Analyzed:**
|
||||
- `docs/reports/intentional-patterns-validation.md` - Pattern verdicts and recommendations
|
||||
- `docs/reports/nonsense-logic.md` - Original 23 issues identified
|
||||
- `.claude/plans/cleanup-worker-service-nonsense-logic.md` - Existing cleanup plan
|
||||
- `src/services/worker-service.ts` (813 lines) - Current state
|
||||
|
||||
**Current State:**
|
||||
- File has been reduced from 1445 lines to 813 lines in prior refactoring
|
||||
- `runInteractiveSetup` still exists at line 439 (~200 lines of dead code)
|
||||
- Re-export at line 78: `export { updateCursorContextForProject };`
|
||||
- MCP version hardcoded "1.0.0" at line 159
|
||||
- Fallback agents set at lines 144-146 without verification
|
||||
- Unused imports: `fs`, `spawn`, `homedir`, `readline` at lines 13-17
|
||||
|
||||
**Allowed APIs (from validation report):**
|
||||
- Exit code 0 pattern: **KEEP** (documented Windows Terminal workaround)
|
||||
- `as Error` casts: **KEEP** (documented project policy)
|
||||
- Dual init tracking: **KEEP** (serves async + sync callers)
|
||||
- Signal handler ref pattern: **KEEP** (standard JS mutable state sharing)
|
||||
- Empty MCP capabilities: **KEEP** (correct per MCP spec)
|
||||
|
||||
**Actions Required:**
|
||||
| Pattern | Action | Priority |
|
||||
|---------|--------|----------|
|
||||
| Re-export for circular import | Remove (no actual circular dep) | LOW |
|
||||
| Fallback agent without check | Add availability verification | HIGH |
|
||||
| MCP version hardcoded | Update to use package.json | LOW |
|
||||
| Dead code `runInteractiveSetup` | Delete (~200 lines) | HIGH |
|
||||
| Unused imports | Delete | LOW |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Delete Dead Code (HIGH PRIORITY)
|
||||
|
||||
### 1.1 Delete `runInteractiveSetup` Function
|
||||
|
||||
**What:** Delete lines 435-639 (approximately 200 lines)
|
||||
**File:** `src/services/worker-service.ts`
|
||||
|
||||
**Location confirmed:** Line 439 starts `async function runInteractiveSetup(): Promise<number>`
|
||||
|
||||
**Steps:**
|
||||
1. Read worker-service.ts lines 435-650 to find exact boundaries
|
||||
2. Delete the section comment and entire function
|
||||
3. Run build to verify no compile errors
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
grep -n "runInteractiveSetup" src/services/worker-service.ts
|
||||
# Expected: No output (function deleted)
|
||||
npm run build
|
||||
# Expected: No errors
|
||||
```
|
||||
|
||||
### 1.2 Remove Unused Imports
|
||||
|
||||
**What:** Delete imports only used by dead code
|
||||
**Lines to delete:** 13-17 (check each)
|
||||
|
||||
**Current imports to remove:**
|
||||
```typescript
|
||||
import * as fs from 'fs'; // Line 13 - UNUSED (namespace never accessed)
|
||||
import { spawn } from 'child_process'; // Line 14 - UNUSED (MCP uses StdioClientTransport)
|
||||
import { homedir } from 'os'; // Line 15 - Only in dead code
|
||||
import * as readline from 'readline'; // Line 17 - Only in dead code
|
||||
```
|
||||
|
||||
**Keep:**
|
||||
```typescript
|
||||
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs'; // Line 16 - CHECK
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. After deleting `runInteractiveSetup`, grep each import
|
||||
2. Delete any with zero usages
|
||||
3. Run build to verify
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
grep -n "^import \* as fs" src/services/worker-service.ts
|
||||
grep -n "import { spawn }" src/services/worker-service.ts
|
||||
# Expected: No output
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 1.3 Remove Unused CursorHooksInstaller Imports
|
||||
|
||||
**After deleting dead code, check:**
|
||||
```typescript
|
||||
import {
|
||||
updateCursorContextForProject, // KEEP (re-exported)
|
||||
handleCursorCommand, // KEEP (used in main)
|
||||
detectClaudeCode, // DELETE (only in dead code)
|
||||
findCursorHooksDir, // DELETE (only in dead code)
|
||||
installCursorHooks, // DELETE (only in dead code)
|
||||
configureCursorMcp // DELETE (only in dead code)
|
||||
} from './integrations/CursorHooksInstaller.js';
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
grep "detectClaudeCode\|findCursorHooksDir\|installCursorHooks\|configureCursorMcp" src/services/worker-service.ts
|
||||
# Expected: Only import line (which gets trimmed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Fix Fallback Agent Oversight (HIGH PRIORITY)
|
||||
|
||||
### 2.1 Add SDKAgent Availability Check
|
||||
|
||||
**Problem:** Lines 144-146 set Claude SDK as fallback without verifying it's configured
|
||||
```typescript
|
||||
this.geminiAgent.setFallbackAgent(this.sdkAgent);
|
||||
this.openRouterAgent.setFallbackAgent(this.sdkAgent);
|
||||
```
|
||||
|
||||
**Risk:** User chooses Gemini because they lack Claude credentials → transient Gemini error → fallback to Claude SDK → cascading failure
|
||||
|
||||
**Solution Options:**
|
||||
|
||||
**Option A: Add isConfigured() method to SDKAgent**
|
||||
1. Add method to SDKAgent that checks for valid Claude SDK credentials
|
||||
2. Only set fallback if `sdkAgent.isConfigured()` returns true
|
||||
3. Log warning when fallback unavailable
|
||||
|
||||
**Pattern to follow (from SDKAgent.ts constructor):**
|
||||
```typescript
|
||||
// Check if Claude SDK can be initialized
|
||||
public isConfigured(): boolean {
|
||||
// Claude SDK uses subprocess, check if claude command exists
|
||||
try {
|
||||
// Check for ANTHROPIC_API_KEY or claude CLI availability
|
||||
return !!process.env.ANTHROPIC_API_KEY || this.checkClaudeCliAvailable();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Option B: Document limitation (minimal fix)**
|
||||
Add comment explaining the risk:
|
||||
```typescript
|
||||
// NOTE: Fallback to Claude SDK may fail if user lacks Claude credentials
|
||||
// Consider adding availability check in future (Issue #XXX)
|
||||
this.geminiAgent.setFallbackAgent(this.sdkAgent);
|
||||
```
|
||||
|
||||
**Recommended: Option A**
|
||||
|
||||
**Steps:**
|
||||
1. Read SDKAgent.ts to understand initialization pattern
|
||||
2. Add `isConfigured()` method that checks Claude CLI/credentials
|
||||
3. Update worker-service.ts to conditionally set fallback
|
||||
4. Add warning log when fallback unavailable
|
||||
5. Run tests
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
grep -n "isConfigured" src/services/worker/SDKAgent.ts
|
||||
# Expected: Method definition
|
||||
grep -n "setFallbackAgent" src/services/worker-service.ts
|
||||
# Expected: Conditional calls with isConfigured check
|
||||
npm test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Remove Unnecessary Re-Export (LOW PRIORITY)
|
||||
|
||||
### 3.1 Fix Misleading Re-Export
|
||||
|
||||
**Current (worker-service.ts:77-78):**
|
||||
```typescript
|
||||
// Re-export updateCursorContextForProject for SDK agents
|
||||
export { updateCursorContextForProject };
|
||||
```
|
||||
|
||||
**Issue:** Comment implies avoiding circular import, but investigation found NO circular dependency exists.
|
||||
|
||||
**Import chain:**
|
||||
```
|
||||
CursorHooksInstaller.ts (defines) → worker-service.ts (imports, re-exports) → ResponseProcessor.ts (imports)
|
||||
```
|
||||
|
||||
**ResponseProcessor.ts could import directly from CursorHooksInstaller.ts**
|
||||
|
||||
**Options:**
|
||||
1. **Remove re-export entirely** - Update ResponseProcessor.ts to import from CursorHooksInstaller directly
|
||||
2. **Fix comment** - Update to reflect actual reason (API surface simplification)
|
||||
|
||||
**Recommended: Option 1 (cleaner)**
|
||||
|
||||
**Steps:**
|
||||
1. Update `src/services/worker/agents/ResponseProcessor.ts`:
|
||||
- Change: `import { updateCursorContextForProject } from '../../worker-service.js';`
|
||||
- To: `import { updateCursorContextForProject } from '../../integrations/CursorHooksInstaller.js';`
|
||||
2. Delete re-export from worker-service.ts (lines 77-78)
|
||||
3. Run build to verify
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
grep -n "export { updateCursorContextForProject" src/services/worker-service.ts
|
||||
# Expected: No output
|
||||
grep -n "updateCursorContextForProject" src/services/worker/agents/ResponseProcessor.ts
|
||||
# Expected: Import from CursorHooksInstaller
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Update MCP Version (LOW PRIORITY)
|
||||
|
||||
### 4.1 Use Package Version for MCP Client
|
||||
|
||||
**Current (worker-service.ts:157-160):**
|
||||
```typescript
|
||||
this.mcpClient = new Client({
|
||||
name: 'worker-search-proxy',
|
||||
version: '1.0.0' // Hardcoded, should match package.json (9.0.4)
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**Also affects (from report):**
|
||||
- `src/services/sync/ChromaSync.ts:126-131`
|
||||
- MCP server (separate file)
|
||||
|
||||
**Pattern to follow:**
|
||||
```typescript
|
||||
import { version } from '../../package.json' assert { type: 'json' };
|
||||
|
||||
this.mcpClient = new Client({
|
||||
name: 'worker-search-proxy',
|
||||
version: version
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**Alternative (if JSON import not supported):**
|
||||
```typescript
|
||||
import { readFileSync } from 'fs';
|
||||
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'));
|
||||
|
||||
this.mcpClient = new Client({
|
||||
name: 'worker-search-proxy',
|
||||
version: pkg.version
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Check if JSON import assertion works in project
|
||||
2. Update worker-service.ts MCP client initialization
|
||||
3. Update ChromaSync.ts similarly
|
||||
4. Run build to verify
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
grep -n "version: '1.0.0'" src/services/worker-service.ts src/services/sync/ChromaSync.ts
|
||||
# Expected: No output
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 4.2 Add MCP Capabilities Comment
|
||||
|
||||
**Current:**
|
||||
```typescript
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**Add clarifying comment:**
|
||||
```typescript
|
||||
}, {
|
||||
// MCP spec: Clients accept all server capabilities; no declaration needed
|
||||
capabilities: {}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Verification
|
||||
|
||||
### 5.1 Build Check
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
**Expected:** No TypeScript errors
|
||||
|
||||
### 5.2 Test Suite
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
**Expected:** All tests pass
|
||||
|
||||
### 5.3 Grep for Anti-Patterns
|
||||
```bash
|
||||
# Verify dead code removed
|
||||
grep -r "runInteractiveSetup" src/
|
||||
# Expected: No matches
|
||||
|
||||
# Verify unused imports removed
|
||||
grep "import \* as fs from 'fs'" src/services/worker-service.ts
|
||||
# Expected: No match
|
||||
|
||||
# Verify re-export removed
|
||||
grep "export { updateCursorContextForProject" src/services/worker-service.ts
|
||||
# Expected: No match
|
||||
|
||||
# Verify fallback has check
|
||||
grep -A2 "setFallbackAgent" src/services/worker-service.ts
|
||||
# Expected: Conditional with isConfigured check
|
||||
```
|
||||
|
||||
### 5.4 Runtime Check
|
||||
```bash
|
||||
npm run build-and-sync
|
||||
# Manually verify worker starts and basic operations work
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Phase | Description | Lines Changed | Priority |
|
||||
|-------|-------------|---------------|----------|
|
||||
| Phase 1 | Delete dead code + imports | ~200 deleted | HIGH |
|
||||
| Phase 2 | Add fallback verification | ~10 added | HIGH |
|
||||
| Phase 3 | Remove re-export | ~5 changed | LOW |
|
||||
| Phase 4 | Update MCP version | ~3 changed | LOW |
|
||||
| Phase 5 | Verification | N/A | N/A |
|
||||
|
||||
**Execution Order:** Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5
|
||||
|
||||
**Note:** Each phase should be followed by verification (build + test) before proceeding.
|
||||
|
||||
---
|
||||
|
||||
## Patterns Confirmed KEEP (No Action)
|
||||
|
||||
These patterns were validated as intentional:
|
||||
|
||||
1. **Exit code 0 always** - Windows Terminal tab accumulation workaround (commit 222a73da)
|
||||
2. **`as Error` casts** - Documented project policy with anti-pattern detection
|
||||
3. **Dual init tracking** - Promise for async, flag for sync callers
|
||||
4. **Signal handler ref pattern** - Standard JS mutable state sharing
|
||||
5. **Empty MCP capabilities** - Correct per MCP client spec
|
||||
@@ -0,0 +1,394 @@
|
||||
# Plan: Remove Worker Start Calls - In-Process Architecture
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Current architecture has problematic spawn patterns:
|
||||
1. `hooks.json` calls `worker-service.cjs start` which spawns a daemon
|
||||
2. Spawning is buggy on Windows - **HARD RULE: NO SPAWN**
|
||||
3. `user-message` hook is deprecated
|
||||
4. `smart-install` was supposed to chain: `smart-install && stop && context`
|
||||
|
||||
## Target Architecture
|
||||
|
||||
**NO SPAWN - Worker runs in-process within hook command**
|
||||
|
||||
```
|
||||
SessionStart:
|
||||
smart-install && stop && context
|
||||
```
|
||||
|
||||
Flow:
|
||||
1. `smart-install` - Install dependencies if needed
|
||||
2. `stop` - Kill any existing worker (clean slate)
|
||||
3. `context` - Hook starts worker IN-PROCESS, becomes the worker
|
||||
|
||||
**Key insight:** The first hook that needs the worker **becomes** the worker. No spawn, no daemon. The hook process IS the worker process.
|
||||
|
||||
---
|
||||
|
||||
## Current vs Target hooks.json
|
||||
|
||||
### Current (BROKEN)
|
||||
```json
|
||||
"SessionStart": [
|
||||
{ "hooks": [
|
||||
{ "command": "node smart-install.js" },
|
||||
{ "command": "bun worker-service.cjs start" }, // REMOVE - spawn
|
||||
{ "command": "bun worker-service.cjs hook ... context" },
|
||||
{ "command": "bun worker-service.cjs hook ... user-message" } // REMOVE - deprecated
|
||||
]}
|
||||
]
|
||||
```
|
||||
|
||||
### Target
|
||||
```json
|
||||
"SessionStart": [
|
||||
{ "hooks": [
|
||||
{ "command": "node smart-install.js && bun worker-service.cjs stop && bun worker-service.cjs hook claude-code context" }
|
||||
]}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files Involved
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `plugin/hooks/hooks.json` | Restructure to chained commands, remove start/user-message |
|
||||
| `src/services/worker-service.ts` | `hook` case: start worker in-process if not running |
|
||||
| `src/cli/handlers/*.ts` | May need adjustment for in-process execution |
|
||||
| `src/shared/worker-utils.ts` | `ensureWorkerRunning()` → adapt for in-process |
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Documentation Discovery
|
||||
|
||||
### Available APIs
|
||||
|
||||
**From `src/services/infrastructure/HealthMonitor.ts`:**
|
||||
- `isPortInUse(port): Promise<boolean>`
|
||||
- `waitForHealth(port, timeoutMs): Promise<boolean>`
|
||||
- `httpShutdown(port): Promise<void>`
|
||||
|
||||
**From `src/services/worker-service.ts`:**
|
||||
- `WorkerService` class - the actual worker
|
||||
- `stop` command - shuts down worker via HTTP
|
||||
- `--daemon` case - starts WorkerService (currently only used after spawn)
|
||||
|
||||
**BANNED (spawn patterns):**
|
||||
- ~~`spawnDaemon()`~~ - NO SPAWN
|
||||
- ~~`fork()`~~ - NO SPAWN
|
||||
- ~~`spawn()` with detached~~ - NO SPAWN
|
||||
|
||||
### Anti-Patterns
|
||||
- **NO SPAWN** - Hard rule, Windows buggy
|
||||
- No `restart` command - removed for same reason
|
||||
- No detached processes
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Modify `hook` Case for In-Process Worker
|
||||
|
||||
### Location
|
||||
`src/services/worker-service.ts:564-576`
|
||||
|
||||
### Current Code
|
||||
```typescript
|
||||
case 'hook': {
|
||||
const platform = process.argv[3];
|
||||
const event = process.argv[4];
|
||||
if (!platform || !event) {
|
||||
console.error('Usage: claude-mem hook <platform> <event>');
|
||||
process.exit(1);
|
||||
}
|
||||
const { hookCommand } = await import('../cli/hook-command.js');
|
||||
await hookCommand(platform, event);
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Target Code
|
||||
```typescript
|
||||
case 'hook': {
|
||||
const platform = process.argv[3];
|
||||
const event = process.argv[4];
|
||||
if (!platform || !event) {
|
||||
console.error('Usage: claude-mem hook <platform> <event>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if worker already running (port in use = valid, another process has it)
|
||||
const portInUse = await isPortInUse(port);
|
||||
if (portInUse) {
|
||||
// Port in use - either healthy worker or something else
|
||||
// Proceed with hook via HTTP to existing worker
|
||||
const { hookCommand } = await import('../cli/hook-command.js');
|
||||
await hookCommand(platform, event);
|
||||
break;
|
||||
}
|
||||
|
||||
// Port free - start worker IN THIS PROCESS (no spawn!)
|
||||
logger.info('SYSTEM', 'Starting worker in-process for hook');
|
||||
const worker = new WorkerService();
|
||||
|
||||
// Start worker (non-blocking, returns when server listening)
|
||||
await worker.start();
|
||||
|
||||
// Now execute hook logic - worker is running in this process
|
||||
// Can call handler directly (in-process) or via HTTP to self
|
||||
const { hookCommand } = await import('../cli/hook-command.js');
|
||||
await hookCommand(platform, event);
|
||||
|
||||
// DON'T exit - this process IS the worker now
|
||||
// Worker stays alive serving requests
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Behavior
|
||||
- If port in use → hook runs via HTTP to existing worker, then exits
|
||||
- If port free → start worker in-process, run hook, process stays alive as worker
|
||||
|
||||
### Verification
|
||||
- [ ] Stop worker, run hook command → should start worker and stay alive
|
||||
- [ ] Worker already running, run hook command → should complete and exit
|
||||
- [ ] `lsof -i :37777` shows hook process IS the worker
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Update hooks.json - Chained Commands
|
||||
|
||||
### Location
|
||||
`plugin/hooks/hooks.json`
|
||||
|
||||
### Target Structure
|
||||
```json
|
||||
{
|
||||
"description": "Claude-mem memory system hooks",
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "startup|clear|compact",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/smart-install.js\" && bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" stop && bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code context",
|
||||
"timeout": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-init",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code observation",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code summarize",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Summary
|
||||
1. SessionStart: Chain `smart-install && stop && context` in single command
|
||||
2. Remove `user-message` hook (deprecated)
|
||||
3. Remove all separate `start` commands
|
||||
4. Other hooks unchanged (just hook command, auto-starts if needed)
|
||||
|
||||
### Verification
|
||||
- [ ] JSON valid: `cat plugin/hooks/hooks.json | jq .`
|
||||
- [ ] No `start` command: `grep -c '"start"' plugin/hooks/hooks.json` = 0
|
||||
- [ ] No `user-message`: `grep -c 'user-message' plugin/hooks/hooks.json` = 0
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Handle "Port In Use" Gracefully
|
||||
|
||||
### Scenario
|
||||
Another process has port 37777 (not our worker). Hook should handle gracefully.
|
||||
|
||||
### Current Behavior
|
||||
`ensureWorkerRunning()` polls for 15 seconds, then throws error.
|
||||
|
||||
### Target Behavior
|
||||
If port in use but not healthy (not our worker):
|
||||
- Hook is "valid" - don't block Claude Code
|
||||
- Return graceful response (empty context, etc.)
|
||||
- Log warning for debugging
|
||||
|
||||
### Location
|
||||
`src/shared/worker-utils.ts:117-141`
|
||||
|
||||
### Changes
|
||||
```typescript
|
||||
export async function ensureWorkerRunning(): Promise<boolean> {
|
||||
const port = getWorkerPort();
|
||||
|
||||
// Quick health check (2 seconds max)
|
||||
try {
|
||||
if (await isWorkerHealthy()) {
|
||||
await checkWorkerVersion();
|
||||
return true; // Worker healthy
|
||||
}
|
||||
} catch (e) {
|
||||
// Not healthy
|
||||
}
|
||||
|
||||
// Port might be in use by something else
|
||||
// Return false but don't throw - let caller decide
|
||||
logger.warn('SYSTEM', 'Worker not healthy, hook will proceed gracefully');
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### Handler Updates
|
||||
Update handlers to handle `ensureWorkerRunning()` returning false:
|
||||
```typescript
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
// Return graceful empty response
|
||||
return { output: '', exitCode: HOOK_EXIT_CODES.SUCCESS };
|
||||
}
|
||||
```
|
||||
|
||||
### Verification
|
||||
- [ ] Start non-worker process on 37777, run hook → completes gracefully
|
||||
- [ ] No 15-second hang when port blocked
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Remove Deprecated Code
|
||||
|
||||
### Remove `user-message` Handler (if unused elsewhere)
|
||||
- [ ] Check if `user-message.ts` is used anywhere else
|
||||
- [ ] Remove from `src/cli/handlers/index.ts` if safe
|
||||
- [ ] Consider keeping file but removing from hooks.json only
|
||||
|
||||
### Remove `start` Command (optional)
|
||||
The `start` command in worker-service.ts can stay for manual use:
|
||||
```bash
|
||||
bun worker-service.cjs start # Manual start if needed
|
||||
```
|
||||
But it should NOT be called from hooks.json.
|
||||
|
||||
### Verification
|
||||
- [ ] `npm run build` succeeds
|
||||
- [ ] No references to removed handlers in hooks.json
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Update Handler `ensureWorkerRunning()` Calls
|
||||
|
||||
### Context
|
||||
Each handler currently calls `ensureWorkerRunning()` which polls for 15 seconds.
|
||||
|
||||
With in-process architecture:
|
||||
- If hook started worker in-process → worker is THIS process, no HTTP needed
|
||||
- If worker already running → HTTP to existing worker
|
||||
|
||||
### Decision
|
||||
**Keep handler calls** but modify `ensureWorkerRunning()` to:
|
||||
1. Return quickly if port is in use (assume valid)
|
||||
2. Return true if in-process worker (detect via global flag?)
|
||||
3. Graceful false return instead of throwing
|
||||
|
||||
### Files
|
||||
- `src/cli/handlers/context.ts:15`
|
||||
- `src/cli/handlers/session-init.ts:15`
|
||||
- `src/cli/handlers/observation.ts:14`
|
||||
- `src/cli/handlers/summarize.ts:17`
|
||||
- `src/cli/handlers/file-edit.ts:15`
|
||||
|
||||
### Verification
|
||||
- [ ] Handlers don't hang on port-in-use scenarios
|
||||
- [ ] In-process worker scenario works
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Final Verification
|
||||
|
||||
### Tests
|
||||
- [ ] `bun test` - All tests pass
|
||||
- [ ] `npm run build-and-sync` - Build succeeds
|
||||
|
||||
### Manual Tests
|
||||
|
||||
**Test 1: Clean Start**
|
||||
```bash
|
||||
bun plugin/scripts/worker-service.cjs stop
|
||||
# Start new Claude Code session
|
||||
# Verify: context hook starts worker in-process
|
||||
# Verify: lsof -i :37777 shows the hook process
|
||||
```
|
||||
|
||||
**Test 2: Worker Already Running**
|
||||
```bash
|
||||
bun plugin/scripts/worker-service.cjs stop
|
||||
bun plugin/scripts/worker-service.cjs hook claude-code context &
|
||||
# Wait for worker to start
|
||||
bun plugin/scripts/worker-service.cjs hook claude-code observation
|
||||
# Verify: observation hook exits after completing (doesn't stay alive)
|
||||
```
|
||||
|
||||
**Test 3: Port Blocked**
|
||||
```bash
|
||||
bun plugin/scripts/worker-service.cjs stop
|
||||
nc -l 37777 & # Block port with netcat
|
||||
bun plugin/scripts/worker-service.cjs hook claude-code context
|
||||
# Verify: completes gracefully, doesn't hang
|
||||
kill %1 # Clean up netcat
|
||||
```
|
||||
|
||||
**Test 4: Full Session**
|
||||
```bash
|
||||
# Start fresh Claude Code session
|
||||
# Do some work (creates observations)
|
||||
# End session (Ctrl+C or /exit)
|
||||
# Verify: summarize hook ran, observations saved
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Hook stays alive forever | Expected - it's the worker now |
|
||||
| Multiple hooks compete for port | First one wins, others use HTTP |
|
||||
| Graceful shutdown on session end | Stop command in chain handles this |
|
||||
| Windows compatibility | No spawn = no Windows issues |
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise:
|
||||
1. Restore hooks.json with separate start commands
|
||||
2. Revert worker-service.ts hook case changes
|
||||
3. No database changes to rollback
|
||||
@@ -1,22 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38082 | 10:13 PM | ✅ | Merge Conflict Resolution - Kept Feature Branch Versions | ~431 |
|
||||
|
||||
**test-audit-2026-01-05.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37776 | 6:35 PM | 🔵 | Test Audit Reveals Quality Issues and Architecture Recommendations | ~372 |
|
||||
| #37775 | " | 🔵 | Test Audit Identifies Zero Coverage for Logger FormatTool Tests | ~280 |
|
||||
| #37747 | 6:20 PM | 🔵 | Comprehensive Test Suite Audit Completed: 41 Files Analyzed | ~664 |
|
||||
| #37736 | 6:16 PM | 🔵 | Test Suite Audit Reveals Critical Test Failure Root Cause | ~660 |
|
||||
| #37735 | " | ✅ | Test Suite Audit Report Generated: 41 Tests Scored and Analyzed | ~634 |
|
||||
| #37732 | 6:15 PM | 🔵 | Test Quality Audit Completed: Identified Critical Mock Pollution Issue | ~490 |
|
||||
</claude-mem-context>
|
||||
@@ -26,49 +26,4 @@ Manages semantic versioning for the claude-mem project itself. Handles updating
|
||||
## Adding New Skills
|
||||
|
||||
**For claude-mem development** → Add to `.claude/skills/`
|
||||
**For end users** → Add to `plugin/skills/` (gets distributed with plugin)
|
||||
|
||||
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 9, 2025
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #5901 | 6:54 PM | ✅ | Project Skills Documentation Created | ~317 |
|
||||
|
||||
### Dec 13, 2025
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24725 | 4:07 PM | 🔵 | Claude Skills Infrastructure for Automation | ~220 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #26354 | 9:20 PM | 🔵 | PR #317 Second CLAUDE.md Compliance Review Confirms No Violations | ~442 |
|
||||
| #26353 | " | 🔵 | PR #317 CLAUDE.md Compliance Review Completed | ~402 |
|
||||
| #26193 | 8:15 PM | 🔵 | PR spans 21 files with net addition of 374 lines across codebase | ~375 |
|
||||
| #26173 | 8:08 PM | ✅ | Updated Skills CLAUDE.md Documentation for Version Bump | ~277 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33311 | 3:09 PM | ✅ | Version 8.2.3 Release Deployed with Worker Stability Improvements | ~434 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38082 | 10:13 PM | ✅ | Merge Conflict Resolution - Kept Feature Branch Versions | ~431 |
|
||||
</claude-mem-context>
|
||||
**For end users** → Add to `plugin/skills/` (gets distributed with plugin)
|
||||
@@ -1,21 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 13, 2025
|
||||
|
||||
**feature_request.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25012 | 6:41 PM | 🟣 | Auto-Convert Feature Requests to GitHub Discussions | ~298 |
|
||||
| #25011 | " | ✅ | Staged GitHub Feature Request Automation Files | ~206 |
|
||||
| #25009 | 6:40 PM | ✅ | Feature Request Template Auto-Labeling Configured | ~241 |
|
||||
| #24995 | 6:26 PM | 🔵 | Standard Feature Request Template Configuration | ~260 |
|
||||
|
||||
**bug_report.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24994 | 6:26 PM | 🔵 | Standard Bug Report Template Configuration | ~258 |
|
||||
| #24992 | " | 🔵 | GitHub Issue Templates Located | ~188 |
|
||||
</claude-mem-context>
|
||||
@@ -7,6 +7,12 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Before submitting
|
||||
|
||||
- [ ] I searched [existing issues](https://github.com/thedotmack/claude-mem/issues) and confirmed this is not a duplicate
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Quick Bug Report (Recommended)
|
||||
|
||||
**Use the automated bug report generator** for comprehensive diagnostics:
|
||||
|
||||
@@ -7,6 +7,12 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Before submitting
|
||||
|
||||
- [ ] I searched [existing issues](https://github.com/thedotmack/claude-mem/issues) and confirmed this is not a duplicate
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
@@ -1,82 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 13, 2025
|
||||
|
||||
**convert-feature-requests.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25022 | 6:48 PM | ✅ | Workflow Fix Committed to Repository | ~289 |
|
||||
| #25021 | " | 🔴 | Fixed Issue Number Reference in Workflow Steps | ~277 |
|
||||
| #25020 | " | 🔴 | Workflow String Interpolation Fixed by Consolidating Steps | ~339 |
|
||||
| #25019 | 6:47 PM | 🔵 | GitHub Workflow Automates Feature Request Triage | ~328 |
|
||||
| #25012 | 6:41 PM | 🟣 | Auto-Convert Feature Requests to GitHub Discussions | ~298 |
|
||||
| #25011 | " | ✅ | Staged GitHub Feature Request Automation Files | ~206 |
|
||||
| #25010 | 6:40 PM | 🟣 | GitHub Action Workflow for Feature Request Auto-Conversion | ~414 |
|
||||
|
||||
**summary.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25002 | 6:38 PM | 🔵 | AI Summary Workflow for New Issues | ~239 |
|
||||
|
||||
**claude.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24997 | 6:27 PM | 🔵 | Claude Code Action Workflow for Issue and PR Comments | ~242 |
|
||||
| #24727 | 4:08 PM | 🔵 | GitHub Automation Baseline Assessment | ~312 |
|
||||
| #24722 | 4:06 PM | 🔵 | Existing Claude Workflow Trigger Configuration | ~233 |
|
||||
|
||||
**claude-code-review.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24996 | 6:27 PM | 🔵 | Existing GitHub Actions Workflows Identified | ~199 |
|
||||
| #24723 | 4:06 PM | 🔵 | Automated PR Review Workflow Pattern | ~268 |
|
||||
| #24720 | " | 🔵 | GitHub Workflows Inventory | ~142 |
|
||||
|
||||
### Dec 17, 2025
|
||||
|
||||
**issue-list-query**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28918 | 7:27 PM | 🔵 | Four open issues identified - MCP connection, Bun PATH, web UI path, and endless mode | ~432 |
|
||||
|
||||
**pr-list-query**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28917 | 7:27 PM | 🔵 | Recent PRs audit reveals comprehensive Windows stabilization and MCP fixes | ~414 |
|
||||
|
||||
**windows-ci.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28655 | 5:30 PM | ✅ | Windows CI Removal Committed to Repository | ~253 |
|
||||
| #28654 | " | ✅ | Windows CI Workflow File Removed | ~174 |
|
||||
| #28650 | 5:26 PM | ✅ | Committed Windows CI Workflow Simplification | ~213 |
|
||||
| #28649 | " | ✅ | Removed Build and Install Steps from Windows CI | ~278 |
|
||||
| #28648 | " | 🔵 | Windows CI Workflow Includes Build Step | ~288 |
|
||||
| #28644 | 5:24 PM | ✅ | Modified 27 files with 693 additions and 239 deletions for Windows support | ~447 |
|
||||
| #28625 | 5:19 PM | 🟣 | Windows CI Testing Workflow Deployed | ~303 |
|
||||
| #28624 | " | ✅ | Windows CI Workflow File Staged for Commit | ~163 |
|
||||
| #28623 | " | 🔵 | Windows CI Workflow File Present But Untracked | ~178 |
|
||||
| #28622 | 5:18 PM | 🟣 | Windows CI Pipeline with Worker Lifecycle Testing | ~326 |
|
||||
|
||||
### Dec 31, 2025
|
||||
|
||||
**claude-code-review.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34627 | 3:01 PM | 🔵 | Claude Code Review GitHub Action Provides Automated PR Review Integration | ~478 |
|
||||
|
||||
**claude.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34626 | 3:01 PM | 🔵 | Test-Driven Validation Agent Performing Extensive Infrastructure Analysis | ~501 |
|
||||
|
||||
### Jan 6, 2026
|
||||
|
||||
**windows-ci.yml**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38108 | 12:15 AM | 🔵 | Complete Windows Zombie Port Bug Technical Deep Dive | ~935 |
|
||||
</claude-mem-context>
|
||||
+5
-1
@@ -17,6 +17,7 @@ package-lock.json
|
||||
bun.lock
|
||||
private/
|
||||
datasets/
|
||||
Auto Run Docs/
|
||||
|
||||
# Generated UI files (built from viewer-template.html)
|
||||
src/ui/viewer.html
|
||||
@@ -30,4 +31,7 @@ src/ui/viewer.html
|
||||
|
||||
# Prevent other malformed path directories
|
||||
http*/
|
||||
https*/
|
||||
https*/
|
||||
|
||||
# Ignore WebStorm project files (for dinosaur IDE users)
|
||||
.idea/
|
||||
|
||||
+516
-299
@@ -2,6 +2,522 @@
|
||||
|
||||
All notable changes to claude-mem.
|
||||
|
||||
## [v10.0.1] - 2026-02-11
|
||||
|
||||
## What's Changed
|
||||
|
||||
### OpenClaw Observation Feed
|
||||
- Enabled SSE observation feed for OpenClaw agent sessions, allowing real-time streaming of observations to connected OpenClaw clients
|
||||
- Fixed `ObservationSSEPayload.project` type to be nullable, preventing type errors when project context is unavailable
|
||||
- Added `EnvManager` support for OpenClaw environment configuration
|
||||
|
||||
### Build Artifacts
|
||||
- Rebuilt worker service and MCP server with latest changes
|
||||
|
||||
## [v10.0.0] - 2026-02-11
|
||||
|
||||
## OpenClaw Plugin — Persistent Memory for OpenClaw Agents
|
||||
|
||||
Claude-mem now has an official [OpenClaw](https://openclaw.ai) plugin, bringing persistent memory to agents running on the OpenClaw gateway. This is a major milestone — claude-mem's memory system is no longer limited to Claude Code sessions.
|
||||
|
||||
### What It Does
|
||||
|
||||
The plugin bridges claude-mem's observation pipeline with OpenClaw's embedded runner (`pi-embedded`), which calls the Anthropic API directly without spawning a `claude` process. Three core capabilities:
|
||||
|
||||
1. **Observation Recording** — Captures every tool call from OpenClaw agents and sends it to the claude-mem worker for AI-powered compression and storage
|
||||
2. **MEMORY.md Live Sync** — Writes a continuously-updated memory timeline to each agent's workspace, so agents start every session with full context from previous work
|
||||
3. **Observation Feed** — Streams new observations to messaging channels (Telegram, Discord, Slack, Signal, WhatsApp, LINE) in real-time via SSE
|
||||
|
||||
### Quick Start
|
||||
|
||||
Add claude-mem to your OpenClaw gateway config:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"project": "my-project",
|
||||
"syncMemoryFile": true,
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "your-chat-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The claude-mem worker service must be running on the same machine (`localhost:37777`).
|
||||
|
||||
### Commands
|
||||
|
||||
- `/claude-mem-status` — Worker health check, active sessions, feed connection state
|
||||
- `/claude-mem-feed` — Show/toggle observation feed status
|
||||
- `/claude-mem-feed on|off` — Enable/disable feed
|
||||
|
||||
### How the Event Lifecycle Works
|
||||
|
||||
```
|
||||
OpenClaw Gateway
|
||||
├── session_start ──────────→ Init claude-mem session
|
||||
├── before_agent_start ─────→ Sync MEMORY.md + track workspace
|
||||
├── tool_result_persist ────→ Record observation + re-sync MEMORY.md
|
||||
├── agent_end ──────────────→ Summarize + complete session
|
||||
├── session_end ────────────→ Clean up session tracking
|
||||
└── gateway_start ──────────→ Reset all tracking
|
||||
```
|
||||
|
||||
All observation recording and MEMORY.md syncs are fire-and-forget — they never block the agent.
|
||||
|
||||
📖 Full documentation: [OpenClaw Integration Guide](https://docs.claude-mem.ai/docs/openclaw-integration)
|
||||
|
||||
---
|
||||
|
||||
## Windows Platform Improvements
|
||||
|
||||
- **ProcessManager**: Migrated daemon spawning from deprecated WMIC to PowerShell `Start-Process` with `-WindowStyle Hidden`
|
||||
- **ChromaSync**: Re-enabled vector search on Windows (was previously disabled entirely)
|
||||
- **Worker Service**: Added unified DB-ready gate middleware — all DB-dependent endpoints now wait for initialization instead of returning "Database not initialized" errors
|
||||
- **EnvManager**: Switched from fragile allowlist to simple blocklist for subprocess env vars (only strips `ANTHROPIC_API_KEY` per Issue #733)
|
||||
|
||||
## Session Management Fixes
|
||||
|
||||
- Fixed unbounded session tracking map growth — maps are now cleaned up on `session_end`
|
||||
- Session init moved to `session_start` and `after_compaction` hooks for correct lifecycle handling
|
||||
|
||||
## SSE Fixes
|
||||
|
||||
- Fixed stream URL consistency across the codebase
|
||||
- Fixed multi-line SSE data frame parsing (concatenates `data:` lines per SSE spec)
|
||||
|
||||
## Issue Triage
|
||||
|
||||
Closed 37+ duplicate/stale/invalid issues across multiple triage phases, significantly cleaning up the issue tracker.
|
||||
|
||||
## [v9.1.1] - 2026-02-07
|
||||
|
||||
## Critical Bug Fix: Worker Initialization Failure
|
||||
|
||||
**v9.1.0 was unable to initialize its database on existing installations.** This patch fixes the root cause and several related issues.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Fix FOREIGN KEY constraint failure during migration** — The `addOnUpdateCascadeToForeignKeys` migration (schema v21) crashed when orphaned observations existed (observations whose `memory_session_id` has no matching row in `sdk_sessions`). Fixed by disabling FK checks (`PRAGMA foreign_keys = OFF`) during table recreation, following SQLite's recommended migration pattern.
|
||||
|
||||
- **Remove hardcoded CHECK constraints on observation type column** — Multiple locations enforced `CHECK(type IN ('decision', 'bugfix', ...))` but the mode system (v8.0.0+) allows custom observation types, causing constraint violations. Removed all 5 occurrences across `SessionStore.ts`, `migrations.ts`, and `migrations/runner.ts`.
|
||||
|
||||
- **Fix Express middleware ordering for initialization guard** — The `/api/*` guard middleware that waits for DB initialization was registered AFTER routes, so Express matched routes before the guard. Moved guard middleware registration BEFORE route registrations. Added dedicated early handler for `/api/context/inject` to fail-open during init.
|
||||
|
||||
### New
|
||||
|
||||
- **Restored mem-search skill** — Recreated `plugin/skills/mem-search/SKILL.md` with the 3-layer workflow (search → timeline → batch fetch) updated for the current MCP tool set.
|
||||
|
||||
## [v9.1.0] - 2026-02-07
|
||||
|
||||
## v9.1.0 — The Great PR Triage
|
||||
|
||||
100 open PRs reviewed, triaged, and resolved. 157 commits, 123 files changed, +6,104/-721 lines. This release focuses on stability, security, and community contributions.
|
||||
|
||||
### Highlights
|
||||
|
||||
- **100 PR triage**: Reviewed every open PR — merged 48, cherry-picked 13, closed 39 (stale/duplicate/YAGNI)
|
||||
- **Fail-open hook architecture**: Hooks no longer block Claude Code prompts when the worker is starting up
|
||||
- **DB initialization guard**: All API endpoints now wait for database initialization instead of crashing with "Database not initialized"
|
||||
- **Security hardening**: CORS restricted to localhost, XSS defense-in-depth via DOMPurify
|
||||
- **3 new features**: Manual memory save, project exclusion, folder exclude setting
|
||||
|
||||
---
|
||||
|
||||
### Security
|
||||
|
||||
- **CORS restricted to localhost** — Worker API no longer accepts cross-origin requests from arbitrary websites. Only localhost/127.0.0.1 origins allowed. (PR #917 by @Spunky84)
|
||||
- **XSS defense-in-depth** — Added DOMPurify sanitization to TerminalPreview.tsx viewer component (concept from PR #896)
|
||||
|
||||
### New Features
|
||||
|
||||
- **Manual memory storage** — New \`save_memory\` MCP tool and \`POST /api/memory/save\` endpoint for explicit memory capture (PR #662 by @darconada, closes #645)
|
||||
- **Project exclusion setting** — \`CLAUDE_MEM_EXCLUDED_PROJECTS\` glob patterns to exclude entire projects from tracking (PR #920 by @Spunky84)
|
||||
- **Folder exclude setting** — \`CLAUDE_MEM_FOLDER_MD_EXCLUDE\` JSON array to exclude paths from CLAUDE.md generation, fixing Xcode/drizzle build conflicts (PR #699 by @leepokai, closes #620)
|
||||
- **Folder CLAUDE.md opt-in** — \`CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED\` now defaults to \`false\` (opt-in) instead of always-on (PR #913 by @superbiche)
|
||||
- **Generate/clean CLI commands** — \`generate\` and \`clean\` commands for CLAUDE.md management with \`--dry-run\` support (PR #657 by @thedotmack)
|
||||
- **Ragtime email investigation** — Batch processor for email investigation workflows (PR #863 by @thedotmack)
|
||||
|
||||
### Hook Resilience (Fail-Open Architecture)
|
||||
|
||||
Hooks no longer block Claude Code when the worker is unavailable or slow:
|
||||
|
||||
- **Graceful hook failures** — Hooks exit 0 with empty responses instead of crashing with exit 2 (PR #973 by @farikh)
|
||||
- **Fail-open context injection** — Returns empty context during initialization instead of 503 (PR #959 by @rodboev)
|
||||
- **Fetch timeouts** — All hook fetch calls have timeouts via \`fetchWithTimeout()\` helper (PR #964 by @rodboev)
|
||||
- **Removed stale user-message hook** — Eliminated startup error from incorrectly bundled hook (PR #960 by @rodboev)
|
||||
- **DB initialization middleware** — All \`/api/*\` routes now wait for DB init with 30s timeout instead of crashing
|
||||
|
||||
### Windows Stability
|
||||
|
||||
- **Path spaces fix** — bun-runner.js no longer fails for Windows usernames with spaces (PR #972 by @farikh)
|
||||
- **Spawn guard** — 2-minute cooldown prevents repeated worker popup windows on startup failure
|
||||
|
||||
### Process & Zombie Management
|
||||
|
||||
- **Daemon children cleanup** — Orphan reaper now catches idle daemon child processes (PR #879 by @boaz-robopet)
|
||||
- **Expanded orphan cleanup** — Startup cleanup now targets mcp-server.cjs and worker-service.cjs processes
|
||||
- **Session-complete hook** — New Stop phase 2 hook removes sessions from active map, enabling effective orphan reaper cleanup (PR #844 by @thusdigital, fixes #842)
|
||||
|
||||
### Session Management
|
||||
|
||||
- **Prompt-too-long termination** — Sessions terminate cleanly instead of infinite retry loops (PR #934 by @jayvenn21)
|
||||
- **Infinite restart prevention** — Max 3 restart attempts with exponential backoff, prevents runaway API costs (PR #693 by @ajbmachon)
|
||||
- **Orphaned message fallback** — Messages from terminated sessions drain via Gemini/OpenRouter fallback (PR #937 by @jayvenn21, fixes #936)
|
||||
- **Project field backfill** — Sessions correctly scoped when PostToolUse creates session before UserPromptSubmit (PR #940 by @miclip)
|
||||
- **Provider-aware recovery** — Startup recovery uses correct provider instead of hardcoding SDKAgent (PR #741 by @licutis)
|
||||
- **AbortController reset** — Prevents infinite "Generator aborted" loops after session abort (PR #627 by @TranslateMe)
|
||||
- **Stateless provider IDs** — Synthetic memorySessionId generation for Gemini/OpenRouter (concept from PR #615 by @JiehoonKwak)
|
||||
- **Duplicate generator prevention** — Legacy init endpoint uses idempotent \`ensureGeneratorRunning()\` (PR #932 by @jayvenn21)
|
||||
- **DB readiness wait** — Session-init endpoint waits for database initialization (PR #828 by @rajivsinclair)
|
||||
- **Image-only prompt support** — Empty/media prompts use \`[media prompt]\` placeholder (concept from PR #928 by @iammike)
|
||||
|
||||
### CLAUDE.md Path & Generation
|
||||
|
||||
- **Race condition fix** — Two-pass detection prevents corruption when Claude Code edits CLAUDE.md (concept from PR #974 by @cheapsteak)
|
||||
- **Duplicate path prevention** — Detects \`frontend/frontend/\` style nested duplicates (concept from PR #836 by @Glucksberg)
|
||||
- **Unsafe directory exclusion** — Blocks generation in \`res/\`, \`.git/\`, \`build/\`, \`node_modules/\`, \`__pycache__/\` (concept from PR #929 by @jayvenn21)
|
||||
|
||||
### Chroma/Vector Search
|
||||
|
||||
- **ID/metadata alignment fix** — Search results no longer misaligned after deduplication (PR #887 by @abkrim)
|
||||
- **Transport zombie prevention** — Connection error handlers now close transport (PR #769 by @jenyapoyarkov)
|
||||
- **Zscaler SSL support** — Enterprise environments with SSL inspection now work via combined cert path (PR #884 by @RClark4958)
|
||||
|
||||
### Parser & Config
|
||||
|
||||
- **Nested XML tag handling** — Parser correctly extracts fields with nested XML content (PR #835 by @Glucksberg)
|
||||
- **Graceful empty transcripts** — Transcript parser returns empty string instead of crashing (PR #862 by @DennisHartrampf)
|
||||
- **Gemini model name fix** — Corrected \`gemini-3-flash\` → \`gemini-3-flash-preview\` (PR #831 by @Glucksberg)
|
||||
- **CLAUDE_CONFIG_DIR support** — Plugin paths respect custom config directory (PR #634 by @Kuroakira, fixes #626)
|
||||
- **Env var priority** — \`env > file > defaults\` ordering via \`applyEnvOverrides()\` (PR #712 by @cjpeterein)
|
||||
- **Minimum Bun version check** — smart-install.js enforces Bun 1.1.14+ (PR #524 by @quicktime, fixes #519)
|
||||
- **Stdin timeout** — JSON self-delimiting detection with 30s safety timeout prevents hook hangs (PR #771 by @rajivsinclair, fixes #727)
|
||||
- **FK constraint prevention** — \`ensureMemorySessionIdRegistered()\` guard + \`ON UPDATE CASCADE\` schema migration (PR #889 by @Et9797, fixes #846)
|
||||
- **Cursor bun runtime** — Cursor hooks use bun instead of node, fixing bun:sqlite crashes (PR #721 by @polux0)
|
||||
|
||||
### Documentation
|
||||
|
||||
- **9 README PRs merged**: formatting fixes, Korean/Japanese/Chinese render fixes, documentation link updates, Traditional Chinese + Urdu translations (PRs #953, #898, #864, #637, #636, #894, #907, #691 by @Leonard013, @youngsu5582, @eltociear, @WuMingDao, @fengluodb, @PeterDaveHello, @yasirali646)
|
||||
- **Windows setup note** — npm PATH instructions (PR #919 by @kamran-khalid-v9)
|
||||
- **Issue templates** — Duplicate check checkbox added (PR #970 by @bmccann36)
|
||||
|
||||
### Community Contributors
|
||||
|
||||
Thank you to the 35+ contributors whose PRs were reviewed in this release:
|
||||
|
||||
@Spunky84, @farikh, @rodboev, @boaz-robopet, @jayvenn21, @ajbmachon, @miclip, @licutis, @TranslateMe, @JiehoonKwak, @rajivsinclair, @iammike, @cheapsteak, @Glucksberg, @abkrim, @jenyapoyarkov, @RClark4958, @DennisHartrampf, @Kuroakira, @cjpeterein, @quicktime, @polux0, @Et9797, @thusdigital, @superbiche, @darconada, @leepokai, @Leonard013, @youngsu5582, @eltociear, @WuMingDao, @fengluodb, @PeterDaveHello, @yasirali646, @kamran-khalid-v9, @bmccann36
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v9.0.17...v9.1.0
|
||||
|
||||
## [v9.0.17] - 2026-02-05
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Fix Fresh Install Bun PATH Resolution (#818)
|
||||
|
||||
On fresh installations, hooks would fail because Bun wasn't in PATH until terminal restart. The `smart-install.js` script installs Bun to `~/.bun/bin/bun`, but the current shell session doesn't have it in PATH.
|
||||
|
||||
**Fix:** Introduced `bun-runner.js` — a Node.js wrapper that searches common Bun installation locations across all platforms:
|
||||
- PATH (via `which`/`where`)
|
||||
- `~/.bun/bin/bun` (default install location)
|
||||
- `/usr/local/bin/bun`
|
||||
- `/opt/homebrew/bin/bun` (macOS Homebrew)
|
||||
- `/home/linuxbrew/.linuxbrew/bin/bun` (Linuxbrew)
|
||||
- Windows: `%LOCALAPPDATA%\bun` or fallback paths
|
||||
|
||||
All 9 hook definitions updated to use `node bun-runner.js` instead of direct `bun` calls.
|
||||
|
||||
**Files changed:**
|
||||
- `plugin/scripts/bun-runner.js` — New 88-line Bun discovery script
|
||||
- `plugin/hooks/hooks.json` — All hook commands now route through bun-runner
|
||||
|
||||
Fixes #818 | PR #827 by @bigphoot
|
||||
|
||||
## [v9.0.16] - 2026-02-05
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Fix Worker Startup Timeout (#811, #772, #729)
|
||||
|
||||
Resolves the "Worker did not become ready within 15 seconds" timeout error that could prevent hooks from communicating with the worker service.
|
||||
|
||||
**Root cause:** `isWorkerHealthy()` and `waitForHealth()` were checking `/api/readiness`, which returns 503 until full initialization completes — including MCP connection setup that can take 5+ minutes. Hooks only have a 15-second timeout window.
|
||||
|
||||
**Fix:** Switched to `/api/health` (liveness check), which returns 200 as soon as the HTTP server is listening. This is sufficient for hook communication since the worker accepts requests while background initialization continues.
|
||||
|
||||
**Files changed:**
|
||||
- `src/shared/worker-utils.ts` — `isWorkerHealthy()` now checks `/api/health`
|
||||
- `src/services/infrastructure/HealthMonitor.ts` — `waitForHealth()` now checks `/api/health`
|
||||
- `tests/infrastructure/health-monitor.test.ts` — Updated test expectations
|
||||
|
||||
### PR Merge Tasks
|
||||
- PR #820 merged with full verification pipeline (rebase, code review, build verification, test, manual verification)
|
||||
|
||||
## [v9.0.15] - 2026-02-05
|
||||
|
||||
## Security Fix
|
||||
|
||||
### Isolated Credentials (#745)
|
||||
- **Prevents API key hijacking** from random project `.env` files
|
||||
- Credentials now sourced exclusively from `~/.claude-mem/.env`
|
||||
- Only whitelisted environment variables passed to SDK `query()` calls
|
||||
- Authentication method logging shows whether using Claude Code CLI subscription billing or explicit API key
|
||||
|
||||
This is a security-focused patch release that hardens credential handling to prevent unintended API key usage from project directories.
|
||||
|
||||
## [v9.0.14] - 2026-02-05
|
||||
|
||||
## In-Process Worker Architecture
|
||||
|
||||
This release includes the merged in-process worker architecture from PR #722, which fundamentally improves how hooks interact with the worker service.
|
||||
|
||||
### Changes
|
||||
|
||||
- **In-process worker architecture** - Hook processes now become the worker when port 37777 is available, eliminating Windows spawn issues
|
||||
- **Hook command improvements** - Added `skipExit` option to `hook-command.ts` for chained command execution
|
||||
- **Worker health checks** - `worker-utils.ts` now returns boolean status for cleaner health monitoring
|
||||
- **Massive CLAUDE.md cleanup** - Removed 76 redundant documentation files (4,493 lines removed)
|
||||
- **Chained hook configuration** - `hooks.json` now supports chained commands for complex workflows
|
||||
|
||||
### Technical Details
|
||||
|
||||
The in-process architecture means hooks no longer need to spawn separate worker processes. When port 37777 is available, the hook itself becomes the worker, providing:
|
||||
- Faster startup times
|
||||
- Better resource utilization
|
||||
- Elimination of process spawn failures on Windows
|
||||
|
||||
Full PR: https://github.com/thedotmack/claude-mem/pull/722
|
||||
|
||||
## [v9.0.13] - 2026-02-05
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Zombie Observer Prevention (#856)
|
||||
|
||||
Fixed a critical issue where observer processes could become "zombies" - lingering indefinitely without activity. This release adds:
|
||||
|
||||
- **3-minute idle timeout**: SessionQueueProcessor now automatically terminates after 3 minutes of inactivity
|
||||
- **Race condition fix**: Resolved spurious wakeup issues by resetting `lastActivityTime` on queue activity
|
||||
- **Comprehensive test coverage**: Added 11 new tests for the idle timeout mechanism
|
||||
|
||||
This fix prevents resource leaks from orphaned observer processes that could accumulate over time.
|
||||
|
||||
## [v9.0.12] - 2026-01-28
|
||||
|
||||
## Fix: Authentication failure from observer session isolation
|
||||
|
||||
**Critical bugfix** for users who upgraded to v9.0.11.
|
||||
|
||||
### Problem
|
||||
|
||||
v9.0.11 introduced observer session isolation using `CLAUDE_CONFIG_DIR` override, which inadvertently broke authentication:
|
||||
|
||||
```
|
||||
Invalid API key · Please run /login
|
||||
```
|
||||
|
||||
This happened because Claude Code stores credentials in the config directory, and overriding it prevented access to existing auth tokens.
|
||||
|
||||
### Solution
|
||||
|
||||
Observer sessions now use the SDK's `cwd` option instead:
|
||||
- Sessions stored under `~/.claude-mem/observer-sessions/` project
|
||||
- Auth credentials in `~/.claude/` remain accessible
|
||||
- Observer sessions still won't pollute `claude --resume` lists
|
||||
|
||||
### Affected Users
|
||||
|
||||
Anyone running v9.0.11 who saw "Invalid API key" errors should upgrade immediately.
|
||||
|
||||
---
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
## [v9.0.11] - 2026-01-28
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Observer Session Isolation (#837)
|
||||
Observer sessions created by claude-mem were polluting the `claude --resume` list, cluttering it with internal plugin sessions that users never intend to resume. In one user's case, 74 observer sessions out of ~220 total (34% noise).
|
||||
|
||||
**Solution**: Observer processes now use a dedicated config directory (`~/.claude-mem/observer-config/`) to isolate their session files from user sessions.
|
||||
|
||||
Thanks to @Glucksberg for this fix! Fixes #832.
|
||||
|
||||
### Stale memory_session_id Crash Prevention (#839)
|
||||
After a worker restart, stale `memory_session_id` values in the database could cause crashes when attempting to resume SDK conversations. The existing guard didn't protect against this because session data was loaded from the database.
|
||||
|
||||
**Solution**: Clear `memory_session_id` when loading sessions from the database (not from cache). The key insight: if a session isn't in memory, any database `memory_session_id` is definitely stale.
|
||||
|
||||
Thanks to @bigph00t for this fix! Fixes #817.
|
||||
|
||||
---
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v9.0.10...v9.0.11
|
||||
|
||||
## [v9.0.10] - 2026-01-26
|
||||
|
||||
## Bug Fix
|
||||
|
||||
**Fixed path format mismatch causing folder CLAUDE.md files to show "No recent activity" (#794)** - Thanks @bigph00t!
|
||||
|
||||
The folder-level CLAUDE.md generation was failing to find observations due to a path format mismatch between how API queries used absolute paths and how the database stored relative paths. The `isDirectChild()` function's simple prefix match always returned false in these cases.
|
||||
|
||||
**Root cause:** PR #809 (v9.0.9) only masked this bug by skipping file creation when "no activity" was detected. Since ALL folders were affected, this prevented file creation entirely. This PR provides the actual fix.
|
||||
|
||||
**Changes:**
|
||||
- Added new shared module `src/shared/path-utils.ts` with robust path normalization and matching utilities
|
||||
- Updated `SessionSearch.ts`, `regenerate-claude-md.ts`, and `claude-md-utils.ts` to use shared path utilities
|
||||
- Added comprehensive test coverage (61 new tests) for path matching edge cases
|
||||
|
||||
---
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v9.0.9] - 2026-01-26
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Prevent Creation of Empty CLAUDE.md Files (#809)
|
||||
|
||||
Previously, claude-mem would create new `CLAUDE.md` files in project directories even when there was no activity to display, cluttering codebases with empty context files showing only "*No recent activity*".
|
||||
|
||||
**What changed:** The `updateFolderClaudeMdFiles` function now checks if the formatted content contains no activity before writing. If a `CLAUDE.md` file doesn't already exist and there's nothing to show, it will be skipped entirely. Existing files will still be updated to reflect "No recent activity" if that's the current state.
|
||||
|
||||
**Impact:** Cleaner project directories - only folders with actual activity will have `CLAUDE.md` context files created.
|
||||
|
||||
Thanks to @maxmillienjr for this contribution!
|
||||
|
||||
## [v9.0.8] - 2026-01-26
|
||||
|
||||
## Fix: Prevent Zombie Process Accumulation (Issue #737)
|
||||
|
||||
This release fixes a critical issue where Claude haiku subprocesses spawned by the SDK weren't terminating properly, causing zombie process accumulation. One user reported 155 processes consuming 51GB RAM.
|
||||
|
||||
### Root Causes Addressed
|
||||
- SDK's SpawnedProcess interface hides subprocess PIDs
|
||||
- `deleteSession()` didn't verify subprocess exit
|
||||
- `abort()` was fire-and-forget with no confirmation
|
||||
- No mechanism to track or clean up orphaned processes
|
||||
|
||||
### Solution
|
||||
- **ProcessRegistry module**: Tracks spawned Claude subprocesses via PID
|
||||
- **Custom spawn**: Uses SDK's `spawnClaudeCodeProcess` option to capture PIDs
|
||||
- **Signal propagation**: Passes signal parameter to enable AbortController integration
|
||||
- **Graceful shutdown**: Waits for subprocess exit in `deleteSession()` with 5s timeout
|
||||
- **SIGKILL escalation**: Force-kills processes that don't exit gracefully
|
||||
- **Orphan reaper**: Safety net running every 5 minutes to clean up any missed processes
|
||||
- **Race detection**: Warns about multiple processes per session (race condition indicator)
|
||||
|
||||
### Files Changed
|
||||
- `src/services/worker/ProcessRegistry.ts` (new): PID registry and reaper
|
||||
- `src/services/worker/SDKAgent.ts`: Use custom spawn to capture PIDs
|
||||
- `src/services/worker/SessionManager.ts`: Verify subprocess exit on delete
|
||||
- `src/services/worker-service.ts`: Start/stop orphan reaper
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v9.0.7...v9.0.8
|
||||
|
||||
Fixes #737
|
||||
|
||||
## [v9.0.6] - 2026-01-22
|
||||
|
||||
## Windows Console Popup Fix
|
||||
|
||||
This release eliminates the annoying console window popups that Windows users experienced when claude-mem spawned background processes.
|
||||
|
||||
### Fixed
|
||||
- **Windows console popups eliminated** - Daemon spawn and Chroma operations no longer create visible console windows (#748, #708, #681, #676)
|
||||
- **Race condition in PID file writing** - Worker now writes its own PID file after listen() succeeds, ensuring reliable process tracking on all platforms
|
||||
|
||||
### Changed
|
||||
- **Chroma temporarily disabled on Windows** - Vector search is disabled on Windows while we migrate to a popup-free architecture. Keyword search and all other memory features continue to work. A follow-up release will re-enable Chroma.
|
||||
- **Slash command discoverability** - Added YAML frontmatter to `/do` and `/make-plan` commands
|
||||
|
||||
### Technical Details
|
||||
- Uses WMIC for detached process spawning on Windows
|
||||
- PID file location unchanged, but now written by worker process
|
||||
- Cross-platform: Linux/macOS behavior unchanged
|
||||
|
||||
### Contributors
|
||||
- @bigph00t (Alexander Knigge)
|
||||
|
||||
## [v9.0.5] - 2026-01-14
|
||||
|
||||
## Major Worker Service Cleanup
|
||||
|
||||
This release contains a significant refactoring of `worker-service.ts`, removing ~216 lines of dead code and simplifying the architecture.
|
||||
|
||||
### Refactoring
|
||||
- **Removed dead code**: Deleted `runInteractiveSetup` function (defined but never called)
|
||||
- **Cleaned up imports**: Removed unused imports (fs namespace, spawn, homedir, readline, existsSync, writeFileSync, readFileSync, mkdirSync)
|
||||
- **Removed fallback agent concept**: Users who choose Gemini/OpenRouter now get those providers directly without hidden fallback behavior
|
||||
- **Eliminated re-export indirection**: ResponseProcessor now imports directly from CursorHooksInstaller instead of through worker-service
|
||||
|
||||
### Security Fix
|
||||
- **Removed dangerous ANTHROPIC_API_KEY check**: Claude Code uses CLI authentication, not direct API calls. The previous check could accidentally use a user's API key (from other projects) which costs 20x more than Claude Code's pricing
|
||||
|
||||
### Build Improvements
|
||||
- **Dynamic MCP version management**: MCP server and client versions now use build-time injected values from package.json instead of hardcoded strings, ensuring version synchronization
|
||||
|
||||
### Documentation
|
||||
- Added Anti-Pattern Czar Generalization Analysis report
|
||||
- Updated README with $CMEM links and contract address
|
||||
- Added comprehensive cleanup and validation plans for worker-service.ts
|
||||
|
||||
## [v9.0.4] - 2026-01-10
|
||||
|
||||
## What's New
|
||||
|
||||
This release adds the `/do` and `/make-plan` development commands to the plugin distribution, making them available to all users who install the plugin from the marketplace.
|
||||
|
||||
### Features
|
||||
|
||||
- **Development Commands Now Distributed with Plugin** (#666)
|
||||
- `/do` command - Execute tasks with structured workflow
|
||||
- `/make-plan` command - Create detailed implementation plans
|
||||
- Commands now available at `plugin/commands/` for all users
|
||||
|
||||
### Documentation
|
||||
|
||||
- Revised Arabic README for clarity and corrections (#661)
|
||||
|
||||
### Full Changelog
|
||||
|
||||
https://github.com/thedotmack/claude-mem/compare/v9.0.3...v9.0.4
|
||||
|
||||
## [v9.0.3] - 2026-01-10
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Hook Framework JSON Status Output (#655)
|
||||
|
||||
Fixed an issue where the worker service startup wasn't producing proper JSON status output for the Claude Code hook framework. This caused hooks to appear stuck or unresponsive during worker initialization.
|
||||
|
||||
**Changes:**
|
||||
- Added `buildStatusOutput()` function for generating structured JSON status output
|
||||
- Worker now outputs JSON with `status`, `message`, and `continue` fields on stdout
|
||||
- Proper exit code 0 ensures Windows Terminal compatibility (no tab accumulation)
|
||||
- `continue: true` flag ensures Claude Code continues processing after hook execution
|
||||
|
||||
**Technical Details:**
|
||||
- Extracted status output generation into a pure, testable function
|
||||
- Added comprehensive test coverage in `tests/infrastructure/worker-json-status.test.ts`
|
||||
- 23 passing tests covering unit, CLI integration, and hook framework compatibility
|
||||
|
||||
## Housekeeping
|
||||
|
||||
- Removed obsolete error handling baseline file
|
||||
|
||||
## [v9.0.2] - 2026-01-10
|
||||
|
||||
## Bug Fixes
|
||||
@@ -990,302 +1506,3 @@ Set in ~/.claude-mem/settings.json:
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v8.0.0] - 2025-12-23
|
||||
|
||||
## 🌍 Major Features
|
||||
|
||||
### **Mode System**: Context-aware observation capture tailored to different workflows
|
||||
- **Code Development mode** (default): Tracks bugfixes, features, refactors, and more
|
||||
- **Email Investigation mode**: Optimized for email analysis workflows
|
||||
- Extensible architecture for custom domains
|
||||
|
||||
### **28 Language Support**: Full multilingual memory
|
||||
- Arabic, Bengali, Chinese, Czech, Danish, Dutch, Finnish, French, German, Greek
|
||||
- Hebrew, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Norwegian, Polish
|
||||
- Portuguese (Brazilian), Romanian, Russian, Spanish, Swedish, Thai, Turkish
|
||||
- Ukrainian, Vietnamese
|
||||
- All observations, summaries, and narratives generated in your chosen language
|
||||
|
||||
### **Inheritance Architecture**: Language modes inherit from base modes
|
||||
- Consistent observation types across languages
|
||||
- Locale-specific output while maintaining structural integrity
|
||||
- JSON-based configuration for easy customization
|
||||
|
||||
## 🔧 Technical Improvements
|
||||
|
||||
- **ModeManager**: Centralized mode loading and configuration validation
|
||||
- **Dynamic Prompts**: SDK prompts now adapt based on active mode
|
||||
- **Mode-Specific Icons**: Observation types display contextual icons/emojis per mode
|
||||
- **Fail-Fast Error Handling**: Complete removal of silent failures across all layers
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
- New docs/public/modes.mdx documenting the mode system
|
||||
- 28 translated README files for multilingual community support
|
||||
- Updated configuration guide for mode selection
|
||||
|
||||
## 🔨 Breaking Changes
|
||||
|
||||
- **None** - Mode system is fully backward compatible
|
||||
- Default mode is 'code' (existing behavior)
|
||||
- Settings: New `CLAUDE_MEM_MODE` option (defaults to 'code')
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.4.5...v8.0.0
|
||||
**View PR**: https://github.com/thedotmack/claude-mem/pull/412
|
||||
|
||||
## [v7.4.5] - 2025-12-21
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Fix missing `formatDateTime` import in SearchManager that broke `get_context_timeline` mem-search function
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v7.4.4] - 2025-12-21
|
||||
|
||||
## What's Changed
|
||||
|
||||
* Code quality: comprehensive nonsense audit cleanup (20 issues) by @thedotmack in #400
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.4.3...v7.4.4
|
||||
|
||||
## [v7.4.3] - 2025-12-20
|
||||
|
||||
Added Discord notification script for release announcements.
|
||||
|
||||
### Added
|
||||
- `scripts/discord-release-notify.js` - Posts formatted release notifications to Discord using webhook URL from `.env`
|
||||
- `npm run discord:notify <version>` - New npm script to trigger Discord notifications
|
||||
- Updated version-bump skill workflow to include Discord notification step
|
||||
|
||||
### Configuration
|
||||
Set `DISCORD_UPDATES_WEBHOOK` in your `.env` file to enable release notifications.
|
||||
|
||||
---
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v7.4.2] - 2025-12-20
|
||||
|
||||
Patch release v7.4.2
|
||||
|
||||
## Changes
|
||||
- Refactored worker commands from npm scripts to claude-mem CLI
|
||||
- Added path alias script
|
||||
- Fixed Windows worker stop/restart reliability (#395)
|
||||
- Simplified build commands section in CLAUDE.md
|
||||
|
||||
## [v7.4.1] - 2025-12-19
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **MCP Server**: Redirect logs to stderr to preserve JSON-RPC protocol (#396)
|
||||
- MCP uses stdio transport where stdout is reserved for JSON-RPC messages
|
||||
- Console.log was writing startup logs to stdout, causing Claude Desktop to parse log lines as JSON and fail
|
||||
|
||||
## [v7.4.0] - 2025-12-18
|
||||
|
||||
## What's New
|
||||
|
||||
### MCP Tool Token Reduction
|
||||
|
||||
Optimized MCP tool definitions for reduced token consumption in Claude Code sessions through progressive parameter disclosure.
|
||||
|
||||
**Changes:**
|
||||
- Streamlined MCP tool schemas with minimal inline definitions
|
||||
- Added `get_schema()` tool for on-demand parameter documentation
|
||||
- Enhanced worker API with operation-based instruction loading
|
||||
|
||||
This release improves session efficiency by reducing the token overhead of MCP tool definitions while maintaining full functionality through progressive disclosure.
|
||||
|
||||
---
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v7.3.9] - 2025-12-18
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fix MCP server compatibility and web UI path resolution
|
||||
|
||||
This patch release addresses compatibility issues with the MCP server and resolves path resolution problems in the web UI.
|
||||
|
||||
## [v7.3.8] - 2025-12-18
|
||||
|
||||
## Security Fix
|
||||
|
||||
Added localhost-only protection for admin endpoints to prevent DoS attacks when worker service is bound to 0.0.0.0 for remote UI access.
|
||||
|
||||
### Changes
|
||||
- Created `requireLocalhost` middleware to restrict admin endpoints
|
||||
- Applied to `/api/admin/restart` and `/api/admin/shutdown`
|
||||
- Returns 403 Forbidden for non-localhost requests
|
||||
|
||||
### Security Impact
|
||||
Prevents unauthorized shutdown/restart of worker service when exposed on network.
|
||||
|
||||
Fixes security concern raised in #368.
|
||||
|
||||
## [v7.3.7] - 2025-12-17
|
||||
|
||||
## Windows Platform Stabilization
|
||||
|
||||
This patch release includes comprehensive improvements for Windows platform stability and reliability.
|
||||
|
||||
### Key Improvements
|
||||
|
||||
- **Worker Readiness Tracking**: Added `/api/readiness` endpoint with MCP/SDK initialization flags to prevent premature connection attempts
|
||||
- **Process Tree Cleanup**: Implemented recursive process enumeration on Windows to prevent zombie socket processes
|
||||
- **Bun Runtime Migration**: Migrated worker wrapper from Node.js to Bun for consistency and reliability
|
||||
- **Centralized Project Name Utility**: Consolidated duplicate project name extraction logic with Windows drive root handling
|
||||
- **Enhanced Error Messages**: Added platform-aware logging and detailed Windows troubleshooting guidance
|
||||
- **Subprocess Console Hiding**: Standardized `windowsHide: true` across all child process spawns to prevent console window flashing
|
||||
|
||||
### Technical Details
|
||||
|
||||
- Worker service tracks MCP and SDK readiness states separately
|
||||
- ChromaSync service properly tracks subprocess PIDs for Windows cleanup
|
||||
- Worker wrapper uses Bun runtime with enhanced socket cleanup via process tree enumeration
|
||||
- Increased timeouts on Windows platform (30s worker startup, 10s hook timeouts)
|
||||
- Logger utility includes platform and PID information for better debugging
|
||||
|
||||
This represents a major reliability improvement for Windows users, eliminating common issues with worker startup failures, orphaned processes, and zombie sockets.
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.6...v7.3.7
|
||||
|
||||
## [v7.3.6] - 2025-12-17
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Enhanced SDKAgent response handling and message processing
|
||||
|
||||
## [v7.3.5] - 2025-12-17
|
||||
|
||||
## What's Changed
|
||||
* fix(windows): solve zombie port problem with wrapper architecture by @ToxMox in https://github.com/thedotmack/claude-mem/pull/372
|
||||
* chore: bump version to 7.3.5 by @thedotmack in https://github.com/thedotmack/claude-mem/pull/375
|
||||
|
||||
## New Contributors
|
||||
* @ToxMox made their first contribution in https://github.com/thedotmack/claude-mem/pull/372
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.4...v7.3.5
|
||||
|
||||
## [v7.3.4] - 2025-12-17
|
||||
|
||||
Patch release for bug fixes and minor improvements
|
||||
|
||||
## [v7.3.3] - 2025-12-16
|
||||
|
||||
## What's Changed
|
||||
|
||||
- Remove all better-sqlite3 references from codebase (#357)
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.2...v7.3.3
|
||||
|
||||
## [v7.3.2] - 2025-12-16
|
||||
|
||||
## 🪟 Windows Console Fix
|
||||
|
||||
Fixes blank console windows appearing for Windows 11 users during claude-mem operations.
|
||||
|
||||
### What Changed
|
||||
|
||||
- **Windows**: Uses PowerShell `Start-Process -WindowStyle Hidden` to properly hide worker process
|
||||
- **Security**: Added PowerShell string escaping to follow security best practices
|
||||
- **Unix/Mac**: No changes (continues to work as before)
|
||||
|
||||
### Root Cause
|
||||
|
||||
The issue was caused by a Node.js limitation where `windowsHide: true` doesn't work with `detached: true` in `child_process.spawn()`. This affects both Bun and Node.js since Bun inherits Node.js process spawning semantics.
|
||||
|
||||
See: https://github.com/nodejs/node/issues/21825
|
||||
|
||||
### Security Note
|
||||
|
||||
While all paths in the PowerShell command are application-controlled (not user input), we've added proper escaping to follow security best practices. If an attacker could modify bun installation paths or plugin directories, they would already have full filesystem access including the database.
|
||||
|
||||
### Related
|
||||
|
||||
- Fixes #304 (Multiple visible console windows)
|
||||
- Merged PR #339
|
||||
- Testing documented in PR #315
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
None - fully backward compatible.
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.1...v7.3.2
|
||||
|
||||
## [v7.3.1] - 2025-12-16
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
### Pending Messages Cleanup (Issue #353)
|
||||
|
||||
Fixed unbounded database growth in the `pending_messages` table by implementing proper cleanup logic:
|
||||
|
||||
- **Content Clearing**: `markProcessed()` now clears `tool_input` and `tool_response` when marking messages as processed, preventing duplicate storage of transcript data that's already saved in observations
|
||||
- **Count-Based Retention**: `cleanupProcessed()` now keeps only the 100 most recent processed messages for UI display, deleting older ones automatically
|
||||
- **Automatic Cleanup**: Cleanup runs automatically after processing messages in `SDKAgent.processSDKResponse()`
|
||||
|
||||
### What This Fixes
|
||||
|
||||
- Prevents database from growing unbounded with duplicate transcript content
|
||||
- Keeps metadata (tool_name, status, timestamps) for recent messages
|
||||
- Maintains UI functionality while optimizing storage
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Files Modified:**
|
||||
- `src/services/sqlite/PendingMessageStore.ts` - Cleanup logic implementation
|
||||
- `src/services/worker/SDKAgent.ts` - Periodic cleanup calls
|
||||
|
||||
**Database Behavior:**
|
||||
- Pending/processing messages: Keep full transcript data (needed for processing)
|
||||
- Processed messages: Clear transcript, keep metadata only (observations already saved)
|
||||
- Retention: Last 100 processed messages for UI feedback
|
||||
|
||||
### Related
|
||||
|
||||
- Fixes #353 - Observations not being saved
|
||||
- Part of the pending messages persistence feature (from PR #335)
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.0...v7.3.1
|
||||
|
||||
## [v7.3.0] - 2025-12-16
|
||||
|
||||
## Features
|
||||
|
||||
- **Table-based search output**: Unified timeline formatting with cleaner, more organized presentation of search results grouped by date and file
|
||||
- **Simplified API**: Removed unused format parameter from MCP search tools for cleaner interface
|
||||
- **Shared formatting utilities**: Extracted common timeline formatting logic into reusable module
|
||||
- **Batch observations endpoint**: Added `/api/observations/batch` endpoint for efficient retrieval of multiple observations by ID array
|
||||
|
||||
## Changes
|
||||
|
||||
- **Default model upgrade**: Changed default model from Haiku to Sonnet for better observation quality
|
||||
- **Removed fake URIs**: Replaced claude-mem:// pseudo-protocol with actual HTTP API endpoints for citations
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Fixed undefined debug function calls in MCP server
|
||||
- Fixed skillPath variable scoping bug in instructions endpoint
|
||||
- Extracted magic numbers to named constants for better code maintainability
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.2.4...v7.3.0
|
||||
|
||||
## [v7.2.4] - 2025-12-15
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Updated endless mode setup instructions with improved configuration guidance for better user experience
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.2.3...v7.2.4
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<p align="center">
|
||||
Official $CMEM Links:
|
||||
<a href="https://bags.fm/2TsmuYUrsctE57VLckZBYEEzdokUF8j8e1GavekWBAGS">Bags.fm</a> •
|
||||
<a href="https://jup.ag/tokens/2TsmuYUrsctE57VLckZBYEEzdokUF8j8e1GavekWBAGS">Jupiter</a> •
|
||||
<a href="https://photon-sol.tinyastro.io/en/lp/6MzFAkWnac6GSK1EdFX93dZeukGfzrFq4UHWarhGSQyd">Photon</a> •
|
||||
<a href="https://dexscreener.com/solana/6mzfakwnac6gsk1edfx93dzeukgfzrfq4uhwarhgsqyd">DEXScreener</a>
|
||||
</p>
|
||||
|
||||
<p align="center">Official CA: 2TsmuYUrsctE57VLckZBYEEzdokUF8j8e1GavekWBAGS (on Solana)</p>
|
||||
|
||||
<h1 align="center">
|
||||
<br>
|
||||
@@ -14,6 +22,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="docs/i18n/README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="docs/i18n/README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="docs/i18n/README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="docs/i18n/README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="docs/i18n/README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +42,7 @@
|
||||
<a href="docs/i18n/README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="docs/i18n/README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="docs/i18n/README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="docs/i18n/README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="docs/i18n/README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="docs/i18n/README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="docs/i18n/README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -101,9 +111,9 @@
|
||||
Start a new Claude Code session in the terminal and enter the following commands:
|
||||
|
||||
```
|
||||
> /plugin marketplace add thedotmack/claude-mem
|
||||
/plugin marketplace add thedotmack/claude-mem
|
||||
|
||||
> /plugin install claude-mem
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
Restart Claude Code. Context from previous sessions will automatically appear in new sessions.
|
||||
@@ -125,7 +135,7 @@ Restart Claude Code. Context from previous sessions will automatically appear in
|
||||
|
||||
## Documentation
|
||||
|
||||
📚 **[View Full Documentation](docs/)** - Browse markdown docs on GitHub
|
||||
📚 **[View Full Documentation](https://docs.claude-mem.ai/)** - Browse on official website
|
||||
|
||||
### Getting Started
|
||||
|
||||
@@ -174,7 +184,7 @@ See [Architecture Overview](https://docs.claude-mem.ai/architecture/overview) fo
|
||||
|
||||
## MCP Search Tools
|
||||
|
||||
Claude-Mem provides intelligent memory search through **4 MCP tools** following a token-efficient **3-layer workflow pattern**:
|
||||
Claude-Mem provides intelligent memory search through **5 MCP tools** following a token-efficient **3-layer workflow pattern**:
|
||||
|
||||
**The 3-Layer Workflow:**
|
||||
|
||||
@@ -187,6 +197,7 @@ Claude-Mem provides intelligent memory search through **4 MCP tools** following
|
||||
- Start with `search` to get an index of results
|
||||
- Use `timeline` to see what was happening around specific observations
|
||||
- Use `get_observations` to fetch full details for relevant IDs
|
||||
- Use `save_memory` to manually store important information
|
||||
- **~10x token savings** by filtering before fetching details
|
||||
|
||||
**Available MCP Tools:**
|
||||
@@ -194,7 +205,8 @@ Claude-Mem provides intelligent memory search through **4 MCP tools** following
|
||||
1. **`search`** - Search memory index with full-text queries, filters by type/date/project
|
||||
2. **`timeline`** - Get chronological context around a specific observation or query
|
||||
3. **`get_observations`** - Fetch full observation details by IDs (always batch multiple IDs)
|
||||
4. **`__IMPORTANT`** - Workflow documentation (always visible to Claude)
|
||||
4. **`save_memory`** - Manually save a memory/observation for semantic search
|
||||
5. **`__IMPORTANT`** - Workflow documentation (always visible to Claude)
|
||||
|
||||
**Example Usage:**
|
||||
|
||||
@@ -206,6 +218,9 @@ search(query="authentication bug", type="bugfix", limit=10)
|
||||
|
||||
// Step 3: Fetch full details
|
||||
get_observations(ids=[123, 456])
|
||||
|
||||
// Save important information manually
|
||||
save_memory(text="API requires auth header X-API-Key", title="API Auth")
|
||||
```
|
||||
|
||||
See [Search Tools Guide](https://docs.claude-mem.ai/usage/search-tools) for detailed examples.
|
||||
@@ -228,6 +243,17 @@ See **[Beta Features Documentation](https://docs.claude-mem.ai/beta-features)**
|
||||
- **uv**: Python package manager for vector search (auto-installed if missing)
|
||||
- **SQLite 3**: For persistent storage (bundled)
|
||||
|
||||
---
|
||||
### Windows Setup Notes
|
||||
|
||||
If you see an error like:
|
||||
|
||||
```powershell
|
||||
npm : The term 'npm' is not recognized as the name of a cmdlet
|
||||
```
|
||||
|
||||
Make sure Node.js and npm are installed and added to your PATH. Download the latest Node.js installer from https://nodejs.org and restart your terminal after installation.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
@@ -299,6 +325,8 @@ See the [LICENSE](LICENSE) file for full details.
|
||||
- **Documentation**: [docs/](docs/)
|
||||
- **Issues**: [GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- **Repository**: [github.com/thedotmack/claude-mem](https://github.com/thedotmack/claude-mem)
|
||||
- **Official X Account**: [@Claude_Memory](https://x.com/Claude_Memory)
|
||||
- **Official Discord**: [Join Discord](https://discord.com/invite/J4wttp9vDu)
|
||||
- **Author**: Alex Newman ([@thedotmack](https://github.com/thedotmack))
|
||||
|
||||
---
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 29, 2025
|
||||
|
||||
**save-file-edit.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34270 | 10:45 PM | 🔵 | Save File Edit Hook Captures File Modifications as Tool Observations | ~495 |
|
||||
|
||||
**session-summary.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34268 | 10:44 PM | 🔵 | Session Summary Hook Generates Summaries and Updates Context on Stop | ~498 |
|
||||
|
||||
**save-observation.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34267 | 10:44 PM | 🔵 | Save Observation Hook Captures MCP and Shell Executions | ~494 |
|
||||
|
||||
**context-inject.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34266 | 10:44 PM | 🔵 | Context Inject Hook Refreshes Memory Context Before Prompt Submission | ~498 |
|
||||
| #34165 | 9:41 PM | 🔵 | Context Injection Hook Implementation for Cursor | ~466 |
|
||||
|
||||
**session-init.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34264 | 10:43 PM | 🔵 | Session Init Hook Initializes Sessions on Prompt Submission | ~514 |
|
||||
|
||||
**common.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34261 | 10:43 PM | 🔵 | Cursor Hooks Common Shell Library Provides Core Utilities | ~381 |
|
||||
| #34237 | 10:31 PM | 🔄 | Removed arbitrary array index validation from json_get function | ~421 |
|
||||
|
||||
**STANDALONE-SETUP.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34258 | 10:39 PM | ✅ | Updated STANDALONE-SETUP.md to recommend user-level installation | ~265 |
|
||||
| #34257 | " | 🔵 | Claude-Mem Command Reference for Cursor Integration | ~245 |
|
||||
| #34256 | " | ✅ | Updated STANDALONE-SETUP.md to recommend user-level installation | ~305 |
|
||||
| #34252 | 10:38 PM | 🔵 | Cursor Hooks Installation and Worker Setup Process | ~258 |
|
||||
| #34224 | 10:14 PM | ✅ | Completed Bun Migration by Updating All Windows Commands | ~392 |
|
||||
| #34223 | " | ✅ | Updated Windows Installation Commands to Use Bun | ~354 |
|
||||
| #34222 | 10:13 PM | ✅ | Updated Quick Reference Table to Use Bun Commands | ~361 |
|
||||
| #34221 | " | ✅ | Updated Step 5 Status Check Command to Use Bun | ~353 |
|
||||
| #34220 | " | ✅ | Updated Step 4 Worker Start Command to Use Bun | ~360 |
|
||||
| #34219 | 10:12 PM | ✅ | Updated Step 3 Hook Installation Commands to Use Bun | ~322 |
|
||||
| #34218 | " | ✅ | Updated STANDALONE-SETUP Step 1 Commands to Use Bun Instead of NPM | ~357 |
|
||||
| #34217 | " | ✅ | Updated STANDALONE-SETUP Prerequisites to Require Bun Runtime | ~341 |
|
||||
| #34215 | 10:08 PM | 🔵 | Retrieved Detailed Cursor Integration Implementation History | ~676 |
|
||||
| #34214 | 10:07 PM | 🔵 | Cursor Integration Feature Set Discovered via Memory Search | ~427 |
|
||||
|
||||
**QUICKSTART.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34255 | 10:39 PM | ✅ | Quickstart Documentation Reordered to Recommend User-Level Installation First | ~265 |
|
||||
| #34251 | 10:38 PM | 🔵 | Quickstart Documentation Shows CLI-Based Installation Method | ~257 |
|
||||
| #34225 | 10:14 PM | ✅ | Updated QUICKSTART Worker Restart Command to Use Bun | ~308 |
|
||||
|
||||
**README.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34254 | 10:38 PM | ✅ | Updated README to recommend user-level installation over project-level | ~345 |
|
||||
| #34253 | " | 🔵 | Cursor hooks installation documented with quick install CLI and manual options | ~327 |
|
||||
| #34250 | " | 🔵 | Documentation references installation types and project-level concepts | ~311 |
|
||||
| #34226 | 10:14 PM | ✅ | Updated README Quick Install Commands to Use Bun | ~311 |
|
||||
|
||||
**install.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34249 | 10:37 PM | ✅ | Install Script Usage Message Updated to Recommend User-Level Installation | ~256 |
|
||||
| #34248 | " | ✅ | Marked user-level installation as recommended in install script | ~255 |
|
||||
| #34246 | " | ✅ | Simplified path rewriting logic after enterprise mode removal | ~291 |
|
||||
| #34245 | " | ✅ | Simplified conditional logic after removing enterprise installation code | ~266 |
|
||||
| #34244 | 10:36 PM | ✅ | Removed enterprise installation mode from cursor-hooks installer | ~298 |
|
||||
| #34243 | " | ✅ | Removed enterprise installation option from Cursor hooks installer | ~292 |
|
||||
| #34242 | " | 🔵 | Cursor hooks installation script copies and configures hooks with path adjustments | ~387 |
|
||||
| #34240 | 10:33 PM | 🔵 | Cursor hooks installation paths and requirements vary by deployment mode | ~312 |
|
||||
| #34239 | " | 🔵 | Cursor hooks installation supports enterprise mode | ~240 |
|
||||
| #34233 | 10:26 PM | ⚖️ | Implemented PR 493 fixes with mixed necessity and complexity trade-offs | ~610 |
|
||||
| #34232 | " | 🔵 | PR 493 review identified security and concurrency issues requiring fixes | ~560 |
|
||||
| #34228 | 10:21 PM | 🔴 | Fixed sed portability issue in install.sh | ~318 |
|
||||
|
||||
**common.ps1**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34238 | 10:31 PM | 🔄 | Rollback complete: simplified over-engineered concurrency and validation code | ~434 |
|
||||
| #34231 | 10:25 PM | 🔴 | Fixed race conditions and security vulnerabilities in Cursor integration | ~562 |
|
||||
|
||||
**hooks.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34184 | 9:51 PM | 🔵 | Cursor Hooks Configuration Schema | ~375 |
|
||||
|
||||
### Dec 31, 2025
|
||||
|
||||
**session-init.sh**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34675 | 3:37 PM | 🔵 | API Endpoint /api/sessions/init Expects contentSessionId Parameter | ~401 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38078 | 9:54 PM | ✅ | CLAUDE.md Documentation Cleanup - 1,233 Lines Removed Across 18 Files | ~590 |
|
||||
|
||||
**INTEGRATION.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37995 | 9:01 PM | 🔵 | CLAUDE_MEM_WORKER_HOST setting implementation pattern | ~304 |
|
||||
|
||||
**README.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37990 | 9:00 PM | 🔵 | CLAUDE_MEM_WORKER_HOST setting used across 19 files | ~289 |
|
||||
|
||||
### Jan 7, 2026
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38195 | 7:35 PM | ✅ | Context-hook enhanced with promotional footer and user-message-hook removed from SessionStart | ~376 |
|
||||
| #38194 | " | 🔵 | Working tree contains 10 modified files ready for commit | ~303 |
|
||||
</claude-mem-context>
|
||||
+77
-1
@@ -3,5 +3,81 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
### Nov 6, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #4241 | 11:19 PM | 🟣 | Object-Oriented Architecture Design Document Created | ~662 |
|
||||
| #4240 | 11:11 PM | 🟣 | Worker Service Rewrite Blueprint Created | ~541 |
|
||||
| #4239 | 11:07 PM | 🟣 | Comprehensive Worker Service Performance Analysis Document Created | ~541 |
|
||||
| #4238 | 10:59 PM | 🔵 | Overhead Analysis Document Checked | ~203 |
|
||||
|
||||
### Nov 7, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #4609 | 6:33 PM | ✅ | PR #69 Successfully Merged to Main Branch | ~516 |
|
||||
| #4600 | 6:31 PM | 🟣 | Added Worker Service Documentation Suite | ~441 |
|
||||
| #4597 | " | 🔄 | Worker Service Refactored to Object-Oriented Architecture | ~473 |
|
||||
|
||||
### Nov 8, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #5539 | 10:20 PM | 🔵 | Harsh critical audit of context-hook reveals systematic anti-patterns | ~3154 |
|
||||
| #5497 | 9:29 PM | 🔵 | Harsh critical audit of context-hook reveals systematic anti-patterns | ~2815 |
|
||||
| #5495 | 9:28 PM | 🔵 | Context Hook Audit Reveals Project Anti-Patterns | ~660 |
|
||||
| #5476 | 9:17 PM | 🔵 | Critical Code Audit Identified 14 Anti-Patterns in Context Hook | ~887 |
|
||||
| #5391 | 8:45 PM | 🔵 | Critical Code Quality Audit of Context Hook Implementation | ~720 |
|
||||
| #5150 | 7:37 PM | 🟣 | Troubleshooting Skill Added to Claude-Mem Plugin | ~427 |
|
||||
|
||||
### Nov 9, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #6161 | 11:55 PM | 🔵 | YC W26 Application Research and Preparation Completed for Claude-Mem | ~1628 |
|
||||
| #6155 | 11:47 PM | ✅ | Comprehensive Y Combinator Winter 2026 Application Notes Created | ~1045 |
|
||||
| #5979 | 7:58 PM | 🔵 | Smart Contextualization Feature Architecture | ~560 |
|
||||
| #5971 | 7:49 PM | 🔵 | Hooks Reference Documentation Structure | ~448 |
|
||||
| #5929 | 7:08 PM | ✅ | Documentation Updates for v5.4.0 Skill-Based Search Migration | ~604 |
|
||||
| #5927 | " | ✅ | Updated Configuration Documentation for Skill-Based Search | ~497 |
|
||||
| #5920 | 7:05 PM | ✅ | Renamed Architecture Documentation File Reference | ~271 |
|
||||
|
||||
### Nov 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #11515 | 8:22 PM | 🔵 | Smart Contextualization Architecture Retrieved with Command Hook Pattern Details | ~502 |
|
||||
|
||||
### Dec 8, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22294 | 9:43 PM | 🔵 | Documentation Site Structure Located | ~359 |
|
||||
|
||||
### Dec 12, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24430 | 8:27 PM | ✅ | Removed Final Platform Check Reference from Linux Section | ~320 |
|
||||
| #24429 | " | ✅ | Final Platform Check Reference Removal from Linux Section | ~274 |
|
||||
| #24428 | " | ✅ | Corrected Second Line Number Reference for Migration Marker Logic | ~267 |
|
||||
| #24427 | 8:26 PM | ✅ | Updated Line Number Reference for PM2 Cleanup Implementation | ~260 |
|
||||
| #24426 | " | ✅ | Removed Platform Check from Manual Marker Deletion Scenario | ~338 |
|
||||
| #24425 | " | ✅ | Removed Platform Check from Fresh Install Scenario Flow | ~314 |
|
||||
| #24424 | 8:25 PM | ✅ | Renumbered Manual Marker Deletion Scenario | ~285 |
|
||||
| #24423 | " | ✅ | Renumbered Fresh Install Scenario | ~243 |
|
||||
| #24422 | " | ✅ | Removed Obsolete Windows Platform Detection Scenario | ~311 |
|
||||
| #24421 | " | ✅ | Removed Platform Check from macOS Migration Documentation | ~294 |
|
||||
| #24420 | 8:24 PM | ✅ | Platform Check Removed from Migration Documentation | ~288 |
|
||||
| #24417 | 8:16 PM | ✅ | Code Reference Example Updated to Reflect Actual Cross-Platform Implementation | ~366 |
|
||||
| #24416 | " | ✅ | Architecture Decision Documentation Updated to Reflect Cross-Platform PM2 Cleanup Rationale | ~442 |
|
||||
| #24415 | 8:15 PM | ✅ | Migration Marker Lifecycle Documentation Updated for Unified Cross-Platform Behavior | ~463 |
|
||||
| #24414 | " | ✅ | Platform Comparison Table Updated to Reflect Unified Cross-Platform Migration | ~351 |
|
||||
| #24413 | " | ✅ | Windows Platform-Specific Documentation Completely Rewritten for Unified Migration | ~428 |
|
||||
| #24412 | " | ✅ | User Experience Timeline Updated for Cross-Platform PM2 Cleanup | ~291 |
|
||||
| #24411 | 8:14 PM | ✅ | Migration Marker Lifecycle Documentation Updated for All Platforms | ~277 |
|
||||
| #24410 | " | ✅ | Marker File Platform Behavior Documentation Updated for Unified Migration | ~282 |
|
||||
| #24409 | " | ✅ | Migration Steps Documentation Updated for Cross-Platform PM2 Cleanup | ~278 |
|
||||
| #24408 | 8:13 PM | ✅ | PM2 Migration Documentation Updated to Remove Windows Platform Check | ~280 |
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,213 @@
|
||||
# Claude-Mem PR Shipping Report
|
||||
*Generated: 2026-02-04*
|
||||
|
||||
## Executive Summary
|
||||
|
||||
6 PRs analyzed for shipping readiness. **1 is ready to merge**, 4 have conflicts, 1 is too large for easy review.
|
||||
|
||||
| PR | Title | Status | Recommendation |
|
||||
|----|-------|--------|----------------|
|
||||
| **#856** | Idle timeout for zombie processes | ✅ **MERGEABLE** | **Ship it** |
|
||||
| #700 | Windows Terminal popup fix | ⚠️ Conflicts | Rebase, then ship |
|
||||
| #722 | In-process worker architecture | ⚠️ Conflicts | Rebase, high impact |
|
||||
| #657 | generate/clean CLI commands | ⚠️ Conflicts | Rebase, then ship |
|
||||
| #863 | Ragtime email investigation | 🔍 Needs review | Research pending |
|
||||
| #464 | Sleep Agent Pipeline (contributor) | 🔴 Too large | Request split or dedicated review |
|
||||
|
||||
---
|
||||
|
||||
## Ready to Ship
|
||||
|
||||
### PR #856: Idle Timeout for Zombie Observer Processes
|
||||
**Status:** ✅ MERGEABLE (no conflicts)
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Additions | 928 |
|
||||
| Deletions | 171 |
|
||||
| Files | 8 |
|
||||
| Risk | Low-Medium |
|
||||
|
||||
**What it does:**
|
||||
- Adds 3-minute idle timeout to `SessionQueueProcessor`
|
||||
- Prevents zombie observer processes that were causing 13.4GB swap usage
|
||||
- Processes exit gracefully after inactivity instead of waiting forever
|
||||
|
||||
**Why ship it:**
|
||||
- Fixes real user-reported issue (79 zombie processes)
|
||||
- Well-tested (11 new tests, 440 lines of test coverage)
|
||||
- Clean implementation, preventive approach
|
||||
- Supersedes PR #848's reactive cleanup
|
||||
- No conflicts, ready to merge
|
||||
|
||||
**Review notes:**
|
||||
- 1 Greptile bot comment (addressed)
|
||||
- Race condition fix included
|
||||
- Enhanced logging added
|
||||
|
||||
---
|
||||
|
||||
## Needs Rebase (Have Conflicts)
|
||||
|
||||
### PR #700: Windows Terminal Popup Fix
|
||||
**Status:** ⚠️ CONFLICTING
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Additions | 187 |
|
||||
| Deletions | 399 |
|
||||
| Files | 8 |
|
||||
| Risk | Medium |
|
||||
|
||||
**What it does:**
|
||||
- Eliminates Windows Terminal popup by removing spawn-based daemon
|
||||
- Worker `start` command becomes daemon directly (no child spawn)
|
||||
- Removes `restart` command (users do `stop` then `start`)
|
||||
- Net simplification: -212 lines
|
||||
|
||||
**Breaking changes:**
|
||||
- `restart` command removed
|
||||
|
||||
**Review status:**
|
||||
- ✅ 1 APPROVAL from @volkanfirat (Jan 15, 2026)
|
||||
|
||||
**Action needed:** Resolve conflicts, then ready to ship.
|
||||
|
||||
---
|
||||
|
||||
### PR #722: In-Process Worker Architecture
|
||||
**Status:** ⚠️ CONFLICTING
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Additions | 869 |
|
||||
| Deletions | 4,658 |
|
||||
| Files | 112 |
|
||||
| Risk | High |
|
||||
|
||||
**What it does:**
|
||||
- Hook processes become the worker (no separate daemon spawning)
|
||||
- First hook that needs worker becomes the worker
|
||||
- Eliminates Windows spawn issues ("NO SPAWN" rule)
|
||||
- 761 tests pass
|
||||
|
||||
**Architectural impact:** HIGH
|
||||
- Fundamentally changes worker lifecycle
|
||||
- Hook processes stay alive (they ARE the worker)
|
||||
- First hook wins port 37777, others use HTTP
|
||||
|
||||
**Action needed:** Resolve conflicts. Consider relationship with PR #700 (both touch worker architecture).
|
||||
|
||||
---
|
||||
|
||||
### PR #657: Generate/Clean CLI Commands
|
||||
**Status:** ⚠️ CONFLICTING
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Additions | 1,184 |
|
||||
| Deletions | 5,057 |
|
||||
| Files | 104 |
|
||||
| Risk | Medium |
|
||||
|
||||
**What it does:**
|
||||
- Adds `claude-mem generate` and `claude-mem clean` CLI commands
|
||||
- Fixes validation bugs (deleted folders recreated from stale DB)
|
||||
- Fixes Windows path handling
|
||||
- Adds automatic shell alias installation
|
||||
- Disables subdirectory CLAUDE.md files by default
|
||||
|
||||
**Breaking changes:**
|
||||
- Default behavior change: folder CLAUDE.md now disabled by default
|
||||
|
||||
**Action needed:** Resolve conflicts, complete Windows testing.
|
||||
|
||||
---
|
||||
|
||||
## Needs Attention
|
||||
|
||||
### PR #863: Ragtime Email Investigation
|
||||
**Status:** 🔍 Research pending
|
||||
|
||||
Research agent did not return results. Manual review needed.
|
||||
|
||||
---
|
||||
|
||||
### PR #464: Sleep Agent Pipeline (Contributor: @laihenyi)
|
||||
**Status:** 🔴 Too large for effective review
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Additions | 15,430 |
|
||||
| Deletions | 469 |
|
||||
| Files | 73 |
|
||||
| Wait time | 37+ days |
|
||||
| Risk | High |
|
||||
|
||||
**What it does:**
|
||||
- Sleep Agent Pipeline with memory tiering
|
||||
- Supersession detection
|
||||
- Session Statistics API (`/api/session/:id/stats`)
|
||||
- StatusLine + PreCompact hooks
|
||||
- Context Generator improvements
|
||||
- Self-healing CI workflow
|
||||
|
||||
**Concerns:**
|
||||
| Issue | Details |
|
||||
|-------|---------|
|
||||
| 🔴 Size | 15K+ lines is too large for effective review |
|
||||
| 🔴 SupersessionDetector | Single file with 1,282 additions |
|
||||
| 🟡 No tests visible | Test plan checkboxes unchecked |
|
||||
| 🟡 Self-healing CI | Auto-fix workflow could cause infinite commit loops |
|
||||
| 🟡 Serena config | Adds `.serena/` tooling |
|
||||
|
||||
**Recommendation:**
|
||||
1. **Option A:** Request contributor split into 4-5 smaller PRs
|
||||
2. **Option B:** Allocate dedicated review time (several hours)
|
||||
3. **Option C:** Cherry-pick specific features (hooks, stats API)
|
||||
|
||||
**Note:** Contributor has been waiting 37+ days. Deserves response either way.
|
||||
|
||||
---
|
||||
|
||||
## Shipping Strategy
|
||||
|
||||
### Phase 1: Quick Wins (This Week)
|
||||
1. **Merge #856** — Ready now, fixes real user issue
|
||||
2. **Rebase #700** — Has approval, Windows fix needed
|
||||
3. **Rebase #657** — Useful CLI commands
|
||||
|
||||
### Phase 2: Architecture (Careful Review)
|
||||
4. **Review #722** — High impact, conflicts with #700 approach?
|
||||
- Both PRs eliminate spawning but in different ways
|
||||
- May need to pick one approach
|
||||
|
||||
### Phase 3: Contributor PR
|
||||
5. **Respond to #464** — Options:
|
||||
- Ask for split
|
||||
- Schedule dedicated review
|
||||
- Cherry-pick subset
|
||||
|
||||
### Phase 4: Investigation
|
||||
6. **Manual review #863** — Ragtime email feature
|
||||
|
||||
---
|
||||
|
||||
## Conflict Resolution Order
|
||||
|
||||
Since multiple PRs have conflicts, suggested rebase order:
|
||||
|
||||
1. **#856** (merge first — no conflicts)
|
||||
2. **#700** (rebase onto main after #856)
|
||||
3. **#657** (rebase onto main after #700)
|
||||
4. **#722** (rebase last — may conflict with #700 architecturally)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Ready | Conflicts | Needs Work |
|
||||
|-------|-----------|------------|
|
||||
| 1 PR (#856) | 3 PRs (#700, #722, #657) | 2 PRs (#464, #863) |
|
||||
|
||||
**Immediate action:** Merge #856, then rebase the conflict PRs in order.
|
||||
@@ -1,77 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 13, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #7806 | 4:54 PM | 🔵 | PR #101 Enhancement: Continuation Prompt Token Reduction | ~634 |
|
||||
|
||||
### Nov 16, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #9976 | 11:35 PM | 🔵 | Endless Mode Architecture Plan Documented | ~661 |
|
||||
| #9967 | 11:18 PM | ⚖️ | Endless Mode Architecture: Immutable Storage with Ephemeral Transform | ~217 |
|
||||
|
||||
### Nov 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #10131 | 1:22 AM | 🔵 | Endless Mode Token Economics Analysis Output: Complete Infrastructure Impact | ~542 |
|
||||
| #10130 | " | ✅ | Integration of Actual Compute Savings Analysis into Main Execution Flow | ~258 |
|
||||
| #10129 | " | 🔵 | Prompt Caching Economics: User Cost vs. Anthropic Compute Cost Divergence | ~451 |
|
||||
| #10126 | 1:19 AM | 🔴 | Fix Return Statement Variable Names in playTheTapeThrough Function | ~313 |
|
||||
| #10125 | " | ✅ | Redesign Timeline Display to Show Fresh/Cached Token Breakdown and Real Dollar Costs | ~501 |
|
||||
| #10124 | " | ✅ | Replace Estimated Cost Model with Actual Caching-Based Costs in Anthropic Scale Analysis | ~516 |
|
||||
| #10123 | " | ✅ | Pivot Session Length Comparison Table from Token to Cost Metrics | ~413 |
|
||||
| #10122 | " | ✅ | Add Dual Reporting: Token Count vs Actual Cost in Comparison Output | ~410 |
|
||||
| #10121 | 1:18 AM | ✅ | Apply Prompt Caching Cost Model to Endless Mode Calculation Function | ~501 |
|
||||
| #10120 | " | ✅ | Integrate Prompt Caching Cost Calculations into Without-Endless-Mode Function | ~426 |
|
||||
| #10119 | " | ✅ | Display Prompt Caching Pricing in Initial Calculator Output | ~297 |
|
||||
| #10118 | " | ✅ | Add Prompt Caching Pricing Model to Token Economics Calculator | ~316 |
|
||||
| #10115 | 1:15 AM | 🟣 | Token Economics Calculator for Endless Mode Sessions | ~465 |
|
||||
| #10013 | 12:13 AM | 🔵 | Duplicate Agent SDK TypeScript Reference Documentation | ~340 |
|
||||
| #10012 | " | 🔵 | Agent SDK TypeScript API Reference Complete | ~349 |
|
||||
|
||||
### Nov 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #11738 | 11:51 PM | ⚖️ | Comprehensive Architecture Document Created for Phase 1 | ~868 |
|
||||
| #11711 | 11:44 PM | 🔵 | Language Model Tool Documentation Index | ~282 |
|
||||
| #11710 | " | 🔵 | Language Model Tool API Implementation Guide | ~718 |
|
||||
| #11709 | 11:43 PM | 🔵 | Comprehensive Copilot Extension Implementation Plan | ~624 |
|
||||
| #11708 | " | 🔵 | VS Code Chat Sample Documentation Unavailable | ~327 |
|
||||
| #11707 | " | 🔵 | VS Code Language Model API Structure and Capabilities | ~515 |
|
||||
| #11705 | " | ⚖️ | VS Code Extension Development Planning Phase Initiated | ~327 |
|
||||
| #11206 | 3:01 PM | 🔵 | mem-search skill architecture and migration details retrieved in full format | ~538 |
|
||||
|
||||
### Nov 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #15538 | 8:36 PM | 🔵 | Context Document for Landing Page Refinements | ~381 |
|
||||
| #15314 | 5:04 PM | 🔵 | Endless Mode Documentation Post Retrieved with 156 Lines | ~671 |
|
||||
|
||||
### Dec 20, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31257 | 8:58 PM | ⚖️ | Eight Conflict Detection Hypotheses Evaluated with Simulation Results | ~525 |
|
||||
| #31256 | " | 🔵 | Supersession vs Conflict Detection Feature Analysis | ~515 |
|
||||
|
||||
### Dec 30, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34520 | 2:34 PM | 🔵 | V2 Example Code Demonstrates All Key Patterns | ~537 |
|
||||
|
||||
### Jan 7, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38209 | 7:39 PM | 🔵 | Claude Code Hooks System Architecture and Usage | ~491 |
|
||||
</claude-mem-context>
|
||||
@@ -1,38 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 12, 2025
|
||||
|
||||
**README.ar.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24246 | 2:43 AM | 🟣 | Comprehensive Translation System Added with 22 Language READMEs | ~386 |
|
||||
|
||||
**README.zh.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24241 | 2:35 AM | ✅ | Internationalized README Files Moved to Dedicated i18n Directory | ~284 |
|
||||
|
||||
### Dec 22, 2025
|
||||
|
||||
**README.ja.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31948 | 8:08 PM | ✅ | Batch Updated All Translation READMEs with Language Navigation | ~400 |
|
||||
|
||||
**README.zh.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31947 | 8:07 PM | ✅ | Added Language Navigation Menu to Chinese Translation README | ~412 |
|
||||
| #31945 | 8:06 PM | 🟣 | Multi-language navigation added to internationalized README files | ~386 |
|
||||
| #31942 | 8:01 PM | 🔵 | Internationalization Documentation Coverage | ~315 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
**README.*.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33540 | 10:55 PM | 🔵 | Grep search found mem-search references in internationalized documentation | ~577 |
|
||||
</claude-mem-context>
|
||||
+55
-47
@@ -1,5 +1,4 @@
|
||||
🌐 هذه ترجمة آلية. نرحب بالتصحيحات من المجتمع!
|
||||
|
||||
<section dir="rtl">
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
@@ -14,6 +13,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +33,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -43,7 +44,8 @@
|
||||
<a href="README.no.md">🇳🇴 Norsk</a>
|
||||
</p>
|
||||
|
||||
<h4 align="center">نظام ضغط الذاكرة المستمرة المبني لـ <a href="https://claude.com/claude-code" target="_blank">Claude Code</a>.</h4>
|
||||
<h4 align="center">أداة إضافية لـ <a href="https://claude.com/claude-code" target="_blank">Claude Code</a> تعمل على أتمتة تسجيل معلومات الجلسات السابقه، وضغطها, ثم حقن السياق ذي الصلة في الجلسات المستقبلية.
|
||||
</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
@@ -80,25 +82,27 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#البدء-السريع">البدء السريع</a> •
|
||||
<a href="#كيف-يعمل">كيف يعمل</a> •
|
||||
<a href="#أدوات-البحث-mcp">أدوات البحث</a> •
|
||||
<a href="#التوثيق">التوثيق</a> •
|
||||
<a href="#الإعدادات">الإعدادات</a> •
|
||||
<a href="#استكشاف-الأخطاء-وإصلاحها">استكشاف الأخطاء وإصلاحها</a> •
|
||||
<a href="#الترخيص">الترخيص</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
يحافظ Claude-Mem بسلاسة على السياق عبر الجلسات من خلال التقاط الملاحظات حول استخدام الأدوات تلقائيًا، وإنشاء ملخصات دلالية، وإتاحتها للجلسات المستقبلية. هذا يمكّن Claude من الحفاظ على استمرارية المعرفة حول المشاريع حتى بعد انتهاء الجلسات أو إعادة الاتصال.
|
||||
<a href="#بداية-سريعة">بداية سريعة</a> •
|
||||
<a href="#كيف-يعمل">كيف يعمل</a> •
|
||||
<a href="#أدوات-البحث-mcp-search-tools">أدوات البحث</a> •
|
||||
<a href="#المستندات">التوثيق</a> •
|
||||
<a href="#الإعدادات">الإعدادات</a> •
|
||||
<a href="#استكشاف-الأخطاء-وإصلاحها">استكشاف الأخطاء وإصلاحها</a> •
|
||||
<a href="#الترخيص-license">الترخيص</a>
|
||||
</p>
|
||||
|
||||
<p align="center" dir="rtl">
|
||||
Claude-Mem هو نظام متطور مصمم لضغط وحفظ الذاكرة لسياق عمل Claude Code. وظيفته الأساسية هي جعل "كلود" يتذكر ما فعله في جلسات العمل السابقة بسلاسة، عبر تسجيل تحركاته، وإنشاء ملخصات ذكية، واستدعائها في الجلسات المستقبلية. هذا يضمن عدم ضياع سياق المشروع حتى لو أغلقت البرنامج وفتحته لاحقاً.
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## البدء السريع
|
||||
## بداية سريعة
|
||||
|
||||
ابدأ جلسة Claude Code جديدة في الطرفية وأدخل الأوامر التالية:
|
||||
للبدء، افتح "Claude Code" في مبنى الأوامر (Terminal) واكتب الأوامر التالية:
|
||||
<div dir="ltr" align="left">
|
||||
|
||||
```
|
||||
> /plugin marketplace add thedotmack/claude-mem
|
||||
@@ -106,32 +110,34 @@
|
||||
> /plugin install claude-mem
|
||||
```
|
||||
|
||||
أعد تشغيل Claude Code. سيظهر السياق من الجلسات السابقة تلقائيًا في الجلسات الجديدة.
|
||||
</div>
|
||||
|
||||
بمجرد إعادة تشغيل Claude Code، سيتم استدعاء السياق من الجلسات السابقة تلقائيا عند الحاجة.
|
||||
|
||||
**الميزات الرئيسية:**
|
||||
|
||||
- 🧠 **ذاكرة مستمرة** - يبقى السياق عبر الجلسات
|
||||
- 📊 **الكشف التدريجي** - استرجاع الذاكرة بطبقات مع رؤية تكلفة الرموز
|
||||
- 🔍 **بحث قائم على المهارات** - استعلم عن تاريخ مشروعك باستخدام مهارة mem-search
|
||||
- 🖥️ **واجهة مستخدم ويب** - بث الذاكرة المباشر على http://localhost:37777
|
||||
- 💻 **مهارة Claude Desktop** - ابحث في الذاكرة من محادثات Claude Desktop
|
||||
- 🔒 **التحكم في الخصوصية** - استخدم وسوم `<private>` لاستبعاد المحتوى الحساس من التخزين
|
||||
- ⚙️ **إعدادات السياق** - تحكم دقيق في السياق الذي يتم حقنه
|
||||
- 🤖 **تشغيل تلقائي** - لا يتطلب تدخلاً يدويًا
|
||||
- 🔗 **الاستشهادات** - رجوع إلى الملاحظات السابقة باستخدام المعرفات (الوصول عبر http://localhost:37777/api/observation/{id} أو عرض الكل في عارض الويب على http://localhost:37777)
|
||||
- 🧪 **قناة تجريبية** - جرّب الميزات التجريبية مثل Endless Mode عبر تبديل الإصدار
|
||||
- 🧠 **ذاكرة مستديمه**: سياق عملك لا ينتهي بانتهاء الجلسة، بل ينتقل معك للجلسة التالية.
|
||||
- 📊 **الكشف التدريجي** (Progressive Disclosure): نظام ذكي يستدعي المعلومات على طبقات، مما يمنحك رؤية واضحة لاستهلاك الـ "Tokens" (التكلفة).
|
||||
- 🔍 **بحث سريع** - استعلم عن سجل مشروعك باستخدام خاصية `mem-search`.
|
||||
- 🖥️ **واجهة مستخدم ويب** - رؤية معلومات الذاكرة مع تحديث فوري عبر المتصفح من خلال الرابط: http://localhost:37777
|
||||
- 💻 **تكامل مع Claude Desktop** - إمكانية البحث في الذاكرة مباشرة من واجهة Claude المكتبية
|
||||
- 🔒 **التحكم في الخصوصية** - دعم وسم `<private>` لمنع النظام من تخزين أي معلومات حساسة.
|
||||
- ⚙️ **إعدادات السياق** - تحكم دقيق في السياق (context) التي سيتم حقنها في سياق المحادثة.
|
||||
- 🤖 **أتمتة كاملة:** - النظام يعمل في الخلفية دون الحاجة لتدخل يدوي منك.
|
||||
- 🔗 **الاستشهادات** - رجوع إلى الملاحظات السابقة باستخدام (http://localhost:37777/api/observation/{id} أو عرض جميع المعلومات على http://localhost:37777)
|
||||
- 🧪 **مزايا التجريبيه** - تجربة مميزات مثل "الوضع اللانهائي" (Endless Mode).
|
||||
|
||||
---
|
||||
|
||||
## التوثيق
|
||||
## المستندات
|
||||
|
||||
📚 **[عرض التوثيق الكامل](docs/)** - تصفح مستندات markdown على GitHub
|
||||
📚 **[عرض التوثيق الكامل](https://docs.claude-mem.ai/)** - تصفح على الموقع الرسمي
|
||||
|
||||
### البدء
|
||||
|
||||
- **[دليل التثبيت](https://docs.claude-mem.ai/installation)** - البدء السريع والتثبيت المتقدم
|
||||
- **[دليل الاستخدام](https://docs.claude-mem.ai/usage/getting-started)** - كيف يعمل Claude-Mem تلقائيًا
|
||||
- **[أدوات البحث](https://docs.claude-mem.ai/usage/search-tools)** - استعلم عن تاريخ مشروعك باللغة الطبيعية
|
||||
- **[أدوات البحث](https://docs.claude-mem.ai/usage/search-tools)** - استعلم عن سجل مشروعك بلغتك
|
||||
- **[الميزات التجريبية](https://docs.claude-mem.ai/beta-features)** - جرّب الميزات التجريبية مثل Endless Mode
|
||||
|
||||
### أفضل الممارسات
|
||||
@@ -142,9 +148,9 @@
|
||||
### البنية المعمارية
|
||||
|
||||
- **[نظرة عامة](https://docs.claude-mem.ai/architecture/overview)** - مكونات النظام وتدفق البيانات
|
||||
- **[تطور البنية المعمارية](https://docs.claude-mem.ai/architecture-evolution)** - الرحلة من v3 إلى v5
|
||||
- **[بنية الخطافات](https://docs.claude-mem.ai/hooks-architecture)** - كيف يستخدم Claude-Mem خطافات دورة الحياة
|
||||
- **[مرجع الخطافات](https://docs.claude-mem.ai/architecture/hooks)** - شرح 7 سكريبتات خطافات
|
||||
- **[تطور البنية المعمارية](https://docs.claude-mem.ai/architecture-evolution)** - تطور المعمارية من v3 إلى v5
|
||||
- **[بنية برامج الربط (Hooks)](https://docs.claude-mem.ai/hooks-architecture)** - كيف يستخدم Claude-Mem خطافات دورة الحياة
|
||||
- **[مرجع برامج الربط (Hooks)](https://docs.claude-mem.ai/architecture/hooks)** - شرح 7 سكريبتات خطافات
|
||||
- **[خدمة العامل](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API وإدارة Bun
|
||||
- **[قاعدة البيانات](https://docs.claude-mem.ai/architecture/database)** - مخطط SQLite وبحث FTS5
|
||||
- **[بنية البحث](https://docs.claude-mem.ai/architecture/search-architecture)** - البحث المختلط مع قاعدة بيانات المتجهات Chroma
|
||||
@@ -161,24 +167,23 @@
|
||||
|
||||
**المكونات الأساسية:**
|
||||
|
||||
1. **5 خطافات دورة الحياة** - SessionStart، UserPromptSubmit، PostToolUse، Stop، SessionEnd (6 سكريبتات خطافات)
|
||||
2. **تثبيت ذكي** - فاحص التبعيات المخزنة مؤقتًا (سكريبت ما قبل الخطاف، ليس خطاف دورة حياة)
|
||||
1. **5 برامج ربط (Hooks)** - SessionStart، UserPromptSubmit، PostToolUse، Stop، SessionEnd
|
||||
2. **تثبيت ذكي** - فاحص التبعيات المخزنة مؤقتًا
|
||||
3. **خدمة العامل** - HTTP API على المنفذ 37777 مع واجهة مستخدم عارض الويب و10 نقاط نهاية للبحث، تديرها Bun
|
||||
4. **قاعدة بيانات SQLite** - تخزن الجلسات، الملاحظات، الملخصات
|
||||
5. **مهارة mem-search** - استعلامات اللغة الطبيعية مع الكشف التدريجي
|
||||
6. **قاعدة بيانات المتجهات Chroma** - البحث المختلط الدلالي + الكلمات المفتاحية لاسترجاع السياق الذكي
|
||||
6. **قاعدة بيانات المتجهات Chroma** - البحث الدلالي الهجين + الكلمات المفتاحية لاسترجاع السياق الذكي
|
||||
|
||||
انظر [نظرة عامة على البنية المعمارية](https://docs.claude-mem.ai/architecture/overview) للتفاصيل.
|
||||
|
||||
---
|
||||
|
||||
## مهارة mem-search
|
||||
|
||||
## أدوات البحث (MCP Search Tools)
|
||||
يوفر Claude-Mem بحثًا ذكيًا من خلال مهارة mem-search التي تُستدعى تلقائيًا عندما تسأل عن العمل السابق:
|
||||
|
||||
**كيف يعمل:**
|
||||
- فقط اسأل بشكل طبيعي: *"ماذا فعلنا في الجلسة الأخيرة؟"* أو *"هل أصلحنا هذا الخطأ من قبل؟"*
|
||||
- يستدعي Claude تلقائيًا مهارة mem-search للعثور على السياق ذي الصلة
|
||||
- يستدعي Claude تلقائيًا خاصية mem-search للعثور على السياق ذي الصلة
|
||||
|
||||
**عمليات البحث المتاحة:**
|
||||
|
||||
@@ -193,7 +198,7 @@
|
||||
9. **الجدول الزمني حسب الاستعلام** - البحث عن الملاحظات والحصول على سياق الجدول الزمني حول أفضل تطابق
|
||||
10. **مساعدة API** - الحصول على توثيق API البحث
|
||||
|
||||
**أمثلة على استعلامات اللغة الطبيعية:**
|
||||
**أمثلة على الاستعلامات:**
|
||||
|
||||
```
|
||||
"What bugs did we fix last session?"
|
||||
@@ -219,8 +224,7 @@
|
||||
|
||||
- **Node.js**: 18.0.0 أو أعلى
|
||||
- **Claude Code**: أحدث إصدار مع دعم الإضافات
|
||||
- **Bun**: بيئة تشغيل JavaScript ومدير العمليات (يُثبت تلقائيًا إذا كان مفقودًا)
|
||||
- **uv**: مدير حزم Python للبحث المتجهي (يُثبت تلقائيًا إذا كان مفقودًا)
|
||||
- **Bun & uv**: (يتم تثبيتهما تلقائياً) لإدارة العمليات والبحث المتجه.
|
||||
- **SQLite 3**: للتخزين المستمر (مدمج)
|
||||
|
||||
---
|
||||
@@ -241,7 +245,7 @@
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
إذا واجهت مشكلات، صِف المشكلة لـ Claude وستقوم مهارة troubleshoot تلقائيًا بتشخيصها وتوفير الإصلاحات.
|
||||
إذا واجهت مشكلة، اشرحها لـ Claude وسيقوم بتشغيل خاصية troubleshoot لإصلاحها ذاتياً.
|
||||
|
||||
انظر **[دليل استكشاف الأخطاء وإصلاحها](https://docs.claude-mem.ai/troubleshooting)** للمشكلات الشائعة والحلول.
|
||||
|
||||
@@ -250,27 +254,29 @@
|
||||
## تقارير الأخطاء
|
||||
|
||||
أنشئ تقارير أخطاء شاملة باستخدام المولّد الآلي:
|
||||
<div align=left>
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm run bug-report
|
||||
```
|
||||
</div>
|
||||
|
||||
## المساهمة
|
||||
|
||||
المساهمات مرحب بها! يُرجى:
|
||||
|
||||
1. عمل Fork للمستودع
|
||||
2. إنشاء فرع ميزة
|
||||
1. عمل Fork للمشروع (Repository)
|
||||
2. إنشاء فرع (branch)
|
||||
3. إجراء التغييرات مع الاختبارات
|
||||
4. تحديث التوثيق
|
||||
4. تحديث المستندات عند الحاجه
|
||||
5. تقديم Pull Request
|
||||
|
||||
انظر [دليل التطوير](https://docs.claude-mem.ai/development) لسير عمل المساهمة.
|
||||
|
||||
---
|
||||
|
||||
## الترخيص
|
||||
## الترخيص (License)
|
||||
|
||||
هذا المشروع مرخص بموجب **ترخيص GNU Affero العام الإصدار 3.0** (AGPL-3.0).
|
||||
|
||||
@@ -298,4 +304,6 @@ npm run bug-report
|
||||
|
||||
---
|
||||
|
||||
**مبني باستخدام Claude Agent SDK** | **مدعوم بواسطة Claude Code** | **صُنع باستخدام TypeScript**
|
||||
**مبني باستخدام Claude Agent SDK** | **مدعوم بواسطة Claude Code** | **صُنع باستخدام TypeScript**
|
||||
|
||||
</section>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Claude Code পুনরায় চালু করুন। পূর্ব
|
||||
|
||||
## ডকুমেন্টেশন
|
||||
|
||||
📚 **[সম্পূর্ণ ডকুমেন্টেশন দেখুন](docs/)** - GitHub-এ markdown ডক্স ব্রাউজ করুন
|
||||
📚 **[সম্পূর্ণ ডকুমেন্টেশন দেখুন](https://docs.claude-mem.ai/)** - অফিসিয়াল ওয়েবসাইটে ব্রাউজ করুন
|
||||
|
||||
### শুরু করা
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Restartujte Claude Code. Kontext z předchozích sezení se automaticky objeví
|
||||
|
||||
## Dokumentace
|
||||
|
||||
📚 **[Zobrazit kompletní dokumentaci](docs/)** - Procházejte dokumentaci v markdown na GitHubu
|
||||
📚 **[Zobrazit kompletní dokumentaci](https://docs.claude-mem.ai/)** - Procházet na oficiálních stránkách
|
||||
|
||||
### Začínáme
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Genstart Claude Code. Kontekst fra tidligere sessioner vil automatisk vises i ny
|
||||
|
||||
## Dokumentation
|
||||
|
||||
📚 **[Se Fuld Dokumentation](docs/)** - Gennemse markdown-dokumenter på GitHub
|
||||
📚 **[Se Fuld Dokumentation](https://docs.claude-mem.ai/)** - Gennemse på den officielle hjemmeside
|
||||
|
||||
### Kom Godt I Gang
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Starten Sie Claude Code neu. Kontext aus vorherigen Sitzungen wird automatisch i
|
||||
|
||||
## Dokumentation
|
||||
|
||||
📚 **[Vollständige Dokumentation anzeigen](docs/)** - Markdown-Dokumentation auf GitHub durchsuchen
|
||||
📚 **[Vollständige Dokumentation anzeigen](https://docs.claude-mem.ai/)** - Auf der offiziellen Website durchsuchen
|
||||
|
||||
### Erste Schritte
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@
|
||||
|
||||
## Τεκμηρίωση
|
||||
|
||||
📚 **[Προβολή Πλήρους Τεκμηρίωσης](docs/)** - Περιήγηση στα markdown έγγραφα στο GitHub
|
||||
📚 **[Προβολή Πλήρους Τεκμηρίωσης](https://docs.claude-mem.ai/)** - Περιήγηση στον επίσημο ιστότοπο
|
||||
|
||||
### Ξεκινώντας
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -35,6 +36,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -127,7 +129,7 @@ Reinicia Claude Code. El contexto de sesiones anteriores aparecerá automáticam
|
||||
|
||||
## Documentación
|
||||
|
||||
📚 **[Ver Documentación Completa](docs/)** - Explora documentos markdown en GitHub
|
||||
📚 **[Ver Documentación Completa](https://docs.claude-mem.ai/)** - Navegar en el sitio web oficial
|
||||
|
||||
### Primeros Pasos
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +34,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -125,7 +127,7 @@ Käynnistä Claude Code uudelleen. Aiempien istuntojen konteksti ilmestyy automa
|
||||
|
||||
## Dokumentaatio
|
||||
|
||||
📚 **[Näytä täydellinen dokumentaatio](docs/)** - Selaa markdown-dokumentteja GitHubissa
|
||||
📚 **[Näytä täydellinen dokumentaatio](https://docs.claude-mem.ai/)** - Selaa virallisella verkkosivustolla
|
||||
|
||||
### Aloitus
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Redémarrez Claude Code. Le contexte des sessions précédentes apparaîtra auto
|
||||
|
||||
## Documentation
|
||||
|
||||
📚 **[Voir la documentation complète](docs/)** - Parcourez la documentation markdown sur GitHub
|
||||
📚 **[Voir la documentation complète](https://docs.claude-mem.ai/)** - Parcourir sur le site officiel
|
||||
|
||||
### Pour commencer
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +34,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -125,7 +127,7 @@
|
||||
|
||||
## תיעוד
|
||||
|
||||
📚 **[צפה בתיעוד המלא](docs/)** - עיין במסמכי markdown ב-GitHub
|
||||
📚 **[צפה בתיעוד המלא](https://docs.claude-mem.ai/)** - דפדף באתר הרשמי
|
||||
|
||||
### תחילת העבודה
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Claude Code को पुनः आरंभ करें। पिछले स
|
||||
|
||||
## दस्तावेज़ीकरण
|
||||
|
||||
📚 **[पूर्ण दस्तावेज़ीकरण देखें](docs/)** - GitHub पर markdown दस्तावेज़ ब्राउज़ करें
|
||||
📚 **[पूर्ण दस्तावेज़ीकरण देखें](https://docs.claude-mem.ai/)** - आधिकारिक वेबसाइट पर ब्राउज़ करें
|
||||
|
||||
### शुरुआत करना
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Indítsa újra a Claude Code-ot. A korábbi munkamenetek kontextusa automatikusa
|
||||
|
||||
## Dokumentáció
|
||||
|
||||
📚 **[Teljes dokumentáció megtekintése](docs/)** - Markdown dokumentumok böngészése GitHub-on
|
||||
📚 **[Teljes dokumentáció megtekintése](https://docs.claude-mem.ai/)** - Böngészés a hivatalos weboldalon
|
||||
|
||||
### Első lépések
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Restart Claude Code. Konteks dari sesi sebelumnya akan secara otomatis muncul di
|
||||
|
||||
## Dokumentasi
|
||||
|
||||
📚 **[Lihat Dokumentasi Lengkap](docs/)** - Telusuri dokumen markdown di GitHub
|
||||
📚 **[Lihat Dokumentasi Lengkap](https://docs.claude-mem.ai/)** - Jelajahi di situs web resmi
|
||||
|
||||
### Memulai
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Riavvia Claude Code. Il contesto delle sessioni precedenti apparirà automaticam
|
||||
|
||||
## Documentazione
|
||||
|
||||
📚 **[Visualizza Documentazione Completa](docs/)** - Sfoglia i documenti markdown su GitHub
|
||||
📚 **[Visualizza Documentazione Completa](https://docs.claude-mem.ai/)** - Sfoglia sul sito ufficiale
|
||||
|
||||
### Per Iniziare
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Claude Codeを再起動します。以前のセッションからのコンテキ
|
||||
|
||||
## ドキュメント
|
||||
|
||||
📚 **[完全なドキュメントを見る](docs/)** - GitHubでマークダウンドキュメントを閲覧
|
||||
📚 **[完全なドキュメントを見る](https://docs.claude-mem.ai/)** - 公式ウェブサイトで閲覧
|
||||
|
||||
### はじめに
|
||||
|
||||
@@ -212,7 +214,7 @@ Claude-Memは、過去の作業について尋ねると自動的に呼び出さ
|
||||
|
||||
Claude-Memは、**Endless Mode**(拡張セッション用の生体模倣メモリアーキテクチャ)などの実験的機能を備えた**ベータチャネル**を提供します。http://localhost:37777 → SettingsのWebビューアUIから安定版とベータ版を切り替えます。
|
||||
|
||||
Endless Modeと試用方法の詳細については、**[ベータ機能ドキュメント](https://docs.claude-mem.ai/beta-features)**を参照してください。
|
||||
Endless Modeと試用方法の詳細については、**[ベータ機能ドキュメント](https://docs.claude-mem.ai/beta-features)** を参照してください。
|
||||
|
||||
---
|
||||
|
||||
@@ -230,13 +232,13 @@ Endless Modeと試用方法の詳細については、**[ベータ機能ドキ
|
||||
|
||||
設定は`~/.claude-mem/settings.json`で管理されます(初回実行時にデフォルト値で自動作成)。AIモデル、ワーカーポート、データディレクトリ、ログレベル、コンテキスト注入設定を構成します。
|
||||
|
||||
利用可能なすべての設定と例については、**[設定ガイド](https://docs.claude-mem.ai/configuration)**を参照してください。
|
||||
利用可能なすべての設定と例については、**[設定ガイド](https://docs.claude-mem.ai/configuration)** を参照してください。
|
||||
|
||||
---
|
||||
|
||||
## 開発
|
||||
|
||||
ビルド手順、テスト、コントリビューションワークフローについては、**[開発ガイド](https://docs.claude-mem.ai/development)**を参照してください。
|
||||
ビルド手順、テスト、コントリビューションワークフローについては、**[開発ガイド](https://docs.claude-mem.ai/development)** を参照してください。
|
||||
|
||||
---
|
||||
|
||||
@@ -244,7 +246,7 @@ Endless Modeと試用方法の詳細については、**[ベータ機能ドキ
|
||||
|
||||
問題が発生した場合は、Claudeに問題を説明すると、troubleshootスキルが自動的に診断して修正を提供します。
|
||||
|
||||
よくある問題と解決策については、**[トラブルシューティングガイド](https://docs.claude-mem.ai/troubleshooting)**を参照してください。
|
||||
よくある問題と解決策については、**[トラブルシューティングガイド](https://docs.claude-mem.ai/troubleshooting)** を参照してください。
|
||||
|
||||
---
|
||||
|
||||
@@ -286,7 +288,7 @@ Copyright (C) 2025 Alex Newman (@thedotmack). All rights reserved.
|
||||
- 派生作品もAGPL-3.0の下でライセンスする必要があります
|
||||
- このソフトウェアには保証がありません
|
||||
|
||||
**Ragtimeに関する注意**: `ragtime/`ディレクトリは**PolyForm Noncommercial License 1.0.0**の下で個別にライセンスされています。詳細は[ragtime/LICENSE](ragtime/LICENSE)を参照してください。
|
||||
**Ragtimeに関する注意**: `ragtime/`ディレクトリは **PolyForm Noncommercial License 1.0.0** の下で個別にライセンスされています。詳細は[ragtime/LICENSE](ragtime/LICENSE)を参照してください。
|
||||
|
||||
---
|
||||
|
||||
@@ -299,4 +301,4 @@ Copyright (C) 2025 Alex Newman (@thedotmack). All rights reserved.
|
||||
|
||||
---
|
||||
|
||||
**Claude Agent SDKで構築** | **Claude Codeで動作** | **TypeScriptで作成**
|
||||
**Claude Agent SDKで構築** | **Claude Codeで動作** | **TypeScriptで作成**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -114,19 +116,19 @@ Claude Code를 재시작하세요. 이전 세션의 컨텍스트가 자동으로
|
||||
- 🧠 **지속적인 메모리** - 세션 간 컨텍스트 유지
|
||||
- 📊 **점진적 공개** - 토큰 비용 가시성을 갖춘 계층화된 메모리 검색
|
||||
- 🔍 **스킬 기반 검색** - mem-search 스킬로 프로젝트 기록 쿼리
|
||||
- 🖥️ **웹 뷰어 UI** - http://localhost:37777에서 실시간 메모리 스트림 확인
|
||||
- 🖥️ **웹 뷰어 UI** - http://localhost:37777 에서 실시간 메모리 스트림 확인
|
||||
- 💻 **Claude Desktop 스킬** - Claude Desktop 대화에서 메모리 검색
|
||||
- 🔒 **개인정보 제어** - `<private>` 태그를 사용하여 민감한 콘텐츠를 저장소에서 제외
|
||||
- ⚙️ **컨텍스트 설정** - 주입되는 컨텍스트에 대한 세밀한 제어
|
||||
- 🤖 **자동 작동** - 수동 개입 불필요
|
||||
- 🔗 **인용** - ID로 과거 관찰 참조 (http://localhost:37777/api/observation/{id}를 통해 액세스하거나 http://localhost:37777의 웹 뷰어에서 모두 보기)
|
||||
- 🔗 **인용** - ID로 과거 관찰 참조 (http://localhost:37777/api/observation/{id} 를 통해 액세스하거나 http://localhost:37777 의 웹 뷰어에서 모두 보기)
|
||||
- 🧪 **베타 채널** - 버전 전환을 통해 Endless Mode와 같은 실험적 기능 사용
|
||||
|
||||
---
|
||||
|
||||
## 문서
|
||||
|
||||
📚 **[전체 문서 보기](docs/)** - GitHub에서 마크다운 문서 탐색
|
||||
📚 **[전체 문서 보기](https://docs.claude-mem.ai/)** - 공식 웹사이트에서 찾아보기
|
||||
|
||||
### 시작하기
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +34,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -125,7 +127,7 @@ Herstart Claude Code. Context van eerdere sessies verschijnt automatisch in nieu
|
||||
|
||||
## Documentatie
|
||||
|
||||
📚 **[Bekijk Volledige Documentatie](docs/)** - Blader door markdown documenten op GitHub
|
||||
📚 **[Bekijk Volledige Documentatie](https://docs.claude-mem.ai/)** - Bladeren op de officiële website
|
||||
|
||||
### Aan de Slag
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Start Claude Code på nytt. Kontekst fra tidligere økter vil automatisk vises i
|
||||
|
||||
## Dokumentasjon
|
||||
|
||||
📚 **[Se Full Dokumentasjon](docs/)** - Bla gjennom markdown-dokumenter på GitHub
|
||||
📚 **[Se Full Dokumentasjon](https://docs.claude-mem.ai/)** - Bla gjennom på det offisielle nettstedet
|
||||
|
||||
### Komme I Gang
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +34,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -125,7 +127,7 @@ Uruchom ponownie Claude Code. Kontekst z poprzednich sesji automatycznie pojawi
|
||||
|
||||
## Dokumentacja
|
||||
|
||||
📚 **[Wyświetl Pełną Dokumentację](docs/)** - Przeglądaj dokumentację markdown na GitHub
|
||||
📚 **[Wyświetl Pełną Dokumentację](https://docs.claude-mem.ai/)** - Przeglądaj na oficjalnej stronie
|
||||
|
||||
### Pierwsze Kroki
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Reinicie o Claude Code. O contexto de sessões anteriores aparecerá automaticam
|
||||
|
||||
## Documentação
|
||||
|
||||
📚 **[Ver Documentação Completa](docs/)** - Navegue pelos documentos markdown no GitHub
|
||||
📚 **[Ver Documentação Completa](https://docs.claude-mem.ai/)** - Navegar no site oficial
|
||||
|
||||
### Começando
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Reporniți Claude Code. Contextul din sesiunile anterioare va apărea automat î
|
||||
|
||||
## Documentație
|
||||
|
||||
📚 **[Vizualizați Documentația Completă](docs/)** - Răsfoiți documentele markdown pe GitHub
|
||||
📚 **[Vizualizați Documentația Completă](https://docs.claude-mem.ai/)** - Răsfoiți pe site-ul oficial
|
||||
|
||||
### Introducere
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@
|
||||
|
||||
## Документация
|
||||
|
||||
📚 **[Просмотреть полную документацию](docs/)** - Просмотр markdown-документов на GitHub
|
||||
📚 **[Просмотреть полную документацию](https://docs.claude-mem.ai/)** - Просмотр на официальном сайте
|
||||
|
||||
### Начало работы
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Starta om Claude Code. Kontext från tidigare sessioner kommer automatiskt att v
|
||||
|
||||
## Dokumentation
|
||||
|
||||
📚 **[Visa fullständig dokumentation](docs/)** - Bläddra bland markdown-dokument på GitHub
|
||||
📚 **[Visa fullständig dokumentation](https://docs.claude-mem.ai/)** - Bläddra på den officiella webbplatsen
|
||||
|
||||
### Komma igång
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +34,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -125,7 +127,7 @@
|
||||
|
||||
## เอกสาร
|
||||
|
||||
📚 **[ดูเอกสารฉบับเต็ม](docs/)** - เรียกดูเอกสาร markdown บน GitHub
|
||||
📚 **[ดูเอกสารฉบับเต็ม](https://docs.claude-mem.ai/)** - เรียกดูบนเว็บไซต์อย่างเป็นทางการ
|
||||
|
||||
### เริ่มต้นใช้งาน
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -33,6 +34,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -125,7 +127,7 @@ Claude Code'u yeniden başlatın. Önceki oturumlardaki bağlam otomatik olarak
|
||||
|
||||
## Dokümantasyon
|
||||
|
||||
📚 **[Tam Dokümantasyonu Görüntüle](docs/)** - GitHub'da markdown dökümanlarına göz atın
|
||||
📚 **[Tam Dokümantasyonu Görüntüle](https://docs.claude-mem.ai/)** - Resmi web sitesinde göz atın
|
||||
|
||||
### Başlarken
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@
|
||||
|
||||
## Документація
|
||||
|
||||
📚 **[Переглянути повну документацію](docs/)** - Переглядайте markdown документи на GitHub
|
||||
📚 **[Переглянути повну документацію](https://docs.claude-mem.ai/)** - Переглянути на офіційному сайті
|
||||
|
||||
### Початок роботи
|
||||
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
<section dir="rtl">
|
||||
🌐 یہ ایک خودکار ترجمہ ہے۔ کمیونٹی کی اصلاحات کا خیر مقدم ہے!
|
||||
|
||||
---
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-dark-mode.webp">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-light-mode.webp">
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-light-mode.webp" alt="Claude-Mem" width="400">
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
</h1>
|
||||
|
||||
<p align="center" dir="ltr">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
<a href="README.es.md">🇪🇸 Español</a> •
|
||||
<a href="README.de.md">🇩🇪 Deutsch</a> •
|
||||
<a href="README.fr.md">🇫🇷 Français</a>
|
||||
<a href="README.he.md">🇮🇱 עברית</a> •
|
||||
<a href="README.ar.md">🇸🇦 العربية</a> •
|
||||
<a href="README.ru.md">🇷🇺 Русский</a> •
|
||||
<a href="README.pl.md">🇵🇱 Polski</a> •
|
||||
<a href="README.cs.md">🇨🇿 Čeština</a> •
|
||||
<a href="README.nl.md">🇳🇱 Nederlands</a> •
|
||||
<a href="README.tr.md">🇹🇷 Türkçe</a> •
|
||||
<a href="README.uk.md">🇺🇦 Українська</a> •
|
||||
<a href="README.vi.md">🇻🇳 Tiếng Việt</a> •
|
||||
<a href="README.id.md">🇮🇩 Indonesia</a> •
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
<a href="README.el.md">🇬🇷 Ελληνικά</a> •
|
||||
<a href="README.hu.md">🇭🇺 Magyar</a> •
|
||||
<a href="README.fi.md">🇫🇮 Suomi</a> •
|
||||
<a href="README.da.md">🇩🇰 Dansk</a> •
|
||||
<a href="README.no.md">🇳🇴 Norsk</a>
|
||||
</p>
|
||||
|
||||
<h4 align="center"><a href="https://claude.com/claude-code" target="_blank">Claude Code</a> کے لیے بنایا گیا مستقل میموری کمپریشن سسٹم۔</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
<img src="https://img.shields.io/badge/License-AGPL%203.0-blue.svg" alt="License">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/version-6.5.0-green.svg" alt="Version">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node">
|
||||
</a>
|
||||
<a href="https://github.com/thedotmack/awesome-claude-code">
|
||||
<img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/15496" target="_blank">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/trendshift-badge-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/trendshift-badge.svg">
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/trendshift-badge.svg" alt="thedotmack/claude-mem | Trendshift" width="250" height="55"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/cm-preview.gif" alt="Claude-Mem Preview" width="800">
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#تیز-رفتار-شروعات">تیز رفتار شروعات</a> •
|
||||
<a href="#یہ-کیسے-کام-کرتا-ہے">یہ کیسے کام کرتا ہے</a> •
|
||||
<a href="#تلاش-کے-اوزار">تلاش کے اوزار</a> •
|
||||
<a href="#دستاویزات">دستاویزات</a> •
|
||||
<a href="#ترتیبات">ترتیبات</a> •
|
||||
<a href="#مسائل-کی-تشخیص">مسائل کی تشخیص</a> •
|
||||
<a href="#لائسنس">لائسنس</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Claude-Mem خودکار طور پر ٹول کے استعمال کے بعد کے مشاہدات کو ریکارڈ کرتا ہے، سیمانٹک خلاصے تیار کرتا ہے اور انہیں مستقبل کے سیشنز میں دستیاب کرتا ہے تاکہ آپ سیشن میں براہ راست تناسب محفوظ رہے۔ یہ Claude کو سیشن ختم ہونے یا دوبارہ جڑنے کے بعد بھی منصوبے کے بارے میں معلومات کی مسلسلیت برقرار رکھنے کے قابل بناتا ہے۔
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## تیز رفتار شروعات
|
||||
|
||||
ٹرمنل میں نیا Claude Code سیشن شروع کریں اور ہیں کمانڈز درج کریں:
|
||||
|
||||
```
|
||||
> /plugin marketplace add thedotmack/claude-mem
|
||||
|
||||
> /plugin install claude-mem
|
||||
```
|
||||
|
||||
Claude Code کو دوبارہ شروع کریں۔ سابقہ سیشن کا تناسب خودکار طور پر نئے سیشن میں موجود ہوگا۔
|
||||
|
||||
**اہم خصوصیات:**
|
||||
|
||||
- 🧠 **مستقل میموری** - تناسب سیشن کے دوران برقرار رہتا ہے
|
||||
- 📊 **بتدریج ظہور** - لیئرڈ میموری کی بازیافت ٹوکن کی لاگت کی نمائندگی کے ساتھ
|
||||
- 🔍 **کمکردہ تلاش** - mem-search مہارت کے ساتھ اپنے منصوبے کی تاریخ میں تلاش کریں
|
||||
- 🖥️ **ویب ویور یو آئی** - http://localhost:37777 پر حقیقی وقت میموری اسٹریم
|
||||
- 💻 **Claude Desktop مہارت** - Claude Desktop بات چیت سے میموری تلاش کریں
|
||||
- 🔒 **رازداری کے کنٹرولز** - حساس مواد کو ذخیرہ سے خارج کرنے کے لیے `<private>` ٹیگ استعمال کریں
|
||||
- ⚙️ **تناسب کی ترتیبات** - کون سا تناسب انجیکٹ کیا جائے اس پر باریک کنٹرول
|
||||
- 🤖 **خودکار آپریشن** - کسی دستی مداخلت کی ضرورت نہیں
|
||||
- 🔗 **حوالہ** - ID کے ذریعے سابقہ مشاہدات کا حوالہ دیں (http://localhost:37777/api/observation/{id} کے ذریعے رسائی حاصل کریں یا تمام کو http://localhost:37777 پر ویب ویور میں دیکھیں)
|
||||
- 🧪 **بیٹا چینل** - ورژن تبدیل کرنے کے ذریعے Endless Mode جیسی تجرباتی خصوصیات آزمائیں
|
||||
|
||||
---
|
||||
|
||||
## دستاویزات
|
||||
|
||||
📚 **[مکمل دستاویزات دیکھیں](docs/)** - GitHub پر markdown ڈاکس کو براؤز کریں
|
||||
|
||||
### شروعات کرنا
|
||||
|
||||
- **[انسٹالیشن گائیڈ](https://docs.claude-mem.ai/installation)** - تیز رفتار شروعات اور اعلیٰ درجے کی انسٹالیشن
|
||||
- **[استعمال گائیڈ](https://docs.claude-mem.ai/usage/getting-started)** - Claude-Mem خودکار طور پر کیسے کام کرتا ہے
|
||||
- **[تلاش کے اوزار](https://docs.claude-mem.ai/usage/search-tools)** - قدرتی زبان کے ساتھ اپنے منصوبے کی تاریخ میں تلاش کریں
|
||||
- **[بیٹا خصوصیات](https://docs.claude-mem.ai/beta-features)** - Endless Mode جیسی تجرباتی خصوصیات آزمائیں
|
||||
|
||||
### بہترین طریقہ کار
|
||||
|
||||
- **[تناسب انجینیئرنگ](https://docs.claude-mem.ai/context-engineering)** - AI ایجنٹ کے تناسب کی اہمیت کے اصول
|
||||
- **[بتدریج ظہور](https://docs.claude-mem.ai/progressive-disclosure)** - Claude-Mem کے تناسب کی تیاری کی حکمت عملی کے پیچھے فلسفہ
|
||||
|
||||
### تعمیر
|
||||
|
||||
- **[جائزہ](https://docs.claude-mem.ai/architecture/overview)** - نظام کے اجزاء اور ڈیٹا کے بہاؤ
|
||||
- **[تعمیر کا ارتقاء](https://docs.claude-mem.ai/architecture-evolution)** - v3 سے v5 تک کا سفر
|
||||
- **[ہکس تعمیر](https://docs.claude-mem.ai/hooks-architecture)** - Claude-Mem لائف سائیکل ہکس کا استعمال کیسے کرتا ہے
|
||||
- **[ہکس حوالہ](https://docs.claude-mem.ai/architecture/hooks)** - 7 ہک اسکرپٹس کی تشریح
|
||||
- **[ورکر سروس](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API اور Bun انتظام
|
||||
- **[ڈیٹا بیس](https://docs.claude-mem.ai/architecture/database)** - SQLite اسکیما اور FTS5 تلاش
|
||||
- **[تلاش تعمیر](https://docs.claude-mem.ai/architecture/search-architecture)** - Chroma ویکٹر ڈیٹا بیس کے ساتھ ہائبرڈ تلاش
|
||||
|
||||
### ترتیبات اور ترقی
|
||||
|
||||
- **[ترتیبات](https://docs.claude-mem.ai/configuration)** - ماحول کے متغیرات اور سیٹنگز
|
||||
- **[ترقی](https://docs.claude-mem.ai/development)** - تعمیر، جانچ، حصہ داری
|
||||
- **[مسائل کی تشخیص](https://docs.claude-mem.ai/troubleshooting)** - عام مسائل اور حل
|
||||
|
||||
---
|
||||
|
||||
## یہ کیسے کام کرتا ہے
|
||||
|
||||
**اہم اجزاء:**
|
||||
|
||||
1. **5 لائف سائیکل ہکس** - SessionStart، UserPromptSubmit، PostToolUse، Stop، SessionEnd (6 ہک اسکرپٹس)
|
||||
2. **سمارٹ انسٹالیشن** - کیش شدہ منحصرات چیکر (پری ہک اسکرپٹ، لائف سائیکل ہک نہیں)
|
||||
3. **ورکر سروس** - ویب ویور UI اور 10 تلاش کے endpoints کے ساتھ پورٹ 37777 پر HTTP API، Bun کے ذریعے برتاؤ
|
||||
4. **SQLite ڈیٹا بیس** - سیشنز، مشاہدات، خلاصہ ذخیرہ کرتا ہے
|
||||
5. **mem-search مہارت** - بتدریج ظہور کے ساتھ قدرتی زبان کے سوالات
|
||||
6. **Chroma ویکٹر ڈیٹا بیس** - ہائبرڈ سیمانٹک + کلیدی لفظ تلاش ذہین تناسب کی بازیافت کے لیے
|
||||
|
||||
تفصیلات کے لیے [تعمیر کا جائزہ](https://docs.claude-mem.ai/architecture/overview) دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## MCP تلاش کے اوزار
|
||||
|
||||
Claude-Mem ٹوکن-موثر **3-لیئر ورک فلو پیٹرن** کی پیروی کرتے ہوئے **4 MCP اوزار** کے ذریعے ذہین میموری تلاش فراہم کرتا ہے:
|
||||
|
||||
**3-لیئر ورک فلو:**
|
||||
|
||||
1. **`search`** - IDs کے ساتھ کمپیکٹ انڈیکس حاصل کریں (~50-100 ٹوکن/نتیجہ)
|
||||
2. **`timeline`** - دلچسپ نتائج کے ارد گرد زمانی تناسب حاصل کریں
|
||||
3. **`get_observations`** - فلٹر شدہ IDs کے لیے صرف مکمل تفصیلات حاصل کریں (~500-1,000 ٹوکن/نتیجہ)
|
||||
|
||||
**یہ کیسے کام کرتا ہے:**
|
||||
- Claude آپ کی میموری میں تلاش کے لیے MCP اوزار استعمال کرتا ہے
|
||||
- نتائج کا انڈیکس حاصل کرنے کے لیے `search` سے شروع کریں
|
||||
- مخصوص مشاہدات کے ارد گرد کیا ہو رہا تھا دیکھنے کے لیے `timeline` استعمال کریں
|
||||
- متعلقہ IDs کے لیے مکمل تفصیلات حاصل کرنے کے لیے `get_observations` استعمال کریں
|
||||
- تفصیلات حاصل کرنے سے پہلے فلٹرنگ کے ذریعے **~10x ٹوکن کی بچت**
|
||||
|
||||
**دستیاب MCP اوزار:**
|
||||
|
||||
1. **`search`** - مکمل متن کی تلاش کے سوالات کے ساتھ میموری انڈیکس تلاش کریں، قسم/تاریخ/منصوبے کے لحاظ سے فلٹر کریں
|
||||
2. **`timeline`** - مخصوص مشاہدہ یا سوال کے ارد گرد زمانی تناسب حاصل کریں
|
||||
3. **`get_observations`** - IDs کے ذریعے مکمل مشاہدہ تفصیلات حاصل کریں (ہمیشہ متعدد IDs کو بیچ کریں)
|
||||
4. **`__IMPORTANT`** - ورک فلو دستاویزات (ہمیشہ Claude کو نظر آتی ہے)
|
||||
|
||||
**استعمال کی مثال:**
|
||||
|
||||
```typescript
|
||||
// مرحلہ 1: انڈیکس کے لیے تلاش کریں
|
||||
search(query="authentication bug", type="bugfix", limit=10)
|
||||
|
||||
// مرحلہ 2: انڈیکس کا جائزہ لیں، متعلقہ IDs کی شناخت کریں (مثلاً، #123, #456)
|
||||
|
||||
// مرحلہ 3: مکمل تفصیلات حاصل کریں
|
||||
get_observations(ids=[123, 456])
|
||||
```
|
||||
|
||||
تفصیلی مثالوں کے لیے [تلاش کے اوزار گائیڈ](https://docs.claude-mem.ai/usage/search-tools) دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## بیٹا خصوصیات
|
||||
|
||||
Claude-Mem ایک **بیٹا چینل** فراہم کرتا ہے جس میں **Endless Mode** جیسی تجرباتی خصوصیات ہیں (بڑھی ہوئی سیشنز کے لیے حیاتی نقل میموری کی تعمیر)۔ http://localhost:37777 → Settings میں ویب ویور UI سے مستحکم اور بیٹا ورژن کے درمیان سوئچ کریں۔
|
||||
|
||||
Endless Mode اور اسے کیسے آزمائیں اس کے بارے میں تفصیلات کے لیے **[بیٹا خصوصیات دستاویزات](https://docs.claude-mem.ai/beta-features)** دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## نظام کی ضروریات
|
||||
|
||||
- **Node.js**: 18.0.0 یا اس سے اوپر
|
||||
- **Claude Code**: پلگ ان سپورٹ کے ساتھ جدید ترین ورژن
|
||||
- **Bun**: JavaScript رن ٹائم اور پروسیس مینیجر (غیر موجود ہو تو خودکار طور پر انسٹال ہوگا)
|
||||
- **uv**: ویکٹر تلاش کے لیے Python پیکج مینیجر (غیر موجود ہو تو خودکار طور پر انسٹال ہوگا)
|
||||
- **SQLite 3**: مستقل اسٹوریج کے لیے (بنڈل شدہ)
|
||||
|
||||
---
|
||||
|
||||
## ترتیبات
|
||||
|
||||
سیٹنگز `~/.claude-mem/settings.json` میں منظم ہیں (پہلی رن میں ڈیفالٹ کے ساتھ خودکار طور پر بنائی جاتی ہے)۔ AI ماڈل، ورکر پورٹ، ڈیٹا ڈائریکٹری، لاگ لیول اور تناسب انجیکشن سیٹنگز کو ترتیب دیں۔
|
||||
|
||||
تمام دستیاب سیٹنگز اور مثالوں کے لیے **[ترتیبات گائیڈ](https://docs.claude-mem.ai/configuration)** دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## ترقی
|
||||
|
||||
تعمیر کی ہدایات، جانچ اور حصہ داری کے کام کے بہاؤ کے لیے **[ترقی گائیڈ](https://docs.claude-mem.ai/development)** دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## مسائل کی تشخیص
|
||||
|
||||
اگر مسائل کا سامنا ہو تو Claude کو مسئلہ بتائیں اور troubleshoot مہارت خودکار طور پر تشخیص دے گی اور حل فراہم کرے گی۔
|
||||
|
||||
عام مسائل اور حل کے لیے **[مسائل کی تشخیص گائیڈ](https://docs.claude-mem.ai/troubleshooting)** دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## خرابی کی رپورٹ
|
||||
|
||||
خودکار جنریٹر کے ساتھ تفصیلی خرابی کی رپورٹ تیار کریں:
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm run bug-report
|
||||
```
|
||||
|
||||
## حصہ داری
|
||||
|
||||
حصہ داری کا خیر مقدم ہے! براہ کرم:
|
||||
|
||||
1. رپوزیٹری کو فورک کریں
|
||||
2. ایک خصوصیت کی برانچ بنائیں
|
||||
3. ٹیسٹ کے ساتھ اپنی تبدیلیاں کریں
|
||||
4. دستاویزات کو اپڈیٹ کریں
|
||||
5. ایک Pull Request جمع کریں
|
||||
|
||||
حصہ داری کے کام کے بہاؤ کے لیے [ترقی گائیڈ](https://docs.claude-mem.ai/development) دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## لائسنس
|
||||
|
||||
یہ منصوبہ **GNU Affero General Public License v3.0** (AGPL-3.0) کے تحت لائسنس ہے۔
|
||||
|
||||
Copyright (C) 2025 Alex Newman (@thedotmack)۔ تمام حقوق محفوظ ہیں۔
|
||||
|
||||
مکمل تفصیلات کے لیے [LICENSE](LICENSE) فائل دیکھیں۔
|
||||
|
||||
**اس کا مطلب کیا ہے:**
|
||||
|
||||
- آپ اس سافٹ ویئر کو آزادی سے استعمال، تبدیل اور تقسیم کر سکتے ہیں
|
||||
- اگر آپ اسے تبدیل کریں اور نیٹ ورک سرور میں نشر کریں تو آپ کو اپنا سورس کوڈ دستیاب کرنا ہوگا
|
||||
- ماخوذ کام بھی AGPL-3.0 کے تحت لائسنس ہونے چاہیں
|
||||
- اس سافٹ ویئر کے لیے کوئی وارنٹی نہیں
|
||||
|
||||
**Ragtime کے بارے میں نوٹ**: `ragtime/` ڈائریکٹری الگ سے **PolyForm Noncommercial License 1.0.0** کے تحت لائسنس ہے۔ تفصیلات کے لیے [ragtime/LICENSE](ragtime/LICENSE) دیکھیں۔
|
||||
|
||||
---
|
||||
|
||||
## معاونت
|
||||
|
||||
- **دستاویزات**: [docs/](docs/)
|
||||
- **مسائل**: [GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- **رپوزیٹری**: [github.com/thedotmack/claude-mem](https://github.com/thedotmack/claude-mem)
|
||||
- **مصنف**: Alex Newman ([@thedotmack](https://github.com/thedotmack))
|
||||
|
||||
---
|
||||
|
||||
**Claude Agent SDK کے ساتھ بنایا گیا** | **Claude Code کے ذریعے طاقت ور** | **TypeScript کے ساتھ بنایا گیا**
|
||||
|
||||
</section>
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@ Khởi động lại Claude Code. Ngữ cảnh từ các phiên trước sẽ t
|
||||
|
||||
## Tài Liệu
|
||||
|
||||
📚 **[Xem Tài Liệu Đầy Đủ](docs/)** - Duyệt tài liệu markdown trên GitHub
|
||||
📚 **[Xem Tài Liệu Đầy Đủ](https://docs.claude-mem.ai/)** - Duyệt trên trang web chính thức
|
||||
|
||||
### Bắt Đầu
|
||||
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
🌐 這是自動翻譯。歡迎社群貢獻修正!
|
||||
|
||||
---
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-dark-mode.webp">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-light-mode.webp">
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-light-mode.webp" alt="Claude-Mem" width="400">
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
<a href="README.es.md">🇪🇸 Español</a> •
|
||||
<a href="README.de.md">🇩🇪 Deutsch</a> •
|
||||
<a href="README.fr.md">🇫🇷 Français</a>
|
||||
<a href="README.he.md">🇮🇱 עברית</a> •
|
||||
<a href="README.ar.md">🇸🇦 العربية</a> •
|
||||
<a href="README.ru.md">🇷🇺 Русский</a> •
|
||||
<a href="README.pl.md">🇵🇱 Polski</a> •
|
||||
<a href="README.cs.md">🇨🇿 Čeština</a> •
|
||||
<a href="README.nl.md">🇳🇱 Nederlands</a> •
|
||||
<a href="README.tr.md">🇹🇷 Türkçe</a> •
|
||||
<a href="README.uk.md">🇺🇦 Українська</a> •
|
||||
<a href="README.vi.md">🇻🇳 Tiếng Việt</a> •
|
||||
<a href="README.id.md">🇮🇩 Indonesia</a> •
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
<a href="README.el.md">🇬🇷 Ελληνικά</a> •
|
||||
<a href="README.hu.md">🇭🇺 Magyar</a> •
|
||||
<a href="README.fi.md">🇫🇮 Suomi</a> •
|
||||
<a href="README.da.md">🇩🇰 Dansk</a> •
|
||||
<a href="README.no.md">🇳🇴 Norsk</a>
|
||||
</p>
|
||||
|
||||
<h4 align="center">為 <a href="https://claude.com/claude-code" target="_blank">Claude Code</a> 打造的持久記憶壓縮系統</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
<img src="https://img.shields.io/badge/License-AGPL%203.0-blue.svg" alt="License">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/version-6.5.0-green.svg" alt="Version">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node">
|
||||
</a>
|
||||
<a href="https://github.com/thedotmack/awesome-claude-code">
|
||||
<img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/15496" target="_blank">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/trendshift-badge-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/trendshift-badge.svg">
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/trendshift-badge.svg" alt="thedotmack/claude-mem | Trendshift" width="250" height="55"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/cm-preview.gif" alt="Claude-Mem Preview" width="800">
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#快速開始">快速開始</a> •
|
||||
<a href="#運作原理">運作原理</a> •
|
||||
<a href="#mcp-搜尋工具">搜尋工具</a> •
|
||||
<a href="#文件">文件</a> •
|
||||
<a href="#設定">設定</a> •
|
||||
<a href="#疑難排解">疑難排解</a> •
|
||||
<a href="#授權條款">授權條款</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Claude-Mem 透過自動擷取工具使用觀察、產生語意摘要並在未來的工作階段中提供使用,無縫保留跨工作階段的脈絡。這使 Claude 即使在工作階段結束或重新連線後,仍能維持對專案的知識連續性。
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
在終端機中開啟新的 Claude Code 工作階段,並輸入以下指令:
|
||||
|
||||
```
|
||||
> /plugin marketplace add thedotmack/claude-mem
|
||||
|
||||
> /plugin install claude-mem
|
||||
```
|
||||
|
||||
重新啟動 Claude Code。先前工作階段的脈絡將自動出現在新的工作階段中。
|
||||
|
||||
**主要功能:**
|
||||
|
||||
- 🧠 **持久記憶** - 脈絡跨工作階段保留
|
||||
- 📊 **漸進式揭露** - 具有 Token 成本可見性的分層記憶擷取
|
||||
- 🔍 **技能式搜尋** - 使用 mem-search 技能查詢專案歷史
|
||||
- 🖥️ **網頁檢視介面** - 在 http://localhost:37777 即時檢視記憶串流
|
||||
- 💻 **Claude Desktop 技能** - 從 Claude Desktop 對話中搜尋記憶
|
||||
- 🔒 **隱私控制** - 使用 `<private>` 標籤排除敏感內容的儲存
|
||||
- ⚙️ **脈絡設定** - 精細控制注入哪些脈絡
|
||||
- 🤖 **自動運作** - 無需手動介入
|
||||
- 🔗 **引用** - 使用 ID 參考過去的觀察(透過 http://localhost:37777/api/observation/{id} 存取,或在 http://localhost:37777 的網頁檢視器中檢視全部)
|
||||
- 🧪 **Beta 通道** - 透過版本切換試用 Endless Mode 等實驗性功能
|
||||
|
||||
---
|
||||
|
||||
## 文件
|
||||
|
||||
📚 **[檢視完整文件](docs/)** - 在 GitHub 上瀏覽 Markdown 文件
|
||||
|
||||
### 入門指南
|
||||
|
||||
- **[安裝指南](https://docs.claude-mem.ai/installation)** - 快速開始與進階安裝
|
||||
- **[使用指南](https://docs.claude-mem.ai/usage/getting-started)** - Claude-Mem 如何自動運作
|
||||
- **[搜尋工具](https://docs.claude-mem.ai/usage/search-tools)** - 使用自然語言查詢專案歷史
|
||||
- **[Beta 功能](https://docs.claude-mem.ai/beta-features)** - 試用 Endless Mode 等實驗性功能
|
||||
|
||||
### 最佳實務
|
||||
|
||||
- **[脈絡工程](https://docs.claude-mem.ai/context-engineering)** - AI 代理脈絡最佳化原則
|
||||
- **[漸進式揭露](https://docs.claude-mem.ai/progressive-disclosure)** - Claude-Mem 脈絡啟動策略背後的理念
|
||||
|
||||
### 架構
|
||||
|
||||
- **[概覽](https://docs.claude-mem.ai/architecture/overview)** - 系統元件與資料流程
|
||||
- **[架構演進](https://docs.claude-mem.ai/architecture-evolution)** - 從 v3 到 v5 的旅程
|
||||
- **[Hooks 架構](https://docs.claude-mem.ai/hooks-architecture)** - Claude-Mem 如何使用生命週期掛鉤
|
||||
- **[Hooks 參考](https://docs.claude-mem.ai/architecture/hooks)** - 7 個掛鉤腳本說明
|
||||
- **[Worker 服務](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API 與 Bun 管理
|
||||
- **[資料庫](https://docs.claude-mem.ai/architecture/database)** - SQLite 結構描述與 FTS5 搜尋
|
||||
- **[搜尋架構](https://docs.claude-mem.ai/architecture/search-architecture)** - 使用 Chroma 向量資料庫的混合搜尋
|
||||
|
||||
### 設定與開發
|
||||
|
||||
- **[設定](https://docs.claude-mem.ai/configuration)** - 環境變數與設定
|
||||
- **[開發](https://docs.claude-mem.ai/development)** - 建置、測試、貢獻
|
||||
- **[疑難排解](https://docs.claude-mem.ai/troubleshooting)** - 常見問題與解決方案
|
||||
|
||||
---
|
||||
|
||||
## 運作原理
|
||||
|
||||
**核心元件:**
|
||||
|
||||
1. **5 個生命週期掛鉤** - SessionStart、UserPromptSubmit、PostToolUse、Stop、SessionEnd(6 個掛鉤腳本)
|
||||
2. **智慧安裝** - 快取的相依性檢查器(pre-hook 腳本,非生命週期掛鉤)
|
||||
3. **Worker 服務** - 連接埠 37777 上的 HTTP API,含網頁檢視介面與 10 個搜尋端點,由 Bun 管理
|
||||
4. **SQLite 資料庫** - 儲存工作階段、觀察、摘要
|
||||
5. **mem-search 技能** - 具有漸進式揭露的自然語言查詢
|
||||
6. **Chroma 向量資料庫** - 用於智慧脈絡擷取的混合語意 + 關鍵字搜尋
|
||||
|
||||
詳情請參閱[架構概覽](https://docs.claude-mem.ai/architecture/overview)。
|
||||
|
||||
---
|
||||
|
||||
## MCP 搜尋工具
|
||||
|
||||
Claude-Mem 透過遵循 Token 高效的 **3 層工作流程模式**,以 **4 個 MCP 工具**提供智慧記憶搜尋:
|
||||
|
||||
**3 層工作流程:**
|
||||
|
||||
1. **`search`** - 取得精簡索引與 ID(每筆結果約 50-100 tokens)
|
||||
2. **`timeline`** - 取得有趣結果周圍的時間脈絡
|
||||
3. **`get_observations`** - 僅為過濾後的 ID 擷取完整詳情(每筆結果約 500-1,000 tokens)
|
||||
|
||||
**運作方式:**
|
||||
|
||||
- Claude 使用 MCP 工具搜尋您的記憶
|
||||
- 從 `search` 開始取得結果索引
|
||||
- 使用 `timeline` 檢視特定觀察周圍發生的事情
|
||||
- 使用 `get_observations` 擷取相關 ID 的完整詳情
|
||||
- 透過在擷取詳情前過濾,**節省約 10 倍 token**
|
||||
|
||||
**可用的 MCP 工具:**
|
||||
|
||||
1. **`search`** - 使用全文查詢搜尋記憶索引,依類型/日期/專案過濾
|
||||
2. **`timeline`** - 取得特定觀察或查詢周圍的時間脈絡
|
||||
3. **`get_observations`** - 依 ID 擷取完整觀察詳情(批次處理多個 ID)
|
||||
4. **`__IMPORTANT`** - 工作流程文件(Claude 永遠可見)
|
||||
|
||||
**使用範例:**
|
||||
|
||||
```typescript
|
||||
// 步驟 1:搜尋索引
|
||||
search(query="authentication bug", type="bugfix", limit=10)
|
||||
|
||||
// 步驟 2:檢閱索引,識別相關 ID(例如 #123、#456)
|
||||
|
||||
// 步驟 3:擷取完整詳情
|
||||
get_observations(ids=[123, 456])
|
||||
```
|
||||
|
||||
詳細範例請參閱[搜尋工具指南](https://docs.claude-mem.ai/usage/search-tools)。
|
||||
|
||||
---
|
||||
|
||||
## Beta 功能
|
||||
|
||||
Claude-Mem 提供具有實驗性功能的 **Beta 通道**,例如 **Endless Mode**(用於延長工作階段的仿生記憶架構)。在 http://localhost:37777 → Settings 的網頁檢視介面中切換穩定版與 Beta 版。
|
||||
|
||||
有關 Endless Mode 與如何試用的詳情,請參閱 **[Beta 功能文件](https://docs.claude-mem.ai/beta-features)**。
|
||||
|
||||
---
|
||||
|
||||
## 系統需求
|
||||
|
||||
- **Node.js**:18.0.0 或更高版本
|
||||
- **Claude Code**:具有外掛支援的最新版本
|
||||
- **Bun**:JavaScript 執行環境與程序管理員(如缺少將自動安裝)
|
||||
- **uv**:用於向量搜尋的 Python 套件管理員(如缺少將自動安裝)
|
||||
- **SQLite 3**:用於持久儲存(已內建)
|
||||
|
||||
---
|
||||
|
||||
## 設定
|
||||
|
||||
設定在 `~/.claude-mem/settings.json` 中管理(首次執行時自動以預設值建立)。設定 AI 模型、Worker 連接埠、資料目錄、日誌層級與脈絡注入設定。
|
||||
|
||||
所有可用設定與範例請參閱 **[設定指南](https://docs.claude-mem.ai/configuration)**。
|
||||
|
||||
---
|
||||
|
||||
## 開發
|
||||
|
||||
建置說明、測試與貢獻工作流程請參閱 **[開發指南](https://docs.claude-mem.ai/development)**。
|
||||
|
||||
---
|
||||
|
||||
## 疑難排解
|
||||
|
||||
如遇問題,向 Claude 描述問題,troubleshoot 技能將自動診斷並提供修正。
|
||||
|
||||
常見問題與解決方案請參閱 **[疑難排解指南](https://docs.claude-mem.ai/troubleshooting)**。
|
||||
|
||||
---
|
||||
|
||||
## 錯誤回報
|
||||
|
||||
使用自動產生器建立完整的錯誤回報:
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm run bug-report
|
||||
```
|
||||
|
||||
## 貢獻
|
||||
|
||||
歡迎貢獻!請依照以下步驟:
|
||||
|
||||
1. Fork 儲存庫
|
||||
2. 建立功能分支
|
||||
3. 加入測試並進行變更
|
||||
4. 更新文件
|
||||
5. 提交 Pull Request
|
||||
|
||||
貢獻工作流程請參閱[開發指南](https://docs.claude-mem.ai/development)。
|
||||
|
||||
---
|
||||
|
||||
## 授權條款
|
||||
|
||||
本專案採用 **GNU Affero 通用公共授權條款 v3.0**(AGPL-3.0)授權。
|
||||
|
||||
Copyright (C) 2025 Alex Newman (@thedotmack). All rights reserved.
|
||||
|
||||
完整詳情請參閱 [LICENSE](LICENSE) 檔案。
|
||||
|
||||
**這代表什麼:**
|
||||
|
||||
- 您可以自由使用、修改與散佈此軟體
|
||||
- 如果您修改並部署於網路伺服器上,您必須公開您的原始碼
|
||||
- 衍生作品也必須採用 AGPL-3.0 授權
|
||||
- 本軟體不提供任何擔保
|
||||
|
||||
**關於 Ragtime 的說明**:`ragtime/` 目錄採用 **PolyForm Noncommercial License 1.0.0** 另行授權。詳情請參閱 [ragtime/LICENSE](ragtime/LICENSE)。
|
||||
|
||||
---
|
||||
|
||||
## 支援
|
||||
|
||||
- **文件**:[docs/](docs/)
|
||||
- **Issues**:[GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- **儲存庫**:[github.com/thedotmack/claude-mem](https://github.com/thedotmack/claude-mem)
|
||||
- **官方 X 帳號**:[@Claude_Memory](https://x.com/Claude_Memory)
|
||||
- **官方 Discord**:[加入 Discord](https://discord.com/invite/J4wttp9vDu)
|
||||
- **作者**:Alex Newman ([@thedotmack](https://github.com/thedotmack))
|
||||
|
||||
---
|
||||
|
||||
**使用 Claude Agent SDK 建置** | **由 Claude Code 驅動** | **以 TypeScript 開發**
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="README.zh.md">🇨🇳 中文</a> •
|
||||
<a href="README.zh-tw.md">🇹🇼 繁體中文</a> •
|
||||
<a href="README.ja.md">🇯🇵 日本語</a> •
|
||||
<a href="README.pt-br.md">🇧🇷 Português</a> •
|
||||
<a href="README.ko.md">🇰🇷 한국어</a> •
|
||||
@@ -34,6 +35,7 @@
|
||||
<a href="README.th.md">🇹🇭 ไทย</a> •
|
||||
<a href="README.hi.md">🇮🇳 हिन्दी</a> •
|
||||
<a href="README.bn.md">🇧🇩 বাংলা</a> •
|
||||
<a href="README.ur.md">🇵🇰 اردو</a> •
|
||||
<a href="README.ro.md">🇷🇴 Română</a> •
|
||||
<a href="README.sv.md">🇸🇪 Svenska</a> •
|
||||
<a href="README.it.md">🇮🇹 Italiano</a> •
|
||||
@@ -126,7 +128,7 @@
|
||||
|
||||
## 文档
|
||||
|
||||
📚 **[查看完整文档](docs/)** - 在 GitHub 上浏览 Markdown 文档
|
||||
📚 **[查看完整文档](https://docs.claude-mem.ai/)** - 在官方网站浏览
|
||||
|
||||
### 入门指南
|
||||
|
||||
@@ -212,7 +214,7 @@ Claude-Mem 通过 mem-search 技能提供智能搜索,当您询问过去的工
|
||||
|
||||
Claude-Mem 提供**测试版渠道**,包含实验性功能,如**无尽模式**(用于扩展会话的仿生记忆架构)。从 Web 查看器界面 http://localhost:37777 → 设置 切换稳定版和测试版。
|
||||
|
||||
详见**[测试版功能文档](https://docs.claude-mem.ai/beta-features)**了解无尽模式的详细信息和试用方法。
|
||||
详见 **[测试版功能文档](https://docs.claude-mem.ai/beta-features)** 了解无尽模式的详细信息和试用方法。
|
||||
|
||||
---
|
||||
|
||||
@@ -230,13 +232,13 @@ Claude-Mem 提供**测试版渠道**,包含实验性功能,如**无尽模式**(
|
||||
|
||||
设置在 `~/.claude-mem/settings.json` 中管理(首次运行时自动创建默认设置)。可配置 AI 模型、worker 端口、数据目录、日志级别和上下文注入设置。
|
||||
|
||||
详见**[配置指南](https://docs.claude-mem.ai/configuration)**了解所有可用设置和示例。
|
||||
详见 **[配置指南](https://docs.claude-mem.ai/configuration)** 了解所有可用设置和示例。
|
||||
|
||||
---
|
||||
|
||||
## 开发
|
||||
|
||||
详见**[开发指南](https://docs.claude-mem.ai/development)**了解构建说明、测试和贡献工作流程。
|
||||
详见 **[开发指南](https://docs.claude-mem.ai/development)** 了解构建说明、测试和贡献工作流程。
|
||||
|
||||
---
|
||||
|
||||
@@ -244,7 +246,7 @@ Claude-Mem 提供**测试版渠道**,包含实验性功能,如**无尽模式**(
|
||||
|
||||
如果遇到问题,向 Claude 描述问题,troubleshoot 技能将自动诊断并提供修复方案。
|
||||
|
||||
详见**[故障排除指南](https://docs.claude-mem.ai/troubleshooting)**了解常见问题和解决方案。
|
||||
详见 **[故障排除指南](https://docs.claude-mem.ai/troubleshooting)** 了解常见问题和解决方案。
|
||||
|
||||
---
|
||||
|
||||
@@ -301,4 +303,4 @@ Copyright (C) 2025 Alex Newman (@thedotmack)。保留所有权利。
|
||||
|
||||
**使用 Claude Agent SDK 构建** | **由 Claude Code 驱动** | **使用 TypeScript 制作**
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
+1
-89
@@ -85,92 +85,4 @@ npx mintlify dev
|
||||
|
||||
**Simple Rule**:
|
||||
- `/docs/public/` = Official user documentation (Mintlify .mdx files) ← YOU ARE HERE
|
||||
- `/docs/context/` = Internal docs, plans, references, audits
|
||||
|
||||
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #11206 | 3:01 PM | 🔵 | mem-search skill architecture and migration details retrieved in full format | ~538 |
|
||||
|
||||
### Nov 21, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #13221 | 2:01 AM | 🔴 | Fixed broken markdown link to Viewer UI documentation | ~316 |
|
||||
| #13220 | 2:00 AM | 🔴 | Escaped HTML less-than symbol in universal architecture timeout documentation | ~316 |
|
||||
| #13216 | 1:54 AM | ✅ | Universal Architecture Added to Navigation | ~330 |
|
||||
| #13215 | " | 🟣 | Universal AI Memory Architecture Documentation Created | ~732 |
|
||||
| #13213 | 1:50 AM | 🔵 | Introduction Page Content and Recent v6.0.0 Release | ~495 |
|
||||
| #13212 | " | 🔵 | Architecture Evolution Documentation Structure | ~408 |
|
||||
| #13211 | " | 🔵 | Mintlify Documentation Site Configuration | ~430 |
|
||||
| #13209 | 1:48 AM | 🔵 | Public Documentation Structure and Guidelines | ~383 |
|
||||
|
||||
### Nov 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #14994 | 2:22 PM | ✅ | Version Channel Section Added to Configuration Documentation | ~301 |
|
||||
| #14993 | " | ✅ | Beta Features Added to Documentation Navigation | ~188 |
|
||||
| #14992 | 2:21 PM | 🟣 | Beta Features Documentation Page Created | ~488 |
|
||||
| #14991 | " | 🔵 | Mintlify Navigation Structure and Documentation Groups | ~394 |
|
||||
| #14989 | " | 🔵 | Installation Documentation with Quick Start and Verification Steps | ~383 |
|
||||
| #14988 | " | 🔵 | Configuration Documentation Structure and Environment Variables | ~338 |
|
||||
|
||||
### Nov 26, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #16190 | 10:22 PM | 🔵 | RAGTIME Search Retrieved Five Observations About Claude-Mem vs RAG Architecture | ~637 |
|
||||
|
||||
### Dec 3, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #19884 | 9:42 PM | 🔵 | Configuration system and environment variables | ~701 |
|
||||
| #19878 | 9:40 PM | 🔵 | Installation process and system architecture | ~486 |
|
||||
|
||||
### Dec 8, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22335 | 10:26 PM | 🔵 | Mintlify documentation configuration analyzed | ~534 |
|
||||
| #22311 | 9:47 PM | 🔵 | Comprehensive Hooks Architecture Documentation Review | ~263 |
|
||||
| #22297 | 9:43 PM | 🔵 | Mintlify Documentation Framework Configuration | ~446 |
|
||||
| #22294 | " | 🔵 | Documentation Site Structure Located | ~359 |
|
||||
|
||||
### Dec 9, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #23179 | 10:44 PM | ✅ | Removed explanatory reasons from tool exclusion documentation | ~297 |
|
||||
|
||||
### Dec 15, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27038 | 6:02 PM | 🔵 | 95% token reduction claims found only in private experimental documents, not in main public docs | ~513 |
|
||||
| #27037 | " | 🔵 | Branch switching functionality exists in SettingsRoutes with UI switcher removal intent | ~463 |
|
||||
| #26986 | 5:24 PM | ✅ | Updated Endless Mode latency warning in beta features documentation | ~299 |
|
||||
|
||||
### Dec 29, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33938 | 6:27 PM | 🔵 | Relevant CLAUDE.md Context Identified for PR #492 | ~435 |
|
||||
| #33750 | 12:25 AM | ✅ | Documentation Update: Removed Version Number from Architecture Evolution | ~281 |
|
||||
|
||||
### Jan 7, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38233 | 7:42 PM | ✅ | Renumbered SessionEnd Hook from 6 to 5 | ~315 |
|
||||
| #38229 | 7:41 PM | ✅ | Renumbered PostToolUse Hook from 4 to 3 | ~278 |
|
||||
| #38225 | " | ✅ | Updated Hook Count Description in Hooks Architecture Documentation | ~352 |
|
||||
</claude-mem-context>
|
||||
- `/docs/context/` = Internal docs, plans, references, audits
|
||||
@@ -1,38 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #11206 | 3:01 PM | 🔵 | mem-search skill architecture and migration details retrieved in full format | ~538 |
|
||||
|
||||
### Nov 21, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #13218 | 1:58 AM | 🔴 | Escaped HTML special character in MDX documentation | ~261 |
|
||||
|
||||
### Dec 3, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #19891 | 9:43 PM | 🔵 | Seven hook scripts across five lifecycle events | ~713 |
|
||||
|
||||
### Dec 15, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27040 | 6:03 PM | 🔵 | Comprehensive search confirms no 95% claims exist in main branch public documentation | ~508 |
|
||||
| #27037 | 6:02 PM | 🔵 | Branch switching functionality exists in SettingsRoutes with UI switcher removal intent | ~463 |
|
||||
|
||||
### Jan 7, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38221 | 7:41 PM | ✅ | Removed User Message Hook Documentation Section | ~339 |
|
||||
| #38218 | 7:40 PM | ✅ | Updated Hook Configuration Documentation to Match Implementation | ~382 |
|
||||
| #38212 | " | 🔵 | 5-Stage Hook Lifecycle Architecture for Memory Agent | ~668 |
|
||||
</claude-mem-context>
|
||||
@@ -26,7 +26,7 @@ Settings are managed in `~/.claude-mem/settings.json`. The file is auto-created
|
||||
| Setting | Default | Description |
|
||||
|-------------------------------|---------------------------------|---------------------------------------|
|
||||
| `CLAUDE_MEM_GEMINI_API_KEY` | — | Gemini API key ([get free key](https://aistudio.google.com/app/apikey)) |
|
||||
| `CLAUDE_MEM_GEMINI_MODEL` | `gemini-2.5-flash-lite` | Gemini model: `gemini-2.5-flash-lite`, `gemini-2.5-flash`, `gemini-3-flash` |
|
||||
| `CLAUDE_MEM_GEMINI_MODEL` | `gemini-2.5-flash-lite` | Gemini model: `gemini-2.5-flash-lite`, `gemini-2.5-flash`, `gemini-3-flash-preview` |
|
||||
|
||||
See [Gemini Provider](usage/gemini-provider) for detailed configuration and free tier information.
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 29, 2025
|
||||
|
||||
**gemini-setup.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34346 | 11:11 PM | 🟣 | Gemini Free Tier Integration Guide | ~413 |
|
||||
| #34337 | 11:10 PM | 🔵 | Cursor Documentation Available | ~161 |
|
||||
| #34331 | 11:05 PM | 🔴 | Fixed Broken Links in cursor/gemini-setup.mdx | ~253 |
|
||||
| #34326 | 11:04 PM | 🔵 | Broken Links in Cursor Gemini Setup Documentation | ~324 |
|
||||
| #34320 | 11:03 PM | 🔵 | Mintlify Broken Links Detected in Documentation | ~292 |
|
||||
| #34215 | 10:08 PM | 🔵 | Retrieved Detailed Cursor Integration Implementation History | ~676 |
|
||||
| #34214 | 10:07 PM | 🔵 | Cursor Integration Feature Set Discovered via Memory Search | ~427 |
|
||||
| #34148 | 9:28 PM | 🟣 | Cursor IDE Integration with Cross-Platform Hooks and Documentation | ~514 |
|
||||
| #34112 | 9:07 PM | 🟣 | Committed Cursor Public Documentation to Repository | ~427 |
|
||||
| #34106 | 9:05 PM | 🟣 | Created Cursor-Specific Gemini Setup Guide | ~563 |
|
||||
|
||||
**index.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34339 | 11:10 PM | 🟣 | Cursor IDE Integration with Persistent Memory | ~394 |
|
||||
| #34335 | 11:06 PM | 🟣 | Mintlify Documentation Linting Successfully Completed | ~409 |
|
||||
| #34330 | 11:05 PM | 🔴 | Fixed Remaining Broken Links in cursor/index.mdx Next Steps Section | ~284 |
|
||||
| #34329 | " | 🔴 | Fixed Broken Links in cursor/index.mdx Detailed Guides Section | ~269 |
|
||||
| #34325 | 11:04 PM | 🔵 | Multiple Broken Links in Cursor Index Documentation | ~329 |
|
||||
| #34216 | 10:08 PM | 🔵 | Additional Cursor Integration Details Retrieved for Post Writing | ~600 |
|
||||
| #34105 | 9:05 PM | 🟣 | Created Cursor Integration Landing Page | ~522 |
|
||||
|
||||
**openrouter-setup.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34332 | 11:05 PM | 🔴 | Fixed Broken Links in cursor/openrouter-setup.mdx | ~283 |
|
||||
| #34324 | 11:04 PM | 🔵 | Broken Link Syntax Identified in Cursor Documentation | ~329 |
|
||||
| #34107 | 9:06 PM | 🟣 | Created Cursor-Specific OpenRouter Setup Guide | ~573 |
|
||||
|
||||
**cursor**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34322 | 11:03 PM | 🔵 | Cursor Directory Files Confirmed to Exist | ~224 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
**gemini-setup.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36751 | 12:32 AM | 🔵 | Gemini-Related Files Located Across Project | ~242 |
|
||||
</claude-mem-context>
|
||||
@@ -103,7 +103,7 @@ Open http://localhost:37777 to see the memory viewer.
|
||||
|-------|---------------|-------|
|
||||
| `gemini-2.5-flash-lite` | 10 (4,000 with billing) | **Default.** Fastest, highest free tier RPM |
|
||||
| `gemini-2.5-flash` | 5 (1,000 with billing) | Higher capability |
|
||||
| `gemini-3-flash` | 5 (1,000 with billing) | Latest model |
|
||||
| `gemini-3-flash-preview` | 5 (1,000 with billing) | Latest model |
|
||||
|
||||
To change the model, update your settings:
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
"modes",
|
||||
"development",
|
||||
"troubleshooting",
|
||||
"platform-integration"
|
||||
"platform-integration",
|
||||
"openclaw-integration"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -65,6 +65,7 @@ Inherits all behavior from Code Mode but instructs Claude to generate **all** me
|
||||
| **Hindi** | `code--hi` | हिन्दी |
|
||||
| **Hungarian** | `code--hu` | Magyar |
|
||||
| **Indonesian** | `code--id` | Bahasa Indonesia |
|
||||
| **Urdu** | `code--ur` | اردو |
|
||||
| **Italian** | `code--it` | Italiano |
|
||||
| **Japanese** | `code--ja` | 日本語 |
|
||||
| **Korean** | `code--ko` | 한국어 |
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
---
|
||||
title: OpenClaw Integration
|
||||
description: Persistent memory for OpenClaw agents — observation recording, MEMORY.md live sync, and real-time observation feeds
|
||||
icon: dragon
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The OpenClaw plugin gives claude-mem persistent memory to agents running on the [OpenClaw](https://openclaw.ai) gateway. It handles three things:
|
||||
|
||||
1. **Observation recording** — Captures tool usage from OpenClaw's embedded runner and sends it to the claude-mem worker for AI processing
|
||||
2. **MEMORY.md live sync** — Writes a continuously-updated timeline to each agent's workspace so agents always have context from previous sessions
|
||||
3. **Observation feed** — Streams new observations to messaging channels (Telegram, Discord, Slack, etc.) in real-time via SSE
|
||||
|
||||
<Info>
|
||||
OpenClaw's embedded runner (`pi-embedded`) calls the Anthropic API directly without spawning a `claude` process, so claude-mem's standard hooks never fire. This plugin bridges that gap by using OpenClaw's event system to capture the same data.
|
||||
</Info>
|
||||
|
||||
## How It Works
|
||||
|
||||
```plaintext
|
||||
OpenClaw Gateway
|
||||
│
|
||||
├── before_agent_start ──→ Sync MEMORY.md + Init session
|
||||
├── tool_result_persist ──→ Record observation + Re-sync MEMORY.md
|
||||
├── agent_end ────────────→ Summarize + Complete session
|
||||
└── gateway_start ────────→ Reset session tracking
|
||||
│
|
||||
▼
|
||||
Claude-Mem Worker (localhost:37777)
|
||||
├── POST /api/sessions/init
|
||||
├── POST /api/sessions/observations
|
||||
├── POST /api/sessions/summarize
|
||||
├── POST /api/sessions/complete
|
||||
├── GET /api/context/inject ──→ MEMORY.md content
|
||||
└── GET /stream ─────────────→ SSE → Messaging channels
|
||||
```
|
||||
|
||||
### Event Lifecycle
|
||||
|
||||
<Steps>
|
||||
<Step title="Agent starts (before_agent_start)">
|
||||
When an OpenClaw agent starts, the plugin does two things:
|
||||
|
||||
1. **Syncs MEMORY.md** — Fetches the latest timeline from the worker's `/api/context/inject` endpoint and writes it to `MEMORY.md` in the agent's workspace directory. This gives the agent context from all previous sessions before it starts working.
|
||||
|
||||
2. **Initializes a session** — Sends the user prompt to `POST /api/sessions/init` so the worker can create a new session and start processing.
|
||||
|
||||
Short prompts (under 10 characters) skip session init but still sync MEMORY.md.
|
||||
</Step>
|
||||
<Step title="Tool use recorded (tool_result_persist)">
|
||||
Every time the agent uses a tool (Read, Write, Bash, etc.), the plugin:
|
||||
|
||||
1. **Sends the observation** to `POST /api/sessions/observations` with the tool name, input, and truncated response (max 1000 chars)
|
||||
2. **Re-syncs MEMORY.md** with the latest timeline from the worker
|
||||
|
||||
Both operations are fire-and-forget — they don't block the agent from continuing work. The MEMORY.md file gets progressively richer as the session continues.
|
||||
|
||||
Tools prefixed with `memory_` are skipped to avoid recursive recording.
|
||||
</Step>
|
||||
<Step title="Agent finishes (agent_end)">
|
||||
When the agent completes, the plugin extracts the last assistant message and sends it to `POST /api/sessions/summarize`, then calls `POST /api/sessions/complete` to close the session. Both are fire-and-forget.
|
||||
</Step>
|
||||
<Step title="Gateway restarts (gateway_start)">
|
||||
Clears all session tracking (session IDs, workspace directory mappings) so agents get fresh state after a gateway restart.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### MEMORY.md Live Sync
|
||||
|
||||
The plugin writes a `MEMORY.md` file to each agent's workspace directory containing the full timeline of observations and summaries from previous sessions. This file is updated:
|
||||
|
||||
- On every `before_agent_start` event (agent gets fresh context before starting)
|
||||
- On every `tool_result_persist` event (context stays current during the session)
|
||||
|
||||
The content comes from the worker's `GET /api/context/inject?projects=<project>` endpoint, which generates a formatted markdown timeline from the SQLite database.
|
||||
|
||||
<Info>
|
||||
MEMORY.md updates are fire-and-forget. They run in the background without blocking the agent. The file reflects whatever the worker has processed so far — it doesn't wait for the current observation to be fully processed before writing.
|
||||
</Info>
|
||||
|
||||
### Observation Feed (SSE → Messaging)
|
||||
|
||||
The plugin runs a background service that connects to the worker's SSE stream (`GET /stream`) and forwards `new_observation` events to a configured messaging channel. This lets you monitor what your agents are learning in real-time from Telegram, Discord, Slack, or any supported OpenClaw channel.
|
||||
|
||||
The SSE connection uses exponential backoff (1s → 30s) for automatic reconnection.
|
||||
|
||||
## Setting Up the Observation Feed
|
||||
|
||||
The observation feed sends a formatted message to your OpenClaw channel every time claude-mem creates a new observation. Each message includes the observation title and subtitle so you can follow along as your agents work.
|
||||
|
||||
Messages look like this in your channel:
|
||||
|
||||
```
|
||||
🧠 Claude-Mem Observation
|
||||
**Implemented retry logic for API client**
|
||||
Added exponential backoff with configurable max retries to handle transient failures
|
||||
```
|
||||
|
||||
### Step 1: Choose your channel
|
||||
|
||||
The observation feed works with any channel that your OpenClaw gateway has configured. You need two pieces of information:
|
||||
|
||||
- **Channel type** — The name of the channel plugin registered with OpenClaw (e.g., `telegram`, `discord`, `slack`, `signal`, `whatsapp`, `line`)
|
||||
- **Target ID** — The chat ID, channel ID, or user ID where messages should be sent
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Telegram" icon="telegram">
|
||||
**Channel type:** `telegram`
|
||||
|
||||
**Target ID:** Your Telegram chat ID (numeric). To find it:
|
||||
1. Message [@userinfobot](https://t.me/userinfobot) on Telegram
|
||||
2. It will reply with your chat ID (e.g., `123456789`)
|
||||
3. For group chats, the ID is negative (e.g., `-1001234567890`)
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "123456789"
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Discord" icon="discord">
|
||||
**Channel type:** `discord`
|
||||
|
||||
**Target ID:** The Discord channel ID. To find it:
|
||||
1. Enable Developer Mode in Discord (Settings → Advanced → Developer Mode)
|
||||
2. Right-click the channel → Copy Channel ID
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "discord",
|
||||
"to": "1234567890123456789"
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Slack" icon="slack">
|
||||
**Channel type:** `slack`
|
||||
|
||||
**Target ID:** The Slack channel ID (not the channel name). To find it:
|
||||
1. Open the channel in Slack
|
||||
2. Click the channel name at the top
|
||||
3. Scroll to the bottom of the channel details — the ID looks like `C01ABC2DEFG`
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "slack",
|
||||
"to": "C01ABC2DEFG"
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Signal" icon="signal-messenger">
|
||||
**Channel type:** `signal`
|
||||
|
||||
**Target ID:** The Signal phone number or group ID configured in your OpenClaw gateway.
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "signal",
|
||||
"to": "+1234567890"
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="WhatsApp" icon="whatsapp">
|
||||
**Channel type:** `whatsapp`
|
||||
|
||||
**Target ID:** The WhatsApp phone number or group JID configured in your OpenClaw gateway.
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "whatsapp",
|
||||
"to": "+1234567890"
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="LINE" icon="line">
|
||||
**Channel type:** `line`
|
||||
|
||||
**Target ID:** The LINE user ID or group ID from the LINE Developer Console.
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "line",
|
||||
"to": "U1234567890abcdef"
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Step 2: Add the config to your gateway
|
||||
|
||||
Add the `observationFeed` block to your claude-mem plugin config in your OpenClaw gateway configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"project": "my-project",
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "123456789"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The `channel` value must match a channel plugin that is already configured and running on your OpenClaw gateway. If the channel isn't registered, you'll see `Unknown channel type: <channel>` in the logs.
|
||||
</Warning>
|
||||
|
||||
### Step 3: Verify the connection
|
||||
|
||||
After starting the gateway, check that the feed is connected:
|
||||
|
||||
1. **Check the logs** — You should see:
|
||||
```
|
||||
[claude-mem] Observation feed starting — channel: telegram, target: 123456789
|
||||
[claude-mem] Connecting to SSE stream at http://localhost:37777/stream
|
||||
[claude-mem] Connected to SSE stream
|
||||
```
|
||||
|
||||
2. **Use the status command** — Run `/claude-mem-feed` in any OpenClaw chat to see:
|
||||
```
|
||||
Claude-Mem Observation Feed
|
||||
Enabled: yes
|
||||
Channel: telegram
|
||||
Target: 123456789
|
||||
Connection: connected
|
||||
```
|
||||
|
||||
3. **Trigger a test** — Have an agent do some work. When the worker processes the tool usage into an observation, you'll receive a message in your configured channel.
|
||||
|
||||
<Info>
|
||||
The feed only sends `new_observation` events — not raw tool usage. Observations are generated asynchronously by the worker's AI agent, so there's a 1-2 second delay between tool use and the observation message appearing in your channel.
|
||||
</Info>
|
||||
|
||||
### Troubleshooting the Feed
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
|---------|-------|-----|
|
||||
| `Connection: disconnected` | Worker not running or wrong port | Check `workerPort` config, run `npm run worker:status` |
|
||||
| `Connection: reconnecting` | Worker was running but connection dropped | The plugin auto-reconnects with backoff — wait up to 30s |
|
||||
| `Unknown channel type` in logs | Channel plugin not loaded on gateway | Verify your OpenClaw gateway has the channel plugin configured |
|
||||
| No messages appearing | Feed connected but no observations being created | Check that agents are running and the worker is processing observations |
|
||||
| `Observation feed disabled` in logs | `enabled` is `false` or missing | Set `observationFeed.enabled` to `true` |
|
||||
| `Observation feed misconfigured` in logs | Missing `channel` or `to` | Both `channel` and `to` are required |
|
||||
|
||||
## Installation
|
||||
|
||||
Add `claude-mem` to your OpenClaw gateway's plugin configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"project": "my-project",
|
||||
"syncMemoryFile": true,
|
||||
"workerPort": 37777,
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "your-chat-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
The claude-mem worker service must be running on the same machine as the OpenClaw gateway. The plugin communicates with it via HTTP on `localhost:37777`.
|
||||
</Note>
|
||||
|
||||
## Configuration
|
||||
|
||||
<ParamField body="project" type="string" default="openclaw">
|
||||
Project name for scoping observations in the memory database. All observations from this gateway will be stored under this project name.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="syncMemoryFile" type="boolean" default={true}>
|
||||
Enable automatic MEMORY.md sync to agent workspaces. Set to `false` if you don't want the plugin writing files to workspace directories.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="workerPort" type="number" default={37777}>
|
||||
Port for the claude-mem worker service. Override if your worker runs on a non-default port.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="observationFeed.enabled" type="boolean" default={false}>
|
||||
Enable live observation streaming to messaging channels.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="observationFeed.channel" type="string">
|
||||
Channel type: `telegram`, `discord`, `signal`, `slack`, `whatsapp`, `line`
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="observationFeed.to" type="string">
|
||||
Target chat/user/channel ID to send observations to.
|
||||
</ParamField>
|
||||
|
||||
## Commands
|
||||
|
||||
### /claude-mem-feed
|
||||
|
||||
Show or toggle the observation feed status.
|
||||
|
||||
```
|
||||
/claude-mem-feed # Show current status
|
||||
/claude-mem-feed on # Request enable
|
||||
/claude-mem-feed off # Request disable
|
||||
```
|
||||
|
||||
### /claude-mem-status
|
||||
|
||||
Check worker health and session status.
|
||||
|
||||
```
|
||||
/claude-mem-status
|
||||
```
|
||||
|
||||
Returns worker status, port, active session count, and observation feed connection state.
|
||||
|
||||
## Architecture
|
||||
|
||||
The plugin uses HTTP calls to the already-running claude-mem worker service rather than spawning subprocesses. This means:
|
||||
|
||||
- No `bun` dependency required on the gateway
|
||||
- No process spawn overhead per event
|
||||
- Uses the same worker API that Claude Code hooks use
|
||||
- All operations are non-blocking (fire-and-forget where possible)
|
||||
|
||||
### Session Tracking
|
||||
|
||||
Each OpenClaw agent session gets a unique `contentSessionId` (format: `openclaw-<sessionKey>-<timestamp>`) that maps to a claude-mem session in the worker. The plugin tracks:
|
||||
|
||||
- `sessionIds` — Maps OpenClaw session keys to content session IDs
|
||||
- `workspaceDirsBySessionKey` — Maps session keys to workspace directories so `tool_result_persist` events can sync MEMORY.md even when the event context doesn't include `workspaceDir`
|
||||
|
||||
Both maps are cleared on `gateway_start`.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Claude-mem worker service running on `localhost:37777` (or configured port)
|
||||
- OpenClaw gateway with plugin support
|
||||
- Network access between gateway and worker (localhost only)
|
||||
@@ -1,131 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 25, 2025
|
||||
|
||||
**gemini-provider.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32789 | 9:49 PM | 🟣 | Gemini AI Provider Integration Merged to Main | ~409 |
|
||||
|
||||
**manual-recovery.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32654 | 8:51 PM | 🔵 | Identified multiple files related to queue recovery | ~375 |
|
||||
|
||||
### Dec 26, 2025
|
||||
|
||||
**openrouter-provider.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32925 | 10:26 PM | 🔵 | OpenRouter Provider Integration Proposed in PR 448 | ~543 |
|
||||
| #32924 | 10:21 PM | 🟣 | OpenRouter Provider Documentation | ~501 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
**claude-desktop.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33651 | 11:44 PM | 🔴 | Migration 17 Wrapped in Transaction with Documentation Updates | ~331 |
|
||||
| #33650 | 11:43 PM | 🔵 | Code Changes Ready for Token Optimizations PR | ~292 |
|
||||
| #33648 | " | ✅ | Documentation Installation Steps Renumbered | ~283 |
|
||||
| #33647 | 11:42 PM | ✅ | Removed Skill Installation Steps from Claude Desktop Documentation | ~347 |
|
||||
| #33646 | " | ✅ | Updated Documentation to Reflect Streamlined 3-Tool MCP Architecture | ~391 |
|
||||
| #33643 | 11:41 PM | 🔵 | Documentation Uses Inconsistent Naming for MCP Server | ~403 |
|
||||
| #33639 | " | 🔵 | Pull Request Review Identified Critical Migration Risk | ~457 |
|
||||
| #33638 | 11:40 PM | 🔵 | Pull Request Review Identified Critical Migration Risk and Token Optimization Success | ~415 |
|
||||
| #33636 | 11:35 PM | ✅ | Major Documentation and Code Cleanup Removed 4,929 Lines | ~381 |
|
||||
| #33598 | 11:15 PM | 🔵 | Filtered MCP search query successfully returning rename history with type constraints | ~386 |
|
||||
| #33597 | 11:14 PM | 🔵 | MCP search tool successfully retrieving mem-search to mcp-search rename history | ~361 |
|
||||
| #33539 | 10:54 PM | ✅ | Updated configuration examples to use mcp-search as MCP server key | ~449 |
|
||||
| #33538 | " | ✅ | Updated Step 3 installation instructions to reference mcp-search MCP server | ~250 |
|
||||
| #33537 | " | ✅ | Updated prerequisites documentation to reference mcp-search MCP server | ~266 |
|
||||
| #33536 | 10:53 PM | 🔵 | Identified documentation file requiring MCP server name update | ~451 |
|
||||
| #33526 | 10:47 PM | 🔵 | Claude Desktop skill installation guide references mem-search server and skill | ~388 |
|
||||
|
||||
**search-tools.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33540 | 10:55 PM | 🔵 | Grep search found mem-search references in internationalized documentation | ~577 |
|
||||
|
||||
**openrouter-provider.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33312 | 3:09 PM | ✅ | OpenRouter Provider Documentation | ~497 |
|
||||
|
||||
### Dec 29, 2025
|
||||
|
||||
**gemini-provider.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34335 | 11:06 PM | 🟣 | Mintlify Documentation Linting Successfully Completed | ~409 |
|
||||
| #34333 | 11:05 PM | 🔴 | Fixed Broken Links in usage/gemini-provider.mdx | ~285 |
|
||||
| #34328 | 11:04 PM | 🔵 | Broken Link in Usage Gemini Provider Documentation | ~330 |
|
||||
| #34320 | 11:03 PM | 🔵 | Mintlify Broken Links Detected in Documentation | ~292 |
|
||||
| #34103 | 9:05 PM | 🔵 | Gemini Provider Documentation Covers Free Tier and Configuration | ~480 |
|
||||
|
||||
**openrouter-provider.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34334 | 11:05 PM | 🔴 | Fixed All Broken Links in usage/openrouter-provider.mdx | ~339 |
|
||||
| #34327 | 11:04 PM | 🔵 | Broken Links in Usage OpenRouter Provider Documentation | ~337 |
|
||||
|
||||
**usage**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34323 | 11:03 PM | 🔵 | Usage Directory Files Confirmed to Exist | ~280 |
|
||||
|
||||
**search-tools.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33763 | 12:27 AM | ✅ | Pull request #480 created for MCP architecture documentation updates | ~423 |
|
||||
| #33760 | 12:26 AM | ✅ | Major documentation overhaul across 6 files with 908 additions | ~367 |
|
||||
| #33702 | 12:09 AM | ⚖️ | Documentation Update Strategy Finalized for MCP Architecture Transition | ~845 |
|
||||
| #33694 | 12:06 AM | 🔵 | Search Tools Documentation Describes Deleted Skill Architecture | ~615 |
|
||||
| #33679 | 12:03 AM | 🔵 | Search Tools Documentation Structure and Skill-Based Architecture | ~473 |
|
||||
|
||||
**claude-desktop.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33703 | 12:10 AM | 🔵 | Final Documentation Review Confirms Update Requirements | ~756 |
|
||||
| #33699 | 12:08 AM | ✅ | Claude Desktop Documentation Successfully Updated for MCP Tools | ~583 |
|
||||
| #33689 | 12:05 AM | 🔴 | Migration 17 Transaction Safety and Documentation Updates | ~436 |
|
||||
| #33681 | 12:03 AM | ✅ | Claude Desktop Documentation Updated for MCP Tools Workflow | ~491 |
|
||||
| #33675 | 12:02 AM | 🔄 | Major Documentation and Code Cleanup in MCP Clarity Branch | ~491 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
**gemini-provider.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36751 | 12:32 AM | 🔵 | Gemini-Related Files Located Across Project | ~242 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**folder-context.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38086 | 10:42 PM | ✅ | Merged PR with comprehensive CLAUDE.md documentation system | ~478 |
|
||||
| #38066 | 9:50 PM | ✅ | v9.0 Documentation Audit Completed with 14 Files Updated | ~547 |
|
||||
| #38064 | " | ⚖️ | 9.0 Release Documentation Audit Complete - Major Gaps Identified | ~997 |
|
||||
| #38053 | 9:47 PM | 🔵 | Folder Context Documentation Exists But Marked As Disabled By Default | ~616 |
|
||||
|
||||
**getting-started.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38042 | 9:44 PM | 🔵 | Getting Started Documentation Review for Live Context Gap | ~411 |
|
||||
|
||||
**claude-desktop.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37617 | 5:32 PM | ⚖️ | PR #558 Review Requirements Categorized by Priority | ~637 |
|
||||
| #37561 | 4:50 PM | 🔵 | Claude Desktop mem-search Skill Documentation Confirms Platform-Specific Feature | ~393 |
|
||||
|
||||
**private-tags.mdx**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37512 | 3:22 PM | 🔵 | Privacy Tag System Release History and Documentation Evolution | ~749 |
|
||||
| #37505 | 3:21 PM | 🔵 | Comprehensive Dual-Tag Privacy System Architecture and Implementation Details | ~915 |
|
||||
</claude-mem-context>
|
||||
@@ -39,7 +39,7 @@ Claude-mem supports Google's Gemini API as an alternative to the Claude Agent SD
|
||||
|---------|--------|---------|-------------|
|
||||
| `CLAUDE_MEM_PROVIDER` | `claude`, `gemini` | `claude` | AI provider for observation extraction |
|
||||
| `CLAUDE_MEM_GEMINI_API_KEY` | string | — | Your Gemini API key |
|
||||
| `CLAUDE_MEM_GEMINI_MODEL` | `gemini-2.5-flash-lite`, `gemini-2.5-flash`, `gemini-3-flash` | `gemini-2.5-flash-lite` | Gemini model to use |
|
||||
| `CLAUDE_MEM_GEMINI_MODEL` | `gemini-2.5-flash-lite`, `gemini-2.5-flash`, `gemini-3-flash-preview` | `gemini-2.5-flash-lite` | Gemini model to use |
|
||||
| `CLAUDE_MEM_GEMINI_BILLING_ENABLED` | `true`, `false` | `false` | Skip rate limiting if billing is enabled on Google Cloud |
|
||||
|
||||
### Using the Settings UI
|
||||
@@ -79,7 +79,7 @@ The settings file takes precedence over the environment variable.
|
||||
|-------|--------------|-------|
|
||||
| `gemini-2.5-flash-lite` | 10 | Default, recommended for free tier (highest RPM) |
|
||||
| `gemini-2.5-flash` | 5 | Higher capability, lower rate limit |
|
||||
| `gemini-3-flash` | 5 | Latest model, lower rate limit |
|
||||
| `gemini-3-flash-preview` | 5 | Latest model, lower rate limit |
|
||||
|
||||
## Provider Switching
|
||||
|
||||
@@ -139,7 +139,7 @@ Google has two rate limit tiers for free usage:
|
||||
|-------|-----|-----|
|
||||
| gemini-2.5-flash-lite | 10 | 250K |
|
||||
| gemini-2.5-flash | 5 | 250K |
|
||||
| gemini-3-flash | 5 | 250K |
|
||||
| gemini-3-flash-preview | 5 | 250K |
|
||||
|
||||
Claude-mem enforces these limits automatically with built-in delays between requests. Processing may be slower but stays within limits.
|
||||
|
||||
@@ -149,7 +149,7 @@ Claude-mem enforces these limits automatically with built-in delays between requ
|
||||
|-------|-----|-----|
|
||||
| gemini-2.5-flash-lite | 4,000 | 4M |
|
||||
| gemini-2.5-flash | 1,000 | 1M |
|
||||
| gemini-3-flash | 1,000 | 1M |
|
||||
| gemini-3-flash-preview | 1,000 | 1M |
|
||||
|
||||
<Tip>
|
||||
**Recommended**: Enable billing on your Google Cloud project to unlock much higher rate limits. You won't be charged unless you exceed the generous free quota. This allows claude-mem to process observations instantly instead of waiting between requests.
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
# Anti-Pattern Czar Generalization Analysis
|
||||
|
||||
*Generated: January 10, 2026*
|
||||
|
||||
This report analyzes whether the `/anti-pattern-czar` command and its underlying detector script can be generalized for use in any TypeScript codebase or other programming languages.
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The anti-pattern detection system in claude-mem consists of two components:
|
||||
1. **`/anti-pattern-czar`** - An interactive workflow command for detecting and fixing error handling anti-patterns
|
||||
2. **`detect-error-handling-antipatterns.ts`** - The underlying static analysis script
|
||||
|
||||
**Verdict:** The core detection patterns are highly generalizable to any TypeScript/JavaScript codebase. However, the current implementation has claude-mem-specific hardcoding that would need to be extracted into configuration for broader use.
|
||||
|
||||
---
|
||||
|
||||
## Current Implementation Analysis
|
||||
|
||||
### Detection Methodology
|
||||
|
||||
The script uses **purely regex-based detection** (no AST parsing) with two phases:
|
||||
|
||||
1. **Line-by-Line Pattern Matching** - Scans for known anti-patterns:
|
||||
- `ERROR_STRING_MATCHING` - Fragile `error.message.includes('keyword')` checks
|
||||
- `PARTIAL_ERROR_LOGGING` - Logging `error.message` instead of full error object
|
||||
- `ERROR_MESSAGE_GUESSING` - Multiple `.includes()` chains for error classification
|
||||
- `PROMISE_EMPTY_CATCH` - `.catch(() => {})` handlers
|
||||
- `PROMISE_CATCH_NO_LOGGING` - Promise catches without logging
|
||||
|
||||
2. **Try-Catch Block Analysis** - Brace-depth tracking to identify:
|
||||
- `EMPTY_CATCH` - Catch blocks with no meaningful code
|
||||
- `NO_LOGGING_IN_CATCH` - Catch blocks without logging/throwing
|
||||
- `LARGE_TRY_BLOCK` - More than 10 significant lines (uncertain error source)
|
||||
- `GENERIC_CATCH` - No `instanceof` or error type discrimination
|
||||
- `CATCH_AND_CONTINUE_CRITICAL_PATH` - Logging but not failing in critical code
|
||||
|
||||
### Claude-Mem Specific Elements
|
||||
|
||||
| Element | Location | Generalization Required |
|
||||
|---------|----------|-------------------------|
|
||||
| `CRITICAL_PATHS` array | Lines 24-30 | Extract to config file |
|
||||
| Script path in command | anti-pattern-czar.md | Make path configurable |
|
||||
| Severity thresholds | Line 10 limit | Make configurable |
|
||||
| Directory to scan | `src/` hardcoded | Accept as parameter |
|
||||
| Exclusions | `node_modules`, `dist` | Make configurable |
|
||||
|
||||
---
|
||||
|
||||
## Comparison with Industry Tools
|
||||
|
||||
### ESLint Rules Coverage
|
||||
|
||||
| Anti-Pattern | ESLint Equivalent | Coverage Gap |
|
||||
|--------------|-------------------|--------------|
|
||||
| Empty catch blocks | `no-empty` | Fully covered |
|
||||
| Catch-and-rethrow | `no-useless-catch` | Fully covered |
|
||||
| Floating promises | `@typescript-eslint/no-floating-promises` | Fully covered |
|
||||
| Partial error logging | None | **Gap** |
|
||||
| Error string matching | None | **Gap** |
|
||||
| Error message guessing | None | **Gap** |
|
||||
| Large try blocks | `sonarjs/cognitive-complexity` | Partial |
|
||||
| Critical path continuation | None | **Gap** |
|
||||
|
||||
### Unique Value Proposition
|
||||
|
||||
The claude-mem detector catches patterns that **no standard ESLint rule addresses**:
|
||||
|
||||
1. **Partial Error Logging** - Logging `error.message` loses stack traces
|
||||
2. **Error String Matching** - Fragile `if (error.message.includes('timeout'))` patterns
|
||||
3. **Error Message Guessing** - Chained `.includes()` for error classification
|
||||
4. **Critical Path Continuation** - Logging but continuing in code that should fail
|
||||
|
||||
These patterns represent **real debugging nightmares** that caused hours of investigation in claude-mem's development.
|
||||
|
||||
---
|
||||
|
||||
## Generalization Recommendations
|
||||
|
||||
### Tier 1: Quick Generalization (Configuration)
|
||||
|
||||
Extract hardcoded values to a config file:
|
||||
|
||||
```json
|
||||
{
|
||||
"sourceDir": "src/",
|
||||
"criticalPaths": ["**/services/*.ts", "**/core/*.ts"],
|
||||
"excludeDirs": ["node_modules", "dist", "test"],
|
||||
"largeBlockThreshold": 10,
|
||||
"overrideComment": "// [ANTI-PATTERN IGNORED]:"
|
||||
}
|
||||
```
|
||||
|
||||
**Effort:** 2-4 hours
|
||||
|
||||
### Tier 2: ESLint Plugin (Broader Adoption)
|
||||
|
||||
Convert patterns to ESLint custom rules for standard toolchain integration:
|
||||
|
||||
```javascript
|
||||
// eslint-plugin-error-hygiene
|
||||
module.exports = {
|
||||
rules: {
|
||||
'no-partial-error-logging': { /* ... */ },
|
||||
'no-error-string-matching': { /* ... */ },
|
||||
'no-error-message-guessing': { /* ... */ },
|
||||
'critical-path-must-fail': { /* ... */ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
- Integrates with existing toolchains
|
||||
- IDE integration via ESLint plugins
|
||||
- Auto-fix support possible
|
||||
- Community-standard distribution
|
||||
|
||||
**Effort:** 1-2 weeks
|
||||
|
||||
### Tier 3: Multi-Language Support
|
||||
|
||||
The regex patterns could be adapted for:
|
||||
- **Go** - `defer` with empty recover, error checking patterns
|
||||
- **Python** - `except:` without logging, bare `except Exception:`
|
||||
- **Rust** - `.unwrap()` in production paths, `_` pattern for `Result`
|
||||
|
||||
**Effort:** 1 week per language
|
||||
|
||||
---
|
||||
|
||||
## Architecture for General Use
|
||||
|
||||
```
|
||||
error-pattern-detector/
|
||||
├── config/
|
||||
│ ├── default.json # Sensible defaults
|
||||
│ └── schema.json # Config validation
|
||||
├── patterns/
|
||||
│ ├── typescript/ # TS-specific patterns
|
||||
│ │ ├── empty-catch.ts
|
||||
│ │ ├── partial-logging.ts
|
||||
│ │ └── critical-path.ts
|
||||
│ └── shared/ # Cross-language patterns
|
||||
│ ├── large-try-block.ts
|
||||
│ └── swallowed-errors.ts
|
||||
├── reporters/
|
||||
│ ├── console.ts # CLI output
|
||||
│ ├── json.ts # Machine-readable
|
||||
│ ├── sarif.ts # GitHub/IDE integration
|
||||
│ └── markdown.ts # Report generation
|
||||
├── cli.ts # Entry point
|
||||
└── index.ts # Programmatic API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PR #666 Review Context
|
||||
|
||||
The PR review raised a valid concern: the `/anti-pattern-czar` command references a script (`scripts/anti-pattern-test/detect-error-handling-antipatterns.ts`) that only exists in the claude-mem development repository.
|
||||
|
||||
**Options:**
|
||||
|
||||
1. **Keep as development tool** - Don't distribute with plugin (recommended by reviewer)
|
||||
2. **Bundle the detector** - Include the script in the plugin distribution
|
||||
3. **Extract to standalone package** - Publish as `@claude-mem/error-pattern-detector` and depend on it
|
||||
|
||||
Option 3 enables both plugin distribution and community adoption.
|
||||
|
||||
---
|
||||
|
||||
## Conclusions
|
||||
|
||||
### What's Generalizable
|
||||
|
||||
| Component | Generalizability | Notes |
|
||||
|-----------|------------------|-------|
|
||||
| Regex detection patterns | High | Universal to TS/JS |
|
||||
| Brace-depth tracking | High | Works for any curly-brace language |
|
||||
| Override comment syntax | High | Adoptable by any project |
|
||||
| Report formatting | High | Standard markdown output |
|
||||
| 4-step workflow | High | Applicable to any codebase |
|
||||
|
||||
### What's Claude-Mem Specific
|
||||
|
||||
| Component | Specificity | Extraction Effort |
|
||||
|-----------|-------------|-------------------|
|
||||
| Critical path file list | High | Configuration file |
|
||||
| Script location | High | Path parameter |
|
||||
| Severity philosophy | Medium | Documentation |
|
||||
| Exit codes | Low | Already standard |
|
||||
|
||||
### Recommendation
|
||||
|
||||
**Invest in Tier 2 (ESLint Plugin)** - The patterns detected are genuinely unique and valuable. Standard ESLint rules miss these debugging nightmares. An ESLint plugin would:
|
||||
|
||||
1. Enable adoption in any TS/JS project
|
||||
2. Integrate with existing CI/CD pipelines
|
||||
3. Provide IDE feedback in real-time
|
||||
4. Allow community contributions to pattern library
|
||||
5. Create a marketable open-source project
|
||||
|
||||
The name `eslint-plugin-error-hygiene` captures the philosophy: maintaining clean error handling practices to prevent silent failures.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Short-term:** Extract configuration to enable use in other projects
|
||||
2. **Medium-term:** Create ESLint plugin with AST-based detection (more robust than regex)
|
||||
3. **Long-term:** Multi-language support, SARIF output for security tool integration
|
||||
|
||||
---
|
||||
|
||||
*Report generated by analyzing PR #666 review comments, the anti-pattern-czar.md command, and detect-error-handling-antipatterns.ts implementation.*
|
||||
@@ -1,17 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 3, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36651 | 11:03 PM | 🔵 | Critical Design Decision Documented: Memory Session ID Must Never Equal Content Session ID | ~481 |
|
||||
|
||||
### Jan 8, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38731 | 6:49 PM | 🟣 | Comprehensive Sonnet vs Opus Behavioral Analysis Report Generated and Saved | ~700 |
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,336 @@
|
||||
# Intentional Patterns Validation Report
|
||||
|
||||
**Generated:** 2026-01-13
|
||||
**Purpose:** Validate whether "intentional" patterns in worker-service.ts are truly justified
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Pattern | Verdict | Evidence Quality | Recommendation |
|
||||
|---------|---------|------------------|----------------|
|
||||
| Exit code 0 always | **JUSTIFIED** | HIGH | Keep (well documented) |
|
||||
| Circular import re-export | **UNNECESSARY** | HIGH | Remove (no actual circular dep) |
|
||||
| Fallback agent without check | **OVERSIGHT** | HIGH | Fix (real bug risk) |
|
||||
| MCP version hardcoded | **COSMETIC** | MEDIUM | Update to match package.json |
|
||||
| Empty MCP capabilities | **INTENTIONAL** | LOW | Add documentation comment |
|
||||
| `as Error` casts | **JUSTIFIED** | HIGH | Keep (documented policy) |
|
||||
|
||||
---
|
||||
|
||||
## Pattern 1: Exit Code 0 Always
|
||||
|
||||
### Evidence
|
||||
|
||||
| Category | Details |
|
||||
|----------|---------|
|
||||
| **Locations** | 8 explicit `process.exit(0)` calls in worker-service.ts |
|
||||
| **Documentation** | CLAUDE.md lines 44-54, CHANGELOG v9.0.2 |
|
||||
| **Git History** | Commit 222a73da (Jan 8, 2026) - detailed explanation |
|
||||
| **Tests** | 23 passing tests in `worker-json-status.test.ts` |
|
||||
| **Comments** | 5 detailed comments at each exit point |
|
||||
|
||||
### Justification
|
||||
|
||||
```markdown
|
||||
## Exit Code Strategy (from CLAUDE.md)
|
||||
|
||||
- **Exit 0**: Success or graceful shutdown (Windows Terminal closes tabs)
|
||||
- **Exit 1**: Non-blocking error (stderr shown to user, continues)
|
||||
- **Exit 2**: Blocking error (stderr fed to Claude for processing)
|
||||
|
||||
**Philosophy**: Worker/hook errors exit with code 0 to prevent Windows Terminal
|
||||
tab accumulation. The wrapper/plugin layer handles restart logic.
|
||||
```
|
||||
|
||||
### Commit Evidence
|
||||
|
||||
```
|
||||
commit 222a73da5dc875e666c3dd2c96c9d178dd7b884d
|
||||
Date: Thu Jan 8 15:02:56 2026 -0500
|
||||
|
||||
fix: graceful exit strategy to prevent Windows Terminal tab accumulation (#625)
|
||||
|
||||
Problem:
|
||||
Windows Terminal keeps tabs open when processes exit with code 1, leading
|
||||
to tab accumulation during worker lifecycle operations.
|
||||
|
||||
Solution:
|
||||
Implemented graceful exit strategy using exit code 0 for all expected failure
|
||||
scenarios. The wrapper and plugin handle restart logic.
|
||||
```
|
||||
|
||||
### Verdict: **JUSTIFIED**
|
||||
|
||||
- Real Windows Terminal behavior documented
|
||||
- Comprehensive test coverage validating pattern
|
||||
- Consistent implementation across all exit points
|
||||
- Error status communicated via JSON, not exit code
|
||||
|
||||
### Risk
|
||||
|
||||
- Breaks Unix convention but trades correctness for UX
|
||||
- Shell scripts calling worker commands won't detect errors via `$?`
|
||||
- Mitigated by JSON status output for programmatic consumers
|
||||
|
||||
---
|
||||
|
||||
## Pattern 2: Circular Import Re-Export
|
||||
|
||||
### The Code (worker-service.ts:77-78)
|
||||
|
||||
```typescript
|
||||
// Re-export updateCursorContextForProject for SDK agents
|
||||
export { updateCursorContextForProject };
|
||||
```
|
||||
|
||||
### Import Chain Analyzed
|
||||
|
||||
```
|
||||
CursorHooksInstaller.ts (defines function)
|
||||
↓
|
||||
worker-service.ts (imports, re-exports)
|
||||
↓
|
||||
ResponseProcessor.ts (imports from worker-service.ts)
|
||||
```
|
||||
|
||||
### Actual Circular Dependency: **NONE EXISTS**
|
||||
|
||||
```
|
||||
CursorHooksInstaller.ts → imports nothing from worker-service.ts ✓
|
||||
ResponseProcessor.ts → only imports the re-exported function ✓
|
||||
```
|
||||
|
||||
ResponseProcessor.ts **could** import directly:
|
||||
|
||||
```typescript
|
||||
// Current (via re-export):
|
||||
import { updateCursorContextForProject } from '../../worker-service.js';
|
||||
|
||||
// Alternative (direct - would work fine):
|
||||
import { updateCursorContextForProject } from '../../integrations/CursorHooksInstaller.js';
|
||||
```
|
||||
|
||||
### Verdict: **UNNECESSARY**
|
||||
|
||||
- Comment claims "avoids circular imports" but no circular dependency exists
|
||||
- Likely a precaution during refactoring that became stale
|
||||
- Harmless but misleading
|
||||
|
||||
### Recommendation
|
||||
|
||||
- **Option A**: Remove re-export, update ResponseProcessor.ts import path
|
||||
- **Option B**: Update comment to explain actual reason (e.g., "API surface simplification")
|
||||
|
||||
---
|
||||
|
||||
## Pattern 3: Fallback Agent Without Verification
|
||||
|
||||
### The Code (worker-service.ts:144-146)
|
||||
|
||||
```typescript
|
||||
this.geminiAgent.setFallbackAgent(this.sdkAgent);
|
||||
this.openRouterAgent.setFallbackAgent(this.sdkAgent);
|
||||
```
|
||||
|
||||
### Fallback Trigger Logic
|
||||
|
||||
```typescript
|
||||
// GeminiAgent.ts:284-294
|
||||
if (shouldFallbackToClaude(error) && this.fallbackAgent) {
|
||||
logger.warn('SDK', 'Gemini API failed, falling back to Claude SDK', {...});
|
||||
return this.fallbackAgent.startSession(session, worker);
|
||||
}
|
||||
```
|
||||
|
||||
### Problem Scenario
|
||||
|
||||
1. User chooses Gemini because they **don't have Claude credentials**
|
||||
2. Gemini encounters transient error (429 rate limit, 503 server error)
|
||||
3. Code attempts fallback to Claude SDK
|
||||
4. Claude SDK fails (no credentials) → **cascading failure**
|
||||
5. User sees cryptic error, session lost
|
||||
|
||||
### What's Checked vs What's NOT
|
||||
|
||||
| Check | Implemented |
|
||||
|-------|-------------|
|
||||
| `this.fallbackAgent` is not null | ✅ Yes |
|
||||
| Fallback agent initialized successfully | ❌ No |
|
||||
| Fallback agent has valid credentials | ❌ No |
|
||||
| Fallback agent can make API calls | ❌ No |
|
||||
|
||||
### Verdict: **OVERSIGHT - Real Bug Risk**
|
||||
|
||||
- Documentation claims "seamless fallback"
|
||||
- No health check verifies fallback is functional
|
||||
- Users without Claude credentials face silent failure mode
|
||||
|
||||
### Recommendation
|
||||
|
||||
Add verification at initialization:
|
||||
|
||||
```typescript
|
||||
// Option 1: Verify fallback can initialize
|
||||
if (this.sdkAgent.isConfigured()) {
|
||||
this.geminiAgent.setFallbackAgent(this.sdkAgent);
|
||||
}
|
||||
|
||||
// Option 2: Log warning when fallback unavailable
|
||||
if (!this.sdkAgent.isConfigured()) {
|
||||
logger.warn('WORKER', 'Claude SDK not configured - Gemini fallback disabled');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern 4: Hardcoded MCP Version "1.0.0"
|
||||
|
||||
### Locations (3 instances)
|
||||
|
||||
| File | Line | Version |
|
||||
|------|------|---------|
|
||||
| worker-service.ts | 157-160 | `1.0.0` |
|
||||
| ChromaSync.ts | 126-131 | `1.0.0` |
|
||||
| mcp-server.ts | 236-245 | `1.0.0` |
|
||||
|
||||
### Version Mismatch
|
||||
|
||||
| Source | Version |
|
||||
|--------|---------|
|
||||
| package.json | `9.0.4` |
|
||||
| MCP SDK | `1.25.1` |
|
||||
| MCP Client/Server instances | `1.0.0` |
|
||||
|
||||
### Does It Matter?
|
||||
|
||||
**Investigation found:**
|
||||
- MCP servers do NOT validate client version
|
||||
- Connections succeed regardless of version value
|
||||
- Version appears to be for logging/debugging only (like HTTP User-Agent)
|
||||
|
||||
### Verdict: **COSMETIC - Low Priority**
|
||||
|
||||
- Functionally doesn't matter
|
||||
- Inconsistent with package version is confusing
|
||||
- Should be updated for cleanliness
|
||||
|
||||
### Recommendation
|
||||
|
||||
```typescript
|
||||
// Update to use package version
|
||||
import { version } from '../../package.json' assert { type: 'json' };
|
||||
|
||||
this.mcpClient = new Client({
|
||||
name: 'worker-search-proxy',
|
||||
version: version // Use actual package version
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern 5: Empty MCP Capabilities
|
||||
|
||||
### The Code
|
||||
|
||||
```typescript
|
||||
{ capabilities: {} } // All 3 MCP client instances
|
||||
```
|
||||
|
||||
### Investigation
|
||||
|
||||
- MCP specification: **Servers** declare capabilities (tools, resources, prompts)
|
||||
- MCP specification: **Clients** don't typically declare capabilities
|
||||
- No validation found in any MCP server
|
||||
- Pattern works correctly
|
||||
|
||||
### Verdict: **INTENTIONAL - Documentation Gap**
|
||||
|
||||
- Empty capabilities is likely correct for clients
|
||||
- MCP SDK documentation doesn't clarify this
|
||||
- Works fine in practice
|
||||
|
||||
### Recommendation
|
||||
|
||||
Add clarifying comment:
|
||||
|
||||
```typescript
|
||||
// MCP spec: Clients accept all server capabilities; no declaration needed
|
||||
{ capabilities: {} }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern 6: `as Error` Casts
|
||||
|
||||
### Locations (8 in worker-service.ts)
|
||||
|
||||
Lines: 236, 314, 317, 339, 393, 469, 636, 796
|
||||
|
||||
### Why It's Used
|
||||
|
||||
TypeScript 4.0+ catch clauses have `unknown` type:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
// ...
|
||||
} catch (error) { // error: unknown (not Error)
|
||||
logger.error('X', 'msg', {}, error as Error); // Cast needed for logger
|
||||
}
|
||||
```
|
||||
|
||||
### Project Documentation
|
||||
|
||||
**File:** `scripts/anti-pattern-test/CLAUDE.md`
|
||||
|
||||
Establishes explicit error handling policy with:
|
||||
- 5 questions before writing try-catch
|
||||
- Forbidden patterns list
|
||||
- Anti-pattern detection script
|
||||
- Critical paths protection
|
||||
|
||||
### Anti-Pattern Detection
|
||||
|
||||
```bash
|
||||
bun run scripts/anti-pattern-test/detect-error-handling-antipatterns.ts
|
||||
```
|
||||
|
||||
Scans for 7 anti-patterns including:
|
||||
- Empty catch blocks
|
||||
- Catch without logging
|
||||
- Generic error handling
|
||||
|
||||
### Verdict: **JUSTIFIED - Documented Policy**
|
||||
|
||||
- Explicit project convention with tooling support
|
||||
- Alternative (type guards) would add verbosity
|
||||
- Logger requires Error type for stack trace
|
||||
- Pre-commit validation enforces consistency
|
||||
|
||||
---
|
||||
|
||||
## Action Items Summary
|
||||
|
||||
| Pattern | Action | Priority |
|
||||
|---------|--------|----------|
|
||||
| Exit code 0 | Keep as-is | N/A |
|
||||
| Circular import re-export | Remove or fix comment | LOW |
|
||||
| Fallback agent | **Add availability check** | **HIGH** |
|
||||
| MCP version | Update to package.json version | LOW |
|
||||
| Empty capabilities | Add documentation comment | LOW |
|
||||
| `as Error` casts | Keep as-is | N/A |
|
||||
|
||||
---
|
||||
|
||||
## Questions for Your Validation
|
||||
|
||||
1. **Exit code 0**: Is the Windows Terminal workaround acceptable, or should we exit non-zero and document that users need to parse JSON status?
|
||||
|
||||
2. **Circular import**: Should we remove the re-export (cleaner) or update the comment to reflect the real reason?
|
||||
|
||||
3. **Fallback agent**: Should we:
|
||||
- A) Add initialization-time verification
|
||||
- B) Document the limitation and keep as-is
|
||||
- C) Allow users to disable fallback behavior
|
||||
|
||||
4. **MCP version**: Worth updating all 3 instances, or leave as cosmetic debt?
|
||||
@@ -0,0 +1,450 @@
|
||||
# Unjustified Logic Report - worker-service.ts
|
||||
|
||||
**Generated:** 2026-01-13
|
||||
**Source:** `src/services/worker-service.ts` (1445 lines)
|
||||
**Status:** Pending Review
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
23 items identified lacking clear justification. Categorized by severity.
|
||||
|
||||
---
|
||||
|
||||
## HIGH SEVERITY
|
||||
|
||||
### 1. Dead Function: `runInteractiveSetup` (~275 lines)
|
||||
|
||||
**Location:** Lines 837-1111
|
||||
|
||||
```typescript
|
||||
async function runInteractiveSetup(): Promise<number> {
|
||||
// ~275 lines of interactive wizard code
|
||||
}
|
||||
```
|
||||
|
||||
**What it does:** Interactive CLI wizard for Cursor setup.
|
||||
|
||||
**Why it's questionable:** Function is defined but **never called** anywhere. Grep shows only the definition. The `main()` switch handles 'cursor' via `handleCursorCommand`, not this function.
|
||||
|
||||
**Justification status:** No justification found. Appears to be dead code from refactoring.
|
||||
|
||||
---
|
||||
|
||||
## MEDIUM SEVERITY
|
||||
|
||||
### 2. 5-Minute Initialization Timeout
|
||||
|
||||
**Location:** Lines 464-478
|
||||
|
||||
```typescript
|
||||
const timeoutMs = 300000; // 5 minutes
|
||||
await Promise.race([this.initializationComplete, timeoutPromise]);
|
||||
```
|
||||
|
||||
**What it does:** Blocks `/api/context/inject` for up to 5 minutes.
|
||||
|
||||
**Why it's questionable:** HTTP request hanging for 5 minutes is extreme.
|
||||
|
||||
**Justification status:** "5 minutes seems excessive but matches MCP init timeout for consistency" - **circular reasoning**.
|
||||
|
||||
---
|
||||
|
||||
### 3. Redundant Signal Handler Synchronization
|
||||
|
||||
**Location:** Lines 412-434
|
||||
|
||||
```typescript
|
||||
const shutdownRef = { value: this.isShuttingDown };
|
||||
const handler = createSignalHandler(() => this.shutdown(), shutdownRef);
|
||||
process.on('SIGTERM', () => {
|
||||
this.isShuttingDown = shutdownRef.value;
|
||||
handler('SIGTERM');
|
||||
});
|
||||
```
|
||||
|
||||
**What it does:** Creates reference object, passes to handler, copies value back.
|
||||
|
||||
**Why it's questionable:** Overly complex. `this.isShuttingDown` could be used directly via closure.
|
||||
|
||||
**Justification status:** "Signal handler needs mutable reference" - but closure would work.
|
||||
|
||||
---
|
||||
|
||||
### 4. Dual Initialization Tracking (Promise + Flag)
|
||||
|
||||
**Location:** Lines 322-326, 633-634
|
||||
|
||||
```typescript
|
||||
private initializationComplete: Promise<void>;
|
||||
private initializationCompleteFlag: boolean = false;
|
||||
```
|
||||
|
||||
**What it does:** Maintains both Promise and boolean for same state.
|
||||
|
||||
**Why it's questionable:** Two sources of truth. Promise could resolve to boolean, or sync code could use a different pattern.
|
||||
|
||||
**Justification status:** Comments explain separately but not why both needed.
|
||||
|
||||
---
|
||||
|
||||
### 5. Over-Commenting (~40% of file)
|
||||
|
||||
**Location:** Throughout
|
||||
|
||||
```typescript
|
||||
// WHAT: Imports centralized logging utility with structured output
|
||||
// WHY: All worker logs go through this for consistent formatting
|
||||
import { logger } from '../utils/logger.js';
|
||||
```
|
||||
|
||||
**What it does:** WHAT/WHY comments on nearly every line.
|
||||
|
||||
**Why it's questionable:** Many describe obvious code. Creates visual noise. `import { logger }` is self-explanatory.
|
||||
|
||||
**Justification status:** No justification for this density.
|
||||
|
||||
---
|
||||
|
||||
### 6. Exit Code 0 Always (Even on Errors)
|
||||
|
||||
**Location:** Lines 1142, 1272-1287, 1417-1420
|
||||
|
||||
```typescript
|
||||
function exitWithStatus(status: 'ready' | 'error', message?: string): never {
|
||||
console.log(JSON.stringify(output));
|
||||
process.exit(0); // Always 0, even on error
|
||||
}
|
||||
```
|
||||
|
||||
**What it does:** Exits 0 regardless of success/failure.
|
||||
|
||||
**Why it's questionable:** Breaks Unix convention. Hides failures from scripts/monitoring.
|
||||
|
||||
**Justification status:** "Windows Terminal keeps tabs open on non-zero exit" - **trades correctness for UI convenience**.
|
||||
|
||||
---
|
||||
|
||||
### 7. Fallback Agent Without Verification
|
||||
|
||||
**Location:** Lines 357-363
|
||||
|
||||
```typescript
|
||||
this.geminiAgent.setFallbackAgent(this.sdkAgent);
|
||||
this.openRouterAgent.setFallbackAgent(this.sdkAgent);
|
||||
```
|
||||
|
||||
**What it does:** Sets Claude SDK as fallback for alternative providers.
|
||||
|
||||
**Why it's questionable:** User may choose Gemini because they DON'T have Claude subscription. Fallback would fail.
|
||||
|
||||
**Justification status:** "If Gemini fails, falls back to Claude SDK (if available)" - doesn't verify availability.
|
||||
|
||||
---
|
||||
|
||||
### 8. Re-Export to Avoid Circular Import
|
||||
|
||||
**Location:** Line 191
|
||||
|
||||
```typescript
|
||||
export { updateCursorContextForProject };
|
||||
```
|
||||
|
||||
**What it does:** Re-exports imported function.
|
||||
|
||||
**Why it's questionable:** Creates odd import path. Masks architectural issue (circular dependency).
|
||||
|
||||
**Justification status:** "Avoids circular imports" - acknowledges architecture problem.
|
||||
|
||||
---
|
||||
|
||||
## LOW SEVERITY
|
||||
|
||||
### 9. Unused Import: `import * as fs`
|
||||
|
||||
**Location:** Line 22
|
||||
|
||||
```typescript
|
||||
import * as fs from 'fs';
|
||||
```
|
||||
|
||||
**What it does:** Imports fs namespace.
|
||||
|
||||
**Why it's questionable:** Namespace never used. Only specific named imports (line 34) are used.
|
||||
|
||||
**Justification status:** Comment claims "Used for file operations" - **false**.
|
||||
|
||||
---
|
||||
|
||||
### 10. Unused Import: `spawn`
|
||||
|
||||
**Location:** Line 26
|
||||
|
||||
```typescript
|
||||
import { spawn } from 'child_process';
|
||||
```
|
||||
|
||||
**What it does:** Imports spawn function.
|
||||
|
||||
**Why it's questionable:** Never used. MCP spawning uses `StdioClientTransport` internally.
|
||||
|
||||
**Justification status:** Comment claims "Worker spawns MCP server" - **misleading**.
|
||||
|
||||
---
|
||||
|
||||
### 11. `onRestart` = `onShutdown` (Identical Callbacks)
|
||||
|
||||
**Location:** Lines 395-396
|
||||
|
||||
```typescript
|
||||
onShutdown: () => this.shutdown(),
|
||||
onRestart: () => this.shutdown()
|
||||
```
|
||||
|
||||
**What it does:** Both callbacks do the exact same thing.
|
||||
|
||||
**Why it's questionable:** Naming implies different behavior.
|
||||
|
||||
**Justification status:** No justification for why restart just calls shutdown.
|
||||
|
||||
---
|
||||
|
||||
### 12. 100ms Magic Number in Recovery Loop
|
||||
|
||||
**Location:** Line 767
|
||||
|
||||
```typescript
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
```
|
||||
|
||||
**What it does:** 100ms delay between session recovery.
|
||||
|
||||
**Why it's questionable:** Why 100ms specifically? Not 50ms or 200ms?
|
||||
|
||||
**Justification status:** "Prevents thundering herd" - purpose explained, value unexplained.
|
||||
|
||||
---
|
||||
|
||||
### 13. Dynamic Import Already Loaded
|
||||
|
||||
**Location:** Lines 709-710
|
||||
|
||||
```typescript
|
||||
const { PendingMessageStore } = await import('./sqlite/PendingMessageStore.js');
|
||||
```
|
||||
|
||||
**What it does:** Dynamic import in `processPendingQueues`.
|
||||
|
||||
**Why it's questionable:** Same import in `initializeBackground` (line 558). Already loaded by auto-recovery call.
|
||||
|
||||
**Justification status:** "Lazy load because method may not be called often" - **misleading**, always called at startup.
|
||||
|
||||
---
|
||||
|
||||
### 14. Defensive Null Check for Race Condition
|
||||
|
||||
**Location:** Lines 663-669
|
||||
|
||||
```typescript
|
||||
if (!session) return;
|
||||
```
|
||||
|
||||
**What it does:** Early returns if session null.
|
||||
|
||||
**Why it's questionable:** Comment admits "Session could be deleted between queue check and processor start" - hints at design issue.
|
||||
|
||||
**Justification status:** Justified, but suggests architecture problem.
|
||||
|
||||
---
|
||||
|
||||
### 15. Eager Broadcaster Init (Before Server)
|
||||
|
||||
**Location:** Lines 347-349
|
||||
|
||||
```typescript
|
||||
this.sseBroadcaster = new SSEBroadcaster();
|
||||
```
|
||||
|
||||
**What it does:** Creates broadcaster in constructor.
|
||||
|
||||
**Why it's questionable:** Comment says "SSE clients can connect before background init" - but server not started yet.
|
||||
|
||||
**Justification status:** Comment is **technically incorrect**.
|
||||
|
||||
---
|
||||
|
||||
### 16. Hardcoded MCP Version
|
||||
|
||||
**Location:** Lines 385-388
|
||||
|
||||
```typescript
|
||||
this.mcpClient = new Client({
|
||||
name: 'worker-search-proxy',
|
||||
version: '1.0.0' // Hardcoded, doesn't match package.json
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**What it does:** Hardcodes version to 1.0.0.
|
||||
|
||||
**Why it's questionable:** Doesn't match actual package version.
|
||||
|
||||
**Justification status:** No justification for specific version.
|
||||
|
||||
---
|
||||
|
||||
### 17. Nullable SearchRoutes After Init Complete
|
||||
|
||||
**Location:** Lines 314, 479-484
|
||||
|
||||
```typescript
|
||||
private searchRoutes: SearchRoutes | null = null;
|
||||
// After awaiting initializationComplete:
|
||||
if (!this.searchRoutes) {
|
||||
res.status(503).json({ error: 'Search routes not initialized' });
|
||||
}
|
||||
```
|
||||
|
||||
**What it does:** Null check after init should be complete.
|
||||
|
||||
**Why it's questionable:** If init succeeded, should never be null.
|
||||
|
||||
**Justification status:** Explains async nature, not why remains nullable after.
|
||||
|
||||
---
|
||||
|
||||
### 18. Complex ESM/CJS Module Detection
|
||||
|
||||
**Location:** Lines 1433-1439
|
||||
|
||||
```typescript
|
||||
const isMainModule = typeof require !== 'undefined' && typeof module !== 'undefined'
|
||||
? require.main === module || !module.parent
|
||||
: import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith('worker-service');
|
||||
```
|
||||
|
||||
**What it does:** Complex conditional for both module systems.
|
||||
|
||||
**Why it's questionable:** File is ESM-only (uses `import`). CJS checks unnecessary.
|
||||
|
||||
**Justification status:** "Works with both ESM and CommonJS" - but file is ESM-only.
|
||||
|
||||
---
|
||||
|
||||
### 19. Self-Questioning Comment
|
||||
|
||||
**Location:** Line 466
|
||||
|
||||
```typescript
|
||||
// REASON: 5 minutes seems excessive but matches MCP init timeout for consistency
|
||||
```
|
||||
|
||||
**What it does:** Comment admits code is questionable.
|
||||
|
||||
**Why it's questionable:** If author thought excessive when writing, deserves investigation.
|
||||
|
||||
**Justification status:** Self-acknowledged as questionable.
|
||||
|
||||
---
|
||||
|
||||
### 20. `homedir` Import (Only Used in Dead Code)
|
||||
|
||||
**Location:** Line 30
|
||||
|
||||
```typescript
|
||||
import { homedir } from 'os';
|
||||
```
|
||||
|
||||
**What it does:** Imports homedir.
|
||||
|
||||
**Why it's questionable:** Only used in `runInteractiveSetup` (dead code).
|
||||
|
||||
**Justification status:** Unused if dead code removed.
|
||||
|
||||
---
|
||||
|
||||
### 21. Unused Default Parameter
|
||||
|
||||
**Location:** Line 702
|
||||
|
||||
```typescript
|
||||
async processPendingQueues(sessionLimit: number = 10)
|
||||
```
|
||||
|
||||
**What it does:** Default of 10.
|
||||
|
||||
**Why it's questionable:** Only call uses 50 (line 639). Default never used.
|
||||
|
||||
**Justification status:** No justification for 10 vs actual usage of 50.
|
||||
|
||||
---
|
||||
|
||||
### 22. Empty Capabilities Object
|
||||
|
||||
**Location:** Line 388
|
||||
|
||||
```typescript
|
||||
}, { capabilities: {} });
|
||||
```
|
||||
|
||||
**What it does:** Passes empty capabilities to MCP client.
|
||||
|
||||
**Why it's questionable:** No explanation of what capabilities exist or why none needed.
|
||||
|
||||
**Justification status:** No justification found.
|
||||
|
||||
---
|
||||
|
||||
### 23. Unsafe `as Error` Casts
|
||||
|
||||
**Location:** Multiple (lines 513, 651, 771, etc.)
|
||||
|
||||
```typescript
|
||||
}, error as Error);
|
||||
```
|
||||
|
||||
**What it does:** Casts unknown to Error.
|
||||
|
||||
**Why it's questionable:** Caught value might not be Error.
|
||||
|
||||
**Justification status:** Common TypeScript pattern, acceptable but potentially unsafe.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Table
|
||||
|
||||
| # | Issue | Severity | Action |
|
||||
|---|-------|----------|--------|
|
||||
| 1 | Dead `runInteractiveSetup` (~275 lines) | HIGH | Delete |
|
||||
| 2 | 5-minute timeout | MEDIUM | Reduce to 30s |
|
||||
| 3 | Redundant signal sync | MEDIUM | Simplify |
|
||||
| 4 | Dual init tracking | MEDIUM | Unify |
|
||||
| 5 | Over-commenting | MEDIUM | Reduce |
|
||||
| 6 | Exit 0 always | MEDIUM | Reconsider |
|
||||
| 7 | Fallback without check | MEDIUM | Verify availability |
|
||||
| 8 | Re-export for circular | MEDIUM | Fix architecture |
|
||||
| 9 | Unused `fs` namespace | LOW | Delete |
|
||||
| 10 | Unused `spawn` | LOW | Delete |
|
||||
| 11 | Identical callbacks | LOW | Clarify/merge |
|
||||
| 12 | 100ms magic number | LOW | Document or configure |
|
||||
| 13 | Redundant dynamic import | LOW | Remove |
|
||||
| 14 | Defensive null (design smell) | LOW | Review architecture |
|
||||
| 15 | Early broadcaster init | LOW | Fix comment |
|
||||
| 16 | Hardcoded MCP version | LOW | Use package.json |
|
||||
| 17 | Nullable after init | LOW | Clarify lifecycle |
|
||||
| 18 | CJS checks in ESM | LOW | Remove |
|
||||
| 19 | Self-questioning comment | LOW | Investigate |
|
||||
| 20 | `homedir` in dead code | LOW | Delete with dead code |
|
||||
| 21 | Unused default param | LOW | Remove or document |
|
||||
| 22 | Empty capabilities | LOW | Document |
|
||||
| 23 | Unsafe error casts | LOW | Add type guards |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. **Immediate:** Delete dead `runInteractiveSetup` function (275 lines, ~19% of file)
|
||||
2. **Immediate:** Remove unused imports (`fs` namespace, `spawn`)
|
||||
3. **Short-term:** Reduce 5-minute timeout to 30 seconds
|
||||
4. **Short-term:** Simplify signal handler pattern
|
||||
5. **Consider:** Reduce comment density to improve readability
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
@@ -0,0 +1,69 @@
|
||||
# Dockerfile.e2e — End-to-end test: install claude-mem plugin on a real OpenClaw instance
|
||||
# Simulates the complete plugin installation flow a user would follow.
|
||||
#
|
||||
# Usage:
|
||||
# docker build -f Dockerfile.e2e -t openclaw-e2e-test . && docker run --rm openclaw-e2e-test
|
||||
#
|
||||
# Interactive (for human testing):
|
||||
# docker run --rm -it openclaw-e2e-test /bin/bash
|
||||
|
||||
FROM ghcr.io/openclaw/openclaw:main
|
||||
|
||||
USER root
|
||||
|
||||
# Install curl for health checks in e2e-verify.sh, and TypeScript for building
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
|
||||
RUN npm install -g typescript@5
|
||||
|
||||
# Create staging directory for the plugin source
|
||||
WORKDIR /tmp/claude-mem-plugin
|
||||
|
||||
# Copy plugin source files
|
||||
COPY package.json tsconfig.json openclaw.plugin.json ./
|
||||
COPY src/ ./src/
|
||||
|
||||
# Build the plugin (TypeScript → JavaScript)
|
||||
# NODE_ENV=production is set in the base image; override to install devDependencies
|
||||
RUN NODE_ENV=development npm install && npx tsc
|
||||
|
||||
# Create the installable plugin package:
|
||||
# OpenClaw `plugins install` expects package.json with openclaw.extensions field.
|
||||
# The package name must match the plugin ID in openclaw.plugin.json (claude-mem).
|
||||
# Only include the main plugin entry point, not test/mock files.
|
||||
RUN mkdir -p /tmp/claude-mem-installable/dist && \
|
||||
cp dist/index.js /tmp/claude-mem-installable/dist/ && \
|
||||
cp dist/index.d.ts /tmp/claude-mem-installable/dist/ 2>/dev/null || true && \
|
||||
cp openclaw.plugin.json /tmp/claude-mem-installable/ && \
|
||||
node -e " \
|
||||
const pkg = { \
|
||||
name: 'claude-mem', \
|
||||
version: '1.0.0', \
|
||||
type: 'module', \
|
||||
main: 'dist/index.js', \
|
||||
openclaw: { extensions: ['./dist/index.js'] } \
|
||||
}; \
|
||||
require('fs').writeFileSync('/tmp/claude-mem-installable/package.json', JSON.stringify(pkg, null, 2)); \
|
||||
"
|
||||
|
||||
# Switch back to app directory and node user for installation
|
||||
WORKDIR /app
|
||||
USER node
|
||||
|
||||
# Create the OpenClaw config directory
|
||||
RUN mkdir -p /home/node/.openclaw
|
||||
|
||||
# Install the plugin using OpenClaw's official CLI
|
||||
RUN node openclaw.mjs plugins install /tmp/claude-mem-installable
|
||||
|
||||
# Enable the plugin
|
||||
RUN node openclaw.mjs plugins enable claude-mem
|
||||
|
||||
# Copy the e2e verification script and mock worker
|
||||
COPY --chown=node:node e2e-verify.sh /app/e2e-verify.sh
|
||||
USER root
|
||||
RUN chmod +x /app/e2e-verify.sh && \
|
||||
cp /tmp/claude-mem-plugin/dist/mock-worker.js /app/mock-worker.js
|
||||
USER node
|
||||
|
||||
# Default: run the automated verification
|
||||
CMD ["/bin/bash", "/app/e2e-verify.sh"]
|
||||
@@ -0,0 +1,418 @@
|
||||
# Claude-Mem OpenClaw Plugin — Setup Guide
|
||||
|
||||
This guide walks through setting up the claude-mem plugin on an OpenClaw gateway from scratch. Follow every step in order. By the end, your agents will have persistent memory across sessions, a live-updating MEMORY.md in their workspace, and optionally a real-time observation feed streaming to a messaging channel.
|
||||
|
||||
## Step 1: Clone the Claude-Mem Repo
|
||||
|
||||
First, clone the claude-mem repository to a location accessible by your OpenClaw gateway. This gives you the worker service source and the plugin code.
|
||||
|
||||
```bash
|
||||
cd /opt # or wherever you want to keep it
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
You'll need **bun** installed for the worker service. If you don't have it:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
```
|
||||
|
||||
## Step 2: Get the Worker Running
|
||||
|
||||
The claude-mem worker is an HTTP service on port 37777. It stores observations, generates summaries, and serves the context timeline. The plugin talks to it over HTTP — it doesn't matter where the worker is running, just that it's reachable on localhost:37777.
|
||||
|
||||
### Check if it's already running
|
||||
|
||||
If this machine also runs Claude Code with claude-mem installed, the worker may already be running:
|
||||
|
||||
```bash
|
||||
curl http://localhost:37777/api/health
|
||||
```
|
||||
|
||||
**Got `{"status":"ok"}`?** The worker is already running. Skip to Step 3.
|
||||
|
||||
**Got connection refused or no response?** The worker isn't running. Continue below.
|
||||
|
||||
### If Claude Code has claude-mem installed
|
||||
|
||||
If claude-mem is installed as a Claude Code plugin (at `~/.claude/plugins/marketplaces/thedotmack/`), start the worker from that installation:
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
curl http://localhost:37777/api/health
|
||||
```
|
||||
|
||||
**Got `{"status":"ok"}`?** You're set. Skip to Step 3.
|
||||
|
||||
**Still not working?** Check `npm run worker:status` for error details, or check that bun is installed and on your PATH.
|
||||
|
||||
### If there's no Claude Code installation
|
||||
|
||||
Run the worker from the cloned repo:
|
||||
|
||||
```bash
|
||||
cd /opt/claude-mem # wherever you cloned it
|
||||
npm run worker:start
|
||||
```
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
curl http://localhost:37777/api/health
|
||||
```
|
||||
|
||||
**Got `{"status":"ok"}`?** You're set. Move to Step 3.
|
||||
|
||||
**Still not working?** Debug steps:
|
||||
- Check that bun is installed: `bun --version`
|
||||
- Check the worker status: `npm run worker:status`
|
||||
- Check if something else is using port 37777: `lsof -i :37777`
|
||||
- Check logs: `npm run worker:logs` (if available)
|
||||
- Try running it directly to see errors: `bun plugin/scripts/worker-service.cjs start`
|
||||
|
||||
## Step 3: Add the Plugin to Your Gateway
|
||||
|
||||
Add the `claude-mem` plugin to your OpenClaw gateway configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"project": "my-project",
|
||||
"syncMemoryFile": true,
|
||||
"workerPort": 37777
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Config fields explained
|
||||
|
||||
- **`project`** (string, default: `"openclaw"`) — The project name that scopes all observations in the memory database. Use a unique name per gateway/use-case so observations don't mix. For example, if this gateway runs a coding bot, use `"coding-bot"`.
|
||||
|
||||
- **`syncMemoryFile`** (boolean, default: `true`) — When enabled, the plugin writes a `MEMORY.md` file to each agent's workspace directory. This file contains the full timeline of observations and summaries from previous sessions, and it updates on every tool use so agents always have fresh context. Set to `false` only if you don't want the plugin writing files to agent workspaces.
|
||||
|
||||
- **`workerPort`** (number, default: `37777`) — The port where the claude-mem worker service is listening. Only change this if you configured the worker to use a different port.
|
||||
|
||||
## Step 4: Restart the Gateway and Verify
|
||||
|
||||
Restart your OpenClaw gateway so it picks up the new plugin configuration. After restart, check the gateway logs for:
|
||||
|
||||
```
|
||||
[claude-mem] OpenClaw plugin loaded — v1.0.0 (worker: 127.0.0.1:37777)
|
||||
```
|
||||
|
||||
If you see this, the plugin is loaded. You can also verify by running `/claude-mem-status` in any OpenClaw chat:
|
||||
|
||||
```
|
||||
Claude-Mem Worker Status
|
||||
Status: ok
|
||||
Port: 37777
|
||||
Active sessions: 0
|
||||
Observation feed: disconnected
|
||||
```
|
||||
|
||||
The observation feed shows `disconnected` because we haven't configured it yet. That's next.
|
||||
|
||||
## Step 5: Verify Observations Are Being Recorded
|
||||
|
||||
Have an agent do some work. The plugin automatically records observations through these OpenClaw events:
|
||||
|
||||
1. **`before_agent_start`** — Initializes a claude-mem session when the agent starts, syncs MEMORY.md to the workspace
|
||||
2. **`tool_result_persist`** — Records each tool use (Read, Write, Bash, etc.) as an observation, re-syncs MEMORY.md
|
||||
3. **`agent_end`** — Summarizes the session and marks it complete
|
||||
|
||||
All of this happens automatically. No additional configuration needed.
|
||||
|
||||
To verify it's working, check the agent's workspace directory for a `MEMORY.md` file after the agent runs. It should contain a formatted timeline of observations.
|
||||
|
||||
You can also check the worker's viewer UI at http://localhost:37777 to see observations appearing in real time.
|
||||
|
||||
## Step 6: Set Up the Observation Feed (Streaming to a Channel)
|
||||
|
||||
The observation feed connects to the claude-mem worker's SSE (Server-Sent Events) stream and forwards every new observation to a messaging channel in real time. Your agents learn things, and you see them learning in your Telegram/Discord/Slack/etc.
|
||||
|
||||
### What you'll see
|
||||
|
||||
Every time claude-mem creates a new observation from your agent's tool usage, a message like this appears in your channel:
|
||||
|
||||
```
|
||||
🧠 Claude-Mem Observation
|
||||
**Implemented retry logic for API client**
|
||||
Added exponential backoff with configurable max retries to handle transient failures
|
||||
```
|
||||
|
||||
### Pick your channel
|
||||
|
||||
You need two things:
|
||||
- **Channel type** — Must match a channel plugin already running on your OpenClaw gateway
|
||||
- **Target ID** — The chat/channel/user ID where messages go
|
||||
|
||||
#### Telegram
|
||||
|
||||
Channel type: `telegram`
|
||||
|
||||
To find your chat ID:
|
||||
1. Message @userinfobot on Telegram — https://t.me/userinfobot
|
||||
2. It replies with your numeric chat ID (e.g., `123456789`)
|
||||
3. For group chats, the ID is negative (e.g., `-1001234567890`)
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "123456789"
|
||||
}
|
||||
```
|
||||
|
||||
#### Discord
|
||||
|
||||
Channel type: `discord`
|
||||
|
||||
To find your channel ID:
|
||||
1. Enable Developer Mode in Discord: Settings → Advanced → Developer Mode
|
||||
2. Right-click the target channel → Copy Channel ID
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "discord",
|
||||
"to": "1234567890123456789"
|
||||
}
|
||||
```
|
||||
|
||||
#### Slack
|
||||
|
||||
Channel type: `slack`
|
||||
|
||||
To find your channel ID (not the channel name):
|
||||
1. Open the channel in Slack
|
||||
2. Click the channel name at the top
|
||||
3. Scroll to the bottom of the channel details — the ID looks like `C01ABC2DEFG`
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "slack",
|
||||
"to": "C01ABC2DEFG"
|
||||
}
|
||||
```
|
||||
|
||||
#### Signal
|
||||
|
||||
Channel type: `signal`
|
||||
|
||||
Use the phone number or group ID configured in your OpenClaw gateway's Signal plugin.
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "signal",
|
||||
"to": "+1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
#### WhatsApp
|
||||
|
||||
Channel type: `whatsapp`
|
||||
|
||||
Use the phone number or group JID configured in your OpenClaw gateway's WhatsApp plugin.
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "whatsapp",
|
||||
"to": "+1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
#### LINE
|
||||
|
||||
Channel type: `line`
|
||||
|
||||
Use the user ID or group ID from the LINE Developer Console.
|
||||
|
||||
```json
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "line",
|
||||
"to": "U1234567890abcdef"
|
||||
}
|
||||
```
|
||||
|
||||
### Add it to your config
|
||||
|
||||
Your complete plugin config should now look like this (using Telegram as an example):
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"project": "my-project",
|
||||
"syncMemoryFile": true,
|
||||
"workerPort": 37777,
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "123456789"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Restart and verify
|
||||
|
||||
Restart the gateway. Check the logs for these three lines in order:
|
||||
|
||||
```
|
||||
[claude-mem] Observation feed starting — channel: telegram, target: 123456789
|
||||
[claude-mem] Connecting to SSE stream at http://localhost:37777/stream
|
||||
[claude-mem] Connected to SSE stream
|
||||
```
|
||||
|
||||
Then run `/claude-mem-feed` in any OpenClaw chat:
|
||||
|
||||
```
|
||||
Claude-Mem Observation Feed
|
||||
Enabled: yes
|
||||
Channel: telegram
|
||||
Target: 123456789
|
||||
Connection: connected
|
||||
```
|
||||
|
||||
If `Connection` shows `connected`, you're done. Have an agent do some work and watch observations stream to your channel.
|
||||
|
||||
## Commands Reference
|
||||
|
||||
The plugin registers two commands:
|
||||
|
||||
### /claude-mem-status
|
||||
|
||||
Reports worker health and current session state.
|
||||
|
||||
```
|
||||
/claude-mem-status
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Claude-Mem Worker Status
|
||||
Status: ok
|
||||
Port: 37777
|
||||
Active sessions: 2
|
||||
Observation feed: connected
|
||||
```
|
||||
|
||||
### /claude-mem-feed
|
||||
|
||||
Shows observation feed status. Accepts optional `on`/`off` argument.
|
||||
|
||||
```
|
||||
/claude-mem-feed — show status
|
||||
/claude-mem-feed on — request enable (update config to persist)
|
||||
/claude-mem-feed off — request disable (update config to persist)
|
||||
```
|
||||
|
||||
## How It All Works
|
||||
|
||||
```
|
||||
OpenClaw Gateway
|
||||
│
|
||||
├── before_agent_start ──→ Sync MEMORY.md + Init session
|
||||
├── tool_result_persist ──→ Record observation + Re-sync MEMORY.md
|
||||
├── agent_end ────────────→ Summarize + Complete session
|
||||
└── gateway_start ────────→ Reset session tracking
|
||||
│
|
||||
▼
|
||||
Claude-Mem Worker (localhost:37777)
|
||||
├── POST /api/sessions/init
|
||||
├── POST /api/sessions/observations
|
||||
├── POST /api/sessions/summarize
|
||||
├── POST /api/sessions/complete
|
||||
├── GET /api/context/inject ──→ MEMORY.md content
|
||||
└── GET /stream ─────────────→ SSE → Messaging channels
|
||||
```
|
||||
|
||||
### MEMORY.md live sync
|
||||
|
||||
The plugin writes `MEMORY.md` to each agent's workspace with the full observation timeline. It updates:
|
||||
- On every `before_agent_start` — agent gets fresh context before starting
|
||||
- On every `tool_result_persist` — context stays current as the agent works
|
||||
|
||||
Updates are fire-and-forget (non-blocking). The agent is never held up waiting for MEMORY.md to write.
|
||||
|
||||
### Observation recording
|
||||
|
||||
Every tool use (Read, Write, Bash, etc.) is sent to the claude-mem worker as an observation. The worker's AI agent processes it into a structured observation with title, subtitle, facts, concepts, and narrative. Tools prefixed with `memory_` are skipped to avoid recursive recording.
|
||||
|
||||
### Session lifecycle
|
||||
|
||||
- **`before_agent_start`** — Creates a session in the worker, syncs MEMORY.md. Short prompts (under 10 chars) skip session init but still sync.
|
||||
- **`tool_result_persist`** — Records observation (fire-and-forget), re-syncs MEMORY.md (fire-and-forget). Tool responses are truncated to 1000 characters.
|
||||
- **`agent_end`** — Sends the last assistant message for summarization, then completes the session. Both fire-and-forget.
|
||||
- **`gateway_start`** — Clears all session tracking (session IDs, workspace mappings) so agents start fresh.
|
||||
|
||||
### Observation feed
|
||||
|
||||
A background service connects to the worker's SSE stream and forwards `new_observation` events to a configured messaging channel. The connection auto-reconnects with exponential backoff (1s → 30s max).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | What to check |
|
||||
|---------|---------------|
|
||||
| Worker health check fails | Is bun installed? (`bun --version`). Is something else on port 37777? (`lsof -i :37777`). Try running directly: `bun plugin/scripts/worker-service.cjs start` |
|
||||
| Worker started from Claude Code install but not responding | Check `cd ~/.claude/plugins/marketplaces/thedotmack && npm run worker:status`. May need `npm run worker:restart`. |
|
||||
| Worker started from cloned repo but not responding | Check `cd /path/to/claude-mem && npm run worker:status`. Make sure you ran `npm install && npm run build` first. |
|
||||
| No MEMORY.md appearing | Check that `syncMemoryFile` is not set to `false`. Verify the agent's event context includes `workspaceDir`. |
|
||||
| Observations not being recorded | Check gateway logs for `[claude-mem]` messages. The worker must be running and reachable on localhost:37777. |
|
||||
| Feed shows `disconnected` | Worker's `/stream` endpoint not reachable. Check `workerPort` matches the actual worker port. |
|
||||
| Feed shows `reconnecting` | Connection dropped. The plugin auto-reconnects — wait up to 30 seconds. |
|
||||
| `Unknown channel type` in logs | The channel plugin (e.g., telegram) isn't loaded on your gateway. Make sure the channel is configured and running. |
|
||||
| `Observation feed disabled` in logs | Set `observationFeed.enabled` to `true` in your config. |
|
||||
| `Observation feed misconfigured` in logs | Both `observationFeed.channel` and `observationFeed.to` are required. |
|
||||
| No messages in channel despite `connected` | The feed only sends processed observations, not raw tool usage. There's a 1-2 second delay. Make sure the worker is actually processing observations (check http://localhost:37777). |
|
||||
|
||||
## Full Config Reference
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"project": "openclaw",
|
||||
"syncMemoryFile": true,
|
||||
"workerPort": 37777,
|
||||
"observationFeed": {
|
||||
"enabled": false,
|
||||
"channel": "telegram",
|
||||
"to": "123456789"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `project` | string | `"openclaw"` | Project name scoping observations in the database |
|
||||
| `syncMemoryFile` | boolean | `true` | Write MEMORY.md to agent workspaces |
|
||||
| `workerPort` | number | `37777` | Claude-mem worker service port |
|
||||
| `observationFeed.enabled` | boolean | `false` | Stream observations to a messaging channel |
|
||||
| `observationFeed.channel` | string | — | Channel type: `telegram`, `discord`, `slack`, `signal`, `whatsapp`, `line` |
|
||||
| `observationFeed.to` | string | — | Target chat/channel/user ID |
|
||||
@@ -0,0 +1,279 @@
|
||||
# OpenClaw Claude-Mem Plugin — Testing Guide
|
||||
|
||||
## Quick Start (Docker)
|
||||
|
||||
The fastest way to test the plugin is using the pre-built Docker E2E environment:
|
||||
|
||||
```bash
|
||||
cd openclaw
|
||||
|
||||
# Automated test (builds, installs plugin on real OpenClaw, verifies everything)
|
||||
./test-e2e.sh
|
||||
|
||||
# Interactive shell (for manual exploration)
|
||||
./test-e2e.sh --interactive
|
||||
|
||||
# Just build the image
|
||||
./test-e2e.sh --build-only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Layers
|
||||
|
||||
### 1. Unit Tests (fastest)
|
||||
|
||||
```bash
|
||||
cd openclaw
|
||||
npm test # compiles TypeScript, runs 17 tests
|
||||
```
|
||||
|
||||
Tests plugin registration, service lifecycle, command handling, SSE integration, and all 6 channel types.
|
||||
|
||||
### 2. Smoke Test
|
||||
|
||||
```bash
|
||||
node test-sse-consumer.js
|
||||
```
|
||||
|
||||
Quick check that the plugin loads and registers its service + command correctly.
|
||||
|
||||
### 3. Container Unit Tests (fresh install)
|
||||
|
||||
```bash
|
||||
./test-container.sh # Unit tests in clean Docker
|
||||
./test-container.sh --full # Integration tests with mock worker
|
||||
```
|
||||
|
||||
### 4. E2E on Real OpenClaw (Docker)
|
||||
|
||||
```bash
|
||||
./test-e2e.sh
|
||||
```
|
||||
|
||||
This is the most comprehensive test. It:
|
||||
1. Uses the official `ghcr.io/openclaw/openclaw:main` Docker image
|
||||
2. Installs the plugin via `openclaw plugins install` (same as a real user)
|
||||
3. Enables the plugin via `openclaw plugins enable`
|
||||
4. Starts a mock claude-mem worker on port 37777
|
||||
5. Starts the OpenClaw gateway with plugin config
|
||||
6. Verifies the plugin loads, connects to SSE, and processes events
|
||||
|
||||
**All 16 checks must pass.**
|
||||
|
||||
---
|
||||
|
||||
## Human E2E Testing (Interactive Docker)
|
||||
|
||||
For manual walkthrough testing, use the interactive Docker mode:
|
||||
|
||||
```bash
|
||||
./test-e2e.sh --interactive
|
||||
```
|
||||
|
||||
This drops you into a fully-configured OpenClaw container with the plugin pre-installed.
|
||||
|
||||
### Step-by-step inside the container
|
||||
|
||||
#### 1. Verify plugin is installed
|
||||
|
||||
```bash
|
||||
node openclaw.mjs plugins list
|
||||
node openclaw.mjs plugins info claude-mem
|
||||
node openclaw.mjs plugins doctor
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- `claude-mem` appears in the plugins list as "enabled" or "loaded"
|
||||
- Info shows version 1.0.0, source at `/home/node/.openclaw/extensions/claude-mem/`
|
||||
- Doctor reports no issues
|
||||
|
||||
#### 2. Inspect plugin files
|
||||
|
||||
```bash
|
||||
ls -la /home/node/.openclaw/extensions/claude-mem/
|
||||
cat /home/node/.openclaw/extensions/claude-mem/openclaw.plugin.json
|
||||
cat /home/node/.openclaw/extensions/claude-mem/package.json
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- `dist/index.js` exists (compiled plugin)
|
||||
- `openclaw.plugin.json` has `"id": "claude-mem"` and `"kind": "memory"`
|
||||
- `package.json` has `openclaw.extensions` field pointing to `./dist/index.js`
|
||||
|
||||
#### 3. Start mock worker
|
||||
|
||||
```bash
|
||||
node /app/mock-worker.js &
|
||||
```
|
||||
|
||||
Verify it's running:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:37777/health
|
||||
# → {"status":"ok"}
|
||||
|
||||
curl -s --max-time 3 http://localhost:37777/stream
|
||||
# → data: {"type":"connected","message":"Mock worker SSE stream"}
|
||||
# → data: {"type":"new_observation","observation":{...}}
|
||||
```
|
||||
|
||||
#### 4. Configure and start gateway
|
||||
|
||||
```bash
|
||||
cat > /home/node/.openclaw/openclaw.json << 'EOF'
|
||||
{
|
||||
"gateway": {
|
||||
"mode": "local",
|
||||
"auth": {
|
||||
"mode": "token",
|
||||
"token": "e2e-test-token"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"slots": {
|
||||
"memory": "claude-mem"
|
||||
},
|
||||
"entries": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"workerPort": 37777,
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "test-chat-id-12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
node openclaw.mjs gateway --allow-unconfigured --verbose --token e2e-test-token
|
||||
```
|
||||
|
||||
**Expected in gateway logs:**
|
||||
- `[claude-mem] OpenClaw plugin loaded — v1.0.0`
|
||||
- `[claude-mem] Observation feed starting — channel: telegram, target: test-chat-id-12345`
|
||||
- `[claude-mem] Connecting to SSE stream at http://localhost:37777/stream`
|
||||
- `[claude-mem] Connected to SSE stream`
|
||||
|
||||
#### 5. Run automated verification (optional)
|
||||
|
||||
From a second shell in the container (or after stopping the gateway):
|
||||
|
||||
```bash
|
||||
/bin/bash /app/e2e-verify.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manual E2E (Real OpenClaw + Real Worker)
|
||||
|
||||
For testing with a real claude-mem worker and real messaging channel:
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- OpenClaw gateway installed and configured
|
||||
- Claude-Mem worker running on port 37777
|
||||
- Plugin built: `cd openclaw && npm run build`
|
||||
|
||||
### 1. Install the plugin
|
||||
|
||||
```bash
|
||||
# Build the plugin
|
||||
cd openclaw && npm run build
|
||||
|
||||
# Install on OpenClaw (from the openclaw/ directory)
|
||||
openclaw plugins install .
|
||||
|
||||
# Enable it
|
||||
openclaw plugins enable claude-mem
|
||||
```
|
||||
|
||||
### 2. Configure
|
||||
|
||||
Edit `~/.openclaw/openclaw.json` to add plugin config:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"entries": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"workerPort": 37777,
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "YOUR_CHAT_ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Supported channels:** `telegram`, `discord`, `signal`, `slack`, `whatsapp`, `line`
|
||||
|
||||
### 3. Restart gateway
|
||||
|
||||
```bash
|
||||
openclaw restart
|
||||
```
|
||||
|
||||
**Look for in logs:**
|
||||
- `[claude-mem] OpenClaw plugin loaded — v1.0.0`
|
||||
- `[claude-mem] Connected to SSE stream`
|
||||
|
||||
### 4. Trigger an observation
|
||||
|
||||
Start a Claude Code session with claude-mem enabled and perform any action. The worker will emit a `new_observation` SSE event.
|
||||
|
||||
### 5. Verify delivery
|
||||
|
||||
Check the target messaging channel for:
|
||||
|
||||
```
|
||||
🧠 Claude-Mem Observation
|
||||
**Observation Title**
|
||||
Optional subtitle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `api.log is not a function`
|
||||
The plugin was built against the wrong API. Ensure `src/index.ts` uses `api.logger.info()` not `api.log()`. Rebuild with `npm run build`.
|
||||
|
||||
### Worker not running
|
||||
- **Symptom:** `SSE stream error: fetch failed. Reconnecting in 1s`
|
||||
- **Fix:** Start the worker: `cd /path/to/claude-mem && npm run build-and-sync`
|
||||
|
||||
### Port mismatch
|
||||
- **Fix:** Ensure `workerPort` in config matches the worker's actual port (default: 37777)
|
||||
|
||||
### Channel not configured
|
||||
- **Symptom:** `Observation feed misconfigured — channel or target missing`
|
||||
- **Fix:** Add both `channel` and `to` to `observationFeed` in config
|
||||
|
||||
### Unknown channel type
|
||||
- **Fix:** Use: `telegram`, `discord`, `signal`, `slack`, `whatsapp`, or `line`
|
||||
|
||||
### Feed disabled
|
||||
- **Symptom:** `Observation feed disabled`
|
||||
- **Fix:** Set `observationFeed.enabled: true`
|
||||
|
||||
### Messages not arriving
|
||||
1. Verify the bot/integration is configured in the target channel
|
||||
2. Check the target ID (`to`) is correct
|
||||
3. Look for `Failed to send to <channel>` in logs
|
||||
4. Test the channel via OpenClaw's built-in tools
|
||||
|
||||
### Memory slot conflict
|
||||
- **Symptom:** `plugin disabled (memory slot set to "memory-core")`
|
||||
- **Fix:** Add `"slots": { "memory": "claude-mem" }` to plugins config
|
||||
Executable
+265
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env bash
|
||||
# e2e-verify.sh — Automated E2E verification for claude-mem plugin on OpenClaw
|
||||
#
|
||||
# This script verifies the complete plugin installation and operation flow:
|
||||
# 1. Plugin is installed and visible in OpenClaw
|
||||
# 2. Plugin loads correctly when gateway starts
|
||||
# 3. Mock worker SSE stream is consumed by the plugin
|
||||
# 4. Observations are received and formatted
|
||||
#
|
||||
# Exit 0 = all checks passed, Exit 1 = failure
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
TOTAL=0
|
||||
|
||||
pass() {
|
||||
PASS=$((PASS + 1))
|
||||
TOTAL=$((TOTAL + 1))
|
||||
echo " PASS: $1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
FAIL=$((FAIL + 1))
|
||||
TOTAL=$((TOTAL + 1))
|
||||
echo " FAIL: $1"
|
||||
}
|
||||
|
||||
section() {
|
||||
echo ""
|
||||
echo "=== $1 ==="
|
||||
}
|
||||
|
||||
# ─── Phase 1: Plugin Discovery ───
|
||||
|
||||
section "Phase 1: Plugin Discovery"
|
||||
|
||||
# Check plugin is listed
|
||||
PLUGIN_LIST=$(node /app/openclaw.mjs plugins list 2>&1)
|
||||
if echo "$PLUGIN_LIST" | grep -q "claude-mem"; then
|
||||
pass "Plugin appears in 'plugins list'"
|
||||
else
|
||||
fail "Plugin NOT found in 'plugins list'"
|
||||
echo "$PLUGIN_LIST"
|
||||
fi
|
||||
|
||||
# Check plugin info
|
||||
PLUGIN_INFO=$(node /app/openclaw.mjs plugins info claude-mem 2>&1 || true)
|
||||
if echo "$PLUGIN_INFO" | grep -qi "claude-mem"; then
|
||||
pass "Plugin info shows claude-mem details"
|
||||
else
|
||||
fail "Plugin info failed"
|
||||
echo "$PLUGIN_INFO"
|
||||
fi
|
||||
|
||||
# Check plugin is enabled
|
||||
if echo "$PLUGIN_LIST" | grep -A1 "claude-mem" | grep -qi "enabled\|loaded"; then
|
||||
pass "Plugin is enabled"
|
||||
else
|
||||
# Try to check via info
|
||||
if echo "$PLUGIN_INFO" | grep -qi "enabled\|loaded"; then
|
||||
pass "Plugin is enabled (via info)"
|
||||
else
|
||||
fail "Plugin does not appear enabled"
|
||||
echo "$PLUGIN_INFO"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check plugin doctor reports no issues
|
||||
DOCTOR_OUT=$(node /app/openclaw.mjs plugins doctor 2>&1 || true)
|
||||
if echo "$DOCTOR_OUT" | grep -qi "no.*issue\|0 issue"; then
|
||||
pass "Plugin doctor reports no issues"
|
||||
else
|
||||
fail "Plugin doctor reports issues"
|
||||
echo "$DOCTOR_OUT"
|
||||
fi
|
||||
|
||||
# ─── Phase 2: Plugin Files ───
|
||||
|
||||
section "Phase 2: Plugin Files"
|
||||
|
||||
# Check extension directory exists
|
||||
EXTENSIONS_DIR="/home/node/.openclaw/extensions/openclaw-plugin"
|
||||
if [ ! -d "$EXTENSIONS_DIR" ]; then
|
||||
# Try alternative naming
|
||||
EXTENSIONS_DIR="/home/node/.openclaw/extensions/claude-mem"
|
||||
if [ ! -d "$EXTENSIONS_DIR" ]; then
|
||||
# Search for it
|
||||
FOUND_DIR=$(find /home/node/.openclaw/extensions/ -name "openclaw.plugin.json" -exec dirname {} \; 2>/dev/null | head -1 || true)
|
||||
if [ -n "$FOUND_DIR" ]; then
|
||||
EXTENSIONS_DIR="$FOUND_DIR"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -d "$EXTENSIONS_DIR" ]; then
|
||||
pass "Plugin directory exists: $EXTENSIONS_DIR"
|
||||
else
|
||||
fail "Plugin directory not found under /home/node/.openclaw/extensions/"
|
||||
ls -la /home/node/.openclaw/extensions/ 2>/dev/null || echo " (extensions dir not found)"
|
||||
fi
|
||||
|
||||
# Check key files exist
|
||||
for FILE in "openclaw.plugin.json" "dist/index.js" "package.json"; do
|
||||
if [ -f "$EXTENSIONS_DIR/$FILE" ]; then
|
||||
pass "File exists: $FILE"
|
||||
else
|
||||
fail "File missing: $FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# ─── Phase 3: Mock Worker + Plugin Integration ───
|
||||
|
||||
section "Phase 3: Mock Worker + Plugin Integration"
|
||||
|
||||
# Start mock worker in background
|
||||
echo " Starting mock claude-mem worker..."
|
||||
node /app/mock-worker.js &
|
||||
MOCK_PID=$!
|
||||
|
||||
# Wait for mock worker to be ready
|
||||
for i in $(seq 1 10); do
|
||||
if curl -sf http://localhost:37777/health > /dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if curl -sf http://localhost:37777/health > /dev/null 2>&1; then
|
||||
pass "Mock worker health check passed"
|
||||
else
|
||||
fail "Mock worker health check failed"
|
||||
kill $MOCK_PID 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Test SSE stream connectivity (curl with max-time to capture initial SSE frame)
|
||||
SSE_TEST=$(curl -s --max-time 2 http://localhost:37777/stream 2>/dev/null || true)
|
||||
if echo "$SSE_TEST" | grep -q "connected"; then
|
||||
pass "SSE stream returns connected event"
|
||||
else
|
||||
fail "SSE stream did not return connected event"
|
||||
echo " Got: $(echo "$SSE_TEST" | head -5)"
|
||||
fi
|
||||
|
||||
# ─── Phase 4: Gateway + Plugin Load ───
|
||||
|
||||
section "Phase 4: Gateway Startup with Plugin"
|
||||
|
||||
# Create a minimal config that enables the plugin with the mock worker.
|
||||
# The memory slot must be set to "claude-mem" to match what `plugins install` configured.
|
||||
# Gateway auth is disabled via token for headless testing.
|
||||
mkdir -p /home/node/.openclaw
|
||||
cat > /home/node/.openclaw/openclaw.json << 'EOFCONFIG'
|
||||
{
|
||||
"gateway": {
|
||||
"mode": "local",
|
||||
"auth": {
|
||||
"mode": "token",
|
||||
"token": "e2e-test-token"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"slots": {
|
||||
"memory": "claude-mem"
|
||||
},
|
||||
"entries": {
|
||||
"claude-mem": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"workerPort": 37777,
|
||||
"observationFeed": {
|
||||
"enabled": true,
|
||||
"channel": "telegram",
|
||||
"to": "test-chat-id-12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOFCONFIG
|
||||
|
||||
pass "OpenClaw config written with plugin enabled"
|
||||
|
||||
# Start gateway in background and capture output
|
||||
GATEWAY_LOG="/tmp/gateway.log"
|
||||
echo " Starting OpenClaw gateway (timeout 15s)..."
|
||||
OPENCLAW_GATEWAY_TOKEN=e2e-test-token timeout 15 node /app/openclaw.mjs gateway --allow-unconfigured --verbose --token e2e-test-token > "$GATEWAY_LOG" 2>&1 &
|
||||
GATEWAY_PID=$!
|
||||
|
||||
# Give the gateway time to start and load plugins
|
||||
sleep 5
|
||||
|
||||
# Check if gateway started
|
||||
if kill -0 $GATEWAY_PID 2>/dev/null; then
|
||||
pass "Gateway process is running"
|
||||
else
|
||||
fail "Gateway process exited early"
|
||||
echo " Gateway log:"
|
||||
cat "$GATEWAY_LOG" 2>/dev/null | tail -30
|
||||
fi
|
||||
|
||||
# Check gateway log for plugin load messages
|
||||
if grep -qi "claude-mem" "$GATEWAY_LOG" 2>/dev/null; then
|
||||
pass "Gateway log mentions claude-mem plugin"
|
||||
else
|
||||
fail "Gateway log does not mention claude-mem"
|
||||
echo " Gateway log (last 20 lines):"
|
||||
tail -20 "$GATEWAY_LOG" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Check for plugin loaded message
|
||||
if grep -q "plugin loaded" "$GATEWAY_LOG" 2>/dev/null || grep -q "v1.0.0" "$GATEWAY_LOG" 2>/dev/null; then
|
||||
pass "Plugin load message found in gateway log"
|
||||
else
|
||||
fail "Plugin load message not found"
|
||||
fi
|
||||
|
||||
# Check for observation feed messages
|
||||
if grep -qi "observation feed" "$GATEWAY_LOG" 2>/dev/null; then
|
||||
pass "Observation feed activity in gateway log"
|
||||
else
|
||||
fail "No observation feed activity detected"
|
||||
fi
|
||||
|
||||
# Check for SSE connection to mock worker
|
||||
if grep -qi "connected.*SSE\|SSE.*stream\|connecting.*SSE" "$GATEWAY_LOG" 2>/dev/null; then
|
||||
pass "SSE connection activity detected"
|
||||
else
|
||||
fail "No SSE connection activity in log"
|
||||
fi
|
||||
|
||||
# ─── Cleanup ───
|
||||
|
||||
section "Cleanup"
|
||||
kill $GATEWAY_PID 2>/dev/null || true
|
||||
kill $MOCK_PID 2>/dev/null || true
|
||||
wait $GATEWAY_PID 2>/dev/null || true
|
||||
wait $MOCK_PID 2>/dev/null || true
|
||||
echo " Processes stopped."
|
||||
|
||||
# ─── Summary ───
|
||||
|
||||
echo ""
|
||||
echo "==============================="
|
||||
echo " E2E Test Results"
|
||||
echo "==============================="
|
||||
echo " Total: $TOTAL"
|
||||
echo " Passed: $PASS"
|
||||
echo " Failed: $FAIL"
|
||||
echo "==============================="
|
||||
|
||||
if [ "$FAIL" -gt 0 ]; then
|
||||
echo ""
|
||||
echo " SOME TESTS FAILED"
|
||||
echo ""
|
||||
echo " Full gateway log:"
|
||||
cat "$GATEWAY_LOG" 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " ALL TESTS PASSED"
|
||||
exit 0
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"id": "claude-mem",
|
||||
"name": "Claude-Mem (Persistent Memory)",
|
||||
"description": "Official OpenClaw plugin for Claude-Mem. Records observations from embedded runner sessions and streams them to messaging channels.",
|
||||
"kind": "integration",
|
||||
"version": "1.0.0",
|
||||
"author": "thedotmack",
|
||||
"homepage": "https://claude-mem.com",
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"syncMemoryFile": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Automatically sync MEMORY.md on session start"
|
||||
},
|
||||
"workerPort": {
|
||||
"type": "number",
|
||||
"default": 37777,
|
||||
"description": "Port for Claude-Mem worker service"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"default": "openclaw",
|
||||
"description": "Project name for scoping observations in the memory database"
|
||||
},
|
||||
"observationFeed": {
|
||||
"type": "object",
|
||||
"description": "Live observation feed — streams observations to any OpenClaw channel in real-time",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable live observation feed to messaging channels"
|
||||
},
|
||||
"channel": {
|
||||
"type": "string",
|
||||
"description": "Channel type: telegram, discord, signal, slack, whatsapp, line"
|
||||
},
|
||||
"to": {
|
||||
"type": "string",
|
||||
"description": "Target chat/user ID to send observations to"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@openclaw/claude-mem",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "tsc && node --test dist/index.test.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.2.1",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./dist/index.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,962 @@
|
||||
import { describe, it, beforeEach, afterEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createServer, type Server, type IncomingMessage, type ServerResponse } from "node:http";
|
||||
import { mkdtemp, readFile, rm } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import claudeMemPlugin from "./index.js";
|
||||
|
||||
function createMockApi(pluginConfigOverride: Record<string, any> = {}) {
|
||||
const logs: string[] = [];
|
||||
const sentMessages: Array<{ to: string; text: string; channel: string; opts?: any }> = [];
|
||||
|
||||
let registeredService: any = null;
|
||||
const registeredCommands: Map<string, any> = new Map();
|
||||
const eventHandlers: Map<string, Function[]> = new Map();
|
||||
|
||||
const api = {
|
||||
id: "claude-mem",
|
||||
name: "Claude-Mem (Persistent Memory)",
|
||||
version: "1.0.0",
|
||||
source: "/test/extensions/claude-mem/dist/index.js",
|
||||
config: {},
|
||||
pluginConfig: pluginConfigOverride,
|
||||
logger: {
|
||||
info: (message: string) => { logs.push(message); },
|
||||
warn: (message: string) => { logs.push(message); },
|
||||
error: (message: string) => { logs.push(message); },
|
||||
debug: (message: string) => { logs.push(message); },
|
||||
},
|
||||
registerService: (service: any) => {
|
||||
registeredService = service;
|
||||
},
|
||||
registerCommand: (command: any) => {
|
||||
registeredCommands.set(command.name, command);
|
||||
},
|
||||
on: (event: string, callback: Function) => {
|
||||
if (!eventHandlers.has(event)) {
|
||||
eventHandlers.set(event, []);
|
||||
}
|
||||
eventHandlers.get(event)!.push(callback);
|
||||
},
|
||||
runtime: {
|
||||
channel: {
|
||||
telegram: {
|
||||
sendMessageTelegram: async (to: string, text: string) => {
|
||||
sentMessages.push({ to, text, channel: "telegram" });
|
||||
},
|
||||
},
|
||||
discord: {
|
||||
sendMessageDiscord: async (to: string, text: string) => {
|
||||
sentMessages.push({ to, text, channel: "discord" });
|
||||
},
|
||||
},
|
||||
signal: {
|
||||
sendMessageSignal: async (to: string, text: string) => {
|
||||
sentMessages.push({ to, text, channel: "signal" });
|
||||
},
|
||||
},
|
||||
slack: {
|
||||
sendMessageSlack: async (to: string, text: string) => {
|
||||
sentMessages.push({ to, text, channel: "slack" });
|
||||
},
|
||||
},
|
||||
whatsapp: {
|
||||
sendMessageWhatsApp: async (to: string, text: string, opts?: { verbose: boolean }) => {
|
||||
sentMessages.push({ to, text, channel: "whatsapp", opts });
|
||||
},
|
||||
},
|
||||
line: {
|
||||
sendMessageLine: async (to: string, text: string) => {
|
||||
sentMessages.push({ to, text, channel: "line" });
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
api: api as any,
|
||||
logs,
|
||||
sentMessages,
|
||||
getService: () => registeredService,
|
||||
getCommand: (name?: string) => {
|
||||
if (name) return registeredCommands.get(name);
|
||||
return registeredCommands.get("claude-mem-feed");
|
||||
},
|
||||
getEventHandlers: (event: string) => eventHandlers.get(event) || [],
|
||||
fireEvent: async (event: string, data: any, ctx: any = {}) => {
|
||||
const handlers = eventHandlers.get(event) || [];
|
||||
for (const handler of handlers) {
|
||||
await handler(data, ctx);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("claudeMemPlugin", () => {
|
||||
it("registers service, commands, and event handlers on load", () => {
|
||||
const { api, logs, getService, getCommand, getEventHandlers } = createMockApi();
|
||||
claudeMemPlugin(api);
|
||||
|
||||
assert.ok(getService(), "service should be registered");
|
||||
assert.equal(getService().id, "claude-mem-observation-feed");
|
||||
assert.ok(getCommand("claude-mem-feed"), "feed command should be registered");
|
||||
assert.ok(getCommand("claude-mem-status"), "status command should be registered");
|
||||
assert.ok(getEventHandlers("session_start").length > 0, "session_start handler registered");
|
||||
assert.ok(getEventHandlers("after_compaction").length > 0, "after_compaction handler registered");
|
||||
assert.ok(getEventHandlers("before_agent_start").length > 0, "before_agent_start handler registered");
|
||||
assert.ok(getEventHandlers("tool_result_persist").length > 0, "tool_result_persist handler registered");
|
||||
assert.ok(getEventHandlers("agent_end").length > 0, "agent_end handler registered");
|
||||
assert.ok(getEventHandlers("gateway_start").length > 0, "gateway_start handler registered");
|
||||
assert.ok(logs.some((l) => l.includes("plugin loaded")));
|
||||
});
|
||||
|
||||
describe("service start", () => {
|
||||
it("logs disabled when feed not enabled", async () => {
|
||||
const { api, logs, getService } = createMockApi({});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
assert.ok(logs.some((l) => l.includes("feed disabled")));
|
||||
});
|
||||
|
||||
it("logs disabled when enabled is false", async () => {
|
||||
const { api, logs, getService } = createMockApi({
|
||||
observationFeed: { enabled: false },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
assert.ok(logs.some((l) => l.includes("feed disabled")));
|
||||
});
|
||||
|
||||
it("logs misconfigured when channel is missing", async () => {
|
||||
const { api, logs, getService } = createMockApi({
|
||||
observationFeed: { enabled: true, to: "123" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
assert.ok(logs.some((l) => l.includes("misconfigured")));
|
||||
});
|
||||
|
||||
it("logs misconfigured when to is missing", async () => {
|
||||
const { api, logs, getService } = createMockApi({
|
||||
observationFeed: { enabled: true, channel: "telegram" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
assert.ok(logs.some((l) => l.includes("misconfigured")));
|
||||
});
|
||||
});
|
||||
|
||||
describe("service stop", () => {
|
||||
it("logs disconnection on stop", async () => {
|
||||
const { api, logs, getService } = createMockApi({});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().stop({});
|
||||
assert.ok(logs.some((l) => l.includes("feed stopped")));
|
||||
});
|
||||
});
|
||||
|
||||
describe("command handler", () => {
|
||||
it("returns not configured when no feedConfig", async () => {
|
||||
const { api, getCommand } = createMockApi({});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
||||
assert.ok(result.includes("not configured"));
|
||||
});
|
||||
|
||||
it("returns status when no args", async () => {
|
||||
const { api, getCommand } = createMockApi({
|
||||
observationFeed: { enabled: true, channel: "telegram", to: "123" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
||||
assert.ok(result.includes("Enabled: yes"));
|
||||
assert.ok(result.includes("Channel: telegram"));
|
||||
assert.ok(result.includes("Target: 123"));
|
||||
assert.ok(result.includes("Connection:"));
|
||||
});
|
||||
|
||||
it("handles 'on' argument", async () => {
|
||||
const { api, logs, getCommand } = createMockApi({
|
||||
observationFeed: { enabled: false },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const result = await getCommand().handler({ args: "on", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed on", config: {} });
|
||||
assert.ok(result.includes("enable requested"));
|
||||
assert.ok(logs.some((l) => l.includes("enable requested")));
|
||||
});
|
||||
|
||||
it("handles 'off' argument", async () => {
|
||||
const { api, logs, getCommand } = createMockApi({
|
||||
observationFeed: { enabled: true },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const result = await getCommand().handler({ args: "off", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed off", config: {} });
|
||||
assert.ok(result.includes("disable requested"));
|
||||
assert.ok(logs.some((l) => l.includes("disable requested")));
|
||||
});
|
||||
|
||||
it("shows connection state in status output", async () => {
|
||||
const { api, getCommand } = createMockApi({
|
||||
observationFeed: { enabled: false, channel: "slack", to: "#general" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const result = await getCommand().handler({ args: "", channel: "slack", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
||||
assert.ok(result.includes("Connection: disconnected"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Observation I/O event handlers", () => {
|
||||
let workerServer: Server;
|
||||
let workerPort: number;
|
||||
let receivedRequests: Array<{ method: string; url: string; body: any }> = [];
|
||||
|
||||
function startWorkerMock(): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
workerServer = createServer((req: IncomingMessage, res: ServerResponse) => {
|
||||
let body = "";
|
||||
req.on("data", (chunk) => { body += chunk.toString(); });
|
||||
req.on("end", () => {
|
||||
let parsedBody: any = null;
|
||||
try { parsedBody = JSON.parse(body); } catch {}
|
||||
|
||||
receivedRequests.push({
|
||||
method: req.method || "GET",
|
||||
url: req.url || "/",
|
||||
body: parsedBody,
|
||||
});
|
||||
|
||||
// Handle different endpoints
|
||||
if (req.url === "/api/health") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "ok" }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url === "/api/sessions/init") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ sessionDbId: 1, promptNumber: 1, skipped: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url === "/api/sessions/observations") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "queued" }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url === "/api/sessions/summarize") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "queued" }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url === "/api/sessions/complete") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "completed" }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url?.startsWith("/api/context/inject")) {
|
||||
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
||||
res.end("# Claude-Mem Context\n\n## Timeline\n- Session 1: Did some work");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url === "/stream") {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
workerServer.listen(0, () => {
|
||||
const address = workerServer.address();
|
||||
if (address && typeof address === "object") {
|
||||
resolve(address.port);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
receivedRequests = [];
|
||||
workerPort = await startWorkerMock();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
workerServer?.close();
|
||||
});
|
||||
|
||||
it("session_start sends session init to worker", async () => {
|
||||
const { api, logs, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("session_start", {
|
||||
sessionId: "test-session-1",
|
||||
}, { sessionKey: "agent-1" });
|
||||
|
||||
// Wait for HTTP request
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const initRequest = receivedRequests.find((r) => r.url === "/api/sessions/init");
|
||||
assert.ok(initRequest, "should send init request to worker");
|
||||
assert.equal(initRequest!.body.project, "openclaw");
|
||||
assert.ok(initRequest!.body.contentSessionId.startsWith("openclaw-agent-1-"));
|
||||
assert.ok(logs.some((l) => l.includes("Session initialized")));
|
||||
});
|
||||
|
||||
it("session_start calls init on worker", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("session_start", { sessionId: "test-session-1" }, {});
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const initRequests = receivedRequests.filter((r) => r.url === "/api/sessions/init");
|
||||
assert.equal(initRequests.length, 1, "should init on session_start");
|
||||
});
|
||||
|
||||
it("after_compaction re-inits session on worker", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("after_compaction", { messageCount: 5, compactedCount: 3 }, {});
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const initRequests = receivedRequests.filter((r) => r.url === "/api/sessions/init");
|
||||
assert.equal(initRequests.length, 1, "should re-init after compaction");
|
||||
});
|
||||
|
||||
it("before_agent_start calls init for session privacy check", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", { prompt: "hello" }, {});
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const initRequests = receivedRequests.filter((r) => r.url === "/api/sessions/init");
|
||||
assert.equal(initRequests.length, 1, "before_agent_start should init session");
|
||||
});
|
||||
|
||||
it("tool_result_persist sends observation to worker", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
// Establish contentSessionId via session_start
|
||||
await fireEvent("session_start", { sessionId: "s1" }, { sessionKey: "test-agent" });
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
// Fire tool result event
|
||||
await fireEvent("tool_result_persist", {
|
||||
toolName: "Read",
|
||||
params: { file_path: "/src/index.ts" },
|
||||
message: {
|
||||
content: [{ type: "text", text: "file contents here..." }],
|
||||
},
|
||||
}, { sessionKey: "test-agent" });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const obsRequest = receivedRequests.find((r) => r.url === "/api/sessions/observations");
|
||||
assert.ok(obsRequest, "should send observation to worker");
|
||||
assert.equal(obsRequest!.body.tool_name, "Read");
|
||||
assert.deepEqual(obsRequest!.body.tool_input, { file_path: "/src/index.ts" });
|
||||
assert.equal(obsRequest!.body.tool_response, "file contents here...");
|
||||
assert.ok(obsRequest!.body.contentSessionId.startsWith("openclaw-test-agent-"));
|
||||
});
|
||||
|
||||
it("tool_result_persist skips memory_ tools", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("tool_result_persist", {
|
||||
toolName: "memory_search",
|
||||
params: {},
|
||||
}, {});
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const obsRequest = receivedRequests.find((r) => r.url === "/api/sessions/observations");
|
||||
assert.ok(!obsRequest, "should skip memory_ tools");
|
||||
});
|
||||
|
||||
it("tool_result_persist truncates long responses", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const longText = "x".repeat(2000);
|
||||
await fireEvent("tool_result_persist", {
|
||||
toolName: "Bash",
|
||||
params: { command: "ls" },
|
||||
message: {
|
||||
content: [{ type: "text", text: longText }],
|
||||
},
|
||||
}, {});
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const obsRequest = receivedRequests.find((r) => r.url === "/api/sessions/observations");
|
||||
assert.ok(obsRequest, "should send observation");
|
||||
assert.equal(obsRequest!.body.tool_response.length, 1000, "should truncate to 1000 chars");
|
||||
});
|
||||
|
||||
it("agent_end sends summarize and complete to worker", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
// Establish session
|
||||
await fireEvent("session_start", { sessionId: "s1" }, { sessionKey: "summarize-test" });
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
// Fire agent end
|
||||
await fireEvent("agent_end", {
|
||||
messages: [
|
||||
{ role: "user", content: "help me" },
|
||||
{ role: "assistant", content: "Here is the solution..." },
|
||||
],
|
||||
}, { sessionKey: "summarize-test" });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const summarizeRequest = receivedRequests.find((r) => r.url === "/api/sessions/summarize");
|
||||
assert.ok(summarizeRequest, "should send summarize to worker");
|
||||
assert.equal(summarizeRequest!.body.last_assistant_message, "Here is the solution...");
|
||||
assert.ok(summarizeRequest!.body.contentSessionId.startsWith("openclaw-summarize-test-"));
|
||||
|
||||
const completeRequest = receivedRequests.find((r) => r.url === "/api/sessions/complete");
|
||||
assert.ok(completeRequest, "should send complete to worker");
|
||||
assert.ok(completeRequest!.body.contentSessionId.startsWith("openclaw-summarize-test-"));
|
||||
});
|
||||
|
||||
it("agent_end extracts text from array content", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("session_start", { sessionId: "s1" }, { sessionKey: "array-content" });
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
await fireEvent("agent_end", {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "text", text: "First part" },
|
||||
{ type: "text", text: "Second part" },
|
||||
],
|
||||
},
|
||||
],
|
||||
}, { sessionKey: "array-content" });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const summarizeRequest = receivedRequests.find((r) => r.url === "/api/sessions/summarize");
|
||||
assert.ok(summarizeRequest, "should send summarize");
|
||||
assert.equal(summarizeRequest!.body.last_assistant_message, "First part\nSecond part");
|
||||
});
|
||||
|
||||
it("uses custom project name from config", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort, project: "my-project" });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("session_start", { sessionId: "s1" }, {});
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const initRequest = receivedRequests.find((r) => r.url === "/api/sessions/init");
|
||||
assert.ok(initRequest, "should send init");
|
||||
assert.equal(initRequest!.body.project, "my-project");
|
||||
});
|
||||
|
||||
it("claude-mem-status command reports worker health", async () => {
|
||||
const { api, getCommand } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const statusCmd = getCommand("claude-mem-status");
|
||||
assert.ok(statusCmd, "status command should exist");
|
||||
|
||||
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
|
||||
assert.ok(result.includes("Status: ok"));
|
||||
assert.ok(result.includes(`Port: ${workerPort}`));
|
||||
});
|
||||
|
||||
it("claude-mem-status reports unreachable when worker is down", async () => {
|
||||
workerServer.close();
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const { api, getCommand } = createMockApi({ workerPort: 59999 });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
const statusCmd = getCommand("claude-mem-status");
|
||||
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
|
||||
assert.ok(result.includes("unreachable"));
|
||||
});
|
||||
|
||||
it("reuses same contentSessionId for same sessionKey", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("session_start", { sessionId: "s1" }, { sessionKey: "reuse-test" });
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
await fireEvent("tool_result_persist", {
|
||||
toolName: "Read",
|
||||
params: { file_path: "/src/index.ts" },
|
||||
message: { content: [{ type: "text", text: "contents" }] },
|
||||
}, { sessionKey: "reuse-test" });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const initRequest = receivedRequests.find((r) => r.url === "/api/sessions/init");
|
||||
const obsRequest = receivedRequests.find((r) => r.url === "/api/sessions/observations");
|
||||
assert.ok(initRequest && obsRequest, "both requests should exist");
|
||||
assert.equal(
|
||||
initRequest!.body.contentSessionId,
|
||||
obsRequest!.body.contentSessionId,
|
||||
"should reuse contentSessionId for same sessionKey"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("MEMORY.md context sync", () => {
|
||||
let workerServer: Server;
|
||||
let workerPort: number;
|
||||
let receivedRequests: Array<{ method: string; url: string; body: any }> = [];
|
||||
let tmpDir: string;
|
||||
let contextResponse = "# Claude-Mem Context\n\n## Timeline\n- Session 1: Did some work";
|
||||
|
||||
function startWorkerMock(): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
workerServer = createServer((req: IncomingMessage, res: ServerResponse) => {
|
||||
let body = "";
|
||||
req.on("data", (chunk) => { body += chunk.toString(); });
|
||||
req.on("end", () => {
|
||||
let parsedBody: any = null;
|
||||
try { parsedBody = JSON.parse(body); } catch {}
|
||||
|
||||
receivedRequests.push({
|
||||
method: req.method || "GET",
|
||||
url: req.url || "/",
|
||||
body: parsedBody,
|
||||
});
|
||||
|
||||
if (req.url?.startsWith("/api/context/inject")) {
|
||||
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
||||
res.end(contextResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.url === "/api/sessions/init") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ sessionDbId: 1, promptNumber: 1, skipped: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "ok" }));
|
||||
});
|
||||
});
|
||||
workerServer.listen(0, () => {
|
||||
const address = workerServer.address();
|
||||
if (address && typeof address === "object") {
|
||||
resolve(address.port);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
receivedRequests = [];
|
||||
contextResponse = "# Claude-Mem Context\n\n## Timeline\n- Session 1: Did some work";
|
||||
workerPort = await startWorkerMock();
|
||||
tmpDir = await mkdtemp(join(tmpdir(), "claude-mem-test-"));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
workerServer?.close();
|
||||
await rm(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("writes MEMORY.md to workspace on before_agent_start", async () => {
|
||||
const { api, logs, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "sync-test", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const contextRequest = receivedRequests.find((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.ok(contextRequest, "should request context from worker");
|
||||
assert.ok(contextRequest!.url!.includes("projects=openclaw"));
|
||||
|
||||
const memoryContent = await readFile(join(tmpDir, "MEMORY.md"), "utf-8");
|
||||
assert.ok(memoryContent.includes("Claude-Mem Context"), "MEMORY.md should contain context");
|
||||
assert.ok(memoryContent.includes("Session 1"), "MEMORY.md should contain timeline");
|
||||
assert.ok(logs.some((l) => l.includes("MEMORY.md synced")));
|
||||
});
|
||||
|
||||
it("syncs MEMORY.md on every before_agent_start call", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "First prompt for this agent",
|
||||
}, { sessionKey: "agent-a", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const firstContextRequests = receivedRequests.filter((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.equal(firstContextRequests.length, 1, "first call should fetch context");
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Second prompt for same agent",
|
||||
}, { sessionKey: "agent-a", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const allContextRequests = receivedRequests.filter((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.equal(allContextRequests.length, 2, "should re-fetch context on every call");
|
||||
});
|
||||
|
||||
it("syncs MEMORY.md on tool_result_persist via fire-and-forget", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
// Init session to register workspace dir
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "tool-sync", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const preToolContextRequests = receivedRequests.filter((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.equal(preToolContextRequests.length, 1, "before_agent_start should sync once");
|
||||
|
||||
// Fire tool result — should trigger another MEMORY.md sync
|
||||
await fireEvent("tool_result_persist", {
|
||||
toolName: "Read",
|
||||
params: { file_path: "/src/app.ts" },
|
||||
message: { content: [{ type: "text", text: "file contents" }] },
|
||||
}, { sessionKey: "tool-sync" });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const postToolContextRequests = receivedRequests.filter((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.equal(postToolContextRequests.length, 2, "tool_result_persist should trigger another sync");
|
||||
|
||||
const memoryContent = await readFile(join(tmpDir, "MEMORY.md"), "utf-8");
|
||||
assert.ok(memoryContent.includes("Claude-Mem Context"), "MEMORY.md should be updated");
|
||||
});
|
||||
|
||||
it("skips MEMORY.md sync when syncMemoryFile is false", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort, syncMemoryFile: false });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "no-sync", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const contextRequest = receivedRequests.find((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.ok(!contextRequest, "should not fetch context when sync disabled");
|
||||
});
|
||||
|
||||
it("skips MEMORY.md sync when no workspaceDir in context", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "no-workspace" });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const contextRequest = receivedRequests.find((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.ok(!contextRequest, "should not fetch context without workspaceDir");
|
||||
});
|
||||
|
||||
it("skips writing MEMORY.md when context is empty", async () => {
|
||||
contextResponse = " ";
|
||||
const { api, logs, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "empty-ctx", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
assert.ok(!logs.some((l) => l.includes("MEMORY.md synced")), "should not log sync for empty context");
|
||||
});
|
||||
|
||||
it("gateway_start resets sync tracking so next agent re-syncs", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
// First sync
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "agent-1", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const firstContextRequests = receivedRequests.filter((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.equal(firstContextRequests.length, 1);
|
||||
|
||||
// Gateway restart
|
||||
await fireEvent("gateway_start", {}, {});
|
||||
|
||||
// Second sync after gateway restart — same workspace should re-sync
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me after gateway restart",
|
||||
}, { sessionKey: "agent-1", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const allContextRequests = receivedRequests.filter((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.equal(allContextRequests.length, 2, "should re-fetch context after gateway restart");
|
||||
});
|
||||
|
||||
it("uses custom project name in context inject URL", async () => {
|
||||
const { api, fireEvent } = createMockApi({ workerPort, project: "my-bot" });
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await fireEvent("before_agent_start", {
|
||||
prompt: "Help me write a function",
|
||||
}, { sessionKey: "proj-test", workspaceDir: tmpDir });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const contextRequest = receivedRequests.find((r) => r.url?.startsWith("/api/context/inject"));
|
||||
assert.ok(contextRequest, "should request context");
|
||||
assert.ok(contextRequest!.url!.includes("projects=my-bot"), "should use custom project name");
|
||||
});
|
||||
});
|
||||
|
||||
describe("SSE stream integration", () => {
|
||||
let server: Server;
|
||||
let serverPort: number;
|
||||
let serverResponses: ServerResponse[] = [];
|
||||
|
||||
function startSSEServer(): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
server = createServer((req: IncomingMessage, res: ServerResponse) => {
|
||||
if (req.url !== "/stream") {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
});
|
||||
serverResponses.push(res);
|
||||
});
|
||||
server.listen(0, () => {
|
||||
const address = server.address();
|
||||
if (address && typeof address === "object") {
|
||||
resolve(address.port);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
serverResponses = [];
|
||||
serverPort = await startSSEServer();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const res of serverResponses) {
|
||||
try {
|
||||
res.end();
|
||||
} catch {}
|
||||
}
|
||||
server?.close();
|
||||
});
|
||||
|
||||
it("connects to SSE stream and receives new_observation events", async () => {
|
||||
const { api, logs, sentMessages, getService } = createMockApi({
|
||||
workerPort: serverPort,
|
||||
observationFeed: { enabled: true, channel: "telegram", to: "12345" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
|
||||
// Wait for connection
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
assert.ok(logs.some((l) => l.includes("Connecting to SSE stream")));
|
||||
|
||||
// Send an SSE event
|
||||
const observation = {
|
||||
type: "new_observation",
|
||||
observation: {
|
||||
id: 1,
|
||||
title: "Test Observation",
|
||||
subtitle: "Found something interesting",
|
||||
type: "discovery",
|
||||
project: "test",
|
||||
prompt_number: 1,
|
||||
created_at_epoch: Date.now(),
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
for (const res of serverResponses) {
|
||||
res.write(`data: ${JSON.stringify(observation)}\n\n`);
|
||||
}
|
||||
|
||||
// Wait for processing
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
assert.equal(sentMessages.length, 1);
|
||||
assert.equal(sentMessages[0].channel, "telegram");
|
||||
assert.equal(sentMessages[0].to, "12345");
|
||||
assert.ok(sentMessages[0].text.includes("Test Observation"));
|
||||
assert.ok(sentMessages[0].text.includes("Found something interesting"));
|
||||
|
||||
await getService().stop({});
|
||||
});
|
||||
|
||||
it("filters out non-observation events", async () => {
|
||||
const { api, sentMessages, getService } = createMockApi({
|
||||
workerPort: serverPort,
|
||||
observationFeed: { enabled: true, channel: "discord", to: "channel-id" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
// Send non-observation events
|
||||
for (const res of serverResponses) {
|
||||
res.write(`data: ${JSON.stringify({ type: "processing_status", isProcessing: true })}\n\n`);
|
||||
res.write(`data: ${JSON.stringify({ type: "session_started", sessionId: "abc" })}\n\n`);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
assert.equal(sentMessages.length, 0, "non-observation events should be filtered");
|
||||
|
||||
await getService().stop({});
|
||||
});
|
||||
|
||||
it("handles observation with null subtitle", async () => {
|
||||
const { api, sentMessages, getService } = createMockApi({
|
||||
workerPort: serverPort,
|
||||
observationFeed: { enabled: true, channel: "telegram", to: "999" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
for (const res of serverResponses) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({
|
||||
type: "new_observation",
|
||||
observation: { id: 2, title: "No Subtitle", subtitle: null },
|
||||
timestamp: Date.now(),
|
||||
})}\n\n`
|
||||
);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
assert.equal(sentMessages.length, 1);
|
||||
assert.ok(sentMessages[0].text.includes("No Subtitle"));
|
||||
assert.ok(!sentMessages[0].text.includes("null"));
|
||||
|
||||
await getService().stop({});
|
||||
});
|
||||
|
||||
it("handles observation with null title", async () => {
|
||||
const { api, sentMessages, getService } = createMockApi({
|
||||
workerPort: serverPort,
|
||||
observationFeed: { enabled: true, channel: "telegram", to: "999" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
for (const res of serverResponses) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({
|
||||
type: "new_observation",
|
||||
observation: { id: 3, title: null, subtitle: "Has subtitle" },
|
||||
timestamp: Date.now(),
|
||||
})}\n\n`
|
||||
);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
assert.equal(sentMessages.length, 1);
|
||||
assert.ok(sentMessages[0].text.includes("Untitled"));
|
||||
|
||||
await getService().stop({});
|
||||
});
|
||||
|
||||
it("uses custom workerPort from config", async () => {
|
||||
const { api, logs, getService } = createMockApi({
|
||||
workerPort: serverPort,
|
||||
observationFeed: { enabled: true, channel: "telegram", to: "12345" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
assert.ok(logs.some((l) => l.includes(`127.0.0.1:${serverPort}`)));
|
||||
|
||||
await getService().stop({});
|
||||
});
|
||||
|
||||
it("logs unknown channel type", async () => {
|
||||
const { api, logs, sentMessages, getService } = createMockApi({
|
||||
workerPort: serverPort,
|
||||
observationFeed: { enabled: true, channel: "matrix", to: "room-id" },
|
||||
});
|
||||
claudeMemPlugin(api);
|
||||
|
||||
await getService().start({});
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
for (const res of serverResponses) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({
|
||||
type: "new_observation",
|
||||
observation: { id: 4, title: "Test", subtitle: null },
|
||||
timestamp: Date.now(),
|
||||
})}\n\n`
|
||||
);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
assert.equal(sentMessages.length, 0);
|
||||
assert.ok(logs.some((l) => l.includes("Unsupported channel type: matrix")));
|
||||
|
||||
await getService().stop({});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,737 @@
|
||||
import { writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
// Minimal type declarations for the OpenClaw Plugin SDK.
|
||||
// These match the real OpenClawPluginApi provided by the gateway at runtime.
|
||||
// See: https://docs.openclaw.ai/plugin
|
||||
|
||||
interface PluginLogger {
|
||||
debug?: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
warn: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
}
|
||||
|
||||
interface PluginServiceContext {
|
||||
config: Record<string, unknown>;
|
||||
workspaceDir?: string;
|
||||
stateDir: string;
|
||||
logger: PluginLogger;
|
||||
}
|
||||
|
||||
interface PluginCommandContext {
|
||||
senderId?: string;
|
||||
channel: string;
|
||||
isAuthorizedSender: boolean;
|
||||
args?: string;
|
||||
commandBody: string;
|
||||
config: Record<string, unknown>;
|
||||
}
|
||||
|
||||
type PluginCommandResult = string | { text: string } | { text: string; format?: string };
|
||||
|
||||
// OpenClaw event types for agent lifecycle
|
||||
interface BeforeAgentStartEvent {
|
||||
prompt?: string;
|
||||
}
|
||||
|
||||
interface ToolResultPersistEvent {
|
||||
toolName?: string;
|
||||
params?: Record<string, unknown>;
|
||||
message?: {
|
||||
content?: Array<{ type: string; text?: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
interface AgentEndEvent {
|
||||
messages?: Array<{
|
||||
role: string;
|
||||
content: string | Array<{ type: string; text?: string }>;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface SessionStartEvent {
|
||||
sessionId: string;
|
||||
resumedFrom?: string;
|
||||
}
|
||||
|
||||
interface AfterCompactionEvent {
|
||||
messageCount: number;
|
||||
tokenCount?: number;
|
||||
compactedCount: number;
|
||||
}
|
||||
|
||||
interface SessionEndEvent {
|
||||
sessionId: string;
|
||||
messageCount: number;
|
||||
durationMs?: number;
|
||||
}
|
||||
|
||||
interface EventContext {
|
||||
sessionKey?: string;
|
||||
workspaceDir?: string;
|
||||
agentId?: string;
|
||||
}
|
||||
|
||||
type EventCallback<T> = (event: T, ctx: EventContext) => void | Promise<void>;
|
||||
|
||||
interface OpenClawPluginApi {
|
||||
id: string;
|
||||
name: string;
|
||||
version?: string;
|
||||
source: string;
|
||||
config: Record<string, unknown>;
|
||||
pluginConfig?: Record<string, unknown>;
|
||||
logger: PluginLogger;
|
||||
registerService: (service: {
|
||||
id: string;
|
||||
start: (ctx: PluginServiceContext) => void | Promise<void>;
|
||||
stop?: (ctx: PluginServiceContext) => void | Promise<void>;
|
||||
}) => void;
|
||||
registerCommand: (command: {
|
||||
name: string;
|
||||
description: string;
|
||||
acceptsArgs?: boolean;
|
||||
requireAuth?: boolean;
|
||||
handler: (ctx: PluginCommandContext) => PluginCommandResult | Promise<PluginCommandResult>;
|
||||
}) => void;
|
||||
on: ((event: "before_agent_start", callback: EventCallback<BeforeAgentStartEvent>) => void) &
|
||||
((event: "tool_result_persist", callback: EventCallback<ToolResultPersistEvent>) => void) &
|
||||
((event: "agent_end", callback: EventCallback<AgentEndEvent>) => void) &
|
||||
((event: "session_start", callback: EventCallback<SessionStartEvent>) => void) &
|
||||
((event: "session_end", callback: EventCallback<SessionEndEvent>) => void) &
|
||||
((event: "after_compaction", callback: EventCallback<AfterCompactionEvent>) => void) &
|
||||
((event: "gateway_start", callback: EventCallback<Record<string, never>>) => void);
|
||||
runtime: {
|
||||
channel: Record<string, Record<string, (...args: any[]) => Promise<any>>>;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SSE Observation Feed Types
|
||||
// ============================================================================
|
||||
|
||||
interface ObservationSSEPayload {
|
||||
id: number;
|
||||
memory_session_id: string;
|
||||
session_id: string;
|
||||
type: string;
|
||||
title: string | null;
|
||||
subtitle: string | null;
|
||||
text: string | null;
|
||||
narrative: string | null;
|
||||
facts: string | null;
|
||||
concepts: string | null;
|
||||
files_read: string | null;
|
||||
files_modified: string | null;
|
||||
project: string | null;
|
||||
prompt_number: number;
|
||||
created_at_epoch: number;
|
||||
}
|
||||
|
||||
interface SSENewObservationEvent {
|
||||
type: "new_observation";
|
||||
observation: ObservationSSEPayload;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
type ConnectionState = "disconnected" | "connected" | "reconnecting";
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Configuration
|
||||
// ============================================================================
|
||||
|
||||
interface ClaudeMemPluginConfig {
|
||||
syncMemoryFile?: boolean;
|
||||
project?: string;
|
||||
workerPort?: number;
|
||||
observationFeed?: {
|
||||
enabled?: boolean;
|
||||
channel?: string;
|
||||
to?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Constants
|
||||
// ============================================================================
|
||||
|
||||
const MAX_SSE_BUFFER_SIZE = 1024 * 1024; // 1MB
|
||||
const DEFAULT_WORKER_PORT = 37777;
|
||||
const TOOL_RESULT_MAX_LENGTH = 1000;
|
||||
|
||||
// Agent emoji map for observation feed messages.
|
||||
// When creating a new OpenClaw agent, add its agentId and emoji here.
|
||||
const AGENT_EMOJI_MAP: Record<string, string> = {
|
||||
"main": "🦞",
|
||||
"openclaw": "🦞",
|
||||
"devops": "🔧",
|
||||
"architect": "📐",
|
||||
"researcher": "🔍",
|
||||
"code-reviewer": "🔎",
|
||||
"coder": "💻",
|
||||
"tester": "🧪",
|
||||
"debugger": "🐛",
|
||||
"opsec": "🛡️",
|
||||
"cloudfarm": "☁️",
|
||||
"extractor": "📦",
|
||||
};
|
||||
|
||||
// Project prefixes that indicate Claude Code sessions (not OpenClaw agents)
|
||||
const CLAUDE_CODE_EMOJI = "⌨️";
|
||||
const OPENCLAW_DEFAULT_EMOJI = "🦀";
|
||||
|
||||
function getSourceLabel(project: string | null | undefined): string {
|
||||
if (!project) return OPENCLAW_DEFAULT_EMOJI;
|
||||
// OpenClaw agent projects are formatted as "openclaw-<agentId>"
|
||||
if (project.startsWith("openclaw-")) {
|
||||
const agentId = project.slice("openclaw-".length);
|
||||
const emoji = AGENT_EMOJI_MAP[agentId] || OPENCLAW_DEFAULT_EMOJI;
|
||||
return `${emoji} ${agentId}`;
|
||||
}
|
||||
// OpenClaw project without agent suffix
|
||||
if (project === "openclaw") {
|
||||
return `🦞 openclaw`;
|
||||
}
|
||||
// Everything else is from Claude Code (project = working directory name)
|
||||
const emoji = CLAUDE_CODE_EMOJI;
|
||||
return `${emoji} ${project}`;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Worker HTTP Client
|
||||
// ============================================================================
|
||||
|
||||
function workerBaseUrl(port: number): string {
|
||||
return `http://127.0.0.1:${port}`;
|
||||
}
|
||||
|
||||
async function workerPost(
|
||||
port: number,
|
||||
path: string,
|
||||
body: Record<string, unknown>,
|
||||
logger: PluginLogger
|
||||
): Promise<Record<string, unknown> | null> {
|
||||
try {
|
||||
const response = await fetch(`${workerBaseUrl(port)}${path}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!response.ok) {
|
||||
logger.warn(`[claude-mem] Worker POST ${path} returned ${response.status}`);
|
||||
return null;
|
||||
}
|
||||
return (await response.json()) as Record<string, unknown>;
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
logger.warn(`[claude-mem] Worker POST ${path} failed: ${message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function workerPostFireAndForget(
|
||||
port: number,
|
||||
path: string,
|
||||
body: Record<string, unknown>,
|
||||
logger: PluginLogger
|
||||
): void {
|
||||
fetch(`${workerBaseUrl(port)}${path}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
}).catch((error: unknown) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
logger.warn(`[claude-mem] Worker POST ${path} failed: ${message}`);
|
||||
});
|
||||
}
|
||||
|
||||
async function workerGetText(
|
||||
port: number,
|
||||
path: string,
|
||||
logger: PluginLogger
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const response = await fetch(`${workerBaseUrl(port)}${path}`);
|
||||
if (!response.ok) {
|
||||
logger.warn(`[claude-mem] Worker GET ${path} returned ${response.status}`);
|
||||
return null;
|
||||
}
|
||||
return await response.text();
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
logger.warn(`[claude-mem] Worker GET ${path} failed: ${message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SSE Observation Feed
|
||||
// ============================================================================
|
||||
|
||||
function formatObservationMessage(observation: ObservationSSEPayload): string {
|
||||
const title = observation.title || "Untitled";
|
||||
const source = getSourceLabel(observation.project);
|
||||
let message = `${source}\n**${title}**`;
|
||||
if (observation.subtitle) {
|
||||
message += `\n${observation.subtitle}`;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
// Explicit mapping from channel name to [runtime namespace key, send function name].
|
||||
// These match the PluginRuntime.channel structure in the OpenClaw SDK.
|
||||
const CHANNEL_SEND_MAP: Record<string, { namespace: string; functionName: string }> = {
|
||||
telegram: { namespace: "telegram", functionName: "sendMessageTelegram" },
|
||||
whatsapp: { namespace: "whatsapp", functionName: "sendMessageWhatsApp" },
|
||||
discord: { namespace: "discord", functionName: "sendMessageDiscord" },
|
||||
slack: { namespace: "slack", functionName: "sendMessageSlack" },
|
||||
signal: { namespace: "signal", functionName: "sendMessageSignal" },
|
||||
imessage: { namespace: "imessage", functionName: "sendMessageIMessage" },
|
||||
line: { namespace: "line", functionName: "sendMessageLine" },
|
||||
};
|
||||
|
||||
function sendToChannel(
|
||||
api: OpenClawPluginApi,
|
||||
channel: string,
|
||||
to: string,
|
||||
text: string
|
||||
): Promise<void> {
|
||||
const mapping = CHANNEL_SEND_MAP[channel];
|
||||
if (!mapping) {
|
||||
api.logger.warn(`[claude-mem] Unsupported channel type: ${channel}`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const channelApi = api.runtime.channel[mapping.namespace];
|
||||
if (!channelApi) {
|
||||
api.logger.warn(`[claude-mem] Channel "${channel}" not available in runtime`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const senderFunction = channelApi[mapping.functionName];
|
||||
if (!senderFunction) {
|
||||
api.logger.warn(`[claude-mem] Channel "${channel}" has no ${mapping.functionName} function`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// WhatsApp requires a third options argument with { verbose: boolean }
|
||||
const args: unknown[] = channel === "whatsapp"
|
||||
? [to, text, { verbose: false }]
|
||||
: [to, text];
|
||||
|
||||
return senderFunction(...args).catch((error: unknown) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
api.logger.error(`[claude-mem] Failed to send to ${channel}: ${message}`);
|
||||
});
|
||||
}
|
||||
|
||||
async function connectToSSEStream(
|
||||
api: OpenClawPluginApi,
|
||||
port: number,
|
||||
channel: string,
|
||||
to: string,
|
||||
abortController: AbortController,
|
||||
setConnectionState: (state: ConnectionState) => void
|
||||
): Promise<void> {
|
||||
let backoffMs = 1000;
|
||||
const maxBackoffMs = 30000;
|
||||
|
||||
while (!abortController.signal.aborted) {
|
||||
try {
|
||||
setConnectionState("reconnecting");
|
||||
api.logger.info(`[claude-mem] Connecting to SSE stream at ${workerBaseUrl(port)}/stream`);
|
||||
|
||||
const response = await fetch(`${workerBaseUrl(port)}/stream`, {
|
||||
signal: abortController.signal,
|
||||
headers: { Accept: "text/event-stream" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`SSE stream returned HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.body) {
|
||||
throw new Error("SSE stream response has no body");
|
||||
}
|
||||
|
||||
setConnectionState("connected");
|
||||
backoffMs = 1000;
|
||||
api.logger.info("[claude-mem] Connected to SSE stream");
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
if (buffer.length > MAX_SSE_BUFFER_SIZE) {
|
||||
api.logger.warn("[claude-mem] SSE buffer overflow, clearing buffer");
|
||||
buffer = "";
|
||||
}
|
||||
|
||||
const frames = buffer.split("\n\n");
|
||||
buffer = frames.pop() || "";
|
||||
|
||||
for (const frame of frames) {
|
||||
// SSE spec: concatenate all data: lines with \n
|
||||
const dataLines = frame
|
||||
.split("\n")
|
||||
.filter((line) => line.startsWith("data:"))
|
||||
.map((line) => line.slice(5).trim());
|
||||
if (dataLines.length === 0) continue;
|
||||
|
||||
const jsonStr = dataLines.join("\n");
|
||||
if (!jsonStr) continue;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(jsonStr);
|
||||
if (parsed.type === "new_observation" && parsed.observation) {
|
||||
const event = parsed as SSENewObservationEvent;
|
||||
const message = formatObservationMessage(event.observation);
|
||||
await sendToChannel(api, channel, to, message);
|
||||
}
|
||||
} catch (parseError: unknown) {
|
||||
const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
|
||||
api.logger.warn(`[claude-mem] Failed to parse SSE frame: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (abortController.signal.aborted) {
|
||||
break;
|
||||
}
|
||||
setConnectionState("reconnecting");
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
api.logger.warn(`[claude-mem] SSE stream error: ${errorMessage}. Reconnecting in ${backoffMs / 1000}s`);
|
||||
}
|
||||
|
||||
if (abortController.signal.aborted) break;
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
||||
backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
|
||||
}
|
||||
|
||||
setConnectionState("disconnected");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Entry Point
|
||||
// ============================================================================
|
||||
|
||||
export default function claudeMemPlugin(api: OpenClawPluginApi): void {
|
||||
const userConfig = (api.pluginConfig || {}) as ClaudeMemPluginConfig;
|
||||
const workerPort = userConfig.workerPort || DEFAULT_WORKER_PORT;
|
||||
const baseProjectName = userConfig.project || "openclaw";
|
||||
|
||||
function getProjectName(ctx: EventContext): string {
|
||||
if (ctx.agentId) {
|
||||
return `openclaw-${ctx.agentId}`;
|
||||
}
|
||||
return baseProjectName;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Session tracking for observation I/O
|
||||
// ------------------------------------------------------------------
|
||||
const sessionIds = new Map<string, string>();
|
||||
const workspaceDirsBySessionKey = new Map<string, string>();
|
||||
const syncMemoryFile = userConfig.syncMemoryFile !== false; // default true
|
||||
|
||||
function getContentSessionId(sessionKey?: string): string {
|
||||
const key = sessionKey || "default";
|
||||
if (!sessionIds.has(key)) {
|
||||
sessionIds.set(key, `openclaw-${key}-${Date.now()}`);
|
||||
}
|
||||
return sessionIds.get(key)!;
|
||||
}
|
||||
|
||||
async function syncMemoryToWorkspace(workspaceDir: string): Promise<void> {
|
||||
const contextText = await workerGetText(
|
||||
workerPort,
|
||||
`/api/context/inject?projects=${encodeURIComponent(baseProjectName)}`,
|
||||
api.logger
|
||||
);
|
||||
if (contextText && contextText.trim().length > 0) {
|
||||
try {
|
||||
await writeFile(join(workspaceDir, "MEMORY.md"), contextText, "utf-8");
|
||||
api.logger.info(`[claude-mem] MEMORY.md synced to ${workspaceDir}`);
|
||||
} catch (writeError: unknown) {
|
||||
const msg = writeError instanceof Error ? writeError.message : String(writeError);
|
||||
api.logger.warn(`[claude-mem] Failed to write MEMORY.md: ${msg}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: session_start — init claude-mem session (fires on /new, /reset)
|
||||
// ------------------------------------------------------------------
|
||||
api.on("session_start", async (_event, ctx) => {
|
||||
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||
|
||||
await workerPost(workerPort, "/api/sessions/init", {
|
||||
contentSessionId,
|
||||
project: getProjectName(ctx),
|
||||
prompt: "",
|
||||
}, api.logger);
|
||||
|
||||
api.logger.info(`[claude-mem] Session initialized: ${contentSessionId}`);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: after_compaction — re-init session after context compaction
|
||||
// ------------------------------------------------------------------
|
||||
api.on("after_compaction", async (_event, ctx) => {
|
||||
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||
|
||||
await workerPost(workerPort, "/api/sessions/init", {
|
||||
contentSessionId,
|
||||
project: getProjectName(ctx),
|
||||
prompt: "",
|
||||
}, api.logger);
|
||||
|
||||
api.logger.info(`[claude-mem] Session re-initialized after compaction: ${contentSessionId}`);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: before_agent_start — init session + sync MEMORY.md + track workspace
|
||||
// ------------------------------------------------------------------
|
||||
api.on("before_agent_start", async (_event, ctx) => {
|
||||
// Track workspace dir so tool_result_persist can sync MEMORY.md later
|
||||
if (ctx.workspaceDir) {
|
||||
workspaceDirsBySessionKey.set(ctx.sessionKey || "default", ctx.workspaceDir);
|
||||
}
|
||||
|
||||
// Initialize session in the worker so observations are not skipped
|
||||
// (the privacy check requires a stored user prompt to exist)
|
||||
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||
await workerPost(workerPort, "/api/sessions/init", {
|
||||
contentSessionId,
|
||||
project: getProjectName(ctx),
|
||||
prompt: ctx.sessionKey || "agent run",
|
||||
}, api.logger);
|
||||
|
||||
// Sync MEMORY.md before agent runs (provides context to agent)
|
||||
if (syncMemoryFile && ctx.workspaceDir) {
|
||||
await syncMemoryToWorkspace(ctx.workspaceDir);
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: tool_result_persist — record tool observations + sync MEMORY.md
|
||||
// ------------------------------------------------------------------
|
||||
api.on("tool_result_persist", (event, ctx) => {
|
||||
api.logger.info(`[claude-mem] tool_result_persist fired: tool=${event.toolName ?? "unknown"} agent=${ctx.agentId ?? "none"} session=${ctx.sessionKey ?? "none"}`);
|
||||
const toolName = event.toolName;
|
||||
if (!toolName || toolName.startsWith("memory_")) return;
|
||||
|
||||
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||
|
||||
// Extract result text from message content
|
||||
let toolResponseText = "";
|
||||
const content = event.message?.content;
|
||||
if (Array.isArray(content)) {
|
||||
const textBlock = content.find(
|
||||
(block) => block.type === "tool_result" || block.type === "text"
|
||||
);
|
||||
if (textBlock && "text" in textBlock) {
|
||||
toolResponseText = String(textBlock.text).slice(0, TOOL_RESULT_MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Fire-and-forget: send observation + sync MEMORY.md in parallel
|
||||
workerPostFireAndForget(workerPort, "/api/sessions/observations", {
|
||||
contentSessionId,
|
||||
tool_name: toolName,
|
||||
tool_input: event.params || {},
|
||||
tool_response: toolResponseText,
|
||||
cwd: "",
|
||||
}, api.logger);
|
||||
|
||||
const workspaceDir = ctx.workspaceDir || workspaceDirsBySessionKey.get(ctx.sessionKey || "default");
|
||||
if (syncMemoryFile && workspaceDir) {
|
||||
syncMemoryToWorkspace(workspaceDir);
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: agent_end — summarize and complete session
|
||||
// ------------------------------------------------------------------
|
||||
api.on("agent_end", async (event, ctx) => {
|
||||
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||
|
||||
// Extract last assistant message for summarization
|
||||
let lastAssistantMessage = "";
|
||||
if (Array.isArray(event.messages)) {
|
||||
for (let i = event.messages.length - 1; i >= 0; i--) {
|
||||
const message = event.messages[i];
|
||||
if (message?.role === "assistant") {
|
||||
if (typeof message.content === "string") {
|
||||
lastAssistantMessage = message.content;
|
||||
} else if (Array.isArray(message.content)) {
|
||||
lastAssistantMessage = message.content
|
||||
.filter((block) => block.type === "text")
|
||||
.map((block) => block.text || "")
|
||||
.join("\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Await summarize so the worker receives it before complete.
|
||||
// This also gives in-flight tool_result_persist observations time to arrive
|
||||
// (they use fire-and-forget and may still be in transit).
|
||||
await workerPost(workerPort, "/api/sessions/summarize", {
|
||||
contentSessionId,
|
||||
last_assistant_message: lastAssistantMessage,
|
||||
}, api.logger);
|
||||
|
||||
workerPostFireAndForget(workerPort, "/api/sessions/complete", {
|
||||
contentSessionId,
|
||||
}, api.logger);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: session_end — clean up session tracking to prevent unbounded growth
|
||||
// ------------------------------------------------------------------
|
||||
api.on("session_end", async (_event, ctx) => {
|
||||
const key = ctx.sessionKey || "default";
|
||||
sessionIds.delete(key);
|
||||
workspaceDirsBySessionKey.delete(key);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Event: gateway_start — clear session tracking for fresh start
|
||||
// ------------------------------------------------------------------
|
||||
api.on("gateway_start", async () => {
|
||||
workspaceDirsBySessionKey.clear();
|
||||
sessionIds.clear();
|
||||
api.logger.info("[claude-mem] Gateway started — session tracking reset");
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Service: SSE observation feed → messaging channels
|
||||
// ------------------------------------------------------------------
|
||||
let sseAbortController: AbortController | null = null;
|
||||
let connectionState: ConnectionState = "disconnected";
|
||||
let connectionPromise: Promise<void> | null = null;
|
||||
|
||||
api.registerService({
|
||||
id: "claude-mem-observation-feed",
|
||||
start: async (_ctx) => {
|
||||
if (sseAbortController) {
|
||||
sseAbortController.abort();
|
||||
if (connectionPromise) {
|
||||
await connectionPromise;
|
||||
connectionPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
const feedConfig = userConfig.observationFeed;
|
||||
|
||||
if (!feedConfig?.enabled) {
|
||||
api.logger.info("[claude-mem] Observation feed disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!feedConfig.channel || !feedConfig.to) {
|
||||
api.logger.warn("[claude-mem] Observation feed misconfigured — channel or target missing");
|
||||
return;
|
||||
}
|
||||
|
||||
api.logger.info(`[claude-mem] Observation feed starting — channel: ${feedConfig.channel}, target: ${feedConfig.to}`);
|
||||
|
||||
sseAbortController = new AbortController();
|
||||
connectionPromise = connectToSSEStream(
|
||||
api,
|
||||
workerPort,
|
||||
feedConfig.channel,
|
||||
feedConfig.to,
|
||||
sseAbortController,
|
||||
(state) => { connectionState = state; }
|
||||
);
|
||||
},
|
||||
stop: async (_ctx) => {
|
||||
if (sseAbortController) {
|
||||
sseAbortController.abort();
|
||||
sseAbortController = null;
|
||||
}
|
||||
if (connectionPromise) {
|
||||
await connectionPromise;
|
||||
connectionPromise = null;
|
||||
}
|
||||
connectionState = "disconnected";
|
||||
api.logger.info("[claude-mem] Observation feed stopped — SSE connection closed");
|
||||
},
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Command: /claude-mem-feed — status & toggle
|
||||
// ------------------------------------------------------------------
|
||||
api.registerCommand({
|
||||
name: "claude-mem-feed",
|
||||
description: "Show or toggle Claude-Mem observation feed status",
|
||||
acceptsArgs: true,
|
||||
handler: async (ctx) => {
|
||||
const feedConfig = userConfig.observationFeed;
|
||||
|
||||
if (!feedConfig) {
|
||||
return "Observation feed not configured. Add observationFeed to your plugin config.";
|
||||
}
|
||||
|
||||
const arg = ctx.args?.trim();
|
||||
|
||||
if (arg === "on") {
|
||||
api.logger.info("[claude-mem] Feed enable requested via command");
|
||||
return "Feed enable requested. Update observationFeed.enabled in your plugin config to persist.";
|
||||
}
|
||||
|
||||
if (arg === "off") {
|
||||
api.logger.info("[claude-mem] Feed disable requested via command");
|
||||
return "Feed disable requested. Update observationFeed.enabled in your plugin config to persist.";
|
||||
}
|
||||
|
||||
return [
|
||||
"Claude-Mem Observation Feed",
|
||||
`Enabled: ${feedConfig.enabled ? "yes" : "no"}`,
|
||||
`Channel: ${feedConfig.channel || "not set"}`,
|
||||
`Target: ${feedConfig.to || "not set"}`,
|
||||
`Connection: ${connectionState}`,
|
||||
].join("\n");
|
||||
},
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Command: /claude-mem-status — worker health check
|
||||
// ------------------------------------------------------------------
|
||||
api.registerCommand({
|
||||
name: "claude-mem-status",
|
||||
description: "Check Claude-Mem worker health and session status",
|
||||
handler: async () => {
|
||||
const healthText = await workerGetText(workerPort, "/api/health", api.logger);
|
||||
if (!healthText) {
|
||||
return `Claude-Mem worker unreachable at port ${workerPort}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const health = JSON.parse(healthText);
|
||||
return [
|
||||
"Claude-Mem Worker Status",
|
||||
`Status: ${health.status || "unknown"}`,
|
||||
`Port: ${workerPort}`,
|
||||
`Active sessions: ${sessionIds.size}`,
|
||||
`Observation feed: ${connectionState}`,
|
||||
].join("\n");
|
||||
} catch {
|
||||
return `Claude-Mem worker responded but returned unexpected data`;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
api.logger.info(`[claude-mem] OpenClaw plugin loaded — v1.0.0 (worker: 127.0.0.1:${workerPort})`);
|
||||
}
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# test-e2e.sh — Run E2E test of claude-mem plugin on real OpenClaw
|
||||
#
|
||||
# Usage:
|
||||
# ./test-e2e.sh # Automated E2E test (build + run + verify)
|
||||
# ./test-e2e.sh --interactive # Drop into shell for manual testing
|
||||
# ./test-e2e.sh --build-only # Just build the image, don't run
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
IMAGE_NAME="openclaw-claude-mem-e2e"
|
||||
|
||||
echo "=== Building E2E test image ==="
|
||||
echo " Base: ghcr.io/openclaw/openclaw:main"
|
||||
echo " Plugin: @claude-mem/openclaw-plugin (PR #1012)"
|
||||
echo ""
|
||||
|
||||
docker build -f Dockerfile.e2e -t "$IMAGE_NAME" .
|
||||
|
||||
if [ "${1:-}" = "--build-only" ]; then
|
||||
echo ""
|
||||
echo "Image built: $IMAGE_NAME"
|
||||
echo "Run manually with: docker run --rm $IMAGE_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Running E2E verification ==="
|
||||
echo ""
|
||||
|
||||
if [ "${1:-}" = "--interactive" ]; then
|
||||
echo "Dropping into interactive shell."
|
||||
echo ""
|
||||
echo "Useful commands inside the container:"
|
||||
echo " node openclaw.mjs plugins list # Verify plugin is installed"
|
||||
echo " node openclaw.mjs plugins info claude-mem # Plugin details"
|
||||
echo " node openclaw.mjs plugins doctor # Check for issues"
|
||||
echo " node /app/mock-worker.js & # Start mock worker"
|
||||
echo " node openclaw.mjs gateway --allow-unconfigured --verbose # Start gateway"
|
||||
echo " /bin/bash /app/e2e-verify.sh # Run automated verification"
|
||||
echo ""
|
||||
docker run --rm -it "$IMAGE_NAME" /bin/bash
|
||||
else
|
||||
docker run --rm "$IMAGE_NAME"
|
||||
fi
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Smoke test for OpenClaw claude-mem plugin registration.
|
||||
* Validates the plugin structure works independently of the full OpenClaw runtime.
|
||||
*
|
||||
* Run: node test-sse-consumer.js
|
||||
*/
|
||||
|
||||
import claudeMemPlugin from "./dist/index.js";
|
||||
|
||||
let registeredService = null;
|
||||
const registeredCommands = new Map();
|
||||
const eventHandlers = new Map();
|
||||
const logs = [];
|
||||
|
||||
const mockApi = {
|
||||
id: "claude-mem",
|
||||
name: "Claude-Mem (Persistent Memory)",
|
||||
version: "1.0.0",
|
||||
source: "/test/extensions/claude-mem/dist/index.js",
|
||||
config: {},
|
||||
pluginConfig: {},
|
||||
logger: {
|
||||
info: (message) => { logs.push(message); },
|
||||
warn: (message) => { logs.push(message); },
|
||||
error: (message) => { logs.push(message); },
|
||||
debug: (message) => { logs.push(message); },
|
||||
},
|
||||
registerService: (service) => {
|
||||
registeredService = service;
|
||||
},
|
||||
registerCommand: (command) => {
|
||||
registeredCommands.set(command.name, command);
|
||||
},
|
||||
on: (event, callback) => {
|
||||
if (!eventHandlers.has(event)) {
|
||||
eventHandlers.set(event, []);
|
||||
}
|
||||
eventHandlers.get(event).push(callback);
|
||||
},
|
||||
runtime: {
|
||||
channel: {
|
||||
telegram: { sendMessageTelegram: async () => {} },
|
||||
discord: { sendMessageDiscord: async () => {} },
|
||||
signal: { sendMessageSignal: async () => {} },
|
||||
slack: { sendMessageSlack: async () => {} },
|
||||
whatsapp: { sendMessageWhatsApp: async () => {} },
|
||||
line: { sendMessageLine: async () => {} },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Call the default export with mock API
|
||||
claudeMemPlugin(mockApi);
|
||||
|
||||
// Verify registration
|
||||
let failures = 0;
|
||||
|
||||
if (!registeredService) {
|
||||
console.error("FAIL: No service was registered");
|
||||
failures++;
|
||||
} else if (registeredService.id !== "claude-mem-observation-feed") {
|
||||
console.error(
|
||||
`FAIL: Service ID is "${registeredService.id}", expected "claude-mem-observation-feed"`
|
||||
);
|
||||
failures++;
|
||||
} else {
|
||||
console.log("OK: Service registered with id 'claude-mem-observation-feed'");
|
||||
}
|
||||
|
||||
if (!registeredCommands.has("claude-mem-feed")) {
|
||||
console.error("FAIL: No 'claude-mem-feed' command registered");
|
||||
failures++;
|
||||
} else {
|
||||
console.log("OK: Command registered with name 'claude-mem-feed'");
|
||||
}
|
||||
|
||||
if (!registeredCommands.has("claude-mem-status")) {
|
||||
console.error("FAIL: No 'claude-mem-status' command registered");
|
||||
failures++;
|
||||
} else {
|
||||
console.log("OK: Command registered with name 'claude-mem-status'");
|
||||
}
|
||||
|
||||
const expectedEvents = ["before_agent_start", "tool_result_persist", "agent_end", "gateway_start"];
|
||||
for (const event of expectedEvents) {
|
||||
if (!eventHandlers.has(event) || eventHandlers.get(event).length === 0) {
|
||||
console.error(`FAIL: No handler registered for '${event}'`);
|
||||
failures++;
|
||||
} else {
|
||||
console.log(`OK: Event handler registered for '${event}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!logs.some((l) => l.includes("plugin loaded"))) {
|
||||
console.error("FAIL: Plugin did not log a load message");
|
||||
failures++;
|
||||
} else {
|
||||
console.log("OK: Plugin logged load message");
|
||||
}
|
||||
|
||||
if (failures > 0) {
|
||||
console.error(`\nFAIL: ${failures} check(s) failed`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("\nPASS: Plugin registers service, commands, and event handlers correctly");
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
+32
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.3",
|
||||
"version": "10.0.2",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
@@ -26,6 +26,21 @@
|
||||
"url": "https://github.com/thedotmack/claude-mem/issues"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
},
|
||||
"./sdk": {
|
||||
"types": "./dist/sdk/index.d.ts",
|
||||
"import": "./dist/sdk/index.js"
|
||||
},
|
||||
"./modes/*": "./plugin/modes/*"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"plugin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"bun": ">=1.0.0"
|
||||
@@ -51,7 +66,7 @@
|
||||
"claude-md:regenerate": "bun scripts/regenerate-claude-md.ts",
|
||||
"claude-md:dry-run": "bun scripts/regenerate-claude-md.ts --dry-run",
|
||||
"translate-readme": "bun scripts/translate-readme/cli.ts -v -o docs/i18n README.md",
|
||||
"translate:tier1": "npm run translate-readme -- zh ja pt-br ko es de fr",
|
||||
"translate:tier1": "npm run translate-readme -- zh zh-tw ja pt-br ko es de fr",
|
||||
"translate:tier2": "npm run translate-readme -- he ar ru pl cs nl tr uk",
|
||||
"translate:tier3": "npm run translate-readme -- vi id th hi bn ro sv",
|
||||
"translate:tier4": "npm run translate-readme -- it el hu fi da no",
|
||||
@@ -67,12 +82,24 @@
|
||||
"test:search": "bun test tests/worker/search/",
|
||||
"test:context": "bun test tests/context/",
|
||||
"test:infra": "bun test tests/infrastructure/",
|
||||
"test:server": "bun test tests/server/"
|
||||
"test:server": "bun test tests/server/",
|
||||
"prepublishOnly": "npm run build",
|
||||
"release": "np",
|
||||
"release:patch": "np patch --no-cleanup",
|
||||
"release:minor": "np minor --no-cleanup",
|
||||
"release:major": "np major --no-cleanup"
|
||||
},
|
||||
"np": {
|
||||
"yarn": false,
|
||||
"contents": ".",
|
||||
"testScript": "test",
|
||||
"2fa": false
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
||||
"@modelcontextprotocol/sdk": "^1.25.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"dompurify": "^3.3.1",
|
||||
"express": "^4.18.2",
|
||||
"glob": "^11.0.3",
|
||||
"handlebars": "^4.7.8",
|
||||
@@ -83,11 +110,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"esbuild": "^0.27.2",
|
||||
"np": "^11.0.2",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.3.0"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 6, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.3",
|
||||
"version": "10.0.2",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
+2
-59
@@ -1,66 +1,9 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 10, 2025
|
||||
### Jan 10, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #6276 | 12:58 PM | 🔴 | MCP Configuration Path Missing "plugin/" Directory Component | ~418 |
|
||||
| #6274 | 12:57 PM | 🔵 | Installed MCP Configuration Uses Incorrect Path | ~389 |
|
||||
| #6270 | " | 🔵 | MCP Search Server Enable/Disable Documentation | ~314 |
|
||||
| #6253 | 12:55 PM | 🔵 | MCP Configuration Files Are Identical | ~311 |
|
||||
| #6250 | 12:54 PM | 🔵 | MCP Search Server Connection Failure Reported | ~329 |
|
||||
| #6249 | " | 🔴 | MCP Enable/Disable Implementation Overcomplicated | ~329 |
|
||||
| #6244 | 12:50 PM | 🔴 | MCP search configuration persistence fixed in settings UI | ~378 |
|
||||
| #6225 | 12:44 PM | 🔄 | Renamed MCP Template File from .optional to .template Convention | ~336 |
|
||||
| #6217 | 12:40 PM | ✅ | MCP Documentation Updated to Reference UI Toggle | ~360 |
|
||||
| #6214 | 12:39 PM | ✅ | Repository Cleanup - Removed Temporary Documentation Files | ~321 |
|
||||
| #6201 | 12:28 PM | 🟣 | Created Quick Start Guide for Enabling MCP Search | ~338 |
|
||||
| #6193 | 12:27 PM | 🟣 | Created Optional MCP Configuration File | ~313 |
|
||||
| #6190 | " | 🟣 | Created optional MCP configuration file for user-controlled search server enablement | ~364 |
|
||||
| #6189 | " | 🟣 | Created MCP search server enable/disable documentation | ~307 |
|
||||
| #6183 | 12:26 PM | 🔵 | MCP Search Server Commented Out in Plugin Configuration | ~322 |
|
||||
| #6180 | 12:25 PM | 🔵 | MCP configuration file current state | ~127 |
|
||||
|
||||
### Dec 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #23444 | 2:25 PM | 🟣 | Build Pipeline Execution Successful | ~293 |
|
||||
|
||||
### Dec 11, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24055 | 2:55 PM | ✅ | Build Successful with Bun Runtime Shebangs | ~355 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #26800 | 11:39 PM | ✅ | Version 7.2.3 Build Complete With Worker Restart Fix | ~394 |
|
||||
| #26766 | 11:30 PM | ⚖️ | Root Cause Identified: Missing Post-Install Worker Restart Trigger in Plugin Update Flow | ~604 |
|
||||
| #25815 | 5:31 PM | 🔵 | Comprehensive MCP Server and SKILL.md Structure Analysis | ~575 |
|
||||
| #25813 | " | 🔵 | MCP Server Configuration with Stdio Transport | ~367 |
|
||||
|
||||
### Dec 16, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27555 | 4:48 PM | ✅ | Version bump committed to main branch | ~242 |
|
||||
| #27554 | " | ✅ | Project built successfully with version 7.3.1 | ~306 |
|
||||
|
||||
### Dec 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28922 | 7:28 PM | 🔵 | Plugin MCP Server Configuration for mem-search | ~245 |
|
||||
|
||||
### Dec 19, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #30096 | 8:09 PM | 🔵 | Claude-Mem Plugin Includes MCP Server Configuration | ~246 |
|
||||
| #39050 | 3:44 PM | 🔵 | Plugin commands directory is empty | ~255 |
|
||||
</claude-mem-context>
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
description: "Execute a plan using subagents for implementation"
|
||||
argument-hint: "[task or plan reference]"
|
||||
---
|
||||
|
||||
You are an ORCHESTRATOR.
|
||||
|
||||
Primary instruction: deploy subagents to execute *all* work for #$ARGUMENTS.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
description: "Create an implementation plan with documentation discovery"
|
||||
argument-hint: "[feature or task description]"
|
||||
---
|
||||
|
||||
You are an ORCHESTRATOR.
|
||||
|
||||
Create an LLM-friendly plan in phases that can be executed consecutively in new chat contexts.
|
||||
@@ -1,8 +1,6 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Oct 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# Bugfix Sprint: 2026-01-10
|
||||
|
||||
## Critical Priority (Blocks Users)
|
||||
|
||||
### #646 - Plugin bricks Claude Code - stdin fstat EINVAL crash
|
||||
- **Impact**: Plugin completely bricks Claude Code on Linux. Users cannot recover without manual config editing.
|
||||
- **Root Cause**: Bun's stdin handling causes fstat EINVAL when reading piped input
|
||||
- **Related Discussion**: Check if this is a Bun upstream issue
|
||||
- [ ] Investigate stdin handling in hook scripts
|
||||
- [ ] Test on Linux with stdin piping
|
||||
- [ ] Implement fix
|
||||
|
||||
### #623 - Crash-recovery loop when memory_session_id not captured
|
||||
- **Impact**: Infinite loop consuming API tokens, growing queue unbounded
|
||||
- **Root Cause**: memory_session_id not captured, causes repeated crash-recovery
|
||||
- [ ] Add null/undefined check for memory_session_id
|
||||
- [ ] Add circuit breaker for crash-recovery attempts
|
||||
|
||||
## High Priority
|
||||
|
||||
### #638 - Worker startup missing JSON output causes hooks to appear stuck
|
||||
- **Impact**: UI appears stuck during worker startup
|
||||
- [ ] Ensure worker startup emits proper JSON status
|
||||
- [ ] Add progress feedback to hook output
|
||||
|
||||
### #641/#609 - CLAUDE.md files in subdirectories
|
||||
- **Impact**: CLAUDE.md files scattered throughout project directories
|
||||
- **Note**: This is a documented feature request that was never implemented (setting exists but doesn't work)
|
||||
- [ ] Implement the `disableSubdirectoryCLAUDEmd` setting properly
|
||||
- [ ] Or change default behavior to not create subdirectory files
|
||||
|
||||
### #635 - JSON parsing error prevents folder context generation
|
||||
- **Impact**: Folder context not generating, breaks context injection
|
||||
- **Root Cause**: String spread instead of array spread
|
||||
- [ ] Fix the JSON parsing logic
|
||||
- [ ] Add proper error handling
|
||||
|
||||
## Medium Priority
|
||||
|
||||
### #582 - Tilde paths create literal ~ directories
|
||||
- **Impact**: Directories named "~" created instead of expanding to home
|
||||
- [ ] Use path expansion for tilde in all path operations
|
||||
- [ ] Audit all path handling code
|
||||
|
||||
### #642/#643 - ChromaDB search fails due to initialization timing
|
||||
- **Impact**: Search fails with JSON parse error
|
||||
- **Note**: #643 is a duplicate of #642
|
||||
- [ ] Fix initialization timing issue
|
||||
- [ ] Add proper async/await handling
|
||||
|
||||
### #626 - HealthMonitor hardcodes ~/.claude path
|
||||
- **Impact**: Fails for users with custom config directories
|
||||
- [ ] Use configurable path instead of hardcoded ~/.claude
|
||||
- [ ] Respect CLAUDE_CONFIG_DIR or similar env var
|
||||
|
||||
### #598 - Too many messages pollute conversation history
|
||||
- **Impact**: Hook messages clutter conversation
|
||||
- [ ] Reduce verbosity of hook messages
|
||||
- [ ] Make message frequency configurable
|
||||
|
||||
## Low Priority (Code Quality)
|
||||
|
||||
### #648 - Empty catch blocks swallow JSON parse errors
|
||||
- [ ] Add proper error logging to catch blocks in SessionSearch.ts
|
||||
|
||||
### #649 - Inconsistent logging in CursorHooksInstaller
|
||||
- [ ] Replace console.log with structured logger
|
||||
- **Note**: 177 console.log calls across 20 files identified
|
||||
|
||||
## Won't Fix / Not a Bug
|
||||
|
||||
### #632 - Feature request for disabling CLAUDE.md in subdirectories
|
||||
- **Note**: Covered by #641 implementation
|
||||
|
||||
### #633 - Help request about Cursor integration
|
||||
- **Note**: Documentation/support issue, not a bug
|
||||
|
||||
### #640 - Arabic README translation
|
||||
- **Note**: Documentation PR, not a bugfix
|
||||
|
||||
### #624 - Beta testing strategy proposal
|
||||
- **Note**: Enhancement proposal, not a bugfix
|
||||
|
||||
## Already Fixed in v9.0.2
|
||||
|
||||
- Windows Terminal tab accumulation (#625/#628)
|
||||
- Windows 11 compatibility - WMIC to PowerShell migration
|
||||
- Claude Code 2.1.1 compatibility + path validation (#614)
|
||||
|
||||
---
|
||||
|
||||
**Recommended Approach**: Fix #646 first (critical blocker), then #623 (crash loop), then work through high priority issues in order of impact.
|
||||
+25
-13
@@ -1,6 +1,18 @@
|
||||
{
|
||||
"description": "Claude-mem memory system hooks",
|
||||
"hooks": {
|
||||
"Setup": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "startup|clear|compact",
|
||||
@@ -12,17 +24,12 @@
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"timeout": 60
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code context",
|
||||
"timeout": 60
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code user-message",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code context",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
@@ -33,12 +40,12 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"timeout": 60
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-init",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-init",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
@@ -50,12 +57,12 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"timeout": 60
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code observation",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code observation",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
@@ -66,13 +73,18 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||
"timeout": 60
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code summarize",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code summarize",
|
||||
"timeout": 120
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-complete",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "Code Development (Urdu)",
|
||||
"prompts": {
|
||||
"footer": "IMPORTANT! DO NOT do any work right now other than generating this OBSERVATIONS from tool use messages - and remember that you are a memory agent designed to summarize a DIFFERENT claude code session, not this one.\n\nNever reference yourself or your own actions. Do not output anything other than the observation content formatted in the XML structure above. All other output is ignored by the system, and the system has been designed to be smart about token usage. Please spend your tokens wisely on useful observations.\n\nRemember that we record these observations as a way of helping us stay on track with our progress, and to help us keep important decisions and changes at the forefront of our minds! :) Thank you so much for your help!\n\nLANGUAGE REQUIREMENTS: Please write the observation data in اردو",
|
||||
|
||||
"xml_title_placeholder": "[**title**: بنیادی کام یا موضوع کو بیان کرنے والا مختصر عنوان]",
|
||||
"xml_subtitle_placeholder": "[**subtitle**: ایک جملے میں وضاحت (زیادہ سے زیادہ 24 الفاظ)]",
|
||||
"xml_fact_placeholder": "[مختصر، خود کفیل بیان]",
|
||||
"xml_narrative_placeholder": "[**narrative**: مکمل تناسب: کیا کیا گیا، یہ کیسے کام کرتا ہے، یہ کیوں اہم ہے]",
|
||||
"xml_concept_placeholder": "[علم-نوع-قسم]",
|
||||
"xml_file_placeholder": "[فائل/کا/راستہ]",
|
||||
|
||||
"xml_summary_request_placeholder": "[مختصر عنوان جو صارف کے درخواست اور بحث/کیے گئے کام کا خلاصہ بیان کرتا ہے]",
|
||||
"xml_summary_investigated_placeholder": "[اب تک کیا دریافت کیا گیا ہے؟ کیا جائزہ لیا گیا ہے؟]",
|
||||
"xml_summary_learned_placeholder": "[آپ نے چیزوں کے کام کرنے کے طریقے کے بارے میں کیا سیکھا؟]",
|
||||
"xml_summary_completed_placeholder": "[اب تک کون سا کام مکمل ہوا ہے؟ کیا بھیجا گیا یا تبدیل کیا گیا؟]",
|
||||
"xml_summary_next_steps_placeholder": "[اس سیشن میں آپ فعال طور پر کس پر کام کر رہے ہیں یا آگے کام کرنے کا منصوبہ بنا رہے ہیں؟]",
|
||||
"xml_summary_notes_placeholder": "[موجودہ پیشرفت پر اضافی بصیرت یا نوٹس]",
|
||||
|
||||
"continuation_instruction": "IMPORTANT: Continue generating observations from tool use messages using the XML structure below.\n\nLANGUAGE REQUIREMENTS: Please write the observation data in اردو",
|
||||
|
||||
"summary_footer": "IMPORTANT! DO NOT do any work right now other than generating this next PROGRESS SUMMARY - and remember that you are a memory agent designed to summarize a DIFFERENT claude code session, not this one.\n\nNever reference yourself or your own actions. Do not output anything other than the summary content formatted in the XML structure above. All other output is ignored by the system, and the system has been designed to be smart about token usage. Please spend your tokens wisely on useful summary content.\n\nThank you, this summary will be very useful for keeping track of our progress!\n\nLANGUAGE REQUIREMENTS: Please write ALL summary content (request, investigated, learned, completed, next_steps, notes) in اردو"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem-plugin",
|
||||
"version": "9.0.2",
|
||||
"version": "10.0.2",
|
||||
"private": true,
|
||||
"description": "Runtime dependencies for claude-mem bundled hooks",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 4, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Bun Runner - Finds and executes Bun even when not in PATH
|
||||
*
|
||||
* This script solves the fresh install problem where:
|
||||
* 1. smart-install.js installs Bun to ~/.bun/bin/bun
|
||||
* 2. But Bun isn't in PATH until terminal restart
|
||||
* 3. Subsequent hooks fail because they can't find `bun`
|
||||
*
|
||||
* Usage: node bun-runner.js <script> [args...]
|
||||
*
|
||||
* Fixes #818: Worker fails to start on fresh install
|
||||
*/
|
||||
import { spawnSync, spawn } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
const IS_WINDOWS = process.platform === 'win32';
|
||||
|
||||
/**
|
||||
* Find Bun executable - checks PATH first, then common install locations
|
||||
*/
|
||||
function findBun() {
|
||||
// Try PATH first
|
||||
const pathCheck = spawnSync(IS_WINDOWS ? 'where' : 'which', ['bun'], {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
shell: IS_WINDOWS
|
||||
});
|
||||
|
||||
if (pathCheck.status === 0 && pathCheck.stdout.trim()) {
|
||||
return 'bun'; // Found in PATH
|
||||
}
|
||||
|
||||
// Check common installation paths (handles fresh installs before PATH reload)
|
||||
// Windows: Bun installs to ~/.bun/bin/bun.exe (same as smart-install.js)
|
||||
// Unix: Check default location plus common package manager paths
|
||||
const bunPaths = IS_WINDOWS
|
||||
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||
: [
|
||||
join(homedir(), '.bun', 'bin', 'bun'),
|
||||
'/usr/local/bin/bun',
|
||||
'/opt/homebrew/bin/bun',
|
||||
'/home/linuxbrew/.linuxbrew/bin/bun'
|
||||
];
|
||||
|
||||
for (const bunPath of bunPaths) {
|
||||
if (existsSync(bunPath)) {
|
||||
return bunPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get args: node bun-runner.js <script> [args...]
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error('Usage: node bun-runner.js <script> [args...]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const bunPath = findBun();
|
||||
|
||||
if (!bunPath) {
|
||||
console.error('Error: Bun not found. Please install Bun: https://bun.sh');
|
||||
console.error('After installation, restart your terminal.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Spawn Bun with the provided script and args
|
||||
// Use spawn (not spawnSync) to properly handle stdio
|
||||
// Note: Don't use shell mode on Windows - it breaks paths with spaces in usernames
|
||||
// Use windowsHide to prevent a visible console window from spawning on Windows
|
||||
const child = spawn(bunPath, args, {
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
child.on('error', (err) => {
|
||||
console.error(`Failed to start Bun: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Executable
+228
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# claude-mem Setup Hook
|
||||
# Ensures dependencies are installed before plugin runs
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Use CLAUDE_PLUGIN_ROOT if available, otherwise detect from script location
|
||||
if [[ -z "${CLAUDE_PLUGIN_ROOT:-}" ]]; then
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
else
|
||||
ROOT="$CLAUDE_PLUGIN_ROOT"
|
||||
fi
|
||||
|
||||
MARKER="$ROOT/.install-version"
|
||||
PKG_JSON="$ROOT/package.json"
|
||||
|
||||
# Colors (when terminal supports it)
|
||||
if [[ -t 2 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
else
|
||||
RED='' GREEN='' YELLOW='' BLUE='' NC=''
|
||||
fi
|
||||
|
||||
log_info() { echo -e "${BLUE}ℹ${NC} $*" >&2; }
|
||||
log_ok() { echo -e "${GREEN}✓${NC} $*" >&2; }
|
||||
log_warn() { echo -e "${YELLOW}⚠${NC} $*" >&2; }
|
||||
log_error() { echo -e "${RED}✗${NC} $*" >&2; }
|
||||
|
||||
#
|
||||
# Detect Bun - check PATH and common locations
|
||||
#
|
||||
find_bun() {
|
||||
# Try PATH first
|
||||
if command -v bun &>/dev/null; then
|
||||
echo "bun"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check common install locations
|
||||
local paths=(
|
||||
"$HOME/.bun/bin/bun"
|
||||
"/usr/local/bin/bun"
|
||||
"/opt/homebrew/bin/bun"
|
||||
)
|
||||
|
||||
for p in "${paths[@]}"; do
|
||||
if [[ -x "$p" ]]; then
|
||||
echo "$p"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Detect uv - check PATH and common locations
|
||||
#
|
||||
find_uv() {
|
||||
# Try PATH first
|
||||
if command -v uv &>/dev/null; then
|
||||
echo "uv"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check common install locations
|
||||
local paths=(
|
||||
"$HOME/.local/bin/uv"
|
||||
"$HOME/.cargo/bin/uv"
|
||||
"/usr/local/bin/uv"
|
||||
"/opt/homebrew/bin/uv"
|
||||
)
|
||||
|
||||
for p in "${paths[@]}"; do
|
||||
if [[ -x "$p" ]]; then
|
||||
echo "$p"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Get package.json version
|
||||
#
|
||||
get_pkg_version() {
|
||||
if [[ -f "$PKG_JSON" ]]; then
|
||||
# Simple grep-based extraction (no jq dependency)
|
||||
grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PKG_JSON" | head -1 | sed 's/.*"\([^"]*\)"$/\1/'
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Get marker version (if exists)
|
||||
#
|
||||
get_marker_version() {
|
||||
if [[ -f "$MARKER" ]]; then
|
||||
grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$MARKER" | head -1 | sed 's/.*"\([^"]*\)"$/\1/'
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Get marker's recorded bun version
|
||||
#
|
||||
get_marker_bun() {
|
||||
if [[ -f "$MARKER" ]]; then
|
||||
grep -o '"bun"[[:space:]]*:[[:space:]]*"[^"]*"' "$MARKER" | head -1 | sed 's/.*"\([^"]*\)"$/\1/'
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Check if install is needed
|
||||
#
|
||||
needs_install() {
|
||||
# No node_modules? Definitely need install
|
||||
if [[ ! -d "$ROOT/node_modules" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# No marker? Need install
|
||||
if [[ ! -f "$MARKER" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local pkg_ver marker_ver bun_ver marker_bun
|
||||
pkg_ver=$(get_pkg_version)
|
||||
marker_ver=$(get_marker_version)
|
||||
|
||||
# Version mismatch? Need install
|
||||
if [[ "$pkg_ver" != "$marker_ver" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Bun version changed? Need install
|
||||
if BUN_PATH=$(find_bun); then
|
||||
bun_ver=$("$BUN_PATH" --version 2>/dev/null || echo "")
|
||||
marker_bun=$(get_marker_bun)
|
||||
if [[ -n "$bun_ver" && "$bun_ver" != "$marker_bun" ]]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# All good, no install needed
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Write version marker after successful install
|
||||
#
|
||||
write_marker() {
|
||||
local bun_ver uv_ver pkg_ver
|
||||
pkg_ver=$(get_pkg_version)
|
||||
bun_ver=$("$BUN_PATH" --version 2>/dev/null || echo "unknown")
|
||||
|
||||
if UV_PATH=$(find_uv); then
|
||||
uv_ver=$("$UV_PATH" --version 2>/dev/null | head -1 || echo "unknown")
|
||||
else
|
||||
uv_ver="not-installed"
|
||||
fi
|
||||
|
||||
cat > "$MARKER" <<EOF
|
||||
{
|
||||
"version": "$pkg_ver",
|
||||
"bun": "$bun_ver",
|
||||
"uv": "$uv_ver",
|
||||
"installedAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
# 1. Check for Bun
|
||||
BUN_PATH=$(find_bun) || true
|
||||
if [[ -z "$BUN_PATH" ]]; then
|
||||
log_error "Bun runtime not found!"
|
||||
echo "" >&2
|
||||
echo "claude-mem requires Bun to run. Please install it:" >&2
|
||||
echo "" >&2
|
||||
echo " curl -fsSL https://bun.sh/install | bash" >&2
|
||||
echo "" >&2
|
||||
echo "Or on macOS with Homebrew:" >&2
|
||||
echo "" >&2
|
||||
echo " brew install oven-sh/bun/bun" >&2
|
||||
echo "" >&2
|
||||
echo "Then restart your terminal and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUN_VERSION=$("$BUN_PATH" --version 2>/dev/null || echo "unknown")
|
||||
log_ok "Bun $BUN_VERSION found at $BUN_PATH"
|
||||
|
||||
# 2. Check for uv (optional - for Python/Chroma support)
|
||||
UV_PATH=$(find_uv) || true
|
||||
if [[ -z "$UV_PATH" ]]; then
|
||||
log_warn "uv not found (optional - needed for Python/Chroma vector search)"
|
||||
echo " To install: curl -LsSf https://astral.sh/uv/install.sh | sh" >&2
|
||||
else
|
||||
UV_VERSION=$("$UV_PATH" --version 2>/dev/null | head -1 || echo "unknown")
|
||||
log_ok "uv $UV_VERSION found"
|
||||
fi
|
||||
|
||||
# 3. Install dependencies if needed
|
||||
if needs_install; then
|
||||
log_info "Installing dependencies with Bun..."
|
||||
|
||||
if ! "$BUN_PATH" install --cwd "$ROOT"; then
|
||||
log_error "Failed to install dependencies"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
write_marker
|
||||
log_ok "Dependencies installed ($(get_pkg_version))"
|
||||
else
|
||||
log_ok "Dependencies up to date ($(get_marker_version))"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -65,6 +65,36 @@ function getBunPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum required bun version
|
||||
* v1.1.14+ required for .changes property and multi-statement SQL support
|
||||
*/
|
||||
const MIN_BUN_VERSION = '1.1.14';
|
||||
|
||||
/**
|
||||
* Compare semver versions
|
||||
*/
|
||||
function compareVersions(v1, v2) {
|
||||
const parts1 = v1.split('.').map(Number);
|
||||
const parts2 = v2.split('.').map(Number);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const p1 = parts1[i] || 0;
|
||||
const p2 = parts2[i] || 0;
|
||||
if (p1 > p2) return 1;
|
||||
if (p1 < p2) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if bun version meets minimum requirements
|
||||
*/
|
||||
function isBunVersionSufficient() {
|
||||
const version = getBunVersion();
|
||||
if (!version) return false;
|
||||
return compareVersions(version, MIN_BUN_VERSION) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bun version if installed
|
||||
*/
|
||||
@@ -377,7 +407,7 @@ function installDeps() {
|
||||
|
||||
// Main execution
|
||||
try {
|
||||
// Step 1: Ensure Bun is installed (REQUIRED)
|
||||
// Step 1: Ensure Bun is installed and meets minimum version (REQUIRED)
|
||||
if (!isBunInstalled()) {
|
||||
installBun();
|
||||
|
||||
@@ -389,6 +419,25 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1.5: Ensure Bun version is sufficient
|
||||
if (!isBunVersionSufficient()) {
|
||||
const currentVersion = getBunVersion();
|
||||
console.error(`⚠️ Bun ${currentVersion} is outdated. Minimum required: ${MIN_BUN_VERSION}`);
|
||||
console.error(' Upgrading bun...');
|
||||
try {
|
||||
execSync('bun upgrade', { stdio: 'inherit', shell: IS_WINDOWS });
|
||||
if (!isBunVersionSufficient()) {
|
||||
console.error(`❌ Bun upgrade failed. Please manually upgrade: bun upgrade`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.error(`✅ Bun upgraded to ${getBunVersion()}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to upgrade bun: ${error.message}`);
|
||||
console.error(' Please manually upgrade: bun upgrade');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Ensure uv is installed (REQUIRED for vector search)
|
||||
if (!isUvInstalled()) {
|
||||
installUv();
|
||||
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Statusline Counts — lightweight project-scoped observation counter
|
||||
*
|
||||
* Returns JSON with observation and prompt counts for the given project,
|
||||
* suitable for integration into Claude Code's statusLineCommand.
|
||||
*
|
||||
* Usage:
|
||||
* bun statusline-counts.js <cwd>
|
||||
* bun statusline-counts.js /home/user/my-project
|
||||
*
|
||||
* Output (JSON, stdout):
|
||||
* {"observations": 42, "prompts": 15, "project": "my-project"}
|
||||
*
|
||||
* The project name is derived from basename(cwd). Observations are counted
|
||||
* with a WHERE project = ? filter so only the current project's data is
|
||||
* returned — preventing inflated counts from cross-project observations.
|
||||
*
|
||||
* Performance: ~10ms typical (direct SQLite read, no HTTP, no worker dependency)
|
||||
*/
|
||||
import { Database } from "bun:sqlite";
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { homedir } from "os";
|
||||
import { join, basename } from "path";
|
||||
|
||||
const cwd = process.argv[2] || process.env.CLAUDE_CWD || process.cwd();
|
||||
const project = basename(cwd);
|
||||
|
||||
try {
|
||||
// Resolve data directory: env var → settings.json → default
|
||||
let dataDir = process.env.CLAUDE_MEM_DATA_DIR || join(homedir(), ".claude-mem");
|
||||
if (!process.env.CLAUDE_MEM_DATA_DIR) {
|
||||
const settingsPath = join(dataDir, "settings.json");
|
||||
if (existsSync(settingsPath)) {
|
||||
try {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
if (settings.CLAUDE_MEM_DATA_DIR) dataDir = settings.CLAUDE_MEM_DATA_DIR;
|
||||
} catch { /* use default */ }
|
||||
}
|
||||
}
|
||||
|
||||
const dbPath = join(dataDir, "claude-mem.db");
|
||||
if (!existsSync(dbPath)) {
|
||||
console.log(JSON.stringify({ observations: 0, prompts: 0, project }));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const db = new Database(dbPath, { readonly: true });
|
||||
|
||||
const obs = db.query("SELECT COUNT(*) as c FROM observations WHERE project = ?").get(project);
|
||||
// user_prompts links to projects through sdk_sessions.content_session_id
|
||||
const prompts = db.query(
|
||||
`SELECT COUNT(*) as c FROM user_prompts up
|
||||
JOIN sdk_sessions s ON s.content_session_id = up.content_session_id
|
||||
WHERE s.project = ?`
|
||||
).get(project);
|
||||
console.log(JSON.stringify({ observations: obs.c, prompts: prompts.c, project }));
|
||||
db.close();
|
||||
} catch (e) {
|
||||
console.log(JSON.stringify({ observations: 0, prompts: 0, project, error: e.message }));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user