docs(sdk): fige le contrat command-and-feedback et aligne l'exemple hello-plugin

Documente la règle publique menu -> command handler -> tâche de fond optionnelle
-> feedback (docs/commands-and-feedback.md + README), clarifie via JSDoc les
invariants de runCommand/recordOnly/ownerAgentId dans runtime.ts, et met à jour
l'exemple hello-plugin (feedback structuré launched/skipped, watch non-fatal)
pour qu'il illustre fidèlement le contrat documenté. Publie docs/ dans le
package npm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 15:51:30 +02:00
parent e509e796b4
commit 5b55996558
7 changed files with 318 additions and 29 deletions

View File

@ -6,7 +6,10 @@ It exercises the current plugin primitives end to end:
- top-level menu: `Hello Plugin`;
- menu entry: `hello-plugin`;
- command: `hello-plugin`, returning `hello-world`;
- command: `hello-plugin`, returning readable feedback:
`{ status: "launched", taskId, state, message }` when it starts a background
task, or `{ status: "skipped", reason, message }` when a precondition is not
met;
- layout contribution: `hello-plugin.hello-world`, rendered as `hello-world`.
- plugin-owned storage: activation count, command run count and initialization flag;
- tooling capability: logs the focused workspace project when `ctx.services` is available.
@ -42,7 +45,15 @@ During activation the plugin logs:
- successful registration of the `hello-plugin` command;
- successful registration of the `hello-plugin.hello-world` layout;
- availability of the workspace service from the `tooling` runtime capability;
- best-effort workspace watch setup, including a non-fatal log when unavailable;
- the first layout render, including project/node identifiers.
During command invocation the plugin logs and returns structured feedback for
programmatic callers. The current human menu-click UI does not guarantee display
of that return value.
- skipped command feedback when no project or no `helloPlugin.ownerAgentId` is available;
- launched background command feedback when `helloPlugin.ownerAgentId` is configured;
These messages are intentionally small and stable so installation, bundle import, activation and
layout rendering failures can be separated quickly in IdeA logs/devtools.

View File

@ -1,6 +1,19 @@
import type { ActivateContext } from "@idea/plugin-sdk";
import type { ActivateContext, CommandTaskStatus } from "@idea/plugin-sdk";
import { STORAGE_KEYS } from "../constants.js";
export type HelloCommandFeedback =
| {
status: "skipped";
reason: "services-unavailable" | "no-focused-project" | "missing-owner-agent";
message: string;
}
| {
status: "launched";
taskId: string;
state: CommandTaskStatus["state"];
message: string;
};
export async function useWorkspaceSdk(ctx: ActivateContext): Promise<void> {
const workspace = ctx.services?.workspace;
if (!workspace) return;
@ -44,14 +57,18 @@ export async function useWorkspaceSdk(ctx: ActivateContext): Promise<void> {
messages: diagnostic?.messages
});
const watch = await workspace.watch(".ideai", (event) => {
ctx.logger.info("workspace watch event", {
path: event.path,
kind: event.kind,
operation: event.operation
});
}, project.id);
ctx.subscriptions.push(watch);
try {
const watch = await workspace.watch(".ideai", (event) => {
ctx.logger.info("workspace watch event", {
path: event.path,
kind: event.kind,
operation: event.operation
});
}, project.id);
ctx.subscriptions.push(watch);
} catch (error) {
ctx.logger.info("workspace watch unavailable", { error });
}
const events = await ctx.services?.events.subscribe(
{
@ -70,14 +87,42 @@ export async function useWorkspaceSdk(ctx: ActivateContext): Promise<void> {
}
);
if (events) ctx.subscriptions.push(events);
}
export async function runHelloCommandTask(ctx: ActivateContext): Promise<HelloCommandFeedback> {
if (!ctx.services) {
const feedback = {
status: "skipped",
reason: "services-unavailable",
message: "The tooling service facade is unavailable for hello-plugin."
} as const;
ctx.logger.info("hello command skipped", feedback);
return feedback;
}
const project = await ctx.services.workspace.getCurrentProject();
if (!project) {
const feedback = {
status: "skipped",
reason: "no-focused-project",
message: "Open a project before launching the hello-plugin task."
} as const;
ctx.logger.info("hello command skipped", feedback);
return feedback;
}
const ownerAgentId = await ctx.storage?.get<string>(STORAGE_KEYS.ownerAgentId);
if (!ownerAgentId) {
ctx.logger.info("command task example skipped: no owner agent configured");
return;
const feedback = {
status: "skipped",
reason: "missing-owner-agent",
message: "Configure helloPlugin.ownerAgentId in plugin storage before launching the task."
} as const;
ctx.logger.info("hello command skipped", feedback);
return feedback;
}
const task = await ctx.services?.tasks.runCommand({
const task = await ctx.services.tasks.runCommand({
projectId: project.id,
ownerAgentId,
label: "Hello plugin command",
@ -87,12 +132,17 @@ export async function useWorkspaceSdk(ctx: ActivateContext): Promise<void> {
recordOnly: true
});
if (task) {
const status = await ctx.services?.tasks.getCommandStatus(task.taskId);
ctx.logger.info("command task launched", {
taskId: task.taskId,
state: status?.state ?? task.state,
exitCode: status?.exitCode ?? task.exitCode
});
}
const status = await ctx.services.tasks.getCommandStatus(task.taskId);
const feedback = {
status: "launched",
taskId: task.taskId,
state: status?.state ?? task.state,
message: "Started the hello-plugin background command."
} as const;
ctx.logger.info("hello command task launched", {
...feedback,
exitCode: status?.exitCode ?? task.exitCode
});
return feedback;
}

View File

@ -2,7 +2,7 @@ import type { ActivateContext, IdeAPluginModule } from "@idea/plugin-sdk";
import { COMMAND_ID, LAYOUT_TYPE } from "./constants.js";
import { HelloWorldLayout } from "./core/layout.js";
import { initializePluginStorage, recordCommandRun } from "./core/storage.js";
import { useWorkspaceSdk } from "./core/workspace.js";
import { runHelloCommandTask, useWorkspaceSdk } from "./core/workspace.js";
export function activate(ctx: ActivateContext): void {
ctx.logger.info("activating hello-plugin", {
@ -14,8 +14,9 @@ export function activate(ctx: ActivateContext): void {
const commandDisposable = ctx.commands?.registerCommand(COMMAND_ID, async () => {
const commandRunCount = await recordCommandRun(ctx);
ctx.logger.info("command executed", { commandId: COMMAND_ID, commandRunCount });
return "hello-world";
const feedback = await runHelloCommandTask(ctx);
ctx.logger.info("command executed", { commandId: COMMAND_ID, commandRunCount, feedback });
return feedback;
});
if (commandDisposable) {