Skip to content

[Bug]: specify extension add silently skips command registration when target agent directory does not exist #2769

@stn1slv

Description

@stn1slv

Bug Description

specify extension add can finish with the message "Extension installed successfully" while registering zero commands for any AI agent. When this happens, the registry entry contains "registered_commands": {}, no command file or skill is created for the agent, and the command never appears in the agent (for example, it is not available as a slash command in Claude Code).

The root cause is that command registration is conditional on the target agent's output directory already existing in the project, and the skip is silent. There is no warning when no agent matched, and the success output still lists the manifest's provided commands, so the user has no signal that registration did not happen.

This was reported by users of two community extensions:

In both cases the extension manifests and command files are valid and follow the Extension API. The problem is in the core installation flow.

Steps to Reproduce

  1. Have a project that contains .specify/ but where the Claude agent output directory .claude/skills/ does not exist (for example, the project was not initialized for the Claude agent, or that directory was removed).
  2. Run:
    specify extension add archive --from https://github.com/stn1slv/spec-kit-archive/archive/refs/tags/v1.0.0.zip
    
  3. Observe the CLI prints "Extension installed successfully" and lists the provided command speckit.archive.run.
  4. Inspect .specify/extensions/.registry. The entry shows "registered_commands": {}.
  5. Inspect .claude/skills/. No speckit-archive-run/SKILL.md was created.
  6. The command is not available in the agent.

Expected Behavior

One of the following:

  1. The installer registers the command for the configured agent even when the agent output directory does not yet exist (creating the directory as needed), or
  2. If registration is intentionally limited to already-detected agents, the installer clearly warns the user when zero agents matched, so the silent "installed but unusable" state cannot happen.

In all cases, the success message should reflect what was actually registered, not just the commands declared in the manifest.

Actual Behavior

Registration is skipped silently and the install is reported as successful. The registry records "registered_commands": {} and the command is unusable.

Root Cause (source analysis)

Analysis is against the current main branch (also present in releases checked from v0.5.0 to v0.8.18).

  1. ExtensionManager.install_from_directory in src/specify_cli/extensions.py calls the registrar to register commands for all agents and stores the result, even when it is empty:

    registered_commands = {}
    if register_commands:
        registrar = CommandRegistrar()
        registered_commands = registrar.register_commands_for_all_agents(
            manifest, dest_dir, self.project_root, link_outputs=link_commands
        )
    ...
    self.registry.add(manifest.id, {
        ...
        "registered_commands": registered_commands,  # may be {}
        ...
    })
  2. CommandRegistrar.register_commands_for_all_agents in src/specify_cli/agents.py only registers for an agent when its resolved output directory already exists:

    for agent_name, agent_config in self.AGENT_CONFIGS.items():
        detect_dir_str = agent_config.get("detect_dir")
        if detect_dir_str:
            detect_path = project_root / detect_dir_str
            if not detect_path.exists():
                continue
        agent_dir = self._resolve_agent_dir(agent_name, agent_config, project_root)
        if agent_dir.exists():          # <-- silent gate
            ...

    For Claude, registrar_config["dir"] is .claude/skills. If that directory does not exist at install time, Claude is skipped with no message, and the function returns {}.

  3. The install success path in src/specify_cli/__init__.py prints "Extension installed successfully", then lists manifest.commands regardless of what was registered. It reports registered_skills count but never reports or warns about an empty registered_commands:

    console.print("\n[green]✓[/green] Extension installed successfully!")
    ...
    console.print("\n[bold cyan]Provided commands:[/bold cyan]")
    for cmd in manifest.commands:
        console.print(f"  • {cmd['name']} - {cmd.get('description', '')}")

Suggested Fix

  • At minimum, warn when register_commands_for_all_agents returns an empty result, and tell the user how to register the command later (for example, by running specify integration switch <agent>, which already re-registers enabled extensions via register_enabled_extensions_for_agent).
  • Better, register the command for the project's configured agent even when its output directory does not yet exist, creating the directory as part of installation.
  • Make the post-install summary reflect actual registrations per agent, not just the manifest's declared commands.

Environment

  • Specify CLI Version: 0.8.16 (source analysis also covered v0.5.0 through v0.8.18)
  • AI Agent: Claude Code
  • Operating System: macOS (Darwin Kernel 25.4.0, arm64)
  • Python Version: 3.14.5

Additional Context

A working repair for affected users is to ensure the agent is initialized so its directory exists, then either re-add the extension or run specify integration switch claude to re-register enabled extensions. The silent skip is the core issue: the install reports success while producing an unusable state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions