Skip to content

Add dynamic tab completions for command arguments via System.CommandLine CompletionSources - #15

Merged
andyalm merged 12 commits into
mainfrom
claude/explore-commandline-autocomplete-B3oyU
Feb 22, 2026
Merged

Add dynamic tab completions for command arguments via System.CommandLine CompletionSources#15
andyalm merged 12 commits into
mainfrom
claude/explore-commandline-autocomplete-B3oyU

Conversation

@andyalm

@andyalm andyalm commented Feb 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add dynamic CompletionSources to command arguments so dotnet-suggest provides tab completions from the xrepo registry:
    • ref: registered repo names, package IDs, and file path completions for .csproj files
    • which / where: registered package IDs
    • repo unregister: registered repo names
    • unref: registered repo names and package IDs
  • Change --solution option type from string to FileInfo so System.CommandLine provides built-in file path tab completion
  • Add tab completion setup instructions to README, including a fixed zsh completion script that addresses bugs in the upstream dotnet-suggest script zsh output
  • Fix bug in unref command: The name argument was documented but being ignored — xrepo unref always removed all references regardless of whether a name was specified. Now xrepo unref <name> selectively removes only the project references associated with the given repo or package, while xrepo unref (with no argument) continues to remove everything.

How it works

System.CommandLine 2.0's RootCommand already includes the [suggest] directive, so xrepo supports the dotnet-suggest completion protocol out of the box. The CompletionSources.Add() calls provide dynamic values from the xrepo registry, and Option<FileInfo?> triggers native file path completion in the shell.

Selective unref

UnrefCommand now reads the optional name argument and dispatches to either:

  • UnrefAll (no argument): removes all XRepoReference ItemGroups from every consuming project, removes the xrepo solution folder, and runs dotnet restore.
  • UnrefByName (repo name or package ID): resolves the name to specific project paths, removes only those project references, and only removes the xrepo solution folder if no xrepo references remain anywhere.

New methods added to ConsumingProject:

  • RemoveXRepoProjectReference(string projectPath) — removes a specific reference, cleans up empty ItemGroups
  • HasXRepoProjectReferences() — checks if any XRepoReference ItemGroups remain

Test plan

  • All 61 tests pass (dotnet test) — 60 unit tests + 1 scenario test
  • Verified [suggest] directive returns subcommands
  • Verified [suggest] returns options for specific commands
  • Verified zsh tab completion works for repo names, package IDs, and file paths
  • New unit tests for RemoveXRepoProjectReference and HasXRepoProjectReferences
  • New integration-style tests verifying the ref/unref flow only links packages actually consumed by the solution

🤖 Generated with Claude Code

claude and others added 8 commits February 21, 2026 20:09
…ine CompletionSources

Add CompletionSources to command arguments so that dotnet-suggest can
provide dynamic tab completions from the xrepo registry:
- ref: suggests registered repo names and package IDs
- which/where: suggests registered package IDs
- repo unregister: suggests registered repo names
- unref: suggests registered repo names

https://claude.ai/code/session_01QqNiEkuZb4LPkFdZ8sV1rC
System.CommandLine automatically provides file path completions when
an option is typed as FileInfo instead of string.

https://claude.ai/code/session_01QqNiEkuZb4LPkFdZ8sV1rC
The ref command accepts repo names, package IDs, or .csproj file paths,
but tab completion only suggested repos and packages. Now it also
enumerates .csproj files from the current directory tree.

https://claude.ai/code/session_01QqNiEkuZb4LPkFdZ8sV1rC
Instead of pre-enumerating all .csproj files recursively, use
WordToComplete from the completion context to provide path-based
completions. This shows subdirectories and .csproj files matching
what the user has typed so far, allowing navigation to any .csproj
on the file system.

https://claude.ai/code/session_01QqNiEkuZb4LPkFdZ8sV1rC
Move the file path completion logic from RefCommand into a shared
FileCompletions.Get() helper that accepts file extensions. Add
solution file (.sln/.slnx) completion to the --solution option on
both RefCommand and UnrefCommand.

https://claude.ai/code/session_01QqNiEkuZb4LPkFdZ8sV1rC
Tests cover: empty input, directory prefix navigation, partial filename
filtering, partial directory name filtering, nonexistent directory,
multiple extensions, case insensitivity, and trailing separator on
directory completions.

https://claude.ai/code/session_01QqNiEkuZb4LPkFdZ8sV1rC
@andyalm

andyalm commented Feb 22, 2026

Copy link
Copy Markdown
Owner Author

Code review

Found 1 issue:

  1. UnrefCommand.nameArg has a CompletionSource wired to the repo registry, but parseResult.GetValue(nameArg) is never called in the action handler. The argument description says "The name of the repo to unref (omit to unref all)", implying selective removal, but the command always removes all xrepo references regardless. The pre-existing unused argument is now actively misleading because tab completion suggests repo names for a parameter that is silently ignored. Either the completion source should be removed, or the action handler should be updated to honor the name argument.

{
var nameArg = new Argument<string?>("name")
{
Description = "The name of the repo to unref (omit to unref all)",
Arity = ArgumentArity.ZeroOrOne
};
nameArg.CompletionSources.Add(ctx =>
environment.RepoRegistry.GetRepos().Select(r => new CompletionItem(r.Name))
);
var solutionOption = new Option<FileInfo?>("--solution", "-s")
{
Description = "The path to the solution file. Auto-detected if not specified."
};
solutionOption.CompletionSources.Add(ctx =>
FileCompletions.Get(ctx.WordToComplete, ".sln", ".slnx")
);
Arguments.Add(nameArg);
Options.Add(solutionOption);
this.SetAction(parseResult =>
{
var solutionPath = SolutionHelper.ResolveSolutionPath(parseResult.GetValue(solutionOption)?.FullName);
var solutionFile = SolutionFile.Read(solutionPath);
var allConsumingProjects = solutionFile.ConsumingProjects().ToArray();
foreach (var project in allConsumingProjects)
{
if (project.RemoveXRepoProjectReferences())
project.Save();
}
solutionFile.RemoveSolutionFolder("xrepo");
solutionFile.Write();
Console.WriteLine("All xrepo project references have been removed. Running dotnet restore...");
SolutionHelper.DotnetRestore(solutionPath);
});

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

…t reference removal and improve maintainability. Added tests for `ResolveProjects` behavior.
System.CommandLine 2.0.3 renders CompletionSources values as the
argument placeholder in --help output. Setting HelpName on each
Argument and Option that has dynamic completions tells the help
builder to display a clean label instead.
…updated `Ref` and `Unref` commands to use the new logic.
@andyalm
andyalm merged commit 66dfaa0 into main Feb 22, 2026
1 check passed
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.

2 participants