AgentSkillsCN

outfitter-fieldguide

为您梳理 @outfitter/* 包的路线图。涵盖模式、模板,以及与传输无关的 Handler 系统背后的思考。适用于与 @outfitter/*、Result 类型、Handler 合约、错误分类打交道,或当提及 Result、Handler、ValidationError、NotFoundError、OutfitterError,或诸如 contracts、cli、mcp、daemon、config、logging 等包名时使用。

SKILL.md
--- frontmatter
name: outfitter-fieldguide
version: 0.2.0
description: "Your trail map for @outfitter/* packages. Patterns, templates, and the thinking behind transport-agnostic handler systems. Use when working with @outfitter/*, Result types, Handler contract, error taxonomy, or when Result, Handler, ValidationError, NotFoundError, OutfitterError, or package names like contracts, cli, mcp, daemon, config, logging are mentioned."

Outfitter Field Guide

Your trail map for building with @outfitter/* packages. This guide covers the patterns, the templates, and—just as importantly—the why behind it all.

Why We Built This

We kept solving the same problems across projects: config loading, error handling, CLI output modes, MCP server boilerplate. Every tool needed the same foundation. So we extracted it.

Agents Are the New Users

The patterns assume you're building tools that agents will consume—structured output, typed errors, predictable behavior. Humans benefit too; agents just make the stakes clearer.

When an AI agent calls your CLI or MCP tool, it needs:

  • Structured output it can parse (JSON when piped, human-readable otherwise)
  • Typed errors with categories it can reason about (retry? abort? ask user?)
  • Predictable exit codes for scripting and automation
  • Consistent behavior across transport surfaces

The stack enforces these properties by design, not discipline.

Errors Are Data, Not Exceptions

Traditional error handling (throw/catch) loses context, breaks type safety, and makes control flow unpredictable. We treat errors as first-class data:

typescript
const error = new NotFoundError("user", "user-123");
error._tag;        // "NotFoundError" — for pattern matching
error.category;    // "not_found" — maps to exit code 2, HTTP 404
error.message;     // Human-readable
error.toJSON();    // Serializes cleanly for agents

Ten categories cover all failure modes. Each maps to exit codes (CLI) and HTTP status (API/MCP). Agents can make retry decisions without parsing error strings.

Tests First, Always

Tests define behavior; implementations follow. The workflow:

  1. Red: Write a failing test that defines expected behavior
  2. Green: Minimal code to pass
  3. Refactor: Improve while staying green

The test proves the behavior exists. A failing test proves it doesn't—yet.

One Definition, Many Derivations

Types, schemas, and contracts have exactly one source:

ConcernSource of TruthDerives
Input validationZod schemaTypeScript types, JSON Schema
Error categoriesErrorCategory typeExit codes, HTTP status
CLI flag namesHandler input typesMCP tool parameters

If two things must stay in sync, one derives from the other.

Bun-Native When Possible

Bun APIs before npm packages—faster, zero-dependency:

NeedUse
HashingBun.hash()
GlobbingBun.Glob
SemverBun.semver
ShellBun.$
SQLitebun:sqlite
UUID v7Bun.randomUUIDv7()

The Core Idea

Handlers are pure functions returning Result<T, E>. CLI and MCP are thin adapters over the same logic. Write the handler once, expose it everywhere.

typescript
type Handler<TInput, TOutput, TError extends OutfitterError> = (
  input: TInput,
  ctx: HandlerContext
) => Promise<Result<TOutput, TError>>;

This buys you:

  • Testability — Call the function directly, no transport mocking
  • Reusability — Same handler serves CLI, MCP, HTTP
  • Type Safety — Input, output, and error types are explicit
  • Composability — Handlers wrap handlers

The Package Landscape

Dependencies flow one direction: Foundation → Runtime → Tooling.

code
┌─────────────────────────────────────────────────────────────────┐
│                        TOOLING TIER                              │
│  @outfitter/testing   @outfitter/tooling                        │
└─────────────────────────────────────────────────────────────────┘
                              ▲
┌─────────────────────────────────────────────────────────────────┐
│                        RUNTIME TIER                              │
│  @outfitter/cli    @outfitter/mcp    @outfitter/daemon          │
│  @outfitter/config @outfitter/logging @outfitter/file-ops       │
│  @outfitter/state  @outfitter/index                             │
└─────────────────────────────────────────────────────────────────┘
                              ▲
┌─────────────────────────────────────────────────────────────────┐
│                       FOUNDATION TIER                            │
│  @outfitter/contracts    @outfitter/types                       │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  META: @outfitter/kit (version coordination across all tiers) │
└─────────────────────────────────────────────────────────────────┘
PackageWhat It DoesReach For When...
@outfitter/contractsResult types, errors, Handler contractAlways. This is the foundation.
@outfitter/typesType utilities, collection helpersYou need type manipulation
@outfitter/cliCommands, output modes, formattingBuilding CLI applications
@outfitter/mcpServer framework, tool registrationBuilding AI agent tools
@outfitter/configXDG paths, config loadingYou have configuration
@outfitter/loggingStructured logging, redactionYou need logging (you do)
@outfitter/daemonLifecycle, IPC, health checksBuilding background services
@outfitter/file-opsAtomic writes, locking, secure pathsFile operations that matter
@outfitter/statePagination, cursor statePaginated data
@outfitter/indexSQLite FTS5, WAL mode, BM25 rankingFull-text search indexing
@outfitter/testingTest harnesses, fixturesTesting (always)
@outfitter/toolingBiome, TypeScript, Lefthook presetsProject setup (dev dependency)
@outfitter/kitVersion coordination meta-packageEnsuring compatible versions

Trail Map: Designing a System

Five things to know when building with the Outfitter stack. For the complete design process with templates, see guides/architecture.md.

1. Know Your Terrain

Before writing code, understand:

  • Transport surfaces — CLI, MCP, HTTP, or all three?
  • Domain operations — What actions does the system perform?
  • Failure modes — What can go wrong? (these map to error taxonomy)
  • External dependencies — APIs, databases, file system?

2. Design the Handler Layer

For each domain operation:

  1. Define input type (Zod schema)
  2. Define output type
  3. Identify error types (from taxonomy)
  4. Write the signature: Handler<Input, Output, Error1 | Error2>
typescript
const CreateUserInputSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
});

interface User {
  id: string;
  email: string;
  name: string;
}

const createUser: Handler<unknown, User, ValidationError | ConflictError>;

3. Map Errors to the Taxonomy

Ten categories. Memorize the exit codes—you'll use them.

CategoryExitHTTPClassWhen
validation1400ValidationErrorBad input, schema failures
not_found2404NotFoundErrorResource doesn't exist
conflict3409ConflictErrorAlready exists, version mismatch
permission4403PermissionErrorForbidden action
timeout5504TimeoutErrorTook too long
rate_limit6429RateLimitErrorToo many requests
network7503NetworkErrorConnection failures
internal8500InternalErrorBugs, unexpected errors
auth9401AuthErrorAuthentication required
cancelled130499CancelledErrorUser hit Ctrl+C

4. Pick Your Packages

Start with @outfitter/contracts. Always.

  • Building a CLI? Add @outfitter/cli
  • Building MCP tools? Add @outfitter/mcp
  • Touching files? Add @outfitter/config (paths) + @outfitter/file-ops (safety)

5. Wire Up Context Flow

Decide:

  • Entry points — Where does context get created?
  • What's in context — Logger, config, signal, workspaceRoot
  • Tracing — How does requestId flow through?

Guides

Deeper dives into specific topics.

GuideWhat's CoveredLocation
Getting StartedFirst handler, CLI + MCP adaptersguides/getting-started.md
Architecture Design5-step process, templates, constraintsguides/architecture.md

Pattern Deep Dives

When you need the details, not just the overview.

PatternWhat's CoveredLocation
Handler ContractInput, context, Resultpatterns/handler.md
Error Taxonomy10 categories, exit/HTTP mappingpatterns/errors.md
Result UtilitiesCreating, checking, transformingpatterns/results.md
CLI PatternsCommands, output modes, paginationpatterns/cli.md
MCP PatternsTools, resources, promptspatterns/mcp.md
Daemon PatternsLifecycle, IPC, health checkspatterns/daemon.md
File OperationsAtomic writes, locking, pathspatterns/file-ops.md
LoggingStructured logging, redactionpatterns/logging.md
TestingHarnesses, fixtures, mockspatterns/testing.md
Converting CodeMigrating to the stackpatterns/conversion.md

Templates: Just Add Code

Copy, paste, customize.

TemplateForLocation
HandlerTransport-agnostic business logictemplates/handler.md
Handler TestTesting handlers with Buntemplates/handler-test.md
CLI CommandCommander.js wrappertemplates/cli-command.md
MCP ToolZod-schema tool definitiontemplates/mcp-tool.md
Daemon ServiceBackground service with IPCtemplates/daemon-service.md

Quick Start

bash
# Foundation first
bun add @outfitter/contracts

# Then what you need
bun add @outfitter/cli       # CLI apps
bun add @outfitter/mcp       # MCP servers
bun add @outfitter/logging   # Structured logging
bun add @outfitter/config    # XDG-compliant config
bun add @outfitter/index     # Full-text search

# Dev dependencies
bun add -D @outfitter/testing @outfitter/tooling

# Version coordination (optional)
bun add @outfitter/kit

New here? Start with guides/getting-started.md.

The Rules

Do

  • Use Result types, not exceptions
  • Map domain errors to taxonomy categories
  • Design handlers as pure functions: (input, ctx) => Result
  • Include error types in handler signatures
  • Validate at handler entry with createValidator
  • Pass context through all handler calls
  • Test handlers directly—no transport layer needed

Don't

  • Throw exceptions in handlers
  • Put transport-specific logic in handlers
  • Hardcode paths (use XDG via @outfitter/config)
  • Skip error type planning
  • Couple handlers to specific transports
  • Use console.log (use ctx.logger)

Troubleshooting

Something not working? Use kit:debug-outfitter for systematic investigation with structured reports. Common issues:

  • Result always error — Check for missing await on async handlers
  • Type narrowing broken — Don't reassign Result variables after checking
  • MCP tool not appearing — Register before start(), add .describe() to schema fields
  • Wrong exit code — Use exitWithError(), not process.exit()

If the issue is in Outfitter itself, use kit:outfitter-feedback to file a bug.