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.
@@ -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."
@@ -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,12 @@
1
+ {
2
+ "name": "claude-mneme",
3
+ "version": "2.9.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "install-deps": "npm install",
7
+ "test": "node --test scripts/utils.test.mjs"
8
+ },
9
+ "dependencies": {
10
+ "@anthropic-ai/claude-agent-sdk": "^0.2.31"
11
+ }
12
+ }
@@ -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);