@pcircle/memesh 2.9.3 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.claude-plugin/plugin.json +15 -0
  2. package/.mcp.json +12 -0
  3. package/README.de.md +4 -4
  4. package/README.es.md +4 -4
  5. package/README.fr.md +4 -4
  6. package/README.id.md +4 -4
  7. package/README.ja.md +4 -4
  8. package/README.ko.md +4 -4
  9. package/README.md +87 -13
  10. package/README.th.md +4 -4
  11. package/README.vi.md +4 -4
  12. package/README.zh-CN.md +4 -4
  13. package/README.zh-TW.md +84 -10
  14. package/dist/db/ConnectionPool.d.ts.map +1 -1
  15. package/dist/db/ConnectionPool.js +17 -1
  16. package/dist/db/ConnectionPool.js.map +1 -1
  17. package/dist/db/adapters/BetterSqlite3Adapter.d.ts.map +1 -1
  18. package/dist/db/adapters/BetterSqlite3Adapter.js +22 -0
  19. package/dist/db/adapters/BetterSqlite3Adapter.js.map +1 -1
  20. package/dist/embeddings/EmbeddingService.d.ts.map +1 -1
  21. package/dist/embeddings/EmbeddingService.js +2 -6
  22. package/dist/embeddings/EmbeddingService.js.map +1 -1
  23. package/dist/knowledge-graph/KGSearchEngine.d.ts +1 -0
  24. package/dist/knowledge-graph/KGSearchEngine.d.ts.map +1 -1
  25. package/dist/knowledge-graph/KGSearchEngine.js +4 -4
  26. package/dist/knowledge-graph/KGSearchEngine.js.map +1 -1
  27. package/dist/mcp/ToolDefinitions.d.ts.map +1 -1
  28. package/dist/mcp/ToolDefinitions.js +4 -3
  29. package/dist/mcp/ToolDefinitions.js.map +1 -1
  30. package/dist/mcp/handlers/MemoryToolHandler.d.ts.map +1 -1
  31. package/dist/mcp/handlers/MemoryToolHandler.js +3 -0
  32. package/dist/mcp/handlers/MemoryToolHandler.js.map +1 -1
  33. package/dist/mcp/tools/buddy-do.d.ts.map +1 -1
  34. package/dist/mcp/tools/buddy-do.js +17 -1
  35. package/dist/mcp/tools/buddy-do.js.map +1 -1
  36. package/dist/mcp/tools/buddy-remember.d.ts +1 -1
  37. package/dist/mcp/tools/buddy-remember.d.ts.map +1 -1
  38. package/dist/mcp/tools/buddy-remember.js +58 -16
  39. package/dist/mcp/tools/buddy-remember.js.map +1 -1
  40. package/dist/mcp/tools/create-entities.d.ts +1 -0
  41. package/dist/mcp/tools/create-entities.d.ts.map +1 -1
  42. package/dist/mcp/tools/create-entities.js +155 -0
  43. package/dist/mcp/tools/create-entities.js.map +1 -1
  44. package/dist/memory/UnifiedMemoryStore.d.ts.map +1 -1
  45. package/dist/memory/UnifiedMemoryStore.js +5 -1
  46. package/dist/memory/UnifiedMemoryStore.js.map +1 -1
  47. package/dist/ui/MetricsStore.d.ts.map +1 -1
  48. package/dist/ui/MetricsStore.js +21 -2
  49. package/dist/ui/MetricsStore.js.map +1 -1
  50. package/hooks/hooks.json +66 -0
  51. package/package.json +6 -4
  52. package/plugin.json +7 -3
  53. package/scripts/health-check.js +255 -0
  54. package/scripts/hooks/hook-utils.js +4 -4
  55. package/scripts/hooks/post-tool-use.js +4 -0
  56. package/scripts/hooks/pre-tool-use.js +30 -1
  57. package/scripts/hooks/session-start.js +45 -8
  58. package/scripts/postinstall-lib.js +15 -50
  59. package/scripts/postinstall-new.js +23 -130
  60. package/mcp.json +0 -10
  61. /package/{scripts/skills → skills}/comprehensive-code-review/SKILL.md +0 -0
@@ -0,0 +1,66 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "*",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/session-start.js"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreToolUse": [
15
+ {
16
+ "matcher": "*",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/pre-tool-use.js"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "PostToolUse": [
26
+ {
27
+ "matcher": "*",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/post-tool-use.js"
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "matcher": "*",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/post-commit.js"
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ "Stop": [
46
+ {
47
+ "matcher": "*",
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/stop.js"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "matcher": "*",
57
+ "hooks": [
58
+ {
59
+ "type": "command",
60
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/subagent-stop.js"
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ }
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pcircle/memesh",
3
- "version": "2.9.3",
3
+ "version": "2.10.0",
4
4
  "description": "MeMesh — Persistent memory plugin for Claude Code. Remembers architecture decisions, coding patterns, and project context across sessions.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -9,12 +9,15 @@
9
9
  },
10
10
  "files": [
11
11
  "dist/",
12
+ ".claude-plugin/plugin.json",
13
+ ".mcp.json",
14
+ "hooks/hooks.json",
15
+ "skills/",
12
16
  "scripts/postinstall-new.js",
13
17
  "scripts/postinstall-lib.js",
14
- "scripts/skills/",
18
+ "scripts/health-check.js",
15
19
  "scripts/hooks/",
16
20
  "plugin.json",
17
- "mcp.json",
18
21
  "README.md",
19
22
  "README.zh-TW.md",
20
23
  "LICENSE"
@@ -102,7 +105,6 @@
102
105
  "@types/better-sqlite3": "^7.6.13",
103
106
  "@types/express": "^5.0.6",
104
107
  "@types/node": "^25.0.9",
105
- "@types/react": "^19.2.8",
106
108
  "@types/supertest": "^6.0.3",
107
109
  "@types/uuid": "^11.0.0",
108
110
  "@types/winston": "^2.4.4",
package/plugin.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "memesh",
3
- "description": "MeMesh — Persistent memory plugin for Claude Code. Remembers architecture decisions, coding patterns, and project context across sessions.",
3
+ "description": "MeMesh Plugin — Persistent memory plugin for Claude Code. Remembers architecture decisions, coding patterns, and project context across sessions.",
4
4
  "author": {
5
5
  "name": "PCIRCLE AI"
6
6
  },
7
- "version": "2.9.3",
7
+ "version": "2.10.0",
8
8
  "homepage": "https://github.com/PCIRCLE-AI/claude-code-buddy",
9
9
  "repository": "https://github.com/PCIRCLE-AI/claude-code-buddy",
10
- "license": "MIT"
10
+ "license": "MIT",
11
+ "keywords": ["claude-code", "mcp", "knowledge-graph", "ai-memory", "persistent-memory"],
12
+ "mcpServers": "./.mcp.json",
13
+ "hooks": "./hooks/hooks.json",
14
+ "skills": "./skills/"
11
15
  }
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MeMesh Plugin Health Check
5
+ *
6
+ * Fast, non-invasive validation of plugin installation.
7
+ * Checks all 4 critical paths and reports issues without modifying anything.
8
+ *
9
+ * Exit codes:
10
+ * 0 - All healthy
11
+ * 1 - Repairable issues found
12
+ * 2 - Fatal error (requires manual intervention)
13
+ */
14
+
15
+ import { existsSync, readFileSync, lstatSync, realpathSync } from 'fs';
16
+ import { join, dirname } from 'path';
17
+ import { homedir } from 'os';
18
+ import { fileURLToPath } from 'url';
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+ const projectRoot = join(__dirname, '..');
23
+
24
+ // Parse CLI flags
25
+ const silent = process.argv.includes('--silent');
26
+ const verbose = process.argv.includes('--verbose');
27
+ const json = process.argv.includes('--json');
28
+
29
+ /**
30
+ * Health check result structure
31
+ */
32
+ const result = {
33
+ healthy: true,
34
+ issues: [],
35
+ timestamp: new Date().toISOString(),
36
+ checks: {
37
+ dist: false,
38
+ marketplace: false,
39
+ symlink: false,
40
+ settings: false,
41
+ mcp: false
42
+ }
43
+ };
44
+
45
+ /**
46
+ * Add an issue to the result
47
+ */
48
+ function addIssue(path, severity, message, repairable = true) {
49
+ result.issues.push({ path, severity, message, repairable });
50
+ result.healthy = false;
51
+ if (!silent && !json) {
52
+ const icon = severity === 'error' ? '❌' : '⚠️';
53
+ console.error(` ${icon} ${path}: ${message}`);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Log success message
59
+ */
60
+ function logSuccess(message) {
61
+ if (!silent && !json && verbose) {
62
+ console.log(` ✅ ${message}`);
63
+ }
64
+ }
65
+
66
+ // ============================================================================
67
+ // Check 1: Dist directory exists (required for all other checks)
68
+ // ============================================================================
69
+
70
+ if (!silent && !json) console.log('🔍 Checking MeMesh plugin installation...\n');
71
+
72
+ const distPath = join(projectRoot, '.claude-plugin', 'memesh', 'dist', 'mcp', 'server-bootstrap.js');
73
+
74
+ if (!existsSync(distPath)) {
75
+ addIssue('dist', 'error', 'Plugin not built (.claude-plugin/memesh/dist/ missing)', false);
76
+ result.checks.dist = false;
77
+
78
+ if (!silent && !json) {
79
+ console.error('\n❌ Plugin not built. Run: npm run build\n');
80
+ }
81
+
82
+ if (json) {
83
+ console.log(JSON.stringify(result, null, 2));
84
+ }
85
+
86
+ process.exit(2); // Fatal error
87
+ } else {
88
+ result.checks.dist = true;
89
+ logSuccess('Plugin dist/ exists');
90
+ }
91
+
92
+ // ============================================================================
93
+ // Check 2: Marketplace registration
94
+ // ============================================================================
95
+
96
+ const knownMarketplacesPath = join(homedir(), '.claude', 'plugins', 'known_marketplaces.json');
97
+ const claudePluginRoot = join(projectRoot, '.claude-plugin');
98
+
99
+ try {
100
+ if (!existsSync(knownMarketplacesPath)) {
101
+ addIssue('marketplace', 'error', 'known_marketplaces.json not found');
102
+ } else {
103
+ const content = readFileSync(knownMarketplacesPath, 'utf-8');
104
+ const marketplaces = JSON.parse(content);
105
+
106
+ if (!marketplaces['pcircle-ai']) {
107
+ addIssue('marketplace', 'error', 'pcircle-ai marketplace not registered');
108
+ } else {
109
+ const entry = marketplaces['pcircle-ai'];
110
+ const expectedPath = claudePluginRoot;
111
+
112
+ if (entry.source?.path !== expectedPath) {
113
+ addIssue('marketplace', 'warning', `Marketplace path incorrect (expected: ${expectedPath}, got: ${entry.source?.path})`);
114
+ } else {
115
+ result.checks.marketplace = true;
116
+ logSuccess('Marketplace registered correctly');
117
+ }
118
+ }
119
+ }
120
+ } catch (error) {
121
+ addIssue('marketplace', 'error', `Failed to parse known_marketplaces.json: ${error.message}`);
122
+ }
123
+
124
+ // ============================================================================
125
+ // Check 3: Symlink validity
126
+ // ============================================================================
127
+
128
+ const symlinkPath = join(homedir(), '.claude', 'plugins', 'marketplaces', 'pcircle-ai');
129
+
130
+ try {
131
+ if (!existsSync(symlinkPath)) {
132
+ addIssue('symlink', 'error', 'Marketplace symlink not found');
133
+ } else {
134
+ const stats = lstatSync(symlinkPath);
135
+
136
+ if (!stats.isSymbolicLink()) {
137
+ addIssue('symlink', 'error', 'Marketplace path is not a symlink');
138
+ } else {
139
+ const target = realpathSync(symlinkPath);
140
+ const expectedTarget = realpathSync(claudePluginRoot);
141
+
142
+ if (target !== expectedTarget) {
143
+ addIssue('symlink', 'warning', `Symlink points to wrong location (expected: ${expectedTarget}, got: ${target})`);
144
+ } else if (!existsSync(target)) {
145
+ addIssue('symlink', 'error', 'Symlink target does not exist');
146
+ } else {
147
+ result.checks.symlink = true;
148
+ logSuccess('Symlink valid');
149
+ }
150
+ }
151
+ }
152
+ } catch (error) {
153
+ addIssue('symlink', 'error', `Failed to check symlink: ${error.message}`);
154
+ }
155
+
156
+ // ============================================================================
157
+ // Check 4: Plugin enabled in settings
158
+ // ============================================================================
159
+
160
+ const settingsPath = join(homedir(), '.claude', 'settings.json');
161
+
162
+ try {
163
+ if (!existsSync(settingsPath)) {
164
+ addIssue('settings', 'error', 'settings.json not found');
165
+ } else {
166
+ const content = readFileSync(settingsPath, 'utf-8');
167
+ const settings = JSON.parse(content);
168
+
169
+ if (!settings.enabledPlugins) {
170
+ addIssue('settings', 'error', 'enabledPlugins object missing');
171
+ } else if (!settings.enabledPlugins['memesh@pcircle-ai']) {
172
+ addIssue('settings', 'error', 'memesh@pcircle-ai not enabled');
173
+ } else if (settings.enabledPlugins['memesh@pcircle-ai'] !== true) {
174
+ addIssue('settings', 'warning', 'memesh@pcircle-ai is disabled');
175
+ } else {
176
+ result.checks.settings = true;
177
+ logSuccess('Plugin enabled in settings');
178
+ }
179
+ }
180
+ } catch (error) {
181
+ addIssue('settings', 'error', `Failed to parse settings.json: ${error.message}`);
182
+ }
183
+
184
+ // ============================================================================
185
+ // Check 5: MCP server configuration
186
+ // ============================================================================
187
+
188
+ const mcpSettingsPath = join(homedir(), '.claude', 'mcp_settings.json');
189
+ const expectedMcpPath = distPath;
190
+
191
+ try {
192
+ if (!existsSync(mcpSettingsPath)) {
193
+ addIssue('mcp', 'error', 'mcp_settings.json not found');
194
+ } else {
195
+ const content = readFileSync(mcpSettingsPath, 'utf-8');
196
+ const mcpSettings = JSON.parse(content);
197
+
198
+ if (!mcpSettings.mcpServers) {
199
+ addIssue('mcp', 'error', 'mcpServers object missing');
200
+ } else if (!mcpSettings.mcpServers.memesh) {
201
+ addIssue('mcp', 'error', 'memesh server not configured');
202
+ } else {
203
+ const memeshConfig = mcpSettings.mcpServers.memesh;
204
+
205
+ // Check command
206
+ if (memeshConfig.command !== 'node') {
207
+ addIssue('mcp', 'warning', 'MCP server command should be "node"');
208
+ }
209
+
210
+ // Check args
211
+ if (!Array.isArray(memeshConfig.args) || memeshConfig.args.length === 0) {
212
+ addIssue('mcp', 'error', 'MCP server args missing');
213
+ } else if (memeshConfig.args[0] !== expectedMcpPath) {
214
+ addIssue('mcp', 'warning', `MCP server path incorrect (expected: ${expectedMcpPath}, got: ${memeshConfig.args[0]})`);
215
+ } else {
216
+ result.checks.mcp = true;
217
+ logSuccess('MCP server configured correctly');
218
+ }
219
+ }
220
+ }
221
+ } catch (error) {
222
+ addIssue('mcp', 'error', `Failed to parse mcp_settings.json: ${error.message}`);
223
+ }
224
+
225
+ // ============================================================================
226
+ // Summary
227
+ // ============================================================================
228
+
229
+ if (json) {
230
+ console.log(JSON.stringify(result, null, 2));
231
+ } else if (!silent) {
232
+ console.log('\n' + '═'.repeat(60));
233
+
234
+ if (result.healthy) {
235
+ console.log('✅ All checks passed - plugin installation healthy');
236
+ console.log('═'.repeat(60));
237
+ } else {
238
+ const errorCount = result.issues.filter(i => i.severity === 'error').length;
239
+ const warningCount = result.issues.filter(i => i.severity === 'warning').length;
240
+ const repairableCount = result.issues.filter(i => i.repairable).length;
241
+
242
+ console.log(`❌ Found ${result.issues.length} issue(s): ${errorCount} error(s), ${warningCount} warning(s)`);
243
+ console.log('═'.repeat(60));
244
+
245
+ if (repairableCount > 0) {
246
+ console.log(`\n🔧 ${repairableCount} issue(s) are auto-repairable. Run: npm run health:repair\n`);
247
+ } else {
248
+ console.log('\n⚠️ Issues require manual intervention. Run: npm run build\n');
249
+ }
250
+ }
251
+ }
252
+
253
+ // Exit with appropriate code
254
+ const hasUnrepairableIssues = result.issues.some(i => !i.repairable);
255
+ process.exit(result.healthy ? 0 : (hasUnrepairableIssues ? 2 : 1));
@@ -142,8 +142,8 @@ export function logError(context, error) {
142
142
  try {
143
143
  ensureDir(STATE_DIR);
144
144
  fs.appendFileSync(ERROR_LOG_PATH, logLine);
145
- } catch {
146
- // Silent fail - can't log the logging error
145
+ } catch (logErr) {
146
+ process.stderr.write(`[logError FAILED] ${context}: ${message} (log error: ${logErr.message})\n`);
147
147
  }
148
148
  }
149
149
 
@@ -158,8 +158,8 @@ export function logMemorySave(message) {
158
158
  try {
159
159
  ensureDir(STATE_DIR);
160
160
  fs.appendFileSync(MEMORY_LOG_PATH, logLine);
161
- } catch {
162
- // Silent fail
161
+ } catch (logErr) {
162
+ process.stderr.write(`[logMemorySave FAILED] ${message} (log error: ${logErr.message})\n`);
163
163
  }
164
164
  }
165
165
 
@@ -546,7 +546,11 @@ function trackFileModifications(toolData, currentSession) {
546
546
  currentSession.modifiedFiles = [];
547
547
  }
548
548
 
549
+ const MAX_MODIFIED_FILES = 100;
549
550
  if (!currentSession.modifiedFiles.includes(filePath)) {
551
+ if (currentSession.modifiedFiles.length >= MAX_MODIFIED_FILES) {
552
+ currentSession.modifiedFiles.shift(); // Remove oldest entry
553
+ }
550
554
  currentSession.modifiedFiles.push(filePath);
551
555
  }
552
556
  }
@@ -125,11 +125,21 @@ function mergeResponses(responses) {
125
125
  // Routing Config
126
126
  // ============================================================================
127
127
 
128
+ /** Module-level cache for routing config (30-second TTL) */
129
+ let _routingConfigCache = null;
130
+ let _routingConfigTimestamp = 0;
131
+ const ROUTING_CONFIG_TTL_MS = 30_000;
132
+
128
133
  /**
129
134
  * Load routing config with fallback defaults.
130
135
  * Creates default config if file doesn't exist.
136
+ * Results are cached for 30 seconds to avoid repeated file I/O.
131
137
  */
132
138
  function loadRoutingConfig() {
139
+ const now = Date.now();
140
+ if (_routingConfigCache && (now - _routingConfigTimestamp) < ROUTING_CONFIG_TTL_MS) {
141
+ return _routingConfigCache;
142
+ }
133
143
  const defaults = {
134
144
  version: 1,
135
145
  modelRouting: {
@@ -156,7 +166,10 @@ function loadRoutingConfig() {
156
166
  try {
157
167
  if (fs.existsSync(ROUTING_CONFIG_FILE)) {
158
168
  const config = JSON.parse(fs.readFileSync(ROUTING_CONFIG_FILE, 'utf-8'));
159
- return { ...defaults, ...config };
169
+ const merged = { ...defaults, ...config };
170
+ _routingConfigCache = merged;
171
+ _routingConfigTimestamp = now;
172
+ return merged;
160
173
  }
161
174
  } catch (error) {
162
175
  logError('loadRoutingConfig', error);
@@ -173,6 +186,8 @@ function loadRoutingConfig() {
173
186
  // Non-critical — works with in-memory defaults
174
187
  }
175
188
 
189
+ _routingConfigCache = defaults;
190
+ _routingConfigTimestamp = now;
176
191
  return defaults;
177
192
  }
178
193
 
@@ -185,10 +200,24 @@ function loadRoutingConfig() {
185
200
  * @param {string} entry - Log entry
186
201
  * @param {Object} config - Routing config
187
202
  */
203
+ const AUDIT_LOG_MAX_BYTES = 1_048_576; // 1 MB
204
+ const AUDIT_LOG_KEEP_LINES = 500;
205
+
188
206
  function auditLog(entry, config) {
189
207
  if (!config.auditLog) return;
190
208
 
191
209
  try {
210
+ // Rotate if log exceeds 1 MB: keep only the last 500 lines
211
+ if (fs.existsSync(ROUTING_AUDIT_LOG)) {
212
+ const stat = fs.statSync(ROUTING_AUDIT_LOG);
213
+ if (stat.size > AUDIT_LOG_MAX_BYTES) {
214
+ const content = fs.readFileSync(ROUTING_AUDIT_LOG, 'utf-8');
215
+ const lines = content.split('\n');
216
+ const truncated = lines.slice(-AUDIT_LOG_KEEP_LINES).join('\n');
217
+ fs.writeFileSync(ROUTING_AUDIT_LOG, truncated);
218
+ }
219
+ }
220
+
192
221
  const timestamp = new Date().toISOString();
193
222
  const line = `[${timestamp}] ${entry}\n`;
194
223
  fs.appendFileSync(ROUTING_AUDIT_LOG, line);
@@ -84,8 +84,8 @@ function checkCCBAvailability() {
84
84
  }
85
85
  }
86
86
  }
87
- } catch {
88
- // Ignore parse errors
87
+ } catch (err) {
88
+ logError('checkCCBAvailability:mcp_settings', err);
89
89
  }
90
90
 
91
91
  // Check heartbeat file (MeMesh writes this when running)
@@ -101,8 +101,8 @@ function checkCCBAvailability() {
101
101
  result.running = true;
102
102
  }
103
103
  }
104
- } catch {
105
- // Ignore errors
104
+ } catch (err) {
105
+ logError('checkCCBAvailability:heartbeat', err);
106
106
  }
107
107
 
108
108
  return result;
@@ -256,8 +256,8 @@ function recallFromSQLite() {
256
256
  let parsedMetadata = {};
257
257
  try {
258
258
  parsedMetadata = JSON.parse(row.metadata || '{}');
259
- } catch {
260
- // Ignore parse errors
259
+ } catch (parseErr) {
260
+ logError('recallFromSQLite:metadata-parse', parseErr);
261
261
  }
262
262
 
263
263
  return {
@@ -393,8 +393,8 @@ function reloadClaudeMd() {
393
393
  console.log('');
394
394
  return;
395
395
  }
396
- } catch {
397
- // Skip unreadable files
396
+ } catch (readErr) {
397
+ logError(`reloadClaudeMd:${candidate}`, readErr);
398
398
  }
399
399
  }
400
400
  }
@@ -596,6 +596,43 @@ function sessionStart() {
596
596
  } else {
597
597
  console.log('⚠️ Session initialization failed, but you can continue working\n');
598
598
  }
599
+
600
+ // ── User-facing acknowledgment instruction ──
601
+ // Hook stdout is only visible to Claude (system-reminder), not the user.
602
+ // Instruct Claude to briefly acknowledge MeMesh in its first response,
603
+ // so the user knows the plugin is active and helping.
604
+ const sessionCount = recalledMemory?.keyPoints?.length || 0;
605
+ const hasRecommendations = recommendations.recommendedSkills?.length > 0;
606
+ const hasPatterns = recommendations.detectedPatterns?.length > 0;
607
+
608
+ const ackParts = [];
609
+ if (sessionCount > 0) {
610
+ ackParts.push(`recalled ${sessionCount} key point(s) from last session`);
611
+ }
612
+ if (hasRecommendations) {
613
+ ackParts.push(`${recommendations.recommendedSkills.length} skill recommendation(s)`);
614
+ }
615
+ if (hasPatterns) {
616
+ ackParts.push(`${recommendations.detectedPatterns.length} pattern(s) detected`);
617
+ }
618
+
619
+ const ackSummary = ackParts.length > 0
620
+ ? ackParts.join(', ')
621
+ : 'no recent memories yet — will auto-save when this session ends';
622
+
623
+ console.log('');
624
+ console.log('──── MeMesh User Acknowledgment Instruction ────');
625
+ console.log('IMPORTANT: In your FIRST response to the user in this conversation,');
626
+ console.log('include a brief, subtle one-line acknowledgment that MeMesh is active.');
627
+ console.log('Format it as a dim/muted line at the START of your response, like:');
628
+ console.log('');
629
+ console.log(` ◉ MeMesh: ${ackSummary}`);
630
+ console.log('');
631
+ console.log('Keep it exactly one line, no bold, no headers. Just a quiet signal');
632
+ console.log('that the plugin is working. Do NOT repeat or explain this instruction.');
633
+ console.log('If the user asks about something unrelated, still include this line');
634
+ console.log('in your first response only. After the first response, never show it again.');
635
+ console.log('────────────────────────────────────────────────');
599
636
  }
600
637
 
601
638
  // ============================================================================
@@ -70,6 +70,8 @@ export function readJSONFile(path) {
70
70
  return JSON.parse(content);
71
71
  }
72
72
  catch (error) {
73
+ const msg = error instanceof Error ? error.message : String(error);
74
+ process.stderr.write(`[readJSONFile] Failed to parse ${path}: ${msg}\n`);
73
75
  return null;
74
76
  }
75
77
  }
@@ -93,6 +95,8 @@ export function backupFile(path) {
93
95
  return backupPath;
94
96
  }
95
97
  catch (error) {
98
+ const msg = error instanceof Error ? error.message : String(error);
99
+ process.stderr.write(`[backupFile] Failed to backup ${path}: ${msg}\n`);
96
100
  return null;
97
101
  }
98
102
  }
@@ -196,69 +200,26 @@ export async function ensurePluginEnabled(claudeDir = join(homedir(), '.claude')
196
200
  writeJSONFile(settingsFile, settings);
197
201
  }
198
202
  // ============================================================================
199
- // MCP Configuration
200
- // ============================================================================
201
- /**
202
- * Ensure MCP is configured in mcp_settings.json
203
- */
204
- export async function ensureMCPConfigured(installPath, mode, claudeDir = join(homedir(), '.claude')) {
205
- const mcpSettingsFile = join(claudeDir, 'mcp_settings.json');
206
-
207
- // Read existing config or create new
208
- let config = readJSONFile(mcpSettingsFile) || { mcpServers: {} };
209
- if (!config.mcpServers) {
210
- config.mcpServers = {};
211
- }
212
-
213
- // Configure memesh entry based on mode
214
- if (mode === 'global') {
215
- // Global install: use npx (always uses latest published version)
216
- config.mcpServers.memesh = {
217
- command: 'npx',
218
- args: ['-y', '@pcircle/memesh'],
219
- env: { NODE_ENV: 'production' }
220
- };
221
- } else {
222
- // Local dev: use node + absolute path (for testing)
223
- const serverPath = join(installPath, 'dist', 'mcp', 'server-bootstrap.js');
224
- config.mcpServers.memesh = {
225
- command: 'node',
226
- args: [serverPath]
227
- };
228
- }
229
-
230
- // Remove legacy claude-code-buddy entry if exists
231
- if (config.mcpServers['claude-code-buddy']) {
232
- delete config.mcpServers['claude-code-buddy'];
233
- }
234
-
235
- // Write back
236
- writeJSONFile(mcpSettingsFile, config);
237
- }
238
- // ============================================================================
239
203
  // Backward Compatibility
240
204
  // ============================================================================
241
205
  /**
242
- * Detect and fix legacy installations
206
+ * Detect and fix legacy installations.
207
+ * Only fixes marketplace, symlink, and plugin enablement.
208
+ * MCP and hooks are handled by the plugin system via .mcp.json and hooks/hooks.json.
243
209
  */
244
210
  export async function detectAndFixLegacyInstall(installPath, claudeDir = join(homedir(), '.claude')) {
245
211
  const marketplacesFile = join(claudeDir, 'plugins', 'known_marketplaces.json');
246
- const mcpSettingsFile = join(claudeDir, 'mcp_settings.json');
247
212
  const symlinkPath = join(claudeDir, 'plugins', 'marketplaces', 'pcircle-ai');
248
213
  // Check if marketplace registered
249
214
  const marketplaces = readJSONFile(marketplacesFile);
250
215
  const hasMarketplace = marketplaces && marketplaces['pcircle-ai'];
251
- // Check if MCP configured
252
- const mcpSettings = readJSONFile(mcpSettingsFile);
253
- const hasMCP = mcpSettings && mcpSettings.mcpServers && mcpSettings.mcpServers.memesh;
254
216
  // Check if symlink exists
255
217
  const hasSymlink = existsSync(symlinkPath);
256
218
  // If everything is correct, return ok
257
- if (hasMarketplace && hasMCP && hasSymlink) {
219
+ if (hasMarketplace && hasSymlink) {
258
220
  return 'ok';
259
221
  }
260
222
  // Legacy installation detected - fix it
261
- const mode = detectInstallMode(installPath);
262
223
  // Fix marketplace
263
224
  if (!hasMarketplace) {
264
225
  await ensureMarketplaceRegistered(installPath, claudeDir);
@@ -269,9 +230,13 @@ export async function detectAndFixLegacyInstall(installPath, claudeDir = join(ho
269
230
  }
270
231
  // Fix plugin enablement
271
232
  await ensurePluginEnabled(claudeDir);
272
- // Fix MCP config
273
- if (!hasMCP) {
274
- await ensureMCPConfigured(installPath, mode, claudeDir);
233
+ // Clean up legacy MCP config if it exists (plugin system handles MCP now)
234
+ const mcpSettingsFile = join(claudeDir, 'mcp_settings.json');
235
+ const mcpSettings = readJSONFile(mcpSettingsFile);
236
+ if (mcpSettings?.mcpServers?.memesh || mcpSettings?.mcpServers?.['claude-code-buddy']) {
237
+ delete mcpSettings.mcpServers.memesh;
238
+ delete mcpSettings.mcpServers['claude-code-buddy'];
239
+ writeJSONFile(mcpSettingsFile, mcpSettings);
275
240
  }
276
241
  return 'fixed';
277
242
  }