Files
IdeaSDK/docs/commands-and-feedback.md
Blomios d1c3c00b4d feat(manifest): un plugin peut contribuer des commandes slash via callback — #165 (QA verte)
Étend le manifeste et la validation SDK : un plugin déclare des commandes
slash (contributes.slashCommands) adossées à une callback (command id
enregistré via ctx.commands.registerCommand). Métadonnées UI exposées :
name, shortDescription, requiresConfirmation, when. Exemple hello-plugin
mis à jour avec une commande /hello.

Ces commandes transitent ensuite par le registry/contrat unifié (#162).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 15:46:05 +02:00

161 lines
5.2 KiB
Markdown

# Commands And Feedback
This document is the normative SDK contract for plugin commands that may launch
background work.
## Contract
Every menu click or plugin slash command follows this sequence:
```text
manifest contribution -> command id -> registered command handler -> optional task -> feedback surfaces
```
- A manifest menu item declares a `command` id; it does not run tools directly.
- A manifest slash command declares a slash `name`, autocomplete metadata and a
`command` id; it does not run tools directly.
- Menu items and slash commands may share the same `command` id, or point to
different handlers. The plugin owns that choice.
- The command handler is the only place that decides whether work should start.
- The host slash-command registry only lists/filters metadata and returns a
callback dispatch effect. The plugin handler decides what the command does.
- A launched process is represented by a background task returned from
`ctx.services.tasks.runCommand()`.
- A skipped command is represented by the command handler return value and logs,
not by a fake task.
- Feedback objects must be stable enough for agents and programmatic callers to
parse, and their messages must be readable by humans.
- 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
a human needs visible feedback today.
## Preconditions
Check preconditions before calling `runCommand()`:
- `ctx.services` exists. Plugins need the `ui` or `tooling` capability for the
service facade.
- `ctx.services.workspace.getCurrentProject()` returned a project, or the caller
supplied a valid `projectId`.
- Required executables, environment variables and workspace files were validated,
preferably with `ctx.services.tooling.diagnose()`.
- `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` are separate values. Do not shell-join user input.
If any required precondition fails, return a skipped result:
```ts
return {
status: "skipped",
reason: "missing-owner-agent",
message: "Configure an owner agent before launching the hello-plugin task."
};
```
## Feedback Surfaces
Command handlers should return one of these shapes, or a plugin-specific object
with equivalent fields:
```ts
type CommandFeedback =
| { status: "skipped"; reason: string; message: string }
| { status: "launched"; taskId: string; state: string; message: string };
```
Use the same `status` vocabulary consistently:
- `skipped`: no background task was created.
- `launched`: a task was created; inspect the task state for later progress.
- `failed`: the handler itself failed before it could return normally.
Visible surfaces are intentionally distinct:
- Command return value: immediate feedback for programmatic callers, agents and
future host surfaces. It is not a guaranteed visible UI surface for current
human menu clicks.
- Plugin logs: diagnostics for developers and operators.
- Work/background-task surfaces: only for tasks actually launched through
`runCommand()`.
- Plugin layouts or files: optional plugin-owned user feedback.
`recordOnly: true` affects completion delivery to the owning agent. It does not
mean hidden, skipped or UI-silent. A record-only command task can still appear in
Work and can still emit background-task events.
## Best-Effort Watches
`ctx.services.workspace.watch()` is not a baseline precondition for commands.
The host may reject it until workspace watch support is delivered. Treat watch
setup as optional and non-fatal:
```ts
try {
const watch = await ctx.services.workspace.watch(".ideai", refresh, project.id);
ctx.subscriptions.push(watch);
} catch (error) {
ctx.logger.info("workspace watch unavailable", { error });
}
```
Commands that depend on fresh workspace state should refresh or re-read that
state when invoked instead of assuming a watch was installed at activation.
## Example
```ts
const project = await ctx.services?.workspace.getCurrentProject();
if (!project) {
return {
status: "skipped",
reason: "no-focused-project",
message: "Open a project before running this command."
};
}
const ownerAgentId = await ctx.storage?.get<string>("myPlugin.ownerAgentId");
if (!ownerAgentId) {
return {
status: "skipped",
reason: "missing-owner-agent",
message: "Configure an owner agent before launching this task."
};
}
const task = await ctx.services.tasks.runCommand({
projectId: project.id,
ownerAgentId,
label: "Run my tool",
command: "npm",
args: ["--version"],
cwd: ".",
recordOnly: true
});
return {
status: "launched",
taskId: task.taskId,
state: task.state,
message: "Started Run my tool."
};
```
## Counterexample
Do not launch a shell command just to produce feedback:
```ts
await ctx.services?.tasks.runCommand({
ownerAgentId: "00000000-0000-0000-0000-000000000000",
label: "Skipped: missing config",
command: "echo",
args: ["missing config"],
recordOnly: true
});
```
This creates misleading Work history, uses a placeholder owner and turns a
precondition failure into a fake task. Return `status: "skipped"` instead.