A package framework for producing per-package Claude Code plugins (CLIs, MCP servers, and skills) as composable, Nix-built derivations for humans and agents like Claude Code.
purse-first is a per-package plugin producer toolkit plus libraries — it
generates and validates individual package manifests under
share/purse-first/<name>/. External tools (e.g. clown) aggregate those
per-package outputs into a consumable plugin set; purse-first itself no longer
assembles or installs a marketplace.
purse-first has three layers:
| Layer | Description |
|---|---|
| Protocol | A convention for Nix derivations to declare Claude Code capabilities via well-known paths under share/purse-first/. Language-agnostic and self-describing — see docs/purse-first-protocol.md. |
| CLI | The purse-first binary — generates per-package plugin.json from package.toml and validates plugin manifests, mappings, and MCP servers. |
| Libraries | go-mcp — building blocks for protocol-conforming MCP servers (command framework, server, transports, self-install). dewey — multi-tier Go utilities, three static analyzers (+ golangci-lint plugin), and the dagnabit rename/export tool. |
purse-first was slimmed to the framework itself. The concrete MCP-server
packages (grit, get-hubbed, chix, …) moved to
amarbel-llc/moxy as moxins; lux was
pulled out and is currently dormant (not published in moxy or any other
active repo). What remains here:
purse-first(CLI) — generates per-packageplugin.jsonfrompackage.tomland validates plugin manifests, mappings, and MCP servers.go-mcp(library) — building blocks for protocol-conforming MCP servers: thecommand.Appframework, the lower-levelserverAPI, stdio + Streamable HTTP transports, andInstallMCPself-registration.dewey(library) — multi-tier Go utilities organized by NATO-phonetic dependency level, thedagnabitrename/export tool, three static analyzers (defererr,repool,seqerror) exposed both as standalone vettools and as a golangci-lint module plugin, and thereflexive-interface-generatorcodegen tool.- In-tree skills —
claude-pluginsandmcp(underskills/).
⚠
go-mcpis mid-overhaul. The library is undergoing a major change and its API is in flux. Treat its interfaces as unstable and avoid building new downstream code against them until the rework settles.
The protocol defines three package flavors. Concrete MCP packages now live in
amarbel-llc/moxy as moxins; the in-tree
skill packages are claude-plugins and mcp.
| Flavor | Description | Examples |
|---|---|---|
| MCP package | Ships an MCP server with optional tool mappings | grit, get-hubbed (in moxy) |
| Skill package | Ships skills only | claude-plugins, mcp (in-tree) |
| MCP + Skill package | Ships both an MCP server and skills | chix (in moxy) |
The CLI is a slim per-package producer toolkit: it generates and validates individual package manifests. Aggregation of per-package outputs is handled by external tools, not by purse-first.
| Command | Purpose |
|---|---|
generate-plugin |
Generate plugin.json from package.toml (--root, --output, --skills-dir) |
validate [path] |
Validate plugin/mapping docs (auto-detects type; --type mcp for an MCP probe; --strict) |
validate-mcp <binary> [args...] |
Probe a binary as an MCP server (initialize + tools/list + resources) |
purse-first packages are built with Nix. Each package conforms to the
share/purse-first/<name>/ convention (a .claude-plugin/plugin.json, optional
mappings.json, skills/, and hooks/); external tools aggregate those
per-package outputs into a consumable plugin set.
# Install the purse-first CLI
nix profile install github:amarbel-llc/purse-first#purse-first
# Generate a package manifest from package.toml
purse-first generate-plugin --root . --output .claude-plugin/plugin.json
# Validate a built package's manifest
purse-first validate result/share/purse-first/<name>/.claude-plugin/plugin.jsonFor the complete protocol specification, see docs/purse-first-protocol.md.
The flake exposes these packages.<system> outputs (see nix flake show):
nix build .#purse-first # the purse-first CLI
nix build .#dagnabit # dewey rename/export tool
nix build .#defererr # dewey static analyzer
nix build .#repool # dewey static analyzer
nix build .#seqerror # dewey static analyzer
nix build .#reflexive-interface-generator # dewey codegen tool
nix build .#golangci-lint-dewey # golangci-lint with the dewey plugin linked in
nix build .#manpages # go-mcp manpage tree
nix build .#go-pkgs # RFC 0001 published Go workspace source
nix build .#go-pkgs-test # RFC 0001 test-only Go workspace sourceThe default nix build (no attribute) builds the purse-first CLI.
libs/dewey is a multi-tier Go library whose packages are ordered by
NATO-phonetic dependency level (0/, alfa/, bravo/, …); a package at level
N may only depend on lower levels. The dagnabit tool repositions and renames
packages across levels and enforces that every leaf name is unique across
the whole tree.
dewey ships three static analyzers:
defererr— flags deferred calls whose returned error is dropped.repool— flags pooled values returned to async.Poolwhile still referenced (no-op outside pool-owning packages).seqerror— flags error-handling mistakes initer.Seqsequences.
Each is available two ways:
- Standalone vettool —
nix build .#defererr(etc.), run viago vet -vettool. - golangci-lint module plugin —
libs/dewey/gclplugin/registers all three withregister.Plugin("dewey", New)so repos already gating ongolangci-lintfold them into that run instead of a separate pass. Wire it via.custom-gcl.yml+golangci-lint custom;Settingslets a consumer opt out per analyzer. Thegclpluginpackage doc comment carries the full config snippet. - Prebuilt custom binary —
nix build .#golangci-lint-deweyproduces golangci-lint with the plugin already linked (the pure-nix, no-network equivalent ofgolangci-lint custom). The golangci-lint version is pinned incmd/golangci-lint-dewey/go.mod, and the plugin compiles from the same tree, so the module-plugin ABI constraint holds by construction. Downstream repos consume it as a flake input instead of runninggolangci-lint customthemselves.
See the caution above — this library's interfaces are unstable while it is being reworked.
The command.App abstraction expects a consuming Go MCP package's main.go to
dispatch on its first argument:
generate-plugin <dir>— build-time: writesplugin.json,mappings.json, andhooks/.hook— Claude CodePreToolUsehandler: denies a built-in tool when an MCP tool should be used instead (fail-open).- no args — runtime: starts the MCP server.
InstallMCP lets a built MCP binary register itself into ~/.claude/mcp.json
(stdio for local binaries, http for MCPURL-configured remotes).
cmd/go-mcp-docs generates the go-mcp manpage tree (the .#manpages output).
| Directory | Purpose |
|---|---|
cmd/purse-first/ |
CLI entrypoint |
cmd/dagnabit/ |
dewey-aware Go rename/export tool |
cmd/go-mcp-docs/ |
Generates the go-mcp manpage tree (the .#manpages output) |
cmd/golangci-lint-dewey/ |
Standalone module: golangci-lint with the dewey plugin linked in (.#golangci-lint-dewey) |
internal/ |
Go internal packages (validate, packagetoml) |
libs/go-mcp/ |
Go MCP server library (command, server, transport, output, purse, jsonrpc, executor, operation, protocol) |
libs/dewey/ |
Multi-tier Go library (internal/, pkgs/ stable facades, cmd/ analyzers, gclplugin/) |
purse/ |
Go package for building package manifests (plugin.json) |
skills/ |
In-tree skill documents (claude-plugins, mcp) |
lib/ |
Nix build expressions (mkGoWorkspaceModule.nix) |
gomod.nix |
Per-system Nix interface to the Go workspace; builds every binary plus the RFC 0001 go-pkgs / go-pkgs-test source derivations |
devenvs/ |
Per-language dev shells composed into the default shell |
zz-tests_bats/ |
BATS integration tests |
docs/ |
Protocol spec, decisions, rfcs, features, plans |
This is a Nix-based project. The justfile follows
eng-design_patterns-justfile(7): bare-verb recipes are aggregates; leaves are
verb-noun. Plain just is the CI gate (and also runs inside
merge-this-session).
just # default = validate lint build test (the CI gate)
just validate # nix flake check + plugin manifest validation
just lint # go vet + dewey-facade drift + read-only treefmt gate (lint-fmt)
just build # sync gomod2nix + nix build (default = purse-first CLI)
just test # all tests (Go + BATS integration)
nix fmt # repo-wide treefmt (Go gofumpt, Nix nixfmt, shell shfmt)
just codemod-fmt-go # go fmt ./... only (quick Go reformat)
just build-nix-gomod2nix # sync go.work and regenerate gomod2nix.tomlAll Go modules share one go.work workspace (root, libs/dewey,
libs/go-mcp, libs/go-mcp/command/huh). External modules are pinned by a
single gomod2nix.toml lockfile at the workspace root; local code changes never
invalidate it. Run just build-nix-gomod2nix after any go.mod / go.sum /
go.work change — CI fails on lockfile drift.
gomod.nix also publishes the RFC 0001 dual outputs go-pkgs and
go-pkgs-test; self-consumption uses go-pkgs-test so each binary's
checkPhase exercises the same artifact downstream consumers receive.
Per eng-versioning(7) multi-artifact release, a single version covers
every published artifact, sourced from one root version.env
(PURSE_FIRST_VERSION). It is read at build time by gomod.nix (CLI version +
dewey buildinfo) and covers the purse-first CLI, the libs/dewey
library and its binaries, and the libs/go-mcp library.
The maintenance recipe group exposes one triple:
just bump-version <sem> # rewrite PURSE_FIRST_VERSION (pure mutation)
just tag <message> # create the signed tag set: v<sem>, libs/dewey/v<sem>, libs/go-mcp/v<sem>
just release <sem> # master-only: changelog → bump → commit → tag set → gh releaseThe path-prefixed tags are required by the Go module proxy to resolve the sub-directory modules. Pre-1.0, MINOR bumps may include breaking changes; post-1.0, semver is strict.