Get started free
Ship

Publish & registries

Your extension stays in your repo. A registry is a git repo of pointers to other repos, so listing costs a pull request and nobody has to trust a build service.

On this page(7 sections)

The shape of it

intentic does not host extension code, build it, sign it, or take a cut of it. A registry entry is a pointer to your repository at a commit, and installing follows that pointer directly from the owner's sandbox to your git host. Three consequences worth stating plainly:

  • Publishing is a pull request. No account, no upload or packaging step; the official registry runs both deterministic scanning and an adversarial agent audit against the exact source in that pull request.
  • You keep the code. Your repo is the artifact. Delisting doesn't delete anything; it only removes a pointer.
  • Registries are plural. The official one is a default, not a gate. A company can run a private registry of internal extensions and never touch ours.

Install without a registry at all

A registry is a convenience for discovery, never a requirement. Anyone with your repo URL and a commit sha can install your extension today from Capabilities → Add → Extension. That's the whole distribution mechanism; the marketplace just saves people from copy-pasting shas.

It also means a private extension needs no publication path: point a sandbox at an internal repo with a token and you're done.

A registry is a git repo

Add a .claude-plugin/marketplace.json to any repository and it is a registry. Mark an entry "kind": "extension" and it installs as a sha-pinned intentic extension.

.claude-plugin/marketplace.json
{
    "name": "acme-extensions",
    "plugins": [
        {
            "name": "acme.incidents",
            "kind": "extension",
            "trust": "listed",
            "description": "Incident triage in the rail: open alerts, acknowledge, jump to the failing run.",
            "version": "1.0.0",
            "icon": "exclamation-triangle",
            "source": { "source": "github", "repo": "acme/incidents", "sha": "9f2c1ab…" }
        },
        {
            "name": "acme.linear",
            "kind": "extension",
            "trust": "listed",
            "description": "Linear connector: the agent reads and files issues from its shell.",
            "version": "0.3.0",
            "logo": "linear",
            "source": { "source": "github", "repo": "acme/linear-connector", "sha": "4d81e07…" }
        }
    ]
}

The file uses Claude Code's plugin-marketplace format. kind and trust are intentic's own markers, and Claude Code ignores fields it doesn't know. So one registry repo can serve both: a team that already publishes agent plugins lists its intentic extensions in the same file, rather than standing up a second thing.

An entry's name is publisher.name read from your manifest, not a label the registry picks. That is the same identity the app installs under, so two publishers can both ship an incidents extension, and a repo that copies somebody else's manifest collides with their listing instead of shadowing it.

A second file sits beside it. registry.generated.json is written by the registry's nightly job and holds only facts read back off the source host: stars, and the last push date. It is separate so a nightly refresh never conflicts with an open pull request, and so a review diff shows the decision being made rather than a churn of star counts. It carries no "latest upstream commit": the approved sha is the one that runs, and advertising a newer one would invite a click that skips the review.

Source shapes

source says where the code is. A bare string is a path inside the registry repo itself; the object forms point outward.

What source can be
// A repo of its own
{ "source": "github", "repo": "acme/incidents", "sha": "9f2c1ab…" }

// Any git host
{ "source": "url", "url": "https://gitlab.com/acme/incidents.git", "sha": "9f2c1ab…" }

// A subdirectory: one repo, several extensions
{ "source": "git-subdir", "url": "https://github.com/acme/tools.git", "path": "ext/incidents", "sha": "9f2c1ab…" }

Give a sha. A branch name is accepted for plugins, but an extension install requires a full 40-character commit sha, so a registry entry without one can't be installed in a click; it makes the user go and find the sha themselves.

A registry you run can point wherever its users can clone. Admission to the official registry is narrower: executable sources must be public HTTPS repositories on GitHub, GitLab, Codeberg or Bitbucket. That keeps both automated reviewers on one inspectable object without placing private credentials in the admission workflow.

Getting listed

Two ways in, and they end at the same place: a pull request against the official registry. There is nowhere to sign up.

  1. Add the topic. Put intentic-extension on your GitHub repository, with an intentic-extension.json at its root. A nightly job resolves your latest commit first, checks the manifest and shipped bundle at that exact sha, and opens the pull request for you. That is the whole submission process. Your logo or icon is copied across with it, so the gallery card and the in-app browse row wear the mark you declared. A listing that declares neither shows its initials.
  2. Or open it yourself. Required if your extension lives in a subdirectory or is hosted on one of the other supported public git hosts, because GitHub topic discovery cannot propose it automatically.
  3. Two independent automated checks read the exact source. Trivy deterministically checks dependencies, committed secrets and deployment configuration. Then an intentic agent inspects the complete tree without executing author code: browser globals and egress, server/process/agent/bin/build surfaces, dependencies and shipped artifacts against source. A finding, blocked or unjudged audit, or missing evidence keeps the pull request unmergeable. A pass records both run identities against the repository, commit and optional subdirectory.

Publisher slugs are first-come and tied to proven ownership of the source repo. Because the listing key is the manifest-derived publisher.name, a scan that finds a repo claiming a slug somebody else already holds refuses it rather than opening a pull request that looks legitimate.

Listing is free and stays cut-free. Publishers can opt into a paid premium tier that draws from the creator pool, one field on your entry, and the whole economy is stated once on Earn.

What the person installing sees

The install dialog renders your manifest in plain English. It lists this extension adds a tile to your rail, reads your workspace logs, runs a background process or puts a tool on your agent's PATH, one line per declared contribution, with the route allowlist spelled out underneath, at one specific commit.

Which makes the manifest a piece of writing rather than only configuration. Declare the narrowest set of routes that works and the dialog is short and reassuring; declare a broad one and it is long and alarming, whatever the code actually does. The installer's page is the whole of what they are shown, and it is worth reading once from their side.

Available today, and next

Everything in Build an extension works now: authoring, sha-pinned install from any git repo public or private, the manifest gate, per-extension settings, and the on/off switch. So does the registry: the gallery, the topic scan that opens your pull request, and browsing plus one-click install straight from Sandbox → Discover, against the official registry or any repo you point it at. The update lifecycle also works now: the badges, powers diff, kept previous version, one-click revert, health watch, blocked-listing advisories, and the per-extension update policies.

Still to come: a typed npm artifact for @intentic/extension-ui so out-of-repo authors get the shell's components with full types.

Building something and want it listed? The build guide is the whole prerequisite. Add the topic when your first sha is pushed and the pull request writes itself.

Type to search every page, in the docs and the API reference.