hermes - 💡(How to fix) Fix [Feature]: Global, self-improving skills have no first-class home — and protecting them forces a DRY violation

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

  • You must curator pin it once per profile, because each profile reads its own .usage.json.
  • Add a profile → the skill arrives unpinned, silently re-exposed to the agent's delete and to curator archival.
  • You can't cleanly factor this out. The config bits (external_dirs, curator.enabled) could in principle be shared via a base config.yaml, but the pin is runtime state in a file that's continuously rewritten by usage counters — symlinking it across profiles is unsafe (mixed telemetry, concurrent writes).
  • There is no declarative pin in the SKILL.md frontmatter — nothing the single shared copy can carry to assert "I'm protected."

Fix Action

Fix / Workaround

  • Creation always writes locally — a new agent-authored skill goes to ~/.hermes/skills/.

  • Updates happen in place — patch, edit, write_file, remove_file, and delete operate on the skill where it is found, including under external_dirs.

  • hermes curator pin <skill> exempts a skill from the curator's auto-transitions (active → stale → archived) and makes skill_manage(action="delete") refuse it. Patches/edits still go through, so a pinned skill can still be improved. That part is well designed.

  • Pin applies to "agent-created" skills, and provenance is binary: anything not in .bundled_manifest or .hub/lock.json is "agent-created" — which includes your hand-authored skills and your external-dir skills. So external/global skills are pinnable, and they are also subject to curator archival by default (unused skills get moved into the local .archive/ after 90 days — which, for an external skill, means yanking it out of the shared vault into one profile's local archive).

RAW_BUFFERClick to expand / collapse

Problem or Use Case

TL;DR Hermes ships two first-class features — profiles and self-improving skills — but the composition of the two has no first-class support. You can share the skill files across profiles via external_dirs, but the protection state that keeps a shared skill alive (the curator pin) is stored per profile, in each profile's local ~/.hermes/skills/.usage.json. So to protect one logically-global skill, you have to pin it again in every profile, and any new profile starts unpinned by default. That is a forced DRY violation for a need that is not an edge case, and I think it points to a scoping mistake in where pin state lives — not just a missing feature.

  • The setup

~/.hermes/skills/ is the single source of truth, and it is per profile — each profile has its own HERMES_HOME and its own state. Bundled, hub-installed, and agent-created skills all land there. To make a skill available beyond one profile/install, the only mechanism is skills.external_dirs in config.yaml — additional folders (e.g. a shared ~/.agents/skills/ or a versioned repo) that Hermes scans alongside the local store. There is no first-class "global" tier: external dirs are a read-overlay, and on a name collision the local copy wins (it silently shadows the external one).

  • How far does self-improvement actually reach into shared skills?

Further than you might expect, which is the good news:

  • Creation always writes locally — a new agent-authored skill goes to ~/.hermes/skills/.
  • Updates happen in place — patch, edit, write_file, remove_file, and delete operate on the skill where it is found, including under external_dirs.

So a shared skill can keep improving in its external home. The catch: an external dir is explicitly not a write-protection boundary. If the directory is writable by the Hermes process, the agent can modify or delete files there. The only hard guarantee is filesystem permissions (make it read-only) — and that also kills the in-place self-improvement. It's one or the other. How protection works today The intended answer to "don't let the agent throw away a skill I rely on" is the curator pin:

  • hermes curator pin <skill> exempts a skill from the curator's auto-transitions (active → stale → archived) and makes skill_manage(action="delete") refuse it. Patches/edits still go through, so a pinned skill can still be improved. That part is well designed.
  • Pin applies to "agent-created" skills, and provenance is binary: anything not in .bundled_manifest or .hub/lock.json is "agent-created" — which includes your hand-authored skills and your external-dir skills. So external/global skills are pinnable, and they are also subject to curator archival by default (unused skills get moved into the local .archive/ after 90 days — which, for an external skill, means yanking it out of the shared vault into one profile's local archive).

The problem is where the pin lives: it's stored as "pinned": true on the skill's entry in ~/.hermes/skills/.usage.json — a per-profile sidecar.

  • The DRY break

One shared skill has exactly one physical copy (in the external dir). But its protection is scattered:

  • You must curator pin it once per profile, because each profile reads its own .usage.json.
  • Add a profile → the skill arrives unpinned, silently re-exposed to the agent's delete and to curator archival.
  • You can't cleanly factor this out. The config bits (external_dirs, curator.enabled) could in principle be shared via a base config.yaml, but the pin is runtime state in a file that's continuously rewritten by usage counters — symlinking it across profiles is unsafe (mixed telemetry, concurrent writes).
  • There is no declarative pin in the SKILL.md frontmatter — nothing the single shared copy can carry to assert "I'm protected."

So "one skill, one protection fact" becomes "one skill, N pin operations to keep in sync, with no mechanism guaranteeing they stay in sync."

  • Why I think this is foundational, not cosmetic

The usual rebuttal is "cross-profile sharing is outside the design envelope." I don't buy it here, because profiles and self-improving skills are both first-class, advertised features. "A skill learned in one context should be usable in another" is the natural composition of two core capabilities, not an exotic stretch — a learning system that forgets at the profile boundary undercuts its own pitch ("the harness grows with you"). And "where state lives" is not a detail. State belongs with the entity it describes. Hermes pinned the protection fact to a per-profile sidecar instead of to the skill — which is the one thing that's actually shared. A misplaced source of truth is a design decision, not a missing knob.

Fair counterpoint I'll grant: profiles are sometimes used precisely to isolate contexts, and not every skill should be global. So the ask is not "make everything global" — it's "give global an opt-in, first-class representation, and don't make protecting a shared skill cost a DRY violation."

Proposed Solution

Proposed fix The clean fix is to let protection travel with the artifact:

1 Declarative protection in frontmatter — e.g. metadata.hermes.pinned: true in SKILL.md. The flag lives in the resolved skill, binds to the correct copy, survives cross-machine sync, and is immune to the name-collision/shadowing problem because it isn't keyed by name in a separate store.

2 (Weaker alternative) a shared pin registry co-located with shared skills (e.g. ~/.agents/pinned-skills/ next to ~/.agents/skills/) that all profiles consult. This works, but it indexes by name and therefore reinherits the shadowing fragility — if a local homonym shadows the shared skill, the registry's pin can apply to the wrong copy. Hence option 1 is cleaner.

This is additive: a frontmatter field plus a read in the skill_manage delete guard and the curator transition pass. It doesn't require changing the primary source of truth.

  • Questions for maintainers / the community
  • Is cross-profile sharing of procedural memory (skills) considered in scope, or are profiles meant to be fully isolated including their skill libraries?
  • Would a declarative protection field in SKILL.md frontmatter be accepted, so pin state can live with the skill rather than in per-profile .usage.json?
  • Relatedly: any plans around a configurable write target for skill creation (cf. the default_write_dir request in #4381) and the curator's treatment of hand-authored/external skills (cf. the curator tracking issue #7816)? Both seem to share the same root: the "shared / global" use case being expressed through per-profile local state.

Curious whether others running multiple profiles have hit this, and how you're working around it (FS read-only? disabling the curator per profile? scripting curator pin on profile creation?).

Alternatives Considered

No response

Feature Type

Configuration option

Scope

Large (new module or significant refactor)

Contribution

  • I'd like to implement this myself and submit a PR

Debug Report (optional)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Feature]: Global, self-improving skills have no first-class home — and protecting them forces a DRY violation