fix(extensions): hyphenate command names in 'extension info' listing (#3744)

The 'Commands:' section of 'specify extension info' for a locally installed
extension printed each command in its manifest dotted form
(e.g. speckit.jira.sync). Cline and Forge register hyphenated command names
(/speckit-jira-sync), so on those projects the displayed names did not match
what the user actually invokes.

Format each name through the active agent's command-name formatter, mirroring
the parity 'extension add' already applies to its 'Provided commands' listing
(#3669) and completing the Forge/Cline command-name parity from #3641/#3642.

Adds a regression test asserting the hyphenated form appears (and the dotted
form does not) for a Forge project.
This commit is contained in:
Quratulain-bilal
2026-07-27 21:56:21 +05:00
committed by GitHub
parent 962f9f0765
commit e6a3ccfb27
2 changed files with 75 additions and 1 deletions

View File

@@ -893,9 +893,30 @@ def extension_info(
console.print()
if ext_manifest.commands:
# Print each command the way the active agent registers it.
# Cline and Forge hyphenate command names (e.g. Forge invokes
# `/speckit-jira-sync`, not the manifest's dotted
# `speckit.jira.sync`), so mirror the same formatting used by
# `extension add`'s "Provided commands" listing — otherwise the
# names shown here don't match what the user actually types.
selected_ai = load_init_options(project_root).get("ai")
if selected_ai == "cline":
from specify_cli.integrations.cline import (
format_cline_command_name as _format_command_name,
)
elif selected_ai == "forge":
from specify_cli.integrations.forge import (
format_forge_command_name as _format_command_name,
)
else:
_format_command_name = None
console.print("[bold]Commands:[/bold]")
for cmd in ext_manifest.commands:
console.print(f"{_escape_markup(str(cmd['name']))}: {_escape_markup(str(cmd.get('description', '')))}")
cmd_name = cmd['name']
if _format_command_name is not None:
cmd_name = _format_command_name(cmd_name)
console.print(f"{_escape_markup(str(cmd_name))}: {_escape_markup(str(cmd.get('description', '')))}")
console.print()
# Show catalog status

View File

@@ -9290,3 +9290,56 @@ def test_forge_extension_install_listing_hyphenates_command_names(
# Forge registers hyphenated command names, so the summary must match.
assert "speckit-test-ext-hello" in result.output
assert "speckit.test-ext.hello" not in result.output
def test_forge_extension_info_hyphenates_command_names(
extension_dir, project_dir, monkeypatch
):
"""`extension info` for an installed extension must show hyphenated
/speckit-<name> command names on a Forge project, matching the names Forge
actually registers — the same parity `extension add`'s listing already has.
"""
import io
import json
import os
from rich.console import Console
from specify_cli.extensions import _commands
init_options = project_dir / ".specify" / "init-options.json"
init_options.write_text(json.dumps({"ai": "forge", "script": "sh"}))
manager = ExtensionManager(project_dir)
manager.install_from_directory(
extension_dir, "1.0.0", register_commands=False
)
# Force the "installed locally, not in catalog" branch (the one that prints
# the local manifest's Commands section) and avoid any network catalog
# lookup.
monkeypatch.setattr(
_commands, "_resolve_catalog_extension", lambda *a, **k: (None, None)
)
# Call the handler directly against a plain captured Console. (Driving it
# through CliRunner reformats output via Rich's live console, which
# recurses under pytest's captured stdout — unrelated to this code path.)
buf = io.StringIO()
original_console = _commands.console
_commands.console = Console(file=buf, force_terminal=False, width=200)
old_cwd = os.getcwd()
try:
os.chdir(project_dir)
_commands.extension_info("test-ext")
except SystemExit:
pass
finally:
os.chdir(old_cwd)
_commands.console = original_console
output = buf.getvalue()
# The Commands section must render the hyphenated form Forge registers,
# not the manifest's dotted name.
assert "speckit-test-ext-hello" in output, output
assert "speckit.test-ext.hello" not in output, output