Source on GitHub

The toolchain

The truewire package: one Python distribution, one CLI, installed with pip install truewire or run with uvx truewire.

T1. The toolchain is written in Python and stays there. It generates four languages; it does not need to be written in them. Python is where the spec model, the mock, the validator and the MCP server already are, and a rewrite buys nothing a caller can see.

The commands

Every command reads truewire.toml, exits non-zero on failure, and prints a result a person can act on.

Command Contract Status
init [name] Write the workspace skeleton: .truewire/, .gitignore, packages/, spec/, docs/, .agents/, AGENTS.md, truewire.toml. Idempotent. shipped
check Every endpoint.json, router and recording satisfies the authoring rules. shipped
capture <endpoint> Call one endpoint live through the project's own client and write the pair. shipped
generate [language] Render the spec into each declared backend. --check reports staleness; --delete removes orphans. shipped
examples Coverage of recordings, --require-verified to demand one per endpoint. shipped
surface Every endpoint in the spec is reachable as a method on the generated client. shipped
standards Docstrings, duplicate schemas, leaked secrets, router coverage. shipped
docs check Every code block type-checks against the generated package. shipped
mock Serve the recordings over real HTTP and WebSocket. shipped
mcp Serve a generated client's endpoints to an agent that calls the API. shipped
import openapi Turn an OpenAPI document into a spec tree. shipped
call <language> <endpoint> [args] Instantiate the client from truewire.toml and .env and make one call. A subscription prints the reply, then each message, until Ctrl+C unsubscribes cleanly. shipped: python, typescript, rust
test [language] Run each declared package's own suite against truewire mock, without the caller knowing pytest from vitest from cargo. shipped
lint [language] Run each declared package's formatter, linter and type checker, configured from the package, reported uniformly. shipped
score The scorecard: every row in score.md, one table, one exit code. target
conform The nightly replay and its report (ADR 0012). target
agents update / agents check Refresh the vendored skills and rules; fail on silent drift. shipped

T2. generate keeps its name. The first draft of the shape called it codegen, which matches the subsystem and the manifest directory, but the command has shipped since 0.1.0 and is named in every skill, every ADR and every README. The rename would cost a deprecation cycle and buy consistency with a noun. codegen stays the name of the subsystem and of .truewire/codegen/.

T3. lint is a Truewire command, not a Justfile. A project should not require a task runner on top of the toolchain that is already installed, and an agent should not have to learn four toolchains' invocations to satisfy one gate. The per-language tools are the real ones — ruff, pyright, eslint, tsc, clippy, rustfmt — configured in the package and invoked by the toolchain.

T4. test and lint take an optional language and default to every declared one. The exit code is the worst of them, and the output names which language failed.

The package

src/truewire/
  schemas/        pydantic models for everything with a schema
    spec/           endpoint.json, router.json, examples
    docs/           docs.yml
    config/         truewire.toml
  workspace/      one handle on the current project
  codegen/        the IR and one backend per language
  mock/           the replay server, one module per transport
  call/           instantiate a client and make one call
  test/           run the declared packages' suites
  score/          the scorecard
  conform/        the nightly replay
  standards/      the lint rules that are not a language's own
  mcp/            serve a generated client to an agent
  cli/            one module per command, no logic of its own
  resources/      templates and files that init writes

T5. cli/ contains argument parsing and output formatting and nothing else. Every command's behaviour is importable without the CLI, because the conformance runner, the tests and any future runner all need it without a subprocess.

T6. Workspace is the single way to reach anything in a project:

workspace = Workspace.load('truewire.toml')
workspace.spec              # walk routers and endpoints
workspace.codegen           # settings and, per language, the manifest
workspace.secrets           # the names, never the values
workspace.packages          # the declared backends and their roots

Nothing else parses truewire.toml, and nothing else joins a path from it.

T7. Every artefact with a schema has one published at truewire.dev/schemas/, and the pydantic model in schemas/ is the source those are generated from. A file the toolchain reads that an editor cannot complete is a file people will get wrong.

Settled questions

These were open when the shape was first drafted. They are settled here so that they stop being re-argued; each can be reopened by evidence, not by preference.

T8. Secrets are environment variables named by [secrets].required, read by the caller, passed to the constructor. No vault, no keyring, no encrypted file in the repo. The toolchain's job is to make a leak detectable, not to manage custody.

T9. Schemas are served from the site and cached under .truewire/cache/, never vendored.

T10. Skills are vendored per project and refreshed by a command (A10); not a plugin, for now.

T11. Docs for a project are published at <name>.truewire.dev, built from docs/ and docs.yml by the site, with the reference section generated from the spec.

T12. The CLI's own tests run in the repository's CI on every push, and the gates a project runs are the gates the toolchain's CI runs on its own examples. A gate that the toolchain cannot pass on its own example projects is not shipped.