1616// Events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PreCompact, SubagentStart, SubagentStop, Stop
1717
1818use super :: { AgentAdapter , HookEntry , HookFormat , McpFormat , McpServerEntry , PluginEntry } ;
19- use std:: path:: PathBuf ;
19+ use std:: path:: { Path , PathBuf } ;
20+
21+ /// Read VS Code agent plugin enablement from state.vscdb.
22+ /// Returns the set of plugin URIs that are disabled (enabled = false).
23+ /// Gracefully returns empty set on any error (DB not found, locked, schema change).
24+ fn read_vscode_disabled_plugins ( vscode_user_dir : & Path ) -> std:: collections:: HashSet < String > {
25+ let db_path = vscode_user_dir
26+ . join ( "globalStorage" )
27+ . join ( "state.vscdb" ) ;
28+ let mut disabled = std:: collections:: HashSet :: new ( ) ;
29+ let Ok ( conn) = rusqlite:: Connection :: open_with_flags (
30+ & db_path,
31+ rusqlite:: OpenFlags :: SQLITE_OPEN_READ_ONLY | rusqlite:: OpenFlags :: SQLITE_OPEN_NO_MUTEX ,
32+ ) else {
33+ return disabled;
34+ } ;
35+ let Ok ( value) : Result < String , _ > = conn. query_row (
36+ "SELECT value FROM ItemTable WHERE key = 'agentPlugins.enablement'" ,
37+ [ ] ,
38+ |row| row. get ( 0 ) ,
39+ ) else {
40+ return disabled;
41+ } ;
42+ // Format: [["file:///path/to/plugin", true/false], ...]
43+ let Ok ( entries) = serde_json:: from_str :: < Vec < ( String , bool ) > > ( & value) else {
44+ return disabled;
45+ } ;
46+ for ( uri, enabled) in entries {
47+ if !enabled {
48+ disabled. insert ( uri) ;
49+ }
50+ }
51+ disabled
52+ }
2053
2154pub struct CopilotAdapter {
2255 home : PathBuf ,
@@ -67,6 +100,9 @@ impl AgentAdapter for CopilotAdapter {
67100 fn detect ( & self ) -> bool {
68101 self . base_dir ( ) . exists ( )
69102 }
103+ fn vscode_user_dir ( & self ) -> Option < PathBuf > {
104+ Some ( self . vscode_user_dir ( ) )
105+ }
70106 fn skill_dirs ( & self ) -> Vec < PathBuf > {
71107 vec ! [
72108 self . base_dir( ) . join( "skills" ) ,
@@ -146,6 +182,7 @@ impl AgentAdapter for CopilotAdapter {
146182 let mut entries = Vec :: new ( ) ;
147183
148184 // 1. Copilot CLI plugins: ~/.copilot/installed-plugins/{marketplace}/{plugin}/
185+ // CLI has no native enable/disable — always enabled if present.
149186 let cli_base = self . base_dir ( ) . join ( "installed-plugins" ) ;
150187 if let Ok ( marketplaces) = std:: fs:: read_dir ( & cli_base) {
151188 for marketplace in marketplaces. flatten ( ) {
@@ -169,6 +206,7 @@ impl AgentAdapter for CopilotAdapter {
169206 source : mp_name. clone ( ) ,
170207 enabled : true ,
171208 path : Some ( plugin. path ( ) ) ,
209+ uri : None ,
172210 installed_at : None ,
173211 updated_at : None ,
174212 } ) ;
@@ -178,8 +216,14 @@ impl AgentAdapter for CopilotAdapter {
178216
179217 // 2. VS Code agent plugins: ~/.vscode/agent-plugins/installed.json
180218 // Each entry has pluginUri pointing to plugins/{name}/ with .github/plugin/plugin.json
219+ // Enable/disable state stored in VS Code's state.vscdb under "agentPlugins.enablement".
181220 let vscode_base = self . home . join ( ".vscode" ) . join ( "agent-plugins" ) ;
182221 let installed_json = vscode_base. join ( "installed.json" ) ;
222+
223+ // Read VS Code agent plugin enablement state from state.vscdb
224+ // Format: [["file:///path/to/plugin", true/false], ...]
225+ let disabled_uris = read_vscode_disabled_plugins ( & self . vscode_user_dir ( ) ) ;
226+
183227 if let Ok ( content) = std:: fs:: read_to_string ( & installed_json)
184228 && let Ok ( registry) = serde_json:: from_str :: < serde_json:: Value > ( & content)
185229 && let Some ( installed) = registry. get ( "installed" ) . and_then ( |v| v. as_array ( ) )
@@ -206,11 +250,13 @@ impl AgentAdapter for CopilotAdapter {
206250 . map ( |n| n. to_string_lossy ( ) . to_string ( ) )
207251 . unwrap_or_else ( || marketplace. to_string ( ) )
208252 } ) ;
253+ let enabled = !disabled_uris. contains ( plugin_uri) ;
209254 entries. push ( PluginEntry {
210255 name,
211256 source : marketplace. to_string ( ) ,
212- enabled : true ,
257+ enabled,
213258 path : Some ( plugin_path) ,
259+ uri : Some ( plugin_uri. to_string ( ) ) ,
214260 installed_at : None ,
215261 updated_at : None ,
216262 } ) ;
0 commit comments