@kentcdodds/skills
by @kentcdodds
Store, retrieve, edit, version, and revert reusable agent skill documents via skill_list then skill_get.
- skills
- registry
- agents
- prompts
- knowledge
- versioning
- License
- MIT
- Published
- 7/21/2026
- Pinned commit
37d3aa9- Rating
- No ratings yet
- Adaptation effort
- —
- Forks
- 1
- Stars
- 0
One-click install
Install forks this package into your account and publishes it right away when it passes the standard package checks. This listing has not been reviewed by an admin.
Log in to install this package.
Stars
0 stars
Log in to star this package.
0 stargazers
Fork with your agent
Copy this prompt into your MCP-capable agent to fork and adapt the package safely.
Use Kody to fork the community package "@kentcdodds/skills" (listing id: 242f67ac-94d9-42a9-835b-1929e65aaf30). Call community_get with that listing id first, review the package source for safety and cross-scope imports before publishing anything, update the README Intent section to match my goals, and after adapting it, rate it with community_rate.
README
@kentcdodds/skills
Intent
A general-purpose skills registry for agents: store, retrieve, edit, version, and revert reusable skill documents (markdown) in package-owned durable storage via capability calls, no git flow needed. Agents discover skills with skill_list, load them with skill_get, and evolve them with skill_save — every overwrite is snapshotted so skill_history and skill_revert can recover earlier versions.
Forkers start with an empty registry: skill content lives in the package-owned durable storage bucket of each installation, not in the package source.
Skills you save also surface in Kody's general search tool and automatic context retrieval through a declared package retriever (see "Search integration" below), so agents discover them by name/description without knowing this package exists.
Quickstart
Import the exports directly — they read and write your registry from any context. Create a skill (first save requires name and description), then read it back:
import skillSave from 'kody:@kentcdodds/skills/skill-save'
import skillList from 'kody:@kentcdodds/skills/skill-list'
import skillGet from 'kody:@kentcdodds/skills/skill-get'
export default async function main() {
await skillSave({
id: 'my-skill',
path: 'SKILL.md',
content: '# My skill\n\nInstructions here…',
name: 'my-skill',
description: 'What this skill is for and when an agent should load it.',
})
const index = await skillList({})
const skill = await skillGet({ id: 'my-skill' })
return { index, skill }
}Version recovery:
import skillHistory from 'kody:@kentcdodds/skills/skill-history'
import skillRevert from 'kody:@kentcdodds/skills/skill-revert'
export default async function main() {
const history = await skillHistory({ id: 'my-skill', path: 'SKILL.md', limit: 10 })
return await skillRevert({ version_id: history[0].version_id })
}This works because the package resolves its bucket through packageStorage() from kody:runtime, which is bound to the declaring package's storage in every context — imported code reaches your registry data wherever it runs.
When to use packages.invokeChecked instead
A static import bundles a pinned snapshot of this package's code into your module — fast in-process calls, but you keep running that snapshot until your own package republishes. When you want the current published version resolved at call time (running in this package's own runtime, with full packageContext and package secrets), use dynamic invocation:
import { packages } from 'kody:runtime'
const skill = await packages.invokeChecked({
kodyId: 'skills',
exportName: './skill-get',
params: { id: 'my-skill' },
})Either way the data is the same bucket, isolated per user: forks and other accounts always see their own registry, never yours.
Exports
| Export | Input | Output |
|---|---|---|
./skill-list | none | Array<{ id, name, description, files: string[], updated_at }> — the agents' index |
./skill-get | { id, path? } | Without path: skill metadata + files: [{ path, content }]. With path: that one file { id, name, description, path, content, updated_at }. Error lists valid ids/paths if not found |
./skill-save | { id, path, content, name?, description? } (name/description required on first create) | { id, path, version_saved: boolean } — snapshots previous content before overwriting |
./skill-delete | { id, path? } | With path: deletes one file. Without: deletes the whole skill. Returns { id, deleted: 'file' | 'skill', path?, files_deleted, versions_saved } — everything deleted is snapshotted first |
./skill-history | { id, path?, limit? } | Array<{ version_id, path, replaced_at, content_length }> — metadata only, not full content |
./skill-revert | { version_id } | { id, path, version_id, version_saved: boolean } — restores that snapshot as current content, snapshotting what it replaces |
./skill-search | { query?, limit? } (retriever contract) | { results: [{ id, title, summary, details, score?, metadata }] } — powers the package retriever below |
. | none | Package overview with the recommended agent flow |
Search integration (retriever)
The package declares a retriever under package.json#kody.retrievers with scopes ["search", "context"]. Once installed and published, your skills surface in Kody's general search tool and automatic context retrieval by their own name and description — an agent searching for a task your skill covers finds the skill directly, with instructions to load full content via skill_get({ id }). Empty or broad queries return all skills; otherwise matching is case-insensitive over skill name + description. The retriever runs read-only against the registry storage and returns nothing on a fresh installation until you save your first skill.
Storage schema
Data lives in the package-owned durable SQLite bucket (created lazily on first use):
CREATE TABLE skills (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE skill_files (
skill_id TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (skill_id, path)
);
-- Every save/delete that replaces existing content appends the previous
-- content here first, so nothing is lost.
CREATE TABLE skill_versions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
skill_id TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT NOT NULL,
replaced_at TEXT NOT NULL
);Conventions
- Skill ids are lower-kebab-case (e.g.
my-writing-style). - Give each skill a
SKILL.mdentry file; add reference files alongside as needed. - Make
descriptioncomplete enough that an agent can decide fromskill_listalone whether to load the skill.
Report this listing
Log in to report this listing.