Skip to content

Fix aspire agent init: hide dotnet-inspect for non-.NET apphosts, don't pre-select playwright#15788

Merged
davidfowl merged 8 commits intomainfrom
copilot/remove-dotnet-inspect-aspire-init
Apr 2, 2026
Merged

Fix aspire agent init: hide dotnet-inspect for non-.NET apphosts, don't pre-select playwright#15788
davidfowl merged 8 commits intomainfrom
copilot/remove-dotnet-inspect-aspire-init

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Description

Two fixes for aspire agent init skill selection, plus shared language detection infrastructure:

1. dotnet-inspect showing for non-.NET apphosts

The dotnet-inspect skill was offered for all AppHost types (including TypeScript). It now has applicableLanguages: [KnownLanguageId.CSharp] so it's only offered when the workspace contains a .NET AppHost. Language detection uses DetectLanguageRecursiveAsync against workspaceRoot (not the working directory) to find apphosts in subdirectories.

2. playwright selected by default

Both playwright and dotnet-inspect were pre-selected (isDefault: true), which meant users had to manually deselect them. Changed both to isDefault: false so users opt-in to these skills.

3. Unified language detection helpers

Moved pattern matching and directory scanning into shared LanguageInfo helpers (MatchesFile, FindInDirectory, MatchesPattern) so DefaultLanguageDiscovery, TestLanguageDiscovery, and DotNetSdkCheck all use the same code path. Split DetectLanguageAsync (flat, immediate directory) from DetectLanguageRecursiveAsync (scans 5 levels deep) to preserve existing behavior for callers that don't need recursion.

Changes

  • SkillDefinition: Added ApplicableLanguages collection and IsApplicableToLanguage(LanguageId?) method with case-insensitive comparison.
  • AgentInitCommand: Detects AppHost language via ILanguageDiscovery.DetectLanguageRecursiveAsync against workspaceRoot and filters skills by applicability.
  • McpInitCommand: Passes ILanguageDiscovery through to AgentInitCommand.
  • LanguageInfo: New shared helpers — MatchesFile(), FindInDirectory(), MatchesPattern(), DetectionRecurseLimit constant.
  • DefaultLanguageDiscovery/TestLanguageDiscovery: Refactored to use LanguageInfo shared helpers. Added DetectLanguageRecursiveAsync.
  • DotNetSdkCheck: Delegates to DetectLanguageRecursiveAsync instead of duplicating FileSystemHelper logic.
  • E2E tests: Updated to reflect playwright no longer being pre-selected (removed Down+Space navigation).
  • Unit tests: Added LanguageInfoTests covering MatchesFile, MatchesPattern, FindInDirectory (including glob, recursion, depth limit).

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
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
    • No

Copilot AI and others added 4 commits April 2, 2026 03:48
- Changed PlaywrightCli and DotnetInspect skills to isDefault: false
- Added IsDotNetOnly property to SkillDefinition
- Marked DotnetInspect as isDotNetOnly: true
- Injected ILanguageDiscovery into AgentInitCommand to detect apphost language
- Filter out .NET-only skills when workspace is not a .NET apphost
- Updated McpInitCommand to pass ILanguageDiscovery through
- Updated tests for new default behavior

Agent-Logs-Url: https://github.com/microsoft/aspire/sessions/2efe429c-2ad0-4968-8439-d8920c8bcc67

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
…alues

- Replaced bool IsDotNetOnly with IReadOnlyList<string> ApplicableLanguages
- Added IsApplicableToLanguage(LanguageId?) method for filtering logic
- DotnetInspect uses applicableLanguages: [KnownLanguageId.CSharp]
- Empty ApplicableLanguages means the skill is language-agnostic
- Updated AgentInitCommand to use IsApplicableToLanguage for filtering
- Updated tests to verify new API

Agent-Logs-Url: https://github.com/microsoft/aspire/sessions/6bbb18ce-c5f3-4973-8902-f7a31cd8e050

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
When no language is detected (e.g., standalone `aspire agent init`),
skills with ApplicableLanguages restrictions are now excluded instead
of being offered. Only language-agnostic skills are shown.

Agent-Logs-Url: https://github.com/microsoft/aspire/sessions/e7ba2164-749f-4929-89df-e9cc7e834670

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 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 -- 15788

Or

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

@davidfowl davidfowl changed the title Replace IsDotNetOnly with ApplicableLanguages on SkillDefinition Fix aspire agent init: hide dotnet-inspect for non-.NET apphosts, don't pre-select playwright Apr 2, 2026
@davidfowl davidfowl marked this pull request as ready for review April 2, 2026 05:38
Copilot AI review requested due to automatic review settings April 2, 2026 05:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adjusts aspire agent init skill selection so language-specific skills are only offered when applicable, and removes opt-out defaults for tools that require extra setup.

Changes:

  • Introduces language applicability on SkillDefinition and filters offered skills based on detected AppHost language.
  • Updates aspire mcp init to pass language discovery through to the shared init implementation.
  • Updates unit tests to validate default-selection and language applicability behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tests/Aspire.Cli.Tests/Agents/CommonAgentApplicatorsTests.cs Updates tests for new default selection behavior and language applicability checks.
src/Aspire.Cli/Commands/McpInitCommand.cs Threads ILanguageDiscovery into the legacy mcp init delegation path.
src/Aspire.Cli/Commands/AgentInitCommand.cs Detects language and filters the skills list before prompting.
src/Aspire.Cli/Agents/SkillDefinition.cs Adds ApplicableLanguages + IsApplicableToLanguage and sets Playwright/dotnet-inspect to opt-in.

Comment thread src/Aspire.Cli/Commands/AgentInitCommand.cs
Comment thread src/Aspire.Cli/Agents/SkillDefinition.cs
Comment thread src/Aspire.Cli/Agents/SkillDefinition.cs
davidfowl and others added 3 commits April 1, 2026 22:47
Move pattern matching and directory scanning into shared methods on
LanguageInfo so that DefaultLanguageDiscovery, TestLanguageDiscovery,
and DotNetSdkCheck all use the same code path:

- LanguageInfo.MatchesFile() — checks a filename against detection patterns
- LanguageInfo.FindInDirectory() — recursive scan using FileSystemHelper
- LanguageInfo.MatchesPattern() — wildcard extension and exact match logic
- LanguageInfo.DetectionRecurseLimit — shared depth constant (5 levels)

DetectLanguageAsync now uses FindInDirectory (proper glob expansion via
Directory.EnumerateFiles) instead of File.Exists which could not match
wildcard patterns like *.csproj.

DotNetSdkCheck.IsDotNetAppHostAsync fallback now delegates to
DetectLanguageAsync instead of duplicating the FileSystemHelper call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DetectLanguageAsync stays flat (immediate directory only) to preserve
existing behavior for callers like AgentInitCommand.

DetectLanguageRecursiveAsync scans up to DetectionRecurseLimit (5) levels
deep, used by DotNetSdkCheck where a broader search is needed.

Both use FileSystemHelper.FindFirstFile for proper glob expansion
(e.g. *.csproj) instead of File.Exists which can't match wildcards.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The agent init command should find apphosts in subdirectories (e.g.
src/MyAppHost/MyAppHost.csproj) to correctly determine which skills
to offer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Aspire.Cli/Commands/AgentInitCommand.cs Outdated
Copy link
Copy Markdown
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

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

3 issues found: 1 bug (wrong directory for language detection), 1 latent bug (case-sensitive language ID comparison), 1 E2E test regression (playwright selection toggled the wrong way).

Comment thread src/Aspire.Cli/Commands/AgentInitCommand.cs
Comment thread src/Aspire.Cli/Agents/SkillDefinition.cs
Comment thread src/Aspire.Cli/Agents/SkillDefinition.cs
- Use workspaceRoot instead of ExecutionContext.WorkingDirectory for
  language detection so nested apphosts are found correctly when the
  user selects a different workspace root in the prompt.

- Use case-insensitive comparison in IsApplicableToLanguage to match
  the rest of the codebase (OrdinalIgnoreCase for language IDs).

- Fix E2E tests that navigated Down+Space to deselect playwright — now
  that playwright is no longer pre-selected, just accept defaults.

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

github-actions bot commented Apr 2, 2026

Re-running the failed jobs in the CI workflow for this pull request because 2 jobs were identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🎬 CLI E2E Test Recordings — 55 recordings uploaded (commit 2f38ff4)

View recordings
Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunEmptyAppHostProject ▶️ View Recording
CreateAndRunJavaEmptyAppHostProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateJavaAppHostWithViteApp ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View Recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View Recording
GlobalMigration_PreservesAllValueTypes ▶️ View Recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View Recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View Recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View Recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View Recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording
TypeScriptAppHostWithProjectReferenceIntegration ▶️ View Recording

📹 Recordings uploaded automatically from CI run #23886832539

// Detect the AppHost language to determine which skills to offer.
// When no language is detected (e.g., standalone `aspire agent init`), language-restricted skills are excluded.
var detectedLanguage = await _languageDiscovery.DetectLanguageRecursiveAsync(ExecutionContext.WorkingDirectory, cancellationToken);
var detectedLanguage = await _languageDiscovery.DetectLanguageRecursiveAsync(workspaceRoot, cancellationToken);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: add test that detection happens in the right place to prevent regression

@davidfowl davidfowl merged commit db6a64c into main Apr 2, 2026
517 of 524 checks passed
@joperezr joperezr added this to the 13.3 milestone Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants