Skip to main content

Overview

An extension package is a single Git repository that contains multiple extensions — for example, an integration, a plugin, and a widget that work together. This is the recommended approach when your extensions are tightly coupled (e.g., a Notion integration + Notion plugin + Notion widget).

Manifest format

Create a radarboard-extension.json file at the root of your repo:
radarboard-extension.json

Fields

Extension entry fields

Repository structure

Scaffolding

Use the scaffold tool to generate a starter extension repo:
This generates a complete repo with:
  • radarboard-extension.json manifest
  • package.json files for each extension with correct SDK dependencies
  • Starter TypeScript code with descriptor exports and capability stubs where relevant
  • biome.json, tsconfig.json, .gitignore
  • README with installation instructions

Capability-aware packaging

When an extension package contains both an integration and a widget for the same product area, keep the ownership model explicit:
  • Integrations should declare capabilities for the shared surfaces they provide.
  • Widgets should declare capabilities for the surfaces they own, using role: "canonical" or role: "specialized".
  • requiredIntegrations still helps with availability filtering, but capability ownership is what Radarboard uses for canonical widget governance.
This matters most when a package adds a new provider to an existing canonical widget. In those cases, the package should usually update the canonical widget’s provider list instead of shipping a second widget that duplicates the same capability.

Installation

When a user installs from a GitHub URL, Radarboard:
  1. Checks for radarboard-extension.json in the repo
  2. If found, validates each declared extension
  3. Clones the repo and extracts each extension to its correct directory (integrations/, plugins/, widgets/)
  4. Updates radarboard.config.ts with all extensions
  5. Runs pnpm generate:extensions and pnpm install
If no manifest is found, it falls back to single-extension validation (checking package.json name prefix).

Validation

Each extension in the package is validated independently:
  • Package name matches the manifest entry
  • Required SDK dependency is present (integration-sdk, plugin-sdk, or widget-sdk)
  • Export map has a . (default) entry
  • No cross-extension imports (widget importing plugin code, etc.)
  • No forbidden workspace dependencies
Run validation locally before publishing:

Dependency rules

Each extension type has allowed workspace dependencies: