Migrate shipped skills from embedded resources to microsoft/aspire-skills delegation
This is a tracking issue (epic) for moving the two skills the Aspire CLI currently ships — aspire and aspireify — from MSBuild-embedded resources baked into the CLI binary to a delegated install from microsoft/aspire-skills, mirroring the existing pattern we already use for playwright-cli.
Scope note. This issue covers only the skills the CLI ships via aspire agent init (.agents/skills/aspire, .agents/skills/aspireify). It explicitly does not touch the contributor-facing skills under .github/skills/ (e.g., code-review, fix-flaky-test, backport-pr, api-review).
Problem
The Aspire CLI embeds two skills as managed resources inside the AOT-compiled binary:
.agents/skills/aspire/ — main Aspire CLI / AppHost workflow skill
.agents/skills/aspireify/ — one-time "complete Aspire initialization" skill
Pulled in via src/Aspire.Cli/Aspire.Cli.csproj:
<EmbeddedResource Include="..\..\.agents\skills\aspire\**\*"
LogicalName="skills.aspire/%(RecursiveDir)%(Filename)%(Extension)" />
<EmbeddedResource Include="..\..\.agents\skills\aspireify\**\*"
LogicalName="skills.aspireify/%(RecursiveDir)%(Filename)%(Extension)" />
…then surfaced via SkillDefinition.Aspire / SkillDefinition.Aspireify, loaded with EmbeddedSkillResourceLoader, and written to the workspace by AgentInitCommand at one of the four supported skill locations (.agents/skills, .claude/skills, .github/skills, .opencode/skill).
Pain points:
- Source-of-truth drift.
microsoft/aspire-skills already has parallel skills/aspire/ and skills/aspireify/ directories. Two source trees → manual sync risk and content rot.
- Skill releases coupled to CLI releases. A user can't pick up an improved skill without a new CLI build.
- Binary bloat in an AOT-compiled CLI. Skill markdown + reference files ship even for users who never run
aspire agent init.
- Inconsistent delivery model.
PlaywrightCliInstaller and DotnetInspect already follow a delegated install pattern with npm + SLSA provenance verification. aspire and aspireify are the outliers.
Target state
microsoft/aspire-skills becomes the single, authoritative source for shipped Aspire skills. aspire agent init delegates install to an external installer, following the playbook we already use for playwright-cli, including:
- Versioned, cached install under the global
.aspire cache directory (the CliExecutionContext.CacheDirectory pattern)
- Supply-chain verification (npm + SLSA provenance, matching
PlaywrightCliInstaller)
- Cleanup of stale cache entries (mirroring
DiskCache MaxCacheAge)
- Offline behavior parity with existing delegated skills — first install requires network; cache serves repeats
The primary delegation mechanism to evaluate is npx skills add microsoft/aspire-skills (or analog), shelled out exactly the way PlaywrightCliInstaller shells out today.
Current implementation map
| Layer |
File |
Role today |
| Authoring |
.agents/skills/aspire/**, .agents/skills/aspireify/** |
Source markdown + references shipped in this repo |
| Build |
src/Aspire.Cli/Aspire.Cli.csproj |
EmbeddedResource Include="..\..\.agents\skills\..." |
| Catalog |
src/Aspire.Cli/Agents/SkillDefinition.cs |
Static list — Aspire, Aspireify, PlaywrightCli, DotnetInspect |
| Resource roots |
src/Aspire.Cli/Agents/CommonAgentApplicators.cs |
AspireSkillResourceRoot = "skills.aspire", AspireifySkillResourceRoot = "skills.aspireify" |
| Loader |
src/Aspire.Cli/Agents/EmbeddedSkillResourceLoader.cs |
Assembly.GetManifestResourceNames() + GetManifestResourceStream() |
| Locations |
src/Aspire.Cli/Agents/SkillLocation.cs |
Standard, ClaudeCode, GitHubSkills, OpenCode |
| Entry point |
src/Aspire.Cli/Commands/AgentInitCommand.cs |
Drives the install pipeline |
| Precedent (delegated) |
src/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cs |
npm + SLSA provenance pattern to mirror |
| Cache precedent |
src/Aspire.Cli/Caching/DiskCache.cs |
TTL + max-age cleanup pattern to mirror |
Phased plan
Each phase below will be filed as its own follow-up issue. Check items as they are completed.
Phase 0 — Public-ize microsoft/aspire-skills (prerequisite)
Phase 1 — Establish microsoft/aspire-skills as the source of truth
Phase 2 — Design the external skill installer in the CLI
Mirror PlaywrightCliInstaller:
Phase 3 — Wire the installer into AgentInitCommand / SkillDefinition
Phase 4 — Remove the embedded resources from microsoft/aspire
Phase 5 — Tests, telemetry, docs
Phase 6 — Version pinning and opt-in newer skills
Non-goals
- Migrating contributor-facing skills under
.github/skills/ (api-review, backport-pr, ci-test-failures, etc.) — those are repo-internal and stay embedded in microsoft/aspire
- Changing how
PlaywrightCliInstaller or DotnetInspect work (already externally sourced)
- Building a generic third-party skill plugin marketplace inside the Aspire CLI
- Localizing skill markdown content (orthogonal effort; if pursued, drive from
microsoft/aspire-skills)
- Replacing the four
SkillLocation targets (.agents/skills, .claude/skills, .github/skills, .opencode/skill) — those remain the install destinations
Open questions
- Distribution channel — npm +
npx skills add (matches Playwright precedent) vs GitHub release tarball vs dotnet tool. Recommend npm to reuse INpmRunner + INpmProvenanceChecker.
npx skills tool authority — is skills an existing package we depend on, or do we need to publish a Microsoft-owned @microsoft/aspire-skills npm package and invoke it directly? Needs concrete confirmation before Phase 2 design lock.
- Manifest schema — JSON Schema-validated
skill-manifest.json at the bundle root vs directory-walk convention. Recommend JSON Schema for forward-compat.
- Cache TTL defaults — reuse
DiskCache defaults (3h expiry / 7d max age) or pick skill-specific values?
- Conflict handling — when an existing
.agents/skills/aspire/ already exists in the workspace from a prior install, do we overwrite, prompt, or skip by default? Recommend prompt (current aspire agent init UX already prompts).
- Failure UX — what does the CLI do when the installer fails (npm missing, network down, provenance check fails)? Recommend a clear actionable error + link to docs; never silently fall back to a stale bundle without telling the user.
- CI dependency direction — does
microsoft/aspire's CI need to pull microsoft/aspire-skills at build time to validate the pinned version exists/resolves? Likely yes.
Success criteria
microsoft/aspire-skills is public and consumable
aspire agent init installs aspire + aspireify skills via the external installer with verified provenance
- Cached skill bundles live under the global Aspire cache directory and are aged out per policy
.agents/skills/aspire/ and .agents/skills/aspireify/ no longer exist in microsoft/aspire
- The two
<EmbeddedResource Include="..\..\.agents\skills\..."> lines are gone from Aspire.Cli.csproj
- New + existing tests pass without relying on embedded skill resources
- Telemetry surfaces which
aspire-skills version each install resolved to
References
microsoft/aspire-skills: https://github.com/microsoft/aspire-skills
- Playwright delegated install precedent:
src/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cs
- Cache precedent:
src/Aspire.Cli/Caching/DiskCache.cs
- Skill locations:
src/Aspire.Cli/Agents/SkillLocation.cs
- Skill catalog:
src/Aspire.Cli/Agents/SkillDefinition.cs
aspire agent init entry: src/Aspire.Cli/Commands/AgentInitCommand.cs
Migrate shipped skills from embedded resources to
microsoft/aspire-skillsdelegationThis is a tracking issue (epic) for moving the two skills the Aspire CLI currently ships —
aspireandaspireify— from MSBuild-embedded resources baked into the CLI binary to a delegated install frommicrosoft/aspire-skills, mirroring the existing pattern we already use forplaywright-cli.Problem
The Aspire CLI embeds two skills as managed resources inside the AOT-compiled binary:
.agents/skills/aspire/— main Aspire CLI / AppHost workflow skill.agents/skills/aspireify/— one-time "complete Aspire initialization" skillPulled in via
src/Aspire.Cli/Aspire.Cli.csproj:…then surfaced via
SkillDefinition.Aspire/SkillDefinition.Aspireify, loaded withEmbeddedSkillResourceLoader, and written to the workspace byAgentInitCommandat one of the four supported skill locations (.agents/skills,.claude/skills,.github/skills,.opencode/skill).Pain points:
microsoft/aspire-skillsalready has parallelskills/aspire/andskills/aspireify/directories. Two source trees → manual sync risk and content rot.aspire agent init.PlaywrightCliInstallerandDotnetInspectalready follow a delegated install pattern with npm + SLSA provenance verification.aspireandaspireifyare the outliers.Target state
microsoft/aspire-skillsbecomes the single, authoritative source for shipped Aspire skills.aspire agent initdelegates install to an external installer, following the playbook we already use forplaywright-cli, including:.aspirecache directory (theCliExecutionContext.CacheDirectorypattern)PlaywrightCliInstaller)DiskCacheMaxCacheAge)The primary delegation mechanism to evaluate is
npx skills add microsoft/aspire-skills(or analog), shelled out exactly the wayPlaywrightCliInstallershells out today.Current implementation map
.agents/skills/aspire/**,.agents/skills/aspireify/**src/Aspire.Cli/Aspire.Cli.csprojEmbeddedResource Include="..\..\.agents\skills\..."src/Aspire.Cli/Agents/SkillDefinition.csAspire,Aspireify,PlaywrightCli,DotnetInspectsrc/Aspire.Cli/Agents/CommonAgentApplicators.csAspireSkillResourceRoot = "skills.aspire",AspireifySkillResourceRoot = "skills.aspireify"src/Aspire.Cli/Agents/EmbeddedSkillResourceLoader.csAssembly.GetManifestResourceNames()+GetManifestResourceStream()src/Aspire.Cli/Agents/SkillLocation.csStandard,ClaudeCode,GitHubSkills,OpenCodesrc/Aspire.Cli/Commands/AgentInitCommand.cssrc/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cssrc/Aspire.Cli/Caching/DiskCache.csPhased plan
Each phase below will be filed as its own follow-up issue. Check items as they are completed.
Phase 0 — Public-ize
microsoft/aspire-skills(prerequisite)microsoft/aspire-skillsfor internal-only content (issue references, partner names, telemetry payloads, etc.)LICENSE,SECURITY.md,CODE_OF_CONDUCT.md,CONTRIBUTING.mdare present and Microsoft-OSS compliantPhase 1 — Establish
microsoft/aspire-skillsas the source of truthmicrosoft/aspire/.agents/skills/{aspire,aspireify}andmicrosoft/aspire-skills/skills/{aspire,aspireify}; pick the authoritative copy per file and mergeskill-manifest.jsonschema covering fields currently captured inSkillDefinition:name,description,isDefaultapplicableLanguages(optional)installExcludedRelativePaths(soevals/continues to be excluded from workspace install while still shipped in the bundle for QA)files[]with relative paths + content hashesnpx skills add(or whatever installer we settle on)microsoft/aspire-skillsthat publishes a tagged release bundle the CLI can pin againstPhase 2 — Design the external skill installer in the CLI
Mirror
PlaywrightCliInstaller:IExternalSkillInstallerwithAspireSkillsInstallerimplementation)npx skills add microsoft/aspire-skills) — confirm tool availability or document a fallbackINpmProvenanceChecker)ExpectedBuildTypepattern)https://github.com/microsoft/aspire-skills)VersionRangeconstant in the installer matching thePlaywrightCliInstaller.VersionRangestyleCliExecutionContext.CacheDirectory):cache/aspire-skills/<package-version>/skills/{aspire,aspireify}/...DiskCache-styleMaxCacheAge(default 7 days for unused versions), configurable viaIConfigurationaspire agent init --helpand a known-issues docPhase 3 — Wire the installer into
AgentInitCommand/SkillDefinitionSkillDefinition.AspireandSkillDefinition.Aspireifyto mark them as externally sourced instead of carryingEmbeddedResourceRootinstallExcludedRelativePaths,isDefault,applicableLanguages) from the fetched manifest instead of hard-coded constantsAgentInitCommandflow:AspireSkillsInstallercache/aspire-skills/<version>/...into chosenSkillLocations, honoringinstallExcludedRelativePaths(e.g.,evals/)EmbeddedSkillResourceLoaderonly as a one-release transition shim if needed; otherwise delete itPhase 4 — Remove the embedded resources from
microsoft/aspire.agents/skills/aspire/and.agents/skills/aspireify/<EmbeddedResource Include="..\..\.agents\skills\...">items fromsrc/Aspire.Cli/Aspire.Cli.csprojsrc/Aspire.Cli/Agents/EmbeddedSkillResourceLoader.cs(andSkillAssetFileif no consumer remains)AspireSkillResourceRoot/AspireifySkillResourceRootconstants fromCommonAgentApplicators.csSkillDefinition.Allstill surfaces the same four skills but two are now externally sourcedPhase 5 — Tests, telemetry, docs
AgentInitCommandintegration tests to use a fakeIExternalSkillInstaller+ fixture cache instead of relying onGetManifestResourceStreamAspireCliTelemetryaspire agent inithelp text +Resources/AgentCommandStrings.resx+ all.xlftranslations to describe the network requirement for first install.agents/skills/as the canonical authoring locationPhase 6 — Version pinning and opt-in newer skills
aspire-skillsversionaspire agent init --skills-version <semver>or env var) for users who want to opt in to a newer skill bundle without waiting for the next CLI releaseNon-goals
.github/skills/(api-review, backport-pr, ci-test-failures, etc.) — those are repo-internal and stay embedded inmicrosoft/aspirePlaywrightCliInstallerorDotnetInspectwork (already externally sourced)microsoft/aspire-skills)SkillLocationtargets (.agents/skills,.claude/skills,.github/skills,.opencode/skill) — those remain the install destinationsOpen questions
npx skills add(matches Playwright precedent) vs GitHub release tarball vs dotnet tool. Recommend npm to reuseINpmRunner+INpmProvenanceChecker.npx skillstool authority — isskillsan existing package we depend on, or do we need to publish a Microsoft-owned@microsoft/aspire-skillsnpm package and invoke it directly? Needs concrete confirmation before Phase 2 design lock.skill-manifest.jsonat the bundle root vs directory-walk convention. Recommend JSON Schema for forward-compat.DiskCachedefaults (3h expiry / 7d max age) or pick skill-specific values?.agents/skills/aspire/already exists in the workspace from a prior install, do we overwrite, prompt, or skip by default? Recommend prompt (currentaspire agent initUX already prompts).microsoft/aspire's CI need to pullmicrosoft/aspire-skillsat build time to validate the pinned version exists/resolves? Likely yes.Success criteria
microsoft/aspire-skillsis public and consumableaspire agent initinstallsaspire+aspireifyskills via the external installer with verified provenance.agents/skills/aspire/and.agents/skills/aspireify/no longer exist inmicrosoft/aspire<EmbeddedResource Include="..\..\.agents\skills\...">lines are gone fromAspire.Cli.csprojaspire-skillsversion each install resolved toReferences
microsoft/aspire-skills: https://github.com/microsoft/aspire-skillssrc/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cssrc/Aspire.Cli/Caching/DiskCache.cssrc/Aspire.Cli/Agents/SkillLocation.cssrc/Aspire.Cli/Agents/SkillDefinition.csaspire agent initentry:src/Aspire.Cli/Commands/AgentInitCommand.cs