fix(cli): resolve duplicate 'skills' subparser crash on Python 3.11+

Fixes #898 — Python 3.11 changed argparse to raise an exception on
duplicate subparser names (CPython #94331). The 'skills' name was
registered twice: once for Skills Hub and once for skills config.

Changes:
- Remove duplicate 'skills' subparser registration
- Add 'config' as a sub-action under the existing 'hermes skills' command
- Route 'hermes skills config' to skills_config module
- Add regression test to catch future duplicates

Migration: 'hermes skills' (config) is now 'hermes skills config'
This commit is contained in:
Bartok Moltbot
2026-03-11 03:33:27 -04:00
committed by teknium1
parent 5abee4fb23
commit ae1c11c5a5
2 changed files with 47 additions and 17 deletions

View File

@@ -2252,8 +2252,8 @@ For more help on a command:
# =========================================================================
skills_parser = subparsers.add_parser(
"skills",
help="Skills Hub — search, install, and manage skills from online registries",
description="Search, install, inspect, audit, and manage skills from GitHub, ClawHub, and other registries."
help="Search, install, configure, and manage skills",
description="Search, install, inspect, audit, configure, and manage skills from GitHub, ClawHub, and other registries."
)
skills_subparsers = skills_parser.add_subparsers(dest="skills_action")
@@ -2307,9 +2307,17 @@ For more help on a command:
tap_rm = tap_subparsers.add_parser("remove", help="Remove a tap")
tap_rm.add_argument("name", help="Tap name to remove")
# config sub-action: interactive enable/disable
skills_subparsers.add_parser("config", help="Interactive skill configuration — enable/disable individual skills")
def cmd_skills(args):
from hermes_cli.skills_hub import skills_command
skills_command(args)
# Route 'config' action to skills_config module
if getattr(args, 'skills_action', None) == 'config':
from hermes_cli.skills_config import skills_command as skills_config_command
skills_config_command(args)
else:
from hermes_cli.skills_hub import skills_command
skills_command(args)
skills_parser.set_defaults(func=cmd_skills)
@@ -2332,19 +2340,6 @@ For more help on a command:
tools_command(args)
tools_parser.set_defaults(func=cmd_tools)
# =========================================================================
# skills command
# =========================================================================
skills_parser = subparsers.add_parser(
"skills",
help="Configure which skills are enabled",
description="Interactive skill configuration — enable/disable individual skills."
)
def cmd_skills(args):
from hermes_cli.skills_config import skills_command
skills_command(args)
skills_parser.set_defaults(func=cmd_skills)
# =========================================================================
# sessions command
# =========================================================================

View File

@@ -0,0 +1,35 @@
"""Test that skills subparser doesn't conflict (regression test for #898)."""
import argparse
def test_no_duplicate_skills_subparser():
"""Ensure 'skills' subparser is only registered once to avoid Python 3.11+ crash.
Python 3.11 changed argparse to raise an exception on duplicate subparser
names instead of silently overwriting (see CPython #94331).
This test will fail with:
argparse.ArgumentError: argument command: conflicting subparser: skills
if the duplicate 'skills' registration is reintroduced.
"""
# Force fresh import of the module where parser is constructed
# If there are duplicate 'skills' subparsers, this import will raise
# argparse.ArgumentError at module load time
import importlib
import sys
# Remove cached module if present
if 'hermes_cli.main' in sys.modules:
del sys.modules['hermes_cli.main']
try:
import hermes_cli.main # noqa: F401
except argparse.ArgumentError as e:
if "conflicting subparser" in str(e):
raise AssertionError(
f"Duplicate subparser detected: {e}. "
"See issue #898 for details."
) from e
raise