Skip to main content

trails

Build with the Trails framework — define trail contracts, open CLI/MCP surfaces, test with examples, debug errors, migrate codebases, run governance. Use when creating trails, adding surfaces, testing, debugging Trails errors, migrating to Trails, running warden, or any work involving @ontrails/* packages.

Informações da origem

Repositório
outfitter-dev/trails
Última atividade na origem
23 de setembro de 2026 às 23:14
Idioma detectado do SKILL.md
inglês
Estrelas
5
Forks
1

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
20 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
description
Build with the Trails framework — define trail contracts, open CLI/MCP surfaces, test with examples, debug errors, migrate codebases, run governance. Use when creating trails, adding surfaces, testing, debugging Trails errors, migrating to Trails, running warden, or any work involving @ontrails/* packages.
license
MIT
metadata
{"skillset.schema":"1","trails":"0.2.3","version":"0.4.0"}
name
trails
# Trails Contract-first TypeScript framework. Define a trail once with typed input, Result output, examples, meta, and an implementation that establishes how it runs — then surface it on CLI, MCP, or HTTP today, with WebSocket planned on the same contract-first model. ## Quick Start ```typescript // 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!' } }], implementation: (input) => Result.ok({ message: `Hello, ${input.name}!` }), }); // 2. Collect into topo const graph = topo('myapp', greetModule); // 3. Open surfaces await surface(graph); // CLI — from @ontrails/commander // await surface(graph); // MCP — from @ontrails/mcp // await surface(graph, { port: 3000 }); // HTTP — from @ontrails/hono or @ontrails/http/bun // 4. Headless execution (no surface needed) const result = await run(graph, 'greet', { name: 'Alice' }); // 5. Test testAll(graph); // Examples + contract suite in one line ``` ## Lexicon Use these terms — they are non-negotiable in Trails codebases. | Term | Meaning | Not this | |------|---------|----------| | `trail` | Unit of work (atomic or composite) | handler, action | | `compose` | Composition declaration and runtime verb | workflow, route | | `topo` | Queryable graph of trails, signals, resources, and relationships | registry, collection | | `implementation` | Authored behavior that establishes how a trail runs from validated input to Result | handler, impl | | `surface` | The boundary-owned one-liner that opens a graph | serve, mount | | `graph` | Local name for a topo instance | app, registry | | `derive` | Produce canonical facts or an intermediate representation from authored truth | operation | | `render` | Present derived facts through a surface or format | operation | | `meta` | Trail annotations and ownership data | tags, metadata | | `warden` | Governance enforcement | linter | ## Package Orientation Current public packages are lockstep at the same Trails framework version. - **Core model:** `@ontrails/core` owns Result, errors, trail/signal/entity/topo contracts, resources, layers, execution, validation, and adapter ports. - **Surfaces:** `@ontrails/commander`, `@ontrails/mcp`, `@ontrails/hono`, and `@ontrails/http/bun` open the same topo on CLI, MCP, Hono HTTP, or Bun-native HTTP. `@ontrails/http` owns shared route derivation, OpenAPI, and the Web Fetch kernel; `@ontrails/cli` owns the framework-agnostic CLI command model; `@ontrails/vite` adapts Trails surfaces for Vite projects. - **Infrastructure:** `@ontrails/config`, `@ontrails/permits`, `@ontrails/store`, and `@ontrails/drizzle` cover config, authorization, schema-derived stores, and Drizzle SQLite bindings. - **Observability:** `@ontrails/observability` defines sink contracts; its `/dev` and `/otel` subpaths provide developer-state and OTel support, while `/logtape` and `/pino` are temporary sink adapters. - **Ecosystem:** `@ontrails/testing` provides contract tests and surface harnesses; `@ontrails/topography` owns TopoGraphs, semantic diffing, lock manifests, topo-store persistence, and Wayfind graph-read query APIs; `@ontrails/warden` owns governance rules. - **Stable install policy:** Normal 0.x releases use the npm `latest` tag. Use the exact package pins in [Getting Started](references/getting-started.md). The 0.x line permits documented API changes in minor releases; patches preserve compatibility. Existing 1.0 beta ranges require an explicit manifest migration. ## Agent Wayfinding When saved Topography artifacts can answer a graph question, use Wayfinder before raw text search: ```bash trails wayfind --overview --root-dir . --json trails wayfind --trails --intent read --root-dir . --json trails wayfind <trail-id> --contract --root-dir . --json trails wayfind <trail-id> --deps --root-dir . --json trails wayfind <trail-id> --impact --map --root-dir . --json trails schema wayfind ``` - Start with `trails wayfind --overview` to learn artifact source, freshness, and graph counts. - Use the selected operator CLI shape for filtered discovery: `trails wayfind --trails`, `--resources`, `--signals`, `--surfaces`, `--trailheads`, `--entities`, `--errors`, or `--adapter <package>`. Attach bounded related facts with `--include adapters`, `errors`, `examples`, `surfaces`, or `versions`. Topography may expose graph-query APIs beyond the operator CLI/MCP selection; check `trails schema wayfind` before constructing shell calls. - Use `trails schema <command...>` when you need accepted CLI routes, aliases, flags, and schemas before constructing shell calls. - Use `wayfind.describe` for a full saved entity record and `wayfind.contract` for a trail or version input/output/intent summary. - Use `wayfind.nearby`, `wayfind.impact`, and `wayfind.diff` for relation context, blast-radius reads, and explicit saved-baseline comparison. - Use `trails wayfind query "<phrase>"` for indexed text queries. Treat Wayfinder as graph-read only; do not assume semantic search, signposts, or implications exist in v0. Wayfinder trails are internal by default. Host apps expose selected queries deliberately, usually as read-only operator tools or MCP resources protected by the host's authorization boundary. Fall back to `rg`, qmd, source reads, or a fresh compile when Wayfinder reports missing or stale artifacts, when the task needs source code that Topography does not derive, or when writing artifacts is outside your current authority. ## Creating Trails ### Atomic vs Composite Trails - **Atomic trail**: does one thing. `(input, ctx) => Result`. Default choice. - **Composite trail**: composes other trails. Declares `composes: [...]`, uses `ctx.compose()`. - **Runnable trail**: an authored contract with an implementation. The runtime runs trails, not implementations. ### Trail ID Conventions Dotted, lowercase, verb-last: `entity.show`, `math.add`, `search`. Dots become CLI subcommands and MCP tool name segments. ### Input Schema Every field gets `.describe()` — this becomes `--help` text, MCP descriptions, and form labels. ```typescript input: z.object({ name: z.string().describe('Entity name to look up'), limit: z.number().default(20).describe('Maximum results'), }) ``` ### Output Schema Required for MCP and HTTP surfaces. Define what Result.ok returns. ### Intent and Flags | 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. | ### Examples Each example is both documentation AND a test case: - **Full match**: `expected: { ... }` — deep equals - **Schema-only**: no expected — validates against output schema - **Error match**: `error: 'NotFoundError'` — asserts error type See [contract-patterns.md](references/contract-patterns.md) for detailed patterns. Copy from [trail.md](templates/trail.md) or [composition.md](templates/composition.md). ## Surfaces Adding a surface is a `surface()` 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. ```typescript import { surface } from '@ontrails/commander'; await surface(graph); ``` Use `cli` on a trail only for canonical command overrides or trail-owned aliases that still normalize into the same trail contract. String aliases are sibling leaf aliases (`find` beside `search`); string-array aliases are absolute command paths (`['wf', 'search']`). Author app-owned compatibility routes in an exported `trailsOverlays = [surfaceOverlay({ cli: { 'wf.search': 'wayfind.search' } })]` in the app module, then pass `trailsOverlays` to the CLI surface's `overlays` option. Compile, validate, Wayfinder, and `trails schema` then inspect the same routes the runtime CLI accepts. See [CLI surface](references/cli-surface.md). Treat aliases, future input mappings, and trailheads as **surface accommodations**: render-level fit adjustments, not alternate behavior. The trail stays the capability. A surface entry is the invocable affordance on a surface; an approach is the way a caller reaches it. Aliases add alternate approaches to the same trail, input mappings normalize surface-shaped input into the same trail input, and trailheads group several trails into one entry while preserving the selected trail ID. Use the ADR-0050 test: if the fit would change intent, permits, errors, outputs, lifecycle, side effects, or hide which trail is running, call it a trail fork and author a distinct or composing trail instead. Classify surface-fit work before editing: | Shape | Classification | | --- | --- | | One trail, another path, no input reshape | Alias | | One trail, surface-shaped input that normalizes honestly | Input mapping | | Many trails, one grouped entry, member trail identity preserved | Trailhead | | Different intent, permits, errors, outputs, lifecycle, side effects, or hidden member identity | Distinct trail or composing trail | **MCP**: Tool names from trail IDs, JSON Schema from Zod, annotations from intent, idempotency, and description. ```typescript import { surface } from '@ontrails/mcp'; await surface(graph); ``` Dense MCP surfaces may use **trailheads** to group related trails into fewer agent-facing tools. A trailhead is surface rendering configuration, not a core `Facet` primitive and not a new domain operation. It groups and selects without merging. Author it in MCP surface options, call it with `{ trail, input }`, and expect successful results as `{ trail, output }` so the underlying trail stays visible. ```typescript await surface(graph, { trailheads: { governance: { description: 'Run project diagnostics and Warden guidance.', mcp: { loading: 'deferred' }, trails: ['doctor', 'warden', 'warden.guide'], }, }, mcpResources: { examples: true, surfaceMap: true }, }); ``` Use `trails://surface-map` and per-trail MCP resources for cold context before guessing at grouped affordances. Adapter-kit may validate resolved derived evidence for future surface adapters, but it does not define or author trailheads. Do not invent `facet()`, `overlapsWith`, or adapter-kit `facet` config. **HTTP**: Routes from trail IDs (dots become path segments), verbs from intent, error responses from taxonomy. Use Hono for framework portability or Bun-native HTTP when you want Bun serving without a third-party runtime; both share the `@ontrails/http` route/fetch kernel. ```typescript import { surface } from '@ontrails/hono'; await surface(graph, { port: 3000 }); ``` ```typescript import { surface } from '@ontrails/http/bun'; await surface(graph, { port: 3000 }); ``` WebSocket is planned, not shipped. See the CLI surface docs, the MCP surface docs, and the HTTP package docs for derivation details. ## Resources Resources declare infrastructure dependencies — databases, API clients, caches — as first-class primitives alongside trails and signals. **Define** a resource with `resource()`: ```typescript const db = resource('db.main', { create: (resourceCtx) => Result.ok(openDatabase(resourceCtx.env?.DATABASE_URL)), dispose: (conn) => conn.close(), health: (conn) => conn.ping(), mock: () => createInMemoryDb(), }); ``` The `create` factory receives `ResourceContext` (`env`, `cwd`, `workspaceRoot`, and validated `config` when the resource declares a config schema — not the full `TrailContext`). Resources are singletons, resolved once per process and cached. **Declare** on trails with `resources: [...]`: ```typescript const search = trail('search', { resources: [db], input: z.object({ query: z.string() }), output: z.array(z.object({ id: z.string(), title: z.string() })), implementation: 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.resource<Database>('db.main')` (dynamic escape hatch). **Test** with zero config — resources with `mock` factories auto-resolve in `testAll(graph)`. Mark live-only dependencies with `unmockable: { reason }` and provide explicit overrides for examples or contracts that need them. ```typescript testAll(graph, () => ({ resources: { 'db.main': createSpecialTestDb() } })); ``` **Governance:** The warden enforces `resource-declarations` (usage matches declarations) and `resource-exists` (resource IDs resolve in the topo). See [contract-patterns.md](references/contract-patterns.md) for declaration patterns and [testing-patterns.md](references/testing-patterns.md) for mock strategies. ## Testing `testAll(graph)` runs the full contract suite in one line: 1. Topo validation (composes, schemas, signals, resources) 2. Example execution (every example as an assertion) 3. Contract checks (output matches schema) 4. Detour verification (targets exist) **TDD workflow**: Define trail with examples → run tests (red) → implement (green) → refactor. Edge cases go in `testTrail(trail, scenarios)`. Use `createComposeContext()` to mock `ctx.compose` for composite trail unit tests. Surface integration uses `@ontrails/testing/cli`, `@ontrails/testing/mcp`, `@ontrails/testing/http`, and `@ontrails/testing/surface-parity`. See [testing-patterns.md](references/testing-patterns.md) for the full testing API. ## Error Taxonomy 17 fixed-category error classes across 10 categories, plus the dynamic `RetryExhaustedError` wrapper, with 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, VersionNotSupportedError | 2 | 404 | No | | conflict | AlreadyExistsError, ConflictError | 3 | 409 | No | | permission | PermissionError, PermitError | 4 | 403 | No | | timeout | TimeoutError | 5 | 504 | Yes |
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub