From 3dcbb6e3a9370550113cca85e173e7b46dc10e6b Mon Sep 17 00:00:00 2001 From: "den (work)" <53200638+localden@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:17:35 -0800 Subject: [PATCH] Refactor feature script logic and update agent context scripts Refactored both Bash and PowerShell create-new-feature scripts to modularize and deduplicate logic for determining the next feature number, including new helper functions for extracting the highest number from specs and branches. Improved branch name cleaning and generation. In update-agent-context scripts, removed redundant updates to AGENTS.md for Copilot, streamlining agent update logic. --- scripts/bash/create-new-feature.sh | 117 ++++++++++---------- scripts/bash/update-agent-context.sh | 8 -- scripts/powershell/create-new-feature.ps1 | 101 ++++++++--------- scripts/powershell/update-agent-context.ps1 | 8 +- 4 files changed, 110 insertions(+), 124 deletions(-) diff --git a/scripts/bash/create-new-feature.sh b/scripts/bash/create-new-feature.sh index ed244282..8e8bcf6c 100644 --- a/scripts/bash/create-new-feature.sh +++ b/scripts/bash/create-new-feature.sh @@ -80,9 +80,56 @@ find_repo_root() { return 1 } +# Function to get highest number from specs directory +get_highest_from_specs() { + local specs_dir="$1" + local highest=0 + + if [ -d "$specs_dir" ]; then + for dir in "$specs_dir"/*; do + [ -d "$dir" ] || continue + dirname=$(basename "$dir") + number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0") + number=$((10#$number)) + if [ "$number" -gt "$highest" ]; then + highest=$number + fi + done + fi + + echo "$highest" +} + +# Function to get highest number from git branches +get_highest_from_branches() { + local highest=0 + + # Get all branches (local and remote) + branches=$(git branch -a 2>/dev/null || echo "") + + if [ -n "$branches" ]; then + while IFS= read -r branch; do + # Clean branch name: remove leading markers and remote prefixes + clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||') + + # Extract feature number if branch matches pattern ###-* + if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then + number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0") + number=$((10#$number)) + if [ "$number" -gt "$highest" ]; then + highest=$number + fi + fi + done <<< "$branches" + fi + + echo "$highest" +} + # Function to check existing branches (local and remote) and return next available number check_existing_branches() { local short_name="$1" + local specs_dir="$2" # Fetch all remotes to get latest branch info (suppress errors if no remotes) git fetch --all --prune 2>/dev/null || true @@ -95,8 +142,8 @@ check_existing_branches() { # Check specs directory as well local spec_dirs="" - if [ -d "$SPECS_DIR" ]; then - spec_dirs=$(find "$SPECS_DIR" -maxdepth 1 -type d -name "[0-9]*-${short_name}" 2>/dev/null | xargs -n1 basename 2>/dev/null | sed 's/-.*//' | sort -n) + if [ -d "$specs_dir" ]; then + spec_dirs=$(find "$specs_dir" -maxdepth 1 -type d -name "[0-9]*-${short_name}" 2>/dev/null | xargs -n1 basename 2>/dev/null | sed 's/-.*//' | sort -n) fi # Combine all sources and get the highest number @@ -111,6 +158,12 @@ check_existing_branches() { echo $((max_num + 1)) } +# Function to clean and format a branch name +clean_branch_name() { + local name="$1" + 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. @@ -133,48 +186,6 @@ cd "$REPO_ROOT" SPECS_DIR="$REPO_ROOT/specs" mkdir -p "$SPECS_DIR" -# Get highest number from specs directory -HIGHEST_FROM_SPECS=0 -if [ -d "$SPECS_DIR" ]; then - for dir in "$SPECS_DIR"/*; do - [ -d "$dir" ] || continue - dirname=$(basename "$dir") - number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0") - number=$((10#$number)) - if [ "$number" -gt "$HIGHEST_FROM_SPECS" ]; then HIGHEST_FROM_SPECS=$number; fi - done -fi - -# Get highest number from branch names (both local and remote) -HIGHEST_FROM_BRANCHES=0 -if [ "$HAS_GIT" = true ]; then - # Get all branches (local and remote) - branches=$(git branch -a 2>/dev/null || echo "") - - if [ -n "$branches" ]; then - while IFS= read -r branch; do - # Clean branch name: remove leading markers and remote prefixes - clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||') - - # Extract feature number if branch matches pattern ###-* - if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then - number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0") - number=$((10#$number)) - if [ "$number" -gt "$HIGHEST_FROM_BRANCHES" ]; then HIGHEST_FROM_BRANCHES=$number; fi - fi - done <<< "$branches" - fi -fi - -# Use the highest number from either source -HIGHEST=$HIGHEST_FROM_SPECS -if [ "$HIGHEST_FROM_BRANCHES" -gt "$HIGHEST" ]; then - HIGHEST=$HIGHEST_FROM_BRANCHES -fi - -NEXT=$((HIGHEST + 1)) -FEATURE_NUM=$(printf "%03d" "$NEXT") - # Function to generate branch name with stop word filtering and length filtering generate_branch_name() { local description="$1" @@ -218,14 +229,15 @@ generate_branch_name() { echo "$result" else # Fallback to original logic if no meaningful words found - echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//' | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//' + local cleaned=$(clean_branch_name "$description") + echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//' fi } # Generate branch name if [ -n "$SHORT_NAME" ]; then # Use provided short name, just clean it up - BRANCH_SUFFIX=$(echo "$SHORT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//') + BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME") else # Generate from description with smart filtering BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION") @@ -235,19 +247,10 @@ fi if [ -z "$BRANCH_NUMBER" ]; then if [ "$HAS_GIT" = true ]; then # Check existing branches on remotes - BRANCH_NUMBER=$(check_existing_branches "$BRANCH_SUFFIX") + BRANCH_NUMBER=$(check_existing_branches "$BRANCH_SUFFIX" "$SPECS_DIR") else # Fall back to local directory check - HIGHEST=0 - if [ -d "$SPECS_DIR" ]; then - for dir in "$SPECS_DIR"/*; do - [ -d "$dir" ] || continue - dirname=$(basename "$dir") - number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0") - number=$((10#$number)) - if [ "$number" -gt "$HIGHEST" ]; then HIGHEST=$number; fi - done - fi + HIGHEST=$(get_highest_from_specs "$SPECS_DIR") BRANCH_NUMBER=$((HIGHEST + 1)) fi fi diff --git a/scripts/bash/update-agent-context.sh b/scripts/bash/update-agent-context.sh index fcb525ac..a375bdc9 100644 --- a/scripts/bash/update-agent-context.sh +++ b/scripts/bash/update-agent-context.sh @@ -62,7 +62,6 @@ AGENT_TYPE="${1:-}" CLAUDE_FILE="$REPO_ROOT/CLAUDE.md" GEMINI_FILE="$REPO_ROOT/GEMINI.md" COPILOT_FILE="$REPO_ROOT/.github/agents/copilot-instructions.md" -COPILOT_AGENTS_FILE="$REPO_ROOT/AGENTS.md" CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc" QWEN_FILE="$REPO_ROOT/QWEN.md" AGENTS_FILE="$REPO_ROOT/AGENTS.md" @@ -588,7 +587,6 @@ update_specific_agent() { ;; copilot) update_agent_file "$COPILOT_FILE" "GitHub Copilot" - update_agent_file "$COPILOT_AGENTS_FILE" "GitHub Copilot (AGENTS.md)" ;; cursor-agent) update_agent_file "$CURSOR_FILE" "Cursor IDE" @@ -650,12 +648,6 @@ update_all_existing_agents() { found_agent=true fi - # Also update AGENTS.md for Copilot if the Copilot directory exists - if [[ -d "$REPO_ROOT/.github/agents" ]]; then - update_agent_file "$COPILOT_AGENTS_FILE" "GitHub Copilot (AGENTS.md)" - found_agent=true - fi - if [[ -f "$CURSOR_FILE" ]]; then update_agent_file "$CURSOR_FILE" "Cursor IDE" found_agent=true diff --git a/scripts/powershell/create-new-feature.ps1 b/scripts/powershell/create-new-feature.ps1 index db92c3f7..351f4e9e 100644 --- a/scripts/powershell/create-new-feature.ps1 +++ b/scripts/powershell/create-new-feature.ps1 @@ -59,6 +59,46 @@ function Find-RepositoryRoot { } } +function Get-HighestNumberFromSpecs { + param([string]$SpecsDir) + + $highest = 0 + if (Test-Path $SpecsDir) { + Get-ChildItem -Path $SpecsDir -Directory | ForEach-Object { + if ($_.Name -match '^(\d+)') { + $num = [int]$matches[1] + if ($num -gt $highest) { $highest = $num } + } + } + } + return $highest +} + +function Get-HighestNumberFromBranches { + param() + + $highest = 0 + try { + $branches = git branch -a 2>$null + if ($LASTEXITCODE -eq 0) { + foreach ($branch in $branches) { + # Clean branch name: remove leading markers and remote prefixes + $cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', '' + + # Extract feature number if branch matches pattern ###-* + if ($cleanBranch -match '^(\d+)-') { + $num = [int]$matches[1] + if ($num -gt $highest) { $highest = $num } + } + } + } + } catch { + # If git command fails, return 0 + Write-Verbose "Could not check Git branches: $_" + } + return $highest +} + function Get-NextBranchNumber { param( [string]$ShortName, @@ -127,6 +167,12 @@ function Get-NextBranchNumber { # Return next number return $maxNum + 1 } + +function ConvertTo-CleanBranchName { + param([string]$Name) + + 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." @@ -150,46 +196,6 @@ Set-Location $repoRoot $specsDir = Join-Path $repoRoot 'specs' New-Item -ItemType Directory -Path $specsDir -Force | Out-Null -# Get highest number from specs directory -$highestFromSpecs = 0 -if (Test-Path $specsDir) { - Get-ChildItem -Path $specsDir -Directory | ForEach-Object { - if ($_.Name -match '^(\d{3})') { - $num = [int]$matches[1] - if ($num -gt $highestFromSpecs) { $highestFromSpecs = $num } - } - } -} - -# Get highest number from branch names (both local and remote) -$highestFromBranches = 0 -try { - $branches = git branch -a 2>$null - if ($LASTEXITCODE -eq 0) { - foreach ($branch in $branches) { - # Clean branch name: remove leading markers and remote prefixes - # The following regex removes: - # - Git's current branch marker ('*') and leading whitespace (e.g., '* main') - # - Remote prefixes (e.g., 'remotes/origin/') - $cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', '' - - # Extract feature number if branch matches pattern ###-* - if ($cleanBranch -match '^(\d{3})-') { - $num = [int]$matches[1] - if ($num -gt $highestFromBranches) { $highestFromBranches = $num } - } - } - } -} catch { - # If git command fails, just continue with specs-only check - Write-Verbose "Could not check Git branches: $_" -} - -# Use the highest number from either source -$highest = [Math]::Max($highestFromSpecs, $highestFromBranches) -$next = $highest + 1 -$featureNum = ('{0:000}' -f $next) - # Function to generate branch name with stop word filtering and length filtering function Get-BranchName { param([string]$Description) @@ -229,7 +235,7 @@ function Get-BranchName { return $result } else { # Fallback to original logic if no meaningful words found - $result = $Description.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', '' + $result = ConvertTo-CleanBranchName -Name $Description $fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3 return [string]::Join('-', $fallbackWords) } @@ -238,7 +244,7 @@ function Get-BranchName { # Generate branch name if ($ShortName) { # Use provided short name, just clean it up - $branchSuffix = $ShortName.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', '' + $branchSuffix = ConvertTo-CleanBranchName -Name $ShortName } else { # Generate from description with smart filtering $branchSuffix = Get-BranchName -Description $featureDesc @@ -251,16 +257,7 @@ if ($Number -eq 0) { $Number = Get-NextBranchNumber -ShortName $branchSuffix -SpecsDir $specsDir } else { # Fall back to local directory check - $highest = 0 - if (Test-Path $specsDir) { - Get-ChildItem -Path $specsDir -Directory | ForEach-Object { - if ($_.Name -match '^(\d{3})') { - $num = [int]$matches[1] - if ($num -gt $highest) { $highest = $num } - } - } - } - $Number = $highest + 1 + $Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1 } } diff --git a/scripts/powershell/update-agent-context.ps1 b/scripts/powershell/update-agent-context.ps1 index a439cc43..741a55a6 100644 --- a/scripts/powershell/update-agent-context.ps1 +++ b/scripts/powershell/update-agent-context.ps1 @@ -47,7 +47,6 @@ $NEW_PLAN = $IMPL_PLAN $CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md' $GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md' $COPILOT_FILE = Join-Path $REPO_ROOT '.github/agents/copilot-instructions.md' -$COPILOT_AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc' $QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md' $AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md' @@ -371,10 +370,7 @@ function Update-SpecificAgent { switch ($Type) { 'claude' { Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code' } 'gemini' { Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI' } - 'copilot' { - Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' - Update-AgentFile -TargetFile $COPILOT_AGENTS_FILE -AgentName 'GitHub Copilot (AGENTS.md)' - } + 'copilot' { Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' } 'cursor-agent' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' } 'qwen' { Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code' } 'opencode' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'opencode' } @@ -396,8 +392,6 @@ function Update-AllExistingAgents { if (Test-Path $CLAUDE_FILE) { if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }; $found = $true } if (Test-Path $GEMINI_FILE) { if (-not (Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI')) { $ok = $false }; $found = $true } if (Test-Path $COPILOT_FILE) { if (-not (Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot')) { $ok = $false }; $found = $true } - # Also update AGENTS.md for Copilot if the Copilot directory exists - if (Test-Path (Join-Path $REPO_ROOT '.github/agents')) { if (-not (Update-AgentFile -TargetFile $COPILOT_AGENTS_FILE -AgentName 'GitHub Copilot (AGENTS.md)')) { $ok = $false }; $found = $true } if (Test-Path $CURSOR_FILE) { if (-not (Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE')) { $ok = $false }; $found = $true } if (Test-Path $QWEN_FILE) { if (-not (Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code')) { $ok = $false }; $found = $true } if (Test-Path $AGENTS_FILE) { if (-not (Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex/opencode')) { $ok = $false }; $found = $true }