feat: add skill rename command and natural language rename detection
- Added 'skill rename <old> <new>' subcommand - Intercept 'rename X to Y' natural language patterns locally - Updates name field inside SKILL.md after rename - Reloads skills immediately after rename
This commit is contained in:
57
main.py
57
main.py
@@ -262,6 +262,18 @@ def ai_handler(msg: IncomingMessage) -> str:
|
||||
if name:
|
||||
return _create_skill_from_description(name, description)
|
||||
|
||||
# Detect natural language skill rename requests
|
||||
_SKILL_RENAME_RE = re.compile(
|
||||
r"rename\s+(?:the\s+)?(?:this\s+)?(?:skill\s+)?(.+?)\s+to\s+(.+)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
rename_match = _SKILL_RENAME_RE.match(text_lower)
|
||||
if rename_match:
|
||||
old_name = rename_match.group(1).strip()
|
||||
new_name = rename_match.group(2).strip().lower().replace(" ", "-")
|
||||
if old_name and new_name:
|
||||
return _handle_skill_command(f"skill rename {old_name} {new_name}")
|
||||
|
||||
# Build context from memory + skills
|
||||
context = _build_context(msg)
|
||||
|
||||
@@ -1112,6 +1124,50 @@ def _handle_skill_command(text: str) -> str:
|
||||
_skills.reload()
|
||||
return f"✅ Skill `{name}` removed."
|
||||
|
||||
elif subcommand == "rename" and len(parts) >= 3:
|
||||
# Parse: skill rename <old> <new> OR skill rename <old> to <new>
|
||||
rename_args = parts[2].strip()
|
||||
rename_parts = re.split(r"\s+to\s+|\s+", rename_args, maxsplit=1)
|
||||
if len(rename_parts) < 2:
|
||||
return "Usage: `skill rename <old_name> <new_name>`"
|
||||
old_name = rename_parts[0].strip()
|
||||
new_name = rename_parts[1].strip().lower().replace(" ", "-")
|
||||
|
||||
cfg = load_config()
|
||||
workspace = os.path.expanduser(cfg.memory.workspace)
|
||||
old_dir = os.path.join(workspace, "skills", old_name)
|
||||
new_dir = os.path.join(workspace, "skills", new_name)
|
||||
|
||||
if not os.path.isdir(old_dir):
|
||||
return f"⚠️ Skill `{old_name}` not found. Run `skill list` to see available skills."
|
||||
if os.path.isdir(new_dir):
|
||||
return f"⚠️ Skill `{new_name}` already exists."
|
||||
|
||||
import shutil
|
||||
shutil.move(old_dir, new_dir)
|
||||
|
||||
# Update the name field inside SKILL.md
|
||||
skill_path = os.path.join(new_dir, "SKILL.md")
|
||||
if os.path.isfile(skill_path):
|
||||
try:
|
||||
with open(skill_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
content = re.sub(
|
||||
r"^(name:\s*).*$",
|
||||
rf"\g<1>{new_name}",
|
||||
content,
|
||||
count=1,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
with open(skill_path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to update name in SKILL.md: {e}")
|
||||
|
||||
if _skills:
|
||||
_skills.reload()
|
||||
return f"✅ Skill renamed: `{old_name}` → `{new_name}`"
|
||||
|
||||
elif subcommand == "reload":
|
||||
if _skills:
|
||||
loaded = _skills.reload()
|
||||
@@ -1124,6 +1180,7 @@ def _handle_skill_command(text: str) -> str:
|
||||
"• `skill list` — List loaded skills\n"
|
||||
"• `skill show <name>` — Show skill content\n"
|
||||
"• `skill create <name>` — Create a new skill template\n"
|
||||
"• `skill rename <old> <new>` — Rename a skill\n"
|
||||
"• `skill remove <name>` — Remove a skill\n"
|
||||
"• `skill reload` — Reload all skills\n"
|
||||
"\nYou can also create skills naturally:\n"
|
||||
|
||||
Reference in New Issue
Block a user