skill-tree
v0.2.0
Published
Library for managing agent skill versions and evolution - extract, iterate, and adapt skills from agent trajectories
Downloads
356
Maintainers
Readme
skill-tree
A TypeScript library for managing agent skill versions and evolution. Store, version, serve, sync, and federate reusable skills for AI agents.
Metrics ownership (as of 0.2.0)
skill-tree does not track per-skill usage metrics. The Skill shape no
longer carries metrics — historical fields like usageCount, successRate,
lastUsed, feedbackScores lived on it but were never updated by any code
path in skill-tree, and the snapshots that consumers wrote at publish time
drifted from the live data.
Live usage tracking belongs to systems that observe agents running:
- cognitive-core (
playbook.evolution.successCount/failureCount,playbook.confidence) is the canonical home.recordSuccessandrecordFailuremutate it on every trajectory.
If you need ranked loadouts driven by live metrics, query that system
first, then pass the resulting skill IDs to skill-tree's loadout compile
via include: [...]. As of 0.2, include is a presence guarantee —
every ID listed is in the result regardless of other filters, in the
order specified. Combine with maxSkills: include.length for "exactly
these N skills" semantics.
skill-tree's LoadoutCriteria no longer supports minSuccessRate or
priorityOrder: 'usage' | 'successRate' | 'recent' — the only
recognized priorityOrder value is 'relevance', currently a no-op
pending semantic ranking.
See CHANGELOG.md for the full migration guide.
Overview
skill-tree helps you build and maintain a library of reusable skills for AI agents by:
- Storing skills in a versioned, searchable format (OpenSkills-compatible)
- Evolving skills through versioning, forking, and merging
- Serving skills via dynamic loadouts with expand/collapse, profiles, and token budgeting
- Materializing skills to agent-discoverable paths (
.claude/skills/,.agent/skills/) with auto-generated AGENTS.md - Syncing skills across agents with git-based multi-agent collaboration
- Federating skills across repositories for cross-organization sharing
Inspired by research on skill libraries (arXiv:2512.17102), Claudeception, and OpenSkills.
Installation
npm install skill-treeQuick Start
import { createSkillBank, type Skill } from 'skill-tree';
// Create a skill bank with filesystem storage
const bank = createSkillBank({
storage: { basePath: './skills' },
});
await bank.initialize();
// Save a skill
await bank.saveSkill({
id: 'typescript-esm-import-fix',
name: 'TypeScript ESM Import Fix',
version: '1.0.0',
description: 'Fix TypeScript ES module import errors',
instructions: 'Add .js extension to all relative imports, even for .ts files.\n\nRun `tsc --noEmit` to verify no import errors.',
author: 'me',
tags: ['typescript', 'esm'],
createdAt: new Date(),
updatedAt: new Date(),
status: 'active',
} as Skill);
// Search for skills
const skills = await bank.searchSkills('typescript');
// Create a new version
const updated = await bank.createVersion('typescript-esm-import-fix', {
solution: 'Add .js extension, or use "moduleResolution": "bundler".',
}, { bumpType: 'minor' });
await bank.shutdown();Core Concepts
Skills
A skill represents a reusable piece of knowledge:
interface Skill {
id: string; // Unique identifier (kebab-case)
name: string; // Human-readable name
version: string; // Semantic version
description: string; // Short summary for discovery and matching
instructions: string; // Free-form markdown body (the SKILL.md content)
tags: string[]; // Categorization
status: SkillStatus; // 'draft' | 'active' | 'deprecated' | 'experimental'
// ... namespace, taxonomy, lineage, serving metadata
}Storage
Two backends:
// Filesystem (JSON source of truth + SQLite cache)
const bank = createSkillBank({
storage: { basePath: './skills' },
});
// Memory (for testing)
const bank = createSkillBank({
storage: { type: 'memory' },
});Skills are stored in the OpenSkills format:
skills/
├── my-skill/
│ ├── SKILL.md # Skill content (YAML + Markdown)
│ └── .skilltree.json # Metadata and lineage
└── .versions/
└── my-skill/
├── 1.0.0.json # Version snapshots
└── 1.1.0.jsonFeatures
Versioning
Semantic versioning with full lineage tracking:
// Create new version
const v2 = await bank.createVersion('my-skill', updates, {
bumpType: 'minor',
changelog: 'Added alternative solution',
});
// Get version history
const history = await bank.getVersionHistory('my-skill');
// Rollback to previous version
const restored = await bank.rollbackSkill('my-skill', '1.0.0');
// Compare versions
const diff = await bank.compareVersions('my-skill', '1.0.0', '2.0.0');
// Fork for a specialized use case
const forked = await bank.forkSkill('my-skill', {
newId: 'my-skill-react',
newName: 'My Skill (React variant)',
reason: 'Specialized for React projects',
});Serving Layer
Dynamic skill loadouts for agent context windows:
const { server } = await bank.createServingLayer({
maxExpanded: 5,
});
// Orchestrator sets loadout based on task
await server.setLoadoutForTask('Fix authentication bug');
// Or use a built-in profile
await server.setLoadoutFromProfile('debugging');
// Render skills into system prompt
const prompt = server.renderSystemPrompt();
// Agent-side API
const view = server.agentListLoadout();
const skill = server.agentExpandSkill('some-skill-id');
server.agentCollapseSkill('some-skill-id');
await server.agentRequestSkills(['another-skill']);Multi-Agent Sync
Git-based skill synchronization:
import { createDefaultSyncConfig } from 'skill-tree';
const bank = createSkillBank({
storage: { basePath: './skills' },
sync: {
config: createDefaultSyncConfig({
repoUrl: '[email protected]:org/skills.git',
agentId: 'agent-1',
}),
pullOnInit: true,
},
});
await bank.initialize(); // auto-pulls remote changes
// Manual sync
await bank.sync.push();
await bank.sync.pull();
const status = await bank.sync.status();
await bank.shutdown(); // flushes pending syncFederation
Connect independent skill repositories:
// Add a remote
await bank.federation.addRemote('team', {
url: '[email protected]:org/team-skills.git',
access: 'read-write',
});
// Browse remote skills
const remoteSkills = await bank.federation.browse('team');
// Import a skill
const result = await bank.federation.import('team', 'useful-pattern');
// Share a skill
await bank.federation.share('my-skill', 'team');
// Check for upstream updates
const updates = await bank.federation.checkUpstream();Events
Subscribe to skill bank events:
// Simple synchronous listeners
const unsubscribe = bank.on((event) => {
switch (event.type) {
case 'skill:created':
console.log('New skill:', event.skill.name);
break;
case 'skill:updated':
console.log('Updated:', event.skill.name, 'from', event.previousVersion);
break;
}
});
// Advanced: async hooks with priority and filtering
bank.getHookRegistry().register({
event: 'storage:after-save',
handler: async (context) => {
console.log('Skill saved to storage');
},
priority: 'normal',
});Materialization
Make skills discoverable by agents following the Agent Skills standard. Skills stored in .skilltree/ are symlinked to standard discovery paths and AGENTS.md is auto-regenerated on changes.
const bank = createSkillBank({
storage: { basePath: './my-project' },
materialization: {
enabled: true,
symlinkPaths: ['.claude/skills', '.agent/skills'],
agentsMdPath: './AGENTS.md',
agentsMdFormat: 'xml', // 'xml' | 'markdown' | 'json'
debounceMs: 500,
},
});
await bank.initialize();
// Symlinks created, AGENTS.md generated
await bank.saveSkill(mySkill);
// Symlinks updated, AGENTS.md regenerated automaticallyThis creates:
.claude/skills/
my-skill -> ../../.skilltree/skills/my-skill (symlink)
.agent/skills/
my-skill -> ../../.skilltree/skills/my-skill (symlink)
AGENTS.md (auto-generated with <!-- SKILLTREE_START/END --> markers)Agents activate skills on demand via the CLI:
# Read a skill to stdout (OpenSkills-compatible progressive disclosure)
skill-tree read my-skill
# Read multiple skills
skill-tree read skill-one,skill-twoAGENTS.md Integration
Bidirectional sync between skill bank and AGENTS.md files:
import { createAgentsSync } from 'skill-tree';
const sync = createAgentsSync(bank.getStorage());
const result = await sync.sync('./AGENTS.md', {
direction: 'bidirectional',
});Namespace Support
Multi-tier skill trees for team environments:
const bank = createSkillBank({
storage: { basePath: './skills' },
namespace: {
agentId: 'agent-1',
team: 'frontend',
defaultScope: 'personal',
defaultVisibility: 'private',
},
});
// List skills by scope
const personal = await bank.listSkills({ scope: 'personal', owner: 'agent-1' });
const teamSkills = await bank.listSkills({ scope: 'team', team: 'frontend' });API Reference
SkillBank
Main orchestrator class, created via createSkillBank(config).
| Method | Description |
|--------|-------------|
| initialize() | Initialize storage, federation, and sync |
| shutdown() | Clean shutdown (flushes sync) |
| getSkill(id, version?) | Get skill by ID |
| listSkills(filter?) | List skills with optional filter |
| searchSkills(query) | Full-text search |
| saveSkill(skill) | Save or update a skill |
| deleteSkill(id, version?) | Delete a skill |
| deprecateSkill(id) | Mark skill as deprecated |
| createVersion(id, updates, options?) | Create new version |
| forkSkill(id, options) | Fork a skill |
| getVersionHistory(id) | Get version history |
| getLineage(id) | Get full lineage |
| rollbackSkill(id, version) | Rollback to version |
| compareVersions(id, vA, vB) | Diff two versions |
| on(handler) | Subscribe to events (returns unsubscribe fn) |
| off(handler) | Unsubscribe from events |
| getStats() | Get skill bank statistics |
| exportAll() | Export all skills |
| importSkills(skills) | Bulk import skills |
| createServingLayer(config?) | Create a SkillGraphServer |
| getStorage() | Access underlying storage adapter |
| getHookRegistry() | Access hook registry |
| sync | SyncManager accessor (throws if not configured) |
| federation | FederationManager accessor (requires basePath) |
Versioning Utilities
import {
parseVersion,
compareVersions,
bumpVersion,
satisfiesRange,
} from 'skill-tree';
bumpVersion('1.2.3', 'minor'); // '1.3.0'
satisfiesRange('1.5.0', '^1.2.0'); // trueSkill Format
Skills use YAML frontmatter + Markdown body (Agent Skills compatible):
---
name: typescript-esm-import-fix
description: |
Fix TypeScript ES module import errors by adding .js extension
version: 1.0.0
author: extracted
status: active
date: 2024-01-15
tags:
- typescript
- esm
- imports
---
TypeScript with ES modules requires explicit .js extensions in imports.
1. Add `.js` extension to relative imports
2. Even for `.ts` files, use `.js` in the import path
Run `tsc` and verify no module resolution errors.License
MIT
