Help us improve
Share bugs, ideas, or general feedback.
Build with the Trails framework — contract-first trails, surfaces, testing, and governance.
npx claudepluginhub outfitter-dev/trailsBuild with the Trails framework — contract-first trails, trailheads, testing, and governance for agent-assisted development.
Claude Code marketplace entries for the plugin-safe Antigravity Awesome Skills library and its compatible editorial bundles.
Production-ready workflow orchestration with 83 marketplace plugins, 191 local specialized agents, and 155 local skills - optimized for granular installation and minimal token usage
Directory of popular Claude Code extensions including development tools, productivity plugins, and MCP integrations
Share bugs, ideas, or general feedback.
Define once. Trailhead everywhere.
Trails is a contract-first TypeScript framework. Define a trail — typed input, Result output, examples, intent — and the framework projects it onto CLI, MCP, HTTP, or WebSocket. One definition, every trailhead, zero drift.
Claude Code — add the marketplace, then install the plugin:
claude plugin marketplace add outfitter-dev/trails
claude plugin install trails@trails
Codex, Cursor, and others — install the skill:
npx skills outfitter-dev/trails
The skill gives your agent the full Trails reference: vocabulary, patterns, error taxonomy, trailhead wiring, testing, and before/after migration examples.
bunx @ontrails/trails create
Follow the prompts — pick a name, choose a starter, select your trailheads. The scaffolder generates a working project with trails, a topo, trailhead wiring, and tests.
Or install manually:
bun add @ontrails/core @ontrails/cli commander zod
bun add -d @ontrails/testing
app.get('/api/projects/:id', async (req, res) => {
try {
const project = await db.projects.findById(req.params.id);
if (!project) {
return res.status(404).json({ error: 'Project not found' });
}
res.json(project);
} catch (err) {
console.error('Failed to fetch project:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
const show = trail('project.show', {
input: z.object({ id: z.string().describe('Project ID') }),
output: projectSchema,
intent: 'read',
examples: [
{ name: 'Found', input: { id: 'p_1' }, expected: { id: 'p_1', name: 'Acme' } },
{ name: 'Missing', input: { id: 'p_0' }, error: 'NotFoundError' },
],
blaze: async (input) => {
const project = await db.projects.findById(input.id);
if (!project) return Result.err(new NotFoundError(`Project ${input.id} not found`));
return Result.ok(project);
},
});
Same logic. But now the framework derives:
myapp project show --id p_1 with --help text, exit code 2 for not-foundmyapp_project_show with JSON Schema input, readOnlyHint annotationtestAll(app) validates the happy path and the error pathYou authored the contract. The framework did the rest.
Each declaration you add to a trail unlocks derived behavior across every trailhead:
| You add | You get for free |
|---|---|
input (Zod schema) | CLI flags + --help text, MCP JSON Schema, input validation |
output (Zod schema) | Contract tests, MCP response typing, trailhead map entries |
intent: 'read' | MCP readOnlyHint, CLI skips confirmation, HTTP GET |
intent: 'destroy' | MCP destructiveHint, CLI auto-adds --dry-run, HTTP DELETE |
examples | Tests (happy + error path), agent guidance, documentation |
crosses | Composition graph, cycle detection, cross coverage in tests |
provisions: [db] | Singleton lifecycle, test mock auto-resolution, warden governance |
detours | Recovery paths, warden validates targets exist |
The value isn't any single feature. It's that they multiply — each declaration makes every trailhead smarter without additional wiring.
import { trail, topo, Result } from '@ontrails/core';
import { trailhead } from '@ontrails/cli/commander';
import { z } from 'zod';
// 1. Define trails
const greet = trail('greet', {
input: z.object({ name: z.string().describe('Who to greet') }),
output: z.object({ message: z.string() }),
intent: 'read',
blaze: (input) => Result.ok({ message: `Hello, ${input.name}!` }),
});
// 2. Collect into topo
const app = topo('myapp', { greet });
// 3. Open trailheads in any connector
trailhead(app); // CLI
// await trailhead(app); // MCP — same trails, same run function
$ myapp greet --name World
{ "message": "Hello, World!" }