docs(sdk): feedbacks menu host génériques — #329 ; README + docs commands-and-feedback/menus/services : failed/skipped sans message Git codé en dur ni payload brut, opened/alreadyOpen succès silencieux, logs host pluginId/commandId/status/reason/message, vue de diagnostic reste accessible quand Git/ADB/Gradle ou autre outil optionnel manque, workspace.execTool rappelé ; QA verte : npm run check, git diff --check

This commit is contained in:
2026-09-19 00:33:57 +02:00
parent 8ff0f475de
commit 32d33b330f
4 changed files with 53 additions and 8 deletions

View File

@ -30,6 +30,12 @@ For short project-scoped external tools, use
[Manifest](docs/manifest.md). The primitive is generic and does not expose a [Manifest](docs/manifest.md). The primitive is generic and does not expose a
native Git/SCM API. native Git/SCM API.
Menu commands that open or focus a plugin view return `status: "opened"`
(including `alreadyOpen: true`) and remain UI-silent on success. Keep diagnostic
views accessible when optional tools are missing, and use the structured
`skipped`/`failed` feedback contract for genuinely impossible actions; see
[Commands And Feedback](docs/commands-and-feedback.md) and [Menus](docs/menus.md).
## Install ## Install
```sh ```sh

View File

@ -1,7 +1,7 @@
# Commands And Feedback # Commands And Feedback
This document is the normative SDK contract for plugin commands that may launch This document is the normative SDK contract for plugin commands, including
background work. commands that open a view or launch background work.
## Contract ## Contract
@ -26,14 +26,16 @@ 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 human menu-click UI presents minimal host-owned feedback for `skipped` - The human menu-click UI presents minimal host-owned feedback for `skipped`
and `failed` results. It intentionally does not expose the raw result payload. and `failed` results. It intentionally does not expose the raw result payload
or assume which tool (Git, ADB, Gradle, and so on) the plugin uses.
For successful actions and richer status, open or focus a plugin layout/window For successful actions and richer status, open or focus a plugin layout/window
from the handler with from the handler with
`ctx.services.windows.open({ layoutType, state })`. `ctx.services.windows.open({ layoutType, state })`.
## Preconditions ## Preconditions
Check preconditions before calling `runCommand()`: Check preconditions before calling `runCommand()`. These are task-launch
preconditions, not prerequisites for opening an explanatory view:
- `ctx.services` exists. Plugins need the `ui` or `tooling` capability for the - `ctx.services` exists. Plugins need the `ui` or `tooling` capability for the
service facade. service facade.
@ -50,6 +52,12 @@ Check preconditions before calling `runCommand()`:
- Do not use `ctx.pluginRoot` as `cwd`; `cwd` remains relative to the project root. - 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. - `command` and `args` are separate values. Do not shell-join user input.
A command whose purpose is to open or focus a diagnostic/configuration view
should call `services.windows.open(...)` before probing optional executables.
Missing Git, ADB, Gradle, or another tool must not block that view when the view
can explain the degraded state and the next step. Probe tools from the view (or
from its data loader) and render the degraded state there.
If any required precondition fails, return a skipped result: If any required precondition fails, return a skipped result:
```ts ```ts
@ -67,21 +75,30 @@ with equivalent fields:
```ts ```ts
type CommandFeedback = type CommandFeedback =
| { status: "opened"; message?: string; alreadyOpen: boolean }
| { status: "skipped"; reason: string; message: string } | { status: "skipped"; reason: string; message: string }
| { status: "failed"; reason?: string; message?: string }
| { status: "launched"; taskId: string; state: string; message: string }; | { status: "launched"; taskId: string; state: string; message: string };
``` ```
Use the same `status` vocabulary consistently: Use the same `status` vocabulary consistently:
- `opened`: the requested view was opened or focused. `alreadyOpen: true` is a
successful focus operation, not a failure or skip.
- `skipped`: no background task was created. - `skipped`: no background task was created.
- `launched`: a task was created; inspect the task state for later progress. - `launched`: a task was created; inspect the task state for later progress.
- `failed`: the handler itself failed before it could return normally. - `failed`: the handler caught a failure and returned structured failure
feedback. An uncaught/rejected handler is also treated as a failure by the
host.
Visible surfaces are intentionally distinct: Visible surfaces are intentionally distinct:
- Command return value: immediate feedback for programmatic callers and agents. - Command return value: immediate feedback for programmatic callers and agents.
The human menu host maps `skipped` and `failed` results to concise feedback; The human menu host maps `skipped` and `failed` results to concise feedback;
plugin-specific fields and messages are not displayed verbatim. plugin-specific fields and messages are not displayed verbatim. `opened`
results, including `alreadyOpen: true`, succeed without an alert. The host
logs structured diagnostics with `pluginId`, `commandId`, `status`, `reason`
and `message`; raw objects, stack traces and internal ids are not user-facing.
- Plugin layout/window: the normative visible feedback surface for menu actions - 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 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. declared in `idea-plugin.json` and registered during activation.
@ -146,6 +163,11 @@ const plugin: IdeAPluginModule = {
export default plugin; export default plugin;
``` ```
The `opened` branch above must remain successful even when an optional tool
diagnosed by `HealthView` is absent. If `windows.open(...)` itself rejects, log
the technical cause and either let the promise reject or return `failed`; the
host will show generic, plugin-neutral feedback rather than the raw message.
Background task feedback: Background task feedback:
```ts ```ts

View File

@ -33,7 +33,10 @@ Then register the command id:
```ts ```ts
export function activate(ctx: ActivateContext): void { export function activate(ctx: ActivateContext): void {
const disposable = ctx.commands?.registerCommand("hello-plugin.open", async () => { const disposable = ctx.commands?.registerCommand("hello-plugin.open", async () => {
await ctx.services?.windows.open({ layoutType: "hello-plugin.dashboard" }); const win = await ctx.services?.windows.open({ layoutType: "hello-plugin.dashboard" });
return win
? { status: "opened", alreadyOpen: win.alreadyOpen }
: { status: "skipped", reason: "ui-service-unavailable", message: "UI service unavailable." };
}); });
if (disposable) ctx.subscriptions.push(disposable); if (disposable) ctx.subscriptions.push(disposable);
} }
@ -47,9 +50,16 @@ export function activate(ctx: ActivateContext): void {
- A missing command handler is surfaced by the host as a retryable reload error; - A missing command handler is surfaced by the host as a retryable reload error;
menu clicks must never fail silently. menu clicks must never fail silently.
- A registered handler's rejected promise is not swallowed: the host logs the - A registered handler's rejected promise is not swallowed: the host logs the
plugin id, command id and reason, then presents a retryable error. In plugin id, command id, status, reason and message, then presents a generic
retryable error without raw payloads. In
particular, return/await `services.windows.open(...)` so native-window particular, return/await `services.windows.open(...)` so native-window
failures follow this path. failures follow this path.
- An open/focus handler returns `status: "opened"` whether `alreadyOpen` is
`false` (new window) or `true` (existing window focused). Neither success
produces an alert.
- Keep diagnostic views reachable when optional executables such as Git, ADB or
Gradle are missing. Let the view explain that degraded state instead of
skipping the menu action before opening it.
- A handler owns precondition checks and feedback. See - A handler owns precondition checks and feedback. See
[Commands And Feedback](commands-and-feedback.md). [Commands And Feedback](commands-and-feedback.md).
- Menu item `when` expressions are evaluated by the host; unsupported or false - Menu item `when` expressions are evaluated by the host; unsupported or false

View File

@ -125,6 +125,13 @@ locations. Use it for configuration the user expects to review/version.
one of the calling plugin's declared layout contributions. See one of the calling plugin's declared layout contributions. See
[Windows](windows.md). [Windows](windows.md).
Resolving this promise means the window was opened or focused. Its
`alreadyOpen` flag reports which happened; both outcomes are successful and a
menu handler should return `status: "opened"`. Do not gate a diagnostic window
on optional tool availability when that window can present the unavailable
state. A rejection means the window operation itself was impossible and may be
logged or returned as `status: "failed"`; do not expose its raw error to users.
## Scheduled tasks ## Scheduled tasks
Project scheduled tasks are exposed through native Tauri/HTTP commands and the Project scheduled tasks are exposed through native Tauri/HTTP commands and the