From 20aaad62b3cd352a3425c1519d7bd27c28120fee Mon Sep 17 00:00:00 2001 From: Abdelrahman Abdallah Date: Mon, 20 Apr 2026 01:09:13 +0200 Subject: [PATCH] Implemented the next missing parity slice around the /skills slash command listing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /skills now enumerates bundled_skills.BUNDLED_SKILLS (matching the npm SkillsMenu component, which lists skills — not slash commands). Previously it mislabeled the slash-command catalog as "Available Skills". Co-Authored-By: Claude Opus 4.7 --- PARITY_CHECKLIST.md | 2 +- src/agent_slash_commands.py | 16 ++++++++++++---- tests/test_agent_runtime.py | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/PARITY_CHECKLIST.md b/PARITY_CHECKLIST.md index 0813056..0e6edb6 100644 --- a/PARITY_CHECKLIST.md +++ b/PARITY_CHECKLIST.md @@ -359,7 +359,7 @@ Missing npm slash commands (from `src/commands/` — 80+ commands total): - [x] `/resume`, `/continue` — Resume a previous conversation - [x] `/rewind`, `/checkpoint` — Restore code/conversation to a previous point - [x] `/sandbox-toggle` — Toggle sandbox mode (alias `/sandbox`) -- [x] `/skills` — List available skills +- [x] `/skills` — List available bundled skills (mirrors `commands/skills/SkillsMenu.tsx`; lists `bundled_skills.BUNDLED_SKILLS`, not slash commands) - [x] `/stats` — Usage statistics and activity - [x] `/stickers` — Order stickers - [x] `/tag` — Toggle a searchable tag on the session diff --git a/src/agent_slash_commands.py b/src/agent_slash_commands.py index 2654b3e..e917076 100644 --- a/src/agent_slash_commands.py +++ b/src/agent_slash_commands.py @@ -1667,11 +1667,19 @@ def _handle_add_dir(agent: 'LocalCodingAgent', args: str, input_text: str) -> Sl def _handle_skills(agent: 'LocalCodingAgent', _args: str, input_text: str) -> SlashCommandResult: - """List available skills.""" + """List bundled skills (mirrors npm `/skills` SkillsMenu listing).""" + from .bundled_skills import get_bundled_skills + lines = ['## Available Skills', ''] - for spec in get_slash_command_specs(): - primary = f'/{spec.names[0]}' - lines.append(f'- `{primary}`: {spec.description}') + for skill in get_bundled_skills(): + if not skill.user_invocable: + continue + header = f'- `{skill.name}`' + if skill.aliases: + header += f' (aliases: {", ".join(skill.aliases)})' + lines.append(f'{header}: {skill.description}') + if skill.when_to_use: + lines.append(f' - When to use: {skill.when_to_use}') lines.extend(['', 'Use the Skill tool to invoke skills programmatically.']) return _local_result(input_text, '\n'.join(lines)) diff --git a/tests/test_agent_runtime.py b/tests/test_agent_runtime.py index e0d953f..ecdbc0d 100644 --- a/tests/test_agent_runtime.py +++ b/tests/test_agent_runtime.py @@ -318,6 +318,25 @@ class AgentRuntimeTests(unittest.TestCase): self.assertEqual(result.tool_calls, 0) self.assertIn('# Permissions', result.final_output) + def test_slash_skills_lists_bundled_skills(self) -> None: + from src.bundled_skills import get_bundled_skills + + with tempfile.TemporaryDirectory() as tmp_dir: + agent = LocalCodingAgent( + model_config=ModelConfig(model='Qwen/Qwen3-Coder-30B-A3B-Instruct'), + runtime_config=AgentRuntimeConfig(cwd=Path(tmp_dir)), + ) + result = agent.run('/skills') + self.assertEqual(result.turns, 0) + self.assertEqual(result.tool_calls, 0) + self.assertIn('Available Skills', result.final_output) + for skill in get_bundled_skills(): + if skill.user_invocable: + self.assertIn(skill.name, result.final_output) + # Slash command names (e.g. 'permissions') must NOT appear — that was + # the old buggy behavior the upstream `/skills` command never did. + self.assertNotIn('/permissions', result.final_output) + def test_clear_runtime_state_drops_session_env_vars(self) -> None: from src.session_env_vars import ( clear_session_env_vars,