diff --git a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py index 833cba4c..3e86a4a1 100644 --- a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py @@ -241,10 +241,16 @@ def _build_command(self) -> list[str]: if self._options.tools is not None: tools = self._options.tools if isinstance(tools, list): - if len(tools) == 0: + # When skills are enabled, ensure "Skill" is in the tools list + # so the skill tool is available for the agent to invoke + tools_list = list(tools) # Copy to avoid mutating the original + if self._options.skills is not None and "Skill" not in tools_list: + tools_list.append("Skill") + + if len(tools_list) == 0: cmd.extend(["--tools", ""]) else: - cmd.extend(["--tools", ",".join(tools)]) + cmd.extend(["--tools", ",".join(tools_list)]) else: # Preset object - 'claude_code' preset maps to 'default' cmd.extend(["--tools", "default"]) diff --git a/tests/test_transport.py b/tests/test_transport.py index 1e61e9ad..def729e9 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -627,6 +627,57 @@ def test_build_command_skills_does_not_duplicate_entries(self): cmd = transport._build_command() assert cmd[cmd.index("--allowedTools") + 1] == "Skill(pdf)" + def test_build_command_skills_all_with_explicit_tools_adds_skill(self): + """When skills='all' is set with explicit tools list, 'Skill' is added to tools.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options( + tools=["WebSearch", "WebFetch", "Read"], + skills="all", + ), + ) + cmd = transport._build_command() + # Skill should be added to the tools list + assert "--tools" in cmd + tools_idx = cmd.index("--tools") + assert cmd[tools_idx + 1] == "WebSearch,WebFetch,Read,Skill" + # Skill should also be in allowed_tools + assert "--allowedTools" in cmd + assert cmd[cmd.index("--allowedTools") + 1] == "Skill" + + def test_build_command_skills_list_with_explicit_tools_adds_skill(self): + """When skills list is set with explicit tools list, 'Skill' is added to tools.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options( + tools=["WebSearch", "WebFetch", "Read"], + skills=["pdf", "docx"], + ), + ) + cmd = transport._build_command() + # Skill should be added to the tools list + assert "--tools" in cmd + tools_idx = cmd.index("--tools") + assert cmd[tools_idx + 1] == "WebSearch,WebFetch,Read,Skill" + # Skill patterns should be in allowed_tools + assert "--allowedTools" in cmd + assert cmd[cmd.index("--allowedTools") + 1] == "Skill(pdf),Skill(docx)" + + def test_build_command_skills_with_explicit_tools_already_has_skill(self): + """When skills='all' is set and 'Skill' already in tools list, it's not duplicated.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options( + tools=["WebSearch", "Skill", "Read"], + skills="all", + ), + ) + cmd = transport._build_command() + # Skill should not be duplicated + assert "--tools" in cmd + tools_idx = cmd.index("--tools") + assert cmd[tools_idx + 1] == "WebSearch,Skill,Read" + @pytest.mark.parametrize( ("skills", "extra", "want_tools", "want_sources", "want_init_skills"), [