Package an Agent Workflow as a Claude Code Plugin

A six-file workflow rebuilt from memory and Slack lands intact 9.6% of the time. Package the workflow, not the prompt.

Package an Agent Workflow as a Claude Code Plugin

A new engineer joins your team on Monday. By Friday she’s shipping, and every feature comes out a little wrong: the agent edits files your linter forbids, skips the architecture review you always run, names things in a convention nobody told it about. She isn’t worse than you. She’s running a reconstruction of your workflow, assembled from your scrollback, two Slack threads, and a wiki page that stopped being true in June.

Your workflow doesn’t exist anywhere she can install it from. It’s three subagents you defined six months ago, a hook you copied off your old laptop, a slash command you keep meaning to document, and a tracker server you configured once and forgot. On your machine it feels like one fluid loop. On hers it’s a scavenger hunt, and at some point she stops asking and starts guessing. Her own estimate, if she’s honest, is that she gets maybe sixty percent of it right.

That estimate is the subject of this post, because it’s a good answer to the wrong question. Sixty percent prices the average piece. A workflow is a chain: every piece has to survive the trip for the loop to work, and the odds of a chain are the product of its links, one factor per piece. Run that product on a real six-file workflow, the one this post builds as its running example, and the odds her rebuild matches all six files are 9.6%, under one in ten. Score every file generously, at a flat 75%, and the answer is still only 17.8%, under one in five.

Everything below is aimed at one enemy, and it needs a name: the retelling. The human transmission step, where each file of your workflow quietly loses whatever the Slack thread didn’t say.

One boundary before the arithmetic, because this blog already has a post with “prompt” in the title. Write the prompt so your team doesn’t have to ships one good prompt as an artifact, server-side, so the team pays for its assembly once. That artifact is one of the six files priced below. This post is about the other five, and about what happens to a whole bundle of them the moment a second person needs all of them at once.

Don’t accept 9.6% as a description of your team. One minute at your own setup settles whether you have this problem.

Open the agent setup you’re proudest of and list its pieces: every subagent, hook, command, and MCP server the loop leans on. Ask one question per piece. Is it a file your teammate gets by cloning a repo or running an install? Or does it live only in your settings, your scrollback, your head?

Every piece already committed and installable: close the tab. Distribution is solved for you, and the arithmetic below prices a problem you don’t have. Two or more pieces that exist nowhere installable: keep reading, because each of those pieces is a bet your teammates are placing without seeing the odds.

If any file of the loop exists nowhere your team can install from, every handover of that loop is a retelling, and the rest of this post prices retellings.

Fix one concrete example and carry it through everything below: your feature loop.

An explorer reads the codebase and reports what already exists. An architect decides how the change fits the system instead of bolting it on. A reviewer checks the diff against your standards before anything lands. Each is a subagent, a separate agent with its own context window and one job, and that split is the point: divide the work, keep each window narrow and deep. A pre-tool-use hook stands in front of every tool call as a deterministic gate that can block the call outright. One slash command, /feature, takes the ticket and runs the three subagents in order. The reviewer and explorer both pull issue context from a tracker MCP server.

Notice what already travels. Your rules file rides inside the repo itself; clone the repository and the rules arrive with it, which is the whole argument of Write it down once and Your AGENTS.md is write-once agent memory. The six pieces of this loop have no such ride. They live in tool config outside the repo, which is exactly why they need a packaging step at all.

Packaged, the whole loop is a plugin with this manifest:

.claude-plugin/plugin.json
{
"name": "feature-loop",
"version": "1.4.0",
"description": "Explorer/architect/reviewer subagents, a pre-tool-use safety gate, and the /feature command",
"commands": ["./commands/feature.md"],
"agents": [
"./agents/explorer.md",
"./agents/architect.md",
"./agents/reviewer.md"
],
"hooks": "./hooks/safety.json",
"mcpServers": "./mcp.json"
}

Count the entries. One command, three agent prompts, one hooks config, one MCP config. Six files. That count comes from the manifest itself, not from a toy sized for convenience, and it is the N behind every number in this post. The prompt you’d have thought to share is one file of six.

The manifest already knows the size of the problem: six files, and only one of them is the prompt.

Sixty percent is a good guess about the wrong question

Section titled “Sixty percent is a good guess about the wrong question”

Now score the retelling. For each file, estimate the fidelity: how close her rebuilt version lands to yours, for a careful teammate working from your Slack thread and your scrollback. These are toy values, invented so you can check everything below with a pencil, and chosen to be plausible:

File rebuilt from the threadFidelityWhy that score
agents/explorer.md80%a subagent prompt rewritten from your summary; close, not identical
agents/architect.md80%same
agents/reviewer.md80%same
commands/feature.md75%the orchestration; step order is easy to subtly reorder
hooks/safety.json50%the exit-code convention, easy to get backwards
mcp.json + tool scope50%the allowed-tools vs disallowed-tools boundary

Average the six and you get 69.17%, which lands right next to her sixty percent guess. Her intuition is fine. It’s answering a question nobody asked, because she doesn’t need the average file to be right. She needs all six files right at once, and that is a different operation.

The average prices a piece. The loop needs every piece, and every-piece arithmetic is multiplication.

Rebuild the loop the way she actually would, one file at a time, and carry the running odds. After the first file, the odds her version matches so far are just that file’s fidelity. After each next file, multiply: she needs the new file right and everything before it still right.

File rebuiltFidelityOdds all files so far match
agents/explorer.md80%80.0000%
agents/architect.md80%64.0000%
agents/reviewer.md80%51.2000%
commands/feature.md75%38.4000%
hooks/safety.json50%19.2000%
mcp.json + tool scope50%9.6000%

0.8 x 0.8 x 0.8 x 0.75 x 0.5 x 0.5 = 0.096. There’s the claim from the top of the post: 9.6%. Under one in ten. Six multiplications, no rounding, nothing hidden. You can run the whole table on paper.

Be generous and the picture barely improves. Score all six files at a flat 75%, better than two of them deserve: that’s 0.75 multiplied six times over, 0.178. 17.8%. Under one in five. The real mixed-fidelity scoring sits below even that, because two of its six files were never as easy as the other four.

The average is 69.17%. The odds are 9.6%. Both are honest numbers; only one is the bet she’s placing.

Why does the whole land so far below its average piece? Because the files are independent, and independent odds compound downward when they all have to happen together.

This is a standard result in reliability engineering, the field that asks why machines with thousands of parts keep working. Wire bulbs into a series string, where the current passes through every bulb, and the string lights only if every bulb lights. The string’s reliability is the product of the bulbs’ reliabilities, never their average, and one 50% bulb does more damage than any 80% bulb can repair, because the math has no channel for repair. The same structure, with files instead of bulbs, gives this effect its name: the series tax. Every file you add charges the whole loop, and the charge is a multiplier on everything downstream.

The tax also explains which files matter most. The two scored at 50% each cut the running total in half. The three scored at 80% each shave a fifth off it. The pieces most likely to be quietly wrong, the veto convention and the tool boundary, do the most damage per file, and they are the two nobody thinks of as “the workflow.” The prompt is the part everyone remembers to share. The exit-code convention is the part that has to survive the retelling.

The series tax: a workflow’s survival odds are the product of its files’ odds, and the riskiest file charges the most.

Zoom out from six files to N independent pieces, all held at that generous flat 75%:

N pieces at 75% eachOdds all N survive
175.00%
256.25%
342.19%
431.64%
617.80%
810.01%
105.63%

Row 6 is this post’s own loop at its most generous, 17.80%, already under one in five, with the real mixed-fidelity figure of 9.6% sitting below the curve. Row 10 is where a real team lands once you count an extra command, a second hook, a skill, and a second MCP server. A ten-piece workflow where every piece individually has a 75% chance of surviving a handover reproduces intact one time in eighteen.

Notice what the table has no row for. Install never even reaches this curve, because there is no transmission step to assign odds to: the marketplace copies the same six files, bit for bit, that you committed.

Doesn’t an install have odds too? Yes, and honesty here matters. A bad version pin, an unreachable source, a stale cache can all fail an install. But that failure is loud and binary: it errors, at install time, where one person sees it and fixes it. A failed reconstruction never errors at all. It produces a plausible loop with one veto convention backwards, and ships code with it for a month before anybody notices. A failed install errors loudly; a failed reconstruction fails silently.

Install has no row on the table because there is nothing to retell: what can still fail is loud, and the quiet failure is gone.

The fix is to seal the six files into one artifact and distribute that. A plugin is a directory with the manifest above plus the six files it names. Three of those files deserve a closer look: two for the conventions that scored 50% in the table, and the command file because it is the one you’d have thought to share.

One assumption rides through the rest of this section: the pieces themselves are already good. Getting a good one written is its own problem, and Commission the skill, don’t write it covers it. What follows is about the bundle.

The hook config points at a script that ships inside the plugin:

hooks/safety.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/check.sh"
}
]
}
]
}
}

check.sh reads each proposed call, compares it against your spec (protected paths, banned commands), and exits. The convention is the fragile part: exit code 2 vetoes the call, and any other non-zero exit merely logs a warning and lets the call through. Write a 1 where the 2 goes and you have shipped a gate that waves everything past, which is exactly why this file was scored at a coin flip. CLAUDE_PLUGIN_ROOT means the plugin resolves its own paths, so the same script works unchanged on every machine that installs it.

The MCP config declares the tracker server, and the command’s frontmatter pins which of its tools the loop may touch:

allowed-tools:
- mcp__tracker__list_issues
- mcp__tracker__get_issue
disallowed-tools:
- mcp__tracker__close_issue
- mcp__tracker__bulk_update

The two lists look like the same idea and are opposites. allowed-tools pre-approves the read calls so the loop runs without stopping to ask; it is friction control, and it fences nothing off. disallowed-tools removes the write tools from the loop’s reach entirely; that is the fence. On your machine the tracker happened to expose only reads, so the distinction never mattered. On hers it also exposes close_issue and bulk_update: same words, opposite blast radius. Configuring one server once is its own win, and One server, not ten integrations covers it, but config-once still leaves the tool-boundary list as a separate fact somebody has to remember. In the bundle it is a file, not a memory.

The command file itself is the easy one: the trigger, with the orchestration written down where everyone’s version of it is identical.

commands/feature.md
---
description: Build a feature through the explore, architect, review loop
---
Run the feature loop for $ARGUMENTS:
1. Invoke the `explorer` subagent. Report every existing module,
test, and convention relevant to this ticket. No code yet.
2. Invoke the `architect` subagent with those findings. It decides
where the change belongs and what it must not touch.
3. Implement the change on the architect's plan.
4. Invoke the `reviewer` subagent. It checks the diff against our
standards. Fix and re-review until it passes.

The bundle is the trigger, the workers, the gate, the reach, and the bounds on that reach: six files, one version number.

Marketplace, project scope, and one gotcha

Section titled “Marketplace, project scope, and one gotcha”

A marketplace is a manifest listing plugins and where to fetch them. Stand one up for your team and every plugin in it becomes installable by name. How it travels is your call: owner/repo on public GitHub for open work, a private repo over SSH so access rides the keys engineers already have, or a local path while you iterate.

The part people get wrong is scope. Plugins install at user, project, or local scope, and the default is user, which follows one person across every repo on one machine. That is the scope of a personal habit. Install at project scope and the plugin lands in enabledPlugins inside the repo’s checked-in .claude/settings.json, so it follows the codebase: every engineer who clones the repo inherits the same six files. (local keeps a plugin in one repo’s uncommitted settings, for trying it before the team commits to it.) For a team standard you want project scope.

One gotcha: plugins load at launch. Install one mid-session and the new command and subagents will not appear; relaunch, or run /reload-plugins. The install worked. The session just predates it.

Project scope turns the plugin from your setting into the repo’s setting: cloning is the install, and the version is the standard.

Run the handover forward. She clones the repo. She adds your marketplace and installs feature-loop at project scope, relaunches, and types /feature PROJ-214. The explorer reads the codebase through the tracker’s read tools, the architect places the change, the reviewer checks the diff, and the hook vetoes anything on the banned list with the same exit code 2 you wrote. What runs on her machine is your loop in the strongest sense: the same files, at the same version, gated by the same convention.

Then the part no retelling can match. You improve the reviewer’s prompt, cut 1.5.0, and her next session picks it up. The loop is now a thing the team versions, and every copy moves with it.

She runs your loop because she installed it, and she keeps running your improvements because she pulls them.

A plugin can carry five kinds of piece, and this loop uses four. Skills, the SKILL.md folders this site covers separately, are a documented plugin component alongside subagents, hooks, commands, and MCP servers. The feature loop ships no skill, so the toy prices six files in four kinds; a loop that carried one would be a seven-file chain with worse odds, at 0.096 times whatever the skill scores. The five-piece picture is in Plugins & marketplaces, and the component itself in Skills.

You are not standing up the first marketplace. An official plugin marketplace and a wider community ecosystem already exist for installing other people’s work. This piece is about the other direction: publishing your team’s own loop to a source your teammates already trust. Those are different acts, and only the second one is argued here.

Bundling this complete is Claude Code’s lane, and that limit should be said out loud. Of the tools this site covers, only Claude Code bundles all five component kinds in a static plugin. Codex bundles some pieces, OpenCode plugins are live code rather than a file bundle, Cursor has no bundle format for this, Copilot’s plugin surface is newer, and Pi has no native MCP to bundle at all. The series tax is universal; the cure, in this complete form, is Claude Code-specific. The tool-by-tool comparison lives in Plugins & marketplaces.

Installing a plugin is a trust decision. The hook script and the MCP server inside the bundle get real tool access on every machine that installs them. This piece assumes the marketplace is your team’s own; an untrusted source turns every convenience above into a supply-chain question, and that question is out of scope here. It is already open on this blog: The confused deputy server and A single authorization gate for agents work exactly that boundary.

One more scope line. This post is about whether the loop your teammate runs is the one you built. When that loop fires is a separate concern, and Triggers are hooks takes it; the two compose, since a packaged loop can itself be wired to an event.

The series tax was a distribution problem. Packaging solves that one and opens a trust problem it cannot solve.

Row 6 of the master table, one last time. A six-piece loop where every piece has a generous 75% chance of surviving a handover reproduces intact 17.8% of the time. The real feature loop, weighted by which of its files people actually get wrong, lands at 9.6%. Installing the plugin sits nowhere on that curve: there is no per-file transmission left to fail quietly, and the failures that remain announce themselves at install time.

Packaging collapses the series tax on day one, and it manufactures a new problem on day two. The same multiplication that made one wrong file sink a retelling now puts one bad version on every machine that trusts your marketplace, at once, with nothing in between to catch it. What happens the day 1.5.0 breaks the loop for the whole team, and what is the rollback story? This post cut the version. It did not answer that.

You paid the series tax once per handover. Now you hold a single point of failure forever, and the rollback story is yours to write.


About the numbers. The six per-file fidelities (80, 80, 80, 75, 50, 50) are toy values, invented for traceability and declared as such; they are plausible scores for a careful rebuild from memory and Slack, and nothing in this post measures any real team’s rebuild success. Every other figure, the running products, the 9.6%, the 69.17% average, the 17.8% generous case, and all seven rows of the master table, is arithmetic that follows from those six inputs and was re-derived with an independent script before publishing. The “maybe sixty percent” line is the opening scene’s stated guess, priced here and never measured. The product rule itself is a standard result from reliability engineering for series systems, borrowed the way this blog borrows the test pyramid: a named idea from a neighboring field, applied to files instead of parts. The cross-tool bundling claims follow this site’s own Plugins & marketplaces comparison. Swap in your own fidelities and the shape holds: as long as any file scores below 100%, the product stays below every factor, and the odds keep falling with every file you add.

For the per-tool mechanics, see Plugins & marketplaces for the distribution unit that ships the whole loop, Subagents for the split-work orchestration inside it, and Hooks for the safety gate that travels with the workflow.