From 76e490e0d063a0120f144e89d976a5361f146ac7 Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Thu, 23 Jul 2026 00:22:31 +0500 Subject: [PATCH] fix(extensions): hyphenate command names in the Forge post-install listing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After `specify extension add`, the "Provided commands" summary hyphenated command names only for Cline. For a Forge project the names were printed in dotted form (e.g. `speckit.test-ext.hello`), but Forge registers them hyphenated (`speckit-test-ext-hello`), so the printed names didn't match what the user actually invokes in Forge. Extend the existing Cline handling to Forge via `format_forge_command_name`, completing the Forge command-name parity already fixed for hook invocations (#3641) and the init next-steps panel (#3642). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 (1M context) --- src/specify_cli/extensions/_commands.py | 8 ++++++- tests/test_extensions.py | 31 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/specify_cli/extensions/_commands.py b/src/specify_cli/extensions/_commands.py index 4494e15114..3c4b940fdf 100644 --- a/src/specify_cli/extensions/_commands.py +++ b/src/specify_cli/extensions/_commands.py @@ -623,16 +623,22 @@ def extension_add( for warning in manifest.warnings: console.print(f"\n[yellow]⚠ Compatibility warning:[/yellow] {_escape_markup(str(warning))}") - is_cline = load_init_options(project_root).get("ai") == "cline" + selected_ai = load_init_options(project_root).get("ai") + is_cline = selected_ai == "cline" + is_forge = selected_ai == "forge" if is_cline: from specify_cli.integrations.cline import format_cline_command_name + if is_forge: + from specify_cli.integrations.forge import format_forge_command_name console.print("\n[bold cyan]Provided commands:[/bold cyan]") for cmd in manifest.commands: cmd_name = cmd['name'] if is_cline: cmd_name = format_cline_command_name(cmd_name) + elif is_forge: + cmd_name = format_forge_command_name(cmd_name) console.print(f" • {_escape_markup(str(cmd_name))} - {_escape_markup(str(cmd.get('description', '')))}") # Report agent skills registration diff --git a/tests/test_extensions.py b/tests/test_extensions.py index 00d8da152f..f26dc2bf2f 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -9010,3 +9010,34 @@ def test_non_utf8_registry_does_not_crash(self, tmp_path, monkeypatch): # Must not raise; must fall back to the "no siblings" path. cfg = ConfigManager(tmp_path, "testext")._get_env_config() assert cfg == {"url": "v"} + + +def test_forge_extension_install_listing_hyphenates_command_names( + extension_dir, project_dir +): + """The post-install 'Provided commands' listing must show hyphenated + /speckit- command names for a Forge project (Forge registers + hyphenated names), mirroring the existing Cline handling.""" + import json + import os + + from typer.testing import CliRunner + + from specify_cli import app + + init_options = project_dir / ".specify" / "init-options.json" + init_options.write_text(json.dumps({"ai": "forge", "script": "sh"})) + + old_cwd = os.getcwd() + try: + os.chdir(project_dir) + result = CliRunner().invoke( + app, ["extension", "add", str(extension_dir), "--dev"] + ) + finally: + os.chdir(old_cwd) + + assert result.exit_code == 0, result.output + # 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