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 قبل أن تقرر التثبيت.

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

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
trails
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.
metadata
{"trails":{"version":"0.2.2"}}
# 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. ## Release Rules Feature work that changes publishable `@ontrails/*` package contents needs branch-local release intent before the PR leaves draft. The normal intent is a `.changeset/*.md` entry for each affected public package. `release:none` is a compatibility no-release override allowed only when the branch touches package files but truly has no user-visible package content change, and the PR, issue, or handoff explains why. Public trail contract changes are release facts. If a branch adds or removes a public trail, changes public visibility, changes an exposed trail's input schema or output schema, or changes surface exposure, add a changeset on the owning branch unless the branch has an explicit and reviewable `release:none` reason. Trail version entries and package semver are distinct: trail versions preserve capability contracts inside a topo, while package semver distributes framework bits through npm. In Graphite stacks, keep release intent branch-local. If the release check reports missing intent for a lower branch, check out that owning branch, add the changeset or no-release rationale there, restack, and re-run `bun run changeset:check` or `trails release check --json` upward. Do not hide lower-branch release gaps with a top-stack cleanup changeset. Good changeset prose names the user-visible change: "Expose `wayfind.contract` through the Trails operator CLI so agents can inspect saved input/output contracts before source reads." Good `release:none` rationale names the non-user-visible scope: "Only updates non-shipping test fixtures under `packages/core/src/__tests__`; no public package files or public trail contracts changed." A bare "internal" is not enough when public contracts, generated artifacts, package docs, or migrations move. During local review, classify missing branch-local release intent for a public trail contract fact as P2. Selected P3 release ideas, such as imported schema tracing or future release targets, should be logged for follow-up rather than folded into the current branch unless they expose a concrete user-visible release gap. ## Distribution-Ready Done Feature work is not complete until the surrounding developer experience is complete or explicitly marked not applicable. Treat docs, examples, guidance, governance, release intent, and migration notes as part of the implementation when the behavior reaches users or agents. Before moving work out of draft, check the affected surfaces: - **Docs and examples:** Update the nearest README, fieldguide, API doc, example, ADR, or runbook that teaches the behavior. - **Agent guidance:** Update skills, plugin guidance, AGENTS files, or tool-specific prompts when agents need a new rule, workflow, or vocabulary. - **Governance:** Add or update Warden rules, generated Warden guides, or drift checks when the behavior creates a governable boundary. - **Release path:** Add a branch-local changeset for publishable package changes, or carry an explicit no-release reason when the change is truly not user-visible. - **Wayfinder dogfood:** Run the Wayfinder dogfood smoke when changing framework surfaces, operator topo exposure, Topography artifacts, Wayfinder queries, or fresh app loading. - **Migration path:** Document compatibility windows, bridge commands, or intentional non-support when existing apps may need to move. - **Publication readiness:** Run publish checks for package-impacting work and record first-time package, dist-tag, registry, or auth considerations. Small internal refactors do not need ceremonial docs. They do need an explicit "not applicable" callout when a reviewer or future agent could reasonably expect docs, skills, changesets, or migration notes. ## 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']`). App-owned compatibility aliases belong in CLI surface options and should also be exported from the app module as `cliAliases` or `trailsCliAliases` so compile, validate, Wayfinder, and `trails schema` inspect the same routes the runtime CLI accepts. 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
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub