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

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