Skip to content

specify init does not copy templates/commands/ to .specify/templates/commands/ — wrap composition strategy fails #3086

Description

@djimit

specify init does not copy templates/commands/ to .specify/templates/commands/

Problem

During specify init, the install_shared_infra function in shared_infra.py copies template files from the bundled templates/ directory to .specify/templates/. However, it only processes files at the top level (src.is_file() check), skipping subdirectories — specifically templates/commands/.

This means the core command templates (e.g., implement.md, specify.md, plan.md, etc.) are installed directly to the agent directory (e.g., .opencode/commands/speckit.implement.md) via the integration system, but they are not kept in .specify/templates/commands/ for the preset resolver to find.

Impact

Preset authors who use the wrap composition strategy for command overrides (e.g., wrapping speckit.implement with a preamble) cannot resolve the core command as a base layer. The PresetResolver.collect_all_layers() method looks for core commands at:

c = self.templates_dir / "commands" / f"{template_name}.md"  # or stem fallback

Since .specify/templates/commands/ doesn't exist after specify init, the resolver finds no base layer, composition fails, and the command is silently not registered (registered_commands stays empty {}).

Reproduction

specify init --here --integration opencode --script sh --ignore-agent-tools
# .specify/templates/commands/ does NOT exist
# .opencode/commands/speckit.implement.md exists (installed by integration)

# Create a preset with a wrap strategy for speckit.implement
specify preset add --dev /path/to/my-preset
# registered_commands is {} — the wrap composition failed silently

Workaround

Manually copy command templates after init:

mkdir -p .specify/templates/commands
cp <spec-kit-source>/templates/commands/*.md .specify/templates/commands/

Suggested fix

In shared_infra.py, the install_shared_infra function should also copy the templates/commands/ subdirectory (with the same overwrite/manifest logic). Something like:

# After the top-level template files loop:
commands_src = templates_src / "commands"
if commands_src.is_dir():
    dest_commands = dest_templates / "commands"
    if _ensure_or_bucket_dir(dest_commands):
        for src_path in commands_src.iterdir():
            if not src_path.is_file() or src_path.name.startswith("."):
                continue
            dst = dest_commands / src_path.name
            rel = dst.relative_to(project_path).as_posix()
            # ... same overwrite/manifest logic as above ...

Alternatively, the PresetResolver could fall back to reading core commands from the agent directory (e.g., .opencode/commands/speckit.implement.md) when .specify/templates/commands/ doesn't have them.

Environment

  • Spec Kit v0.11.3
  • macOS Darwin arm64
  • Integration: opencode

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions