claude-mneme 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +17 -0
- package/CLAUDE.md +98 -0
- package/CONFIG_REFERENCE.md +495 -0
- package/README.md +40 -0
- package/commands/entity.md +64 -0
- package/commands/forget.md +69 -0
- package/commands/remember.md +60 -0
- package/commands/status.md +90 -0
- package/commands/summarize.md +69 -0
- package/hooks/hooks.json +123 -0
- package/package.json +12 -0
- package/scripts/mem-add.mjs +59 -0
- package/scripts/mem-entity.mjs +143 -0
- package/scripts/mem-forget.mjs +245 -0
- package/scripts/mem-status.mjs +319 -0
- package/scripts/mem-summarize.mjs +338 -0
- package/scripts/post-compact.mjs +132 -0
- package/scripts/post-tool-use.mjs +353 -0
- package/scripts/pre-compact.mjs +491 -0
- package/scripts/session-start.mjs +283 -0
- package/scripts/session-stop.mjs +31 -0
- package/scripts/stop-capture.mjs +294 -0
- package/scripts/subagent-stop.mjs +203 -0
- package/scripts/summarize.mjs +428 -0
- package/scripts/sync.mjs +609 -0
- package/scripts/user-prompt-submit.mjs +77 -0
- package/scripts/utils.mjs +2142 -0
- package/scripts/utils.test.mjs +1465 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Look up what Mneme knows about a file, function, or entity
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
The user wants to query what Mneme knows about a specific entity (file, function, error, or package). They may have provided the entity name after the command.
|
|
8
|
+
|
|
9
|
+
If the user provided a query (e.g., `/entity auth.ts`), search for it directly.
|
|
10
|
+
|
|
11
|
+
If no query was provided, ask the user: "What entity would you like to look up? (e.g., a file name, function, or package)"
|
|
12
|
+
|
|
13
|
+
## How to query entities
|
|
14
|
+
|
|
15
|
+
Use the mem-entity script to search the entity index:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-entity.mjs" [options] [query]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
- `--list` - List all indexed entities with their mention counts
|
|
23
|
+
- `--category <cat>` - Filter by category: files, functions, errors, packages
|
|
24
|
+
- `<query>` - Search for entities matching this name (partial match supported)
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Look up a specific file
|
|
30
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-entity.mjs" auth.ts
|
|
31
|
+
|
|
32
|
+
# List all indexed files
|
|
33
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-entity.mjs" --list --category files
|
|
34
|
+
|
|
35
|
+
# Search for functions containing "handle"
|
|
36
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-entity.mjs" --category functions handle
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Output format
|
|
40
|
+
|
|
41
|
+
The script returns JSON with:
|
|
42
|
+
- `matches` - Entities matching the query
|
|
43
|
+
- Each match includes: `name`, `category`, `mentions`, `lastSeen`, `contexts` (recent activity)
|
|
44
|
+
|
|
45
|
+
## How to respond
|
|
46
|
+
|
|
47
|
+
1. Run the query
|
|
48
|
+
2. Present results in a readable format, highlighting:
|
|
49
|
+
- How many times the entity was mentioned
|
|
50
|
+
- When it was last seen
|
|
51
|
+
- Recent contexts where it appeared
|
|
52
|
+
3. If no matches found, suggest similar entities or let user know
|
|
53
|
+
4. For broad queries, summarize the top results
|
|
54
|
+
|
|
55
|
+
## Example response
|
|
56
|
+
|
|
57
|
+
For `/entity auth.ts`:
|
|
58
|
+
|
|
59
|
+
> **auth.ts** (file) — 12 mentions, last seen 2 hours ago
|
|
60
|
+
>
|
|
61
|
+
> Recent activity:
|
|
62
|
+
> - [commit] Added JWT validation to auth middleware
|
|
63
|
+
> - [task] Implementing auth flow
|
|
64
|
+
> - [prompt] User asked about auth token refresh
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Remove entries from remembered items
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
The user wants to remove something from their remembered items. They may have specified what to forget, or you may need to show them the list.
|
|
8
|
+
|
|
9
|
+
## How to use the forget script
|
|
10
|
+
|
|
11
|
+
The mem-forget script has three modes:
|
|
12
|
+
|
|
13
|
+
### List all entries
|
|
14
|
+
```bash
|
|
15
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-forget.mjs" --list
|
|
16
|
+
```
|
|
17
|
+
Returns JSON array of entries with their indices.
|
|
18
|
+
|
|
19
|
+
### Remove entries by index
|
|
20
|
+
```bash
|
|
21
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-forget.mjs" --remove 0,2,3
|
|
22
|
+
```
|
|
23
|
+
Removes entries at the specified indices (comma-separated).
|
|
24
|
+
|
|
25
|
+
### Find matching entries (AI-assisted)
|
|
26
|
+
```bash
|
|
27
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-forget.mjs" --match "description of what to forget"
|
|
28
|
+
```
|
|
29
|
+
Uses AI to identify which entries match the description. Returns matching indices.
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
|
|
33
|
+
### If the user specified what to forget (e.g., `/forget my tabs preference`)
|
|
34
|
+
|
|
35
|
+
1. Run the script with `--match` to find matching entries
|
|
36
|
+
2. If matches found, show the user what will be removed
|
|
37
|
+
3. Ask for confirmation before removing
|
|
38
|
+
4. On confirmation, run `--remove` with the indices
|
|
39
|
+
|
|
40
|
+
### If the user just said `/forget` with no content
|
|
41
|
+
|
|
42
|
+
1. Run `--list` to show all remembered items
|
|
43
|
+
2. Display them numbered (starting from 0) with type and content
|
|
44
|
+
3. Ask the user which ones to remove (they can specify by number or describe what to remove)
|
|
45
|
+
4. If they give numbers: confirm and remove those indices
|
|
46
|
+
5. If they describe what to remove: use `--match` to find them, confirm, then remove
|
|
47
|
+
|
|
48
|
+
## Guidelines
|
|
49
|
+
|
|
50
|
+
1. Always show the user exactly what will be removed before doing it
|
|
51
|
+
2. Require explicit confirmation before removing anything
|
|
52
|
+
3. After removal, confirm what was removed
|
|
53
|
+
4. If no matches found, let the user know and offer to list all entries
|
|
54
|
+
|
|
55
|
+
## Example interactions
|
|
56
|
+
|
|
57
|
+
User: `/forget my preference about tabs`
|
|
58
|
+
→ Run `--match "preference about tabs"`
|
|
59
|
+
→ Show: "Found 1 matching entry: [preference] Prefers tabs over spaces. Remove it?"
|
|
60
|
+
→ On yes: Run `--remove 0`
|
|
61
|
+
→ Confirm: "Removed 1 entry."
|
|
62
|
+
|
|
63
|
+
User: `/forget`
|
|
64
|
+
→ Run `--list`
|
|
65
|
+
→ Show numbered list of all entries
|
|
66
|
+
→ Ask: "Which would you like to remove? (Enter numbers or describe)"
|
|
67
|
+
→ User: "1 and 3"
|
|
68
|
+
→ Confirm: "Remove these 2 entries? [show them]"
|
|
69
|
+
→ On yes: Run `--remove 1,3`
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Save something to memory for future sessions
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
The user wants to save something to memory. They may have provided the content after the /remember command, or you may need to ask them what to remember.
|
|
8
|
+
|
|
9
|
+
If the user provided content (e.g., `/remember I prefer using TypeScript`), save it directly.
|
|
10
|
+
|
|
11
|
+
If no content was provided, ask the user: "What would you like me to remember?"
|
|
12
|
+
|
|
13
|
+
## How to save memories
|
|
14
|
+
|
|
15
|
+
Use the mem-add script to save the memory:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-add.mjs" <type> "<content>"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Types to use:
|
|
22
|
+
- `preference` - User preferences (coding style, tools, workflows)
|
|
23
|
+
- `project` - Project information and status
|
|
24
|
+
- `fact` - Facts about the user or environment
|
|
25
|
+
- `note` - General notes
|
|
26
|
+
- `lesson` - Lessons learned, anti-patterns, approaches that failed — things to avoid repeating
|
|
27
|
+
|
|
28
|
+
## How it works
|
|
29
|
+
|
|
30
|
+
Remembered items are stored in a dedicated persistent file (`remembered.json`) separate from the activity log. They are **never summarized or removed automatically** — they are injected into every session exactly as saved.
|
|
31
|
+
|
|
32
|
+
If the user wants to remove or edit remembered items, they must manually edit the file:
|
|
33
|
+
`~/.claude-mneme/projects/<project>/remembered.json`
|
|
34
|
+
|
|
35
|
+
Let the user know this when saving, e.g.: "Saved. This will persist across all future sessions. To remove it later, edit `remembered.json` in your project's memory directory."
|
|
36
|
+
|
|
37
|
+
## Guidelines
|
|
38
|
+
|
|
39
|
+
1. Parse what the user wants to remember and choose the appropriate type
|
|
40
|
+
2. Keep the content concise but complete
|
|
41
|
+
3. Confirm what was saved and briefly explain persistence
|
|
42
|
+
4. If ambiguous, default to `note` type
|
|
43
|
+
5. Use `lesson` when the user describes something that didn't work, a mistake to avoid, or an anti-pattern discovered
|
|
44
|
+
|
|
45
|
+
## Examples
|
|
46
|
+
|
|
47
|
+
User: `/remember I prefer functional programming over OOP`
|
|
48
|
+
-> Save as `preference`: "Prefers functional programming over OOP"
|
|
49
|
+
|
|
50
|
+
User: `/remember working on a React Native app called GhostTube`
|
|
51
|
+
-> Save as `project`: "Working on React Native app called GhostTube"
|
|
52
|
+
|
|
53
|
+
User: `/remember don't use git reset --hard in this repo, it wipes untracked test fixtures`
|
|
54
|
+
-> Save as `lesson`: "Don't use git reset --hard — it wipes untracked test fixtures"
|
|
55
|
+
|
|
56
|
+
User: `/remember tried using Redis for caching but latency was worse than in-memory due to serialization overhead`
|
|
57
|
+
-> Save as `lesson`: "Redis caching attempt failed — serialization overhead made it slower than in-memory"
|
|
58
|
+
|
|
59
|
+
User: `/remember`
|
|
60
|
+
-> Ask: "What would you like me to remember?"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Check plugin health and diagnose issues
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
The user wants to check the health status of the claude-mneme plugin. Run the health check and report the results clearly.
|
|
8
|
+
|
|
9
|
+
## How to check status
|
|
10
|
+
|
|
11
|
+
Run the health check script:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-status.mjs"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
To clear the error log:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-status.mjs" --clear-errors
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Understanding the output
|
|
24
|
+
|
|
25
|
+
The script returns JSON with:
|
|
26
|
+
|
|
27
|
+
- `overall`: "healthy", "degraded", or "unhealthy"
|
|
28
|
+
- `checks`: Individual check results
|
|
29
|
+
- `errors`: Critical issues that need fixing
|
|
30
|
+
- `warnings`: Non-critical issues to be aware of
|
|
31
|
+
|
|
32
|
+
### Check Categories
|
|
33
|
+
|
|
34
|
+
1. **config** - Is the config file valid?
|
|
35
|
+
2. **claudeBinary** - Can we find the claude executable for summarization?
|
|
36
|
+
3. **directories** - Are memory directories writable?
|
|
37
|
+
4. **memoryFiles** - Status of log, summary, remembered items, entities
|
|
38
|
+
5. **errorLog** - Recent errors from the last 24 hours
|
|
39
|
+
6. **sync** - Sync server configuration (if enabled)
|
|
40
|
+
|
|
41
|
+
## How to respond
|
|
42
|
+
|
|
43
|
+
Present the results in a clear, actionable format:
|
|
44
|
+
|
|
45
|
+
### If healthy:
|
|
46
|
+
> **Plugin Status: Healthy** ✓
|
|
47
|
+
>
|
|
48
|
+
> All checks passed. Memory is working correctly.
|
|
49
|
+
>
|
|
50
|
+
> - Log: X entries
|
|
51
|
+
> - Summary: Last updated Y ago
|
|
52
|
+
> - Remembered: Z items
|
|
53
|
+
|
|
54
|
+
### If degraded:
|
|
55
|
+
> **Plugin Status: Degraded** ⚠️
|
|
56
|
+
>
|
|
57
|
+
> The plugin is working but has warnings:
|
|
58
|
+
> - [list warnings]
|
|
59
|
+
>
|
|
60
|
+
> Recent errors:
|
|
61
|
+
> - [list if any]
|
|
62
|
+
|
|
63
|
+
### If unhealthy:
|
|
64
|
+
> **Plugin Status: Unhealthy** ✗
|
|
65
|
+
>
|
|
66
|
+
> Critical issues found:
|
|
67
|
+
> - [list errors with fixes]
|
|
68
|
+
>
|
|
69
|
+
> **How to fix:**
|
|
70
|
+
> 1. [specific fix instructions]
|
|
71
|
+
|
|
72
|
+
## Common issues and fixes
|
|
73
|
+
|
|
74
|
+
| Issue | Fix |
|
|
75
|
+
|-------|-----|
|
|
76
|
+
| Claude binary not found | Remove `claudePath` from config or set correct path |
|
|
77
|
+
| Directories not writable | Check permissions on `~/.claude-mneme/` |
|
|
78
|
+
| Config parse error | Check JSON syntax in `~/.claude-mneme/config.json` |
|
|
79
|
+
| Log needs summarization | Run `/summarize` to compress the log |
|
|
80
|
+
| Recent errors | Check error details and fix underlying issue |
|
|
81
|
+
|
|
82
|
+
## Clearing errors
|
|
83
|
+
|
|
84
|
+
If the user asks to clear the error log:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-status.mjs" --clear-errors
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Confirm: "Error log cleared. Future errors will be logged fresh."
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Force immediate memory summarization
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
The user wants to manually trigger memory summarization. This compresses older log entries into the structured summary, freeing up space in the activity log.
|
|
8
|
+
|
|
9
|
+
## When to use this
|
|
10
|
+
|
|
11
|
+
Summarization normally runs automatically when the log reaches 50 entries. Use `/summarize` to:
|
|
12
|
+
- Force summarization before that threshold
|
|
13
|
+
- Compress the log after a busy session
|
|
14
|
+
- Update the summary with recent work
|
|
15
|
+
|
|
16
|
+
## How to summarize
|
|
17
|
+
|
|
18
|
+
First, check what would be summarized with a dry run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-summarize.mjs" --dry-run
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This shows:
|
|
25
|
+
- Number of log entries
|
|
26
|
+
- How many would be summarized vs kept
|
|
27
|
+
- Whether a summary already exists
|
|
28
|
+
|
|
29
|
+
If the user confirms (or didn't ask for dry-run), run the actual summarization:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/mem-summarize.mjs"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Understanding the output
|
|
36
|
+
|
|
37
|
+
The script outputs JSON with status:
|
|
38
|
+
|
|
39
|
+
- `status: "success"` - Summarization completed
|
|
40
|
+
- `status: "empty"` - No log entries to summarize
|
|
41
|
+
- `status: "skipped"` - Not enough entries (minimum 3 required)
|
|
42
|
+
- `status: "locked"` - Another summarization is in progress
|
|
43
|
+
- `status: "error"` - Something went wrong
|
|
44
|
+
|
|
45
|
+
## What happens during summarization
|
|
46
|
+
|
|
47
|
+
1. Pending log entries are flushed
|
|
48
|
+
2. Entries are deduplicated (related entries grouped)
|
|
49
|
+
3. Older entries are sent to Haiku for analysis
|
|
50
|
+
4. Summary is updated with new decisions, state, and work
|
|
51
|
+
5. Log is trimmed to keep only recent entries (default: 10)
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
These settings in `~/.claude-mneme/config.json` affect summarization:
|
|
56
|
+
|
|
57
|
+
- `keepRecentEntries` (default: 10) - Entries to keep after summarization
|
|
58
|
+
- `model` (default: "haiku") - Model for summarization
|
|
59
|
+
|
|
60
|
+
## Example responses
|
|
61
|
+
|
|
62
|
+
After successful summarization:
|
|
63
|
+
> "Summarized 42 entries into the project memory. Kept the 10 most recent entries in the log. The summary now includes [brief description of what was added]."
|
|
64
|
+
|
|
65
|
+
If nothing to summarize:
|
|
66
|
+
> "The log only has 5 entries, which is below the minimum threshold. Summarization will run automatically when you have more activity, or you can wait until there's more to compress."
|
|
67
|
+
|
|
68
|
+
If already running:
|
|
69
|
+
> "Summarization is already in progress. Please wait a moment and try again."
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Lightweight memory system hooks",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-start.mjs\"",
|
|
10
|
+
"timeout": 30
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"matcher": "compact",
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-compact.mjs\"",
|
|
20
|
+
"timeout": 30
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"UserPromptSubmit": [
|
|
26
|
+
{
|
|
27
|
+
"hooks": [
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/user-prompt-submit.mjs\"",
|
|
31
|
+
"timeout": 5,
|
|
32
|
+
"async": true
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"PostToolUse": [
|
|
38
|
+
{
|
|
39
|
+
"matcher": "TodoWrite",
|
|
40
|
+
"hooks": [
|
|
41
|
+
{
|
|
42
|
+
"type": "command",
|
|
43
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.mjs\"",
|
|
44
|
+
"timeout": 5,
|
|
45
|
+
"async": true
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"matcher": "TaskCreate",
|
|
51
|
+
"hooks": [
|
|
52
|
+
{
|
|
53
|
+
"type": "command",
|
|
54
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.mjs\"",
|
|
55
|
+
"timeout": 5,
|
|
56
|
+
"async": true
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"matcher": "TaskUpdate",
|
|
62
|
+
"hooks": [
|
|
63
|
+
{
|
|
64
|
+
"type": "command",
|
|
65
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.mjs\"",
|
|
66
|
+
"timeout": 5,
|
|
67
|
+
"async": true
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"matcher": "Bash",
|
|
73
|
+
"hooks": [
|
|
74
|
+
{
|
|
75
|
+
"type": "command",
|
|
76
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.mjs\"",
|
|
77
|
+
"timeout": 5,
|
|
78
|
+
"async": true
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"SubagentStop": [
|
|
84
|
+
{
|
|
85
|
+
"hooks": [
|
|
86
|
+
{
|
|
87
|
+
"type": "command",
|
|
88
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/subagent-stop.mjs\"",
|
|
89
|
+
"timeout": 5,
|
|
90
|
+
"async": true
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"Stop": [
|
|
96
|
+
{
|
|
97
|
+
"hooks": [
|
|
98
|
+
{
|
|
99
|
+
"type": "command",
|
|
100
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/stop-capture.mjs\"",
|
|
101
|
+
"timeout": 10
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"type": "command",
|
|
105
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-stop.mjs\"",
|
|
106
|
+
"timeout": 120
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"PreCompact": [
|
|
112
|
+
{
|
|
113
|
+
"hooks": [
|
|
114
|
+
{
|
|
115
|
+
"type": "command",
|
|
116
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-compact.mjs\"",
|
|
117
|
+
"timeout": 120
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Add a memory entry to the project's persistent remembered.json
|
|
4
|
+
* Usage: node mem-add.mjs <type> <content>
|
|
5
|
+
* Types: fact, project, preference, note
|
|
6
|
+
*
|
|
7
|
+
* Unlike log.jsonl entries, remembered items are never summarized away.
|
|
8
|
+
* Users must manually remove entries they no longer need.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import { ensureMemoryDirs, getProjectName, invalidateCache, withFileLock, logError } from './utils.mjs';
|
|
14
|
+
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
const paths = ensureMemoryDirs(cwd);
|
|
17
|
+
const projectName = getProjectName(cwd);
|
|
18
|
+
|
|
19
|
+
const VALID_TYPES = ['fact', 'project', 'preference', 'note', 'lesson'];
|
|
20
|
+
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
const type = args[0] || 'note';
|
|
23
|
+
const content = args.slice(1).join(' ');
|
|
24
|
+
|
|
25
|
+
if (!content) {
|
|
26
|
+
console.error('Usage: node mem-add.mjs <type> <content>');
|
|
27
|
+
console.error(`Types: ${VALID_TYPES.join(', ')}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!VALID_TYPES.includes(type)) {
|
|
32
|
+
console.error(`Invalid type "${type}". Must be one of: ${VALID_TYPES.join(', ')}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Read-modify-write under lock to prevent lost updates from concurrent sessions
|
|
37
|
+
const lockPath = paths.remembered + '.lock';
|
|
38
|
+
withFileLock(lockPath, () => {
|
|
39
|
+
let entries = [];
|
|
40
|
+
if (existsSync(paths.remembered)) {
|
|
41
|
+
try {
|
|
42
|
+
entries = JSON.parse(readFileSync(paths.remembered, 'utf-8'));
|
|
43
|
+
} catch (e) {
|
|
44
|
+
logError(e, 'mem-add:remembered.json');
|
|
45
|
+
entries = [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
entries.push({
|
|
50
|
+
ts: new Date().toISOString(),
|
|
51
|
+
type,
|
|
52
|
+
content
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
writeFileSync(paths.remembered, JSON.stringify(entries, null, 2) + '\n');
|
|
56
|
+
}, 10);
|
|
57
|
+
|
|
58
|
+
invalidateCache(cwd);
|
|
59
|
+
console.log(`Remembered for "${projectName}": [${type}] ${content}`);
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Entity Query Script
|
|
4
|
+
* Queries the entity index to find information about files, functions, errors, packages.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node mem-entity.mjs [options] [query]
|
|
8
|
+
*
|
|
9
|
+
* Options:
|
|
10
|
+
* --list List all entities
|
|
11
|
+
* --category <cat> Filter by category (files, functions, errors, packages)
|
|
12
|
+
* <query> Search for entities matching this name
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { loadEntityIndex, ensureMemoryDirs, getProjectName } from './utils.mjs';
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const projectName = getProjectName(cwd);
|
|
20
|
+
|
|
21
|
+
// Parse arguments
|
|
22
|
+
let listMode = false;
|
|
23
|
+
let category = null;
|
|
24
|
+
let query = null;
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < args.length; i++) {
|
|
27
|
+
const arg = args[i];
|
|
28
|
+
if (arg === '--list') {
|
|
29
|
+
listMode = true;
|
|
30
|
+
} else if (arg === '--category' && args[i + 1]) {
|
|
31
|
+
category = args[++i];
|
|
32
|
+
} else if (!arg.startsWith('--')) {
|
|
33
|
+
query = arg;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Load entity index
|
|
38
|
+
const index = loadEntityIndex(cwd);
|
|
39
|
+
|
|
40
|
+
// Check if index has any data
|
|
41
|
+
const hasData = index.files && (
|
|
42
|
+
Object.keys(index.files).length > 0 ||
|
|
43
|
+
Object.keys(index.functions || {}).length > 0 ||
|
|
44
|
+
Object.keys(index.errors || {}).length > 0 ||
|
|
45
|
+
Object.keys(index.packages || {}).length > 0
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (!hasData) {
|
|
49
|
+
console.log(JSON.stringify({
|
|
50
|
+
project: projectName,
|
|
51
|
+
status: 'empty',
|
|
52
|
+
message: 'No entities indexed yet. Entities are extracted from log entries as you work.'
|
|
53
|
+
}));
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// List mode - show all entities
|
|
58
|
+
if (listMode) {
|
|
59
|
+
const result = {
|
|
60
|
+
project: projectName,
|
|
61
|
+
status: 'ok',
|
|
62
|
+
categories: {}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const categories = category ? [category] : ['files', 'functions', 'errors', 'packages'];
|
|
66
|
+
|
|
67
|
+
for (const cat of categories) {
|
|
68
|
+
if (!index[cat]) continue;
|
|
69
|
+
|
|
70
|
+
const entities = Object.entries(index[cat])
|
|
71
|
+
.map(([name, data]) => ({
|
|
72
|
+
name,
|
|
73
|
+
mentions: data.mentions,
|
|
74
|
+
lastSeen: data.lastSeen
|
|
75
|
+
}))
|
|
76
|
+
.sort((a, b) => b.mentions - a.mentions);
|
|
77
|
+
|
|
78
|
+
if (entities.length > 0) {
|
|
79
|
+
result.categories[cat] = entities;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(JSON.stringify(result, null, 2));
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Query mode - search for matching entities
|
|
88
|
+
if (query) {
|
|
89
|
+
const queryLower = query.toLowerCase();
|
|
90
|
+
const matches = [];
|
|
91
|
+
|
|
92
|
+
const categories = category ? [category] : ['files', 'functions', 'errors', 'packages'];
|
|
93
|
+
|
|
94
|
+
for (const cat of categories) {
|
|
95
|
+
if (!index[cat]) continue;
|
|
96
|
+
|
|
97
|
+
for (const [name, data] of Object.entries(index[cat])) {
|
|
98
|
+
// Match if name contains query (case-insensitive)
|
|
99
|
+
if (name.toLowerCase().includes(queryLower)) {
|
|
100
|
+
matches.push({
|
|
101
|
+
name,
|
|
102
|
+
category: cat,
|
|
103
|
+
mentions: data.mentions,
|
|
104
|
+
lastSeen: data.lastSeen,
|
|
105
|
+
contexts: data.contexts || []
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Sort by relevance: exact match first, then by mentions
|
|
112
|
+
matches.sort((a, b) => {
|
|
113
|
+
const aExact = a.name.toLowerCase() === queryLower ? 1 : 0;
|
|
114
|
+
const bExact = b.name.toLowerCase() === queryLower ? 1 : 0;
|
|
115
|
+
if (aExact !== bExact) return bExact - aExact;
|
|
116
|
+
return b.mentions - a.mentions;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
console.log(JSON.stringify({
|
|
120
|
+
project: projectName,
|
|
121
|
+
query,
|
|
122
|
+
status: matches.length > 0 ? 'ok' : 'not_found',
|
|
123
|
+
matches: matches.slice(0, 20) // Limit to top 20
|
|
124
|
+
}, null, 2));
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// No query and not list mode - show summary
|
|
129
|
+
const summary = {
|
|
130
|
+
project: projectName,
|
|
131
|
+
status: 'ok',
|
|
132
|
+
lastUpdated: index.lastUpdated,
|
|
133
|
+
counts: {
|
|
134
|
+
files: Object.keys(index.files || {}).length,
|
|
135
|
+
functions: Object.keys(index.functions || {}).length,
|
|
136
|
+
errors: Object.keys(index.errors || {}).length,
|
|
137
|
+
packages: Object.keys(index.packages || {}).length
|
|
138
|
+
},
|
|
139
|
+
hint: 'Use --list to see all entities, or provide a query to search.'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
143
|
+
process.exit(0);
|