From 21ffb5d1e4d7add27de96464cb772f17fa9bf374 Mon Sep 17 00:00:00 2001 From: shreejaykurhade Date: Fri, 24 Apr 2026 22:35:44 +0530 Subject: [PATCH 1/4] fixed bugs and improved perf and docs --- .../content/docs/reference/cli-commands.md | 89 ++++++++++++++++++- src/apm_cli/commands/experimental.py | 1 - src/apm_cli/commands/mcp.py | 86 +++++++++++++++++- src/apm_cli/commands/outdated.py | 2 +- src/apm_cli/commands/pack.py | 4 +- src/apm_cli/commands/runtime.py | 2 +- src/apm_cli/output/script_formatters.py | 8 +- tests/unit/test_cli_consistency.py | 65 ++++++++++++++ tests/unit/test_mcp_command.py | 7 ++ uv.lock | 2 +- 10 files changed, 249 insertions(+), 17 deletions(-) create mode 100644 tests/unit/test_cli_consistency.py diff --git a/docs/src/content/docs/reference/cli-commands.md b/docs/src/content/docs/reference/cli-commands.md index 01b4691f5..e075a229f 100644 --- a/docs/src/content/docs/reference/cli-commands.md +++ b/docs/src/content/docs/reference/cli-commands.md @@ -996,6 +996,22 @@ Alias for [`apm install --mcp`](#apm-install---install-dependencies-and-deploy-l apm mcp install NAME [OPTIONS] [-- COMMAND ARGV...] ``` +**Arguments:** +- `NAME` - MCP server name. Use a registry name for registry installs, or a local name for self-defined stdio and remote servers. + +**Options:** +- `--transport [stdio|http|sse|streamable-http]` - MCP transport. Inferred from `--url` or post-`--` argv when omitted. +- `--url URL` - MCP server URL for `http`, `sse`, or `streamable-http` transports. +- `--env KEY=VALUE` - Environment variable for stdio MCP servers. Repeatable. +- `--header KEY=VALUE` - HTTP header for remote MCP servers. Repeatable. +- `--mcp-version VER` - Pin a registry MCP entry to a specific version. +- `--registry URL` - Custom MCP registry URL for resolving `NAME`. +- `--dev` - Add the server to `devDependencies`. +- `--dry-run` - Show what would be added without writing. +- `--force` - Replace an existing MCP entry. +- `-v, --verbose` - Show detailed output. +- `--no-policy` - Skip org policy enforcement for this invocation. + **Examples:** ```bash # stdio (post-`--` argv) @@ -1699,7 +1715,7 @@ apm runtime remove [OPTIONS] {copilot|codex|llm} - `{copilot|codex|llm}` - Runtime to remove **Options:** -- `--yes` - Confirm the action without prompting +- `-y, --yes` - Confirm the action without prompting #### `apm runtime status` - Show active runtime and preference order @@ -1716,4 +1732,73 @@ apm runtime status ## Experimental Features -`apm experimental` manages opt-in flags that gate new or changing behaviour. Subcommands: `list`, `enable`, `disable`, `reset`. `apm experimental list` also supports `--json`, and `-v` / `--verbose` works on each subcommand. See the full reference in [Experimental Flags](../experimental/). +### `apm experimental` - Manage experimental feature flags + +Manage opt-in flags that gate new or changing behaviour. Running `apm experimental` with no subcommand lists the available flags. + +```bash +apm experimental [OPTIONS] COMMAND [ARGS]... +``` + +**Options:** +- `-v, --verbose` - Show verbose output + +**Subcommands:** + +| Command | Description | +|---------|-------------| +| `list` | List all experimental features | +| `enable NAME` | Enable an experimental feature | +| `disable NAME` | Disable an experimental feature | +| `reset [NAME]` | Reset one feature, or all features, to defaults | + +#### `apm experimental list` + +```bash +apm experimental list [OPTIONS] +``` + +**Options:** +- `--enabled` - Show only enabled features +- `--disabled` - Show only disabled features +- `--json` - Output as a JSON array +- `-v, --verbose` - Show detailed output + +#### `apm experimental enable` + +```bash +apm experimental enable NAME [OPTIONS] +``` + +**Arguments:** +- `NAME` - Experimental feature name + +**Options:** +- `-v, --verbose` - Show verbose output + +#### `apm experimental disable` + +```bash +apm experimental disable NAME [OPTIONS] +``` + +**Arguments:** +- `NAME` - Experimental feature name + +**Options:** +- `-v, --verbose` - Show verbose output + +#### `apm experimental reset` + +```bash +apm experimental reset [NAME] [OPTIONS] +``` + +**Arguments:** +- `NAME` - Optional experimental feature name. Omit to reset all feature overrides. + +**Options:** +- `-y, --yes` - Skip the confirmation prompt when resetting all features +- `-v, --verbose` - Show verbose output + +See the full reference in [Experimental Flags](../experimental/). diff --git a/src/apm_cli/commands/experimental.py b/src/apm_cli/commands/experimental.py index 12905e44b..dfec8f5a6 100644 --- a/src/apm_cli/commands/experimental.py +++ b/src/apm_cli/commands/experimental.py @@ -140,7 +140,6 @@ def _handle_unknown_flag(name: str, logger: CommandLogger) -> None: @click.group( help="Manage experimental feature flags", invoke_without_command=True, - context_settings={"allow_interspersed_args": True, "ignore_unknown_options": True}, ) @click.option("--verbose", "-v", is_flag=True, default=False, help="Show verbose output") @click.pass_context diff --git a/src/apm_cli/commands/mcp.py b/src/apm_cli/commands/mcp.py index 811b90eb2..b785d5f03 100644 --- a/src/apm_cli/commands/mcp.py +++ b/src/apm_cli/commands/mcp.py @@ -84,8 +84,63 @@ def mcp(): " apm mcp install api --transport http --url https://example.com/mcp" ), ) +@click.argument("name", required=True) +@click.option( + "--transport", + type=click.Choice(["stdio", "http", "sse", "streamable-http"]), + default=None, + help="MCP transport. Inferred from --url or post-`--` argv when omitted", +) +@click.option( + "--url", + default=None, + help="MCP server URL for http/sse/streamable-http transports", +) +@click.option( + "--env", + "env_pairs", + multiple=True, + help="Environment variable for stdio MCP, repeatable", +) +@click.option( + "--header", + "header_pairs", + multiple=True, + help="HTTP header for remote MCP, repeatable", +) +@click.option( + "--mcp-version", + "mcp_version", + default=None, + help="Pin MCP registry entry to a specific version", +) +@click.option( + "--registry", + "registry_url", + default=None, + help="MCP registry URL for resolving NAME", +) +@click.option("--dev", is_flag=True, default=False, help="Add to devDependencies") +@click.option("--dry-run", is_flag=True, default=False, help="Show what would be added without writing") +@click.option("--force", is_flag=True, default=False, help="Replace an existing MCP entry") +@click.option("--verbose", "-v", is_flag=True, default=False, help="Show detailed output") +@click.option("--no-policy", is_flag=True, default=False, help="Skip org policy enforcement for this invocation") @click.pass_context -def mcp_install(ctx): +def mcp_install( + ctx, + name, + transport, + url, + env_pairs, + header_pairs, + mcp_version, + registry_url, + dev, + dry_run, + force, + verbose, + no_policy, +): """Forward all args to 'apm install --mcp ...'. Examples: @@ -103,12 +158,35 @@ def mcp_install(ctx): # ``-y`` would be re-parsed as Click options when forwarded to # ``cli.main()``. Re-insert the boundary by inspecting the raw # process argv (same seam the ``install`` command uses). + forwarded = ["install", "--mcp", name] + if transport: + forwarded.extend(["--transport", transport]) + if url: + forwarded.extend(["--url", url]) + for env_pair in env_pairs: + forwarded.extend(["--env", env_pair]) + for header_pair in header_pairs: + forwarded.extend(["--header", header_pair]) + if mcp_version: + forwarded.extend(["--mcp-version", mcp_version]) + if registry_url: + forwarded.extend(["--registry", registry_url]) + if dev: + forwarded.append("--dev") + if dry_run: + forwarded.append("--dry-run") + if force: + forwarded.append("--force") + if verbose: + forwarded.append("--verbose") + if no_policy: + forwarded.append("--no-policy") + _, post_dd = _split_argv_at_double_dash(_get_invocation_argv()) if post_dd: - pre_args = ctx.args[: len(ctx.args) - len(post_dd)] - forwarded = ["install", "--mcp", *pre_args, "--", *post_dd] + forwarded.extend(["--", *post_dd]) else: - forwarded = ["install", "--mcp", *ctx.args] + forwarded.extend(ctx.args) try: cli.main(args=forwarded, standalone_mode=False) diff --git a/src/apm_cli/commands/outdated.py b/src/apm_cli/commands/outdated.py index b34739d0f..13c6781f2 100644 --- a/src/apm_cli/commands/outdated.py +++ b/src/apm_cli/commands/outdated.py @@ -225,7 +225,7 @@ def _check_one_dep(dep, downloader, verbose): @click.option("--parallel-checks", "-j", type=int, default=4, help="Max concurrent remote checks (default: 4, 0 = sequential)") def outdated(global_, verbose, parallel_checks): - """Show outdated locked dependencies. + """Show outdated locked dependencies Compares each locked dependency against the remote to detect staleness. Tag-pinned deps use semver comparison; branch-pinned deps compare commit SHAs. diff --git a/src/apm_cli/commands/pack.py b/src/apm_cli/commands/pack.py index d32eb4c08..f1231e11e 100644 --- a/src/apm_cli/commands/pack.py +++ b/src/apm_cli/commands/pack.py @@ -34,7 +34,7 @@ default="./build", help="Output directory (default: ./build).", ) -@click.option("--dry-run", is_flag=True, default=False, help="Show what would be packed without writing.") +@click.option("--dry-run", is_flag=True, default=False, help="Show what would be packed without writing") @click.option("--force", is_flag=True, default=False, help="On collision, last writer wins.") @click.option("--verbose", "-v", is_flag=True, help="Show detailed packing information") @click.pass_context @@ -107,7 +107,7 @@ def pack_cmd(ctx, fmt, target, archive, output, dry_run, force, verbose): help="Target directory (default: current directory).", ) @click.option("--skip-verify", is_flag=True, default=False, help="Skip bundle completeness check.") -@click.option("--dry-run", is_flag=True, default=False, help="Show what would be unpacked without writing.") +@click.option("--dry-run", is_flag=True, default=False, help="Show what would be unpacked without writing") @click.option("--force", is_flag=True, default=False, help="Deploy despite critical hidden-character findings.") @click.option("--verbose", "-v", is_flag=True, help="Show detailed unpacking information") @click.pass_context diff --git a/src/apm_cli/commands/runtime.py b/src/apm_cli/commands/runtime.py index 40b0f01de..6c89d6c74 100644 --- a/src/apm_cli/commands/runtime.py +++ b/src/apm_cli/commands/runtime.py @@ -124,7 +124,7 @@ def list(): @runtime.command(help="Remove an installed runtime") @click.argument("runtime_name", type=click.Choice(["copilot", "codex", "llm"])) -@click.confirmation_option(prompt="Are you sure you want to remove this runtime?", help="Confirm the action without prompting") +@click.confirmation_option("--yes", "-y", prompt="Are you sure you want to remove this runtime?", help="Confirm the action without prompting") def remove(runtime_name): """Remove an installed runtime from APM management.""" logger = CommandLogger("runtime remove") diff --git a/src/apm_cli/output/script_formatters.py b/src/apm_cli/output/script_formatters.py index d73de659c..2c42bf34d 100644 --- a/src/apm_cli/output/script_formatters.py +++ b/src/apm_cli/output/script_formatters.py @@ -7,8 +7,6 @@ from rich.console import Console from rich.text import Text from rich.panel import Panel - from rich.tree import Tree - from rich import box RICH_AVAILABLE = True except ImportError: RICH_AVAILABLE = False @@ -40,9 +38,9 @@ def format_script_header(self, script_name: str, params: Dict[str, str]) -> List # Main header if self.use_color: - lines.append(self._styled(f" Running script: {script_name}", "cyan bold")) + lines.append(self._styled(f"[>] Running script: {script_name}", "cyan bold")) else: - lines.append(f" Running script: {script_name}") + lines.append(f"[>] Running script: {script_name}") # Parameters tree if any exist if params: @@ -344,4 +342,4 @@ def _styled(self, text: str, style: str) -> str: self.console.print(styled_text, end="") return capture.get() else: - return text \ No newline at end of file + return text diff --git a/tests/unit/test_cli_consistency.py b/tests/unit/test_cli_consistency.py new file mode 100644 index 000000000..589271898 --- /dev/null +++ b/tests/unit/test_cli_consistency.py @@ -0,0 +1,65 @@ +"""Regression tests for CLI help and output consistency.""" + +from click.testing import CliRunner + +from apm_cli.cli import cli +from apm_cli.output.script_formatters import ScriptExecutionFormatter + + +def test_experimental_subcommand_help_is_specific(): + runner = CliRunner() + + list_result = runner.invoke(cli, ["experimental", "list", "--help"]) + assert list_result.exit_code == 0 + assert "Usage: cli experimental list [OPTIONS]" in list_result.output + assert "--enabled" in list_result.output + assert "--disabled" in list_result.output + assert "--json" in list_result.output + + enable_result = runner.invoke(cli, ["experimental", "enable", "--help"]) + assert enable_result.exit_code == 0 + assert "Usage: cli experimental enable [OPTIONS] NAME" in enable_result.output + + disable_result = runner.invoke(cli, ["experimental", "disable", "--help"]) + assert disable_result.exit_code == 0 + assert "Usage: cli experimental disable [OPTIONS] NAME" in disable_result.output + + reset_result = runner.invoke(cli, ["experimental", "reset", "--help"]) + assert reset_result.exit_code == 0 + assert "Usage: cli experimental reset [OPTIONS] [NAME]" in reset_result.output + assert "-y, --yes" in reset_result.output + + +def test_runtime_remove_help_includes_short_yes_alias(): + result = CliRunner().invoke(cli, ["runtime", "remove", "--help"]) + + assert result.exit_code == 0 + assert "-y, --yes" in result.output + + +def test_pack_unpack_dry_run_help_has_no_trailing_period(): + runner = CliRunner() + + pack_result = runner.invoke(cli, ["pack", "--help"]) + assert pack_result.exit_code == 0 + assert "Show what would be packed without writing." not in pack_result.output + assert "Show what would be packed without writing" in pack_result.output + + unpack_result = runner.invoke(cli, ["unpack", "--help"]) + assert unpack_result.exit_code == 0 + assert "Show what would be unpacked without writing." not in unpack_result.output + assert "Show what would be unpacked without writing" in unpack_result.output + + +def test_outdated_top_level_help_description_has_no_trailing_period(): + result = CliRunner().invoke(cli, ["--help"]) + + assert result.exit_code == 0 + assert "outdated Show outdated locked dependencies." not in result.output + assert "outdated Show outdated locked dependencies" in result.output + + +def test_script_run_header_uses_running_status_symbol(): + formatter = ScriptExecutionFormatter(use_color=False) + + assert formatter.format_script_header("build", {})[0] == "[>] Running script: build" diff --git a/tests/unit/test_mcp_command.py b/tests/unit/test_mcp_command.py index 45da4aacd..926afa0c1 100644 --- a/tests/unit/test_mcp_command.py +++ b/tests/unit/test_mcp_command.py @@ -486,6 +486,13 @@ def test_help_shows_alias_message_and_example(self): assert result.exit_code == 0 assert "Alias for 'apm install --mcp'" in result.output assert "apm mcp install fetch" in result.output + assert "Usage: mcp install [OPTIONS] NAME" in result.output + assert "--transport" in result.output + assert "--url" in result.output + assert "--env" in result.output + assert "--header" in result.output + assert "--mcp-version" in result.output + assert "--registry" in result.output def test_forwards_args_to_root_install_with_mcp_flag(self): """Verify the alias invokes the root `cli` with `install --mcp `.""" diff --git a/uv.lock b/uv.lock index b28996d6e..6676a6980 100644 --- a/uv.lock +++ b/uv.lock @@ -179,7 +179,7 @@ wheels = [ [[package]] name = "apm-cli" -version = "0.9.1" +version = "0.9.2" source = { editable = "." } dependencies = [ { name = "click" }, From 65ba1d1c385f3363a67c8d4418dfa0eeef2ea58c Mon Sep 17 00:00:00 2001 From: shreejaykurhade Date: Sun, 26 Apr 2026 03:05:33 +0530 Subject: [PATCH 2/4] fix(cli): restore mcp install passthrough forwarding --- .../.apm/skills/apm-usage/commands.md | 2 +- src/apm_cli/commands/mcp.py | 94 +++---------------- src/apm_cli/commands/pack.py | 10 +- tests/unit/test_cli_consistency.py | 45 ++++++++- uv.lock | 2 +- 5 files changed, 63 insertions(+), 90 deletions(-) diff --git a/packages/apm-guide/.apm/skills/apm-usage/commands.md b/packages/apm-guide/.apm/skills/apm-usage/commands.md index a70f485eb..f219338ce 100644 --- a/packages/apm-guide/.apm/skills/apm-usage/commands.md +++ b/packages/apm-guide/.apm/skills/apm-usage/commands.md @@ -79,7 +79,7 @@ Set `MCP_REGISTRY_URL` (default `https://api.mcp.github.com`) to point all `apm |---------|---------|-----------| | `apm runtime setup {copilot\|codex\|llm}` | Install a runtime | `--version`, `--vanilla` | | `apm runtime list` | Show installed runtimes | -- | -| `apm runtime remove {copilot\|codex\|llm}` | Remove a runtime | `--yes` | +| `apm runtime remove {copilot\|codex\|llm}` | Remove a runtime | `-y`, `--yes` | | `apm runtime status` | Show active runtime | -- | ## Experimental features diff --git a/src/apm_cli/commands/mcp.py b/src/apm_cli/commands/mcp.py index b785d5f03..b868ce81a 100644 --- a/src/apm_cli/commands/mcp.py +++ b/src/apm_cli/commands/mcp.py @@ -83,64 +83,19 @@ def mcp(): " apm mcp install fetch -- npx -y @modelcontextprotocol/server-fetch\n\n" " apm mcp install api --transport http --url https://example.com/mcp" ), + epilog=( + "Common options (see `apm install --mcp --help` for full list):\n" + " --transport [stdio|http|sse|streamable-http]\n" + " --url URL Server URL for remote transports\n" + " --env KEY=VALUE Environment variable (repeatable)\n" + " --header KEY=VALUE HTTP header (repeatable)\n" + " --registry URL Custom registry URL\n" + " --dev / --dry-run / --force / --verbose / --no-policy\n" + ), ) @click.argument("name", required=True) -@click.option( - "--transport", - type=click.Choice(["stdio", "http", "sse", "streamable-http"]), - default=None, - help="MCP transport. Inferred from --url or post-`--` argv when omitted", -) -@click.option( - "--url", - default=None, - help="MCP server URL for http/sse/streamable-http transports", -) -@click.option( - "--env", - "env_pairs", - multiple=True, - help="Environment variable for stdio MCP, repeatable", -) -@click.option( - "--header", - "header_pairs", - multiple=True, - help="HTTP header for remote MCP, repeatable", -) -@click.option( - "--mcp-version", - "mcp_version", - default=None, - help="Pin MCP registry entry to a specific version", -) -@click.option( - "--registry", - "registry_url", - default=None, - help="MCP registry URL for resolving NAME", -) -@click.option("--dev", is_flag=True, default=False, help="Add to devDependencies") -@click.option("--dry-run", is_flag=True, default=False, help="Show what would be added without writing") -@click.option("--force", is_flag=True, default=False, help="Replace an existing MCP entry") -@click.option("--verbose", "-v", is_flag=True, default=False, help="Show detailed output") -@click.option("--no-policy", is_flag=True, default=False, help="Skip org policy enforcement for this invocation") @click.pass_context -def mcp_install( - ctx, - name, - transport, - url, - env_pairs, - header_pairs, - mcp_version, - registry_url, - dev, - dry_run, - force, - verbose, - no_policy, -): +def mcp_install(ctx, name): """Forward all args to 'apm install --mcp ...'. Examples: @@ -158,35 +113,12 @@ def mcp_install( # ``-y`` would be re-parsed as Click options when forwarded to # ``cli.main()``. Re-insert the boundary by inspecting the raw # process argv (same seam the ``install`` command uses). - forwarded = ["install", "--mcp", name] - if transport: - forwarded.extend(["--transport", transport]) - if url: - forwarded.extend(["--url", url]) - for env_pair in env_pairs: - forwarded.extend(["--env", env_pair]) - for header_pair in header_pairs: - forwarded.extend(["--header", header_pair]) - if mcp_version: - forwarded.extend(["--mcp-version", mcp_version]) - if registry_url: - forwarded.extend(["--registry", registry_url]) - if dev: - forwarded.append("--dev") - if dry_run: - forwarded.append("--dry-run") - if force: - forwarded.append("--force") - if verbose: - forwarded.append("--verbose") - if no_policy: - forwarded.append("--no-policy") - _, post_dd = _split_argv_at_double_dash(_get_invocation_argv()) if post_dd: - forwarded.extend(["--", *post_dd]) + pre_args = ctx.args[: len(ctx.args) - len(post_dd)] + forwarded = ["install", "--mcp", name, *pre_args, "--", *post_dd] else: - forwarded.extend(ctx.args) + forwarded = ["install", "--mcp", name, *ctx.args] try: cli.main(args=forwarded, standalone_mode=False) diff --git a/src/apm_cli/commands/pack.py b/src/apm_cli/commands/pack.py index f1231e11e..5b40d072f 100644 --- a/src/apm_cli/commands/pack.py +++ b/src/apm_cli/commands/pack.py @@ -17,25 +17,25 @@ "fmt", type=click.Choice(["apm", "plugin"]), default="apm", - help="Bundle format.", + help="Bundle format", ) @click.option( "--target", "-t", type=TargetParamType(), default=None, - help="Target platform (comma-separated for multiple, e.g. claude,copilot). Use 'all' for every target. Auto-detects if not specified.", + help="Target platform (comma-separated for multiple, e.g. claude,copilot). Use 'all' for every target. Auto-detects if not specified", ) -@click.option("--archive", is_flag=True, default=False, help="Produce a .tar.gz archive.") +@click.option("--archive", is_flag=True, default=False, help="Produce a .tar.gz archive") @click.option( "-o", "--output", type=click.Path(), default="./build", - help="Output directory (default: ./build).", + help="Output directory (default: ./build)", ) @click.option("--dry-run", is_flag=True, default=False, help="Show what would be packed without writing") -@click.option("--force", is_flag=True, default=False, help="On collision, last writer wins.") +@click.option("--force", is_flag=True, default=False, help="On collision, last writer wins") @click.option("--verbose", "-v", is_flag=True, help="Show detailed packing information") @click.pass_context def pack_cmd(ctx, fmt, target, archive, output, dry_run, force, verbose): diff --git a/tests/unit/test_cli_consistency.py b/tests/unit/test_cli_consistency.py index 589271898..fe3c4e0f8 100644 --- a/tests/unit/test_cli_consistency.py +++ b/tests/unit/test_cli_consistency.py @@ -1,5 +1,7 @@ """Regression tests for CLI help and output consistency.""" +from unittest.mock import patch + from click.testing import CliRunner from apm_cli.cli import cli @@ -37,6 +39,45 @@ def test_runtime_remove_help_includes_short_yes_alias(): assert "-y, --yes" in result.output +def test_mcp_install_forwards_unknown_options_before_double_dash(): + runner = CliRunner() + + with runner.isolated_filesystem(), patch( + "apm_cli.commands.install._get_invocation_argv", + return_value=[ + "apm", + "mcp", + "install", + "myserver", + "--target", + "cursor", + "--dry-run", + "--", + "npx", + "-y", + "pkg", + ], + ): + result = runner.invoke( + cli, + [ + "mcp", + "install", + "myserver", + "--target", + "cursor", + "--dry-run", + "--", + "npx", + "-y", + "pkg", + ], + ) + + assert result.exit_code == 0 + assert "would add MCP server 'myserver'" in result.output + + def test_pack_unpack_dry_run_help_has_no_trailing_period(): runner = CliRunner() @@ -55,8 +96,8 @@ def test_outdated_top_level_help_description_has_no_trailing_period(): result = CliRunner().invoke(cli, ["--help"]) assert result.exit_code == 0 - assert "outdated Show outdated locked dependencies." not in result.output - assert "outdated Show outdated locked dependencies" in result.output + assert "Show outdated locked dependencies." not in result.output + assert "Show outdated locked dependencies" in result.output def test_script_run_header_uses_running_status_symbol(): diff --git a/uv.lock b/uv.lock index 6676a6980..b28996d6e 100644 --- a/uv.lock +++ b/uv.lock @@ -179,7 +179,7 @@ wheels = [ [[package]] name = "apm-cli" -version = "0.9.2" +version = "0.9.1" source = { editable = "." } dependencies = [ { name = "click" }, From 508ff9852ca95a6bbe66b4080fff0c99d95798a9 Mon Sep 17 00:00:00 2001 From: shreejaykurhade Date: Sun, 26 Apr 2026 03:18:05 +0530 Subject: [PATCH 3/4] fix(cli): resolve runtime remove merge conflict --- packages/apm-guide/.apm/skills/apm-usage/commands.md | 4 ++-- src/apm_cli/commands/runtime.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/apm-guide/.apm/skills/apm-usage/commands.md b/packages/apm-guide/.apm/skills/apm-usage/commands.md index f219338ce..5df0cada7 100644 --- a/packages/apm-guide/.apm/skills/apm-usage/commands.md +++ b/packages/apm-guide/.apm/skills/apm-usage/commands.md @@ -77,9 +77,9 @@ Set `MCP_REGISTRY_URL` (default `https://api.mcp.github.com`) to point all `apm | Command | Purpose | Key flags | |---------|---------|-----------| -| `apm runtime setup {copilot\|codex\|llm}` | Install a runtime | `--version`, `--vanilla` | +| `apm runtime setup {copilot\|codex\|llm\|gemini}` | Install a runtime | `--version`, `--vanilla` | | `apm runtime list` | Show installed runtimes | -- | -| `apm runtime remove {copilot\|codex\|llm}` | Remove a runtime | `-y`, `--yes` | +| `apm runtime remove {copilot\|codex\|llm\|gemini}` | Remove a runtime | `-y`, `--yes` | | `apm runtime status` | Show active runtime | -- | ## Experimental features diff --git a/src/apm_cli/commands/runtime.py b/src/apm_cli/commands/runtime.py index 6c89d6c74..0823fd816 100644 --- a/src/apm_cli/commands/runtime.py +++ b/src/apm_cli/commands/runtime.py @@ -123,7 +123,7 @@ def list(): @runtime.command(help="Remove an installed runtime") -@click.argument("runtime_name", type=click.Choice(["copilot", "codex", "llm"])) +@click.argument("runtime_name", type=click.Choice(["copilot", "codex", "llm", "gemini"])) @click.confirmation_option("--yes", "-y", prompt="Are you sure you want to remove this runtime?", help="Confirm the action without prompting") def remove(runtime_name): """Remove an installed runtime from APM management.""" From b3d55d620dc6aa2edd6ef27074b45dd39e87cb16 Mon Sep 17 00:00:00 2001 From: shreejaykurhade Date: Sun, 26 Apr 2026 17:15:40 +0530 Subject: [PATCH 4/4] fix(cli): document --mcp-version in mcp install help --- src/apm_cli/commands/mcp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apm_cli/commands/mcp.py b/src/apm_cli/commands/mcp.py index b868ce81a..d7ef6b050 100644 --- a/src/apm_cli/commands/mcp.py +++ b/src/apm_cli/commands/mcp.py @@ -90,6 +90,7 @@ def mcp(): " --env KEY=VALUE Environment variable (repeatable)\n" " --header KEY=VALUE HTTP header (repeatable)\n" " --registry URL Custom registry URL\n" + " --mcp-version VER Pin registry entry to a specific version\n" " --dev / --dry-run / --force / --verbose / --no-policy\n" ), )