-
Notifications
You must be signed in to change notification settings - Fork 75
docs(building-plugins): document npm plugin sources #1227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -327,10 +327,144 @@ Create `.factory-plugin/marketplace.json`: | |||||
| | `description` | No | Shown when browsing marketplaces | | ||||||
| | `owner` | No | Contact information | | ||||||
| | `plugins[].name` | Yes | Plugin identifier | | ||||||
| | `plugins[].source` | Yes | Relative path to plugin directory | | ||||||
| | `plugins[].source` | Yes | Where to fetch the plugin from. A relative path string or a source object. See [Plugin sources](#plugin-sources). | | ||||||
| | `plugins[].description` | No | Shown in plugin browser | | ||||||
| | `plugins[].category` | No | For organizing plugins | | ||||||
|
|
||||||
| ### Plugin sources | ||||||
|
|
||||||
| Each plugin entry's `source` field tells Droid where to fetch the plugin from. The default form, a relative path string like `"./plugin-one"`, points at a directory inside the marketplace repository. For plugins that live elsewhere, use a source object. | ||||||
|
|
||||||
| | Source type | Fields | Use when | | ||||||
| | :---------- | :----- | :------- | | ||||||
| | Relative path | `"./path/to/plugin"` | Plugin lives inside the marketplace repository. | | ||||||
| | `github` | `repo`, `ref?`, `sha?` | Plugin is hosted in its own GitHub repository. | | ||||||
| | `url` | `url`, `ref?`, `sha?` | Plugin is hosted on a non-GitHub git host (GitLab, Bitbucket, self-hosted). | | ||||||
| | `git-subdir` | `url`, `path`, `ref?`, `sha?` | Plugin lives inside a subdirectory of a larger git repository, such as a monorepo. | | ||||||
| | `npm` | `package`, `version?`, `registry?` | Plugin is published as an npm package. | | ||||||
|
|
||||||
| #### npm packages | ||||||
|
|
||||||
| Distribute plugins as npm packages when you already ship to a private registry (Artifactory, CodeArtifact, GitHub Packages, Verdaccio, and so on) and want to reuse that channel for your Droid plugins. Public packages on the npm registry work the same way. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Clarify npm versioning vs git commit-hash note This PR adds npm
Suggested change
|
||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "name": "pr-triage", | ||||||
| "source": { | ||||||
| "source": "npm", | ||||||
| "package": "@your-org/droid-pr-triage" | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Pin to a specific version or range: | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "name": "pr-triage", | ||||||
| "source": { | ||||||
| "source": "npm", | ||||||
| "package": "@your-org/droid-pr-triage", | ||||||
| "version": "2.4.0" | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Install from a private registry: | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "name": "pr-triage", | ||||||
| "source": { | ||||||
| "source": "npm", | ||||||
| "package": "@your-org/droid-pr-triage", | ||||||
| "version": "^2.0.0", | ||||||
| "registry": "https://npm.your-org.example" | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| | Field | Required | Description | | ||||||
| | :---- | :------- | :---------- | | ||||||
| | `package` | Yes | npm package name. Scoped packages (`@scope/name`) are supported. | | ||||||
| | `version` | No | npm version, range, or dist-tag (for example `2.4.0`, `^2.0.0`, `latest`). Defaults to `latest`. | | ||||||
| | `registry` | No | Custom registry URL. Defaults to the system npm registry. The setting is scoped to the install run and never written to your global `~/.npmrc`. | | ||||||
|
|
||||||
| **Layout requirements.** The published package root must contain a plugin manifest. Both the native Droid layout (`.factory-plugin/plugin.json`, `droids/`, `mcp.json`) and the Claude Code layout (`.claude-plugin/plugin.json`, `agents/`, `.mcp.json`) are accepted. Claude Code layouts are translated into Droid form when the package is copied into the plugin cache. | ||||||
|
|
||||||
| A typical published package looks like this: | ||||||
|
|
||||||
| ``` | ||||||
| @your-org/droid-pr-triage/ | ||||||
| ├── .factory-plugin/ | ||||||
| │ └── plugin.json | ||||||
| ├── package.json | ||||||
| ├── skills/ | ||||||
| │ └── classify/ | ||||||
| │ └── SKILL.md | ||||||
| ├── droids/ | ||||||
| │ └── triage-reviewer.md | ||||||
| └── README.md | ||||||
| ``` | ||||||
|
|
||||||
| Set the `files` field in `package.json` so only the plugin payload ships: | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "name": "@your-org/droid-pr-triage", | ||||||
| "version": "2.4.0", | ||||||
| "files": [ | ||||||
| ".factory-plugin", | ||||||
| "skills", | ||||||
| "droids", | ||||||
| "mcp.json", | ||||||
| "hooks", | ||||||
| "README.md" | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| **Hardening.** Droid runs `npm install` in a per-plugin scratch directory with `--ignore-scripts`, `--no-save`, `--no-audit`, and `--no-fund`. Lifecycle scripts (`postinstall`, `preinstall`, and so on) are not executed, and your global npm configuration is not consulted or mutated. | ||||||
|
|
||||||
| <Note> | ||||||
| `npm:` is not a valid marketplace source. Droid only accepts `npm` as a per-plugin source inside a marketplace's `marketplace.json`. To ship a single npm-published plugin to your team without standing up a full marketplace repo, publish a thin wrapper marketplace (see below). | ||||||
| </Note> | ||||||
|
|
||||||
| ##### Wrapper marketplace for a single npm plugin | ||||||
|
|
||||||
| To distribute one npm-published plugin without a dedicated marketplace repo, commit a small `marketplace.json` to any folder. The folder name becomes the marketplace name. | ||||||
|
|
||||||
| ``` | ||||||
| your-org-plugins/ | ||||||
| └── .factory-plugin/ | ||||||
| └── marketplace.json | ||||||
| ``` | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "name": "your-org-plugins", | ||||||
| "owner": { "name": "Your Org Platform Team" }, | ||||||
| "plugins": [ | ||||||
| { | ||||||
| "name": "pr-triage", | ||||||
| "description": "Triage and label new pull requests for the on-call rotation", | ||||||
| "source": { | ||||||
| "source": "npm", | ||||||
| "package": "@your-org/droid-pr-triage", | ||||||
| "version": "^2.0.0" | ||||||
| } | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Teammates add it like any other local marketplace: | ||||||
|
|
||||||
| ```bash | ||||||
| droid plugin marketplace add ./your-org-plugins | ||||||
| droid plugin install pr-triage@your-org-plugins | ||||||
| ``` | ||||||
|
|
||||||
| ### Version management | ||||||
|
|
||||||
| Use semantic versioning in your plugin manifest for documentation purposes: | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Resolve pinning guidance ambiguity across source types
The new Plugin sources table lists per-plugin
ref/shafor git-based sources, but the Version management note later says pinning a plugin requires pinning the marketplace, which is only true for relative-path plugins. This internal contradiction can mislead readers about how to pin plugins depending on the source type.