feat: Plan-aware nested repo branching for multi-module workspaces#2121
feat: Plan-aware nested repo branching for multi-module workspaces#2121sakitA wants to merge 4 commits intogithub:mainfrom
Conversation
… git repos
Add auto-detection of nested independent git repositories (not submodules)
and create matching feature branches in each when running create-new-feature.
Changes:
- common.sh: Add find_nested_git_repos() - discovers nested repos up to 2 levels deep
- common.ps1: Add Find-NestedGitRepos - PowerShell equivalent
- create-new-feature.sh: Create feature branches in all nested repos after root
- create-new-feature.ps1: Same for PowerShell
- specify.md: Document NESTED_REPOS JSON output field
- test_nested_repos.py: 9 tests covering discovery and branch creation
Design decisions:
- Auto-detect via .git presence (no configuration required)
- Non-blocking: nested repo failures are warnings, not errors
- JSON output extended with NESTED_REPOS array [{path, status}]
- Backward-compatible: no change for single-repo workflows
Resolves github#2120
Related to github#1050
Note: This contribution was made with AI assistance (GitHub Copilot).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds coordinated feature-branch creation for workspaces that contain nested, independent git repositories (not formal submodules), so create-new-feature can create/switch the same feature branch across the root repo and detected nested repos and report results in JSON.
Changes:
- Add nested repo discovery helpers (
find_nested_git_repos/Find-NestedGitRepos) scanning up to 2 levels deep with a skip list. - Create/switch matching branches in detected nested repos from both Bash and PowerShell
create-new-featurescripts, and emitNESTED_REPOSin JSON output. - Document
NESTED_REPOSand add pytest coverage for Bash discovery/branching behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
scripts/bash/common.sh |
Adds nested git repo discovery helper for Bash. |
scripts/bash/create-new-feature.sh |
Creates matching branches in discovered nested repos and includes NESTED_REPOS in JSON output. |
scripts/powershell/common.ps1 |
Adds nested git repo discovery helper for PowerShell. |
scripts/powershell/create-new-feature.ps1 |
Creates matching branches in discovered nested repos and includes NESTED_REPOS in JSON output. |
templates/commands/specify.md |
Documents the new NESTED_REPOS JSON field and behavior. |
tests/test_nested_repos.py |
Adds tests for nested repo discovery + Bash branching + JSON output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
scripts/bash/common.sh
Outdated
| _should_skip() { | ||
| local name="$1" | ||
| for skip in "${skip_dirs[@]}"; do | ||
| [ "$name" = "$skip" ] && return 0 | ||
| done | ||
| return 1 | ||
| } |
There was a problem hiding this comment.
In find_nested_git_repos, the helper _should_skip() is defined inside the function but becomes a global function in bash (functions aren't scoped). Because it relies on dynamically-scoped skip_dirs, calling it outside this function (or redefining it elsewhere) can lead to hard-to-debug behavior. Consider replacing this with a case/[[ ... ]] check inline, or use a uniquely-named helper and unset -f it before returning.
scripts/bash/common.sh
Outdated
| else | ||
| # Level 2 | ||
| for grandchild in "$child"/*/; do | ||
| [ -d "$grandchild" ] || continue | ||
| grandchild="${grandchild%/}" | ||
| local gc_name | ||
| gc_name="$(basename "$grandchild")" | ||
| _should_skip "$gc_name" && continue | ||
|
|
||
| if [ -e "$grandchild/.git" ]; then | ||
| if git -C "$grandchild" rev-parse --is-inside-work-tree >/dev/null 2>&1; then | ||
| echo "$grandchild" | ||
| fi | ||
| fi | ||
| done | ||
| fi |
There was a problem hiding this comment.
find_nested_git_repos only scans “level 2” when the level-1 directory does not have a .git marker. That means a repo at $repo_root/child/.git will prevent discovery of another repo at $repo_root/child/grandchild/.git, even though it's still within 2 levels of the root. If the intent is truly “scan up to 2 levels deep”, the level-2 traversal should run regardless (while still applying the skip list).
| else | |
| # Level 2 | |
| for grandchild in "$child"/*/; do | |
| [ -d "$grandchild" ] || continue | |
| grandchild="${grandchild%/}" | |
| local gc_name | |
| gc_name="$(basename "$grandchild")" | |
| _should_skip "$gc_name" && continue | |
| if [ -e "$grandchild/.git" ]; then | |
| if git -C "$grandchild" rev-parse --is-inside-work-tree >/dev/null 2>&1; then | |
| echo "$grandchild" | |
| fi | |
| fi | |
| done | |
| fi | |
| fi | |
| # Level 2 | |
| for grandchild in "$child"/*/; do | |
| [ -d "$grandchild" ] || continue | |
| grandchild="${grandchild%/}" | |
| local gc_name | |
| gc_name="$(basename "$grandchild")" | |
| _should_skip "$gc_name" && continue | |
| if [ -e "$grandchild/.git" ]; then | |
| if git -C "$grandchild" rev-parse --is-inside-work-tree >/dev/null 2>&1; then | |
| echo "$grandchild" | |
| fi | |
| fi | |
| done |
scripts/bash/create-new-feature.sh
Outdated
| while IFS= read -r nested_path; do | ||
| [ -z "$nested_path" ] && continue | ||
| # Normalize: remove trailing slash | ||
| nested_path="${nested_path%/}" | ||
| # Compute relative path for output | ||
| rel_path="${nested_path#"$REPO_ROOT/"}" |
There was a problem hiding this comment.
rel_path="${nested_path#"$REPO_ROOT/"}" uses bash pattern matching in parameter expansion, so repo roots containing glob characters (e.g. [ or *) can produce incorrect stripping and leak absolute paths into NESTED_REPOS.path. A safer approach is to compute the relative path by string length (only after verifying the prefix), or delegate to a tool like python/realpath for literal path handling.
| while IFS= read -r nested_path; do | |
| [ -z "$nested_path" ] && continue | |
| # Normalize: remove trailing slash | |
| nested_path="${nested_path%/}" | |
| # Compute relative path for output | |
| rel_path="${nested_path#"$REPO_ROOT/"}" | |
| repo_root_prefix="${REPO_ROOT%/}/" | |
| while IFS= read -r nested_path; do | |
| [ -z "$nested_path" ] && continue | |
| # Normalize: remove trailing slash | |
| nested_path="${nested_path%/}" | |
| # Compute relative path for output using literal prefix handling | |
| if [ "${nested_path:0:${#repo_root_prefix}}" = "$repo_root_prefix" ]; then | |
| rel_path="${nested_path:${#repo_root_prefix}}" | |
| else | |
| rel_path="$nested_path" | |
| fi |
scripts/powershell/common.ps1
Outdated
| } else { | ||
| # Level 2 | ||
| $grandchildren = Get-ChildItem -Path $child.FullName -Directory -ErrorAction SilentlyContinue | | ||
| Where-Object { $skipDirs -notcontains $_.Name } | ||
|
|
||
| foreach ($gc in $grandchildren) { | ||
| $gcGitMarker = Join-Path $gc.FullName '.git' | ||
| if (Test-Path -LiteralPath $gcGitMarker) { | ||
| try { | ||
| $null = git -C $gc.FullName rev-parse --is-inside-work-tree 2>$null | ||
| if ($LASTEXITCODE -eq 0) { | ||
| $results += $gc.FullName | ||
| } | ||
| } catch { } | ||
| } |
There was a problem hiding this comment.
Find-NestedGitRepos only descends to the second directory level when the level-1 directory does not contain a .git marker. This skips repos that are still within 2 levels of $RepoRoot but happen to be nested under a level-1 repo. If the intended behavior is “scan up to 2 levels deep”, consider always scanning grandchildren (while still excluding .git / other skip dirs).
| } else { | |
| # Level 2 | |
| $grandchildren = Get-ChildItem -Path $child.FullName -Directory -ErrorAction SilentlyContinue | | |
| Where-Object { $skipDirs -notcontains $_.Name } | |
| foreach ($gc in $grandchildren) { | |
| $gcGitMarker = Join-Path $gc.FullName '.git' | |
| if (Test-Path -LiteralPath $gcGitMarker) { | |
| try { | |
| $null = git -C $gc.FullName rev-parse --is-inside-work-tree 2>$null | |
| if ($LASTEXITCODE -eq 0) { | |
| $results += $gc.FullName | |
| } | |
| } catch { } | |
| } | |
| } | |
| # Level 2 | |
| $grandchildren = Get-ChildItem -Path $child.FullName -Directory -ErrorAction SilentlyContinue | | |
| Where-Object { $skipDirs -notcontains $_.Name } | |
| foreach ($gc in $grandchildren) { | |
| $gcGitMarker = Join-Path $gc.FullName '.git' | |
| if (Test-Path -LiteralPath $gcGitMarker) { | |
| try { | |
| $null = git -C $gc.FullName rev-parse --is-inside-work-tree 2>$null | |
| if ($LASTEXITCODE -eq 0) { | |
| $results += $gc.FullName | |
| } | |
| } catch { } |
| if ($hasGit) { | ||
| $nestedRepos = Find-NestedGitRepos -RepoRoot $repoRoot | ||
| foreach ($nestedPath in $nestedRepos) { | ||
| $relPath = $nestedPath.Substring($repoRoot.Length).TrimStart('\', '/') |
There was a problem hiding this comment.
$relPath = $nestedPath.Substring($repoRoot.Length).TrimStart(...) assumes $nestedPath always starts with the exact $repoRoot string (same casing, no trailing separator differences). Using [System.IO.Path]::GetRelativePath($repoRoot, $nestedPath) (or Resolve-Path normalization + a safer relative-path helper) would avoid subtle path mismatches and make output paths more reliable across platforms/filesystems.
| $relPath = $nestedPath.Substring($repoRoot.Length).TrimStart('\', '/') | |
| $relPath = [System.IO.Path]::GetRelativePath($repoRoot, $nestedPath) |
tests/test_nested_repos.py
Outdated
| Pytest tests for nested independent git repository support in create-new-feature scripts. | ||
|
|
||
| Tests cover: | ||
| - Discovery of nested git repos via find_nested_git_repos (bash) / Find-NestedGitRepos (PS) | ||
| - Branch creation in nested repos during feature creation | ||
| - JSON output includes NESTED_REPOS field | ||
| - --dry-run reports nested repos without creating branches | ||
| - --allow-existing-branch propagates to nested repos | ||
| - Excluded directories are skipped | ||
| - Graceful handling when nested repos cannot be branched |
There was a problem hiding this comment.
This test module’s header says it covers PowerShell discovery (Find-NestedGitRepos) and “graceful handling when nested repos cannot be branched”, but there are currently no tests exercising the PowerShell scripts or a nested-branch failure scenario (e.g., dirty worktree causing checkout to fail). Either add those cases (similar to tests/test_timestamp_branches.py’s conditional pwsh runner), or adjust the docstring/scope so it matches what’s actually being validated.
| Pytest tests for nested independent git repository support in create-new-feature scripts. | |
| Tests cover: | |
| - Discovery of nested git repos via find_nested_git_repos (bash) / Find-NestedGitRepos (PS) | |
| - Branch creation in nested repos during feature creation | |
| - JSON output includes NESTED_REPOS field | |
| - --dry-run reports nested repos without creating branches | |
| - --allow-existing-branch propagates to nested repos | |
| - Excluded directories are skipped | |
| - Graceful handling when nested repos cannot be branched | |
| Pytest tests for nested independent git repository support in the Bash | |
| create-new-feature script. | |
| Tests cover: | |
| - Discovery of nested git repos via find_nested_git_repos (bash) | |
| - Branch creation in nested repos during feature creation | |
| - JSON output includes NESTED_REPOS field | |
| - --dry-run reports nested repos without creating branches | |
| - --allow-existing-branch propagates to nested repos | |
| - Excluded directories are skipped |
Enhance nested repo support with two key improvements: 1. Configurable scan depth: - find_nested_git_repos() / Find-NestedGitRepos now accept a depth parameter - Uses recursive traversal instead of hardcoded 2-level nesting - New --scan-depth (Bash) / -ScanDepth (PowerShell) flag - Reads nested_repo_scan_depth from init-options.json 2. Selective branching via --repos flag: - New --repos (Bash) / -Repos (PowerShell) flag accepts comma-separated paths - Only branches repos matching the specified paths - Omit flag to branch all discovered repos (backward compatible) - Enables spec-driven branching: AI agents select relevant repos per feature Tests: 17 tests (8 new for depth + filtering), all passing. Docs: Updated specify.md with --repos, --scan-depth, and init-options.json config. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of automatically creating branches in all nested repos during /speckit.specify, this redesign defers branching to the plantasksimplement workflow: - Specify phase: creates root branch + spec only (no nested repo work) - Plan phase: setup-plan.sh discovers nested repos (NESTED_REPOS in JSON) and the AI agent identifies affected modules from the spec - Tasks phase: generates a Phase 1 setup task for creating feature branches in only the affected nested repos identified by the plan - Implement phase: executes the branch-creation task via git commands Changes: - Revert create-new-feature.sh/.ps1: remove --repos, --scan-depth, and NESTED_REPOS output (specify phase is clean again) - Add nested repo discovery to setup-plan.sh/.ps1 with --scan-depth flag - Update plan.md template: add step for AI to identify affected repos - Update plan-template.md: add Affected Nested Repositories section - Update tasks.md command: guidance for generating branch-creation task - Update tasks-template.md: Phase 1 example for nested repo branching - Update specify.md: remove nested repo guidance, point to plan+tasks - Rewrite tests: 13 tests covering discovery, depth config, setup-plan output, and verification that specify phase doesn't branch nested repos Resolves the concern that at specify time the spec doesn't exist yet, so we cannot know which repos are affected. The AI agent now makes this determination during planning based on spec analysis. Refs: github#2120 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
templates/commands/plan.md:88
- Outline numbering is inconsistent: there are two items numbered
5.(Stop and report, then Check for extension hooks). This can confuse agents/instructions parsing; please renumber the second one to6.(and ensure subsequent numbering remains consistent).
- Phase 1: Generate data-model.md, contracts/, quickstart.md
- Phase 1: Update agent context by running the agent script
- Re-evaluate Constitution Check post-design
5. **Stop and report**: Command ends after Phase 2 planning. Report branch, IMPL_PLAN path, generated artifacts, and affected nested repos (if any).
5. **Check for extension hooks**: After reporting, check if `.specify/extensions.yml` exists in the project root.
- If it exists, read it and look for entries under the `hooks.after_plan` key
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - You must only ever run this script once per feature | ||
| - The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for | ||
| - The JSON output will contain BRANCH_NAME and SPEC_FILE paths | ||
| - For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot") | ||
| - **Note**: Nested git repository branching is handled during the `/speckit.plan` and `/speckit.tasks` phases, not here. The plan identifies affected repos and tasks generates a setup task for branch creation. | ||
|
|
There was a problem hiding this comment.
The PR description/title says create-new-feature now creates matching branches in nested repos and reports NESTED_REPOS, but this note explicitly states nested repo branching is not handled in specify (create-new-feature) and is deferred to plan/tasks. Please reconcile the implementation + docs/tests with the PR description (either implement branching in create-new-feature + add NESTED_REPOS there, or update the PR description/title to match the new workflow).
| class TestCreateFeatureNoNestedBranching: | ||
| def test_no_nested_repos_in_json(self, git_repo_with_nested: Path): | ||
| """create-new-feature JSON output should NOT contain NESTED_REPOS.""" | ||
| result = run_create_feature( | ||
| git_repo_with_nested, | ||
| "--json", "--short-name", "my-feat", "Add a feature", | ||
| ) | ||
| assert result.returncode == 0, result.stderr | ||
| data = json.loads(result.stdout.strip()) | ||
| assert "NESTED_REPOS" not in data | ||
| assert "BRANCH_NAME" in data |
There was a problem hiding this comment.
These tests assert that create-new-feature.sh does not branch nested repos and that its JSON output omits NESTED_REPOS, which directly contradicts the PR description stating create-new-feature now coordinates branching across nested repos and reports NESTED_REPOS. Either the feature implementation is missing from create-new-feature, or the PR description/tests need to be updated to match the intended behavior.
scripts/bash/common.sh
Outdated
| _should_skip() { | ||
| local name="$1" | ||
| for skip in "${skip_dirs[@]}"; do | ||
| [ "$name" = "$skip" ] && return 0 | ||
| done | ||
| return 1 | ||
| } | ||
|
|
||
| _scan_dir() { | ||
| local dir="$1" | ||
| local current_depth="$2" | ||
| for child in "$dir"/*/; do | ||
| [ -d "$child" ] || continue | ||
| child="${child%/}" | ||
| local child_name | ||
| child_name="$(basename "$child")" | ||
| _should_skip "$child_name" && continue | ||
|
|
||
| if [ -e "$child/.git" ]; then | ||
| if git -C "$child" rev-parse --is-inside-work-tree >/dev/null 2>&1; then | ||
| echo "$child" | ||
| fi | ||
| elif [ "$current_depth" -lt "$max_depth" ]; then | ||
| _scan_dir "$child" $((current_depth + 1)) | ||
| fi | ||
| done | ||
| } | ||
|
|
||
| _scan_dir "$repo_root" 1 |
There was a problem hiding this comment.
find_nested_git_repos defines helper functions (_should_skip, _scan_dir) inside the function body. In bash this still creates global function names, which can unintentionally pollute/override other helpers in common.sh and makes the namespace harder to maintain. Consider inlining this logic, using more uniquely prefixed helper names (e.g. __find_nested_git_repos_*), or running the scan in a subshell to avoid exporting generic helper names globally.
| _should_skip() { | |
| local name="$1" | |
| for skip in "${skip_dirs[@]}"; do | |
| [ "$name" = "$skip" ] && return 0 | |
| done | |
| return 1 | |
| } | |
| _scan_dir() { | |
| local dir="$1" | |
| local current_depth="$2" | |
| for child in "$dir"/*/; do | |
| [ -d "$child" ] || continue | |
| child="${child%/}" | |
| local child_name | |
| child_name="$(basename "$child")" | |
| _should_skip "$child_name" && continue | |
| if [ -e "$child/.git" ]; then | |
| if git -C "$child" rev-parse --is-inside-work-tree >/dev/null 2>&1; then | |
| echo "$child" | |
| fi | |
| elif [ "$current_depth" -lt "$max_depth" ]; then | |
| _scan_dir "$child" $((current_depth + 1)) | |
| fi | |
| done | |
| } | |
| _scan_dir "$repo_root" 1 | |
| ( | |
| _should_skip() { | |
| local name="$1" | |
| local skip | |
| for skip in "${skip_dirs[@]}"; do | |
| [ "$name" = "$skip" ] && return 0 | |
| done | |
| return 1 | |
| } | |
| _scan_dir() { | |
| local dir="$1" | |
| local current_depth="$2" | |
| local child | |
| local child_name | |
| for child in "$dir"/*/; do | |
| [ -d "$child" ] || continue | |
| child="${child%/}" | |
| child_name="$(basename "$child")" | |
| _should_skip "$child_name" && continue | |
| if [ -e "$child/.git" ]; then | |
| if git -C "$child" rev-parse --is-inside-work-tree >/dev/null 2>&1; then | |
| echo "$child" | |
| fi | |
| elif [ "$current_depth" -lt "$max_depth" ]; then | |
| _scan_dir "$child" $((current_depth + 1)) | |
| fi | |
| done | |
| } | |
| _scan_dir "$repo_root" 1 | |
| ) |
scripts/bash/setup-plan.sh
Outdated
| # Reset sentinel if --scan-depth was passed without a value | ||
| [ "$SCAN_DEPTH" = "__NEXT__" ] && SCAN_DEPTH="" | ||
|
|
There was a problem hiding this comment.
--scan-depth is accepted as an arbitrary string and is passed through to find_nested_git_repos, which later uses -lt numeric comparisons. If the value is missing or non-numeric (e.g. --scan-depth --json), this can produce integer expression expected errors and unexpected scan behavior. Consider validating that SCAN_DEPTH is a positive integer when parsing args and exiting with a clear error if it isn't.
| # Reset sentinel if --scan-depth was passed without a value | |
| [ "$SCAN_DEPTH" = "__NEXT__" ] && SCAN_DEPTH="" | |
| # Validate --scan-depth argument if provided | |
| if [ "$SCAN_DEPTH" = "__NEXT__" ]; then | |
| echo "ERROR: --scan-depth requires a positive integer value" >&2 | |
| exit 1 | |
| fi | |
| if [ -n "$SCAN_DEPTH" ]; then | |
| case "$SCAN_DEPTH" in | |
| ''|*[!0-9]*|0) | |
| echo "ERROR: --scan-depth must be a positive integer" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| fi |
templates/commands/plan.md
Outdated
| **Nested repo scan depth**: Check `.specify/init-options.json` for `nested_repo_scan_depth`. If present, pass `--scan-depth N` (Bash) or `-ScanDepth N` (PowerShell) to the script. | ||
|
|
There was a problem hiding this comment.
This template instructs the agent to read .specify/init-options.json for nested_repo_scan_depth, but that key doesn't appear to be written/used anywhere else in the repo. Either add support for persisting this option (and document it), or remove/adjust this instruction so agents don’t look for a config value that will never exist.
| **Nested repo scan depth**: Check `.specify/init-options.json` for `nested_repo_scan_depth`. If present, pass `--scan-depth N` (Bash) or `-ScanDepth N` (PowerShell) to the script. |
templates/commands/tasks.md
Outdated
| 3. **Execute task generation workflow**: | ||
| - Load plan.md and extract tech stack, libraries, project structure | ||
| - Load spec.md and extract user stories with their priorities (P1, P2, P3, etc.) | ||
| - If plan.md contains an "Affected Nested Repositories" section: extract the repo paths and reasons. Generate a setup task (in Phase 1) to create the feature branch in each affected nested repo using `git -C <repo_path> checkout -b <BRANCH_NAME>`. The branch name comes from the current feature branch. |
There was a problem hiding this comment.
The generated setup task command should quote <repo_path> (and ideally <BRANCH_NAME>) to handle paths with spaces and avoid shell parsing issues. Consider specifying git -C "<repo_path>" checkout -b "<BRANCH_NAME>" (or equivalent) in this instruction text.
| - If plan.md contains an "Affected Nested Repositories" section: extract the repo paths and reasons. Generate a setup task (in Phase 1) to create the feature branch in each affected nested repo using `git -C <repo_path> checkout -b <BRANCH_NAME>`. The branch name comes from the current feature branch. | |
| - If plan.md contains an "Affected Nested Repositories" section: extract the repo paths and reasons. Generate a setup task (in Phase 1) to create the feature branch in each affected nested repo using `git -C "<repo_path>" checkout -b "<BRANCH_NAME>"`. The branch name comes from the current feature branch. |
| # Discover nested independent git repositories under RepoRoot. | ||
| # Searches up to 2 directory levels deep for subdirectories containing .git | ||
| # (directory or file, covering worktrees/submodules). Excludes the root repo | ||
| # itself and common non-project directories. | ||
| # Returns an array of absolute paths. | ||
| function Find-NestedGitRepos { | ||
| param( | ||
| [string]$RepoRoot = (Get-RepoRoot), | ||
| [int]$MaxDepth = 2 |
There was a problem hiding this comment.
The comment above Find-NestedGitRepos says it "Searches up to 2 directory levels deep", but the implementation accepts a configurable -MaxDepth parameter. Please update the comment to reflect that scan depth is configurable (default 2) so the documentation matches behavior.
- Wrap find_nested_git_repos helpers in subshell to prevent bash global namespace pollution (_should_skip, _scan_dir) - Validate --scan-depth is a positive integer in setup-plan.sh - Remove non-existent nested_repo_scan_depth config reference from plan.md template - Fix duplicate step numbering (two items numbered 5) in plan.md - Quote repo paths in tasks.md git command template - Update Find-NestedGitRepos comment to reflect configurable depth Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Please deliver this as a community preset. See https://github.com/github/spec-kit/tree/main/presets |
Summary
Adds support for nested independent git repositories in multi-module workspaces. Sub-components that live under the root workspace with their own .git but are not formal git submodules.
Instead of automatically branching all nested repos during feature creation, this PR implements a plan-aware, task-driven workflow where the AI agent identifies affected repos from the spec and generates targeted branch-creation tasks.
Resolves #2120
Problem
In multi-module projects where sub-components maintain independent git histories (nested under a shared root), spec-kit only operates on the root repo. Developers must manually create matching feature branches in each affected sub-component.
Workflow
Design Decisions
Changes
Testing
AI Disclosure
This contribution was made with AI assistance (GitHub Copilot), as required by CONTRIBUTING.md.