feat(sdk): add plugin activationScope (app/project)

Manifests can now declare activationScope to defer activation until a
project is focused, instead of always activating at app bootstrap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 14:43:23 +02:00
parent 6bca9cc4c0
commit e509e796b4
3 changed files with 25 additions and 0 deletions

View File

@ -64,10 +64,17 @@ TypeScript.
"version": "0.1.0", "version": "0.1.0",
"main": "dist/index.js", "main": "dist/index.js",
"trustLevel": "full", "trustLevel": "full",
"activationScope": "app",
"contributes": {} "contributes": {}
} }
``` ```
`activationScope` is optional and defaults to `"app"`, which activates the
plugin at app bootstrap without requiring a focused project. Use
`"activationScope": "project"` only when `activate(ctx)` needs project-scoped
services immediately; IdeA will keep that plugin pending until a project is
focused, then activate it once for the app session.
The `main` field is the ESM entrypoint loaded by IdeA. It may import other The `main` field is the ESM entrypoint loaded by IdeA. It may import other
JavaScript files from the same plugin package with relative specifiers: JavaScript files from the same plugin package with relative specifiers:

View File

@ -1,4 +1,5 @@
export type { export type {
IdeAPluginActivationScope,
IdeAPluginCapability, IdeAPluginCapability,
IdeAPluginManifest, IdeAPluginManifest,
IdeAPluginEngineConstraints, IdeAPluginEngineConstraints,

View File

@ -9,6 +9,10 @@ export interface IdeAPluginManifest {
publisher?: string; publisher?: string;
engines?: IdeAPluginEngineConstraints; engines?: IdeAPluginEngineConstraints;
capabilities?: IdeAPluginCapability[]; capabilities?: IdeAPluginCapability[];
/**
* Defaults to "app". Use "project" when activate(ctx) requires a focused project.
*/
activationScope?: IdeAPluginActivationScope;
contributes?: { contributes?: {
menus?: IdeAPluginTopLevelMenuContribution[]; menus?: IdeAPluginTopLevelMenuContribution[];
menuItems?: IdeAPluginMenuItemContribution[]; menuItems?: IdeAPluginMenuItemContribution[];
@ -19,6 +23,8 @@ export interface IdeAPluginManifest {
export type IdeAPluginCapability = "ui" | "mcp" | "tooling"; export type IdeAPluginCapability = "ui" | "mcp" | "tooling";
export type IdeAPluginActivationScope = "app" | "project";
export interface IdeAPluginEngineConstraints { export interface IdeAPluginEngineConstraints {
idea?: string; idea?: string;
} }
@ -100,6 +106,7 @@ export function validatePluginManifest(input: unknown): PluginManifestValidation
validateEngines(input.engines, errors); validateEngines(input.engines, errors);
validateCapabilities(input.capabilities, errors); validateCapabilities(input.capabilities, errors);
validateActivationScope(input.activationScope, errors);
validateContributes(input.contributes, errors); validateContributes(input.contributes, errors);
if (errors.length > 0) { if (errors.length > 0) {
@ -151,6 +158,16 @@ function validateCapabilities(value: unknown, errors: string[]): void {
}); });
} }
function validateActivationScope(value: unknown, errors: string[]): void {
if (value === undefined) {
return;
}
if (value !== "app" && value !== "project") {
errors.push("activationScope must be \"app\" or \"project\" when provided");
}
}
function validateContributes(value: unknown, errors: string[]): void { function validateContributes(value: unknown, errors: string[]): void {
if (value === undefined) { if (value === undefined) {
return; return;