diff --git a/src/Tests/ArgParserTests.cs b/src/Tests/ArgParserTests.cs
index fbdb1d7..59fe54f 100644
--- a/src/Tests/ArgParserTests.cs
+++ b/src/Tests/ArgParserTests.cs
@@ -141,6 +141,100 @@ public void Version_with_a_value_and_no_package_is_still_an_error()
Assert.Contains("PACKAGE_NAME", parsed.Error);
}
+ [Theory]
+ [InlineData("stop", "--version")]
+ [InlineData("stop", "--VERSION")]
+ public void Bare_version_after_a_package_is_forwarded_to_the_tool(params string[] args)
+ {
+ var parsed = ArgParser.Parse(args);
+ var viaSeparator = ArgParser.Parse(args[0], "--", args[1]);
+
+ Assert.True(parsed.Success);
+ Assert.False(parsed.ShowVersion);
+ Assert.Equal("stop", parsed.PackageId);
+ Assert.Null(parsed.Version);
+ Assert.Equal(viaSeparator.ForwardedArguments, parsed.ForwardedArguments);
+ Assert.Equal([args[1]], parsed.ForwardedArguments);
+ }
+
+ [Fact]
+ public void Bare_version_after_a_pinned_package_keeps_the_pin_and_forwards()
+ {
+ var parsed = ArgParser.Parse("stop@1.2.3", "--version");
+
+ Assert.True(parsed.Success);
+ Assert.Equal("stop", parsed.PackageId);
+ Assert.Equal("1.2.3", parsed.Version);
+ Assert.Equal(["--version"], parsed.ForwardedArguments);
+ }
+
+ [Fact]
+ public void Bare_version_after_other_options_still_forwards_only_the_flag()
+ {
+ var parsed = ArgParser.Parse(
+ "stop", "--prerelease", "--source", "https://feed.example", "--version");
+
+ Assert.True(parsed.Success);
+ Assert.False(parsed.ShowVersion);
+ Assert.Equal("stop", parsed.PackageId);
+ Assert.Null(parsed.Version);
+ Assert.True(parsed.Prerelease);
+ Assert.Equal(["https://feed.example"], parsed.Sources);
+ Assert.Equal(["--version"], parsed.ForwardedArguments);
+ }
+
+ [Fact]
+ public void Version_value_after_a_package_still_selects_that_version()
+ {
+ var parsed = ArgParser.Parse("stop", "--version", "1.2.3", "--", "--version");
+
+ Assert.True(parsed.Success);
+ Assert.Equal("stop", parsed.PackageId);
+ Assert.Equal("1.2.3", parsed.Version);
+ Assert.Equal(["--version"], parsed.ForwardedArguments);
+ }
+
+ [Fact]
+ public void Second_bare_version_is_forwarded_when_the_first_already_has_a_value()
+ {
+ var parsed = ArgParser.Parse("stop", "--version", "1.2.3", "--version");
+
+ Assert.True(parsed.Success);
+ Assert.Equal("1.2.3", parsed.Version);
+ Assert.Equal(["--version"], parsed.ForwardedArguments);
+ }
+
+ [Theory]
+ [InlineData("--yes", "--version")]
+ [InlineData("--update", "--version")]
+ [InlineData("--update", "1.2.3", "--version")]
+ public void Bare_version_without_a_package_still_requires_a_value(params string[] args)
+ {
+ var parsed = ArgParser.Parse(args);
+
+ Assert.False(parsed.Success);
+ Assert.False(parsed.ShowVersion);
+ Assert.Contains("Missing value for --version", parsed.Error);
+ }
+
+ [Fact]
+ public void Version_followed_by_another_token_is_still_that_tokens_value()
+ {
+ var parsed = ArgParser.Parse("stop", "--version", "--help");
+
+ Assert.False(parsed.Success);
+ Assert.Contains("Invalid version '--help'", parsed.Error);
+ }
+
+ [Fact]
+ public void Empty_version_assignment_is_still_missing_a_value()
+ {
+ var parsed = ArgParser.Parse("stop", "--version=");
+
+ Assert.False(parsed.Success);
+ Assert.Contains("Missing value for --version", parsed.Error);
+ }
+
[Fact]
public void Update_alone_is_a_self_update_to_latest()
{
diff --git a/src/ndx/ArgParser.cs b/src/ndx/ArgParser.cs
index 7397907..851732a 100644
--- a/src/ndx/ArgParser.cs
+++ b/src/ndx/ArgParser.cs
@@ -4,7 +4,9 @@ namespace ndx;
/// dnx.cmd-compatible argv split: first operand is PACKAGE[@VERSION], listed
/// flags are consumed by ndx, everything else (including tokens after --) is
/// forwarded to the child. --update [VERSION] is a standalone self-update.
-/// A lone --version prints the ndx version.
+/// A lone --version prints the ndx version. After a package id, a
+/// --version with no value is forwarded to the tool, as in
+/// ndx stop -- --version.
///
public static class ArgParser
{
@@ -106,7 +108,18 @@ public static Invocation Parse(IReadOnlyList args)
if (value is null)
{
if (i + 1 >= args.Count)
+ {
+ // `ndx stop --version` names the tool, not a package version.
+ // A following token is still the value (`--version 1.2.3`,
+ // or an invalid one such as `--version --help`).
+ if (IsVersionOption(option) && packageId is not null && !update)
+ {
+ forwarded.Add(arg);
+ continue;
+ }
+
return Invocation.Failed($"Missing value for {option}.");
+ }
value = args[++i];
}
@@ -332,11 +345,12 @@ static bool TryParseIdentity(string token, out string? packageId, out string? ve
static bool IsBareVersionFlag(string token)
{
var (option, inline) = SplitOption(token);
- return option is not null
- && option.Equals("--version", StringComparison.OrdinalIgnoreCase)
- && inline is null;
+ return IsVersionOption(option) && inline is null;
}
+ static bool IsVersionOption(string? option)
+ => option is not null && option.Equals("--version", StringComparison.OrdinalIgnoreCase);
+
static (string? Name, string? InlineValue) SplitOption(string token)
{
if (token.Length < 2 || token[0] != '-')