CREATE AND PUBLISH AN EXTENSION
This guide shows you how to create a TypeScript extension, publish it to the swamp registry, and install it in another repo.
Prerequisites
- A swamp repository (
swamp repo init) - A swamp account (
swamp auth login)
Write the extension model
Create the extension file at extensions/models/disk_usage.ts:
import { z } from "npm:zod@4";
export const model = {
type: "@mycollective/disk-usage",
version: "2026.04.03.1",
globalArguments: z.object({
path: z.string().describe("Filesystem path to check"),
}),
resources: {
result: {
description: "Disk usage statistics",
schema: z.object({
path: z.string(),
totalBytes: z.number(),
usedBytes: z.number(),
availableBytes: z.number(),
usedPercent: z.number(),
}),
lifetime: "infinite" as const,
garbageCollection: 10,
},
},
methods: {
check: {
description: "Check disk usage at the configured path",
arguments: z.object({}),
execute: async (
_args: Record<string, unknown>,
context: Record<string, unknown>,
) => {
const globalArgs = context.globalArgs as { path: string };
const cmd = new Deno.Command("df", {
args: ["-B1", globalArgs.path],
stdout: "piped",
stderr: "piped",
});
const output = await cmd.output();
const stdout = new TextDecoder().decode(output.stdout);
const lines = stdout.trim().split("\n");
const parts = lines[1].split(/\s+/);
const totalBytes = parseInt(parts[1]);
const usedBytes = parseInt(parts[2]);
const availableBytes = parseInt(parts[3]);
const usedPercent = Math.round((usedBytes / totalBytes) * 100);
const writeResource = context.writeResource as (
spec: string,
name: string,
data: Record<string, unknown>,
) => Promise<{ name: string }>;
const resultHandle = await writeResource("result", "result", {
path: globalArgs.path,
totalBytes,
usedBytes,
availableBytes,
usedPercent,
});
return { dataHandles: [resultHandle] };
},
},
},
};Replace @mycollective with your collective name. Run swamp auth whoami to
check your username and collectives.
Pin your imports
The example uses npm:zod@4 — note the @4 version pin. Pin every non-local
import for reproducibility:
npm:lodash-es@4.17.21jsr:@std/assert@1.0.0https://deno.land/std@0.224.0/async/delay.ts(version in the URL)
Any Deno-compatible specifier works — npm:, jsr:, and https: are all
inlined into the bundle at push time. Unpinned specifiers resolve to the
registry's current "latest" when the extension is bundled, so the published
bundle silently changes whenever the upstream package publishes a new version.
Test the model locally
Create a model definition from the new type:
$ swamp model create @mycollective/disk-usage home-disk --global-arg path=/homeThe output confirms the model was created and lists its global arguments and methods.
Run the method to confirm it executes:
$ swamp model method run home-disk checkThe report at the end shows succeeded and the number of resources produced.
Create the manifest
Create extensions/models/manifest.yaml:
manifestVersion: 1
name: "@mycollective/disk-usage"
version: "2026.04.03.1"
description: "Check disk usage statistics for a filesystem path"
models:
- disk_usage.ts
labels:
- monitoring
- filesystemThe name must start with your collective prefix (@username/ or @org/). Run
swamp auth whoami to check which collectives you belong to.
Bump the version
Before each publish, get the next version from the registry:
$ swamp extension version --manifest extensions/models/manifest.yamlYou will see output like:
info extension·version Current published: (none — not yet published)
info extension·version Next version (today): "2026.04.13.1"Update manifest.yaml with the next version. If the model source file also
contains a version field, update it to match.
Format, lint, and validate
Format the extension source:
$ swamp extension fmt extensions/models/manifest.yamlTo check without modifying files, add --check:
$ swamp extension fmt extensions/models/manifest.yaml --checkYou will see output like:
info extension·fmt All quality checks passed.Validate with a dry run before publishing:
$ swamp extension push extensions/models/manifest.yaml --dry-runThe dry run validates the extension without uploading. If the push reports
safety warnings (e.g., Deno.Command() usage), you will be prompted to confirm.
Publish to the registry
$ swamp extension push extensions/models/manifest.yamlYou will see output like:
info extension·push Pushed "@mycollective/disk-usage"@"2026.04.13.1"
info extension·push Extension ID: "bbc4f071-..."
info extension·push Archive size: "1.4KB"
info extension·push "Models: 1, Workflows: 0, Vaults: 0, Bundles: 1"If the version already exists, the CLI offers to bump the MICRO component.
Add --release-notes "description of changes" to attach release notes, or add a
releaseNotes field to the manifest.
Tip
If you use Claude Code, the /swamp-extension-publish skill walks you through
the full publishing prerequisites — repo initialization, authentication,
manifest validation, collective ownership, version bumping, formatting, and
dry-run — before allowing the push.
Install the extension in another repo
In a different swamp repo, pull the extension:
$ swamp extension pull @mycollective/disk-usageThe model type is now available. Create definitions from it with
swamp model create @mycollective/disk-usage <name>.
To restore pulled extensions after cloning (e.g., in CI), run
swamp extension install.