Ver Fonte

feat(config): add disabled tools and skills

Closes #531
Berserk Agent há 1 mês atrás
pai
commit
b03e98683d

+ 12 - 0
oh-my-opencode-slim.schema.json

@@ -197,6 +197,18 @@
         "type": "string"
       }
     },
+    "disabled_tools": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      }
+    },
+    "disabled_skills": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      }
+    },
     "multiplexer": {
       "type": "object",
       "properties": {

+ 10 - 4
src/agents/index.ts

@@ -210,6 +210,7 @@ function injectDisplayNames(
 function applyDefaultPermissions(
   agent: AgentDefinition,
   configuredSkills?: string[],
+  disabledSkills?: string[],
 ): void {
   const existing = (agent.config.permission ?? {}) as Record<
     string,
@@ -220,6 +221,7 @@ function applyDefaultPermissions(
   const skillPermissions = getSkillPermissionsForAgent(
     agent.name,
     configuredSkills,
+    disabledSkills,
   );
 
   // Respect explicit deny on question (councillor)
@@ -381,7 +383,7 @@ export function createAgents(config?: PluginConfig): AgentDefinition[] {
     if (override) {
       applyOverrides(agent, override);
     }
-    applyDefaultPermissions(agent, override?.skills);
+    applyDefaultPermissions(agent, override?.skills, config?.disabled_skills);
     return agent;
   });
 
@@ -405,12 +407,12 @@ export function createAgents(config?: PluginConfig): AgentDefinition[] {
     if (override) {
       applyOverrides(agent, override);
     }
-    applyDefaultPermissions(agent, override?.skills);
+    applyDefaultPermissions(agent, override?.skills, config?.disabled_skills);
     return agent;
   });
 
   const acpSubAgents = protoAcpAgents.map((agent) => {
-    applyDefaultPermissions(agent);
+    applyDefaultPermissions(agent, undefined, config?.disabled_skills);
     return agent;
   });
 
@@ -433,7 +435,11 @@ export function createAgents(config?: PluginConfig): AgentDefinition[] {
     orchestratorPrompts.appendPrompt,
     disabled,
   );
-  applyDefaultPermissions(orchestrator, orchestratorOverride?.skills);
+  applyDefaultPermissions(
+    orchestrator,
+    orchestratorOverride?.skills,
+    config?.disabled_skills,
+  );
   if (orchestratorOverride) {
     applyOverrides(orchestrator, orchestratorOverride);
   }

+ 13 - 3
src/cli/skills.ts

@@ -35,7 +35,10 @@ export const PERMISSION_ONLY_SKILLS: PermissionOnlySkill[] = [
 export function getSkillPermissionsForAgent(
   agentName: string,
   skillList?: string[],
+  disabledSkillNames?: string[],
 ): Record<string, 'allow' | 'ask' | 'deny'> {
+  const disabledSkills = new Set(disabledSkillNames ?? []);
+
   // Orchestrator gets all skills by default, others are restricted
   const permissions: Record<string, 'allow' | 'ask' | 'deny'> = {
     '*': agentName === 'orchestrator' ? 'allow' : 'deny',
@@ -49,10 +52,13 @@ export function getSkillPermissionsForAgent(
         permissions['*'] = 'allow';
       } else if (name.startsWith('!')) {
         permissions[name.slice(1)] = 'deny';
-      } else {
+      } else if (!disabledSkills.has(name)) {
         permissions[name] = 'allow';
       }
     }
+    for (const name of disabledSkills) {
+      permissions[name] = 'deny';
+    }
     return permissions;
   }
 
@@ -61,7 +67,7 @@ export function getSkillPermissionsForAgent(
     const isAllowed =
       skill.allowedAgents.includes('*') ||
       skill.allowedAgents.includes(agentName);
-    if (isAllowed) {
+    if (isAllowed && !disabledSkills.has(skill.name)) {
       permissions[skill.name] = 'allow';
     }
   }
@@ -71,10 +77,14 @@ export function getSkillPermissionsForAgent(
     const isAllowed =
       skill.allowedAgents.includes('*') ||
       skill.allowedAgents.includes(agentName);
-    if (isAllowed) {
+    if (isAllowed && !disabledSkills.has(skill.name)) {
       permissions[skill.name] = 'allow';
     }
   }
 
+  for (const name of disabledSkills) {
+    permissions[name] = 'deny';
+  }
+
   return permissions;
 }

+ 2 - 0
src/config/schema.ts

@@ -311,6 +311,8 @@ export const PluginConfigSchema = z
           "By default, 'observer' is disabled. Remove it from this list and configure a vision-capable model to enable.",
       ),
     disabled_mcps: z.array(z.string()).optional(),
+    disabled_tools: z.array(z.string()).optional(),
+    disabled_skills: z.array(z.string()).optional(),
     // Multiplexer config (new unified config - preferred)
     multiplexer: MultiplexerConfigSchema.optional(),
     // Legacy tmux config (for backward compatibility)

+ 1 - 0
src/hooks/filter-available-skills/index.ts

@@ -104,6 +104,7 @@ export function createFilterAvailableSkillsHook(
     const permissionRules = getSkillPermissionsForAgent(
       agentName,
       configuredSkills,
+      config.disabled_skills,
     );
     permissionRulesByAgent.set(agentName, permissionRules);
     return permissionRules;

+ 21 - 17
src/index.ts

@@ -1,4 +1,4 @@
-import type { Plugin } from '@opencode-ai/plugin';
+import type { Plugin, ToolDefinition } from '@opencode-ai/plugin';
 import { createAgents, getAgentConfigs, getDisabledAgents } from './agents';
 import { buildOrchestratorPrompt } from './agents/orchestrator';
 import { CompanionManager } from './companion/manager';
@@ -147,10 +147,11 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
   let interviewManager: ReturnType<typeof createInterviewManager>;
   let presetManager: ReturnType<typeof createPresetManager>;
   let companionManager: CompanionManager;
-  let councilTools: Record<string, unknown>;
-  let cancelTaskTools: Record<string, unknown>;
+  let councilTools: ReturnType<typeof createCouncilTool>;
+  let cancelTaskTools: ReturnType<typeof createCancelTaskTool>;
   let acpRunTools: Record<string, ReturnType<typeof createAcpRunTool>>;
   let webfetch: ReturnType<typeof createWebfetchTool>;
+  let tools: Record<string, ToolDefinition>;
   let rewriteDisplayNameMentions: ReturnType<
     typeof createDisplayNameMentionRewriter
   >;
@@ -320,12 +321,22 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
         sessionAgentMap.get(sessionID) === 'orchestrator',
     });
 
-    toolCount =
-      Object.keys(councilTools).length +
-      Object.keys(cancelTaskTools).length +
-      Object.keys(acpRunTools).length +
-      1 + // webfetch
-      2; // ast_grep_search, ast_grep_replace
+    tools = {
+      ...councilTools,
+      ...cancelTaskTools,
+      ...acpRunTools,
+      webfetch,
+      ast_grep_search,
+      ast_grep_replace,
+    };
+    if (config.disabled_tools && config.disabled_tools.length > 0) {
+      const disabledTools = new Set(config.disabled_tools);
+      tools = Object.fromEntries(
+        Object.entries(tools).filter(([name]) => !disabledTools.has(name)),
+      );
+    }
+
+    toolCount = Object.keys(tools).length;
   } catch (err) {
     // Plugin init failed: log visibly before re-throwing so the user
     // sees something actionable instead of a silent "loaded but empty".
@@ -436,14 +447,7 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
 
     agent: agents,
 
-    tool: {
-      ...councilTools,
-      ...cancelTaskTools,
-      ...acpRunTools,
-      webfetch,
-      ast_grep_search,
-      ast_grep_replace,
-    },
+    tool: tools,
 
     mcp: mcps,