Skip to content

[Bug]: Extension skill registration silently fails when extension is installed before agent skills directory exists #2682

@ddulic

Description

@ddulic

Bug Description

When specify extension add runs before anything else has created the agent's skills directory (e.g. .claude/skills/ for Claude, .agents/skills/ for Codex), the extension's command is silently not registered as a skill. The registry records "registered_skills": [], no SKILL.md is written, and any hook the extension declares (e.g. after_implement) silently never fires on subsequent core commands — with no warning at install time, at hook-resolution time, or anywhere in specify extension info.

A later install of a different extension (after which the skills dir now exists) only fixes that second extension. The first extension stays permanently unregistered until the user manually reinstalls it.

Steps to Reproduce

On a fresh project where .claude/skills/ does not yet exist:

  1. specify init . --ai claude --ai-skills --force --ignore-agent-tools
  2. Before any other spec-kit command runs, specify extension add --dev /path/to/my-extension where my-extension declares one command and one after_implement hook bound to that command.
  3. Inspect .specify/extensions/.registry:
    "my-extension": {
      "registered_commands": { "copilot": ["..."] },   // claude missing
      "registered_skills": []                           // empty
    }
  4. ls .claude/skills/ — the extension's skill directory is absent.
  5. Now run specify extension add for any other extension; that one's skill does get written to .claude/skills/. The first extension's skill is still missing.
  6. Trigger the relevant core command (e.g. /speckit.implement). The after_implement hook for my-extension never executes; no warning is emitted.

Expected Behavior

specify extension add should produce a working installation regardless of which agent-side directories already exist. With ai_skills enabled (or for native-skills agents like Codex), the skills dir should be created on demand and the extension's commands materialized as SKILL.md. The hook should fire as documented.

If for some reason the install legitimately cannot complete, the CLI should surface a warning rather than silently leaving registered_skills: [].

Actual Behavior

Silent no-op. The user only discovers the missing registration much later when their hook fails to run.

Root Cause

In specify-cli 0.8.12 (matches current main on github/spec-kit):

  • src/specify_cli/extensions.py:831_get_skills_dir returns None when skills_dir.is_dir() is false:

    skills_dir = resolve_skills_dir(self.project_root, agent)
    if not skills_dir.is_dir():
        return None

    _register_extension_skills then returns [] and the install proceeds as if no skills were requested.

  • src/specify_cli/agents.py:711register_commands_for_all_agents has the same gate for slash-command registration:

    for agent_name, agent_config in self.AGENT_CONFIGS.items():
        agent_dir = project_root / agent_config["dir"]
        if agent_dir.exists():
            ...

    For Claude, agent_config["dir"] resolves to .claude/skills (per integrations/claude/__init__.py), so the same precondition bites both registration paths.

PR #1840 added _register_extension_skills but did not guarantee the target directory exists. PR #2404 made the integration switch path resilient by introducing register_enabled_extensions_for_agent, but its trigger is integration_switch only — the failure mode in this issue (extension installed → skills dir appears later via any non-switch path) is not covered.

Suggested Fix

In _get_skills_dir (extensions.py:801–834), once ai_skills_enabled is true (or the agent is in NATIVE_SKILLS_AGENTS), create the directory before returning rather than bailing:

skills_dir = resolve_skills_dir(self.project_root, agent)
skills_dir.mkdir(parents=True, exist_ok=True)
return skills_dir

The same conceptual fix applies to agent_dir.exists() in register_commands_for_all_agents if you want full parity for non-skills agents (though that's a separate consideration — a non-skills agent that hasn't been initialized arguably shouldn't receive commands).

A regression test would install an extension immediately after specify init --ai-skills and assert that .claude/skills/<command>/SKILL.md exists and the registry records the skill. The same test parameterized over claude and codex would cover both code paths.

Specify CLI Version

0.8.12

AI Agent

Claude Code

Operating System

macOS 26.5 (Darwin 25.5.0)

Python Version

3.13 (project) / 3.14 (specify CLI runtime)

Error Logs

No errors. The bug is silent at every stage — install, hook resolution, and specify extension info all report success.

Additional Context

This was discovered while investigating why a mandatory after_implement hook on a third-party Claude extension was not firing during /speckit.implement runs. Registry forensics in .specify/extensions/.registry revealed two extensions installed three minutes apart with different outcomes — the earlier install had empty registered_skills, the later install (after the directory had been created in the interim) had its skill materialized correctly. Confirmed by reading the same code in both the installed CLI (0.8.12) and main on github/spec-kit.

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