Help us improve
Share bugs, ideas, or general feedback.
From trails
Build with the Trails framework — define trail contracts, wire CLI/MCP trailheads, test with examples, debug errors, migrate codebases, run governance. Use when creating trails, adding trailheads, testing, debugging Trails errors, migrating to Trails, running warden, or any work involving @ontrails/* packages.
npx claudepluginhub outfitter-dev/trails --plugin trailsHow this skill is triggered — by the user, by Claude, or both
Slash command
/trails:trailsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Contract-first TypeScript framework. Define a trail once with typed input, Result output, examples, and metadata — then trailhead it on CLI, MCP, HTTP, or WebSocket without drift.
examples/cli-command.mdexamples/composition.mdexamples/express-handler.mdexamples/mcp-tool.mdexamples/patterns.mdreferences/architecture.mdreferences/cli-surface.mdreferences/common-pitfalls.mdreferences/contract-patterns.mdreferences/error-taxonomy.mdreferences/getting-started.mdreferences/mcp-surface.mdreferences/migration-checklist.mdreferences/testing-patterns.mdtemplates/composition.mdtemplates/trail.mdBuilds end-to-end type-safe tRPC APIs with routers, procedures, middleware, subscriptions, and Next.js/React integration for TypeScript full-stack apps.
Guides tRPC end-to-end typesafe API development with router architecture, procedure design, Zod input validation, middleware chaining via unstable_pipe, TRPCError handling, and Vertical Slice pattern.
Designs standardized development workflows using golden path patterns, paved roads, opinionated defaults, templates, and guardrails to optimize onboarding and productivity.
Share bugs, ideas, or general feedback.
Contract-first TypeScript framework. Define a trail once with typed input, Result output, examples, and metadata — then trailhead it on CLI, MCP, HTTP, or WebSocket without drift.
// 1. Define a trail
const greet = trail('greet', {
input: z.object({ name: z.string().describe('Who to greet') }),
output: z.object({ message: z.string() }),
intent: 'read',
examples: [{ name: 'Basic', input: { name: 'World' }, expected: { message: 'Hello, World!' } }],
blaze: (input) => Result.ok({ message: `Hello, ${input.name}!` }),
});
// 2. Collect into topo
const app = topo('myapp', greetModule);
// 3. Blaze on trailheads
trailhead(app); // CLI — from @ontrails/cli/commander
await trailhead(app); // MCP — from @ontrails/mcp
// 4. Headless execution (no trailhead needed)
const result = await run(app, 'greet', { name: 'Alice' });
// 5. Test
testAll(app); // Examples + governance in one line
Use these terms — they are non-negotiable in Trails codebases.
| Term | Meaning | Not this |
|---|---|---|
trail | Unit of work (atomic or composite) | handler, action |
cross | Composition declaration and runtime verb | workflow, route |
topo | Trail collection | registry |
blaze | Open trails on a trailhead | serve, mount |
trailhead | Transport connector (CLI, MCP, HTTP) | transport |
metadata | Trail annotations | tags |
warden | Governance enforcement | linter |
(input, ctx) => Result. Default choice.crosses: [...], uses ctx.cross().Dotted, lowercase, verb-last: entity.show, math.add, search. Dots become CLI subcommands and MCP tool name segments.
Every field gets .describe() — this becomes --help text, MCP descriptions, and form labels.
input: z.object({
name: z.string().describe('Entity name to look up'),
limit: z.number().default(20).describe('Maximum results'),
})
Required for MCP and HTTP trailheads. Define what Result.ok returns.
| Field | Effect |
|---|---|
intent: 'read' | Safe, no side effects. MCP: readOnlyHint. |
intent: 'destroy' | Irreversible. CLI: auto-adds --dry-run. MCP: destructiveHint. |
idempotent: true | Safe to retry. |
Each example is both documentation AND a test case:
expected: { ... } — deep equalserror: 'NotFoundError' — asserts error typeSee contract-patterns.md for detailed patterns. Copy from trail.md or composition.md.
Adding a trailhead is a trailhead() call, not an architecture change. The framework derives everything from the trail contract.
CLI: Flags from Zod, subcommands from dotted IDs, exit codes from error taxonomy.
import { trailhead } from '@ontrails/cli/commander';
trailhead(app);
MCP: Tool names from trail IDs, JSON Schema from Zod, annotations from metadata.
import { trailhead } from '@ontrails/mcp';
await trailhead(app);
HTTP: Routes from trail IDs (dots become path segments), verbs from intent, error responses from taxonomy.
import { trailhead } from '@ontrails/http/hono';
await trailhead(app, { port: 3000 });
See the CLI trailhead docs, the MCP trailhead docs, and the HTTP trailhead docs for derivation details.
Provisions declare infrastructure dependencies — databases, API clients, caches — as first-class primitives alongside trails and events.
Define a provision with provision():
const db = provision('db.main', {
create: (svc) => Result.ok(openDatabase(svc.env?.DATABASE_URL)),
dispose: (conn) => conn.close(),
health: (conn) => conn.ping(),
mock: () => createInMemoryDb(),
});
The create factory receives ProvisionContext (env, cwd, workspaceRoot only — not the full TrailContext). Provisions are singletons, resolved once per process and cached.
Declare on trails with provisions: [...]:
const search = trail('search', {
provisions: [db],
input: z.object({ query: z.string() }),
output: z.array(z.object({ id: z.string(), title: z.string() })),
blaze: async (input, ctx) => {
const conn = db.from(ctx);
return Result.ok(await conn.search(input.query));
},
});
Access via db.from(ctx) (typed, preferred) or ctx.provision<Database>('db.main') (dynamic escape hatch).
Test with zero config — provisions with mock factories auto-resolve in testAll(app). Override explicitly when needed:
testAll(app, () => ({ provisions: { 'db.main': createSpecialTestDb() } }));
Governance: The warden enforces provision-declarations (usage matches declarations) and provision-exists (provision IDs resolve in the topo).
See contract-patterns.md for declaration patterns and testing-patterns.md for mock strategies.
testAll(app) runs the full governance suite in one line:
TDD workflow: Define trail with examples → run tests (red) → implement (green) → refactor.
Edge cases go in testTrail(trail, scenarios). Use createCrossContext() to mock ctx.cross for composite trail unit tests. Trailhead integration uses createCliHarness() / createMcpHarness().
See testing-patterns.md for the full testing API.
13 error classes, deterministic mapping to exit codes, HTTP status, and JSON-RPC codes:
| Category | Classes | Exit | HTTP | Retry |
|---|---|---|---|---|
| validation | ValidationError, AmbiguousError | 1 | 400 | No |
| not_found | NotFoundError | 2 | 404 | No |
| conflict | AlreadyExistsError, ConflictError | 3 | 409 | No |
| permission | PermissionError | 4 | 403 | No |
| timeout | TimeoutError | 5 | 504 | Yes |
| rate_limit | RateLimitError | 6 | 429 | Yes |
| network | NetworkError | 7 | 502 | Yes |
| internal | InternalError, AssertionError | 8 | 500 | No |
| auth | AuthError | 9 | 401 | No |
| cancelled | CancelledError | 130 | 499 | No |
Use the most specific class. Return Result.err(new XError(...)), never throw.
See error-taxonomy.md for constructor signatures and patterns. See common-pitfalls.md for anti-patterns.
Converting existing code to Trails:
testAll()See migration-checklist.md for the detailed checklist.
The warden enforces conventions and detects drift:
trails warden # Convention checks
trails warden --drift # Contract drift vs lock file
Key rules: no throw in blaze functions, no trailhead imports, crosses declarations match ctx.cross() calls, provision declarations match db.from(ctx) / ctx.provision() calls, output schemas present, .describe() on fields.
| Reference | Content |
|---|---|
| getting-started.md | Full install-to-test walkthrough |
| architecture.md | Hexagonal model, package gates, data flow |
| contract-patterns.md | ID naming, schema design, example authoring |
| CLI trailhead docs | Flag derivation, output modes, exit codes |
| MCP trailhead docs | Tool naming, annotations, progress |
| testing-patterns.md | testAll, testTrail, harnesses |
| error-taxonomy.md | All 13 error classes with signatures |
| common-pitfalls.md | 9 anti-patterns with fixes |
| migration-checklist.md | Step-by-step conversion guide |
| trail.md | Annotated trail skeleton |
| composition.md | Annotated composite trail skeleton |
| patterns.md | Before/after: common transformation patterns |
| express-handler.md | Before/after: Express routes → trails |
| cli-command.md | Before/after: Commander commands → trails |
| mcp-tool.md | Before/after: MCP tool handlers → trails |
| composition.md | Before/after: direct calls → ctx.cross |