mirror of
https://github.com/github/spec-kit.git
synced 2026-08-03 06:26:30 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user