diff --git a/README.md b/README.md index bda7292..2481810 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,12 @@ For short project-scoped external tools, use [Manifest](docs/manifest.md). The primitive is generic and does not expose a 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 ```sh diff --git a/docs/commands-and-feedback.md b/docs/commands-and-feedback.md index 41819ed..abaddf8 100644 --- a/docs/commands-and-feedback.md +++ b/docs/commands-and-feedback.md @@ -1,7 +1,7 @@ # Commands And Feedback -This document is the normative SDK contract for plugin commands that may launch -background work. +This document is the normative SDK contract for plugin commands, including +commands that open a view or launch background work. ## 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 parse, and their messages must be readable by humans. - 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 from the handler with `ctx.services.windows.open({ layoutType, state })`. ## 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 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. - `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: ```ts @@ -67,21 +75,30 @@ with equivalent fields: ```ts type CommandFeedback = + | { status: "opened"; message?: string; alreadyOpen: boolean } | { status: "skipped"; reason: string; message: string } + | { status: "failed"; reason?: string; message?: string } | { status: "launched"; taskId: string; state: string; message: string }; ``` 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. - `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: - Command return value: immediate feedback for programmatic callers and agents. 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 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. @@ -146,6 +163,11 @@ const plugin: IdeAPluginModule = { 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: ```ts diff --git a/docs/menus.md b/docs/menus.md index 12bac33..0b72e45 100644 --- a/docs/menus.md +++ b/docs/menus.md @@ -33,7 +33,10 @@ Then register the command id: ```ts export function activate(ctx: ActivateContext): void { 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); } @@ -47,9 +50,16 @@ export function activate(ctx: ActivateContext): void { - A missing command handler is surfaced by the host as a retryable reload error; menu clicks must never fail silently. - 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 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 [Commands And Feedback](commands-and-feedback.md). - Menu item `when` expressions are evaluated by the host; unsupported or false diff --git a/docs/services.md b/docs/services.md index 2872bc3..a26b083 100644 --- a/docs/services.md +++ b/docs/services.md @@ -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 [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 Project scheduled tasks are exposed through native Tauri/HTTP commands and the