Skip to content

feat(scripts): add SPECIFY_INIT_DIR to target a member project from the repo root#2892

Open
PascalThuet wants to merge 2 commits into
github:mainfrom
PascalThuet:feat/monorepo-init-dir
Open

feat(scripts): add SPECIFY_INIT_DIR to target a member project from the repo root#2892
PascalThuet wants to merge 2 commits into
github:mainfrom
PascalThuet:feat/monorepo-init-dir

Conversation

@PascalThuet

@PascalThuet PascalThuet commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds SPECIFY_INIT_DIR as 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/project makes supported core scripts operate on that Spec Kit project.
  • The value must resolve to an existing directory containing .specify/.
  • Invalid values fail fast. Scripts do not silently fall back to cwd or the git toplevel.
  • The git extension feature-branch entrypoint inherits the core resolver when available; if SPECIFY_INIT_DIR is set but only minimal or stale core helpers are available, it errors instead of guessing a root.

Changes

  • Adds Bash and PowerShell project-root resolvers for SPECIFY_INIT_DIR in the core common scripts.
  • Applies the override through core repo/feature path resolution and core feature creation.
  • Makes the bundled git extension feature-branch creation use the centralized core resolver without duplicating validation logic.
  • Documents the environment variable in the core reference.
  • Adds tests for valid targets, invalid targets, relative paths, monorepo-style invocation, file-path rejection, and missing/stale-core git extension fallback.

Validation

  • uv run pytest tests/test_init_dir.py tests/extensions/git/test_git_extension.py -q
  • uvx ruff check tests/extensions/git/test_git_extension.py
  • bash -n extensions/git/scripts/bash/create-new-feature-branch.sh scripts/bash/common.sh scripts/bash/create-new-feature.sh
  • git diff --check

pwsh is not available in my local environment, so PowerShell runtime tests are included but skipped locally.

@PascalThuet PascalThuet marked this pull request as ready for review June 7, 2026 20:55
@PascalThuet PascalThuet requested a review from mnriem as a code owner June 7, 2026 20:55
@mnriem mnriem requested a review from Copilot June 8, 2026 19:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_DIR resolvers 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 thread scripts/powershell/common.ps1
Comment thread extensions/git/scripts/powershell/git-common.ps1 Outdated
Comment thread extensions/agent-context/scripts/powershell/update-agent-context.ps1 Outdated
@mnriem

mnriem commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Architectural feedback

Thanks for the thorough work here — the strict contract (must exist + must contain .specify/) and test coverage are solid.

However, the resolver duplication across 3 script families (core, git-common, agent-context) diverges from how other env var overrides work in Spec Kit. Compare with SPECIFY_FEATURE_DIRECTORY:

  • Read once in core get_feature_paths() / Get-FeaturePathsEnv
  • Extensions that source core get it for free
  • No per-extension copies of the resolution logic

SPECIFY_INIT_DIR should follow the same pattern:

  1. Resolve once in get_repo_root() (core common.sh / common.ps1) — you already do this, and it works.
  2. Drop the duplicated resolvers from git-common.sh, git-common.ps1, and update-agent-context.{sh,ps1}. Extensions that source core already inherit get_repo_root(). Extensions that don't (agent-context) can read the project root from .specify/init-options.json or simply rely on the caller having cd'd — same as today.
  3. The "extensions can run without core installed" scenario is already handled by the existing _find_project_root walk-up-from-script-dir logic in those extensions. SPECIFY_INIT_DIR doesn't need a separate code path for that — if core isn't installed, the env var isn't being resolved by core either, so the contract is moot.

This eliminates ~60 lines of duplicated validation and the drift risk across 3 independent copies, while keeping the strict fail-fast behavior you designed.

@PascalThuet

PascalThuet commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@mnriem done — centralized on the core resolver and dropped the per-extension resolver copies.

  • git-common.{sh,ps1} and update-agent-context.{sh,ps1}: resolver copies removed.
  • Git feature-branch creation now inherits get_repo_root / Get-RepoRoot by sourcing core; there is no duplicate SPECIFY_INIT_DIR resolver in the extension.
  • auto-commit.{sh,ps1}: override dropped; the existing script-dir walk-up still resolves the installed member project.
  • agent-context: back to cwd-based behavior.

One gap this exposed: if SPECIFY_INIT_DIR is set but the git extension only has minimal or stale core helpers available, it could silently ignore the override and target the wrong root. Rather than re-add resolver duplication, the extension now guards that case: SPECIFY_INIT_DIR set + core resolver unavailable → hard error instead of guessing.

Updated on behalf of @PascalThuet by Codex (GPT-5).

@PascalThuet PascalThuet force-pushed the feat/monorepo-init-dir branch from e933b4a to 69efb21 Compare June 9, 2026 15:17
…he repo root

Resolve an explicit SPECIFY_INIT_DIR project override once in the core
get_repo_root / Get-RepoRoot, so a non-interactive / CI caller can target a
member project (the directory containing .specify/) from a monorepo root
without cd. Strict by design: the path must exist and contain .specify/,
otherwise it hard-errors with no silent fallback.

- Single resolver in core; the git feature-branch script inherits it by
  sourcing core, with no per-extension copies.
- PS resolver verifies the resolved path is a directory (Resolve-Path also
  succeeds for files) so a file value errors as "not an existing directory".
- get_feature_paths splits decl/assignment so a SPECIFY_INIT_DIR failure
  propagates instead of being masked by `local`.
- create-new-feature-branch: when core is absent (only git-common loaded) and
  SPECIFY_INIT_DIR is set, hard-error rather than silently using the git root.
- Document SPECIFY_INIT_DIR and SPECIFY_FEATURE_DIRECTORY in the core reference.
- Tests for valid/relative/trailing-slash/file/missing/no-.specify targets,
  feature-axis composition, the no-core guard, and a PowerShell mirror.
@PascalThuet PascalThuet force-pushed the feat/monorepo-init-dir branch from 69efb21 to facd309 Compare June 9, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please resolve conflicts

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.

3 participants