Plugin authoring

OpenSIP CLI is extensible across five shapes, listed in increasing order of effort and capability. Pick the one that matches what you're trying to ship.

| # | Shape | When to use | Effort | Read |

|---|---|---|---|---|

| 1 | Project-local check — a .mjs file under opensip-cli/fit/checks/ | Add one rule to one repo. No publishing. | ~10 lines | Project-local plugins |

| 2 | Project-local recipe — a .mjs file under opensip-cli/fit/recipes/ | Define a named lineup of checks for CI ("quick-smoke for pre-commit"). | ~10 lines | Project-local plugins |

| 3 | Project-local sim scenario — a .mjs file under opensip-cli/sim/scenarios/ | Run load / chaos simulations against your service. | ~30 lines | Project-local plugins |

| 4 | Publishable pack — a fit package declaring the fit-pack marker plus target-domain epoch, or a sim package using the scenarios-* name pattern | Ship the same checks/scenarios across multiple projects, or organize a large in-repo set. | ~100-500 lines + tooling | Publishable packs |

| 5 | Full Tool plugin — an npm package declaring opensipTools.kind: "tool" | Your own subcommand. Fundamentally different from fit/sim/graph (e.g. audit-sec, bench). | ~50-150 lines per Tool | Full Tool plugins |

Pick by question

"I want to ban an API in my codebase." → Shape 1 (project-local check, or the Ban an API pattern guide for a walkthrough).

"I want our team's quality bar shared across five repos." → Shape 4 (publishable packs). Workspace pack if all five are in one monorepo, published npm pack if they're separate.

"I want a different lineup of checks for pre-commit vs. CI." → Shape 2 (two project-local recipes; select with --recipe <name>).

"I want to simulate a load test as part of the gate." → Shape 3 (project-local sim scenario).

"I want a security-audit tool that integrates with the CLI but isn't fit-shaped." → Shape 5 (full Tool plugin). The Tool contract is the seam; the CLI doesn't know what your Tool does.

"I want to add a new language to graph." → Different surface — see adding a language. Graph language adapters use a separate contract from fit language adapters; both are documented but live in different sections.

"I want to add a new language to fit."Language adapters. Authoring path for the fit-side adapter that strips strings/comments for a new language.

The big mental model

Every shape above plugs into the same kernel. The CLI is a generic dispatcher; it discovers your package (by marker, exact pin, or the sim scenarios-* naming pattern), reads a known declaration (a Tool's commandSpecs that the host mounts, or the checks / recipes / scenarios arrays for packs), and routes the rest. There are no hooks, no middleware, no event buses — just a registry walk at startup.

raw-stream output (sanctioned escape hatch)

Most tool commands return a signal-envelope or command-result and let the host dispatch output. Primary commands with multi-mode runtime dispatch (fit, graph, sim) declare output: 'raw-stream' instead: the handler owns render, egress, and exit-code decisions. This is intentional, not a seam bypass — but new commands should default to host dispatch unless multi-mode IO is required. In-repo fitness checks (raw-stream-output-guarded) require an in-file justification when a command spec uses raw-stream.

That's by design. The whole point of the platform is that adding a new tool or pack requires zero CLI changes. For the architecture: the tool-plugin model walks through it end-to-end.

Trust boundary for external tools

Bundled tools are the trusted computing base. Installed and project-local Tool

plugins still run in the host process during the ADR-0054 transition — see

Full Tool plugins — External tool trust boundary.

Use opensip tools validate before adopting a third-party tool in CI.

Where to go next