skill-ecosystem-sync
Skill1 starsChanged 41 days agoApache-2.0View on GitHub
Use when syncing 4 skill registries (مزامنة السجلات). Complete skill ecosystem update workflow — update all skills across 4 registries (npx/skills.sh, GitHub published, ClawHub/Hermes hub, profile swarm), audit modified bundled skills, install missing skills from external repos, and sync to every Flutter swarm profile plus default. This is the ONE skill to load before any "update skills" task.
- Commits and pushes
In this repository18 other files it gives its agents.
- agent-repo-handoff-loopSkill
- device-screen-verificationSkill
- flutter-android-build-systemSkill
- flutter-design-anti-patternsSkill
- flutter-ios-build-systemSkill
- flutter-isar-clean-arch-setupSkill
- flutter-isar-testingSkill
- flutter-lessons-patternsSkill
- flutter-patternsSkill
- flutter-sdk-changelogSkill
- flutter-soul-stewardshipSkill
- github-project-auditSkill
- mcp-bridge-patternsSkill
- repo-front-doorSkill
- specification-writingSkill
- supabase-fullstackSkill
- translation-fidelitySkill
- what-if-analysisSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 5 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 17
After any skill modification — this is the canonical sync procedure
- Very specific: line 51
If repo > local (version higher, or repo has newer LL/pattern entries), restore repo → local instead: rsync -a …
- Distinctive: line 68
Before any sync that touches locally-authored skills, run:
- Very specific: line 135
Run this gate FIRST in every cycle.
- Very specific: line 192
Do not blind-install a not_via_npx line either.
- Very specific: line 260
When hermes skills check finds an update, it preserves our modifications.
- Very specific: line 283
After updating the default profile, sync the profiles that actually carry a skill tree (derive the live list: ls -d …
- Very specific: line 335
Verify: npx skills add jahfaliabdulrahman-dev/hermes-skills -l and confirm the listed count matches the local published set derived above …
- Distinctive: line 339
CLI publishing is NOT yet supported: All skills must be submitted manually:
- Distinctive: line 343
Before submitting, run security scan:
- Distinctive: line 366
Every sync/update run ENDS with a report.
- Very specific: line 419
PITFALL 2: flutter/skills grew from 10 to 22: Always check -l (list) flag before assuming count.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Skill Ecosystem Sync
Complete systematic workflow for updating ALL skills across the ecosystem. This covers 4 registries and the full profile swarm. Counts in this skill are deliberately illustrative, never authoritative — always derive them live (npx skills add <repo> -l, find ~/.hermes/skills -name SKILL.md | wc -l) because published and installed counts drift. Load this skill whenever the user asks to update, check, or sync skills.
Trigger
- "update skills" / "check for updates" / "sync skills"
- "publish skills" / "anything new in skills store"
- After any skill modification — this is the canonical sync procedure
Phase 1: Bidirectional Sync — Our Published Skills
Our skills live at jahfaliabdulrahman-dev/hermes-skills on GitHub.
Pre-step: ensure repo clone exists
/tmp/ is cleared on reboot — the clone may be gone. If missing, clone fresh:
[ -d /tmp/hermes-skills ] || git clone https://github.com/jahfaliabdulrahman-dev/hermes-skills.git /tmp/hermes-skillsPull: GitHub → Local (download any remote changes)
cd /tmp/hermes-skills && git pullCRITICAL — Direction Check BEFORE local → repo rsync
The sync is NOT always local → repo. A local skill can regress (digest rewrite, restored old backup) below the repo's canonical version. Before rsyncing, compare versions both ways:
for skill in <published list>; do
repo_ver=$(git -C /tmp/hermes-skills show HEAD:skills/$skill/SKILL.md 2>/dev/null | grep -m1 '^version' | awk '{print $2}')
local_ver=$(find ~/.hermes/skills -maxdepth 3 -type d -name "$skill" -exec grep -m1 '^version' {}/SKILL.md \; 2>/dev/null | awk '{print $2}')
echo "$skill: repo=$repo_ver local=$local_ver"
doneIf repo > local (version higher, or repo has newer LL/pattern entries), restore repo → local instead:
rsync -a /tmp/hermes-skills/skills/$skill/ ~/.hermes/skills/<category>/$skill/ — do NOT push the regression.
Known failure mode (case study in references/2026-08-08-sync-direction-regression.md): a local skill copy was once a digest while the repo held the canonical version with more accumulated patterns — a blind local→repo push would have deleted public content. Rule: if the repo version is higher or richer, restore repo → local; never push the regression. When versions tie, compare content (line counts, reference files) before choosing direction.
Pre-push personal-path scan (SECURITY GATE)
Local edits often reintroduce hardcoded personal paths into public files. Before committing, scan:
grep -rn "/Users/<system-username>\|<your-home>" /tmp/hermes-skills/skills/ | grep -v "\.git"Any hit = revert that file to HEAD (git checkout -- <file>) AND restore the generalized placeholder into the LOCAL copy too — otherwise the next sync re-dirties it. The recurring pattern: a personal absolute home path (/Users/<name>/...) or a concrete project path (~/Projects/<concrete-project>/) silently replaces the generalized placeholder (<profile-home>, ~/Projects/<project>/) — the full case study with per-file detail is in references/2026-08-08-sync-direction-regression.md.
Generalization gate (authoring hygiene)
Before any sync that touches locally-authored skills, run:
python3 ~/.hermes/scripts/scan_case_hardening.pyExit 1 = a local skill carries case-specific content (session dates, frozen
counts, personal paths, project bindings) in its MAIN SKILL.md. Migrate the
instance to an invariant (or move the case into references/) using the
procedure in the skill-authoring-generalization skill — never sync a skill
that fails this gate, or the contamination ships publicly.
Push: Local → GitHub (upload our improvements)
**CRITICAL — If any locally-authored skills were modified locally, the repo must reflect it. Derive the set live: ls ~/.hermes/skills/*/*/SKILL.md cross-checked against the published-repo listing — never trust a memorized count.
cd /tmp/hermes-skills
# 1. Sync all published skills FROM local → repo.
# The published set is DERIVED from the repo itself (the repo listing is the source of truth —
# it has grown repeatedly and any hardcoded list goes stale within one cycle, PITFALL 11):
for skill in $(git -C /tmp/hermes-skills ls-tree --name-only HEAD:skills/); do
# Find actual path (skills may be in subdirectories)
src=$(find -L ~/.hermes/skills -maxdepth 4 -type d -name "$skill" -exec test -f {}/SKILL.md \; -print | head -1) # -L: follow symlinks (PITFALL 8)
if [ -n "$src" ] && [ -f "$src/SKILL.md" ]; then
rsync -a "$src/" /tmp/hermes-skills/skills/$skill/
fi
done
# 2. Check if any changes exist
git status
# 3. If changes → commit + push
if ! git diff --quiet || ! git diff --cached --quiet; then
git add -A
git commit -m "🔄 Sync: local improvements to published skills"
git push
fiPITFALL: This is BIDIRECTIONAL. Always pull FIRST to avoid conflicts. Then push our improvements.
Verify
cd /tmp/hermes-skills && git log --oneline -3Phase 2: Update npx/skills.sh External Skills
Skills installed from external repos via npx skills.
2-step GATE — derive the source list, never recite it
The repos named anywhere in this file are EXAMPLES that drift out of date. The authoritative registry of
origin is the installer lock (~/.agents/.skill-lock.json), which records a source per installed skill.
Any source the doc does not name is a source nobody checks — new upstream skills in it are never discovered.
SCRIPT=~/.hermes/skills/devops/skill-ecosystem-sync/scripts/upstream_source_map.py
python3 "$SCRIPT" --coverage # exit 2 = SKILL.md is name-list-based instead of deriving from the lockRun this gate FIRST in every cycle. It does NOT check that repos are named in this file — naming them is the
defect. It checks that the PROCEDURE derives the list from the lock (the --sources loop below is present) and
prints the live inventory. A source in the inventory needs action only when 2b's --diff reports missing
skills; a source that is not written down anywhere is the normal state, not a failure.
2a. Update every installed npx skill (all sources at once)
npx skills update -g -y
# MANDATORY right after (PITFALL 23 — the updater re-links what it updated):
python3 ~/.hermes/skills/devops/skill-store-hygiene/scripts/audit_skill_duplicates.py
python3 -c "import json,pathlib,os;p=pathlib.Path.home()/'.hermes/skills/.retired.json';print([n for n in json.loads(p.read_text())['retired'] if (pathlib.Path.home()/'.hermes/skills'/n).exists()] or 'no resurrection')"The second line prints any retired name that came back; remove that registration and say so in the report.
2b. Discover NEW skills, one pass per DERIVED source
for src in $(python3 "$SCRIPT" --sources | grep -v 'jahfaliabdulrahman-dev/hermes-skills'); do
echo "### $src"
npx skills add "$src" -l > "/tmp/upstream-$(echo "$src" | tr '/' '-').txt" 2>&1
python3 "$SCRIPT" --diff "$src" "/tmp/upstream-$(echo "$src" | tr '/' '-').txt"
doneThe --diff output is the ONLY honest answer to "what is new upstream" — it parses the -l listing and
compares it against the lock. It exits 1 rather than printing missing=0 when it parsed zero names, so a
format change upstream surfaces as a failure instead of a clean report.
Our own published repo is excluded above on purpose: it is Phase 1's job, and npx skills update mirrors it
into .agents/ from the PUBLISHED side (PITFALL 20).
2c. Install the missing ones — as an OWNER DECISION per PITFALL 19
Proof-of-intent gate — run BEFORE any install of an ABSENT name. Absence is not the same as "never had it":
a skill can be missing because it was DELIBERATELY removed (too noisy, wrong direction, superseded). Reinstalling it
resurrects a decision the owner already took. Five witnesses, all cheap:
python3 -c "import json,pathlib;d=json.loads((pathlib.Path.home()/'.agents/.skill-lock.json').read_text());print('dismissed:',d.get('dismissed'))"
python3 -c "import json,pathlib;p=pathlib.Path.home()/'.hermes/skills/.retired.json';print('retired:',list(json.loads(p.read_text())['retired']) if p.exists() else [])"
ls ~/.hermes/skills/.archive | grep -x '<name>' # retired deliberately by us
python3 -c "import json,pathlib;d=json.loads((pathlib.Path.home()/'.hermes/skills/.usage.retired.json').read_text());print([k for k in d if '<name>' in k])"
for s in ~/.hermes/skills/.curator_backups/*/skills.tar.gz; do tar tzf "$s" | grep -q "<name>/" && echo "present in $s"; doneThe fifth witness (.retired.json) is the machine-readable one and the only one a future agent will read:
every deliberate retirement writes a row there in the same session, with reason, replaced_by, archive
and the exact restore command. --diff then prints that name as RETIRED-DELIBERATE instead of ABSENT, so
the gate stops asking about it. A retirement with no row is a future reinstall.
Verdict: any witness hit => it existed before => ask the owner, never auto-install. Zero hits across all four
(dismissed empty, not in .archive, not in the retired usage ledger, absent from every weekly snapshot) is the
only state that licenses installing it as a genuinely new upstream skill — and that zero state must be WRITTEN in
the report, because "it was never deleted" is a claim that needs its evidence named.
Do not blind-install a not_via_npx line either. First rule out a RENAME or a functional duplicate (fetch the
upstream SKILL.md, compare with the local skill it would shadow). Genuine new skills install one at a time:
npx skills add <source> -g -y --skill '<name>' # one skill at a time keeps the adoption auditableThen re-run --diff and confirm the count dropped by exactly the number installed. Two legitimate exclusion
reasons to WRITE DOWN instead of installing: (a) duplicate mechanism — a local skill already does the same
job (e.g. an official gradio beside a local "build Gradio UIs" skill; a context7-mcp beside a find-docs
that already drives the ctx7 CLI); (b) no trigger on this machine — e.g. a family of migrating-* skills
for frameworks we do not use. A duplicate is an owner decision (keep local vs swap to official), never a silent
alongside-install.
Known sources on this machine are a measurement, not a list. The set is legitimately wide (Dart/Flutter,
marketing, LangGraph/LangChain, Pydantic AI, Gradio, HuggingFace, Vercel, context7, our own repo) and grows
whenever a pack is adopted — which is exactly why 2a must be a loop over --sources.
2d. A skill with NO lock entry has no upstream (or an unknown one)
Locally authored, bundled, hub-installed and manually adopted skills do not appear in .skill-lock.json. They
must NOT be pushed through npx — and they must not be reported as "up to date" either. Classify them once in
the report: authored-here / bundled / adopted (provenance block in the file) / unknown. unknown is a
finding, not a blank.
2e. Hugging Face skills marketplace (hf skills)
A FIFTH registry, separate from npx and from the Hub: huggingface/skills publishes ~25 first-party
skills (TRL, datasets viewer, Spaces, hf-mem, paper reading…). Drive it from the CLI itself, never from a
remembered list:
hf skills list # authoritative marketplace listing
hf skills add <name> --dest ~/.hermes/cache/scratch/hf-skills # STAGE, never straight into the store--dest is the whole point: without it the CLI writes a DIRECTORY SYMLINK into ~/.agents/skills/ (shared,
read-only to us). Stage → inspect → cp -R the real dirs into ~/.hermes/skills/<category>/ → stamp the
provenance block (upstream URL + commit + licence) → run the frontmatter and duplicate gates. One shared
LICENSE-upstream-<SPDX> at the category root, not a copy per skill. Then verify discovery with a real
skill_view(<name>), not an ls.
Exclusions worth naming in the report: a family serving infrastructure we do not run (the cloud
SageMaker/AWS set on a Mac-only store), and skills whose mechanism already exists locally (an official
trl-training beside a local trl-fine-tuning) — duplicates are an owner decision, never a silent
alongside-install.
Phase 3: ClawHub / Hermes Hub Skills
Skills managed by hermes skills (separate from npx):
# 3a. Check for ClawHub updates
hermes skills check
# 3b. Apply available updates
hermes skills update
# 3c. Check user-modified bundled skills (9 total)
hermes skills list-modifiedKey: user-modified bundled skills. The skills below carry local customizations (derive the current set live — the list drifts as new bundled skills get customized):
design-md,dogfood,google-workspace,hermes-agent,obsidianpowerpoint,requesting-code-review(later merged intocode-review-elite),systematic-debugging,test-driven-development
When hermes skills check finds an update, it preserves our modifications. BUT we must audit quality — ensure our changes are improvements, not regressions.
Audit workflow for each modified skill:
hermes skills diff <name>
# Analyze:
# - Are deletions removing useful content? (google-workspace: YES - bad)
# - Are additions genuinely improving? (requesting-code-review: YES - good; merged into code-review-elite 2026-09-23)
# - Is stock version superior? → reset to stock
# - Are our changes better? → keep
# If stock is better:
echo "y" | hermes skills reset <name> --restoreSee references/2026-07-18-skill-audit-findings.md for the full 8-skill audit results and the google-workspace regression analysis.
Phase 4: Profile Swarm Sync
After updating the default profile, sync the profiles that actually carry a skill tree (derive the live list:
ls -d ~/.hermes/profiles/*/skills — the default profile is the store itself, not a target; external skills live
under ~/.agents/skills/). A profile directory may hold nothing but runtime/ — a session-isolation shell with
no skills tree, because it resolves skills from the default store. Zero matching targets is a legitimate result:
report it and touch nothing (never manufacture a skills/ directory the runtime does not read).
LINKER="$HOME/.hermes/skills/autonomous-ai-agents/botmaker/scripts/link_skill_tree.py"
# usage: python3 "$LINKER" <canonical-skill-dir> <profile>/skills/<name>
# re-running is safe: existing correct links are kept, new source files are added.
# Repair pass — convert every legacy directory symlink under any profile to
# file-level links (harmless to repeat):
for profile_dir in ~/.hermes/profiles/*/; do
for target in "$profile_dir"skills/*; do
{ [ -L "$target" ] && [ -d "$target" ]; } || continue
src="$(cd "$target" && pwd -P)"
rm "$target"
python3 "$LINKER" "$src" "$target"
done
doneSyncing a skill (update or new): one explicit linker call per target profile — keep each profile's set curated; distribute what that profile's mission needs.
Verification:
# 1. No directory symlinks under any profile's skills/ — the invisibility
# defect. botmaker's drift check audits this class fleet-wide.
python3 "$HOME/.hermes/skills/autonomous-ai-agents/botmaker/scripts/drift_check.py"
# 2. Discovery proof — rglob must actually find the linked skills:
python3 -c "
from pathlib import Path
for prof in sorted(Path.home().joinpath('.hermes/profiles').iterdir()):
sk = prof / 'skills'
if sk.is_dir():
n = sum(1 for _ in sk.rglob('SKILL.md'))
print(f'{prof.name}: rglob finds {n} SKILL.md')"Phase 5: Publishing
To skills.sh (via npx)
The repo is auto-discovered when public and properly formatted:
skills/directory withSKILL.mdfilesclawhub-skillsoragent-skillsGitHub topics
Verify: npx skills add jahfaliabdulrahman-dev/hermes-skills -l and confirm the listed count matches the local published set derived above (the count drifts — PITFALL 11).
To ClawHub
CLI publishing is NOT yet supported. All skills must be submitted manually:
Before submitting, run security scan:
hermes skills publish <path> --to clawhubMost skills pass with SAFE verdict; a minority get CAUTION (historically for shell/Python scripts and long code blocks). Historically flagged:
flutter-android-build-system(5 findings) — likely shell scripts flaggedrepo-front-door(5 findings) — likely Python scripts flaggedflutter-patterns(1 finding) — minor
Proactive Capability Audit
Between monthly cycles (or on owner request), run the proactive half — the Capability Auditor pipeline:
- Scheduled scanning — monitor for new extensions targeting Data Engineering, UI/UX automation, Power BI DAX, Python performance.
- Benchmarking & gap analysis — cross-reference new candidates against the current workflow; name the bottleneck each one would resolve.
- Deprecation protocol — if a new skill measurably outperforms an existing one (speed on the owner's hardware, memory, Polars compatibility), flag the old for replacement.
- Structured proposal — every replacement suggestion ships as:
{Skill Name} -> {Technical Gap} -> {Skill to Replace} -> {Implementation Steps} -> {Expected ROI}.
Mandatory Closing Report (owner rule — every sync, no exception)
Every sync/update run ENDS with a report. The most important part is the names: a count alone is not an answer — the owner reads the list. Names are DERIVED from the run's own log, never typed from memory.
Capture the update log while running 2a:
npx -y skills update -g -y > /tmp/skills-update-$(date +%F).log 2>&1
python3 - <<'PY'
import re, pathlib, os
log = sorted(pathlib.Path('/tmp').glob('skills-update-*.log'))[-1]
names = {m.group(1).strip() for l in log.read_text(errors='ignore').splitlines()
if (m := re.search(r'Updated (.+)$', l)) and 'skill' not in m.group(1)}
print(log.name, len(names)); print('\n'.join(sorted(names)))
PYReport shape (six blocks, in this order):
| Block | Content |
| UPDATED (n) | every skill name the updater reports as updated, one per line — the block the owner asked for first |
| NEW (n) | names newly installed this run, with source owner/repo |
| RETIRED (n) | names retired, with replaced_by and the archive path |
| EXCLUDED (n) | names deliberately not installed + the reason class (duplicate mechanism / no trigger / owner decision) |
| STATE | lock count, store count, duplicate audit result, gate verdicts |
| UNPROVEN | what was NOT verified this run, one line, never omitted |
Rules:
- Names before numbers. A report that says «N skills updated» without listing the N is rejected.
- Three states, never merged: updated ≠ newly installed ≠ updated-in-shared-store-only (PITFALL 20). Say which.
- Zero is a result. "NEW: none" and "RETIRED: none" are written explicitly; a missing block reads as work that was not done.
- The UNPROVEN line is not optional — it is the only thing that stops the report from sounding more certain than the run was.
- A run that fails midway still reports: what landed, what did not, and what state the store was left in.
Summary: Total Skills Ecosystem
| Registry | Count | Update Command |
| Our published (GitHub) | 11 | git pull && rsync (bidirectional) |
| npx external (derived from lock) | derive live: upstream_source_map.py | npx skills update -g -y + per-source -l diff |
| ClawHub/Hermes hub | ~8 | hermes skills check && hermes skills update |
| Hermes built-in/bundled | ~40 | hermes update |
| Total default profile | ~89 | — |
| Each profile | ~89 | file-level linked (linker — never dir symlinks) |
Pitfalls
- PITFALL 1: npx vs hermes skills are separate.
npx skills updateupdates skills.sh-installed skills.hermes skills checkupdates ClawHub/bundled skills. Run BOTH. - PITFALL 2: flutter/skills grew from 10 to 22. Always check
-l(list) flag before assuming count. - PITFALL 3: google-workspace modifications were BAD. Our edits removed API scopes, command examples, and safety rules. The stock is superior. Don't blindly keep all modifications.
- PITFALL 4: Path inconsistency. Skills may be at
~/.hermes/skills/<name>/OR~/.hermes/skills/<category>/<name>/. Alwaysfindbefore publishing. - PITFALL 5: PromptScript never supports global installs. Ignore those errors — they're expected.
- PITFALL 6: specification-writing said 18 files but projects use 22+. Updated to v2.0 (27+ files across 22 slots). Ensure this stays current as projects evolve.
- PITFALL 7: Cross-project lesson fragmentation. LL-009/010/011 were discovered in Azdal's
app-spec/00_lessons_learned.mdbut never promoted to the centralflutter-lessons-patternsskill — Claude was saving lessons to project-internal files instead of the cross-project repository. During every sync cycle, check ALL active projectapp-spec/00_lessons_learned.mdfiles (Azdal, Hermex, CarSah) for orphaned LL/DEC entries not yet in the central skill. - PITFALL 8:
find -type dmisses symlinked skills.flutter-design-anti-patternsandsupabase-fullstacklive as symlinks →~/.agents/skills/<name>. Usefind -Lorls -la ~/.hermes/skills/to spot them; rsync from the symlink TARGET. - PITFALL 9:
hermes skills checkreports staleupdate_available. Afterhermes skills update <name>the flag can persist even when content is current. Verify against the official source:curl -sL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/optional-skills/<path>/SKILL.md | diff - <local>(path fromhermes skills inspect <name>or the index cache). Zero diff = flag is stale, ignore. - PITFALL 10: nested skills block the hub updater.
adversarial-ux-testsits underdogfood/which is BOTH a skill dir (has SKILL.md) and a category — the installer refuses ("Refusing to install into 'dogfood'"). Apply the update manually: fetch SKILL.md from the raw GitHub path above andcpit over the local file. Verify with diff. - PITFALL 11: published count drifts. The repo's skill count has grown repeatedly (e.g. when
swarm-executive-controllerjoined it became stale within one cycle). Re-runnpx skills add jahfaliabdulrahman-dev/hermes-skills -lto list; never trust a memorized count. - PITFALL 12: marketing repo count drifts. The external marketing repo's count has repeatedly grown while local installs lagged behind (several skills were once discovered missing locally only during a sync). Install any that
comm -23reports missing; some local dirs are RENAMED (ab-testing→ab-test-setup, cro→form-cro/page-cro/popup-cro, launch→launch-strategy...) — compare by frontmattername:field, not directory name. - PITFALL 13: fresh clones can carry a foreign git identity. A
/tmpclone inherits the machine's global git identity (which may belong to a different project), so commits get publicly misattributed the moment they're pushed. Set the identity locally in the clone BEFORE committing (git config user.name "<repo-account>"; git config user.email "<repo-noreply-email>"); if already committed,git commit --amend --author="<name> <email>" --no-editbefore pushing. - PITFALL 14: HTTPS pushes can die with
send-pack: unexpected disconnect while reading sideband packet. A retry often succeeds; if it persists, setgit config http.version HTTP/1.1andgit config http.postBuffer 524288000in the clone, then push again. Never trust the push output alone — confirm the remote ref (git ls-remote origin refs/heads/mainmust equalgit rev-parse HEAD) before declaring success. - PITFALL 15: never
ln -sa skill directory into a profile. A directory symlink is invisible to Hermes'srglob("SKILL.md")discovery — the skill silently stops existing for that profile. Link file-level withbotmaker/scripts/link_skill_tree.py; audit fleet-wide with botmaker'sdrift_check.py. - PITFALL 16: a third-party
npx skills addwrites into the SHARED store, not into Hermes. It installs packs as directory symlinks~/.hermes/skills/<name> -> ~/.agents/skills/<name>(same inode for Claude Code, OpenClaw, Kiro, Pi). Those skills LOAD normally (skill_viewworks, they appear in the prompt listing), butskill_managecannot see them — it answers "not found in active profile" — and the curator/ledger never record them. Treat every symlinked skill as READ-ONLY for Hermes tooling. To customize one, FORK it: copy the folder to~/.hermes/skills/<name>/as a real directory (writes there are verified to work) and leave the upstream copy intact for the other agents. Verify the class in one command:find ~/.hermes/skills -maxdepth 1 -type l | wc -l. - PITFALL 17:
npx skills remove(global) deletes the canonical copy AND every agent's link. The tool's-g/global scope means a removal is not scoped to the agent that ran it: onenpx skills remove <names>wipes the pack from~/.agents/skills/and from~/.hermes/skills/,~/.claude/skills/,~/.openclaw/skills/,~/.kiro/skills/,~/.pi/agent/skills/at once — with no backup and no entry in any Hermes ledger. And~/.agents/.skill-lock.jsonkeeps entries whose directory no longer exists, so the lock is NOT proof of presence. Before trusting any "N/N installed" report or count, verify the filesystem (ls ~/.agents/skills,find -L ~/.hermes/skills -name SKILL.md | wc -l) and keep a hash manifest so a deletion is provable and restorable. Also note the tool's own default is SYMLINK into each agent dir (--copyis opt-in): Hermes is an officially supported target (~/.hermes/skills/in its agent table), which is exactly why an external install lands in our tree uninvited. - PITFALL 18: a cron/scheduled run's runtime security scanner can block the sync commands themselves.
npx skills ...,curl | python3, and shellfor-loops over globbed paths get BLOCKED (unresolvable package threat-intel, pipe-to-interpreter, "nested executable body") and cron has no approver. Workarounds that pass: (a) invoke the already-cached CLI directly —node ~/.npm/_npx/<hash>/node_modules/skills/bin/cli.mjs <subcommand>(find<hash>withls -d ~/.npm/_npx/*/node_modules/skills); (b) write command output to a file under/tmpand parse it in a separate step that READS the file (python3 <file>) instead of piping into an interpreter; (c) replacefor-loops with a single Python driver script. Never try to "fix" this by granting cron approvals — adapt the command. - PITFALL 19: an external pack can restructure between cycles, so the name diff is not a missing-skills diff. A pack that once split one skill into several can later consolidate them back under new names (a
-llisting then shows names that look new but are renames of installed ones). Do NOT blind-install thecomm -23diff. For each suspected rename, fetch the upstreamSKILL.md(https://raw.githubusercontent.com/<owner>/<repo>/<branch>/skills/<name>/SKILL.md) and compare content against the local skill it would duplicate. If it duplicates one, treat it as an AMBIGUITY: report the mapping (upstream-name ← local-name) and the two options (migrate-and-retire the old names vs install-alongside creating duplicates) as an owner decision — never install duplicates and never delete the old ones silently. - PITFALL 20:
npx skills update -g -yalso refreshes the shared~/.agents/skills/copies of OUR OWN published skills. Because those were installed from the public repo, the updater makes the.agentscopy mirror the PUBLISHED repo — which may be older than the authoritative~/.hermes/skills/copy. Two consequences: (a) always rsync FROM~/.hermes/skills/, never from.agents, when both exist; (b) run the version check with a CHECKSUM diff (rsync -a -n -c -i), because a plain itemize diff reports mtime/permission noise (t/pflags) that masquerades as a content change and can trigger a pointless push or a false "regression" alarm. Only thec/sflags in the itemize string mean the bytes actually differ. - PITFALL 21:
hermes skills diffoutput direction is--- stock / +++ yours. A-line is present in STOCK and ABSENT from your copy — i.e. a potential content REGRESSION, not an addition. Ours sometimes adds sections while silently dropping a rich stock section (the drift is not always additive), and a bundled skill can JUMP to a higher upstream major while the local copy keeps a lower version (a restructure, not an update). Neither is auto-resolvable: flag it as an owner decision (reset to stock vs keep local vs manual merge) instead of guessing. - PITFALL 22: the named repos are not the source list. A sync driven by a hardcoded repo list silently skips every source the doc never names — the lock (
.agents/.skill-lock.json) is the only registry of origin, and it drifts: on this machine it held 9 distinct sources of which 4 were unnamed in this skill (a 22-skill LangGraph/LangChain pack, Pydantic AI, Gradio, HuggingFace), so nothing in their upstreams was ever discovered. Runscripts/upstream_source_map.py --coverageFIRST (exit 2 = the procedure is name-list-based, not lock-derived) and drive 2b from--sources. Corollary:npx skills update -g -yalone looks complete and is not — it refreshes what is installed, never what is new. - PITFALL 23:
npx skills update -g -yRE-CREATES missing Hermes registrations. For every skill it actually updates, the CLI re-links it into each agent dir — including~/.hermes/skills/— so a registration you removed comes back. Measured 2026-10-18: an update run silently re-linked a root symlink for a skill whose canonical copy already lived in a category directory (duplicate twin, caught only by the dedup audit), and it would equally re-link a deliberately RETIRED name whenever that name has an upstream change (it did not return in that run only because its upstream was unchanged). Guard: after every 2a, run the duplicate audit and check.retired.jsonnames for resurrection. Never treat "the update succeeded" as "the store is as I left it". - PITFALL 24:
hf skills list's installed columns are not a presence map for the Hermes store. It only marks skills living in ITS destinations (~/.agents/skills,~/.claude/skills); a skill fully present under~/.hermes/skills/shows blank in every column. Read presence from the store, use the CLI for the catalogue. A forked copy is also invisible tohf skills update— re-import on demand instead of waiting for a sync. - PITFALL 25: upstream marketplace
SKILL.mdfiles may carry no H1. The body can start straight after the frontmatter fence, so a provenance block inserted "after the first heading" silently lands nowhere. Insert it after the closing---, with an H1 fallback only when the frontmatter is absent. - PITFALL 26: a profile directory can be a shell with no
skills/tree at all. Phase 4'srglobthen returns nothing and a naive sync either errors or "helpfully" creates the tree. Derive targets withls -d ~/.hermes/profiles/*/skills; an empty list is the state to REPORT (profile resolves from the default store), not a failure and not a licence to build directories. The repair pass's glob is harmless here — it simply matches nothing — so the defect only shows up as a silently missing Phase 4, which is why the zero must be written down. - PITFALL 27: the updater protects upstream deletions but leaves the lock lying. When an upstream pack drops a
skill,
skills updateprints "appear to have been deleted upstream" and skips the deletion in non-interactive mode — correct for a cron run (nothing is deleted without an owner), but the lock and the local copy both keep the vanished name, so the next--diffshows it as installed-and-current while the upstream no longer ships it. Report such names explicitly asUPSTREAM-DELETED, KEPTand let the owner decide; do not treat "0 removals" as "nothing changed upstream".
Session Reference
The founding session record — the full marathon log (publication, audits, installs, the google-workspace stock-restore decision, and the v1.1.0 bidirectional-sync addition) — lives in references/2026-07-18-skill-audit-findings.md. Case history belongs there, not here; keep this file general.
Support files
scripts/upstream_source_map.py— the source registry probe.--sourcesfeeds the 2b loop;--coverageis the gate (exit 2 = the procedure is name-list-based, not lock-derived);--diff <source> <listing>classifies each upstream-only name asABSENT(not in the store and never retired — the real gap),PRESENT-ELSEWHERE(already installed by another route, i.e. a rename candidate, never a duplicate to install) orRETIRED-DELIBERATE(a row in.retired.json— reinstalling resurrects a decision), and exits 1 on an unparseable listing instead of reporting a false zero.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

