Compare commits

...

6 Commits

Author SHA1 Message Date
e3a0f46f9d merge(sdk): intègre feature/285-plugin-root-runtime-api — pluginRoot officiel activate(ctx) #285 (QA verte: npm run check) 2026-09-08 18:35:24 +02:00
9ce3a557a9 feat(sdk): pluginRoot officiel exposé dans activate(ctx) — #285 (src/runtime: champ pluginRoot = racine du package installé; docs activation-context/commands-and-feedback/services/manifest réécrits du constat d'absence vers le contrat d'usage officiel — runCommand reste project-scoped, pluginRoot utilisable dans command/args, substitution manifest ${pluginRoot}/${appDataDir} inchangée pour mcpServers; exemple hello-plugin: script packagé scripts/hello-task.mjs consommé via ctx.pluginRoot, packaging étendu; QA verte: npm run check — build, typecheck:examples, package:hello-plugin, artefact zip vérifié) 2026-09-08 18:35:24 +02:00
68019dec58 merge(sdk): intègre feature/285-plugin-root-scope-docs — clarification périmètre pluginRoot runtime #285 (QA verte: npm run build) 2026-09-08 17:20:05 +02:00
5f9c2f9193 docs(sdk): clarify pluginRoot scope for runtime plugins — #285 (activation-context: ActivateContext n'expose ni répertoire d'installation ni racine package ni file URL; commands-and-feedback + services: runCommand project/workspace-scoped, aucune résolution package-relative des scripts packagés; manifest: substitution ${pluginRoot}/${appDataDir} étendue par l'hôte uniquement dans contributes.mcpServers command/args/env/cwd; QA verte: npm run build, cohérence champs vs types SDK vérifiée) 2026-09-08 17:19:57 +02:00
7db819c647 merge(sdk): intègre feature/283-plugin-skill-assignment-docs — doc assignation skills plugin aux agents #283 (QA verte: npm run build, liens docs vérifiés) 2026-09-08 16:34:20 +02:00
1b5ab2fa86 docs(sdk): schéma project-plugins.json v1 + assignation skills/tools agents — #283 (project-plugin-assignments: workflow install→enable→assign→relaunch, schéma complet plugins/agents/skills/tools camelCase, ordre d'injection natif puis plugin, tombstones inertes uninstall/reinstall, validation UI/API idempotente vs édition manuelle, reload plugin ≠ relance agent; liens croisés manifest/codex-skills/README; QA verte: npm run build, liens docs vérifiés) 2026-09-08 16:34:03 +02:00
12 changed files with 240 additions and 7 deletions

View File

@ -17,6 +17,7 @@ exports `activate(ctx)`.
- [Services](docs/services.md): workspace, tasks, tooling, events, config, terminal and windows facades.
- [Packaging And Distribution](docs/packaging-distribution.md): build output, archive layout, hot reload and dependency rules.
- [Codex Skills And IdeA Plugins](docs/codex-skills-and-idea-plugins.md): separate IdeA-native plugin skills from Codex plugin discovery.
- [Project Plugin Assignments](docs/project-plugin-assignments.md): enable plugins per project and assign skills and MCP tools to agents.
The installable example lives in [`examples/hello-plugin`](examples/hello-plugin).
It demonstrates a menu command, storage, background-task feedback, a React layout

View File

@ -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;
@ -27,7 +30,8 @@ focused project at invocation/render time.
## Context Fields
- `pluginId`, `pluginDisplayName`, `version`: host-provided identity.
- `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,6 +45,12 @@ focused project at invocation/render time.
The context never exposes internal IdeA gateways or Tauri commands. Use
`ctx.services` and the registration APIs instead.
`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
Push every returned disposable to `ctx.subscriptions`:

View File

@ -40,7 +40,9 @@ loading.
For multi-harness IdeA agent skills, declare `contributes.skills` in
`idea-plugin.json`, ship the referenced Markdown files inside the package,
install the plugin in IdeA, enable it in the target project, and assign the
skills to the target agents.
skills to the target agents. See
[Project Plugin Assignments](project-plugin-assignments.md) for the project
settings schema and the complete install, enable, assign, and relaunch workflow.
For Codex-specific plugin skills, install the Codex plugin through the Codex
plugin mechanism for the intended scope, then assign or enable those skills

View File

@ -43,6 +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` 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:
@ -196,3 +200,20 @@ await ctx.services?.tasks.runCommand({
This creates misleading Work history, uses a placeholder owner and turns a
precondition failure into a fake task. Return `status: "skipped"` instead.
Do not try to reach packaged plugin scripts through workspace-relative paths:
```ts
await ctx.services?.tasks.runCommand({
projectId: project.id,
ownerAgentId,
label: "Run packaged tests",
command: "scripts/run-unity-tests.sh",
cwd: "."
});
```
`scripts/run-unity-tests.sh` above is resolved like any other executable visible
from the project task environment; it is not resolved relative to the plugin
package. Use a project-owned script or an executable available on `PATH` until a
dedicated packaged-resource API exists.

View File

@ -177,6 +177,10 @@ Skill contribution rules:
- Installing a plugin globally is not enough: a project must enable the plugin,
then assign the skill to the target agent.
See [Project Plugin Assignments](project-plugin-assignments.md) for the
`.ideai/project-plugins.json` schema, validation rules, assignment order, and
agent relaunch requirements.
`contributes.layouts` is the only surface for plugin layouts. The same layout
contribution can be mounted inside an IdeA layout cell or opened in a detached OS
window. Do not add a separate manifest contribution type for windows.
@ -243,6 +247,19 @@ Contribution ids are part of the runtime contract:
- `services.windows.open({ layoutType })` only accepts a layout type declared by
the calling plugin.
## MCP Server Paths
`contributes.mcpServers` entries are resolved by the host before starting a
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 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
Use the SDK validator in tests or build tooling:

View File

@ -0,0 +1,154 @@
# Project Plugin Assignments
IdeA installs plugin packages globally, but enables their capabilities per
project and assigns agent-facing capabilities per agent. Project choices are
stored in the project-owned `.ideai/project-plugins.json` file.
The normal workflow is:
1. Install the plugin globally in IdeA.
2. Enable the installed plugin for the target project.
3. Assign selected plugin skills and MCP tools to each target agent.
4. Relaunch the agent so its capability snapshot includes the new assignments.
Installation alone does not enable a plugin in a project. Enabling a plugin does
not automatically grant all of its capabilities to every agent.
## Version 1 Schema
The current schema version is `1`. A complete example is:
```json
{
"version": 1,
"plugins": [
{
"pluginId": "com.example.unity-plugin",
"enabled": true
}
],
"agents": [
{
"agentId": "2a0e42e2-3c5f-4f30-a679-034b460cd59e",
"skills": [
{
"pluginId": "com.example.unity-plugin",
"skillId": "unity.build-debug"
}
],
"tools": [
{
"pluginId": "com.example.unity-plugin",
"serverId": "unity-editor",
"toolName": "unity_build"
}
]
}
]
}
```
The top-level fields are:
- `version`: required schema version. It must currently be `1`; unsupported
versions are rejected.
- `plugins`: project activation entries. Each entry contains the stable global
`pluginId` and an `enabled` boolean.
- `agents`: per-agent assignments. Each entry identifies an IdeA project agent
by `agentId` and contains ordered `skills` and `tools` arrays.
An agent skill reference contains:
- `pluginId`: the id from the owning plugin's `idea-plugin.json`.
- `skillId`: the stable `contributes.skills[].id` within that plugin.
An agent MCP tool reference contains:
- `pluginId`: the id from the owning plugin's `idea-plugin.json`.
- `serverId`: the stable `contributes.mcpServers[].id` within that plugin.
- `toolName`: the exact tool name advertised by that MCP server. The UI accepts
this name as text because a dynamic MCP tool catalogue is not yet available.
Missing `plugins`, `agents`, `skills`, or `tools` arrays default to empty arrays
when IdeA reads the document. Prefer writing them explicitly for clarity and
forward-compatible review.
## Assignment And Injection Order
Array order is significant. IdeA keeps plugin skill assignments in the order in
which they were added. At agent launch, IdeA resolves the agent's native skills
first, in agent-manifest order, then its plugin skills in the order stored in
`agents[].skills`. Unavailable assignments are skipped without reordering the
remaining skills.
The resolved skill catalogue is injected into the agent's orchestration context
before the agent persona. Each supported harness receives the same ordered
IdeA-native capability snapshot. The skill body remains read-only package
content and is available through `idea_skill_read` only when that skill was
resolved for the launched agent.
## Tombstones
Plugin activation entries and agent assignments are intentionally retained when
a plugin, skill, or MCP server is temporarily missing. These dangling entries
are tombstones: they preserve project intent across uninstall/reinstall or a
temporary manifest change.
A tombstone is inert. IdeA skips it when resolving capabilities, and the project
settings UI marks stale skill or server references as missing or obsolete. The
UI lets users remove those references explicitly. Reinstalling and activating a
matching plugin makes a retained assignment resolvable again.
Disabling a plugin also keeps its assignments. They become effective again only
after the plugin is enabled and runtime-active for the project.
## UI And API Validation
Prefer the project plugin settings UI or its backing API commands over manual
editing. They apply the following rules:
- Enabling requires the plugin package to be installed globally. Disabling a
missing plugin is allowed so a retained activation can be cleared.
- Assigning a skill requires an existing project agent, an enabled and
runtime-active plugin, and a matching `contributes.skills[].id`.
- Assigning a tool requires an existing project agent, an enabled and
runtime-active plugin, a matching `contributes.mcpServers[].id`, and a
non-empty `toolName`.
- Assign and unassign operations are idempotent. Assigning an existing reference
does not duplicate it; unassigning a missing reference is harmless.
- Unassign operations remain available for tombstones, so stale references can
be removed even when their plugin capability no longer resolves.
The project settings read API returns both the persisted document and the
currently installed plugin manifests. The UI uses this catalogue to offer only
skills and MCP servers from plugins enabled for the project, while still
surfacing persisted tombstones.
## Manual Editing
The file is project-owned and may be edited manually, for example in version
control or automation. Stop or relaunch affected agents around the edit, keep
the document valid JSON, preserve `version: 1`, use the real project agent UUID,
and copy plugin, skill, and server ids exactly from the plugin manifest.
Manual editing bypasses the mutation checks described above. A syntactically
valid document may therefore contain duplicates, unknown agents, disabled or
missing plugins, unknown capability ids, or an invalid tool name. Such entries
may remain inert or fail later operations. Use the UI/API whenever possible to
receive immediate validation and idempotent updates.
## Reloading Plugins And Relaunching Agents
These actions solve different problems:
- Reload the plugin after rebuilding or replacing its installed package. Reload
refreshes the plugin manifest, runtime bundle, contributions, and packaged
skill Markdown. It does not change project activation or agent assignments.
- Relaunch the agent after enabling or disabling a project plugin, changing its
skill/tool assignments, or manually editing `.ideai/project-plugins.json`.
Agent capabilities are resolved into a launch-time snapshot, so an already
running agent does not acquire assignment changes retroactively.
When both package content and assignments changed, reload the plugin first,
confirm it is enabled for the project, update the assignments, then relaunch the
agent.

View File

@ -37,6 +37,25 @@ Key APIs:
Use `runCommand()` only after preconditions are satisfied. `ownerAgentId` must be
a real agent id when work belongs to an agent workflow.
`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
`services.tooling.diagnose()` checks executables, environment values and files

View File

@ -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

View File

@ -0,0 +1 @@
console.log("hello from a script packaged with @idea/plugin-sdk");

View File

@ -122,12 +122,13 @@ export async function runHelloCommandTask(ctx: ActivateContext): Promise<HelloCo
return feedback;
}
const pluginRoot = ctx.pluginRoot.replace(/[\\/]+$/, "");
const task = await ctx.services.tasks.runCommand({
projectId: project.id,
ownerAgentId,
label: "Hello plugin command",
command: "echo",
args: ["hello from @idea/plugin-sdk"],
command: "node",
args: [`${pluginRoot}/scripts/hello-task.mjs`],
cwd: ".",
recordOnly: true
});

View File

@ -9,12 +9,14 @@ const main = requireString(manifest, "main");
const version = requireString(manifest, "version");
const archivePath = join(pluginRoot, "build", `hello-plugin-${version}.zip`);
const distEntries = await collectFiles(join(pluginRoot, "dist"), "dist");
const scriptEntries = await collectFiles(join(pluginRoot, "scripts"), "scripts");
if (!distEntries.some((entry) => 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;

View File

@ -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;