Skip to content

Display resource commands in VS Code extension tree view#17698

Open
shivamgoel008 wants to merge 17 commits into
microsoft:mainfrom
shivamgoel008:feature/resource-commands-tree-view
Open

Display resource commands in VS Code extension tree view#17698
shivamgoel008 wants to merge 17 commits into
microsoft:mainfrom
shivamgoel008:feature/resource-commands-tree-view

Conversation

@shivamgoel008
Copy link
Copy Markdown

Add support for displaying resource commands (both enabled and disabled) as child items under each resource in the Aspire extension tree view.

Changes:

  • Backend: Add State property to ResourceCommandJson and map all API-visible commands including disabled ones in ResourceSnapshotMapper
  • Extension: Register executeResourceCommandItem command and add CommandsGroupItem/ResourceCommandItem tree elements
  • Tests: Update ResourceSnapshotMapperTests to verify both enabled and disabled commands are included

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change.

Resolves #17667

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Add support for displaying resource commands (both enabled and disabled)
as child items under each resource in the Aspire extension tree view.

Changes:
- Backend: Add State property to ResourceCommandJson and map all
  API-visible commands including disabled ones in ResourceSnapshotMapper
- Extension: Register executeResourceCommandItem command and add
  CommandsGroupItem/ResourceCommandItem tree elements
- Tests: Update ResourceSnapshotMapperTests to verify both enabled
  and disabled commands are included
Copilot AI review requested due to automatic review settings May 29, 2026 21:49
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17698

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17698"

@davidfowl
Copy link
Copy Markdown
Contributor

video, image?

@adamint
Copy link
Copy Markdown
Member

adamint commented May 30, 2026

Yes, please include both!

Copy link
Copy Markdown
Member

@adamint adamint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed by three parallel agents focused on architecture and logical correctness. The PR is small and the tree-view scaffolding is structurally sound, but the mapper widening has wider blast radius than the title suggests and the consumer side doesn't handle the full state vocabulary. Findings ordered by impact.

Blockers / High

  1. Hidden command state leaks through and renders as actionable. ResourceCommandState has three values (Enabled, Disabled, Hidden) per src/Aspire.Hosting/ApplicationModel/CustomResourceSnapshot.cs:330, and AuxiliaryBackchannelRpcTarget.cs:920 stringifies the enum. The new mapper filter only checks visibility, and the consumer check state !== 'Disabled' treats "Hidden" as enabled, so a Hidden command with the default "UI, Api" visibility now gets a run icon, a resourceCommand:enabled context value, and a single-click executeResourceCommandItem binding. Concrete regression: src/Aspire.Hosting/ApplicationModel/ParameterProcessor.cs:296 marks the Save command Hidden until a value is set — the tree view will now render it as a clickable Run before the parameter has any value. (Inline comments on the mapper filter and the TS check.)
  2. CodeLens provider now lights up Disabled/Hidden custom commands as actionable lenses. extension/src/editor/AspireCodeLensProvider.ts:308-324 iterates resource.commands and registers a CodeLens for every non-standard command — previously safe because the backend only emitted Enabled commands. After this PR, every custom command (Disabled or Hidden) becomes a clickable lens in the AppHost source file invoking aspire-vscode.codeLensResourceAction. The CodeLens provider was not updated in this PR; this is a regression to a feature outside the PR's stated scope.

Medium

  1. Mapper relaxation silently changes the wire contract for every consumer, not just the new tree view: DescribeCommand.cs:213,260 (aspire describe JSON / watch NDJSON), ExportCommand.cs:244 (archive), and Mcp/Tools/ListResourcesTool.cs:91 (the MCP tool surfacing resources to LLM clients). None of those consumers were updated to interpret the new state field, so they will treat disabled/hidden commands as if they were available. Consider either gating the widening behind a parameter (includeDisabledCommands) that only the VS Code path opts into, or updating every consumer in tandem and documenting the contract change.
  2. Single-click execution diverges from issue #17667 and is dangerous for destructive commands. The issue explicitly says “Users would still need to right-click or double-click to execute the command.” Setting this.command on ResourceCommandItem fires on a single primary click (including arrow-key navigation in some VS Code configurations), with no confirmation dialog. Commands like stop, restart, drop-database, etc. will fire on accidental click. The right-click menu entry added in package.json is sufficient on its own. (Inline comment on the this.command block.)
  3. executeResourceCommand quick-pick picker is now silently regressed. Same root cause as #3 — the legacy right-click → "Execute resource command" picker at AspireAppHostTreeProvider.ts:851-881 enumerates Object.entries(commands) with no state filter; it previously relied on the producer's implicit filter. After this PR, users see disabled and hidden commands in the picker without visual distinction and selecting them will fail server-side. Filter by cmd.state === 'Enabled' (or visually mark disabled entries) before adding to the quick-pick.
  4. Inconsistency: CLI command picker still uses the strict filter at src/Aspire.Cli/Commands/ResourceCommand.cs:242 (IsCommandAvailableToApi), while the JSON contract no longer does. The CLI picker (aspire resource <name>) hides disabled commands, the VS Code tree shows them greyed, and the MCP tool sees them as actionable — three different views of the same ResourceSnapshotMapper output. Pick one model and apply it consistently.
  5. Backward compat (old extension + new CLI) regresses existing users. Users who upgrade the CLI without updating the extension hit the regressions in #2 and #5 even without the new tree-view feature. There's no backchannel version negotiation that would let an older client opt back into Enabled-only payloads. At minimum, validate the previously-shipped extension does not silently execute disabled commands; ideally, gate the widened mapper behind a capability flag or accept-and-document in release notes.

Low

  1. State docstring omits Hidden and the new mapper comment claims "exclude hidden" even though the code does not filter on state. Both make finding #1 easy to miss in review. Consider documenting all three values and introducing a KnownCommandState constants class parallel to KnownCommandVisibility. (Inline on the docstring and the mapper comment.)
  2. vscode.l10n.t('(disabled)') is inlined rather than declared in extension/src/loc/strings.ts. The repo's AGENTS.md states new localized strings must live in both package.nls.json and src/loc/strings.ts. Functionally fine (still localized at runtime), but invisible to anyone auditing the localization surface. (Inline.)
  3. No extension-side tests were added for CommandsGroupItem, ResourceCommandItem, the state !== 'Disabled' branch, the single-click this.command wiring, or the new executeResourceCommandItem handler. The C# test only asserts on the in-memory mapped object, not on JSON round-trip through the source-generated context. The home for these is extension/src/test/appHostTreeView.test.ts.

Verified safe (not flagged)

  • Tree-item id paths (:commands, :command:<name>) are unique vs :health-checks and other sibling group ids.
  • viewItem == resourceCommand:enabled exact-match is correct; no regex elsewhere falsely matches it.
  • commandName vs selected.label are equivalent at the _runResourceCommand boundary (the existing picker also uses the dictionary key as the label).
  • getChildren parity in workspace and global view modes is correctly maintained.
  • displayName ?? commandName whitespace handling is consistent with the backend mapper's IsNullOrWhiteSpace normalization.
  • commandsLabel is correctly localized in both package.nls.json and loc/strings.ts.
  • aspire-vscode.executeResourceCommandItem is correctly hidden from the command palette via menus.commandPalette.when: false.
  • Secret-redaction parity (commandArguments.containsSecret) is preserved through the new handler.
  • ResourceCommandJson is internal sealed; no public-API or .ats.txt baseline impact.
  • No .verified.* snapshot files capture serialized Commands JSON, so adding State doesn't break Verify baselines.
  • No NuGet.config / global.json / package.json runtime-deps / yarn.lock changes that would violate guardrails.

Comment thread src/Aspire.Cli/Backchannel/ResourceSnapshotMapper.cs Outdated
Comment thread src/Aspire.Cli/Backchannel/ResourceSnapshotMapper.cs Outdated
Comment thread extension/src/views/AspireAppHostTreeProvider.ts Outdated
Comment thread extension/src/views/AspireAppHostTreeProvider.ts Outdated
Comment thread extension/src/views/AspireAppHostTreeProvider.ts Outdated
Comment thread src/Shared/Model/Serialization/ResourceJson.cs Outdated
@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented May 30, 2026

What does it look like? Can you add some screenshots?

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@adamint
Copy link
Copy Markdown
Member

adamint commented May 30, 2026

Final VSIX validation screenshots from a real VS Code Insiders window driven by Playwright CLI:

Resource commands expanded

Resource commands expanded

Enabled command context menu

Enabled command context menu

Validated that enabled and disabled commands render, disabled commands are non-executable, enabled commands expose the context-menu action, and single-clicking a command item does not invoke the CLI.

adamint and others added 9 commits May 30, 2026 15:33
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…iew-fixes

# Conflicts:
#	.github/workflows/extension-release.yml
Copy link
Copy Markdown
Member

@adamint adamint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final review: no issues found after extension, CLI, and workflow review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@shivamgoel008
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@adamint adamint self-assigned this May 30, 2026
@adamint
Copy link
Copy Markdown
Member

adamint commented May 30, 2026

Do NOT merge until 13.4 is released

@adamint adamint added NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) and removed blocked labels May 30, 2026
@adamint adamint added this to the 13.5 milestone May 30, 2026
adamint and others added 4 commits May 30, 2026 19:54
Replace locale-dependent error-text parsing with capability detection through 'aspire config info --json'. Add a 'describe-include-disabled-commands.v1' capability token to the CLI and refactor the extension's getConfigInfo free function into a ConfigInfoProvider class with success caching, in-flight de-duplication, and a hasCapability helper.
Resource command tree items all used the 'run' play icon because the CLI command JSON carries no icon name. Add getResourceCommandIcon to map well-known lifecycle command names (start->play, stop->debug-stop, restart->debug-restart, rebuild->tools) to distinct Codicons, falling back to 'run' for custom commands and tinting disabled commands with disabledForeground.
The play/stop Codicons render with intrinsic green/red theming that is visually distracting in the tree. Force a neutral icon.foreground color for enabled commands so all command glyphs share a consistent gray tone.
Redact secret command argument defaults from serialized resource command JSON and filter resource command tree items to UI-visible commands.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented May 31, 2026

What happens if there are no commands? I would expect the top level "Commands" item isn't visible

@adamint
Copy link
Copy Markdown
Member

adamint commented May 31, 2026

What happens if there are no commands? I would expect the top level "Commands" item isn't visible

Right, commands is not shown in that case.

image

here, store-shipping has no commands

@adamint
Copy link
Copy Markdown
Member

adamint commented May 31, 2026

pushed a couple small follow-ups:

  • resource commands no longer all render with the play icon. the cli command json doesn't carry the dashboard's fluent icon name, so we map the well-known lifecycle commands ourselves: start → play, stop → debug-stop, restart → debug-restart, rebuild → tools, and fall back to a generic run icon for custom commands
  • forced a neutral gray (icon.foreground) on the command icons since the play/stop codicons render green/red by default, which was distracting in the tree. disabled commands keep their glyph tinted with disabledForeground

@shivamgoel008 thank you for the contribution! I went ahead and fixed up the branch, I hope you don't mind 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-cli area-extension NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve display of resource commands in aspire panel

4 participants