Skip to content

Commit 9127095

Browse files
committed
During uninstalltaion step, also remove the CC plugin and marketplace
1 parent 030beb3 commit 9127095

2 files changed

Lines changed: 119 additions & 18 deletions

File tree

src/cli.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import {
1515
VERSION,
1616
MarketplaceStatus,
1717
PluginStatus,
18+
RemovalStatus,
1819
createConfig,
1920
registerPlugin,
21+
unregisterPlugin,
2022
loadSettings,
2123
saveSettings,
2224
type Settings,
@@ -391,30 +393,49 @@ async function cmdUninstall(keepLogs: boolean): Promise<void> {
391393
return;
392394
}
393395

396+
let settings: Settings | null = null;
397+
394398
if (fs.existsSync(SETTINGS_FILE)) {
395-
let settings: Settings | null = null;
396399
try {
397400
settings = loadSettings();
398401
} catch {
399402
// Settings may be corrupt — continue with cleanup
400403
}
404+
}
401405

402-
const socketPath = settings?.daemon_socket ?? path.join(CONFIG_DIR, 'daemon.sock');
406+
const socketPath = settings?.daemon_socket ?? path.join(CONFIG_DIR, 'daemon.sock');
407+
if (fs.existsSync(socketPath)) {
408+
try {
409+
await sendToSocket(socketPath, JSON.stringify({ command: 'shutdown' }));
410+
console.log('✓ Stopped daemon');
411+
} catch {
412+
console.warn('⚠ Could not stop daemon (may already be stopped)');
413+
}
414+
}
403415

404-
if (fs.existsSync(socketPath)) {
405-
try {
406-
await sendToSocket(socketPath, JSON.stringify({ command: 'shutdown' }));
407-
console.log('✓ Stopped daemon');
408-
} catch {
409-
console.warn('⚠ Could not stop daemon (may already be stopped)');
410-
}
411-
try {
412-
fs.unlinkSync(socketPath);
413-
} catch {
414-
// May have been removed by daemon itself
415-
}
416+
if (fs.existsSync(socketPath)) {
417+
try {
418+
fs.unlinkSync(socketPath);
419+
console.log('✓ Removed daemon socket');
420+
} catch {
421+
console.warn('⚠ Could not remove daemon socket');
416422
}
423+
}
424+
425+
const pluginResult = unregisterPlugin();
426+
if (pluginResult.pluginStatus === RemovalStatus.Failed) {
427+
console.warn(`⚠ ${pluginResult.pluginError}`);
428+
} else {
429+
console.log(`✓ Claude plugin ${pluginResult.pluginStatus === RemovalStatus.AlreadyAbsent ? 'already removed' : 'removed'}`);
430+
}
417431

432+
if (pluginResult.marketplaceStatus === RemovalStatus.Failed) {
433+
console.warn(`⚠ ${pluginResult.marketplaceError}`);
434+
} else {
435+
console.log(`✓ Claude marketplace ${pluginResult.marketplaceStatus === RemovalStatus.AlreadyAbsent ? 'already removed' : 'removed'}`);
436+
}
437+
438+
if (fs.existsSync(SETTINGS_FILE)) {
418439
fs.unlinkSync(SETTINGS_FILE);
419440
console.log('✓ Removed configuration');
420441
} else {
@@ -437,10 +458,7 @@ async function cmdUninstall(keepLogs: boolean): Promise<void> {
437458
// Non-empty (e.g., logs kept) — leave it
438459
}
439460

440-
console.log(`\n✓ Uninstall complete!`);
441-
console.log(`\nNote: The Claude Code plugin is still installed.`);
442-
console.log(`To remove it, run in Claude Code:`);
443-
console.log(` /plugin uninstall ${PLUGIN_NAME}@${MARKETPLACE_NAME}`);
461+
console.log('\n✓ Uninstall complete!');
444462
}
445463

446464
// ---------------------------------------------------------------------------

src/setup.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ export interface PluginResult {
4242
pluginStatus: PluginStatus;
4343
}
4444

45+
export enum RemovalStatus {
46+
Removed = 'removed',
47+
AlreadyAbsent = 'already_absent',
48+
Failed = 'failed',
49+
}
50+
51+
export interface UninstallResult {
52+
marketplaceStatus: RemovalStatus;
53+
pluginStatus: RemovalStatus;
54+
marketplaceError?: string;
55+
pluginError?: string;
56+
}
57+
4558
export const CONFIG_DIR = path.join(os.homedir(), '.weave_claude_plugin');
4659
export const SETTINGS_FILE = path.join(CONFIG_DIR, 'settings.json');
4760
export const VERSION = '0.1.0';
@@ -137,6 +150,76 @@ export function registerPlugin(logFile: string): PluginResult {
137150
};
138151
}
139152

153+
/**
154+
* Uninstall the plugin from Claude Code and remove its marketplace.
155+
*
156+
* Requires the `claude` CLI to be in PATH. Idempotent — already-removed
157+
* plugins or marketplaces are treated as success.
158+
*/
159+
export function unregisterPlugin(): UninstallResult {
160+
const claudePath = findClaudeCLI();
161+
if (!claudePath) {
162+
const msg = "'claude' CLI not found in PATH. Skipping Claude plugin and marketplace removal.";
163+
return {
164+
pluginStatus: RemovalStatus.Failed,
165+
marketplaceStatus: RemovalStatus.Failed,
166+
pluginError: msg,
167+
marketplaceError: msg,
168+
};
169+
}
170+
171+
let pluginStatus: RemovalStatus = RemovalStatus.Removed;
172+
let pluginError: string | undefined;
173+
174+
const pluginResult = spawnSync(
175+
claudePath,
176+
['plugin', 'uninstall', `${PLUGIN_NAME}@${MARKETPLACE_NAME}`, '--scope', 'user'],
177+
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] },
178+
);
179+
const pluginAlreadyAbsent = /not installed|not found|unknown plugin|no installed plugin/i
180+
.test((pluginResult.stderr ?? '') + (pluginResult.stdout ?? ''));
181+
if (pluginResult.status !== 0) {
182+
if (pluginAlreadyAbsent) {
183+
pluginStatus = RemovalStatus.AlreadyAbsent;
184+
} else {
185+
const output = ((pluginResult.stderr ?? '') + (pluginResult.stdout ?? '')).trim();
186+
pluginStatus = RemovalStatus.Failed;
187+
pluginError = `Failed to uninstall plugin '${PLUGIN_NAME}': ${output}`;
188+
}
189+
} else if (pluginAlreadyAbsent) {
190+
pluginStatus = RemovalStatus.AlreadyAbsent;
191+
}
192+
193+
let marketplaceStatus: RemovalStatus = RemovalStatus.Removed;
194+
let marketplaceError: string | undefined;
195+
196+
const mktResult = spawnSync(
197+
claudePath,
198+
['plugin', 'marketplace', 'remove', MARKETPLACE_NAME],
199+
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] },
200+
);
201+
const marketplaceAlreadyAbsent = /not found|unknown marketplace|no configured marketplace/i
202+
.test((mktResult.stderr ?? '') + (mktResult.stdout ?? ''));
203+
if (mktResult.status !== 0) {
204+
if (marketplaceAlreadyAbsent) {
205+
marketplaceStatus = RemovalStatus.AlreadyAbsent;
206+
} else {
207+
const output = ((mktResult.stderr ?? '') + (mktResult.stdout ?? '')).trim();
208+
marketplaceStatus = RemovalStatus.Failed;
209+
marketplaceError = `Failed to remove marketplace '${MARKETPLACE_NAME}': ${output}`;
210+
}
211+
} else if (marketplaceAlreadyAbsent) {
212+
marketplaceStatus = RemovalStatus.AlreadyAbsent;
213+
}
214+
215+
return {
216+
pluginStatus,
217+
marketplaceStatus,
218+
pluginError,
219+
marketplaceError,
220+
};
221+
}
222+
140223
export function loadSettings(): Settings {
141224
if (!fs.existsSync(SETTINGS_FILE)) {
142225
throw new Error(`Settings not found at ${SETTINGS_FILE}\nRun: weave-claude-plugin install`);

0 commit comments

Comments
 (0)