feat(scripts): add SPECIFY_INIT_DIR to target a member project from the repo root#2892
Open
PascalThuet wants to merge 6 commits into
Open
feat(scripts): add SPECIFY_INIT_DIR to target a member project from the repo root#2892PascalThuet wants to merge 6 commits into
PascalThuet wants to merge 6 commits into
Conversation
…utside its directory Adds a project-root override so a non-interactive / CI caller can run a Spec Kit command against a member project (e.g. apps/web in a monorepo) from outside that directory, without cd. SPECIFY_INIT_DIR names the project root — the directory containing .specify/ — and is honored by get_repo_root in scripts/bash/common.sh, mirrored in scripts/powershell/common.ps1. Strict by design, per maintainer guidance on github#2834: when set, the path must exist and contain .specify/, otherwise the resolver hard-errors and never falls back to the current directory or the git toplevel (which would silently write to the wrong project's specs/). Relative paths normalize against the current directory (with CDPATH="" to avoid CDPATH-based misresolution); trailing slashes are tolerated; an empty string is treated as unset. Resolution stays two independent axes: SPECIFY_INIT_DIR selects the project, while SPECIFY_FEATURE_DIRECTORY / .specify/feature.json / branch detection select the feature within it (project-then-feature). The bash get_repo_root call sites that feed real resolution are split into decl/assignment so the hard error propagates instead of being masked by `local`. Env var only; the --project CLI flag is deferred. Adds tests/test_init_dir.py (positive + negative, bash and PowerShell) and documents the variable and the two-axis precedence in docs/reference/core.md. Refs: github#2834 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends SPECIFY_INIT_DIR coverage to the bundled Git extension — the actual /speckit.specify before_specify entrypoint — so a monorepo/CI user setting it gets the feature branch created in, and auto-commit run against, the targeted project rather than the cwd/git toplevel. create-new-feature.sh/.ps1 and auto-commit.sh/.ps1 now resolve the project root from SPECIFY_INIT_DIR first, with the same strict contract as core (must exist + contain .specify/, hard error, no silent fallback). Adds bash tests for the git entrypoint. Also addresses the max-effort review of the diff: - common.sh: split the remaining `local x=$(get_repo_root)` / `local x=$(get_current_branch)` call sites (has_git, get_feature_paths) so a SPECIFY_INIT_DIR validation failure is never masked by `local`. - docs: note that the agent-context update selects the most recently modified plan within the resolved project (feature-axis selectors apply to the core feature scripts), and that SPECIFY_INIT_DIR is honored by core + git ext. - CHANGELOG: drop the hand-added [Unreleased] block; the release workflow generates entries from commit subjects, so manual entries would duplicate. Refs: github#2834 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the duplication the review flagged: the strict SPECIFY_INIT_DIR validation was inlined per-script across the git extension. Extract it into a named resolver — resolve_specify_init_dir (bash) / Resolve-SpecifyInitDir (PowerShell) — defined once per independently-shippable library: - core scripts/bash/common.sh + scripts/powershell/common.ps1 (get_repo_root now delegates to it) - extensions/git git-common.sh + git-common.ps1 (create-new-feature and auto-commit call it instead of carrying their own inline copy) The three libraries still each carry one copy because the git and agent-context extensions must run without the core scripts installed — a single cross-library source would couple them — but the per-script inline duplication within the git extension is gone, and each copy is now a named, greppable function that is trivial to diff and keep in sync. Behavior and error strings are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a SPECIFY_INIT_DIR environment variable to let Spec Kit Bash/PowerShell scripts explicitly target a specific member project (the directory containing .specify/) when invoked from outside that project (e.g., monorepo root or CI), with strict validation and no silent fallback behavior.
Changes:
- Added
SPECIFY_INIT_DIRresolvers to core Bash/PowerShell common scripts and threaded the resolved root through feature path/branch resolution. - Updated bundled extension scripts (git + agent-context) to honor
SPECIFY_INIT_DIR, including “legacy core scripts” fallback behavior in the git extension. - Added targeted tests and documented the new environment variable in core docs.
Show a summary per file
| File | Description |
|---|---|
tests/test_init_dir.py |
New end-to-end tests for SPECIFY_INIT_DIR resolution/validation across Bash, PowerShell, and extension entrypoints. |
tests/extensions/git/test_git_extension.py |
Adds compatibility tests ensuring the git extension can honor SPECIFY_INIT_DIR even with legacy core scripts. |
scripts/powershell/common.ps1 |
Introduces Resolve-SpecifyInitDir and makes Get-RepoRoot prefer SPECIFY_INIT_DIR. |
scripts/bash/create-new-feature.sh |
Ensures get_repo_root failures propagate (fail-fast) when resolving the repo root. |
scripts/bash/common.sh |
Adds resolve_specify_init_dir and ensures get_repo_root failures propagate through key call sites. |
extensions/git/scripts/powershell/git-common.ps1 |
Adds extension-local Resolve-SpecifyInitDir for legacy-core compatibility. |
extensions/git/scripts/powershell/create-new-feature.ps1 |
Uses SPECIFY_INIT_DIR as the highest-priority repo-root selector with legacy-core fallback. |
extensions/git/scripts/powershell/auto-commit.ps1 |
Honors SPECIFY_INIT_DIR for auto-commit repo root selection. |
extensions/git/scripts/bash/git-common.sh |
Adds extension-local resolve_specify_init_dir for legacy-core compatibility. |
extensions/git/scripts/bash/create-new-feature.sh |
Uses SPECIFY_INIT_DIR as highest-priority repo-root selector with legacy-core fallback. |
extensions/git/scripts/bash/auto-commit.sh |
Honors SPECIFY_INIT_DIR for auto-commit repo root selection. |
extensions/agent-context/scripts/powershell/update-agent-context.ps1 |
Resolves project root via SPECIFY_INIT_DIR when set. |
extensions/agent-context/scripts/bash/update-agent-context.sh |
Resolves project root via SPECIFY_INIT_DIR when set. |
docs/reference/core.md |
Documents SPECIFY_INIT_DIR behavior and its interaction with feature selection variables. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 14/14 changed files
- Comments generated: 3
Comment on lines
+44
to
+49
| $resolved = Resolve-Path -LiteralPath $initDir -ErrorAction SilentlyContinue | ||
| if (-not $resolved) { | ||
| [Console]::Error.WriteLine("ERROR: SPECIFY_INIT_DIR does not point to an existing directory: $($env:SPECIFY_INIT_DIR)") | ||
| exit 1 | ||
| } | ||
| $initRoot = $resolved.Path |
Comment on lines
+18
to
+23
| $resolved = Resolve-Path -LiteralPath $initDir -ErrorAction SilentlyContinue | ||
| if (-not $resolved) { | ||
| [Console]::Error.WriteLine("ERROR: SPECIFY_INIT_DIR does not point to an existing directory: $($env:SPECIFY_INIT_DIR)") | ||
| exit 1 | ||
| } | ||
| $initRoot = $resolved.Path |
Comment on lines
+61
to
+66
| $resolved = Resolve-Path -LiteralPath $initDir -ErrorAction SilentlyContinue | ||
| if (-not $resolved) { | ||
| [Console]::Error.WriteLine("ERROR: SPECIFY_INIT_DIR does not point to an existing directory: $($env:SPECIFY_INIT_DIR)") | ||
| exit 1 | ||
| } | ||
| $initRoot = $resolved.Path |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
SPECIFY_INIT_DIRas an explicit project-root override for Spec Kit scripts that may be invoked from outside the target project, such as a monorepo root or CI task.Behavior
SPECIFY_INIT_DIR=/path/to/projectmakes supported scripts operate on that Spec Kit project..specify/.cwdor the git toplevel.Changes
SPECIFY_INIT_DIR.git/agent-contextextension scripts.Validation
uv run pytest tests/test_init_dir.py tests/extensions/git/test_git_extension.py tests/test_extensions.py -quvx ruff check tests/extensions/git/test_git_extension.pybash -n extensions/git/scripts/bash/create-new-feature.sh extensions/git/scripts/bash/git-common.sh scripts/bash/common.shgit diff --checkpwshis not available in my local environment, so PowerShell tests are included but skipped locally.