mirror of
https://github.com/thedotmack/claude-mem
synced 2026-04-25 17:15:04 +02:00
Merge main into feat/chroma-http-server
Resolve conflicts between Chroma HTTP server PR and main branch changes (folder CLAUDE.md, exclusion settings, Zscaler SSL, transport cleanup). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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.10",
|
||||
"version": "10.0.6",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Nov 3, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #3366 | 3:40 PM | 🔵 | Claude Mem MCP Search Architecture and Timeline Tool Capabilities | ~438 |
|
||||
</claude-mem-context>
|
||||
@@ -1,20 +0,0 @@
|
||||
<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 |
|
||||
|----|------|---|-------|------|
|
||||
| #2484 | 6:33 PM | 🔴 | Removed slash commands from incorrect root .claude/commands directory | ~268 |
|
||||
|
||||
### Jan 10, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #39054 | 3:45 PM | 🔄 | Development commands removed from root .claude directory | ~249 |
|
||||
| #39053 | " | 🟣 | Added development commands to plugin distribution | ~276 |
|
||||
| #39051 | 3:44 PM | 🔵 | Development commands confirmed in .claude/commands/ | ~315 |
|
||||
| #39049 | " | 🔵 | Development commands located in .claude/commands/ directory | ~293 |
|
||||
</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>
|
||||
176
.claude/plans/bugfix-env-auth.md
Normal file
176
.claude/plans/bugfix-env-auth.md
Normal file
@@ -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.
|
||||
266
.claude/plans/fix-empty-claude-md-files.md
Normal file
266
.claude/plans/fix-empty-claude-md-files.md
Normal file
@@ -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
|
||||
394
.claude/plans/remove-worker-start-calls.md
Normal file
394
.claude/plans/remove-worker-start-calls.md
Normal file
@@ -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)
|
||||
21
.github/ISSUE_TEMPLATE/CLAUDE.md
vendored
21
.github/ISSUE_TEMPLATE/CLAUDE.md
vendored
@@ -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>
|
||||
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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:
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -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 [...]
|
||||
|
||||
|
||||
7
.github/issues/CLAUDE.md
vendored
7
.github/issues/CLAUDE.md
vendored
@@ -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>
|
||||
82
.github/workflows/CLAUDE.md
vendored
82
.github/workflows/CLAUDE.md
vendored
@@ -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>
|
||||
29
.github/workflows/deploy-install-scripts.yml
vendored
Normal file
29
.github/workflows/deploy-install-scripts.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Deploy Install Scripts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'openclaw/install.sh'
|
||||
- 'install/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Copy install scripts to deploy directory
|
||||
run: |
|
||||
mkdir -p install/public
|
||||
cp openclaw/install.sh install/public/openclaw.sh
|
||||
|
||||
- name: Deploy to Vercel
|
||||
uses: amondnet/vercel-action@v25
|
||||
with:
|
||||
vercel-token: ${{ secrets.VERCEL_TOKEN }}
|
||||
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
|
||||
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
vercel-args: '--prod'
|
||||
working-directory: ./install
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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/
|
||||
|
||||
713
CHANGELOG.md
713
CHANGELOG.md
@@ -2,6 +2,489 @@
|
||||
|
||||
All notable changes to claude-mem.
|
||||
|
||||
## [v10.0.6] - 2026-02-13
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **OpenClaw: Fix MEMORY.md project query mismatch** — `syncMemoryToWorkspace` now includes both the base project name and the agent-scoped project name (e.g., both "openclaw" and "openclaw-main") when querying for context injection, ensuring the correct observations are pulled into MEMORY.md.
|
||||
|
||||
- **OpenClaw: Add feed botToken support for Telegram** — Feeds can now configure a dedicated `botToken` for direct Telegram message delivery, bypassing the OpenClaw gateway channel. This fixes scenarios where the gateway bot token couldn't be used for feed messages.
|
||||
|
||||
## Other
|
||||
|
||||
- Changed OpenClaw plugin kind from "integration" to "memory" for accuracy.
|
||||
|
||||
## [v10.0.5] - 2026-02-13
|
||||
|
||||
## OpenClaw Installer & Distribution
|
||||
|
||||
This release introduces the OpenClaw one-liner installer and fixes several OpenClaw plugin issues.
|
||||
|
||||
### New Features
|
||||
|
||||
- **OpenClaw Installer** (`openclaw/install.sh`): Full cross-platform installer script with `curl | bash` support
|
||||
- Platform detection (macOS, Linux, WSL)
|
||||
- Automatic dependency management (Bun, uv, Node.js)
|
||||
- Interactive AI provider setup with settings writer
|
||||
- OpenClaw gateway detection, plugin install, and memory slot configuration
|
||||
- Worker startup and health verification with rich diagnostics
|
||||
- TTY detection, `--provider`/`--api-key` CLI flags
|
||||
- Error recovery and upgrade handling for existing installations
|
||||
- jq/python3/node fallback chain for JSON config writing
|
||||
- **Distribution readiness tests** (`openclaw/test-install.sh`): Comprehensive test suite for the installer
|
||||
- **Enhanced `/api/health` endpoint**: Now returns version, uptime, workerPath, and AI status
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: use `event.prompt` instead of `ctx.sessionKey` for prompt storage in OpenClaw plugin
|
||||
- Fix: detect both `openclaw` and `openclaw.mjs` binary names in gateway discovery
|
||||
- Fix: pass file paths via env vars instead of bash interpolation in `node -e` calls
|
||||
- Fix: handle stale plugin config that blocks OpenClaw CLI during reinstall
|
||||
- Fix: remove stale memory slot reference during reinstall cleanup
|
||||
- Fix: remove opinionated filters from OpenClaw plugin
|
||||
|
||||
## [v10.0.4] - 2026-02-12
|
||||
|
||||
## Revert: v10.0.3 chroma-mcp spawn storm fix
|
||||
|
||||
v10.0.3 introduced regressions. This release reverts the codebase to the stable v10.0.2 state.
|
||||
|
||||
### What was reverted
|
||||
|
||||
- Connection mutex via promise memoization
|
||||
- Pre-spawn process count guard
|
||||
- Hardened `close()` with try-finally + Unix `pkill -P` fallback
|
||||
- Count-based orphan reaper in `ProcessManager`
|
||||
- Circuit breaker (3 failures → 60s cooldown)
|
||||
- `etime`-based sorting for process guards
|
||||
|
||||
### Files restored to v10.0.2
|
||||
|
||||
- `src/services/sync/ChromaSync.ts`
|
||||
- `src/services/infrastructure/GracefulShutdown.ts`
|
||||
- `src/services/infrastructure/ProcessManager.ts`
|
||||
- `src/services/worker-service.ts`
|
||||
- `src/services/worker/ProcessRegistry.ts`
|
||||
- `tests/infrastructure/process-manager.test.ts`
|
||||
- `tests/integration/chroma-vector-sync.test.ts`
|
||||
|
||||
## [v10.0.3] - 2026-02-11
|
||||
|
||||
## Fix: Prevent chroma-mcp spawn storm (PR #1065)
|
||||
|
||||
Fixes a critical bug where killing the worker daemon during active sessions caused **641 chroma-mcp Python processes** to spawn in ~5 minutes, consuming 75%+ CPU and ~64GB virtual memory.
|
||||
|
||||
### Root Cause
|
||||
|
||||
`ChromaSync.ensureConnection()` had no connection mutex. Concurrent fire-and-forget `syncObservation()` calls from multiple sessions raced through the check-then-act guard, each spawning a chroma-mcp subprocess via `StdioClientTransport`. Error-driven reconnection created a positive feedback loop.
|
||||
|
||||
### 5-Layer Defense
|
||||
|
||||
| Layer | Mechanism | Purpose |
|
||||
|-------|-----------|---------|
|
||||
| **0** | Connection mutex via promise memoization | Coalesces concurrent callers onto a single spawn attempt |
|
||||
| **1** | Pre-spawn process count guard (`execFileSync('ps')`) | Kills excess chroma-mcp processes before spawning new ones |
|
||||
| **2** | Hardened `close()` with try-finally + Unix `pkill -P` fallback | Guarantees state reset even on error, kills orphaned children |
|
||||
| **3** | Count-based orphan reaper in `ProcessManager` | Kills by count (not age), catches spawn storms where all processes are young |
|
||||
| **4** | Circuit breaker (3 failures → 60s cooldown) | Stops error-driven reconnection positive feedback loop |
|
||||
|
||||
### Additional Fix
|
||||
|
||||
- Process guards now use `etime`-based sorting instead of PID ordering for reliable age determination (PIDs wrap and don't guarantee ordering)
|
||||
|
||||
### Testing
|
||||
|
||||
- 16 new tests for mutex, circuit breaker, close() hardening, and count guard
|
||||
- All tests pass (947 pass, 3 skip)
|
||||
|
||||
Closes #1063, closes #695. Relates to #1010, #707.
|
||||
|
||||
**Contributors:** @rodboev
|
||||
|
||||
## [v10.0.2] - 2026-02-11
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Prevent daemon silent death from SIGHUP + unhandled errors** — Worker process could silently die when receiving SIGHUP signals or encountering unhandled errors, leaving hooks without a backend. Now properly handles these signals and prevents silent crashes.
|
||||
- **Hook resilience and worker lifecycle improvements** — Comprehensive fixes for hook command error classification, addressing issues #957, #923, #984, #987, and #1042. Hooks now correctly distinguish between worker unavailability errors and other failures.
|
||||
- **Clarify TypeError order dependency in error classifier** — Fixed error classification logic to properly handle TypeError ordering edge cases.
|
||||
|
||||
## New Features
|
||||
|
||||
- **Project-scoped statusline counter utility** — Added `statusline-counts.js` for tracking observation counts per project in the Claude Code status line.
|
||||
|
||||
## Internal
|
||||
|
||||
- Added test coverage for hook command error classification and process manager
|
||||
- Worker service and MCP server lifecycle improvements
|
||||
- Process manager enhancements for better cross-platform stability
|
||||
|
||||
### Contributors
|
||||
- @rodboev — Hook resilience and worker lifecycle fixes (PR #1056)
|
||||
|
||||
## [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
|
||||
@@ -1084,233 +1567,3 @@ Since we're now explicit about recovery instead of silently papering over proble
|
||||
|
||||
This patch release improves stability by adding proper error handling to Chroma vector database sync operations, preventing worker crashes when sync operations timeout.
|
||||
|
||||
## [v8.0.5] - 2025-12-24
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Context Loading**: Fixed observation filtering for non-code modes, ensuring observations are properly retrieved across all mode types
|
||||
|
||||
## Technical Details
|
||||
|
||||
Refactored context loading logic to differentiate between code and non-code modes, resolving issues where mode-specific observations were filtered by stale settings.
|
||||
|
||||
## [v8.0.4] - 2025-12-23
|
||||
|
||||
## Changes
|
||||
|
||||
- Changed worker start script
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v8.0.3] - 2025-12-23
|
||||
|
||||
Fix critical worker crashes on startup (v8.0.2 regression)
|
||||
|
||||
## [v8.0.2] - 2025-12-23
|
||||
|
||||
New "chill" remix of code mode for users who want fewer, more selective observations.
|
||||
|
||||
## Features
|
||||
|
||||
- **code--chill mode**: A behavioral variant that produces fewer observations
|
||||
- Only records things "painful to rediscover" - shipped features, architectural decisions, non-obvious gotchas
|
||||
- Skips routine work, straightforward implementations, and obvious changes
|
||||
- Philosophy: "When in doubt, skip it"
|
||||
|
||||
## Documentation
|
||||
|
||||
- Updated modes.mdx with all 28 language modes (was 10)
|
||||
- Added Code Mode Variants section documenting chill mode
|
||||
|
||||
## Usage
|
||||
|
||||
Set in ~/.claude-mem/settings.json:
|
||||
```json
|
||||
{
|
||||
"CLAUDE_MEM_MODE": "code--chill"
|
||||
}
|
||||
```
|
||||
|
||||
## [v8.0.1] - 2025-12-23
|
||||
|
||||
## 🎨 UI Improvements
|
||||
|
||||
- **Header Redesign**: Moved documentation and X (Twitter) links from settings modal to main header for better accessibility
|
||||
- **Removed Product Hunt Badge**: Cleaned up header layout by removing the Product Hunt badge
|
||||
- **Icon Reorganization**: Reordered header icons for improved UX flow (Docs → X → Discord → GitHub)
|
||||
|
||||
---
|
||||
|
||||
🤖 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
|
||||
|
||||
|
||||
38
README.md
38
README.md
@@ -22,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> •
|
||||
@@ -41,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> •
|
||||
@@ -109,13 +111,23 @@
|
||||
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.
|
||||
|
||||
### 🦞 OpenClaw Gateway
|
||||
|
||||
Install claude-mem as a persistent memory plugin on [OpenClaw](https://openclaw.ai) gateways with a single command:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash
|
||||
```
|
||||
|
||||
The installer handles dependencies, plugin setup, AI provider configuration, worker startup, and optional real-time observation feeds to Telegram, Discord, Slack, and more. See the [OpenClaw Integration Guide](https://docs.claude-mem.ai/openclaw-integration) for details.
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- 🧠 **Persistent Memory** - Context survives across sessions
|
||||
@@ -133,7 +145,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
|
||||
|
||||
@@ -182,7 +194,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:**
|
||||
|
||||
@@ -195,6 +207,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:**
|
||||
@@ -202,7 +215,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:**
|
||||
|
||||
@@ -214,6 +228,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.
|
||||
@@ -236,6 +253,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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
213
docs/PR-SHIPPING-REPORT.md
Normal file
213
docs/PR-SHIPPING-REPORT.md
Normal file
@@ -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,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>
|
||||
@@ -13,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> •
|
||||
@@ -32,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> •
|
||||
@@ -129,7 +131,7 @@ Claude-Mem هو نظام متطور مصمم لضغط وحفظ الذاكرة ل
|
||||
|
||||
## المستندات
|
||||
|
||||
📚 **[عرض التوثيق الكامل](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 @@ 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/)** - Переглянути на офіційному сайті
|
||||
|
||||
### Початок роботи
|
||||
|
||||
|
||||
311
docs/i18n/README.ur.md
Normal file
311
docs/i18n/README.ur.md
Normal file
@@ -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
|
||||
|
||||
|
||||
311
docs/i18n/README.zh-tw.md
Normal file
311
docs/i18n/README.zh-tw.md
Normal file
@@ -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 制作**
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
@@ -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` | 한국어 |
|
||||
|
||||
385
docs/public/openclaw-integration.mdx
Normal file
385
docs/public/openclaw-integration.mdx
Normal file
@@ -0,0 +1,385 @@
|
||||
---
|
||||
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
|
||||
|
||||
Run this one-liner to install everything automatically:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash
|
||||
```
|
||||
|
||||
The installer handles dependency checks (Bun, uv), plugin installation, memory slot configuration, AI provider setup, worker startup, and optional observation feed configuration.
|
||||
|
||||
You can also pre-select options:
|
||||
|
||||
```bash
|
||||
# With a specific AI provider
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --provider=gemini --api-key=YOUR_KEY
|
||||
|
||||
# Fully unattended (defaults to Claude Max Plan)
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --non-interactive
|
||||
|
||||
# Upgrade existing installation
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --upgrade
|
||||
```
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
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.
|
||||
|
||||
@@ -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>
|
||||
2
install/.gitignore
vendored
Normal file
2
install/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vercel
|
||||
public/openclaw.sh
|
||||
12
install/vercel.json
Normal file
12
install/vercel.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"headers": [
|
||||
{
|
||||
"source": "/(.*)\\.sh",
|
||||
"headers": [
|
||||
{ "key": "Content-Type", "value": "text/plain; charset=utf-8" },
|
||||
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=60" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
2
openclaw/.gitignore
vendored
Normal file
2
openclaw/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
69
openclaw/Dockerfile.e2e
Normal file
69
openclaw/Dockerfile.e2e
Normal file
@@ -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"]
|
||||
458
openclaw/SKILL.md
Normal file
458
openclaw/SKILL.md
Normal file
@@ -0,0 +1,458 @@
|
||||
# Claude-Mem OpenClaw Plugin — Setup Guide
|
||||
|
||||
This guide walks through setting up the claude-mem plugin on an OpenClaw gateway. 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.
|
||||
|
||||
## Quick Install (Recommended)
|
||||
|
||||
Run this one-liner to install everything automatically:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash
|
||||
```
|
||||
|
||||
The installer handles dependency checks (Bun, uv), plugin installation, memory slot configuration, AI provider setup, worker startup, and optional observation feed configuration — all interactively.
|
||||
|
||||
### Install with options
|
||||
|
||||
Pre-select your AI provider and API key to skip interactive prompts:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --provider=gemini --api-key=YOUR_KEY
|
||||
```
|
||||
|
||||
For fully unattended installation (defaults to Claude Max Plan, skips observation feed):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --non-interactive
|
||||
```
|
||||
|
||||
To upgrade an existing installation (preserves settings, updates plugin):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --upgrade
|
||||
```
|
||||
|
||||
After installation, skip to [Step 4: Restart the Gateway and Verify](#step-4-restart-the-gateway-and-verify) to confirm everything is working.
|
||||
|
||||
---
|
||||
|
||||
## Manual Setup
|
||||
|
||||
The steps below are for manual installation if you prefer not to use the automated installer, or need to troubleshoot individual steps.
|
||||
|
||||
### 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 |
|
||||
279
openclaw/TESTING.md
Normal file
279
openclaw/TESTING.md
Normal file
@@ -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
|
||||
265
openclaw/e2e-verify.sh
Executable file
265
openclaw/e2e-verify.sh
Executable file
@@ -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
|
||||
1801
openclaw/install.sh
Executable file
1801
openclaw/install.sh
Executable file
File diff suppressed because it is too large
Load Diff
53
openclaw/openclaw.plugin.json
Normal file
53
openclaw/openclaw.plugin.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"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": "memory",
|
||||
"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"
|
||||
},
|
||||
"botToken": {
|
||||
"type": "string",
|
||||
"description": "Optional dedicated Telegram bot token for the feed (bypasses gateway channel)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
openclaw/package.json
Normal file
20
openclaw/package.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
962
openclaw/src/index.test.ts
Normal file
962
openclaw/src/index.test.ts
Normal file
@@ -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({});
|
||||
});
|
||||
});
|
||||
804
openclaw/src/index.ts
Normal file
804
openclaw/src/index.ts
Normal file
@@ -0,0 +1,804 @@
|
||||
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 MessageReceivedEvent {
|
||||
from: string;
|
||||
content: string;
|
||||
timestamp?: number;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface EventContext {
|
||||
sessionKey?: string;
|
||||
workspaceDir?: string;
|
||||
agentId?: string;
|
||||
}
|
||||
|
||||
interface MessageContext {
|
||||
channelId: string;
|
||||
accountId?: string;
|
||||
conversationId?: string;
|
||||
}
|
||||
|
||||
type EventCallback<T> = (event: T, ctx: EventContext) => void | Promise<void>;
|
||||
type MessageEventCallback<T> = (event: T, ctx: MessageContext) => 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: "message_received", callback: MessageEventCallback<MessageReceivedEvent>) => 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;
|
||||
botToken?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Constants
|
||||
// ============================================================================
|
||||
|
||||
const MAX_SSE_BUFFER_SIZE = 1024 * 1024; // 1MB
|
||||
const DEFAULT_WORKER_PORT = 37777;
|
||||
|
||||
// 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" },
|
||||
};
|
||||
|
||||
async function sendDirectTelegram(
|
||||
botToken: string,
|
||||
chatId: string,
|
||||
text: string,
|
||||
logger: PluginLogger
|
||||
): Promise<void> {
|
||||
try {
|
||||
const response = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
chat_id: chatId,
|
||||
text,
|
||||
parse_mode: "Markdown",
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const body = await response.text();
|
||||
logger.warn(`[claude-mem] Direct Telegram send failed (${response.status}): ${body}`);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
logger.warn(`[claude-mem] Direct Telegram send error: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function sendToChannel(
|
||||
api: OpenClawPluginApi,
|
||||
channel: string,
|
||||
to: string,
|
||||
text: string,
|
||||
botToken?: string
|
||||
): Promise<void> {
|
||||
// If a dedicated bot token is provided for Telegram, send directly
|
||||
if (botToken && channel === "telegram") {
|
||||
return sendDirectTelegram(botToken, to, text, api.logger);
|
||||
}
|
||||
|
||||
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,
|
||||
botToken?: string
|
||||
): 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, botToken);
|
||||
}
|
||||
} 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, ctx?: EventContext): Promise<void> {
|
||||
// Include both the base project and agent-scoped project (e.g. "openclaw" + "openclaw-main")
|
||||
const projects = [baseProjectName];
|
||||
const agentProject = ctx ? getProjectName(ctx) : null;
|
||||
if (agentProject && agentProject !== baseProjectName) {
|
||||
projects.push(agentProject);
|
||||
}
|
||||
const contextText = await workerGetText(
|
||||
workerPort,
|
||||
`/api/context/inject?projects=${encodeURIComponent(projects.join(","))}`,
|
||||
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: message_received — capture inbound user prompts from channels
|
||||
// ------------------------------------------------------------------
|
||||
api.on("message_received", async (event, ctx) => {
|
||||
const sessionKey = ctx.conversationId || ctx.channelId || "default";
|
||||
const contentSessionId = getContentSessionId(sessionKey);
|
||||
|
||||
await workerPost(workerPort, "/api/sessions/init", {
|
||||
contentSessionId,
|
||||
project: baseProjectName,
|
||||
prompt: event.content || "[media prompt]",
|
||||
}, api.logger);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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: event.prompt || "agent run",
|
||||
}, api.logger);
|
||||
|
||||
// Sync MEMORY.md before agent runs (provides context to agent)
|
||||
if (syncMemoryFile && ctx.workspaceDir) {
|
||||
await syncMemoryToWorkspace(ctx.workspaceDir, ctx);
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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) return;
|
||||
|
||||
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||
|
||||
// Extract result text from all content blocks
|
||||
let toolResponseText = "";
|
||||
const content = event.message?.content;
|
||||
if (Array.isArray(content)) {
|
||||
toolResponseText = content
|
||||
.filter((block) => (block.type === "tool_result" || block.type === "text") && "text" in block)
|
||||
.map((block) => String(block.text))
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
// 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, ctx);
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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; },
|
||||
feedConfig.botToken
|
||||
);
|
||||
},
|
||||
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})`);
|
||||
}
|
||||
46
openclaw/test-e2e.sh
Executable file
46
openclaw/test-e2e.sh
Executable file
@@ -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
|
||||
2339
openclaw/test-install.sh
Executable file
2339
openclaw/test-install.sh
Executable file
File diff suppressed because it is too large
Load Diff
106
openclaw/test-sse-consumer.js
Normal file
106
openclaw/test-sse-consumer.js
Normal file
@@ -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");
|
||||
}
|
||||
26
openclaw/tsconfig.json
Normal file
26
openclaw/tsconfig.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.10",
|
||||
"version": "10.0.6",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
@@ -66,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",
|
||||
@@ -82,7 +82,18 @@
|
||||
"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",
|
||||
@@ -90,6 +101,7 @@
|
||||
"@modelcontextprotocol/sdk": "^1.25.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"chromadb": "^3.2.2",
|
||||
"dompurify": "^3.3.1",
|
||||
"express": "^4.18.2",
|
||||
"glob": "^11.0.3",
|
||||
"handlebars": "^4.7.8",
|
||||
@@ -100,11 +112,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.10",
|
||||
"version": "10.0.6",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 10, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #39048 | 3:44 PM | 🔵 | Plugin directory contains commands folder | ~276 |
|
||||
| #39050 | 3:44 PM | 🔵 | Plugin commands directory is empty | ~255 |
|
||||
</claude-mem-context>
|
||||
@@ -1,18 +0,0 @@
|
||||
<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 |
|
||||
|----|------|---|-------|------|
|
||||
| #2437 | 4:32 PM | 🟣 | Slash Command Files Created for Quick Settings Toggling | ~478 |
|
||||
|
||||
### Jan 10, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #39052 | 3:44 PM | 🟣 | Commands added to plugin distribution | ~268 |
|
||||
| #39050 | " | 🔵 | Plugin commands directory is empty | ~255 |
|
||||
</claude-mem-context>
|
||||
@@ -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 |
|
||||
|
||||
@@ -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>
|
||||
25
plugin/modes/code--ur.json
Normal file
25
plugin/modes/code--ur.json
Normal file
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem-plugin",
|
||||
"version": "9.0.10",
|
||||
"version": "10.0.6",
|
||||
"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 |
|
||||
|
||||
90
plugin/scripts/bun-runner.js
Normal file
90
plugin/scripts/bun-runner.js
Normal file
@@ -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
228
plugin/scripts/setup.sh
Executable file
228
plugin/scripts/setup.sh
Executable file
@@ -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();
|
||||
|
||||
61
plugin/scripts/statusline-counts.js
Executable file
61
plugin/scripts/statusline-counts.js
Executable file
@@ -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 }));
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,17 +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 |
|
||||
|----|------|---|-------|------|
|
||||
| #11179 | 4:09 AM | 🔵 | Search skill uses unified endpoint but lacks ID-based lookup instructions | ~370 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33558 | 11:05 PM | 🔵 | Current mem-search skill workflow documentation | ~446 |
|
||||
</claude-mem-context>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user