A composition layer for combining agent skills into higher-level capabilities.
Skill ecosystems like skills.sh provide atomic, reusable capabilities for AI agents: fetch a URL, parse HTML, extract data. But real-world tasks rarely map to single skills.
You don't just "fetch a webpage"—you fetch, parse, extract, and transform. Every time.
Today, this composition happens in one of three ways:
- Hardcoded in prompts — Brittle, not reusable
- Embedded in agent code — Opaque, hard to share
- Repeated across projects — Wasteful, inconsistent
combo-skills makes composition explicit, declarative, and reusable.
This is not "Tailwind for skills." We're not creating thousands of atomic utility skills.
combo-skills works with existing skill ecosystems. It does not replace them. Skills remain black boxes—we don't inspect their internals or standardize their interfaces.
We also don't provide a runtime. combo-skills produces artifacts. Execution is your agent framework's responsibility.
# extract-table-from-web.combo.yaml
name: extract-table-from-web
description: Extract table data from a web page as CSV
skills:
- name: fetch-webpage
from: skills.sh
- name: parse-html
from: skills.sh
- name: extract-table
from: skills.sh
- name: convert-to-csv
from: skills.sh
intent: |
Fetch a web page, locate table elements, extract their data,
and produce CSV output. Handle pagination if present.
constraints:
ordering:
- fetch-webpage -> parse-html -> extract-table -> convert-to-csvpnpm combo-skills compile extract-table-from-web.combo.yamloutput/extract-table-from-web/
├── SKILL.md # Complete skill definition
├── metadata.json # Compilation info
└── examples/
└── basic.md # Usage examples
The output is a standard skill artifact, compatible with skills.sh and similar ecosystems.
pnpm add combo-skillsOr use directly with pnpm:
pnpm dlx combo-skills compile my-skill.combo.yaml# Compile a combo skill
combo-skills compile examples/extract-table-from-web.combo.yaml
# Compile with custom output directory
combo-skills compile my-skill.combo.yaml --output ./dist
# Validate without compiling
combo-skills validate my-skill.combo.yaml
# Verbose output
combo-skills compile my-skill.combo.yaml --verboseimport { loadComboSkill, compile } from 'combo-skills';
const combo = await loadComboSkill('my-skill.combo.yaml');
const result = await compile(combo, {
outputDir: './output',
verbose: true,
});
if (result.success) {
console.log(`Compiled: ${result.skill.name}`);
}Compilation is synthesis, not transformation.
When you compile a combo skill:
- Validate — Check the definition against the schema
- Resolve — Fetch skill metadata from registries
- Graph — Build and validate the dependency graph
- Synthesize — Generate a coherent skill artifact
The synthesis step is LLM-driven by default. This means:
- Output is non-deterministic
- Two compilations may produce different (but equivalent) skills
- The goal is coherence and usability, not byte-for-byte reproducibility
If you need determinism, commit your compiled artifacts to version control.
Combo skills are defined in YAML or JSON:
| Field | Required | Description |
|---|---|---|
name |
Yes | Unique skill identifier |
description |
Yes | Human-readable description |
skills |
Yes | List of skills to compose |
intent |
Yes | Natural language composition goal |
constraints |
No | Ordering and data flow rules |
metadata |
No | Author, tags, license |
See docs/combo-skills.md for the complete specification.
- Skills are black boxes — We don't require understanding skill internals
- Intent over implementation — Describe what, not how
- Compilation is synthesis — LLM-driven by design
- Determinism is opt-in — Embrace non-determinism, version outputs
Read docs/vision.md for the full design philosophy.
combo-skills/
├── cli/ # CLI entry point
├── docs/ # Documentation
│ ├── vision.md # Design philosophy
│ ├── combo-skills.md # Combo skill specification
│ └── compilation.md # Compilation process
├── examples/ # Example combo skills
├── schemas/ # JSON Schema for validation
└── src/
├── compiler/ # Compilation logic
│ ├── llmCompiler.ts # LLM-driven synthesis
│ └── graphResolver.ts # Dependency resolution
├── registry/ # Skill resolution
│ └── skillsResolver.ts
├── types.ts # TypeScript types
└── index.ts # Library entry point
This is an early release. The following features are stubbed:
- LLM integration (uses placeholder generation)
- Registry resolution (returns placeholder metadata)
- Schema validation (basic field checking only)
Contributions welcome.
MIT