agent-compiler plugin

Agents are compiled, not stored. Humans author small, addressable behavior modules; a model may turn fuzzy intent into a typed AgentQuery, but everything after that boundary is deterministic: a stdlib-only kernel resolves modules, expands dependencies, fails closed on conflicts and over-ceiling effects, and emits an immutable, content-hashed AgentImage that renders into a harness-native agent definition — every line traceable to its source module.

The invariant every eval defends

Given the same registry revision, query, and compiler version, compilation produces a byte-identical canonical AgentImage and identical hash; every emitted unit carries provenance to a source module; unresolved conflicts, missing dependencies, dependency cycles, and effects above the query's ceiling fail compilation.

“Natural language may select behavior; it may not silently define it.”

Install

/plugin marketplace add JRichlen/agent-plugins
/plugin install agent-compiler@jrichlen

No dependencies beyond python3 — the kernel, renderer, and MCP server are stdlib-only and fully offline. Enabling the plugin auto-starts the kernel MCP server (four pure tools: inspect, compile, explain, render); the CLI is the identical fallback on any other harness.

Why it exists

Agent personas are normally hand-written prose blobs: unversioned, unattributable, silently over-privileged, non-reproducible, impossible to diff. When one “works,” nobody can say which sentence did the work, and when it grows a new power, nobody approved it. agent-compiler replaces prose authoring with a compilation pipeline: behavior lives in small, versioned, addressable modules in a registry; agents are build outputs.

The one non-deterministic step is explicitly fenced off — the model may translate fuzzy intent (“make me a security reviewer for our AWS PRs”) into a typed query. From the query onward, compilation is a pure function. Three claims you can verify yourself in under a minute:

The compiler metaphor, mapped

Compiler conceptagent-compiler realization
Source filesBehavior modules — Markdown with restricted frontmatter and semantic blocks, living in a registry/ directory
SymbolsBehavior units<rule> / <probe> / <example> / <antipattern> blocks, addressed as <module-id>#<block-id>
Build invocation and flagsAgentQuery — typed JSON: role, task, domains, views, stance, environment, risk, effectCeiling, name
Linker and link errorsDependency expansion (requires), conflict resolution (conflicts_with / supersedes), capability and effect linking
Content-addressed object fileAgentImage — one line of canonical JSON with a SHA-256 hash and per-unit provenance
Codegen backendRenderersrender_claude_agent.py emits .claude/agents/<name>.md; other harnesses get sibling renderers, versioned separately from the compiler
Debug symbolsprovenance: {module, version, source, lines} on every unit; explain is the symbol lookup
-WerrorStructured diagnostics, never downgraded to warnings — the kernel fails closed

The pipeline

1

Load registry

Walk the registry, parse frontmatter + blocks, reject bad or duplicate ids, compute the registry revision. Any diagnostic here is fatal before resolution starts.

2

Resolve selection

Views only when named in query.views; behavior when task, role, or a domain matches — and only if the module's applicability (environments / risks) admits the query.

3

Expand dependencies

Transitive closure over requires. Cycles fail; unknown ids fail.

4

Detect conflicts

Superseded modules are dropped; any surviving conflicts_with pair fails compilation.

5

Validate stance

Every stance entry must be a trait some selected view declares — stance is never decorative.

6

Link effects

Effective ceiling = query ceiling ∩ every view's max_effects. Capabilities must resolve to interfaces; their effects must fit inside the ceiling. No ceiling at all fails closed.

7

Emit + canonicalize

Units get provenance and a deterministic sort; the query is canonicalized; the image is one line of canonical JSON.

8

Hash

SHA-256 over the canonical bytes. The hash deliberately excludes the registry revision so unrelated modules can't change an image's identity.

That last point was a real bug, found by running the kernel rather than reviewing it: hashing the whole-registry revision let an unrelated module change an existing image's identity, breaking the metamorphic invariance the evals now pin. The registry's own discipline.build-run-verify module carries the story as an example block — “a defect no amount of re-reading the design had surfaced.”

The registry

The bundled registry ships 22 modules across three kinds. --registry accepts any directory, so a repo can carry its own; the kernel treats both identically. A module's filesystem location never carries meaning — only its id does.

Module anatomy

---
id: security.iam.review
kind: behavior
version: 1.0.0
tasks: [pull-request-review]
domains: [aws, iam]
requires: [behavior.evidence]
capabilities: [scm.pull_request.read, scm.pull_request.files]
---

<rule id="least-privilege" strength="must">
Evaluate permissions against the minimum privileges required by the stated operation.
</rule>

<probe id="wildcard-scope">
Are wildcard resources, actions, or principals being introduced?
</probe>

<antipattern id="unsupported-vulnerability">
Do not describe a theoretical vulnerability without connecting it to
concrete evidence from the reviewed artifact.
</antipattern>

The frontmatter subset is deliberately tiny — scalars, inline lists, one-level dash lists; “no nesting, no multi-line scalars, no anchors.” Anything else is a compile error, never a guess. Even an unknown key is fatal (BAD_MODULE_KEY) — added after a typo'd task: (singular) compiled cleanly and silently deselected a module. Exactly four block kinds exist, none nested.

The three module kinds

KindRoleIn the bundled registry
view An identity: selectors + declared traits + a max_effects ceiling — and no behavior text of its own. “A selector plus a ceiling, not a prompt.” view.engineering-default (the marketplace's house style as one addressable identity, with a nine-edge requires fan-out), view.security-reviewer (capped at network + scm:read), view.researcher
behavior The actual rules, probes, examples, and antipatterns, selected by task / role / domain. behavior.evidence (the universal base, pulled in only via requires); nine discipline.* modules distilled from sibling plugins' invariants; three practice.* modules of attributed external taste (Anthropic agent-authoring, Karpathy, Willison); three preference.typescript.* modules
capability_interface Provider-independent, effect-declaring stubs a behavior module can require. Binding to concrete tools is the human's wiring step. scm.pull_request.read, scm.pull_request.files (both network, scm:read), scm.pull_request.comment (scm:write — a live tripwire: any module requiring it fails at link time under a read-only ceiling, “the effect check is structural, not prose”)

Selection semantics worth memorizing

The governing ADR

ADR 0001 settles how the registry grows: “new registry dimensions are populated as content along existing coordinates, not as new module kinds or query coordinates.” An identity is a view; a preference is domain-scoped behavior; external material is paraphrased with attribution. A first-class identity kind and a preference block were rejected because every language addition is effectively permanent once modules are published against it. The reopening condition is recorded: if “prefer X over Y” as a should-strength rule reads forced as the preference corpus grows, revisit a preference block kind.

What the plugin ships

PieceWhat it is
Skill agent-compiler Governs the one step the kernel cannot: intent → AgentQuery. Interview and infer freely while building the query; show the JSON before compiling; once the query exists, only the kernel decides what the agent is. Ships references/language.md, the module-format spec.
/agent-compile Interview until role, task, domains, views, and ceiling are explicit; show the query; compile; render. “On diagnostics, fix the registry source and recompile; never suppress a failure or paste prose into the rendered output.”
/agent-inspect Pure discovery, no compilation: list modules by kind or tag, show a module's units and edges, or trace a compiled unit back to its source.
scripts/compile.py The deterministic kernel (stdlib-only). Subcommands compile, inspect, explain. Exit 0 success · 1 compile error with structured JSON diagnostics · 2 usage error.
scripts/render_claude_agent.py AgentImage → Claude Code agent markdown, every content line tagged with its unit id. Versioned separately from the compiler so rendering can evolve without invalidating image identity.
scripts/mcp_server.py Stdlib JSON-RPC MCP server, auto-started by Claude Code. Four pure tools: inspect, compile, explain, render. “There is no execute: the server performs no effectful action, writes no files, and makes no network calls.” Even render only returns text — saving the file stays with you.
Hook: suggest-compiler.py On UserPromptSubmit, spots agent-building intent (“make me a … reviewer agent”) and injects one context line pointing at the compiler. “Conservative by design: no match, no output, no noise.”
Hook: guard-compiled-agents.py On Write/Edit/MultiEdit, denies hand-edits to any file carrying the compiled by agent-compiler; imageHash: marker — including a multi-file edit whose second path is the compiled artifact. Fails open on malformed input so a broken hook never blocks unrelated work.
registry/ The 22-module bundled registry described above.
examples/ Two query → golden-image pairs, pinned byte-for-byte by the evals (see below).

Day-to-day use

  1. Say what you want. “Make me a security reviewer for our AWS PRs.” The prompt hook nudges toward the compiler; or invoke /agent-compile directly.
  2. The model builds the query and shows it. Role, task, domains, views, stance, environment, risk — and an effect ceiling. A compile with no ceiling from either the query or a view is refused: “an unconstrained agent must be asked for by listing its effects, never implied by omission.”
  3. Discover exact ids. inspect filters by kind or tag. Fuzzy discovery may suggest ids; the compile step consumes exact ids only.
  4. Compile. Out comes an AgentImage and its hash — or structured diagnostics. On diagnostics, fix the source, not the check: resolve conflicts, add the missing module, raise the ceiling deliberately — never suppress the failure.
  5. Render and save. render returns the agent markdown; you write it to .claude/agents/<name>.md. (That path is Claude Code's location; the AgentImage, not the rendered file, is the contract.)
  6. Interrogate it later. “Why does this agent say X?” → explain --unit security.iam.review#least-privilege → module, version, source file, line range.
  7. Missing behavior? Author a module, then recompile — never paste prose into the rendered agent. The guard hook enforces what the artifact's own header says: DO NOT EDIT BY HAND.

Try it from the command line

cd plugins/agent-compiler
python3 scripts/compile.py compile \
  --registry registry \
  --query examples/queries/security-pr-review.json \
  --out /tmp/image.json
python3 scripts/render_claude_agent.py --image /tmp/image.json

What you get

The rendered artifact for the shipped security-review query — note the provenance comment on every content line and the explicit capability-wiring section:

---
name: security-pr-reviewer
description: Compiled reviewer agent for pull-request-review. Effects capped at: network, scm:read.
---

<!-- compiled by agent-compiler; imageHash: sha256:ccb2af8b…; rendererVersion: 0.1.0;
     DO NOT EDIT BY HAND — edit the registry modules and recompile -->

# security-pr-reviewer

Stance: adversarial, evidence-driven.

## Rules

- **MUST** Every material finding must include concrete evidence or a precise
  source reference.  <!-- behavior.evidence#cite-findings -->
- **MUST** Evaluate permissions against the minimum privileges required by the
  stated operation.  <!-- security.iam.review#least-privilege -->

## Probes to run

- Could this change allow a principal to cross an existing trust boundary?
  <!-- security.iam.review#cross-boundary-access -->
- Are wildcard resources, actions, or principals being introduced?
  <!-- security.iam.review#wildcard-scope -->

## Never

- Do not present a conclusion whose supporting evidence you have not actually
  examined in this session.  <!-- behavior.evidence#unsupported-claim -->
- Do not describe a theoretical vulnerability without connecting it to concrete
  evidence from the reviewed artifact.  <!-- security.iam.review#unsupported-vulnerability -->

## Required capabilities

This agent needs implementations of these provider-independent
interfaces; wire them to the tools your harness actually exposes:

- `scm.pull_request.files`
- `scm.pull_request.read`

Effects this agent may exercise: `network`, `scm:read` (ceiling: network, scm:read).

The two shipped examples show the two shapes an agent takes: this narrow, capability-linked, read-only reviewer (6 units from 2 modules), and engineering-default — the daily-driver identity (17 units from 9 modules, zero capabilities, behavior-only under a broad ceiling), which also demonstrates applicability: discipline.prove-the-undo joins the image only when the query's risk reaches high.

Diagnostics — the fail-closed surface

Every failure is a structured {code, severity, message, nodeIds} diagnostic on stdout with exit 1, never a warning. The kernel's codes:

StageCodes
Registry loadBAD_REGISTRY · BAD_MODULE · BAD_MODULE_KEY · BAD_MODULE_ID · BAD_MODULE_KIND · BAD_MODULE_VERSION · DUPLICATE_ID · BAD_UNIT_ID · DUPLICATE_UNIT_ID
Selection + linkingMISSING_VIEW · MISSING_DEPENDENCY · DEPENDENCY_CYCLE · CONFLICT · UNDECLARED_STANCE · NO_EFFECT_CEILING · MISSING_CAPABILITY · EFFECT_CEILING
MCP facadeBAD_QUERY · BAD_IMAGE · UNKNOWN_UNIT · INTERNAL (surfaced, never a server crash)

Eval coverage

The cheap-tier pack (plugins/agent-compiler/evals/cheap/checks.sh) executes the kernel and asserts the invariant on real output — golden, metamorphic, and fail-closed — rather than grepping for sentences about it. Offline, stdlib-python + bash, sub-second:

Two further tiers prove the invariant beyond the two golden examples:

Honest limits

Relationship to sibling plugins

agent-compiler is, in a real sense, the marketplace's own doctrine made compilable: nine of its registry modules are distillations of sibling plugins' invariants, each citing its source — verify-before-claim, find-before-build, stop-rule, scope-fence, egress-gate, semver-gate, prove-the-undo, plus two from repo practice (build-run-verify and demonstrate-with-misses). Compiling view.engineering-default hands you the house style as one addressable identity.

Further reading