skill-tree
v0.1.4
Published
Library for managing agent skill versions and evolution - extract, iterate, and adapt skills from agent trajectories
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.
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
- 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',
problem: 'TypeScript with ES modules requires explicit .js extensions',
triggerConditions: [{ type: 'error', value: "Cannot find module './utils'" }],
solution: 'Add .js extension to all relative imports.',
verification: 'Run tsc --noEmit to verify no import errors',
examples: [],
author: 'me',
tags: ['typescript', 'esm'],
createdAt: new Date(),
updatedAt: new Date(),
status: 'active',
metrics: { usageCount: 0, successRate: 0, feedbackScores: [] },
} 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
name: string; // Human-readable name
version: string; // Semantic version
description: string; // For search and matching
problem: string; // What problem this solves
triggerConditions: TriggerCondition[]; // When to apply
solution: string; // Step-by-step solution
verification: string; // How to verify it worked
examples: SkillExample[]; // Usage examples
tags: string[]; // Categorization
status: SkillStatus; // 'draft' | 'active' | 'deprecated' | 'experimental'
metrics: SkillMetrics; // Usage tracking
// ... namespace, taxonomy, lineage
}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',
});AGENTS.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 (OpenSkills-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
---
## Problem
TypeScript with ES modules requires explicit .js extensions in imports.
## Trigger Conditions
- **error**: `Cannot find module './utils'`
- **pattern**: `import .* from '\./[^']+(?<!\.js)'`
## Solution
1. Add `.js` extension to relative imports
2. Even for `.ts` files, use `.js` in the import path
## Verification
Run `tsc` and verify no module resolution errors.License
MIT
