> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mibyanai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Curator

> Background maintenance for agent-created skills — usage tracking, staleness, archival, and LLM-driven review

<Info>
  Commands, package names, and image names on this page come from the open-source project that Mibyan Desktop is built on, and can differ from the Mibyan Desktop installer. For the supported Mibyan install and update path, see [Install and update](/products/desktop-guide/install-and-update).
</Info>

The curator is a background maintenance pass for **agent-created skills**. It tracks how often each skill is viewed, used, and patched, moves long-unused skills through `active → stale → archived` states, and periodically spawns a short auxiliary-model review that proposes consolidations or patches drift.

It exists so that skills created via the [self-improvement loop](/desktop/user-guide/features/skills#agent-managed-skills-skill_manage-tool) don't pile up forever. Every time the agent solves a novel problem and saves a skill, that skill lands in `~/.mibyan/skills/`. Without maintenance, you end up with dozens of narrow near-duplicates that pollute the catalog and waste tokens.

By default the curator manages only agent-created skills. With `curator.prune_builtins: true` it can also archive **unused bundled built-in skills** (shipped with the repo) after `archive_after_days` of non-use; this is opt-in because shipped skills silently disappearing from `skills_list` is easy to mistake for a broken install. Hub-installed skills (from [agentskills.io](https://agentskills.io)) are always off-limits. The curator also **never auto-deletes** — the worst outcome is archival into `~/.mibyan/skills/.archive/`, which is recoverable.

Tracks [issue #7816](https://github.com/NousResearch/hermes-agent/issues/7816).

## How it runs

The curator is triggered by an inactivity check, not a cron job. On CLI session start, during gateway housekeeping, and on the Desktop/`mibyan serve` maintenance timer, Mibyan checks whether:

1. Enough time has passed since the last curator run (`interval_hours`, default **7 days**), and
2. The agent has been idle long enough (`min_idle_hours`, default **2 hours**).

Desktop and other `mibyan serve` backends share the existing hourly maintenance timer (first poll after 90 seconds), independently of cron jobs. They measure inactivity from process startup and the most recent chat activity in the same profile, retaining that activity timestamp after a session closes or is reaped, and skip curator while a turn in that profile is running. A connected but inactive window does not block maintenance. This timer also polls personal and organization Skill Sync, subject to those features' own opt-in gates. A running messaging gateway for the same profile owns these chores instead.

The timer services its backend's profile. In-flight maintenance runs in a worker thread; closing the backend does not cooperatively interrupt that pass. Starting multiple independent serve processes for the same profile can still race the curator's interval check.

If both are true, it spawns a background fork of `AIAgent` — the same pattern used by the memory/skill self-improvement nudges. The fork runs in its own prompt cache and never touches the active conversation.

<Info>
  **First-run behavior**

  On a brand-new install (or the first time a pre-curator install ticks after `mibyan update`), the curator **does not run immediately**. The first observation seeds `last_run_at` to "now" and defers the first real pass by one full `interval_hours`. This gives you a full interval to review your skill library, pin anything important, or opt out entirely before the curator ever touches it.

  If you want to see what the curator *would* do before it runs for real, run `mibyan curator run --dry-run` — it produces the same review report without mutating the library.
</Info>

A run has two phases:

1. **Automatic transitions** (deterministic, no LLM). Skills unused for `stale_after_days` (14) become `stale`; skills unused for `archive_after_days` (30) are moved to `~/.mibyan/skills/.archive/`. This is the always-on pruning behavior — it runs whenever the curator is enabled, with no aux-model cost.
   * **Pinned skills** and **skills referenced by any cron job** (including paused/disabled jobs) are skipped entirely — treated like pin for auto-transitions so a slow or paused schedule cannot archive a skill out from under a job. Consolidation also rewrites cron skill references when it merges umbrellas.
   * **Never-used skills** (`use_count == 0`) get a grace floor: they are not archived until they are at least `stale_after_days` old. Zero uses is absence of evidence, not proof the skill is disposable.
2. **LLM consolidation** (single aux-model pass with a high iteration ceiling — a full curation sweep typically takes 50–100 API calls) — **OFF by default**. When `curator.consolidate: true`, the forked agent surveys the agent-created skills, can read any of them with `skill_view`, and decides per-skill whether to keep, patch (via `skill_manage`), consolidate overlapping ones into class-level umbrellas, or archive (via `skill_manage action=delete` with an `absorbed_into` target — the pass has no terminal). The candidate list it is given contains only skills it can actually read and write: bundled built-ins (even with `prune_builtins: true`, which only affects the deterministic archival pass) and skills in `skills.disabled` (which `skill_view` refuses) are never offered to it, so the pass cannot burn its tool budget on refused reads or writes. Consolidation treats a skill as a full package: if a skill has `references/`, `templates/`, `scripts/`, `assets/`, or relative links to those paths, the curator must either keep it standalone, re-home the needed support files and rewrite paths, or archive the entire package unchanged — not flatten only `SKILL.md` into another skill's `references/` file.

<Info>
  **Consolidation is opt-in**

  By default the curator only **prunes** — the deterministic inactivity pass marks skills stale and archives long-unused ones. The opinionated LLM **consolidation** pass (umbrella-building, merging overlapping skills) is off by default because it costs aux-model tokens on every run and makes broad structural changes to your library. Turn it on with `curator.consolidate: true`, or run it once on demand with `mibyan curator run --consolidate`.
</Info>

Pinned skills are off-limits to both the curator's auto-transitions and the agent's own `skill_manage` tool. See [Pinning a skill](#pinning-a-skill) below.

## Configuration

All settings live in `config.yaml` under `curator:` (not `.env` — this isn't a secret). Defaults:

```yaml theme={null}
curator:
  enabled: true
  interval_hours: 168          # 7 days
  min_idle_hours: 2
  stale_after_days: 14
  archive_after_days: 30
  consolidate: false           # LLM umbrella-building pass — opt-in (prune-only by default)
  prune_builtins: false        # opt in to archiving unused bundled built-in skills too (hub skills always exempt)
```

To disable entirely, set `curator.enabled: false`. To keep the always-on pruning but opt into LLM consolidation, set `curator.consolidate: true`.

### Running the review on a cheaper aux model

The curator's LLM review pass is a regular auxiliary task slot — `auxiliary.curator` — alongside Vision, Compression, Session Search, etc. "Auto" means "use my main chat model"; override the slot to pin a specific provider + model for the review pass instead.

**Easiest — `mibyan model`:**

```bash theme={null}
mibyan model                   # → "Auxiliary models — side-task routing"
                               # → pick "Curator" → pick provider → pick model
```

The same picker is available in the web dashboard under the **Models** tab.

**Direct config.yaml (equivalent):**

```yaml theme={null}
auxiliary:
  curator:
    provider: openrouter
    model: google/gemini-3-flash-preview
    timeout: 600               # generous — reviews can take several minutes
```

Leaving `provider: auto` (the default) routes the review pass through whatever your main chat model is, matching the behavior of every other auxiliary task.

<Note>
  **Legacy config**

  Earlier releases used a one-off `curator.auxiliary.{provider,model}` block. That path still works but emits a deprecation log line — please migrate to `auxiliary.curator` above so the curator shares the same plumbing (`mibyan model`, dashboard Models tab, `base_url`, `api_key`, `timeout`, `extra_body`) as every other aux task.
</Note>

## CLI

```bash theme={null}
mibyan curator status         # last run, counts, pinned list, LRU top 5
mibyan curator run            # trigger a run now (blocks until done). Prune-only unless curator.consolidate: true
mibyan curator run --consolidate # force the LLM consolidation pass on for this run, overriding the config default
mibyan curator run --background  # fire-and-forget: start the run in a background thread
mibyan curator run --dry-run  # preview only — report without any mutations
mibyan curator backup         # take a manual snapshot of ~/.mibyan/skills/
mibyan curator rollback       # restore from the newest snapshot
mibyan curator rollback --list     # list available snapshots
mibyan curator rollback --id <ts>  # restore a specific snapshot
mibyan curator rollback -y         # skip the confirmation prompt
mibyan curator pause          # stop runs until resumed
mibyan curator resume
mibyan curator pin <skill>    # never auto-transition this skill
mibyan curator unpin <skill>
mibyan curator adopt <skill>    # hand an unmanaged skill to the curator
mibyan curator adopt --all-unmanaged   # hand over every unmanaged skill
mibyan curator list-unmanaged   # itemize skills with no provenance marker
mibyan curator restore <skill>  # move an archived skill back to active
mibyan curator list-archived    # list skills currently in ~/.mibyan/skills/.archive/
mibyan curator archive <skill>  # manually archive a single skill now
mibyan curator prune [--days N] # bulk-archive agent-created skills idle >= N days (default: `archive_after_days`, 30)
mibyan curator ledger           # list the per-mutation audit ledger (all actors)
mibyan curator ledger --skill <name> --limit 50  # filter/paginate ledger entries
mibyan curator rollback <entry-id>  # undo a single mutation from the ledger
mibyan curator purge [--days N] [--dry-run]  # delete archived skills older than the TTL (explicit only)
```

## Backups and rollback

Before a consolidation pass (`consolidate: true`, the only pass that rewrites skill content in place), Mibyan takes a tar.gz snapshot of `~/.mibyan/skills/` at `~/.mibyan/skills/.curator_backups/<utc-iso>/skills.tar.gz`. The snapshot covers the live skill tree only: `.archive/`, the audit ledger, `.hub/`, and the backups themselves are never rolled in, and a rollback never rewinds them (an older copy would lose archived skills or ledger entries). If a pass archives or consolidates something you didn't want touched, you can undo the whole run with one command:

```bash theme={null}
mibyan curator rollback        # restore newest snapshot (with confirmation)
mibyan curator rollback -y     # skip the prompt
mibyan curator rollback --list # see all snapshots with reason + size
```

The rollback itself is reversible: before replacing the skills tree, Mibyan takes another snapshot tagged `pre-rollback to <target-id>`, so a mistaken rollback can be undone by rolling forward to that one with `--id`.

You can also take manual snapshots at any time with `mibyan curator backup --reason "before-refactor"`. The `--reason` string lands in the snapshot's `manifest.json` and is shown in `--list`.

The default prune-only pass takes no snapshot: it only moves whole directories into `.archive/`, which is its own undo (`mibyan curator restore`), and every mutation is in the ledger below. Snapshots are pruned to `curator.backup.keep` (default 2) on every pass to keep disk usage bounded:

```yaml theme={null}
curator:
  backup:
    enabled: true
    keep: 2
```

Set `curator.backup.enabled: false` to disable automatic snapshotting. The manual `mibyan curator backup` command still works when backups are disabled only if you set `enabled: true` first — the flag gates both paths symmetrically so there's no way to accidentally skip the pre-run snapshot on mutating runs.

`mibyan curator status` also lists the five least-recently-used skills — a quick way to see what's likely to become stale next.

The same subcommands are available as the `/curator` slash command inside a running session (CLI or gateway platforms).

## Audit ledger and single-edit rollback

Whole-run snapshots answer "undo everything the last curator pass did" — but sometimes you want to know *who changed what* and undo exactly one mutation. Every skill mutation — curator auto-transitions, agent `skill_manage` calls, and your own CLI archive/restore/purge — appends one entry to the append-only JSONL ledger at `~/.mibyan/skills/.curator_ledger.jsonl`:

* **actor** — `curator` (background review fork / auto-transitions), `agent` (foreground agent tool calls), or `user` (CLI commands)
* **action** — `create`, `edit`, `patch`, `delete`, `write_file`, `remove_file`, `archive`, `restore`, `purge`, `rollback`
* **evidence** — delete intent (`absorbed_into` for consolidations, empty for prunes, and whether the recoverable-archive path handled it), triggering session id when available
* **before/after** — per-file `{path, sha256}` manifests. File contents are stored content-addressed (deduped by hash) under `~/.mibyan/.curator_backups/blobs/`, so a hundred entries touching the same unchanged file cost one blob.

```bash theme={null}
mibyan curator ledger                  # newest 20 entries
mibyan curator ledger --skill my-skill --limit 50
mibyan curator rollback <entry-id>     # restore that one mutation's before-state
```

Single-entry rollback restores exactly the files that mutation touched (and removes files it created) from the blob store — nothing else in the skills tree moves. Like whole-tree rollback, it takes a safety ledger entry of the current state first and **fails closed**: if the safety capture can't be written, nothing is changed. Because foreground deletes are ledgered too, `mibyan curator rollback <entry-id>` can resurrect a hard-deleted skill.

The ledger is telemetry, never a gate — if writing an entry fails, the mutation still goes through. Disable it with:

```yaml theme={null}
skills:
  ledger: false
```

The file is also size-bounded: once it grows past `skills.ledger_max_bytes` (default 5 MB), the next mutation first rewrites it through the unchanged-file dedup (exactly what `mibyan curator ledger --compact` does) and, if genuinely-divergent entries still exceed the cap, drops the oldest ones regardless of shape — the newest entry always survives, lines in the retained tail are never rewritten or parsed (a malformed line there survives verbatim), and the sweep frees blobs nothing references anymore and older than an hour (a fresh unreferenced blob may belong to a capture another process has not yet recorded). Set it to `0` to keep the ledger append-only forever.

```yaml theme={null}
skills:
  ledger_max_bytes: 5242880   # 0 = never auto-maintain
```

## Archive TTL purge

Archived skills are kept forever by default. If you want `~/.mibyan/skills/.archive/` bounded, set a TTL and purge explicitly — purging never runs automatically, and every purged skill is captured into the ledger (with blobs) first, so even a purge leaves an auditable, recoverable trail:

```yaml theme={null}
curator:
  archive_ttl_days: 180   # 0 (default) = never purge
```

```bash theme={null}
mibyan curator purge --dry-run   # preview what would be deleted
mibyan curator purge             # delete archives older than the TTL (with confirmation)
mibyan curator purge --days 90   # one-off TTL override
```

## What "agent-created" means

The curator only manages skills explicitly marked as **agent-created** in
`~/.mibyan/skills/.usage.json`. A skill qualifies when ALL of the following
are true:

1. Its name is **not** in `~/.mibyan/skills/.bundled_manifest` (bundled skills shipped with the repo).
2. Its name is **not** in `~/.mibyan/skills/.hub/lock.json` (hub-installed skills).
3. Its `.usage.json` entry has `"created_by": "agent"` or `"agent_created": true`.

Currently, only the **background self-improvement review fork** sets this marker
— when it creates a new umbrella skill during its periodic review pass (\~every 10
agent turns). The background fork runs with a write origin of `"background_review"`
(via `tools/skill_provenance.py`), which is the only path that triggers the
`mark_agent_created()` call in `skill_manage`.

Skills the foreground agent creates via `skill_manage(action="create")` during a
conversation (including `/learn`) are **not** marked as agent-created — they are
recorded as `created_by: learn`, which makes them show up in the
[learning journey](/desktop/user-guide/features/memory#learning-journey-journey) right away but is not a
curator opt-in. They are considered user-directed and the curator intentionally
leaves them alone.

<Warning>
  **Your hand-written skills are NOT curated**

  If you manually created a `SKILL.md` or pointed Mibyan at an external skill
  directory, that skill will have a `.usage.json` entry with `created_by: null`
  (or the field absent). The curator will not touch it. The same applies to
  skills the foreground agent created at your request (`created_by: learn`).

  **To see which skills the curator actually manages**, run `mibyan curator status`.
  If the agent-created count is 0, no skills are currently in the curator's
  jurisdiction — the LLM review pass is skipped and the report will show
  `Model: (not resolved) via (not resolved)` with `Duration: 0s`.
</Warning>

### Adopting unmanaged skills

`mibyan curator status` reports an **unmanaged** count alongside the managed
one:

```
curator-managed skills: 43 total  (agent-created=43  bundled=0)
  active     41
  stale       2
  archived    0

unmanaged (no provenance marker): 112 total
  pre-dates marker    34
  foreground-created  78
  never auto-staled or archived — `mibyan curator adopt <name>` hands one over
```

Those 112 are curation-*eligible* but permanently invisible to the lifecycle,
for one of two reasons:

* **pre-dates marker** — the record was written before `created_by` existed, so
  it carries no provenance signal at all. Authorship is genuinely unknowable
  from the record.
* **foreground-created** — a foreground `skill_manage(create)` recorded
  `created_by: learn` (older records: unset) by design, since skills you ask for
  belong to you.

A large library can therefore look fully curated while most of it is
untouchable. `adopt` closes that gap by **declaration**:

```bash theme={null}
mibyan curator list-unmanaged                    # itemize them, with reasons
mibyan curator adopt <name> [<name> ...]         # hand specific skills over
mibyan curator adopt --all-unmanaged --dry-run   # preview the full list
mibyan curator adopt --all-unmanaged             # hand over everything (prompts)
mibyan curator adopt --all-unmanaged --yes       # skip the prompt
```

Adoption writes the same `created_by: agent` marker the background review fork
writes. It does **not** reset the inactivity clock — an adopted skill keeps its
existing `last_activity_at`, so handing over a library you already stopped
using does not buy it a fresh 90-day window. Expect adopted long-idle skills to
go `stale` (or `archived`) on the next pass; that is the point.

Adoption is also what unblocks autonomous *improvement*. The background review
fork refuses to patch a skill that isn't curator-managed, so if it notices one
of your skills is outdated it will say so and recommend adoption rather than
edit it. Foreground (user-directed) edits are never affected — you and the
agent can always edit your own skills on request.

<Note>
  **`created_by` is a policy flag, not a provenance claim**

  The stored field is named `created_by`, but it is consumed as "may autonomous
  curation touch this?" — not "who wrote this file". Those are different
  questions, and for records predating the marker the authorship answer is simply
  unrecoverable. The name is kept because it is already on disk in every
  `.usage.json`; read it as policy. `mibyan curator adopt` changes the policy, and
  says nothing about who authored the file.
</Note>

<Note>
  **Provenance is declared, never inferred**

  Adoption is deliberately manual. Telemetry cannot establish authorship: a skill
  with thousands of patches proves the agent **maintains** it, not that the agent
  **wrote** it — Mibyan edits user-authored skills on your behalf constantly. An
  automatic "looks agent-made, adopt it" heuristic would eventually archive
  something you hand-wrote. `adopt` refuses bundled, hub-installed, external, and
  protected built-in skills, which have an owner other than you.
</Note>

Skills that ARE agent-created follow the full lifecycle:

* `active` → (30d unused) `stale` → (90d unused) `archived`
* Pinned skills bypass all auto-transitions
* Archives are recoverable via `mibyan curator restore <name>`

If you want to protect a specific skill from ever being touched — for example a
hand-authored skill you rely on — use `mibyan curator pin <name>`. See the next
section.

## Pinning a skill

Pinning protects a skill from deletion — both the curator's automated archive passes and the agent's `skill_manage(action="delete")` tool call. Once a skill is pinned:

* The **curator** skips it during auto-transitions (`active → stale → archived`), and its LLM review pass is instructed to leave it alone.
* The **agent's `skill_manage` tool** refuses `delete` on it, pointing the user at `mibyan curator unpin <name>`. Patches and edits still go through, so the agent can improve a pinned skill's content as pitfalls come up without a pin/unpin/re-pin dance.

Pin and unpin with:

```bash theme={null}
mibyan curator pin <skill>
mibyan curator unpin <skill>
```

The flag is stored as `"pinned": true` on the skill's entry in `~/.mibyan/skills/.usage.json`, so it survives across sessions.

Skills named in any cron job's `skills:` list are protected the same way for **auto-transitions** (the curator never stales/archives them while the reference remains), even when the job is paused or disabled. Prefer an explicit pin when you also want `skill_manage delete` blocked.

Only **agent-created** skills can be pinned — `mibyan curator pin` refuses on bundled and hub-installed skills with an explanatory message if you try. Hub-installed skills are never subject to curator mutation. Bundled built-in skills are only touched when you opt in with `curator.prune_builtins: true`, and even then only archived after `archive_after_days` of non-use — never patched, consolidated, or deleted.

A small set of **protected built-ins** can be hardcoded as never-archivable and never-consolidatable, regardless of `curator.prune_builtins`, pin state, or LLM judgment. These back load-bearing UX, so silently archiving one would turn its slash command into an "Unknown command" error with no signal to you. (The set is currently empty — `plan`, its original member, graduated to a built-in `/plan` command with no skill on disk.) Protected built-ins are filtered out of the curator's candidate list entirely, so the consolidation pass never sees them.

If you want a stronger guarantee than "no deletion" — for instance, freezing a skill's content entirely while the agent still reads it — edit `~/.mibyan/skills/<name>/SKILL.md` directly with your editor. The pin guards tool-driven deletion, not your own filesystem access.

## Usage telemetry

The curator maintains a sidecar at `~/.mibyan/skills/.usage.json` with one entry per skill:

```json theme={null}
{
  "my-skill": {
    "use_count": 12,
    "view_count": 34,
    "last_used_at": "2026-04-24T18:12:03Z",
    "last_viewed_at": "2026-04-23T09:44:17Z",
    "patch_count": 3,
    "last_patched_at": "2026-04-20T22:01:55Z",
    "created_at": "2026-03-01T14:20:00Z",
    "state": "active",
    "pinned": false,
    "archived_at": null
  }
}
```

Counters increment when:

* `view_count`: the agent calls `skill_view` on the skill.
* `use_count`: the skill is loaded into a conversation's prompt.
* `patch_count`: `skill_manage patch/edit/write_file/remove_file` runs on the skill.

Bundled and hub-installed skills are explicitly excluded from telemetry writes.

## Per-run reports

Every curator run writes a timestamped directory under `~/.mibyan/logs/curator/`:

```
~/.mibyan/logs/curator/
└── 20260429-111512/
    ├── run.json      # machine-readable: full fidelity, stats, LLM output
    └── REPORT.md     # human-readable summary
```

`REPORT.md` is a quick way to see what a given run did — which skills transitioned, what the LLM reviewer said, which skills it patched. Good for auditing without having to grep `agent.log`.

<Note>
  **No candidates? Report shows `(not resolved)`**

  When the curator has **no agent-created skills** to review, the LLM review pass
  is skipped entirely. The report header will show
  `Model: (not resolved) via (not resolved)` with `Duration: 0s` — this does **not**
  indicate a configuration error or model resolution failure. It simply means there
  were no candidates, so no model was ever invoked. The auto-transition phase still
  runs and reports its counts normally.
</Note>

### Rename map in the summary

If a run consolidated multiple skills under an umbrella (or merged near-duplicates), the user-visible summary printed at the end of the run includes an explicit rename map showing every `old-name → new-name` pair the curator applied. This is in addition to per-skill transition lines, so when a wave of renames lands you can spot them at a glance without diffing the JSON report. The hint also surfaces under `mibyan curator pin` so you can pin the umbrella name immediately if you want to lock the new label in.

## Restoring an archived skill

If the curator archived something you still want:

```bash theme={null}
mibyan curator restore <skill-name>
```

This moves the skill back from `~/.mibyan/skills/.archive/` to the active tree and resets its state to `active`. The restore refuses if a bundled or hub-installed skill has since been installed under the same name (would shadow upstream).

## Disabling per environment

The curator is on by default. To turn it off:

* **For one profile only:** edit `~/.mibyan/config.yaml` (or the active profile's config) and set `curator.enabled: false`.
* **For just one run:** `mibyan curator pause` — the pause persists across sessions; use `resume` to re-enable.

The curator also refuses to run if `min_idle_hours` hasn't elapsed, so on an active dev machine it naturally only runs during quiet stretches.

## See also

* [Skills System](/desktop/user-guide/features/skills) — how skills work in general and the self-improvement loop that creates them
* [Memory](/desktop/user-guide/features/memory) — a parallel background review that maintains long-term memory
* [Bundled Skills Catalog](/desktop/reference/skills-catalog)
* [Issue #7816](https://github.com/NousResearch/hermes-agent/issues/7816) — original proposal and design discussion


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.