memtrace 0.3.23 → 0.3.24
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/README.md
CHANGED
|
@@ -238,7 +238,7 @@ Uses **Structural Significance Budgeting** to surface the minimum set of changes
|
|
|
238
238
|
For manual setup:
|
|
239
239
|
|
|
240
240
|
```bash
|
|
241
|
-
claude plugin marketplace add syncable-dev/memtrace
|
|
241
|
+
claude plugin marketplace add https://github.com/syncable-dev/memtrace-public.git
|
|
242
242
|
claude plugin install memtrace-skills@memtrace --scope user
|
|
243
243
|
claude mcp add memtrace -- memtrace mcp -e MEMTRACE_ARCADEDB_BOLT_URL=bolt://localhost:7687
|
|
244
244
|
```
|
|
@@ -23,6 +23,15 @@ export declare function registerMcpInSettingsAt(settingsPath: string, memtraceBi
|
|
|
23
23
|
* Never overwrites a malformed file.
|
|
24
24
|
*/
|
|
25
25
|
export declare function enablePluginInSettingsAt(settingsPath: string): SettingsMutationResult;
|
|
26
|
+
export interface SettingsCleanupResult {
|
|
27
|
+
changed: boolean;
|
|
28
|
+
backupPath?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Remove every Claude settings entry Memtrace may have written across
|
|
32
|
+
* installer versions and Claude marketplace schema changes.
|
|
33
|
+
*/
|
|
34
|
+
export declare function removeClaudeSettingsEntriesAt(settingsPath: string): SettingsCleanupResult;
|
|
26
35
|
/**
|
|
27
36
|
* Full Claude Code plugin installation.
|
|
28
37
|
*
|
|
@@ -5,7 +5,23 @@ import { execCommand, commandExists } from '../utils.js';
|
|
|
5
5
|
import { safeReadJson, writeJsonAtomic } from '../fs-safe.js';
|
|
6
6
|
const PLUGIN_NAME = 'memtrace-skills';
|
|
7
7
|
const MARKETPLACE_NAME = 'memtrace';
|
|
8
|
-
const MARKETPLACE_REPO = 'syncable-dev/memtrace';
|
|
8
|
+
const MARKETPLACE_REPO = 'syncable-dev/memtrace-public';
|
|
9
|
+
const MARKETPLACE_GIT_URL = `https://github.com/${MARKETPLACE_REPO}.git`;
|
|
10
|
+
const PLUGIN_KEY = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
11
|
+
const MARKETPLACE_SETTING_KEYS = [
|
|
12
|
+
MARKETPLACE_NAME,
|
|
13
|
+
'syncable-dev-memtrace',
|
|
14
|
+
'syncable-dev-memtrace-public',
|
|
15
|
+
];
|
|
16
|
+
const MARKETPLACE_SETTING_CONTAINERS = [
|
|
17
|
+
'extraKnownMarketplaces',
|
|
18
|
+
'marketplaces',
|
|
19
|
+
'pluginMarketplaces',
|
|
20
|
+
'knownMarketplaces',
|
|
21
|
+
];
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
9
25
|
/**
|
|
10
26
|
* Try to register the memtrace MCP server via `claude mcp add-json`.
|
|
11
27
|
* Returns true on success, false on timeout/error/missing CLI.
|
|
@@ -87,7 +103,7 @@ async function tryClaudeCliInstall() {
|
|
|
87
103
|
if (!hasClaude)
|
|
88
104
|
return false;
|
|
89
105
|
try {
|
|
90
|
-
await execCommand(`claude plugin marketplace add ${
|
|
106
|
+
await execCommand(`claude plugin marketplace add --scope user ${MARKETPLACE_GIT_URL}`);
|
|
91
107
|
}
|
|
92
108
|
catch {
|
|
93
109
|
// Marketplace may already exist
|
|
@@ -101,8 +117,7 @@ async function tryClaudeCliInstall() {
|
|
|
101
117
|
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
102
118
|
if (fs.existsSync(settingsPath)) {
|
|
103
119
|
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
104
|
-
|
|
105
|
-
if (settings.enabledPlugins?.[key] === true) {
|
|
120
|
+
if (settings.enabledPlugins?.[PLUGIN_KEY] === true) {
|
|
106
121
|
return true;
|
|
107
122
|
}
|
|
108
123
|
}
|
|
@@ -168,10 +183,10 @@ export function enablePluginInSettingsAt(settingsPath) {
|
|
|
168
183
|
}
|
|
169
184
|
const settings = (value ?? {});
|
|
170
185
|
settings.enabledPlugins = settings.enabledPlugins ?? {};
|
|
171
|
-
settings.enabledPlugins[
|
|
186
|
+
settings.enabledPlugins[PLUGIN_KEY] = true;
|
|
172
187
|
settings.extraKnownMarketplaces = settings.extraKnownMarketplaces ?? {};
|
|
173
188
|
settings.extraKnownMarketplaces[MARKETPLACE_NAME] = {
|
|
174
|
-
source: { source: '
|
|
189
|
+
source: { source: 'git', url: MARKETPLACE_GIT_URL },
|
|
175
190
|
};
|
|
176
191
|
writeJsonAtomic(settingsPath, settings);
|
|
177
192
|
return { registered: true };
|
|
@@ -183,6 +198,79 @@ function enablePluginInSettings() {
|
|
|
183
198
|
const settingsFile = path.join(os.homedir(), '.claude', 'settings.json');
|
|
184
199
|
enablePluginInSettingsAt(settingsFile);
|
|
185
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Remove every Claude settings entry Memtrace may have written across
|
|
203
|
+
* installer versions and Claude marketplace schema changes.
|
|
204
|
+
*/
|
|
205
|
+
export function removeClaudeSettingsEntriesAt(settingsPath) {
|
|
206
|
+
const { value, corrupted, backupPath } = safeReadJson(settingsPath);
|
|
207
|
+
if (corrupted) {
|
|
208
|
+
console.warn(`memtrace: ${settingsPath} is malformed; backed up to ${backupPath}. Skipped Claude settings cleanup.`);
|
|
209
|
+
return { changed: false, backupPath };
|
|
210
|
+
}
|
|
211
|
+
if (!value)
|
|
212
|
+
return { changed: false };
|
|
213
|
+
const settings = value;
|
|
214
|
+
let changed = false;
|
|
215
|
+
const enabledPlugins = settings.enabledPlugins;
|
|
216
|
+
if (isRecord(enabledPlugins)) {
|
|
217
|
+
for (const key of Object.keys(enabledPlugins)) {
|
|
218
|
+
if (key === PLUGIN_KEY || (key.startsWith(`${PLUGIN_NAME}@`) && key.includes('memtrace'))) {
|
|
219
|
+
delete enabledPlugins[key];
|
|
220
|
+
changed = true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (Object.keys(enabledPlugins).length === 0)
|
|
224
|
+
delete settings.enabledPlugins;
|
|
225
|
+
}
|
|
226
|
+
const mcpServers = settings.mcpServers;
|
|
227
|
+
if (isRecord(mcpServers) && Object.hasOwn(mcpServers, 'memtrace')) {
|
|
228
|
+
delete mcpServers.memtrace;
|
|
229
|
+
changed = true;
|
|
230
|
+
if (Object.keys(mcpServers).length === 0)
|
|
231
|
+
delete settings.mcpServers;
|
|
232
|
+
}
|
|
233
|
+
for (const containerName of MARKETPLACE_SETTING_CONTAINERS) {
|
|
234
|
+
const container = settings[containerName];
|
|
235
|
+
if (!isRecord(container))
|
|
236
|
+
continue;
|
|
237
|
+
for (const key of MARKETPLACE_SETTING_KEYS) {
|
|
238
|
+
if (Object.hasOwn(container, key)) {
|
|
239
|
+
delete container[key];
|
|
240
|
+
changed = true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (Object.keys(container).length === 0)
|
|
244
|
+
delete settings[containerName];
|
|
245
|
+
}
|
|
246
|
+
if (changed)
|
|
247
|
+
writeJsonAtomic(settingsPath, settings);
|
|
248
|
+
return { changed };
|
|
249
|
+
}
|
|
250
|
+
function removeClaudeMarketplaceCacheDirs() {
|
|
251
|
+
const marketplacesRoot = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces');
|
|
252
|
+
for (const entry of MARKETPLACE_SETTING_KEYS) {
|
|
253
|
+
const dir = path.join(marketplacesRoot, entry);
|
|
254
|
+
if (fs.existsSync(dir)) {
|
|
255
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function removeClaudeInstalledPluginMetadata() {
|
|
260
|
+
const installedPluginsFile = path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
261
|
+
const { value, corrupted } = safeReadJson(installedPluginsFile);
|
|
262
|
+
if (corrupted || !value || !isRecord(value.plugins))
|
|
263
|
+
return;
|
|
264
|
+
let changed = false;
|
|
265
|
+
for (const key of Object.keys(value.plugins)) {
|
|
266
|
+
if (key === PLUGIN_KEY || (key.startsWith(`${PLUGIN_NAME}@`) && key.includes('memtrace'))) {
|
|
267
|
+
delete value.plugins[key];
|
|
268
|
+
changed = true;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (changed)
|
|
272
|
+
writeJsonAtomic(installedPluginsFile, value);
|
|
273
|
+
}
|
|
186
274
|
/**
|
|
187
275
|
* Register the memtrace MCP server in Claude Code's settings.
|
|
188
276
|
* This adds the MCP server config so Claude Code can connect to memtrace tools.
|
|
@@ -204,9 +292,13 @@ async function registerMcpServer(memtraceBinaryPath) {
|
|
|
204
292
|
* 3. Register MCP server for memtrace tools
|
|
205
293
|
*/
|
|
206
294
|
export async function installClaudePlugin(skills, memtraceBinaryPath) {
|
|
207
|
-
// Step 1:
|
|
295
|
+
// Step 1: make sure Claude sees the public HTTPS marketplace source before
|
|
296
|
+
// the CLI attempts to clone/update it.
|
|
297
|
+
enablePluginInSettings();
|
|
298
|
+
removeClaudeMarketplaceCacheDirs();
|
|
299
|
+
// Step 2: Try CLI install
|
|
208
300
|
await tryClaudeCliInstall();
|
|
209
|
-
// Step
|
|
301
|
+
// Step 3: Find or create cache dir
|
|
210
302
|
let cacheDir = findCliInstalledCacheDir();
|
|
211
303
|
if (cacheDir) {
|
|
212
304
|
const orphanedFile = path.join(cacheDir, '.orphaned_at');
|
|
@@ -216,7 +308,7 @@ export async function installClaudePlugin(skills, memtraceBinaryPath) {
|
|
|
216
308
|
else {
|
|
217
309
|
cacheDir = getClaudePluginCacheDir();
|
|
218
310
|
}
|
|
219
|
-
// Step
|
|
311
|
+
// Step 4: Clean up old versions
|
|
220
312
|
const pluginRoot = getPluginCacheRoot();
|
|
221
313
|
if (fs.existsSync(pluginRoot)) {
|
|
222
314
|
const activeDirName = path.basename(cacheDir);
|
|
@@ -226,7 +318,7 @@ export async function installClaudePlugin(skills, memtraceBinaryPath) {
|
|
|
226
318
|
}
|
|
227
319
|
}
|
|
228
320
|
}
|
|
229
|
-
// Step
|
|
321
|
+
// Step 5: Write skills
|
|
230
322
|
const skillsDir = path.join(cacheDir, 'skills');
|
|
231
323
|
if (fs.existsSync(skillsDir)) {
|
|
232
324
|
fs.rmSync(skillsDir, { recursive: true });
|
|
@@ -241,9 +333,9 @@ export async function installClaudePlugin(skills, memtraceBinaryPath) {
|
|
|
241
333
|
}
|
|
242
334
|
writePluginManifest(cacheDir);
|
|
243
335
|
enablePluginInSettings();
|
|
244
|
-
// Step
|
|
336
|
+
// Step 6: Register MCP server
|
|
245
337
|
await registerMcpServer(memtraceBinaryPath);
|
|
246
|
-
// Step
|
|
338
|
+
// Step 7: Write user-level skills for SDK-based integrations
|
|
247
339
|
writeUserLevelSkills(skills);
|
|
248
340
|
return { cacheDir, skillCount: skills.length };
|
|
249
341
|
}
|
|
@@ -281,18 +373,12 @@ export async function uninstallClaudePlugin() {
|
|
|
281
373
|
const settingsFile = path.join(os.homedir(), '.claude', 'settings.json');
|
|
282
374
|
if (fs.existsSync(settingsFile)) {
|
|
283
375
|
try {
|
|
284
|
-
|
|
285
|
-
const pluginKey = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
286
|
-
if (settings.enabledPlugins && typeof settings.enabledPlugins === 'object') {
|
|
287
|
-
delete settings.enabledPlugins[pluginKey];
|
|
288
|
-
}
|
|
289
|
-
if (settings.mcpServers && typeof settings.mcpServers === 'object') {
|
|
290
|
-
delete settings.mcpServers['memtrace'];
|
|
291
|
-
}
|
|
292
|
-
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
|
|
376
|
+
removeClaudeSettingsEntriesAt(settingsFile);
|
|
293
377
|
}
|
|
294
378
|
catch { /* */ }
|
|
295
379
|
}
|
|
380
|
+
removeClaudeMarketplaceCacheDirs();
|
|
381
|
+
removeClaudeInstalledPluginMetadata();
|
|
296
382
|
// Clean up user-level skills
|
|
297
383
|
const userSkillsDir = path.join(os.homedir(), '.claude', 'skills');
|
|
298
384
|
if (fs.existsSync(userSkillsDir)) {
|
|
@@ -16,6 +16,10 @@ Memtrace is the memory layer of the codebase. It has the full knowledge graph: e
|
|
|
16
16
|
|
|
17
17
|
**97% better accuracy. 83% fewer wasted tokens. No exceptions.**
|
|
18
18
|
|
|
19
|
+
## Value Tracking
|
|
20
|
+
|
|
21
|
+
Do not print usage receipts in normal answers. Memtrace records tool usage, graph facts, file references, and estimated context avoided internally. Users can inspect that in the local UI's Value panel.
|
|
22
|
+
|
|
19
23
|
## Parameter Types — Read This Before Calling Any Tool
|
|
20
24
|
|
|
21
25
|
All memtrace MCP tools are **strictly typed**. Pass JSON numbers (not strings) for integer parameters.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memtrace",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"description": "Code intelligence graph — MCP server + AI agent skills + visualization UI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"fs-extra": "^11.0.0"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
|
-
"@memtrace/darwin-arm64": "0.3.
|
|
40
|
-
"@memtrace/linux-x64": "0.3.
|
|
41
|
-
"@memtrace/win32-x64": "0.3.
|
|
39
|
+
"@memtrace/darwin-arm64": "0.3.24",
|
|
40
|
+
"@memtrace/linux-x64": "0.3.24",
|
|
41
|
+
"@memtrace/win32-x64": "0.3.24"
|
|
42
42
|
},
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=18"
|
|
@@ -18,6 +18,10 @@ Memtrace is the memory layer of the codebase. It has the full knowledge graph: e
|
|
|
18
18
|
|
|
19
19
|
**97% better accuracy. 83% fewer wasted tokens. No exceptions for what's in the graph.**
|
|
20
20
|
|
|
21
|
+
## Value Tracking
|
|
22
|
+
|
|
23
|
+
Do not print usage receipts in normal answers. Memtrace records tool usage, graph facts, file references, and estimated context avoided internally. Users can inspect that in the local UI's Value panel.
|
|
24
|
+
|
|
21
25
|
## What Memtrace actually indexes
|
|
22
26
|
|
|
23
27
|
Memtrace's hybrid search = **BM25 over symbol metadata** (name, signature, file_path, kind) **+ semantic vector search over embedded code bodies** (first ~1500 chars of every Function / Method / Class / Struct / Interface body), fused via Reciprocal Rank Fusion.
|
package/uninstall.js
CHANGED
|
@@ -11,6 +11,18 @@ const { execSync, spawnSync } = require("child_process");
|
|
|
11
11
|
const PLUGIN_NAME = "memtrace-skills";
|
|
12
12
|
const MARKETPLACE_NAME = "memtrace";
|
|
13
13
|
const MCP_SERVER_NAME = "memtrace";
|
|
14
|
+
const PLUGIN_KEY = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
15
|
+
const MARKETPLACE_SETTING_KEYS = [
|
|
16
|
+
MARKETPLACE_NAME,
|
|
17
|
+
"syncable-dev-memtrace",
|
|
18
|
+
"syncable-dev-memtrace-public",
|
|
19
|
+
];
|
|
20
|
+
const MARKETPLACE_SETTING_CONTAINERS = [
|
|
21
|
+
"extraKnownMarketplaces",
|
|
22
|
+
"marketplaces",
|
|
23
|
+
"pluginMarketplaces",
|
|
24
|
+
"knownMarketplaces",
|
|
25
|
+
];
|
|
14
26
|
|
|
15
27
|
const SKILL_NAMES = [
|
|
16
28
|
// Commands
|
|
@@ -71,13 +83,17 @@ function writeSettings(settings) {
|
|
|
71
83
|
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
|
|
72
84
|
}
|
|
73
85
|
|
|
86
|
+
function isRecord(value) {
|
|
87
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
// ── Remove MCP server ───────────────────────────────────────────────────────
|
|
75
91
|
|
|
76
92
|
function removeMcpServer() {
|
|
77
93
|
const settings = readSettings();
|
|
78
94
|
let changed = false;
|
|
79
95
|
|
|
80
|
-
if (settings.mcpServers && settings.mcpServers[MCP_SERVER_NAME]) {
|
|
96
|
+
if (isRecord(settings.mcpServers) && settings.mcpServers[MCP_SERVER_NAME]) {
|
|
81
97
|
delete settings.mcpServers[MCP_SERVER_NAME];
|
|
82
98
|
if (Object.keys(settings.mcpServers).length === 0) {
|
|
83
99
|
delete settings.mcpServers;
|
|
@@ -94,29 +110,82 @@ function removeMcpServer() {
|
|
|
94
110
|
function removePlugin() {
|
|
95
111
|
const settings = readSettings();
|
|
96
112
|
let changed = false;
|
|
97
|
-
const pluginKey = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
98
113
|
|
|
99
|
-
if (settings.enabledPlugins
|
|
100
|
-
|
|
114
|
+
if (isRecord(settings.enabledPlugins)) {
|
|
115
|
+
for (const key of Object.keys(settings.enabledPlugins)) {
|
|
116
|
+
if (key === PLUGIN_KEY || (key.startsWith(`${PLUGIN_NAME}@`) && key.includes("memtrace"))) {
|
|
117
|
+
delete settings.enabledPlugins[key];
|
|
118
|
+
changed = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
101
121
|
if (Object.keys(settings.enabledPlugins).length === 0) {
|
|
102
122
|
delete settings.enabledPlugins;
|
|
103
123
|
}
|
|
104
|
-
changed
|
|
105
|
-
console.log(`memtrace: removed plugin ${pluginKey}`);
|
|
124
|
+
if (changed) console.log(`memtrace: removed plugin ${PLUGIN_KEY}`);
|
|
106
125
|
}
|
|
107
126
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
let removedMarketplace = false;
|
|
128
|
+
for (const containerName of MARKETPLACE_SETTING_CONTAINERS) {
|
|
129
|
+
const container = settings[containerName];
|
|
130
|
+
if (!isRecord(container)) continue;
|
|
131
|
+
for (const key of MARKETPLACE_SETTING_KEYS) {
|
|
132
|
+
if (Object.prototype.hasOwnProperty.call(container, key)) {
|
|
133
|
+
delete container[key];
|
|
134
|
+
changed = true;
|
|
135
|
+
removedMarketplace = true;
|
|
136
|
+
}
|
|
112
137
|
}
|
|
113
|
-
|
|
138
|
+
if (Object.keys(container).length === 0) {
|
|
139
|
+
delete settings[containerName];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (removedMarketplace) {
|
|
114
144
|
console.log(`memtrace: removed marketplace ${MARKETPLACE_NAME}`);
|
|
115
145
|
}
|
|
116
146
|
|
|
117
147
|
if (changed) writeSettings(settings);
|
|
118
148
|
}
|
|
119
149
|
|
|
150
|
+
function removeMarketplaceCaches() {
|
|
151
|
+
const marketplacesRoot = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
152
|
+
for (const entry of MARKETPLACE_SETTING_KEYS) {
|
|
153
|
+
const dir = path.join(marketplacesRoot, entry);
|
|
154
|
+
if (fs.existsSync(dir)) {
|
|
155
|
+
try {
|
|
156
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
157
|
+
console.log(`memtrace: removed marketplace cache ${entry}`);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.warn(`memtrace: failed to remove marketplace cache ${entry}: ${e.message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function removeInstalledPluginMetadata() {
|
|
166
|
+
const installedPluginsFile = path.join(os.homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
167
|
+
if (!fs.existsSync(installedPluginsFile)) return;
|
|
168
|
+
let data;
|
|
169
|
+
try {
|
|
170
|
+
data = JSON.parse(fs.readFileSync(installedPluginsFile, "utf-8"));
|
|
171
|
+
} catch {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (!isRecord(data.plugins)) return;
|
|
175
|
+
|
|
176
|
+
let changed = false;
|
|
177
|
+
for (const key of Object.keys(data.plugins)) {
|
|
178
|
+
if (key === PLUGIN_KEY || (key.startsWith(`${PLUGIN_NAME}@`) && key.includes("memtrace"))) {
|
|
179
|
+
delete data.plugins[key];
|
|
180
|
+
changed = true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (changed) {
|
|
184
|
+
fs.writeFileSync(installedPluginsFile, JSON.stringify(data, null, 2) + "\n");
|
|
185
|
+
console.log("memtrace: removed plugin metadata");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
120
189
|
// ── Try Claude CLI uninstall ────────────────────────────────────────────────
|
|
121
190
|
|
|
122
191
|
function tryClaudeCliUninstall() {
|
|
@@ -140,11 +209,15 @@ function tryClaudeCliUninstall() {
|
|
|
140
209
|
|
|
141
210
|
// ── Main uninstall logic ───────────────────────────────────────────────────
|
|
142
211
|
|
|
143
|
-
function legacyCleanup() {
|
|
212
|
+
function legacyCleanup(options = {}) {
|
|
144
213
|
try { removeSkills(); } catch (e) { console.warn(`memtrace: skill removal failed: ${e.message}`); }
|
|
145
|
-
|
|
214
|
+
if (!options.skipClaudeCli) {
|
|
215
|
+
try { tryClaudeCliUninstall(); } catch { /* silent */ }
|
|
216
|
+
}
|
|
146
217
|
try { removePlugin(); } catch (e) { console.warn(`memtrace: plugin removal failed: ${e.message}`); }
|
|
147
218
|
try { removeMcpServer(); } catch (e) { console.warn(`memtrace: MCP server removal failed: ${e.message}`); }
|
|
219
|
+
try { removeMarketplaceCaches(); } catch (e) { console.warn(`memtrace: marketplace cache cleanup failed: ${e.message}`); }
|
|
220
|
+
try { removeInstalledPluginMetadata(); } catch (e) { console.warn(`memtrace: installed plugin metadata cleanup failed: ${e.message}`); }
|
|
148
221
|
}
|
|
149
222
|
|
|
150
223
|
function run() {
|
|
@@ -159,6 +232,9 @@ function run() {
|
|
|
159
232
|
if (result.status !== 0) {
|
|
160
233
|
console.warn(`memtrace: installer uninstall exited ${result.status}; falling back to legacy cleanup`);
|
|
161
234
|
legacyCleanup();
|
|
235
|
+
} else {
|
|
236
|
+
// Older bundled uninstallers did not remove every marketplace shape.
|
|
237
|
+
legacyCleanup({ skipClaudeCli: true });
|
|
162
238
|
}
|
|
163
239
|
} else {
|
|
164
240
|
// No bundled installer — use legacy JS-only cleanup (Claude-only)
|