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
- 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).
- Run:
specify extension add archive --from https://github.com/stn1slv/spec-kit-archive/archive/refs/tags/v1.0.0.zip
- Observe the CLI prints "Extension installed successfully" and lists the provided command
speckit.archive.run.
- Inspect
.specify/extensions/.registry. The entry shows "registered_commands": {}.
- Inspect
.claude/skills/. No speckit-archive-run/SKILL.md was created.
- The command is not available in the agent.
Expected Behavior
One of the following:
- 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
- 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).
-
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 {}
...
})
-
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 {}.
-
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.
Bug Description
specify extension addcan 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:
speckit.archive.runcommand not registered in.claude/commands/after installation stn1slv/spec-kit-archive#2speckit.reconcile.runcommand not registered in.claude/commands/after installation stn1slv/spec-kit-reconcile#2In 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
.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).speckit.archive.run..specify/extensions/.registry. The entry shows"registered_commands": {}..claude/skills/. Nospeckit-archive-run/SKILL.mdwas created.Expected Behavior
One of the following:
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
mainbranch (also present in releases checked from v0.5.0 to v0.8.18).ExtensionManager.install_from_directoryinsrc/specify_cli/extensions.pycalls the registrar to register commands for all agents and stores the result, even when it is empty:CommandRegistrar.register_commands_for_all_agentsinsrc/specify_cli/agents.pyonly registers for an agent when its resolved output directory already exists: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{}.The install success path in
src/specify_cli/__init__.pyprints "Extension installed successfully", then listsmanifest.commandsregardless of what was registered. It reportsregistered_skillscount but never reports or warns about an emptyregistered_commands:Suggested Fix
register_commands_for_all_agentsreturns an empty result, and tell the user how to register the command later (for example, by runningspecify integration switch <agent>, which already re-registers enabled extensions viaregister_enabled_extensions_for_agent).Environment
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 claudeto re-register enabled extensions. The silent skip is the core issue: the install reports success while producing an unusable state.