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.

معلومات المصدر

المستودع
outfitter-dev/trails
آخر نشاط في المصدر
٢٣ سبتمبر ٢٠٢٦ في ٢٣:١٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٥
التفرعات
١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
20 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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 |
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub