This document is for people who want to publish reusable macro libraries for a vertical and have them discoverable by other Macrokit adopters.
If you want to contribute to the SDK itself (@macrokit/runtime, @macrokit/llm, etc.), that's a different document — see CONTRIBUTING.md (TODO).
Macrokit deliberately separates curated reference implementations from community macro packages.
| Tier | Where | Cadence | Curated by | Examples |
|---|---|---|---|---|
| Official examples | core/examples/<name>/ in this repo |
Reviewed PRs | Macrokit maintainers | The GitHub maintainer agent (examples/github-maintainer/). |
| Community packages | Standalone npm packages, your own repo | Whatever pace you want | You | A library you publish that ships macros for your vertical. |
The split is intentional. Curated examples teach the pattern; community packages let the ecosystem expand without the bottleneck of a single repo's review queue. Both are first-class — the registry lists community packages alongside the official examples, and lint/gate work the same against both.
Upstream into core/examples/ when:
- Your vertical is genuinely general-interest (HR/recruiting, academic paper triage, GitHub maintainer tasks) AND
- The macros do not depend on private credentials, brand lists, or proprietary scoring rules AND
- You're willing to maintain it under the project's review standards.
Publish as a community package when:
- Your vertical is narrow or domain-specific (your company's internal admin tool, your team's CI/CD operations, your industry's compliance flows).
- The macros encode workflow choices your organization made — wrong defaults for someone else's deployment.
- You want versioning and release cadence independent of Macrokit's.
- You don't want your code reviewed by maintainers who don't know your domain.
The vast majority of useful macro work belongs in community packages, not in core/examples/. We curate examples to teach the pattern, not to host every vertical.
Community packages SHOULD be published as:
macrokit-macros-<vertical>(unscoped), e.g.macrokit-macros-paper-triage,macrokit-macros-github-maintainer,macrokit-macros-rfp-intake.@<scope>/macrokit-macros-<vertical>(scoped), e.g.@acme/macrokit-macros-internal-tools.
The literal substring macrokit-macros- makes packages findable by registry search and signals to readers that the package follows the conventions in this document. The <vertical> segment is whatever you'd call the surface or workflow group; pick something specific.
Reserve @macrokit/macros-* and @macrokit-example/* for official maintainer use; community packages should not publish under those scopes.
A package qualifies as a "Macrokit macro package" if all of the following hold. macrokit lint --pkg <path> checks each automatically.
The package MUST export one or more values produced by defineMacro() from @macrokit/authoring. Each macro MUST have all four required fields:
import { defineMacro } from "@macrokit/authoring";
import { z } from "zod";
export const triagePaper = defineMacro({
name: "triage_paper", // required: stable identifier
intent: "summarize and classify an arXiv paper by id", // required: natural-language description for the router
schema: z.object({ paperId: z.string() }), // required: typed argument schema
handler: async ({ paperId }, ctx) => { /* ... */ }, // required: deterministic workflow
});Names must match /^[a-z][a-z0-9_]*$/ (lowercase, digits, underscores; starts with a letter). The intent string is what the router matches user requests against — it must be descriptive, not a placeholder.
Every macro MUST have at least one test fixture. Either:
- A
.test.ts(or.test.tsx) file that importstestMacrofrom@macrokit/authoringand exercises the handler against recorded(args, expected)pairs, OR - A
.fixtures.jsonfile alongside the macro source with the same shape.
The tests don't need to call the LLM — that's the runtime's job. They pin the workflow, which is the part that changes.
The package root MUST contain a README.md covering:
- Vertical name and one-line scope. "Macros for triaging academic preprints across arXiv, Semantic Scholar, and OpenAlex."
- Macro list. Bullet list of
nameandintentfor each exported macro. Copy-pasted fromdefineMacrois fine — the schema lives in the code. - Surface(s) driven. Which app, API, or service does the workflow touch? Include base URLs where applicable.
- Credential requirements. If the macros need an API key, document the env var name and link to where users obtain the key. Do not ship credentials in the package.
- Installation snippet.
npm install your-package @macrokit/authoring.
@macrokit/authoring MUST be declared as a peerDependency, NOT a regular dependency:
This prevents npm-tree duplication when the consumer already has @macrokit/authoring. Bundling it would mean two defineMacro symbols in memory, two schema validators, two registries — silent dispatch failures.
The same applies to zod (or your schema library of choice): peer dep, never bundled.
A macro handler that calls home to your servers — even for "anonymous usage stats" — turns every downstream adopter's deployment into a data leak. The pattern Macrokit exists to enable is local, private, audit-friendly execution. Phoning home defeats that.
- Do NOT include any default outbound HTTP call to a domain you control.
- Do NOT include hardcoded API keys, tokens, OAuth secrets, or service-account JSON. Read them from
process.envand document the env var names in your README. - Do NOT include analytics SDKs, error trackers that auto-init, or any "init-on-import" side effects.
Macros are libraries. Libraries don't open sockets on import.
The single most important Macrokit discipline — "every session that touches a workflow without a macro must encode it as a macro before ending" — applies to community packages too, recursively.
If your package ships ten macros and an adopter's session log shows them invoking three of yours in a row to build a higher-order workflow, the gate flags it. The right response is to ship a composite macro in the next release. Macro libraries that compound are the goal; tool graveyards are the failure mode.
If you find yourself shipping a release every time someone hits the gate, that's healthy — that's exactly the cadence the discipline produces. If you find yourself shipping new utility primitives ("read_file", "fetch", "run_bash") instead of domain macros, push back on the adopter's instinct to compose at runtime.
The registry lives at github.com/macrokit/.github/community/registry.json.
To get your package listed:
- Verify your package passes
macrokit lint --pkg <path>— every check green. - Ship at least five benchmark tasks as part of your repo. A benchmark task is a
(prompt, expected_tool_call, expected_args)triple matching the format in the Macrokit launch benchmark. Five is a minimum; more is better. Tasks let adopters run the harness against your macros with their model of choice. - Have a README that meets the minimums above.
- License the package under Apache 2.0 (preferred) or MIT. We do not list packages under copyleft, source-available, or non-commercial licenses — the ecosystem optics matter, and Macrokit itself is Apache 2.0.
- Open a PR against
github.com/macrokit/.githubadding an entry tocommunity/registry.json. The entry shape and example are documented atcommunity/README.mdin that repo.
Maintainers review listing PRs for: package name follows convention, package is installable from npm, the lint command passes, the README meets the minimum, and a quick smell-check that the macros don't ship credentials or telemetry. Approval is intended to be fast — usually within a few days. Listing does not imply endorsement of the workflows themselves, only that the package meets the structural bar.
If your package is rejected, the rejection comes with the specific failed check; fix and re-open.
In addition to the registry, tag your repo with the GitHub topic macrokit-macros. The topic is the soft-discovery channel: searching topic:macrokit-macros on github.com finds packages whether or not they're in the registry.
If your package is registry-listed, the topic is redundant but harmless. If it's not (yet), the topic still gets you found by Macrokit-curious developers browsing the topic page.
The following are explicit anti-requests for community packages.
- No reference impls in the domains the maintainers operate in commercially. This is the same constraint Macrokit's own examples follow. Healthcare and legal are also generally off-limits as canonical reference impls — compliance gravity makes them a poor learning surface for a public ecosystem.
- No mock LLMs in handlers. If your macro needs an LLM in the inner loop (rare; usually means the workflow should be encoded differently), pull the adapter from
ctx.tools.llm, don't construct one. Stubbing one inside the handler hides where the dependency lives. - No browser launches in module top-level. Browser-driven macros should import
BrowserServiceas a type and read the service fromctx.tools.browser. Launching Chromium at import time turns a casualimportinto a multi-hundred-megabyte download. - No global registry mutation. Don't import
MacroRegistryand side-effect-register your macros at module load. Export the macros as values; let the adopter compose them into their own registry.
When your package is ready:
- Publish to npm.
- Tag the repo with the
macrokit-macrostopic on GitHub. - Open a PR against
github.com/macrokit/.githubadding the registry entry (see the registry README for the exact schema).
The registry PR is the only required step for ecosystem visibility. Everything else is your own release process.
Issues and discussion: github.com/macrokit/core/issues (use the community label).
{ "name": "macrokit-macros-paper-triage", "peerDependencies": { "@macrokit/authoring": "^0.1.0", "zod": "^3.23.0" } }