Skip to content

Comments

Spec Kit: support nested specs/<category>/<NNN-name>#1616

Closed
andrewvlgafc wants to merge 4 commits intogithub:mainfrom
andrewvlgafc:speckit-nested-specs
Closed

Spec Kit: support nested specs/<category>/<NNN-name>#1616
andrewvlgafc wants to merge 4 commits intogithub:mainfrom
andrewvlgafc:speckit-nested-specs

Conversation

@andrewvlgafc
Copy link

@andrewvlgafc andrewvlgafc commented Feb 16, 2026

changes as a title
I have many features but i cannot group it for example by core, tamplates,services, etc
I think this pull request can change situation
Localy all work
feature make by /speckit.specify libraries: hello world
this command make libraries in specs dir and place feature to this dir with own name and max feature ID across all specs

other commands like /speckit.plan feature index work well

andrewvlgafc

This comment was marked as abuse.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for organizing feature specifications into nested category subdirectories (e.g., specs/libraries/001-feature-name/) instead of only flat structure (specs/001-feature-name/). Users can now specify a category prefix when creating features (e.g., /speckit.specify libraries: Add firebird client), and the feature will be created under the appropriate subdirectory while maintaining global feature numbering across all categories.

Changes:

  • Added --specs-subdir parameter support to bash scripts for category-based spec organization
  • Updated template documentation to explain the category prefix syntax and parsing rules
  • Modified spec directory search logic to handle both flat and nested structures with depth-2 recursion

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
templates/commands/specify.md Added documentation for optional category: prefix syntax, parsing rules, and examples; removed PowerShell script reference; updated to reference only bash script with --specs-subdir parameter
scripts/bash/create-new-feature.sh Added --specs-subdir parameter parsing, path normalization logic, conditional feature directory creation under category subdirs, recursive spec search with maxdepth 2, SPECS_SUBDIR in JSON/text output, and environment variable export
scripts/bash/common.sh Updated get_current_branch and find_feature_dir_by_prefix functions to search nested directories using find with depth 2; added SPECIFY_SPECS_SUBDIR environment variable support for category-aware directory resolution
Comments suppressed due to low confidence (5)

scripts/bash/create-new-feature.sh:323

  • The SPECS_SUBDIR is included in the JSON output even when it's empty. While this isn't necessarily wrong, it means the JSON output will contain "SPECS_SUBDIR":"" for features without a category. Consider:
  1. Current behavior: Always include SPECS_SUBDIR in output (even if empty string)
  2. Alternative: Only include SPECS_SUBDIR in JSON when non-empty (would require conditional JSON building)

If keeping current approach, this is fine for consistency. However, document this behavior in the template at line 79 where JSON output is described, so agents know to expect this field even for non-categorized features.

    printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s","SPECS_SUBDIR":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM" "$SPECS_SUBDIR"

scripts/bash/create-new-feature.sh:320

  • The script exports SPECIFY_SPECS_SUBDIR environment variable, which is good for maintaining context. However, the variable is exported even when empty (line 320). This could cause issues in scripts that check for variable existence using [[ -n "${SPECIFY_SPECS_SUBDIR:-}" ]] vs [[ -n "$SPECIFY_SPECS_SUBDIR" ]].

The common.sh find_feature_dir_by_prefix function (line 103) uses [[ -n "${SPECIFY_SPECS_SUBDIR:-}" ]] which correctly handles both unset and empty string cases. This is good defensive programming. However, consider documenting this convention: either always export the variable (even as empty string) or never export when empty, to establish a clear contract for other scripts that might use this variable.

export SPECIFY_SPECS_SUBDIR="$SPECS_SUBDIR"

templates/commands/specify.md:29

  • The example shows "specs/libraries//..." but the actual directory structure created would be "specs/libraries/001-feature-name/spec.md" (not nested under NNN-name). The notation "" is ambiguous - it could mean:
  1. A placeholder for a directory named like "001-feature-name" (correct interpretation)
  2. Nested subdirectories like "specs/libraries/001/feature-name/" (incorrect)

Recommendation: Use "001-feature-name" or "{NNN-feature-name}" instead of "" to make it clearer that this is a single directory name with a numeric prefix, not a path with multiple levels.

Examples:
- `/speckit.specify Add user authentication` -> create in `specs/<NNN-name>/...` (default behavior)
- `/speckit.specify libraries: Add firebird client` -> create in `specs/libraries/<NNN-name>/...`

templates/commands/specify.md:73

  • The template now references only the Bash script path and removes the PowerShell script reference. However, the PowerShell script (scripts/powershell/create-new-feature.ps1) has not been updated with the --specs-subdir feature. This creates an incomplete implementation where:
  1. The template instructs agents to use .specify/scripts/bash/create-new-feature.sh exclusively
  2. PowerShell users who rely on the PowerShell version will not have access to the nested specs feature
  3. This breaks feature parity between Bash and PowerShell implementations

The PR should either:

  • Update the PowerShell script to include the same --specs-subdir functionality, OR
  • Document that this feature is Bash-only (though this would be inconsistent with the repository's cross-platform support pattern)
   c. Run the script `.specify/scripts/bash/create-new-feature.sh` and let it auto-detect the next available number globally (it checks git branches + specs directories, including nested specs):
      - Always pass:
        - `--json`
        - `--short-name "<short-name>"`
        - `"FEATURE_DESCRIPTION"`
      - If `SPECS_SUBDIR` is non-empty, also pass:
        - `--specs-subdir "<category>"`
      - Bash example (no category):
        `.specify/scripts/bash/create-new-feature.sh --json --short-name "user-auth" "Add user authentication"`
      - Bash example (with category):
        `.specify/scripts/bash/create-new-feature.sh --json --short-name "firebird-client" --specs-subdir "libraries" "Add firebird client"`

scripts/bash/create-new-feature.sh:200

  • The path normalization regex s#^/*##; s#/*$## removes leading and trailing slashes from SPECS_SUBDIR, which is good. However, the sed command doesn't validate or sanitize the middle of the path. Consider these edge cases:
  1. Multiple consecutive slashes in the middle: "libraries//core" would remain "libraries//core"
  2. Relative path components: "libraries/../admin" could escape the specs directory
  3. Absolute paths after normalization: if user provides "//etc/passwd", it becomes "etc/passwd" which might not be the intent

Recommendation: Add additional normalization to:

  • Collapse multiple consecutive slashes: s#/\+#/#g
  • Reject or sanitize paths containing ".." components for security
  • Perhaps validate that the resulting path only contains alphanumeric, hyphens, underscores, and single forward slashes
# Normalize specs subdir to avoid accidental paths like "libraries:" or trailing slashes
if [ -n "$SPECS_SUBDIR" ]; then
    SPECS_SUBDIR="$(echo "$SPECS_SUBDIR" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/:$//; s#^/*##; s#/*$##')"
fi

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@andrewvlgafc andrewvlgafc requested a review from mnriem as a code owner February 23, 2026 22:00
@andrewvlgafc
Copy link
Author

@copilot open a new pull request to apply changes based on the comments in this thread

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant