refactor(skills): shipped-set slim — 15 to optional, github 6-way merge, pdf absorbs OCR, channel-gated teams pipeline (index −26%) (#98539)
* refactor(skills): shipped-set slim — 15 skills to optional, github six-way merge, pdf absorbs OCR+nano-pdf, channel-gated teams pipeline
Maintainer-directed shipped-skills curation (skills index 1,900 -> ~1,400
tok/call on desktop; every session pays the index, so this is a per-call
diet on all installs):
- optional-skills moves (installable via skills hub, history preserved):
creative comfyui/ascii-art/excalidraw/pretext/sketch/touchdesigner-mcp;
ALL of mlops (huggingface-hub, llama-cpp, serving-llms-vllm,
weights-and-biases, evaluating-llms-harness — subcategory structure
kept); research-paper-writing (55 supporting files, 17.3K-tok load);
openhue; blogwatcher (first taught the cronjob monitor-field watch
pattern + web_extract instead of pre-cron manual workflows)
- DELETED session-librarian (Aug-12 'inspired by Perplexity Computer'
port, never maintainer-intended; session_search covers discovery)
- github: six skills (auth, issues, pr-workflow, issue-to-pr,
code-review, repo-management) merged into ONE software-development/
github skill — routing body + complete per-workflow references;
benbarclay authorship credited; codebase-inspection rides along;
discipline pins from test_github_issue_to_pr_skill.py preserved
against the reference body in the new test_github_skill.py
- pdf absorbs ocr-and-documents + nano-pdf as references/ + scripts
(extract_pymupdf, extract_marker converted to the argparse house
standard its contract test enforces)
- NEW session_platforms frontmatter gate (metadata.hermes): hides a
skill from the index on gateway channels it is not for; fail-open on
unknown platform; teams-meeting-pipeline gated to [teams, cron]
- blocked-page-recovery: research -> new web category; trigger-first
description ('Use when a fetch fails: 403/429, paywall, WAF, bot
wall.') so the model actually reaches for it on blocked fetches
- docs regenerated via generate-skill-docs.py (195 pages); related_skills
swept repo-wide; tests: 1672 passed (2 openclaw failures pre-existing
on clean main, Windows-local)
* chore: ignore .skills_prompt_snapshot.json (local index cache, accidentally committed)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -212,3 +212,4 @@ native/fts5_cjk/*.so
|
||||
# interrupted; consumed by launch-time recovery. Never commit it (was tracked
|
||||
# by accident via 3a69e34702, removed in the #72002 salvage).
|
||||
.lazy-refresh-incomplete
|
||||
.skills_prompt_snapshot.json
|
||||
|
||||
1
SOUL.md
Normal file
1
SOUL.md
Normal file
@@ -0,0 +1 @@
|
||||
You are Hermes Agent, built by Nous Research. Be direct: match the length of your reply to the weight of the ask — a one-line question gets a one-line answer, and finished work gets a short report of what changed, what's verified, and what's left, never a replay of the process. No filler ("Great question," "I'd be happy to"), no restating the request back, no re-summarizing what you already said, no narrating tool calls the user can see. Plain claims over adjectives; when unsure, say so plainly. Agree because it's right, not because the user said it. Depth is earned — give it when the user asks for detail, teaches, or the stakes demand it, not by default.
|
||||
@@ -1702,8 +1702,23 @@ def _skill_should_show(
|
||||
conditions: dict,
|
||||
available_tools: "set[str] | None",
|
||||
available_toolsets: "set[str] | None",
|
||||
session_platform: "str | None" = None,
|
||||
) -> bool:
|
||||
"""Return False if the skill's conditional activation rules exclude it."""
|
||||
# Gateway-channel gate: independent of tool filtering info, because a
|
||||
# channel-specific skill (e.g. teams-meeting-pipeline) is noise on every
|
||||
# other channel regardless of what tools are available. Fail-open when
|
||||
# the session platform is unknown (offline builds, tests) — hiding a
|
||||
# skill someone might need is worse than one spare index line.
|
||||
wanted_platforms = [
|
||||
str(p).strip().lower()
|
||||
for p in (conditions.get("session_platforms") or [])
|
||||
if str(p).strip()
|
||||
]
|
||||
if wanted_platforms and session_platform:
|
||||
if session_platform.strip().lower() not in wanted_platforms:
|
||||
return False
|
||||
|
||||
if available_tools is None and available_toolsets is None:
|
||||
return True # No filtering info — show everything (backward compat)
|
||||
|
||||
@@ -1864,6 +1879,7 @@ def _build_skills_system_prompt_inner(
|
||||
entry.get("conditions") or {},
|
||||
available_tools,
|
||||
available_toolsets,
|
||||
_platform_hint or None,
|
||||
):
|
||||
continue
|
||||
visible_entries.append(entry)
|
||||
@@ -1886,6 +1902,7 @@ def _build_skills_system_prompt_inner(
|
||||
extract_skill_conditions(frontmatter),
|
||||
available_tools,
|
||||
available_toolsets,
|
||||
_platform_hint or None,
|
||||
):
|
||||
continue
|
||||
visible_entries.append(entry)
|
||||
@@ -1917,6 +1934,7 @@ def _build_skills_system_prompt_inner(
|
||||
extract_skill_conditions(frontmatter),
|
||||
available_tools,
|
||||
available_toolsets,
|
||||
_platform_hint or None,
|
||||
):
|
||||
continue
|
||||
project_names.add(fm_name)
|
||||
@@ -2016,6 +2034,7 @@ def _build_skills_system_prompt_inner(
|
||||
extract_skill_conditions(frontmatter),
|
||||
available_tools,
|
||||
available_toolsets,
|
||||
_platform_hint or None,
|
||||
):
|
||||
continue
|
||||
seen_skill_names.add(frontmatter_name)
|
||||
|
||||
@@ -1018,6 +1018,13 @@ def extract_skill_conditions(frontmatter: Dict[str, Any]) -> Dict[str, List]:
|
||||
"requires_toolsets": hermes.get("requires_toolsets", []),
|
||||
"fallback_for_tools": hermes.get("fallback_for_tools", []),
|
||||
"requires_tools": hermes.get("requires_tools", []),
|
||||
# Gateway-channel gate (maintainer-directed, skills-index slim):
|
||||
# list of session platforms (e.g. ["msteams"]) the skill is FOR.
|
||||
# Unlike top-level ``platforms:`` (host OS), this hides the skill
|
||||
# from the index on every other channel — the teams-meeting
|
||||
# pipeline has no business in a desktop or telegram session's
|
||||
# index. Empty/absent = visible everywhere (backward compat).
|
||||
"session_platforms": hermes.get("session_platforms", []),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,15 @@ prerequisites:
|
||||
|
||||
Track blog and RSS/Atom feed updates with the `blogwatcher-cli` tool. Supports automatic feed discovery, HTML scraping fallback, OPML import, and read/unread article management.
|
||||
|
||||
## Working with Hermes tools (read this first)
|
||||
|
||||
`blogwatcher-cli` is the feed database; Hermes tools do the automation around it:
|
||||
|
||||
- **Recurring watch — use the cronjob tool's `monitor` field, not a bare schedule.** `monitor` runs a script each tick and only wakes the agent when output changes: set it to a script that runs `blogwatcher-cli scan >/dev/null 2>&1 && blogwatcher-cli articles` (deterministic output; new articles = changed output = agent wakes with the diff injected). Unchanged ticks cost zero LLM calls. Set `deliver` to route digests to a chat/channel; add `continuity: true` so consecutive digests can dedupe.
|
||||
- **Reading an article the user asks about**: `web_extract([url])` on the article URL from `blogwatcher-cli articles` — do not re-scrape by hand.
|
||||
- **One-off "watch this page for changes" without feed semantics**: skip this skill; the cronjob tool's `monitor` field accepts an http(s) URL directly.
|
||||
- **Company/competitor tracking with analysis and citations**: prefer the `competitor-news-monitor` skill; blogwatcher is the lighter raw-feed layer it can sit on.
|
||||
|
||||
## Installation
|
||||
|
||||
Pick one method:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user