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 8 commits intogithub:mainfrom
Conversation
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.
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.
|
Please deliver this as a community preset. See https://github.com/github/spec-kit/tree/main/presets |
… 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>
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>
c87e0bb to
914606d
Compare
- 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>
914606d to
749e95b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
@mnriem does presets support |
Replace hardcoded skip list with two-tier discovery: 1. Explicit paths: If nested_repos is defined in init-options.json, validate and return those paths directly (no scanning). 2. .gitignore-based scan: If no explicit config, scan directories but use git check-ignore to skip gitignored dirs instead of a hardcoded skip list. Only .git is hardcoded. Dirs with their own .git are always treated as nested repos (even if gitignored in parent). - Bash: find_nested_git_repos() accepts optional explicit paths (3rd+ args) - PowerShell: Find-NestedGitRepos accepts -ExplicitPaths param - setup-plan.sh/.ps1: reads nested_repos from init-options.json - Python fallback for JSON parsing when jq is unavailable - Windows \r line-ending handling in Python subprocess output - 18 tests: discovery, gitignore filtering, explicit paths, depth, init-options Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- plan.md: clarify JSON parsing instructions (extract first { line)
- common.sh: validate max_depth is a positive integer
- common.ps1: add ValidateRange(1, MaxValue) on -MaxDepth
- setup-plan.ps1: add ValidateRange(0, MaxValue) on -ScanDepth
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ored parents - Update function header comments in common.sh and common.ps1 to accurately describe gitignore-based filtering (no hardcoded skip list) - Document that scanning will NOT descend into gitignored parent directories; use init-options.json nested_repos for those - Implement nested_repo_scan_depth from init-options.json in both setup-plan.sh and setup-plan.ps1 (priority: CLI > config > default 2) - Fix PowerShell -ScanDepth param: default 2, ValidateRange(1, MaxValue), use PSBoundParameters to detect explicit CLI usage - Add returncode assertion before branch-name comparison in tests - Add test: repo under gitignored parent not discovered by scan - Add test: nested_repo_scan_depth from init-options.json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1e8d148 to
80bcbb4
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix stale comment in common.sh (remove 'common non-project directories') - Validate nested_repo_scan_depth from init-options.json (positive integer) - Wrap find_nested_git_repos calls with || fallback in bash (set -e safe) - Wrap Find-NestedGitRepos calls in try/catch in PowerShell - Discovery failures now emit warnings and fall back to empty list Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Closing this PR the functionality has been reimplemented as a community preset per @mnriem's feedback. Preset repository: https://github.com/sakitA/spec-kit-preset-nested-repos The preset approach is better because:
Will submit a catalog PR to |
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
setup-plan.sh/.ps1discovers nested repos (NESTED_REPOSin JSON). AI identifies affected repos from specWhy Core, Not an Extension or Preset
We evaluated all three extensibility mechanisms before choosing a core contribution:
NESTED_REPOSinsetup-plan.shJSON so the AI agent receives discovery data alongsideFEATURE_SPECandBRANCH. Extensions can only add new commands (speckit.{ext-id}.{cmd}), not inject fields into existing script output. A separate discovery command would force a fragile two-script workflow.plan.mdwould require maintaining a full copy of the command that drifts from upstream on every core update.setup-plan.shJSON output,common.shdiscovery function). The template changes are minimal additions (a step, a section), not full replacements. Zero impact on single-repo projects.Design Decisions
init-options.jsontake priority, otherwise scan using.gitignore-based filtering (no hardcoded skip list)--scan-depth N/-ScanDepth Ncontrols how deep to scan (default: 2). Also configurable vianested_repo_scan_depthininit-options.json(priority: CLI > config > default 2)vendor/foo/.gitwhenvendor/is ignored) requires explicitnested_reposconfigDiscovery Modes
1. Explicit paths (
init-options.json)json { "nested_repos": ["components/core", "components/api"], "nested_repo_scan_depth": 3 }When
nested_reposis defined, paths are validated directly no filesystem scanning.nested_repo_scan_depthsets the default scan depth (overridden by CLI--scan-depth).2. .gitignore-based scan (fallback)
When no explicit paths are configured, the discovery function scans subdirectories up to the configured depth. Instead of a hardcoded skip list, it uses
git check-ignoreto skip gitignored directories. Only.gitis hardcoded. Directories with their own.gitare always treated as nested repos, even if gitignored in the parent.Changes
scripts/bash/common.shfind_nested_git_repos()hybrid discovery (explicit paths or .gitignore-based scan), configurable depth, subshell isolationscripts/bash/setup-plan.sh--scan-depthflag, readsnested_reposandnested_repo_scan_depthfrominit-options.json(jq or Python fallback),NESTED_REPOSin JSON outputscripts/powershell/common.ps1Find-NestedGitReposwith-MaxDepthand-ExplicitPathsparams, .gitignore-based filteringscripts/powershell/setup-plan.ps1-ScanDepthparam (default 2, ValidateRange 1+), readsnested_reposandnested_repo_scan_depthfrominit-options.json,NESTED_REPOSin JSON outputtemplates/commands/plan.mdtemplates/plan-template.mdtemplates/commands/tasks.mdtemplates/tasks-template.mdtests/test_nested_repos.pyTesting
tests/test_nested_repos.py, all passingnested_repos+nested_repo_scan_depth), verification that create-new-feature does NOT branch nested reposAI Disclosure
This contribution was made with AI assistance (GitHub Copilot), as required by CONTRIBUTING.md.