Skip to content

Extract CLI skills into dedicated project#188

Draft
Redth wants to merge 1 commit into
mainfrom
redth/skills-project-plan
Draft

Extract CLI skills into dedicated project#188
Redth wants to merge 1 commit into
mainfrom
redth/skills-project-plan

Conversation

@Redth
Copy link
Copy Markdown
Member

@Redth Redth commented Apr 29, 2026

DevFlow skills were embedded directly by Microsoft.Maui.Cli, which made them harder to treat as their own buildable and versioned component. This extracts the bundled skills into a dedicated project while keeping the existing CLI install flow intact.

Changes

  • Adds Microsoft.Maui.Cli.Skills, a net9.0;net10.0 class library that embeds the existing DevFlow skill assets from plugins/dotnet-maui/skills.
  • Updates DevFlowSkillManager to load bundled skill resources from the referenced skills assembly instead of the maui assembly.
  • Wires the new project into MauiLabs.slnx, src/Cli/Cli.slnf, CLI CI path filters, docs, and unit tests.

Validation

  • .dotnet/dotnet build src/Cli/Cli.slnf
  • .dotnet/dotnet test src/Cli/Microsoft.Maui.Cli.UnitTests/Microsoft.Maui.Cli.UnitTests.csproj --no-build
  • Publish smoke check confirming Microsoft.Maui.Cli.Skills.dll is copied with the CLI and maui devflow init installs both bundled skills.

Move the CLI-bundled DevFlow skill resources into a dedicated Microsoft.Maui.Cli.Skills project so the skills can be built and versioned independently while still being consumed by the maui CLI.

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

Expert Code Review — PR #188

Methodology: 3 independent reviewers with adversarial consensus

3 findings posted as inline comments (2 🟡 moderate, 1 🟢 minor)

# Severity Consensus Finding
1 🟡 2/3 Version decoupling incomplete — update safety logic still uses CLI version for downgrade protection
2 🟡 3/3 BundledSkills list and .csproj resource globs can diverge silently, causing runtime failure
3 🟢 2/3 GetBundledSkillVersion() fallback to CLI version masks build issues

Discarded findings (did not reach consensus)

  • Freshness hint never fires when Skills content changes without CLI version bump — 1/3 flagged; both follow-up reviewers disagreed (dormant while IsPackable=false keeps assemblies in lockstep; hash-based detection already covers content changes)
  • CI path filter misses plugin.json and future skills — 1/3 flagged; both follow-up reviewers disagreed (plugin.json is not consumed by CLI build; .csproj changes live under src/Cli/** which is already covered)
  • Public API types in non-packable assembly — 1/3 only, below cap, discarded without follow-up
  • LINQ .Select().ToArray() at static init — 1/3 only, below cap, discarded without follow-up

CI Status

✅ All checks passing (macOS build ✅, Windows build ✅, package ✅, license/CLA ✅)

Test Coverage

✅ PR includes a new unit test (BundledSkillResources_AreLoadedFromSkillsAssembly) validating that resources load from the new assembly and are absent from the CLI assembly. Existing skill manager integration tests still exercise the install flow.

Generated by Expert Code Review · 3 independent reviewers with adversarial consensus

Generated by Expert Code Review (auto) for issue #188 · ● 13.4M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Expert Code Review: 3 findings posted inline (2 moderate, 1 minor). See the lean summary comment for full details.

Generated by Expert Code Review (auto) for issue #188 · ● 13.4M


public static Assembly Assembly => typeof(MauiCliSkillResources).Assembly;

public static IReadOnlyList<MauiCliSkillDefinition> BundledSkills { get; } =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 MODERATE · consensus 3/3

Silent failure if BundledSkills list and .csproj embedded resource globs diverge.

These two manually-maintained lists must stay in sync: the BundledSkills entries here and the EmbeddedResource includes in the .csproj. If a future skill directory is added to the .csproj glob but not to this list (or vice versa):

  • Missing from BundledSkills → resources are embedded but never discovered (silent dead weight)
  • Missing from .csprojLoadSkillBundleAsync finds zero resources and throws InvalidOperationException("No embedded DevFlow skill resources found...") at runtime

The existing unit test hardcodes the two known skill names and wouldn't catch divergence when a third skill is added.

Recommendation: Add a test (or adjust the existing one) that dynamically iterates MauiCliSkillResources.BundledSkills and asserts each entry has at least one embedded resource with prefix {ResourceRoot}/{skill.Id}/, and conversely that every embedded resource prefix maps to a BundledSkills entry.

}

return new SkillBundle(skill.Id, GetCurrentCliVersion(), files, HashBundle(files));
return new SkillBundle(skill.Id, GetBundledSkillVersion(), files, HashBundle(files));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 MODERATE · consensus 2/3

Version decoupling incomplete — update safety logic still coupled to CLI version.

This line correctly records GetBundledSkillVersion() in the bundle, but the update/downgrade safety logic elsewhere (UpsertStateEntry at ~line 809) still writes installedByCliVersion = GetCurrentCliVersion(), and the comparison at ~line 360 evaluates against CLI version. If the Skills assembly is ever versioned independently (a stated goal of this extraction), a skills-only hotfix (e.g., Skills v1.0.1 with CLI still at v1.0) would record installedByCliVersion = "1.0" even though the installed content is from v1.0.1. A subsequent older Skills bundle would bypass downgrade protection since installedByCliVersion == GetCurrentCliVersion().

Recommendation: Write installedByBundleVersion = GetBundledSkillVersion() separately in UpsertStateEntry, and use it (not installedByCliVersion) for the newer/older classification when deciding whether to allow overwrites. Keep installedByCliVersion as diagnostic metadata only.

=> MauiCliSkillResources.Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
?? MauiCliSkillResources.Assembly.GetName().Version?.ToString()
?? GetCurrentCliVersion();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 MINOR · consensus 2/3

Fallback to GetCurrentCliVersion() silently re-couples the assemblies.

If the Skills assembly has no AssemblyInformationalVersionAttribute and no AssemblyVersion (e.g., a malformed build), this silently falls back to the CLI's version. The skillVersion recorded in state would then be the CLI version rather than signaling something is wrong. Future diagnostics comparing bundledVersion vs installedVersion would appear identical, masking the issue.

Recommendation: Consider using "unknown" as the final fallback (consistent with GetCurrentCliVersion()'s own ultimate fallback) to make the failure mode visible rather than silently aliased to the CLI version. This is low-risk given IsPackable=false keeps versions in lockstep today.

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.

1 participant