feat(skills): stacked slash-skill invocations — /skill-a /skill-b do XYZ (#57987)

Inspired by Claude Code v2.1.199 (July 2, 2026): stacked slash-skill
invocations load all leading skills (up to 5), not just the first.

- agent/skill_commands.py: split_stacked_skill_commands() consumes leading
  /skill tokens (stops at the first non-skill token so slash-path arguments
  are never swallowed); build_stacked_skill_invocation_message() composes
  the multi-skill turn reusing the existing bundle scaffolding markers so
  extract_user_instruction_from_skill_message() keeps memory providers
  storing the user's instruction, not N skill bodies.
- cli.py + gateway/run.py: dispatch the stacked path on both surfaces.
- 11 new tests + docs section in skills.md.
This commit is contained in:
Teknium 2026-07-05 02:20:01 -07:00 committed by GitHub
parent 30479961b8
commit 9767e19b60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 363 additions and 7 deletions

34
cli.py
View file

@ -8890,7 +8890,39 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
)
# Check for skill slash commands (/gif-search, /axolotl, etc.)
elif base_cmd in skill_commands:
user_instruction = cmd_original[len(base_cmd):].strip()
rest = cmd_original[len(base_cmd):].strip()
# Stacked slash-skill invocations: `/skill-a /skill-b do XYZ`
# loads every leading skill (up to 5), not just the first.
# Inspired by Claude Code v2.1.199.
from agent.skill_commands import (
build_stacked_skill_invocation_message,
split_stacked_skill_commands,
)
extra_keys, user_instruction = split_stacked_skill_commands(rest)
if extra_keys:
stacked_result = build_stacked_skill_invocation_message(
[base_cmd, *extra_keys],
user_instruction,
task_id=self.session_id,
)
if stacked_result:
msg, loaded_names, missing = stacked_result
print(
f"\n⚡ Loading {len(loaded_names)} stacked skills: "
f"{', '.join(loaded_names)}"
)
if missing:
ChatConsole().print(
f"[yellow]Skipped missing skills: {', '.join(missing)}[/]"
)
if hasattr(self, '_pending_input'):
self._pending_input.put(msg)
else:
ChatConsole().print(
f"[bold red]Failed to load stacked skills for {base_cmd}[/]"
)
return True
user_instruction = rest
msg = build_skill_invocation_message(
base_cmd, user_instruction, task_id=self.session_id
)