diff --git a/backend/apps/agents/agent_manager.py b/backend/apps/agents/agent_manager.py index 7170e530..69aff20c 100644 --- a/backend/apps/agents/agent_manager.py +++ b/backend/apps/agents/agent_manager.py @@ -1921,6 +1921,20 @@ class AgentManager: "type": "preset", "preset": "claude_code", } + # Suppress Claude Code's bundled plugin skills (/init, /review, + # /security-review, /simplify, /loop, /schedule, /update-config, + # /keybindings-help, /fewer-permission-prompts, /claude-api). + # OpenSwarm has its own skills system that injects skill content + # into the user prompt via _resolve_attached_skills, bypassing the + # SDK's Skill tool entirely — so the bundled skills only cause + # confusion: half mutate ~/.claude state OpenSwarm doesn't use + # (settings.json, keybindings.json), and the rest tell the model + # it can invoke slash commands the backend doesn't intercept. + # Empty list is the SDK's documented "skills off" signal — see + # the `skills` field on ClaudeAgentOptions in claude_agent_sdk + # types.py. User-attached skills are unaffected because they + # don't go through the Skill tool. + options_kwargs["skills"] = [] # exclude_dynamic_sections=True tells the CLI to keep # per-user/per-machine grounding (cwd, git status, recent # commits, OS info) out of the cached system prompt prefix diff --git a/backend/tests/test_agent_manager_unit.py b/backend/tests/test_agent_manager_unit.py index 241a23e8..0c0a702b 100644 --- a/backend/tests/test_agent_manager_unit.py +++ b/backend/tests/test_agent_manager_unit.py @@ -416,6 +416,26 @@ def test_compose_system_prompt_all_none_returns_none(): assert AgentManager()._compose_system_prompt(None, None, None) is None +def test_run_agent_loop_disables_bundled_claude_code_skills(): + """Regression guard for `options_kwargs["skills"] = []` in _run_agent_loop. + + The claude-agent-sdk ships a Skill tool that surfaces Claude Code's + bundled plugin skills (/init, /review, /security-review, /simplify, + /loop, /schedule, /update-config, /keybindings-help, + /fewer-permission-prompts, /claude-api). These are inappropriate in + OpenSwarm — half mutate ~/.claude config the backend doesn't read, and + the rest reference slash commands the backend never intercepts. The + SDK's documented "skills off" signal is `skills=[]` on + ClaudeAgentOptions; user-attached skills bypass the Skill tool and + are unaffected. If this assignment ever gets dropped during a rebase + or refactor the bundled skills come back, so we pin it here. + """ + import inspect + + src = inspect.getsource(AgentManager._run_agent_loop) + assert 'options_kwargs["skills"] = []' in src + + def test_resolve_context_paths_empty_returns_empty(): assert AgentManager()._resolve_context_paths(None) == "" assert AgentManager()._resolve_context_paths([]) == ""