docs(sdk): feedback visible via windows.open + séparation Codex skills documentées — commands-and-feedback (surface normative + exemple), layouts-react (type↔contributes.layouts, capacité ui), manifest (exemple complet menu+layout+ui), windows (contrat ui, retour structuré), packaging-distribution (idea_plugin_install_from_directory, sémantique plugin_reloaded/ESM), nouveau codex-skills-and-idea-plugins, README (lien) — #280 QA verte
This commit is contained in:
@ -16,6 +16,7 @@ exports `activate(ctx)`.
|
|||||||
- [Windows](docs/windows.md): opening plugin layouts in detached OS windows.
|
- [Windows](docs/windows.md): opening plugin layouts in detached OS windows.
|
||||||
- [Services](docs/services.md): workspace, tasks, tooling, events, config, terminal and windows facades.
|
- [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.
|
- [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 runtime install from Codex skill discovery.
|
||||||
|
|
||||||
The installable example lives in [`examples/hello-plugin`](examples/hello-plugin).
|
The installable example lives in [`examples/hello-plugin`](examples/hello-plugin).
|
||||||
It demonstrates a menu command, storage, background-task feedback, a React layout
|
It demonstrates a menu command, storage, background-task feedback, a React layout
|
||||||
|
|||||||
41
docs/codex-skills-and-idea-plugins.md
Normal file
41
docs/codex-skills-and-idea-plugins.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Codex Skills And IdeA Plugins
|
||||||
|
|
||||||
|
IdeA runtime plugins and Codex plugins are separate installation and discovery
|
||||||
|
systems.
|
||||||
|
|
||||||
|
An IdeA runtime plugin is described by `idea-plugin.json`. Installing it through
|
||||||
|
IdeA makes its IdeA runtime contributions available: menus, menu items, slash
|
||||||
|
commands, layouts, MCP servers and runtime services declared by the IdeA plugin
|
||||||
|
manifest.
|
||||||
|
|
||||||
|
A Codex plugin is described by `.codex-plugin/plugin.json`. Its `skills` entry
|
||||||
|
points Codex at skill directories such as `skills/*/SKILL.md`. This metadata is
|
||||||
|
for Codex plugin installation and skill discovery, not for IdeA runtime plugin
|
||||||
|
loading.
|
||||||
|
|
||||||
|
## Normative Behavior
|
||||||
|
|
||||||
|
- Installing or reloading an IdeA plugin does not automatically install a bundled
|
||||||
|
Codex plugin.
|
||||||
|
- Installing or reloading an IdeA plugin does not automatically make bundled
|
||||||
|
Codex skills available to agents in the current project.
|
||||||
|
- Installing or reloading an IdeA plugin does not automatically make bundled
|
||||||
|
Codex skills available to agents in other projects.
|
||||||
|
- No `idea-plugin.json` manifest field currently declares Codex skills for IdeA
|
||||||
|
agent assignment.
|
||||||
|
- Bundling `.codex-plugin/plugin.json` and `skills/*/SKILL.md` inside an IdeA
|
||||||
|
plugin package is allowed as distribution content, but it is inert for IdeA
|
||||||
|
runtime plugin loading unless a separate bridge explicitly consumes it.
|
||||||
|
|
||||||
|
## Required Mechanism
|
||||||
|
|
||||||
|
If a plugin needs Codex skills to be available to agents, install the Codex
|
||||||
|
plugin through the Codex plugin mechanism for the intended scope, then assign or
|
||||||
|
enable those skills for the relevant agents according to Codex/IdeA agent
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
Do not rely on IdeA plugin installation as a cross-project skill propagation
|
||||||
|
mechanism. Cross-project skill availability needs an explicit product contract
|
||||||
|
covering scope, provenance, permissions, uninstall behavior, hot reload,
|
||||||
|
collisions and security. Until that bridge exists, document and perform Codex
|
||||||
|
skill installation separately from IdeA runtime plugin installation.
|
||||||
@ -26,8 +26,9 @@ manifest contribution -> command id -> registered command handler -> optional ta
|
|||||||
- Feedback objects must be stable enough for agents and programmatic callers to
|
- Feedback objects must be stable enough for agents and programmatic callers to
|
||||||
parse, and their messages must be readable by humans.
|
parse, and their messages must be readable by humans.
|
||||||
- The current human menu-click UI does not guarantee display of a command
|
- The current human menu-click UI does not guarantee display of a command
|
||||||
handler return value. Use logs, Work/task state or plugin-owned UI/files when
|
handler return value. When a human must see feedback from a menu action, open
|
||||||
a human needs visible feedback today.
|
or focus a plugin layout/window from the handler with
|
||||||
|
`ctx.services.windows.open({ layoutType, state })`.
|
||||||
|
|
||||||
## Preconditions
|
## Preconditions
|
||||||
|
|
||||||
@ -76,6 +77,9 @@ Visible surfaces are intentionally distinct:
|
|||||||
- Command return value: immediate feedback for programmatic callers, agents and
|
- Command return value: immediate feedback for programmatic callers, agents and
|
||||||
future host surfaces. It is not a guaranteed visible UI surface for current
|
future host surfaces. It is not a guaranteed visible UI surface for current
|
||||||
human menu clicks.
|
human menu clicks.
|
||||||
|
- Plugin layout/window: the normative visible feedback surface for menu actions
|
||||||
|
that need to show status, results or next steps to a human. The layout must be
|
||||||
|
declared in `idea-plugin.json` and registered during activation.
|
||||||
- Plugin logs: diagnostics for developers and operators.
|
- Plugin logs: diagnostics for developers and operators.
|
||||||
- Work/background-task surfaces: only for tasks actually launched through
|
- Work/background-task surfaces: only for tasks actually launched through
|
||||||
`runCommand()`.
|
`runCommand()`.
|
||||||
@ -105,6 +109,40 @@ state when invoked instead of assuming a watch was installed at activation.
|
|||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
Visible menu feedback:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import type { IdeAPluginModule, PluginLayoutProps } from "@idea/plugin-sdk";
|
||||||
|
|
||||||
|
function HealthView(_props: PluginLayoutProps<{ source?: string }>) {
|
||||||
|
return <section>Unity tools are ready.</section>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const plugin: IdeAPluginModule = {
|
||||||
|
activate(ctx) {
|
||||||
|
ctx.layouts?.register({
|
||||||
|
type: "unity-plugin.health",
|
||||||
|
component: HealthView
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.commands?.registerCommand("unity-plugin.health", async () => {
|
||||||
|
const win = await ctx.services?.windows.open({
|
||||||
|
layoutType: "unity-plugin.health",
|
||||||
|
state: { source: "menu" }
|
||||||
|
});
|
||||||
|
|
||||||
|
return win
|
||||||
|
? { status: "opened", message: "Opened Unity Health.", alreadyOpen: win.alreadyOpen }
|
||||||
|
: { status: "skipped", reason: "ui-service-unavailable", message: "UI service unavailable." };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default plugin;
|
||||||
|
```
|
||||||
|
|
||||||
|
Background task feedback:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const project = await ctx.services?.workspace.getCurrentProject();
|
const project = await ctx.services?.workspace.getCurrentProject();
|
||||||
if (!project) {
|
if (!project) {
|
||||||
|
|||||||
@ -32,6 +32,26 @@ ctx.layouts?.register({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `type` must exactly match one entry in `contributes.layouts`. The host
|
||||||
|
rejects registrations for undeclared layout types. A plugin that opens this
|
||||||
|
layout for human feedback must also declare the `ui` capability so the UI
|
||||||
|
runtime and window services are part of the public contract.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"capabilities": ["ui", "tooling"],
|
||||||
|
"contributes": {
|
||||||
|
"layouts": [
|
||||||
|
{
|
||||||
|
"type": "hello-plugin.dashboard",
|
||||||
|
"label": "Dashboard",
|
||||||
|
"component": "Dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
- `projectId`: project hosting the layout.
|
- `projectId`: project hosting the layout.
|
||||||
|
|||||||
@ -147,6 +147,61 @@ development loop.
|
|||||||
contribution can be mounted inside an IdeA layout cell or opened in a detached OS
|
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.
|
window. Do not add a separate manifest contribution type for windows.
|
||||||
|
|
||||||
|
For a command that opens a visible plugin window, declare both the menu command
|
||||||
|
and the layout type:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ideaPluginManifestVersion": 1,
|
||||||
|
"id": "com.example.unity-plugin",
|
||||||
|
"displayName": "Unity Developer Tools",
|
||||||
|
"publisher": "Example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Unity integration for IdeA.",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"engines": {
|
||||||
|
"idea": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"trustLevel": "full",
|
||||||
|
"capabilities": ["ui", "tooling"],
|
||||||
|
"activationScope": "app",
|
||||||
|
"contributes": {
|
||||||
|
"menus": [
|
||||||
|
{
|
||||||
|
"id": "unity-plugin.menu",
|
||||||
|
"label": "Unity Developer Tools",
|
||||||
|
"topLevel": true,
|
||||||
|
"order": 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"menuItems": [
|
||||||
|
{
|
||||||
|
"id": "unity-plugin.health.item",
|
||||||
|
"targetMenuId": "unity-plugin.menu",
|
||||||
|
"label": "Health",
|
||||||
|
"command": "unity-plugin.health",
|
||||||
|
"order": 10,
|
||||||
|
"when": "projectOpen"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"layouts": [
|
||||||
|
{
|
||||||
|
"type": "unity-plugin.health",
|
||||||
|
"label": "Unity Health",
|
||||||
|
"component": "UnityHealth",
|
||||||
|
"order": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`capabilities: ["ui"]` is required when a plugin relies on layouts or windows as
|
||||||
|
human-facing UI. Add `"tooling"` when the same plugin uses workspace, task,
|
||||||
|
tooling or terminal services. The `contributes.layouts[].type` value is the
|
||||||
|
public id used by both `ctx.layouts.register({ type, component })` and
|
||||||
|
`ctx.services.windows.open({ layoutType })`.
|
||||||
|
|
||||||
Contribution ids are part of the runtime contract:
|
Contribution ids are part of the runtime contract:
|
||||||
|
|
||||||
- commands can only register ids declared by `contributes.menuItems[*].command`;
|
- commands can only register ids declared by `contributes.menuItems[*].command`;
|
||||||
|
|||||||
@ -102,8 +102,23 @@ After editing plugin source files, rebuild the plugin output first:
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
Then ask an IdeA agent that has the plugin administration tool available to run
|
An IdeA agent can install a development plugin directory only when that agent has
|
||||||
`idea_plugin_reload` with the installed plugin id:
|
the plugin administration MCP tool explicitly allowed. The public tool is
|
||||||
|
`idea_plugin_install_from_directory`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "relative/path/to/hello-plugin",
|
||||||
|
"expectedPluginId": "com.example.hello-plugin"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The path must resolve under the requester's project root. This installs the
|
||||||
|
directory as the recorded development source for later reloads.
|
||||||
|
|
||||||
|
After source edits, rebuild the plugin output and ask an IdeA agent that has the
|
||||||
|
plugin administration tool available to run `idea_plugin_reload` with the
|
||||||
|
installed plugin id:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -122,7 +137,17 @@ reload. Archive installs are fixed package snapshots. They are appropriate for
|
|||||||
distribution, but the reload command is only defined for plugins installed from
|
distribution, but the reload command is only defined for plugins installed from
|
||||||
a directory source.
|
a directory source.
|
||||||
|
|
||||||
Current limitation: backend registry state and plugin MCP/tool contributions are
|
On reload, `plugin_reloaded` tells the UI to rebuild the plugin runtime registry.
|
||||||
reloaded without restarting IdeA. Frontend React contributions that are already
|
When the package content hash changes, plugin ESM URLs change and React layout
|
||||||
loaded in the current UI session may keep their existing module instance until
|
components are loaded from the new package. Existing plugin layout instances may
|
||||||
the relevant plugin surface is recreated or the app session is restarted.
|
remount and lose local React state; host-persisted layout state written through
|
||||||
|
`setState` remains the durable state channel.
|
||||||
|
|
||||||
|
JavaScript module instances cannot be forcibly removed from the browser ESM
|
||||||
|
cache. Register handlers, layouts, watches and subscriptions through the SDK
|
||||||
|
registries and push disposable side effects into `ctx.subscriptions`; global
|
||||||
|
module side effects can otherwise survive a reload. If the package hash did not
|
||||||
|
change, the reload is idempotent and may reuse the same module instance.
|
||||||
|
|
||||||
|
IdeA runtime plugin installation is separate from Codex plugin/skill discovery.
|
||||||
|
See [Codex Skills And IdeA Plugins](codex-skills-and-idea-plugins.md).
|
||||||
|
|||||||
@ -10,9 +10,18 @@ await ctx.services?.windows.open({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`windows.open` is the public API for commands that need to make feedback visible
|
||||||
|
to a human. A command handler may still return structured data, but menu clicks
|
||||||
|
do not currently guarantee that return value is displayed.
|
||||||
|
|
||||||
## Contract
|
## Contract
|
||||||
|
|
||||||
- `layoutType` must match a layout type declared by the calling plugin.
|
- `layoutType` must match a layout type declared by the calling plugin.
|
||||||
|
- The plugin must declare `capabilities: ["ui"]` when it relies on plugin
|
||||||
|
layouts/windows as a human-facing surface. Add `"tooling"` only when the
|
||||||
|
plugin also uses tooling/workspace/task services.
|
||||||
|
- There is no `contributes.windows` manifest key. Windows always host an
|
||||||
|
existing `contributes.layouts` layout.
|
||||||
- The host validates the plugin is runtime-active and the layout exists before
|
- The host validates the plugin is runtime-active and the layout exists before
|
||||||
opening or focusing the window.
|
opening or focusing the window.
|
||||||
- Reopening the same plugin/layout pair focuses the existing window.
|
- Reopening the same plugin/layout pair focuses the existing window.
|
||||||
@ -38,9 +47,13 @@ The returned `label` is a host-owned window identity. Treat it as opaque.
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
ctx.commands?.registerCommand("hello-plugin.open-dashboard", async () => {
|
ctx.commands?.registerCommand("hello-plugin.open-dashboard", async () => {
|
||||||
return ctx.services?.windows.open({
|
const win = await ctx.services?.windows.open({
|
||||||
layoutType: "hello-plugin.dashboard",
|
layoutType: "hello-plugin.dashboard",
|
||||||
state: { source: "menu" }
|
state: { source: "menu" }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return win
|
||||||
|
? { status: "opened", message: "Opened dashboard.", alreadyOpen: win.alreadyOpen }
|
||||||
|
: { status: "skipped", reason: "ui-service-unavailable", message: "UI service unavailable." };
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user