Smart skill index compaction with progressive disclosure, budget control, and usage-frequency learning — works with any agent.
Tackles NousResearch/hermes-agent#22620: "Skill list bloat causes massive context window inflation."
With 250+ skills installed, the full index (name + description for every skill) is injected into the system prompt on every turn — ~6,000 tokens of fixed overhead. Progressive Skill shrinks that to ~1,800 tokens (-70%) while keeping the agent's ability to discover and load the right skill.
Since v3, the project splits into an agent-agnostic decision core (generic/core/, pure Python, zero agent imports) and per-agent adapters:
- Hermes: a thin backend plugin (
__init__.py) installed viahermes plugins install freehul/progressive-skill --enable. It decides which skill categories to demote, records usage frequency, and truncates full descriptions to a budget — without modifying Hermes source. - Any other agent (Claude Code, Codex, ...): drive the same decisions through the bundled
cli.py— see Universal agent usage below.
Three layers, each independently disabled:
When building the skills index, only strong toolset→category links (terminal→devops/github, web→research, …) decide which categories stay fully visible. Everything else is demoted to a single compact line:
leadership (25) ← was: 25 skill names + descriptions
books/comfyui-docs (14)
Demotion is handled by Hermes's native compact_categories mechanism — the plugin only decides what to demote, never re-renders. If upstream changes the function signature, the wrapper catches TypeError and degrades to a plain call (the plugin becomes transparent, never breaks the agent loop).
Skill lists are static, but usage is dynamic. The plugin hooks post_tool_call to record every skill_view / skill_manage call into usage.json:
reasonix: count=3, score=3.00 → autonomous-ai-agents (promoted)
llm-wiki: count=1, score=1.00 → not promoted
Score = count × exp(-Δdays / 30) — recency-decayed, so frequently-used skills keep their whole category fully visible even without a toolset mapping. This is the dynamic signal a static mapping can't provide.
Full categories are truncated to a configurable budget (_LIST_BUDGET_CHARS, default 4600 chars ≈ 1,150 tokens):
- Positive-scored skills (used recently) are kept first — highest first
- Zero-scored skills fill the remaining budget in original order
- Dropped skills remain fully discoverable via
skills_list(category=...)
| Scenario | Before | After | Savings |
|---|---|---|---|
| Desktop (full toolset) | ~6,100 tok | ~1,800 tok | -70% |
| Pure coding | ~6,100 tok | ~1,800 tok | -70% |
| No toolset info (safety) | ~6,100 tok | ~6,100 tok | 0% (safe) |
Verified end-to-end: a fresh session asking "what books have we distilled" correctly found the books/comfyui-docs category through the compact index, expanded it with skills_list, and loaded the right skills — identical discovery behavior to the full index.
hermes plugins install freehul/progressive-skill --enable
# updates: hermes plugins update progressive-skillRequires Hermes CLI or desktop app (any version with agent.prompt_builder.build_skills_system_prompt and the compact_categories kwarg).
Any agent can run the decision core directly — no Hermes required. Full guide in skills/progressive-skill/SKILL.md and AGENTS.md.
# Which categories to demote? (JSON out)
python generic/cli.py demote --snapshot snap.json --usage usage.json --toolsets terminal,web
# Budget-compress a rendered skills index
python generic/cli.py budget --input index.txt --usage usage.json --relevant devops,hermesPrerequisites: Python 3.10+, your own skills snapshot JSON (one entry per skill: {"category": "...", "frontmatter_name": "..."}) and optional usage.json.
All tunables live in plugin.yaml under the config: section (v2.1+). Defaults:
| Key | Default | Meaning |
|---|---|---|
list_budget_chars |
4600 | Hard budget for full-category skill descriptions (~1,150 tok) |
promote_score |
2.0 | Decayed usage score needed to promote a category |
decay_days |
30.0 | Recency decay half-life for usage scores |
always_relevant |
["hermes", "software-development"] | Categories never demoted |
phase3_health_check |
true | Warn when budget transforms match nothing (upstream format drift) |
Changes take effect next session.
- UsageTracker class — module globals replaced by an encapsulated, thread-safe store (no
global, testable in isolation) - Atomic usage.json writes — temp file + fsync +
os.replace; a crash mid-write can never corrupt the stats file - Category data cache — the skills snapshot is read once per mtime change instead of twice per prompt build
- Cross-platform paths — uses
hermes_constants.get_hermes_home()instead of a hard-coded Windows path - Phase 3 health check — if Hermes changes the index format, the plugin logs a warning instead of silently no-oping
- Agent-agnostic core — decision logic extracted to
generic/core/(pure Python, zero Hermes imports). Hermes plugin is now a thin adapter; other agents drive the same logic viacli.py. - CLI —
cli.py demote/cli.py budgetfor universal agent usage - Claude Code skill —
skills/progressive-skill/SKILL.mdentry point - Unit tests —
tests/test_core.py(11 cases, pytest)
- Decision/render separation — the plugin decides which categories to demote; Hermes renders. No regex over rendered output → robust to upstream formatting changes.
- Zero LLM decisions — all disclosure logic is pure rules (toolset mapping + usage scores + budget). Fast, deterministic, token-predictable.
- Conservative by default — demoted categories stay visible as count lines; nothing is ever fully hidden. Everything is one
skills_listcall away. - Safe degradation — signature drift → transparent fallback; missing snapshot → directory scan; missing usage → cold start.
progressive-skill/
├── __init__.py # Hermes adapter (thin, v3)
├── plugin.yaml # Hermes plugin manifest + config section
├── generic/ # agent-agnostic 独立包:core/(决策核心)+ cli.py + README(零 Hermes 依赖,可单独拷贝)
│ ├── core/ # 决策核心:config/catalog/scorer/selector/budget/facade
│ ├── cli.py # 通用 CLI:demote / budget
│ └── README.md # 独立包使用说明
├── skills/progressive-skill/SKILL.md # Claude Code skill entry point
├── AGENTS.md # agent integration guide
├── tests/ # unit tests (pytest)
└── usage.json # created at runtime: skill usage stats
- 中文说明: README.zh-CN.md
- Official docs on skill progressive disclosure: Working with Skills
- Upstream issue: #22620 — Skill list bloat causes massive context window inflation