Skip to content
40 changes: 33 additions & 7 deletions scripts/bash/common.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
#!/usr/bin/env bash
# Common functions and variables for all scripts

# Get repository root, with fallback for non-git repositories
# Find repository root by searching upward for .specify directory
# This is the primary marker for spec-kit projects
find_specify_root() {
local dir="${1:-$(pwd)}"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.specify" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
return 1
Comment thread
mbachorik marked this conversation as resolved.
}

# Get repository root, prioritizing .specify directory over git
# This prevents using a parent git repo when spec-kit is initialized in a subdirectory
get_repo_root() {
# First, look for .specify directory (spec-kit's own marker)
local specify_root
if specify_root=$(find_specify_root); then
echo "$specify_root"
return
fi

# Fallback to git if no .specify found
if git rev-parse --show-toplevel >/dev/null 2>&1; then
git rev-parse --show-toplevel
else
# Fall back to script location for non-git repos
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
(cd "$script_dir/../../.." && pwd)
return
fi

# Final fallback to script location for non-git repos
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
(cd "$script_dir/../../.." && pwd)
}

# Get current branch, with fallback for non-git repositories
Expand Down Expand Up @@ -57,9 +81,11 @@ get_current_branch() {
echo "main" # Final fallback
}

# Check if we have git available
# Check if we have git available at the spec-kit root level
# Returns true only if the .specify root has its own .git directory
has_git() {
git rev-parse --show-toplevel >/dev/null 2>&1
local repo_root=$(get_repo_root)
[ -d "$repo_root/.git" ]
}
Comment thread
mbachorik marked this conversation as resolved.
Comment thread
mbachorik marked this conversation as resolved.

check_feature_branch() {
Expand Down
17 changes: 10 additions & 7 deletions scripts/bash/create-new-feature.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,24 @@ clean_branch_name() {
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
}

# Resolve repository root. Prefer git information when available, but fall back
# to searching for repository markers so the workflow still functions in repositories that
# were initialised with --no-git.
# Resolve repository root using common.sh functions which prioritize .specify over git
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"

if git rev-parse --show-toplevel >/dev/null 2>&1; then
REPO_ROOT=$(git rev-parse --show-toplevel)
HAS_GIT=true
else
REPO_ROOT=$(get_repo_root)
Comment thread
mbachorik marked this conversation as resolved.
if [ -z "$REPO_ROOT" ]; then
# Fallback to local find_repo_root if common.sh couldn't find it
REPO_ROOT="$(find_repo_root "$SCRIPT_DIR")"
if [ -z "$REPO_ROOT" ]; then
echo "Error: Could not determine repository root. Please run this script from within the repository." >&2
exit 1
fi
fi
Comment thread
mbachorik marked this conversation as resolved.
Outdated

# Check if git is available at this repo root (not a parent)
if has_git; then
HAS_GIT=true
else
HAS_GIT=false
fi

Expand Down
41 changes: 33 additions & 8 deletions scripts/powershell/common.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
#!/usr/bin/env pwsh
# Common PowerShell functions analogous to common.sh

# Find repository root by searching upward for .specify directory
# This is the primary marker for spec-kit projects
function Find-SpecifyRoot {
param([string]$StartDir = (Get-Location).Path)

$current = $StartDir
while ($true) {
if (Test-Path (Join-Path $current ".specify")) {
Comment thread
mbachorik marked this conversation as resolved.
Outdated
return $current
}
$parent = Split-Path $current -Parent
if ([string]::IsNullOrEmpty($parent) -or $parent -eq $current) {
return $null
}
$current = $parent
}
}

# Get repository root, prioritizing .specify directory over git
# This prevents using a parent git repo when spec-kit is initialized in a subdirectory
function Get-RepoRoot {
# First, look for .specify directory (spec-kit's own marker)
$specifyRoot = Find-SpecifyRoot
if ($specifyRoot) {
return $specifyRoot
}

# Fallback to git if no .specify found
try {
$result = git rev-parse --show-toplevel 2>$null
if ($LASTEXITCODE -eq 0) {
Expand All @@ -10,8 +37,8 @@ function Get-RepoRoot {
} catch {
# Git command failed
}
# Fall back to script location for non-git repos

# Final fallback to script location for non-git repos
return (Resolve-Path (Join-Path $PSScriptRoot "../../..")).Path
Comment thread
mbachorik marked this conversation as resolved.
Outdated
}

Expand Down Expand Up @@ -58,13 +85,11 @@ function Get-CurrentBranch {
return "main"
}

# Check if we have git available at the spec-kit root level
# Returns true only if the .specify root has its own .git directory
function Test-HasGit {
try {
git rev-parse --show-toplevel 2>$null | Out-Null
return ($LASTEXITCODE -eq 0)
} catch {
return $false
}
$repoRoot = Get-RepoRoot
return (Test-Path (Join-Path $repoRoot ".git"))
}
Comment thread
mbachorik marked this conversation as resolved.

function Test-FeatureBranch {
Expand Down
28 changes: 12 additions & 16 deletions scripts/powershell/create-new-feature.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,23 @@ function ConvertTo-CleanBranchName {

return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
}
$fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
if (-not $fallbackRoot) {
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
exit 1
}

# Load common functions (includes Resolve-Template)
# Load common functions (includes Get-RepoRoot, Test-HasGit, Resolve-Template)
. "$PSScriptRoot/common.ps1"

try {
$repoRoot = git rev-parse --show-toplevel 2>$null
if ($LASTEXITCODE -eq 0) {
$hasGit = $true
} else {
throw "Git not available"
# Use common.ps1 functions which prioritize .specify over git
$repoRoot = Get-RepoRoot
Comment thread
mbachorik marked this conversation as resolved.
if (-not $repoRoot) {
# Fallback to local Find-RepositoryRoot if common.ps1 couldn't find it
$repoRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
if (-not $repoRoot) {
Comment thread
mbachorik marked this conversation as resolved.
Outdated
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
exit 1
}
} catch {
$repoRoot = $fallbackRoot
$hasGit = $false
}

# Check if git is available at this repo root (not a parent)
$hasGit = Test-HasGit

Set-Location $repoRoot

$specsDir = Join-Path $repoRoot 'specs'
Expand Down
Loading