Back to home@ssheleg

sshlg-skills

Eight agent skills, one command, every agent. Umbrella installer for the ssheleg family — super-ux, task-pipeline, agent-sync, make-skill, sheleg-design, seo-aeo-audit, sheleg-dev, agent-stack — across Claude Code, Cursor, Codex and 70+ more agents. Loads in DeepSeek Harness (dsh).

Stars
2
Language
JavaScript
Created
Jul 24, 2026
Updated
Aug 26, 2026

Introduction

sshlg-skills

validate npm license site

Nine agent skills, one command, every agent.

npx sshlg-skills install    # install the whole family
npx sshlg-skills update     # update everything already installed

Read the site → · one page per skill, every routing rule verbatim, and llms.txt for the machines. · Follow @sshlg93 on X

Works with Claude Code (as plugins) and DeepSeek Harness (dsh), plus Cursor, OpenCode, Codex, Kilo, Kimi, Hermes, OpenClaw, Gemini CLI, Windsurf, Zed and the rest of the 70+ agents the vercel skills CLI supports.


Why

A coding agent is good at writing code and bad at almost everything around it. It builds an interface with no idea who uses it. It calls a task done without checking what was actually asked for. It ships a page that looks generated, on a site no search engine or answer engine can read. It reaches for a payment webhook or an agent's tool-calling loop and writes the version that holds until it meets real traffic. And the moment you want it to remember how you work, you are writing a skill — and packaging one correctly is its own afternoon.

Each of these nine takes one of those gaps and gives the agent a contract it has to follow. They are documentation, validators and small standard-library scripts. No services, no telemetry, no API keys.

SkillVersionWhat it does
super-ux0.48.0Scenario-driven UI development. A versioned design chain in docs/ux/ — the product vision → personas and jobs → user flows → a screens-and-states map with Figma frames → traced scenarios → evidence-backed audits → fix plans, plus docs/brand/ for how the product speaks. One /ux entry point that reaches every skill, two doc-drift linters and a contract doctor.
task-pipeline1.78.0Full-cycle delivery orchestrator. An intake grill interrogates the request into a complete brief, then ten gated stages carry it — docs, brainstorm and decompose, spec, plan, build, tests, deploy, post-deploy, wiki, acceptance — refusing to advance until each gate passes. Documentation is a deliverable with its own portable gate, and the retrospective it leaves behind is traceable to the commit that earned each lesson. Ships project-audit: a cold-start audit of a whole project — discovery, probes whose unmet needs read blind rather than clean, production evidence including one artefact served under two channels, and an HTML report beside a JSON sidecar that says what moved.
agent-sync1.17.0Several agents, one repository, no collisions. Leases with a TTL so two agents cannot claim the same work, race-free id reservation, a run journal and a generated board — over a pluggable knowledge cloud. The answer to "two sessions just committed over each other".
make-skill0.23.1A skill that builds skills. Create, retrofit, audit and publish agent skills and Claude Code plugins: conformance to the Agent Skills open standard, Anthropic's platform rules (per-surface runtime limits, the Skills API, evals) and the Claude Code plugin reference, marketplace layout, version sync, validator + CI, every distribution channel, the review checklist for third-party skills, and what a skill author must know about MCP and A2A — the protocols themselves live in agent-stack.
sheleg-design1.50.0The taste layer. Cinematic scroll-driven landing pages — one scroll clock, motion that degrades to calm, WebGL particle formations — plus product-UI style packs each shipping a ready token layer, and the Figma border: tokens as variables, design to code without hand-copied values.
seo-aeo-audit0.25.1Evidence-first website audit for search and answer engines. Ten tracks from crawl access to AI citation mechanics; every finding carries an observation, every recommendation an evidence tier, and the output is a prioritized change plan plus a link-building brief — not a score.
sheleg-dev0.10.0The integration layer a product reaches once it has users: money in, tracking, sign-in, speed. Stripe subscription billing reconciled into your own database — webhook idempotency, renewals, seats and proration, refunds, price drift, and the coupon offered at the cancel step, where a duration=once discount leaves subscription.discounts the moment its invoice finalizes and the offer can be taken every cycle; crypto payments that survive under-payment, duplicate webhooks and rate drift; GA4, Google Ads, Meta and LinkedIn under Consent Mode v2; Google sign-in with the account pre-hijacking guard, and the server-side auth surface behind it; Core Web Vitals work that moves the score rather than the report; and Sentry error tracking wired so it does not forward your own credentials to a third party and so a stack trace names the commit that caused it.
agent-stack0.13.2Production patterns for agent systems, in four skills. The orchestrator — tool-calling loops that survive their own context pressure, pipelines with human checkpoints and resume, provider routing with fallback and health checks, four memory layers with confidence decay and conflict resolution, and the shape of the work decided before the work: an edge that carries no data is no edge, a plan that declares its dependencies is executed in layers rather than in list order, and a parallel layer gets a checker before the node that consumes it. The harness — what the agent is told: system prompts at the right altitude, tool descriptions the model can act on, technique choice with a verdict each, workflow versus agent and static versus dynamic, plus a seven-track audit of an agent somebody else built and a scanner for the defects that are mechanically visible. The evals — suites that measure whether it actually works: judging trajectories, regression fixtures grown from production, judges calibrated before they are trusted. The interop layer — MCP at revision 2026-07-28 and the four features it deprecated, running many servers at once, the registry, A2A 1.0, and the gateway, every reference carrying the spec revision it was read against. Plus the wallet side of reselling LLM access: tiered balances, one markup boundary, two-phase commit against a provider API.
telegram-dev0.1.3Telegram, split by the API each surface actually speaks. telegram-bots — the official HTTP Bot API: update_id as the only idempotency key an update carries, the allowed_updates default that subscribes you to everything except chat_member and the two reaction types while answering ok: true, the webhook secret header that is the real check, rate limits as a design constraint (1/sec per chat, 20/min per group, ~30/sec bulk), and Telegram Stars with a ten-second pre-checkout window and a grant that belongs to successful_payment. telegram-userbots — MTProto and Telethon: the session file as a credential equal to the password, FloodWaitError as the API telling you exactly how long to wait, pinning across minor releases that move session and entity-cache behaviour, and the account-ban risk a bot token does not carry — the skill opens with whether a user account is needed at all. telegram-miniapps — the web layer, whose entire security model is one signed query string: verifying initData server-side, the auth_date window, the Ed25519 path for a third party, and the SDK package whose name moved while the organisation's own page still fronts the old one. Both the verifier and the delivery invariants ship as runnable fixtures with their mutants.

They compose. task-pipeline hands user-facing work to super-ux and takes its leases from agent-sync; make-skill encodes the repo layout the others are built on; sheleg-dev and agent-stack are the reference layer the first six route to rather than doctrine of their own; everything installs through the one launcher in this repo.

The manifesto this pack implements

These skills are the reference implementation of Proof of Done: The Agentic Software Development Manifesto by Sergey Sheleg — a foundation for building software when agents write the code. The manifesto lives in its own repository; this one is where it stops being an argument.

The manifesto's claim is that the unit of progress is not generated code but an evidence-carrying change: one that carries the intent it implements, the evidence that verifies it, the limits of that evidence, and the decision that accepts it. This repository is where that stops being an argument. Each idea below resolves to a file you can open and a command you can run:

The manifesto saysHere it is
Evidence over confidence — a green nobody watched fail is not evidencevalidate.yml28 negative self-tests, each planting the defect its guard exists for and requiring the refusal; test/plant_guard.py fails the step when the plant did not land, so a guard nobody disarmed cannot report as one that held. npm run test:plants is the half you can run here — every member's own gate, fed a dropped trigger
Done is a coverage relation, not a status on a taskreferences/acceptance.md — the ladder walk, seam by seam
An edge with no named payload is chronology drawn as architecturegraph.schema.json refuses an edge whose payload is empty
The queue is an artifact, not the agent's recollectionscripts/graph.py — the model never reads the graph; a 400-node graph and a 4-node graph print the same 27-byte frontier
Never resolve a higher-layer conflict inside a lower-layer loopreferences/loop-guard.md
When an audit starts finding its own last pass's damage, rotate the axisreferences/audit.md
Completion includes residuereferences/residue.md — eight classes, three owners, teardown verified by re-reading state rather than by trusting the reply
A repeated failure becomes a mechanism, not another paragraphreferences/retrospective.md — three grades, retirement triggers, a list capped at ten
Durable truth over conversational statereferences/continuity.md

Two of the manifesto's evidence notes (E1, E4) are incidents from this repository's own runs — it is describing measurements taken here. The four requirements it named as not built yet have since been built, each with its commit: 9d8695d a judgment gate the schema distinguishes from a measurement, fbd8a67 a serves edge that must resolve, 0bc5eb6 expiring verification, and 8b7de18 a node that states its own completion check. What is still owed is filed as open rows in the member's board (skills/task-pipeline/docs/evidence/backlog.md) and in this repository's own (docs/evidence/backlog.md), rather than described as shipped — which is the manifesto's rule applied to the manifesto.

That sentence was wrong for three days after the manifesto stopped saying it, and its own receipt pointed at a different row than the one it named. pod-manifesto now checks the claim from its end (tools/check-currency.py), and this end is where it is written.

If the ideas are useful to you, a ⭐ on this repository helps other people find them.

Install

From npm — nothing to clone:

npx sshlg-skills install

From GitHub — always tracks main:

npx github:ssheleg/sshlg-skills install

Clone with submodules if you want the pinned snapshots and an offline copy:

git clone --recursive https://github.com/ssheleg/sshlg-skills
cd sshlg-skills && ./install.sh

Flags: --agent a,b picks agents, --all covers every agent the CLI supports, --no-claude skips the plugin step, --claude-only does nothing else.

What install actually does

  • Claude Code → each skill as a plugin (claude plugin marketplace add + claude plugin install), never as a plain ~/.claude/skills/ copy.
  • Every other agent → the vercel skills CLI, installed globally into ~/.agents/skills/, with the agent list passed as repeated --agent flags.
  • Then it prunes the plain Claude copies the skills CLI recreates on its own. That duplicate shadows your plugin and silently serves a stale skill — the one failure mode worth automating away.

DeepSeek Harness

Nothing to install beyond the ordinary install, and no plugin to write. dsh reads the Agent Skills standard directly: its local provider scans <agentsHome>/skills at rank 500, which is ~/.agents/skills — the directory the skills CLI already installs into.

Verified two ways on 2026-08-25 rather than read off a page. The subsystem is in the default profile, not an optional add-on:

npx @deepseek-ai/dsh --profile web --dump-default-config | grep skill
#   name: '@deepseek-ai/dsh-skill'
#   name: '@deepseek-ai/dsh-skill-filesystem'
#   name: '@deepseek-ai/dsh-tool-skill'

and the files are where that provider looks:

ls ~/.agents/skills/telegram-bots/     # SKILL.md  references/  fixtures/

Two things worth knowing, both read from the harness's own skills subsystem doc on 2026-08-25:

  • Discovery is layered and nearest-wins, in this order — <projectRoot>/.dsh/skills (100), <projectRoot>/.agents/skills (200), Config.customSkillDirs (300), <dshHome>/skills (400), <agentsHome>/skills (500), bundled (600). A project-local copy therefore overrides the installed one, which is the same shadowing trap Claude Code has and the same remedy: keep one channel per skill.
  • disable-model-invocation and user-invocable are the two front-matter keys dsh reads, both defaulting to true. This family ships neither, so every skill is model- and user-invocable there by default.

A skill is not a dsh plugin, and the distinction is worth keeping. In that harness a plugin is a Cordis module exporting apply(ctx); skills are loaded by one — dsh-skill-filesystem. These repositories carry the dsh-plugin topic because that is the discovery channel the project asks for, and dsh-skill, which is what they actually are.

Just one skill?

Every skill installs standalone; see its own README. For example:

npx skills add ssheleg/seo-aeo-audit
claude plugin marketplace add ssheleg/seo-aeo-audit && claude plugin install seo-aeo-audit@seo-aeo-audit

Update

npx sshlg-skills install              # nothing installed yet — the whole family, any agent
npx sshlg-skills update               # installed but behind — updates everything
npx --yes sshlg-skills@latest list    # what the current release of each member is

The three commands are the whole interface: install when nothing is there, update when it is there and behind, list to see what the current release of each member is. A member updated on its own leaves the bundle in a combination nobody tested, which is why update takes no member argument.

Updates every skills-CLI install and every Claude Code plugin, and materializes the pinned submodules in a checkout without moving the pins. Restart Claude Code afterwards.

update reconciles — it does not only refresh. It updates every skill that is already there, then adds the ones that are not. That second half exists because skills update <id> is a no-op for a skill installed nowhere: it prints ✓ All global skills are up to date about a skill that does not exist, so a member added to the family after your last install would never arrive, and nothing would say so. Reconciling uses the same agent set install does, which is why update takes --agent and --all too.

Flags: --no-claude, --claude-only, --agent a,b, --all, and --bump-pins — off by default so a checkout stays reproducible; pass it when you deliberately want the submodules fast-forwarded to their upstream tips.

Update through the launcher, not through a bare npx skills update <id>. Without an explicit --agent list the skills CLI auto-detects Claude Code and re-creates ~/.claude/skills/<id> — a plain copy that then shadows your plugin. The launcher passes the agent list explicitly and prunes those copies after every run — including under --no-claude, because the copy appears whether or not the run was asked to manage plugins.

Routing — making the family engage by default

A skill's description influences whether a model reaches for it. It does not oblige. So the pack writes a managed routing block into your global agent instructions, and the rules engage in every project instead of only when someone remembers to ask.

The block opens with the map: eight members, the single command that starts each, and one line saying what it closes. It is generated from skills.json, so a release moves it — and install and update refresh it, which is what makes "the instruction your agents read is current" a mechanism rather than a habit.

A map, not a catalogue. Your agent already gets every skill's name and description from its own runtime; copying those here would put one fact in two homes and cost context in every session of every project. What no runtime can derive is the shape — where to start, and in what order the members compose.

Four channels get it, and each only where that agent is actually installed:

AgentWhere
Claude Code~/.claude/CLAUDE.md
Codex~/.codex/AGENTS.md
Gemini CLI~/.gemini/GEMINI.md
Cursor~/.cursor/rules/sshlg-routing.mdc — one file per rule, alwaysApply: true

Cursor's file is ours end to end rather than a block inside yours, so it carries the same sentinel and a file at that name without it is left alone: someone else's rule is not ours to overwrite.

npx sshlg-skills routers              # write it (asks once)
npx sshlg-skills routers --dry-run    # show the diff, change nothing
npx sshlg-skills routers --update     # refresh an existing block, never create one
npx sshlg-skills routers --diff <name>            # your wording vs the packaged one
npx sshlg-skills routers --update --adopt <name>  # take the packaged wording for it

Twelve routers, and they are different axes rather than competing priorities — a landing page passes several, an internal script passes none:

RouterAnswersWhenNeeds installed
super-uxwhat the interface must dothere is user-facing behavioursuper-ux
sheleg-designhow it looks and movesthere is a visual layersheleg-design
copywritinghow it soundstext a product user will readsuper-ux
sheleg-devwhat it runs on to charge, track and sign inmoney, tracking, sign-in or speed is being wiredsheleg-dev
agent-stackhow an agent system is built, judged and meteredthe thing being built is an agentagent-stack
telegram-devwhich Telegram API a surface speaks, and what it coststhe thing being built lives inside Telegramtelegram-dev
seo-llmowhether a machine will find ita logged-out reader can see it
evidence-docswhat proves itsomething is stated as true
task-pipelinehow the change reaches the repothe repository changestask-pipeline
project-auditwhat is actually true of this project right nowthe question is the whole project, not one changetask-pipeline
make-skillhow the skill itself is builta skill or plugin changes shapemake-skill
agent-syncwho is holding this filethe project has coordination onagent-sync

Two of them need no skill behind them. seo-llmo and evidence-docs are rules, not tools, so they hold whether or not anything is installed.

Your own wording wins. Where you already wrote a rule by hand, migration moves your text in verbatim — asides included — and the packaged default is used only for a router you never wrote.

And it keeps winning, which is the part that needs a report. Because your text always takes precedence, a router reworded in a later release never reaches your machine — the update lands in the package and stops at your config file. So routers now names the routers whose wording has diverged, every run. Nothing is applied: --diff <name> shows both sides, and --adopt <name> takes the packaged one for that router only, parking the wording it replaces under adopted:<name> so an on/off toggle can never hand back the wrong text. Your word still wins; silence stops being the decision.

Consent is asked once, recorded, and never asked again. Declining leaves an SSHLG:ROUTERS:OPTOUT marker, which the block's own header names as the way out and which survives a reinstall and a restored dotfile. Everything outside the sentinels is preserved byte for byte.

Turning routers off

npx sshlg-skills config                                  # what this machine wants
npx sshlg-skills config set routers.seo-llmo off
npx sshlg-skills routers --update                        # apply it

Switching a router off removes its section and drops its table row. Switching it back on restores the exact bytes that were there — including wording of yours that migration had moved in. Settings live in ~/.sshlg-skills/config.json (mode 0600) and store deviations only, so a router added in a later release arrives switched on rather than silently off.

Making it engage by itself — hooks

The routing block loads in every session and is still routed around, because prose in a long file loses to whatever spoke last. Hooks are what speak first — and, since v0.42.0, what holds:

npx sshlg-skills hooks                    # what would be wired, and what holds it now
npx sshlg-skills hooks install            # wire it (refuses to take someone else's status line)
npx sshlg-skills hooks install --force    # take it anyway, parking what it displaced
npx sshlg-skills hooks remove             # unwire, and give the displaced one back
npx sshlg-skills injectors                # who else speaks at SessionStart, and from which file
npx sshlg-skills conflicts                # installed skills that land on a router's ground
HookWhat it doesWhat it costs
SessionStartone note saying the block is not advisory; the session's title and the ledger to watch; and, since v0.48.0, one line naming any other enabled plugin that also injects here — silent when there is none~90 tokens per session
UserPromptSubmitnames the route this prompt asks fornothing on the turns that don't need it
PreToolUsecopies before any write to ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md, ~/.cursor/rules/sshlg-routing.mdc and ~/.claude/settings.json — and refuses the write if the copy cannot be taken. Also refuses a bare npx skills update <member>nothing until one of those files is touched
PostToolUseafter any skills-CLI run, names the plain copies shadowing a plugin; after obsidian-wiki setup, puts back the config keys it truncatesnothing on any other command
Notificationa desktop ping when a long run goes idle or a background agent finishesnothing — it emits a terminal sequence
ConfigChangenotices when something else overwrites these entriesnothing — it records; SessionStart reports
FileChangedsays when the run ledger advances a stagenothing — one line, when it moves
statusLinewhere the pipeline is, always visiblenothing — it prints, it doesn't inject

The guard is the reason this pack exists at all. ~/.claude/CLAUDE.md has no version control behind it, and two defects in this repository's own history destroyed or overwrote it — both times the copy that saved it was made by hand. lib/backup.js has ruled since v0.35.0 that a copy which cannot be taken cancels the write; until v0.42.0 that rule only covered writes this pack performed. An agent editing the file with Edit, or redirecting into it from a shell, went nowhere near it. Now every route is covered, and a denial says which copy failed and that the file was not touched.

The matching is done inside the hook rather than by the entry's if filter, because the hooks reference states that filter is best-effort and fails open on a command it cannot parse. A guard with a documented bypass is not a guard.

The status line reads .task-pipeline/run.md, the ledger a pipeline run already keeps:

0✓ 1✓ 2✓ 3✓ 4✓ 5· 6· 7· 8· 9· 10· · 45% · ▶ 4 Plan auto · gates 5/11 · iter 0 · holds — · 40m

The denominator comes from your pipeline.jsonstages[], and from nowhere else. Until v0.43.0 both numbers came from how many stage: lines the ledger happened to hold, so a run at stage 4 of ten printed gates 5/5 — a finished run, at a glance, every time. With no stage list declared, the line prints 5 gates passed: a count claims nothing about what is left, and the example flow's eleven are not a fallback.

Glyphs are passed · in flight · · not entered · failed · skipped — a failed gate and a skipped stage must not look like a passed one. When a manual gate has no verdict the line says ⏸ waiting on you, because that is the only moment nothing advances until you act.

Beside the line, two things you do not have to be looking at the terminal for: the run's position on the taskbar or dock (OSC 9;4), and a desktop ping the moment a manual gate is reached. The four-line progress block is printed by the hook rather than by the agent — the doctrine requires every glyph to come from the verdict the gate wrote and from nothing else, and a process with no memory cannot write one from memory.

Every number is borrowed rather than computed — the iteration count is the number of iter: lines, the gates come from stage: verdicts — and an unreported holds: prints holds —, not holds 0, because a silent stage must not look like a clean one. In a directory with no run it prints nothing.

Routing that acts, not only reminds

The prompt hook names the route. Naming is a hint a model may ignore, so since v0.43.0 the un-routed path is escalated instead: when a prompt reads as work the family routes and no run is open, the first Edit or Write of that turn asks — naming the route, what it owns, and the phrase that declines it.

  • ask, never deny. The routing block's own boundary says a typo, a one-line edit or a mechanical rename does not go through the pipeline, and no hook can tell a typo from a feature. You answer once.
  • Once per turn. A turn that edits forty files asks once.
  • Bash is never gated — that would put a permission prompt in front of running the tests rather than in front of the change.
  • A refusal phrase silences the whole session, not the turn. Someone who said «без пайплайна» has decided.

A hook cannot make a model invoke a skill. It can refuse the un-routed path and name the route; that is the whole of it, and claiming more would be the false guarantee task-pipeline's own hook doctrine warns about.

The prompt hook is deliberately quiet. A question beats any trigger — with one derived exception: a trigger that is itself phrased as a question wins, because seo-aeo-audit advertises «почему упал трафик» and the generic filter was silencing it on the words it owns. Saying a router's refusal phrase silences it, and a prompt with no signal costs nothing. Every word it fires on must already appear in the target skill's own description; a fixture reads the shipped descriptions and fails on any trigger the skill does not itself advertise, so this cannot become a second routing policy that drifts from the skills it routes to.

It will not quietly take a statusLine it did not set. One held by another tool is reported and nothing is written. --force takes it and parks the displaced value in ~/.sshlg-skills/config.json, so hooks remove restores it and the settings file round-trips byte for byte. Every write goes through the same backup path the routing block uses.

Restart Claude Code after installing — hooks are read at session start.

Working on this repository — the gate arrives with the clone

.claude/settings.json is committed, so a checkout brings two project hooks with it. They apply only here and only in Claude Code:

HookWhat it does
PreToolUse on Basha git commit runs npm test first and is refused while it is red, with the failing tail in the reason
PostToolUse on Edit|Writea SKILL.md breaking the Agent Skills front-matter limits is reported in the turn it was written

The commit gate is honest only because of a number: the suite costs ~8.5 s here. At three minutes it would be a gate people route around, and a gate people route around is worse than none — it teaches that gates are noise.

Neither hook runs for any other agent (hooks are a Claude Code feature), so the same rules stay a self-check elsewhere. Working with a different agent, or want them off? Delete the file — nothing else reads it.

Other commands

npx sshlg-skills list       # the family, versions and descriptions
npx sshlg-skills agents     # supported agent ids
npx sshlg-skills conflicts  # installed skills that land on ground a router owns

conflicts is the machine-specific half of the map's arbitration rule. The rule ships — the router decides the route, a competing skill is never a second entry point — but which packs collide is a fact about one laptop, so it is read here instead of written into everyone's block. It reports candidates, not offenders: most of what it finds answers HOW where a router answers WHEN, and a router reaching for one as a tool is the system working.

How it works

A thin, zero-dependency Node launcher over the three mechanisms that already reach these agents — the skills CLI (70+ agents), claude plugin (Claude Code), and git submodule (pinned snapshots). It invents no new install path. It curates the family, drives those three, and encodes the rules that are easy to get wrong: one channel per agent, exact agent ids, repeated --agent flags, full <name>@<name> plugin ids, and pruning shadow copies.

skills.json                  registry — repos, plugin ids, skill names, pins
skills/*                     the eight skills as pinned git submodules
bin/sshlg-skills.js          the launcher (install / update / routers / config / hooks /
                             injectors / conflicts / list / agents)
lib/routers-registry.js      the ten routers — text, table row and required members, in one entry
lib/routers.js               block parsing and rendering; touches no file, by construction
lib/drift.js                 your wording vs the packaged one; pure, like routers.js
lib/plan.js                  the argv handed to the skills CLI — one builder for install and update
lib/inventory.js             the family's map — entry points, not a catalogue
lib/conflicts.js             installed skills on a router's ground — candidates, not offenders
lib/cursor.js                Cursor's channel, which is one file per rule rather than a block
lib/apply.js                 the only module that writes to the instruction files
lib/migrate.js               moves hand-written rules in; never reads inside the block
lib/config.js, lib/store.js  the pack's settings, and the 0600 discipline they share
install.sh                   POSIX fallback (macOS/Linux; use npx on Windows)
test/run.js                  `npm test` — the validator, then every discovered suite
test/validate.py             registry / submodules / version validation
.github/workflows            validation on push and PR + tag-driven release

skills.json is the source of truth — repos, submodule paths, plugin ids, skill names, default agents and each skill's pinned version. The validator keeps it in sync with .gitmodules and with the submodule pointers, so checking out any hub commit installs exactly the skill versions that commit was tested with.

Development

npm test

One entry point: it runs the structural validator, then discovers every test/*_test.js rather than listing them — a list would live in package.json and in the workflow at once, and drift the first time a suite was added to one side. An empty run fails rather than reporting green, and validate.py fails if CI stops calling npm test.

python3 test/check_pins.py
pip install tiktoken && python3 test/audit_bundle.py

Both kept out of npm test on purpose — it must work offline and dependency-free. check_pins.py queries the npm registry — every member's pin, and since v0.87.0 this repository's own newest tag, because a tag that never published is silent everywhere else; audit_bundle.py needs a tokenizer and refuses to estimate without one. The audit reports the always-on token budget, bodies against their cap, trigger-phrase collisions and the installed routing block against the registry.

The site

node scripts/site.js --out _site

One page per member plus the routing page, built from skills.json and lib/routers-registry.js — the same two files the launcher and the operator's routing block are generated from. The site restates nothing: a version, an install identifier or a routing rule on a published page is read at build time, so it cannot drift from the manifest.

The built site is never committed. .github/workflows/pages.yml gates on the whole validate suite through workflow_call, then builds and deploys — a generated page in git drifts from the data it claims to render, and a page built from the tree cannot. test/site_test.js is the half that runs here: it builds into a temp directory and fails when a stated version is not the pinned one, when an internal address does not resolve, when a page hands the reader a launcher command bin/sshlg-skills.js does not dispatch, or when anything on the page would be fetched from another host.

Releases are tag-driven: bump skills.json, package.json and the top CHANGELOG.md entry together, tag vX.Y.Z, and the release workflow cuts the GitHub release from the matching changelog section.

Contributions are welcome — see CONTRIBUTING.md. To report a vulnerability, see SECURITY.md.

Author

Built by ssheleg — sshlg.me

License

MIT © 2026 ssheleg.