Script and template tweaks

This commit is contained in:
Den Delimarsky 🌺
2025-09-20 15:04:25 -07:00
parent f04e01d4a2
commit 1a71b03195
7 changed files with 138 additions and 48 deletions

View File

@@ -14,11 +14,47 @@ get_repo_root() {
# Get current branch, with fallback for non-git repositories # Get current branch, with fallback for non-git repositories
get_current_branch() { get_current_branch() {
# First check if SPECIFY_FEATURE environment variable is set
if [[ -n "${SPECIFY_FEATURE:-}" ]]; then
echo "$SPECIFY_FEATURE"
return
fi
# Then check git if available
if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then
git rev-parse --abbrev-ref HEAD git rev-parse --abbrev-ref HEAD
else return
echo "main" # Default branch name for non-git repos
fi fi
# For non-git repos, try to find the latest feature directory
local repo_root=$(get_repo_root)
local specs_dir="$repo_root/specs"
if [[ -d "$specs_dir" ]]; then
local latest_feature=""
local highest=0
for dir in "$specs_dir"/*; do
if [[ -d "$dir" ]]; then
local dirname=$(basename "$dir")
if [[ "$dirname" =~ ^([0-9]{3})- ]]; then
local number=${BASH_REMATCH[1]}
number=$((10#$number))
if [[ "$number" -gt "$highest" ]]; then
highest=$number
latest_feature=$dirname
fi
fi
fi
done
if [[ -n "$latest_feature" ]]; then
echo "$latest_feature"
return
fi
fi
echo "main" # Final fallback
} }
# Check if we have git available # Check if we have git available

View File

@@ -67,10 +67,14 @@ TEMPLATE="$REPO_ROOT/templates/spec-template.md"
SPEC_FILE="$FEATURE_DIR/spec.md" SPEC_FILE="$FEATURE_DIR/spec.md"
if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
# Set the SPECIFY_FEATURE environment variable for the current session
export SPECIFY_FEATURE="$BRANCH_NAME"
if $JSON_MODE; then if $JSON_MODE; then
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM" printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM"
else else
echo "BRANCH_NAME: $BRANCH_NAME" echo "BRANCH_NAME: $BRANCH_NAME"
echo "SPEC_FILE: $SPEC_FILE" echo "SPEC_FILE: $SPEC_FILE"
echo "FEATURE_NUM: $FEATURE_NUM" echo "FEATURE_NUM: $FEATURE_NUM"
echo "SPECIFY_FEATURE environment variable set to: $BRANCH_NAME"
fi fi

View File

@@ -48,10 +48,14 @@ set -o pipefail
# Configuration and Global Variables # Configuration and Global Variables
#============================================================================== #==============================================================================
REPO_ROOT=$(git rev-parse --show-toplevel) # Get script directory and load common functions
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FEATURE_DIR="$REPO_ROOT/specs/$CURRENT_BRANCH" source "$SCRIPT_DIR/common.sh"
NEW_PLAN="$FEATURE_DIR/plan.md"
# Get all paths and variables from common functions
eval $(get_feature_paths)
NEW_PLAN="$IMPL_PLAN" # Alias for compatibility with existing code
AGENT_TYPE="${1:-}" AGENT_TYPE="${1:-}"
# Agent-specific file paths # Agent-specific file paths
@@ -108,22 +112,24 @@ trap cleanup EXIT INT TERM
#============================================================================== #==============================================================================
validate_environment() { validate_environment() {
# Check if we're in a git repository # Check if we have a current branch/feature (git or non-git)
if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
log_error "Not in a git repository"
exit 1
fi
# Check if we have a current branch
if [[ -z "$CURRENT_BRANCH" ]]; then if [[ -z "$CURRENT_BRANCH" ]]; then
log_error "Unable to determine current git branch" log_error "Unable to determine current feature"
if [[ "$HAS_GIT" == "true" ]]; then
log_info "Make sure you're on a feature branch"
else
log_info "Set SPECIFY_FEATURE environment variable or create a feature first"
fi
exit 1 exit 1
fi fi
# Check if plan.md exists # Check if plan.md exists
if [[ ! -f "$NEW_PLAN" ]]; then if [[ ! -f "$NEW_PLAN" ]]; then
log_error "No plan.md found at $NEW_PLAN" log_error "No plan.md found at $NEW_PLAN"
log_info "Make sure you're on a feature branch with a corresponding spec directory" log_info "Make sure you're working on a feature with a corresponding spec directory"
if [[ "$HAS_GIT" != "true" ]]; then
log_info "Use: export SPECIFY_FEATURE=your-feature-name or create a new feature first"
fi
exit 1 exit 1
fi fi
@@ -142,9 +148,9 @@ extract_plan_field() {
local field_pattern="$1" local field_pattern="$1"
local plan_file="$2" local plan_file="$2"
grep "^**${field_pattern}**: " "$plan_file" 2>/dev/null | \ grep "^\*\*${field_pattern}\*\*: " "$plan_file" 2>/dev/null | \
head -1 | \ head -1 | \
sed "s/^**${field_pattern}**: //" | \ sed "s|^\*\*${field_pattern}\*\*: ||" | \
grep -v "NEEDS CLARIFICATION" | \ grep -v "NEEDS CLARIFICATION" | \
grep -v "^N/A$" || echo "" grep -v "^N/A$" || echo ""
} }
@@ -196,12 +202,9 @@ get_project_structure() {
local project_type="$1" local project_type="$1"
if [[ "$project_type" == *"web"* ]]; then if [[ "$project_type" == *"web"* ]]; then
echo "backend/ echo "backend/\\nfrontend/\\ntests/"
frontend/
tests/"
else else
echo "src/ echo "src/\\ntests/"
tests/"
fi fi
} }
@@ -267,22 +270,26 @@ create_new_agent_file() {
"s/\[PROJECT NAME\]/$project_name/" "s/\[PROJECT NAME\]/$project_name/"
"s/\[DATE\]/$current_date/" "s/\[DATE\]/$current_date/"
"s/\[EXTRACTED FROM ALL PLAN.MD FILES\]/- $NEW_LANG + $NEW_FRAMEWORK ($CURRENT_BRANCH)/" "s/\[EXTRACTED FROM ALL PLAN.MD FILES\]/- $NEW_LANG + $NEW_FRAMEWORK ($CURRENT_BRANCH)/"
"s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|" "s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|g"
"s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|" "s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|"
"s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|" "s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|"
"s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|- $CURRENT_BRANCH: Added $NEW_LANG + $NEW_FRAMEWORK|" "s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|- $CURRENT_BRANCH: Added $NEW_LANG + $NEW_FRAMEWORK|"
) )
for substitution in "${substitutions[@]}"; do for substitution in "${substitutions[@]}"; do
if ! sed -i.bak "$substitution" "$temp_file"; then if ! sed -i.bak -e "$substitution" "$temp_file"; then
log_error "Failed to perform substitution: $substitution" log_error "Failed to perform substitution: $substitution"
rm -f "$temp_file" "$temp_file.bak" rm -f "$temp_file" "$temp_file.bak"
return 1 return 1
fi fi
done done
# Convert \n sequences to actual newlines
sed -i.bak2 's/\\n/\
/g' "$temp_file"
# Clean up backup files # Clean up backup files
rm -f "$temp_file.bak" rm -f "$temp_file.bak" "$temp_file.bak2"
return 0 return 0
} }

View File

@@ -16,6 +16,12 @@ function Get-RepoRoot {
} }
function Get-CurrentBranch { function Get-CurrentBranch {
# First check if SPECIFY_FEATURE environment variable is set
if ($env:SPECIFY_FEATURE) {
return $env:SPECIFY_FEATURE
}
# Then check git if available
try { try {
$result = git rev-parse --abbrev-ref HEAD 2>$null $result = git rev-parse --abbrev-ref HEAD 2>$null
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
@@ -25,7 +31,30 @@ function Get-CurrentBranch {
# Git command failed # Git command failed
} }
# Default branch name for non-git repos # For non-git repos, try to find the latest feature directory
$repoRoot = Get-RepoRoot
$specsDir = Join-Path $repoRoot "specs"
if (Test-Path $specsDir) {
$latestFeature = ""
$highest = 0
Get-ChildItem -Path $specsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d{3})-') {
$num = [int]$matches[1]
if ($num -gt $highest) {
$highest = $num
$latestFeature = $_.Name
}
}
}
if ($latestFeature) {
return $latestFeature
}
}
# Final fallback
return "main" return "main"
} }

View File

@@ -73,6 +73,9 @@ if (Test-Path $template) {
New-Item -ItemType File -Path $specFile | Out-Null New-Item -ItemType File -Path $specFile | Out-Null
} }
# Set the SPECIFY_FEATURE environment variable for the current session
$env:SPECIFY_FEATURE = $branchName
if ($Json) { if ($Json) {
$obj = [PSCustomObject]@{ $obj = [PSCustomObject]@{
BRANCH_NAME = $branchName BRANCH_NAME = $branchName
@@ -86,4 +89,5 @@ if ($Json) {
Write-Output "SPEC_FILE: $specFile" Write-Output "SPEC_FILE: $specFile"
Write-Output "FEATURE_NUM: $featureNum" Write-Output "FEATURE_NUM: $featureNum"
Write-Output "HAS_GIT: $hasGit" Write-Output "HAS_GIT: $hasGit"
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
} }

View File

@@ -3,21 +3,30 @@
param([string]$AgentType) param([string]$AgentType)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$repoRoot = git rev-parse --show-toplevel # Load common functions
$currentBranch = git rev-parse --abbrev-ref HEAD . "$PSScriptRoot/common.ps1"
$featureDir = Join-Path $repoRoot "specs/$currentBranch"
$newPlan = Join-Path $featureDir 'plan.md'
if (-not (Test-Path $newPlan)) { Write-Error "ERROR: No plan.md found at $newPlan"; exit 1 }
$claudeFile = Join-Path $repoRoot 'CLAUDE.md' # Get all paths and variables from common functions
$geminiFile = Join-Path $repoRoot 'GEMINI.md' $paths = Get-FeaturePathsEnv
$copilotFile = Join-Path $repoRoot '.github/copilot-instructions.md'
$cursorFile = Join-Path $repoRoot '.cursor/rules/specify-rules.mdc'
$qwenFile = Join-Path $repoRoot 'QWEN.md'
$agentsFile = Join-Path $repoRoot 'AGENTS.md'
$windsurfFile = Join-Path $repoRoot '.windsurf/rules/specify-rules.md'
Write-Output "=== Updating agent context files for feature $currentBranch ===" $newPlan = $paths.IMPL_PLAN
if (-not (Test-Path $newPlan)) {
Write-Error "ERROR: No plan.md found at $newPlan"
if (-not $paths.HAS_GIT) {
Write-Output "Use: `$env:SPECIFY_FEATURE='your-feature-name' or create a new feature first"
}
exit 1
}
$claudeFile = Join-Path $paths.REPO_ROOT 'CLAUDE.md'
$geminiFile = Join-Path $paths.REPO_ROOT 'GEMINI.md'
$copilotFile = Join-Path $paths.REPO_ROOT '.github/copilot-instructions.md'
$cursorFile = Join-Path $paths.REPO_ROOT '.cursor/rules/specify-rules.mdc'
$qwenFile = Join-Path $paths.REPO_ROOT 'QWEN.md'
$agentsFile = Join-Path $paths.REPO_ROOT 'AGENTS.md'
$windsurfFile = Join-Path $paths.REPO_ROOT '.windsurf/rules/specify-rules.md'
Write-Output "=== Updating agent context files for feature $($paths.CURRENT_BRANCH) ==="
function Get-PlanValue($pattern) { function Get-PlanValue($pattern) {
if (-not (Test-Path $newPlan)) { return '' } if (-not (Test-Path $newPlan)) { return '' }
@@ -34,12 +43,12 @@ $newProjectType = Get-PlanValue 'Project Type'
function Initialize-AgentFile($targetFile, $agentName) { function Initialize-AgentFile($targetFile, $agentName) {
if (Test-Path $targetFile) { return } if (Test-Path $targetFile) { return }
$template = Join-Path $repoRoot '.specify/templates/agent-file-template.md' $template = Join-Path $paths.REPO_ROOT '.specify/templates/agent-file-template.md'
if (-not (Test-Path $template)) { Write-Error "Template not found: $template"; return } if (-not (Test-Path $template)) { Write-Error "Template not found: $template"; return }
$content = Get-Content $template -Raw $content = Get-Content $template -Raw
$content = $content.Replace('[PROJECT NAME]', (Split-Path $repoRoot -Leaf)) $content = $content.Replace('[PROJECT NAME]', (Split-Path $paths.REPO_ROOT -Leaf))
$content = $content.Replace('[DATE]', (Get-Date -Format 'yyyy-MM-dd')) $content = $content.Replace('[DATE]', (Get-Date -Format 'yyyy-MM-dd'))
$content = $content.Replace('[EXTRACTED FROM ALL PLAN.MD FILES]', "- $newLang + $newFramework ($currentBranch)") $content = $content.Replace('[EXTRACTED FROM ALL PLAN.MD FILES]', "- $newLang + $newFramework ($($paths.CURRENT_BRANCH))")
if ($newProjectType -match 'web') { $structure = "backend/`nfrontend/`ntests/" } else { $structure = "src/`ntests/" } if ($newProjectType -match 'web') { $structure = "backend/`nfrontend/`ntests/" } else { $structure = "src/`ntests/" }
$content = $content.Replace('[ACTUAL STRUCTURE FROM PLANS]', $structure) $content = $content.Replace('[ACTUAL STRUCTURE FROM PLANS]', $structure)
if ($newLang -match 'Python') { $commands = 'cd src && pytest && ruff check .' } if ($newLang -match 'Python') { $commands = 'cd src && pytest && ruff check .' }
@@ -48,18 +57,18 @@ function Initialize-AgentFile($targetFile, $agentName) {
else { $commands = "# Add commands for $newLang" } else { $commands = "# Add commands for $newLang" }
$content = $content.Replace('[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]', $commands) $content = $content.Replace('[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]', $commands)
$content = $content.Replace('[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]', "${newLang}: Follow standard conventions") $content = $content.Replace('[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]', "${newLang}: Follow standard conventions")
$content = $content.Replace('[LAST 3 FEATURES AND WHAT THEY ADDED]', "- ${currentBranch}: Added ${newLang} + ${newFramework}") $content = $content.Replace('[LAST 3 FEATURES AND WHAT THEY ADDED]', "- $($paths.CURRENT_BRANCH): Added ${newLang} + ${newFramework}")
$content | Set-Content $targetFile -Encoding UTF8 $content | Set-Content $targetFile -Encoding UTF8
} }
function Update-AgentFile($targetFile, $agentName) { function Update-AgentFile($targetFile, $agentName) {
if (-not (Test-Path $targetFile)) { Initialize-AgentFile $targetFile $agentName; return } if (-not (Test-Path $targetFile)) { Initialize-AgentFile $targetFile $agentName; return }
$content = Get-Content $targetFile -Raw $content = Get-Content $targetFile -Raw
if ($newLang -and ($content -notmatch [regex]::Escape($newLang))) { $content = $content -replace '(## Active Technologies\n)', "`$1- $newLang + $newFramework ($currentBranch)`n" } if ($newLang -and ($content -notmatch [regex]::Escape($newLang))) { $content = $content -replace '(## Active Technologies\n)', "`$1- $newLang + $newFramework ($($paths.CURRENT_BRANCH))`n" }
if ($newDb -and $newDb -ne 'N/A' -and ($content -notmatch [regex]::Escape($newDb))) { $content = $content -replace '(## Active Technologies\n)', "`$1- $newDb ($currentBranch)`n" } if ($newDb -and $newDb -ne 'N/A' -and ($content -notmatch [regex]::Escape($newDb))) { $content = $content -replace '(## Active Technologies\n)', "`$1- $newDb ($($paths.CURRENT_BRANCH))`n" }
if ($content -match '## Recent Changes\n([\s\S]*?)(\n\n|$)') { if ($content -match '## Recent Changes\n([\s\S]*?)(\n\n|$)') {
$changesBlock = $matches[1].Trim().Split("`n") $changesBlock = $matches[1].Trim().Split("`n")
$changesBlock = ,"- ${currentBranch}: Added ${newLang} + ${newFramework}" + $changesBlock $changesBlock = ,"- $($paths.CURRENT_BRANCH): Added ${newLang} + ${newFramework}" + $changesBlock
$changesBlock = $changesBlock | Where-Object { $_ } | Select-Object -First 3 $changesBlock = $changesBlock | Where-Object { $_ } | Select-Object -First 3
$joined = ($changesBlock -join "`n") $joined = ($changesBlock -join "`n")
$content = [regex]::Replace($content, '## Recent Changes\n([\s\S]*?)(\n\n|$)', "## Recent Changes`n$joined`n`n") $content = [regex]::Replace($content, '## Recent Changes\n([\s\S]*?)(\n\n|$)', "## Recent Changes`n$joined`n`n")

View File

@@ -151,7 +151,8 @@ ios/ or android/
- Quickstart test = story validation steps - Quickstart test = story validation steps
5. **Update agent file incrementally** (O(1) operation): 5. **Update agent file incrementally** (O(1) operation):
- Run `{SCRIPT}` for your AI assistant - Run `{SCRIPT}`
**IMPORTANT**: Execute it exactly as specified above. Do not add or remove any arguments.
- If exists: Add only NEW tech from current plan - If exists: Add only NEW tech from current plan
- Preserve manual additions between markers - Preserve manual additions between markers
- Update recent changes (keep last 3) - Update recent changes (keep last 3)