From 9ce3a557a99eb767782403c2167ffaac3bd02691 Mon Sep 17 00:00:00 2001 From: Blomios Date: Tue, 8 Sep 2026 18:35:24 +0200 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20pluginRoot=20officiel=20expos?= =?UTF-8?q?=C3=A9=20dans=20activate(ctx)=20=E2=80=94=20#285=20(src/runtime?= =?UTF-8?q?:=20champ=20pluginRoot=20=3D=20racine=20du=20package=20install?= =?UTF-8?q?=C3=A9;=20docs=20activation-context/commands-and-feedback/servi?= =?UTF-8?q?ces/manifest=20r=C3=A9=C3=A9crits=20du=20constat=20d'absence=20?= =?UTF-8?q?vers=20le=20contrat=20d'usage=20officiel=20=E2=80=94=20runComma?= =?UTF-8?q?nd=20reste=20project-scoped,=20pluginRoot=20utilisable=20dans?= =?UTF-8?q?=20command/args,=20substitution=20manifest=20${pluginRoot}/${ap?= =?UTF-8?q?pDataDir}=20inchang=C3=A9e=20pour=20mcpServers;=20exemple=20hel?= =?UTF-8?q?lo-plugin:=20script=20packag=C3=A9=20scripts/hello-task.mjs=20c?= =?UTF-8?q?onsomm=C3=A9=20via=20ctx.pluginRoot,=20packaging=20=C3=A9tendu;?= =?UTF-8?q?=20QA=20verte:=20npm=20run=20check=20=E2=80=94=20build,=20typec?= =?UTF-8?q?heck:examples,=20package:hello-plugin,=20artefact=20zip=20v?= =?UTF-8?q?=C3=A9rifi=C3=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/activation-context.md | 16 ++++++++----- docs/commands-and-feedback.md | 7 +++--- docs/manifest.md | 7 +++--- docs/services.md | 24 +++++++++++++++----- examples/hello-plugin/README.md | 7 ++++-- examples/hello-plugin/scripts/hello-task.mjs | 1 + examples/hello-plugin/src/core/workspace.ts | 5 ++-- scripts/package-hello-plugin.mjs | 2 ++ src/runtime.ts | 2 ++ 9 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 examples/hello-plugin/scripts/hello-task.mjs diff --git a/docs/activation-context.md b/docs/activation-context.md index afe2175..2814981 100644 --- a/docs/activation-context.md +++ b/docs/activation-context.md @@ -6,7 +6,10 @@ The plugin entrypoint exports `activate(ctx)`. import type { ActivateContext, IdeAPluginModule } from "@idea/plugin-sdk"; export function activate(ctx: ActivateContext): void { - ctx.logger.info("activated", { pluginId: ctx.pluginId }); + ctx.logger.info("activated", { + pluginId: ctx.pluginId, + pluginRoot: ctx.pluginRoot + }); } export default { activate } satisfies IdeAPluginModule; @@ -28,6 +31,7 @@ focused project at invocation/render time. ## Context Fields - `pluginId`: host-provided plugin identity. +- `pluginRoot`: absolute host-local path to the active installed plugin package. - `logger`: `debug`, `info`, `warn`, `error`. - `subscriptions`: push disposables returned by command/layout/watch registrations. @@ -41,11 +45,11 @@ focused project at invocation/render time. The context never exposes internal IdeA gateways or Tauri commands. Use `ctx.services` and the registration APIs instead. -`ActivateContext` does not expose the plugin installation directory, package -root, archive root or a file URL that plugins can convert into a local path. -Plugin runtime code must treat packaged files as unavailable to -`ctx.services.tasks.runCommand()` unless a dedicated public SDK API documents -otherwise. Workspace services resolve project-owned paths only. +`pluginRoot` identifies the committed package currently loaded by IdeA. It is +not the original source directory or archive path and may change after reinstall. +Treat it as read-only, do not persist it, and resolve packaged scripts/assets +under it only while the plugin is active. Workspace services remain confined to +project-owned paths. ## Disposal diff --git a/docs/commands-and-feedback.md b/docs/commands-and-feedback.md index c86dde9..7787086 100644 --- a/docs/commands-and-feedback.md +++ b/docs/commands-and-feedback.md @@ -43,9 +43,10 @@ Check preconditions before calling `runCommand()`: - `ownerAgentId` is a real agent id when the work is owned by an agent workflow. Do not use all-zero placeholders in production. - `cwd` is relative to the project root. -- `command`, `args` and `cwd` do not resolve package-relative plugin resources. - Do not use `runCommand()` to launch scripts shipped inside the plugin package; - there is no public runtime `pluginRoot` path in `activate(ctx)`. +- `command` and `args` do not resolve package-relative plugin resources + implicitly. Build an explicit absolute script path from `ctx.pluginRoot` when + launching read-only files shipped in the installed package. +- Do not use `ctx.pluginRoot` as `cwd`; `cwd` remains relative to the project root. - `command` and `args` are separate values. Do not shell-join user input. If any required precondition fails, return a skipped result: diff --git a/docs/manifest.md b/docs/manifest.md index 98bccaa..b965d1c 100644 --- a/docs/manifest.md +++ b/docs/manifest.md @@ -255,9 +255,10 @@ declared MCP server. In `command`, `args`, `env` and `cwd`, the host expands: - `${pluginRoot}` to the installed plugin package root. - `${appDataDir}` to the host-owned application data directory. -This substitution is limited to manifest-declared MCP server startup. It is not -available from `activate(ctx)`, `ctx.services.workspace` or -`ctx.services.tasks.runCommand()`. +This string substitution is limited to manifest-declared MCP server startup. +Runtime handlers receive the same installed package location separately as +`ctx.pluginRoot`; `ctx.services.tasks.runCommand()` does not perform placeholder +substitution, and workspace APIs remain project-confined. ## Validation diff --git a/docs/services.md b/docs/services.md index 3371aad..282a93b 100644 --- a/docs/services.md +++ b/docs/services.md @@ -37,12 +37,24 @@ Key APIs: Use `runCommand()` only after preconditions are satisfied. `ownerAgentId` must be a real agent id when work belongs to an agent workflow. -`runCommand()` is project/workspace scoped. Its `cwd` option is a relative path -under the project root, and `command`/`args` are not resolved against the -calling plugin's package. The SDK does not currently provide a public -`pluginRoot` or package-path resolver for runtime command handlers. Packaged -scripts cannot be launched through `runCommand()` by referring to their -package-relative path. +`runCommand()` keeps its `cwd` project/workspace scoped: `cwd` must be a relative +path under the project root. The host does not implicitly resolve `command` or +`args` against the plugin package. Use the absolute `ctx.pluginRoot` supplied at +activation time to construct an explicit path to a packaged script, and pass +that path as `command` or as an argument to its interpreter. Treat package files +as read-only. + +```ts +const script = `${ctx.pluginRoot.replace(/[\\/]+$/, "")}/scripts/check.mjs`; +await ctx.services.tasks.runCommand({ + projectId, + ownerAgentId, + label: "Run packaged check", + command: "node", + args: [script], + cwd: "." +}); +``` ## Tooling diff --git a/examples/hello-plugin/README.md b/examples/hello-plugin/README.md index 5e8c172..567b896 100644 --- a/examples/hello-plugin/README.md +++ b/examples/hello-plugin/README.md @@ -16,6 +16,8 @@ It exercises the current plugin primitives end to end: `ctx.services.windows.open(...)` when services are available. - plugin-owned storage: activation count, command run count and initialization flag; - tooling capability: logs the focused workspace project when `ctx.services` is available. +- packaged command script: resolves `scripts/hello-task.mjs` from the official + `ctx.pluginRoot` and launches it with the project kept as the task working directory. The source is intentionally split across multiple TypeScript modules: @@ -23,6 +25,7 @@ The source is intentionally split across multiple TypeScript modules: - `src/constants.ts` owns shared command/layout identifiers; - `src/core/layout.tsx` and `src/core/workspace.ts` hold feature logic. - `src/core/storage.ts` keeps plugin-owned counters and flags in `ctx.storage`. +- `scripts/hello-task.mjs` is a read-only runtime resource included in the archive. The build uses plain `tsc`; it does not bundle the plugin into one file. The archive includes all compiled `dist/**/*.js` files so IdeA can load `dist/index.js` and serve its package-relative imports @@ -36,8 +39,8 @@ npm run package:hello-plugin ``` The installable archive is emitted at `examples/hello-plugin/build/hello-plugin-0.1.0.zip`. -It contains `idea-plugin.json` at the ZIP root and the compiled multi-file ESM output under `dist/`, -including `dist/index.js`, matching the manifest `main` field. +It contains `idea-plugin.json` at the ZIP root, the compiled multi-file ESM output under `dist/`, +including `dist/index.js`, and the packaged handler under `scripts/hello-task.mjs`. ## Diagnostics diff --git a/examples/hello-plugin/scripts/hello-task.mjs b/examples/hello-plugin/scripts/hello-task.mjs new file mode 100644 index 0000000..1865c37 --- /dev/null +++ b/examples/hello-plugin/scripts/hello-task.mjs @@ -0,0 +1 @@ +console.log("hello from a script packaged with @idea/plugin-sdk"); diff --git a/examples/hello-plugin/src/core/workspace.ts b/examples/hello-plugin/src/core/workspace.ts index 093ed83..0b3e847 100644 --- a/examples/hello-plugin/src/core/workspace.ts +++ b/examples/hello-plugin/src/core/workspace.ts @@ -122,12 +122,13 @@ export async function runHelloCommandTask(ctx: ActivateContext): Promise entry.archivePath === main)) { throw new Error(`Built plugin dist does not contain manifest main: ${main}`); } const archiveEntries = [ { archivePath: "idea-plugin.json", sourcePath: manifestPath }, ...distEntries, + ...scriptEntries, { archivePath: "README.md", sourcePath: join(pluginRoot, "README.md") } ]; const DOS_TIME_MIDNIGHT = 0; diff --git a/src/runtime.ts b/src/runtime.ts index e1728c7..a61922d 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -2,6 +2,8 @@ import type { ComponentType, ReactNode } from "react"; export interface ActivateContext { pluginId: string; + /** Absolute host-local root of this active installed plugin package. */ + pluginRoot: string; logger: PluginLogger; subscriptions: CommandDisposable[]; commands?: CommandRegistry;