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:
specify init . --ai claude --ai-skills --force --ignore-agent-tools
- 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.
- Inspect
.specify/extensions/.registry:
"my-extension": {
"registered_commands": { "copilot": ["..."] }, // claude missing
"registered_skills": [] // empty
}
ls .claude/skills/ — the extension's skill directory is absent.
- 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.
- 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:711 — register_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.
Bug Description
When
specify extension addruns 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": [], noSKILL.mdis 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 inspecify 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:specify init . --ai claude --ai-skills --force --ignore-agent-toolsspecify extension add --dev /path/to/my-extensionwheremy-extensiondeclares one command and oneafter_implementhook bound to that command..specify/extensions/.registry:ls .claude/skills/— the extension's skill directory is absent.specify extension addfor any other extension; that one's skill does get written to.claude/skills/. The first extension's skill is still missing./speckit.implement). Theafter_implementhook formy-extensionnever executes; no warning is emitted.Expected Behavior
specify extension addshould produce a working installation regardless of which agent-side directories already exist. Withai_skillsenabled (or for native-skills agents like Codex), the skills dir should be created on demand and the extension's commands materialized asSKILL.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
mainongithub/spec-kit):src/specify_cli/extensions.py:831—_get_skills_dirreturnsNonewhenskills_dir.is_dir()is false:→
_register_extension_skillsthen returns[]and the install proceeds as if no skills were requested.src/specify_cli/agents.py:711—register_commands_for_all_agentshas the same gate for slash-command registration:For Claude,
agent_config["dir"]resolves to.claude/skills(perintegrations/claude/__init__.py), so the same precondition bites both registration paths.PR #1840 added
_register_extension_skillsbut did not guarantee the target directory exists. PR #2404 made the integration switch path resilient by introducingregister_enabled_extensions_for_agent, but its trigger isintegration_switchonly — 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), onceai_skills_enabledis true (or the agent is inNATIVE_SKILLS_AGENTS), create the directory before returning rather than bailing:The same conceptual fix applies to
agent_dir.exists()inregister_commands_for_all_agentsif 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-skillsand assert that.claude/skills/<command>/SKILL.mdexists and the registry records the skill. The same test parameterized overclaudeandcodexwould 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 infoall report success.Additional Context
This was discovered while investigating why a mandatory
after_implementhook on a third-party Claude extension was not firing during/speckit.implementruns. Registry forensics in.specify/extensions/.registryrevealed two extensions installed three minutes apart with different outcomes — the earlier install had emptyregistered_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) andmainon github/spec-kit.