@@ -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+
4558export const CONFIG_DIR = path . join ( os . homedir ( ) , '.weave_claude_plugin' ) ;
4659export const SETTINGS_FILE = path . join ( CONFIG_DIR , 'settings.json' ) ;
4760export 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 = / n o t i n s t a l l e d | n o t f o u n d | u n k n o w n p l u g i n | n o i n s t a l l e d p l u g i n / 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 = / n o t f o u n d | u n k n o w n m a r k e t p l a c e | n o c o n f i g u r e d m a r k e t p l a c e / 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+
140223export 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