Summary
`tools/skill_usage.py:is_agent_created(skill_name)` still uses the original naive check: `skill_name not in (bundled ∪ hub_installed)`. Hub lock keys are ASCII slugs (e.g. `"getnote"`); the skill's SKILL.md `name:` can be non-ASCII (e.g. `"Get笔记"`). The comparison misses and the function wrongly reports the hub skill as agent-created.
Blast radius (narrow)
The automatic curator path no longer depends on this function — PRs #19618 and #19621 rewrote `list_agent_created_skill_names()` to require an explicit `created_by: "agent"` provenance marker, and hub-installed skills never get that marker. But `is_agent_created()` is still consulted by the explicit `hermes curator archive/restore` CLI commands (via `hermes_cli/curator.py`), and by `tools/skill_usage._mutate()`. If a user runs `hermes curator archive Get笔记`, the safety gate would say "yes, this is agent-created" and archive the hub skill.
Proposed fix
Widen `is_agent_created()` to check both the SKILL.md `name:` AND the directory name against the off-limits set. If either matches a bundled manifest key or a hub lock key, the skill is off-limits. This mirrors how `list_agent_created_skill_names()` was originally patched in #19297 (closed as subsumed by #19618, but the insight about directory vs. name still applies to the single-skill function).
Credit
Original bug and fix approach: @liuhao1024 in PR #19297. @vinsew's issue #19293 for the repro.
Repro
- Install a Skills Hub skill whose SKILL.md has a non-ASCII `name:`. Example: a skill whose `name:` is `"Get笔记"` but whose hub slug (and directory name) is `"getnote"`.
- `hermes curator archive "Get笔记"` — currently succeeds and moves the hub skill to `.archive/`. Expected: rejected with "skill is bundled or hub-installed; never archive".
Affected sites
- `tools/skill_usage.py:is_agent_created()` — line 210 on current main
- Callers: `tools/skill_usage._mutate()` (line 310), `tools/skill_usage.archive_skill()` (line 406), `tools/skill_usage.restore_skill()` (line 448), `hermes_cli/curator.py` (lines 217, 230)
Summary
`tools/skill_usage.py:is_agent_created(skill_name)` still uses the original naive check: `skill_name not in (bundled ∪ hub_installed)`. Hub lock keys are ASCII slugs (e.g. `"getnote"`); the skill's SKILL.md `name:` can be non-ASCII (e.g. `"Get笔记"`). The comparison misses and the function wrongly reports the hub skill as agent-created.
Blast radius (narrow)
The automatic curator path no longer depends on this function — PRs #19618 and #19621 rewrote `list_agent_created_skill_names()` to require an explicit `created_by: "agent"` provenance marker, and hub-installed skills never get that marker. But `is_agent_created()` is still consulted by the explicit `hermes curator archive/restore` CLI commands (via `hermes_cli/curator.py`), and by `tools/skill_usage._mutate()`. If a user runs `hermes curator archive Get笔记`, the safety gate would say "yes, this is agent-created" and archive the hub skill.
Proposed fix
Widen `is_agent_created()` to check both the SKILL.md `name:` AND the directory name against the off-limits set. If either matches a bundled manifest key or a hub lock key, the skill is off-limits. This mirrors how `list_agent_created_skill_names()` was originally patched in #19297 (closed as subsumed by #19618, but the insight about directory vs. name still applies to the single-skill function).
Credit
Original bug and fix approach: @liuhao1024 in PR #19297. @vinsew's issue #19293 for the repro.
Repro
Affected sites