# How Template Flattening Works in Spec-Kit: Extracting Nested Directory Structures

> Discover how template flattening in Spec-Kit extracts nested directory structures by copying files using only their basename. Simplify your release package generation.

- Repository: [GitHub/spec-kit](https://github.com/github/spec-kit)
- Tags: internals
- Published: 2026-03-05

---

**Template flattening in Spec-Kit collapses nested directory hierarchies into a single level by copying files using only their basename, discarding parent folder paths during release package generation.**

Spec-Kit is an open-source CLI tool developed by GitHub that standardizes AI agent command templates across multiple platforms. When the project generates distribution packages for agents like Claude, Gemini, or OpenCode, it must extract command files from deeply nested source directories and consolidate them into flat release archives. This article explains how template flattening works when extracting these nested directory structures based on the actual implementation in the `github/spec-kit` repository.

## The Flattening Implementation in release-packages.sh

The core logic resides in [`.github/workflows/scripts/create-release-packages.sh`](https://github.com/github/spec-kit/blob/main/.github/workflows/scripts/create-release-packages.sh) within the `generate_commands` function. This bash script processes hidden agent directories (e.g., `.claude`, `.opencode`) and flattens their nested command structures into a single output directory.

The function executes four distinct operations:

1. **Locate the agent directory** using `find` to discover hidden folders matching the agent name pattern
2. **Resolve the command subdirectory** by extracting the `commands_subdir` value from [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py) using grep and awk
3. **Recursively find template files** matching `*.md` or `*.toml` extensions within the command subdirectory
4. **Flatten and copy** each file to the destination using `basename` to strip directory paths

### The Basename Technique

The critical operation that enables flattening occurs in the file copy command:

```bash
cp "$file" "$dest_dir/$(basename "$file")"

```

By wrapping the source file path in `$(basename ...)`, the command discards all parent directory information. Regardless of whether a file resides at [`.claude/commands/implement/spec.md`](https://github.com/github/spec-kit/blob/main/.claude/commands/implement/spec.md) or [`.claude/commands/spec.md`](https://github.com/github/spec-kit/blob/main/.claude/commands/spec.md), the output becomes simply [`spec.md`](https://github.com/github/spec-kit/blob/main/spec.md) in the destination folder.

## Source Configuration and Agent Detection

The script dynamically determines where command templates live by reading configuration from the Python source. In [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py), the `AGENT_CONFIG` dictionary defines the `commands_subdir` path for each supported agent.

The release script extracts this value at runtime:

```bash
command_subdir=$(grep -i "commands_subdir" -R "${BASE_DIR}/src/specify_cli/__init__.py" | awk -F'"' '{print $2}')
command_subdir=${command_subdir:-commands}

```

If the grep fails to locate the configuration, the script defaults to `"commands"` as the subdirectory name. This flexibility allows Spec-Kit to support agents like `cursor-agent`, `windsurf`, `kiro-cli`, `claude`, `gemini`, `copilot`, `qwen`, and `opencode` while maintaining a consistent flattening behavior.

## Practical Example: Before and After Flattening

Consider the source structure for the OpenCode agent:

```

.opencode/
└── commands/
    ├── implement/
    │   └── feature-spec.md
    └── refactor/
        └── code-cleanup.md

```

After running `generate_commands` with the flattening logic:

```

opencode-package/
├── feature-spec.md
└── code-cleanup.md

```

The nested folders `implement/` and `refactor/` are eliminated, while the Markdown files surface at the package root. This flat structure ensures downstream distribution archives contain only the essential template files without redundant directory nesting.

## Summary

- Template flattening occurs during release package generation in [`.github/workflows/scripts/create-release-packages.sh`](https://github.com/github/spec-kit/blob/main/.github/workflows/scripts/create-release-packages.sh)
- The `generate_commands` function uses `find` to locate agent-specific command templates
- File paths are flattened using the `basename` shell command when copying to the destination directory
- Configuration for subdirectory paths is extracted from [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py) at build time
- Supported agents include Claude, Gemini, Copilot, Cursor, Qwen, OpenCode, Windsurf, and Kiro CLI

## Frequently Asked Questions

### What is template flattening in Spec-Kit?

Template flattening is the process of removing directory hierarchy from nested command templates when creating release packages. Spec-Kit stores agent commands in organized subdirectories during development, but distributes them as a flat list of files to simplify installation and usage.

### Which file types does the flattening process handle?

The release script specifically targets Markdown (`.md`) and TOML (`.toml`) files within agent command directories. The find command uses the pattern `-name "*.md" -o -name "*.toml"` to filter relevant templates while ignoring other file types.

### How does Spec-Kit prevent filename collisions during flattening?

The current implementation does not explicitly handle duplicate basenames in the source analysis provided. If two files share identical names in different subdirectories, such as [`commands/build/spec.md`](https://github.com/github/spec-kit/blob/main/commands/build/spec.md) and [`commands/deploy/spec.md`](https://github.com/github/spec-kit/blob/main/commands/deploy/spec.md), the second copy operation would overwrite the first. The repository structure avoids such collisions through unique naming conventions for each command template.

### Where is the command subdirectory configuration defined?

The subdirectory path is defined in the `AGENT_CONFIG` dictionary within [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py) under the key `commands_subdir`. The release script extracts this value using grep, defaulting to `"commands"` if the configuration cannot be parsed.