Skip to content

Commit ecd9c0a

Browse files
iamaeroplaneclaude
andcommitted
fix(extensions): warn on alias hook canonicalization; fix pluralization (#2017)
- Emit a compatibility warning when a hook ref is silently lifted from alias form (ext.cmd) to canonical (speckit.ext.cmd), so extension authors are notified to update their manifest in both rename and alias-canonicalization cases - Pluralize removal confirmation: '1 command' vs 'N commands from AI agent' - Update hook canonicalization test to assert the new warning is emitted - Fix test assertions to match correct singular/plural strings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9ab59cd commit ecd9c0a

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

src/specify_cli/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3139,7 +3139,8 @@ def extension_remove(
31393139
# Confirm removal
31403140
if not force:
31413141
console.print("\n[yellow]⚠ This will remove:[/yellow]")
3142-
console.print(f" • {cmd_count} commands from AI agent")
3142+
cmd_label = "command" if cmd_count == 1 else "commands"
3143+
console.print(f" • {cmd_count} {cmd_label} from AI agent")
31433144
if skill_count:
31443145
console.print(f" • {skill_count} agent skill(s)")
31453146
console.print(f" • Extension directory: .specify/extensions/{extension_id}/")

src/specify_cli/extensions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,14 @@ def _validate(self):
295295
f"updated to '{final_ref}'. "
296296
f"The extension author should update the manifest."
297297
)
298+
else:
299+
# Alias-form ref was canonicalized (e.g. 'ext.cmd' → 'speckit.ext.cmd').
300+
self.warnings.append(
301+
f"Hook '{hook_name}' referenced alias '{command_ref}'; "
302+
f"updated to canonical command '{final_ref}'. "
303+
f"The extension author should update the manifest to reference "
304+
f"the canonical command name directly."
305+
)
298306

299307
@staticmethod
300308
def _try_correct_command_name(name: str, ext_id: str) -> Optional[str]:

tests/test_extensions.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ def test_hook_alias_ref_canonicalized_to_speckit_form(self, temp_dir, valid_mani
393393

394394
# Hook ref should be lifted to canonical form for skill-mode invocation.
395395
assert manifest.hooks["after_tasks"]["command"] == "speckit.test-ext.greet"
396+
# A compatibility warning should be emitted so the author knows to update.
397+
assert len(manifest.warnings) == 1
398+
assert "test-ext.greet" in manifest.warnings[0]
399+
assert "speckit.test-ext.greet" in manifest.warnings[0]
396400

397401
def test_hook_non_dict_value_raises(self, temp_dir, valid_manifest_data):
398402
"""A non-mapping hooks value raises ValidationError."""
@@ -3144,8 +3148,8 @@ def test_remove_confirmation_counts_primary_command_only(self, extension_dir, pr
31443148
result = runner.invoke(app, ["extension", "remove", "test-ext"], input="n\n")
31453149

31463150
plain = strip_ansi(result.output)
3147-
# The fixture has 1 command and 0 aliases → "1 commands from AI agent"
3148-
assert "1 commands from AI agent" in plain
3151+
# The fixture has 1 command and 0 aliases → "1 command from AI agent"
3152+
assert "1 command from AI agent" in plain
31493153

31503154
def test_remove_confirmation_counts_aliases(self, temp_dir, project_dir):
31513155
"""Removal confirmation shows primary + alias count, not just primary."""
@@ -3198,9 +3202,9 @@ def test_remove_confirmation_counts_aliases(self, temp_dir, project_dir):
31983202
result = runner.invoke(app, ["extension", "remove", "ext-with-alias"], input="n\n")
31993203

32003204
plain = strip_ansi(result.output)
3201-
# 1 primary + 1 alias = "2 commands from AI agent" (not "1")
3205+
# 1 primary + 1 alias = "2 commands from AI agent" (not "1 command")
32023206
assert "2 commands from AI agent" in plain
3203-
assert "1 commands from AI agent" not in plain
3207+
assert "1 command from AI agent" not in plain
32043208

32053209

32063210
class TestExtensionUpdateCLI:

0 commit comments

Comments
 (0)