Skip to content

Commit 3dcbb6e

Browse files
committed
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.
1 parent 24b6d31 commit 3dcbb6e

4 files changed

Lines changed: 110 additions & 124 deletions

File tree

scripts/bash/create-new-feature.sh

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,56 @@ find_repo_root() {
8080
return 1
8181
}
8282

83+
# Function to get highest number from specs directory
84+
get_highest_from_specs() {
85+
local specs_dir="$1"
86+
local highest=0
87+
88+
if [ -d "$specs_dir" ]; then
89+
for dir in "$specs_dir"/*; do
90+
[ -d "$dir" ] || continue
91+
dirname=$(basename "$dir")
92+
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
93+
number=$((10#$number))
94+
if [ "$number" -gt "$highest" ]; then
95+
highest=$number
96+
fi
97+
done
98+
fi
99+
100+
echo "$highest"
101+
}
102+
103+
# Function to get highest number from git branches
104+
get_highest_from_branches() {
105+
local highest=0
106+
107+
# Get all branches (local and remote)
108+
branches=$(git branch -a 2>/dev/null || echo "")
109+
110+
if [ -n "$branches" ]; then
111+
while IFS= read -r branch; do
112+
# Clean branch name: remove leading markers and remote prefixes
113+
clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||')
114+
115+
# Extract feature number if branch matches pattern ###-*
116+
if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then
117+
number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0")
118+
number=$((10#$number))
119+
if [ "$number" -gt "$highest" ]; then
120+
highest=$number
121+
fi
122+
fi
123+
done <<< "$branches"
124+
fi
125+
126+
echo "$highest"
127+
}
128+
83129
# Function to check existing branches (local and remote) and return next available number
84130
check_existing_branches() {
85131
local short_name="$1"
132+
local specs_dir="$2"
86133

87134
# Fetch all remotes to get latest branch info (suppress errors if no remotes)
88135
git fetch --all --prune 2>/dev/null || true
@@ -95,8 +142,8 @@ check_existing_branches() {
95142

96143
# Check specs directory as well
97144
local spec_dirs=""
98-
if [ -d "$SPECS_DIR" ]; then
99-
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)
145+
if [ -d "$specs_dir" ]; then
146+
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)
100147
fi
101148

102149
# Combine all sources and get the highest number
@@ -111,6 +158,12 @@ check_existing_branches() {
111158
echo $((max_num + 1))
112159
}
113160

161+
# Function to clean and format a branch name
162+
clean_branch_name() {
163+
local name="$1"
164+
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
165+
}
166+
114167
# Resolve repository root. Prefer git information when available, but fall back
115168
# to searching for repository markers so the workflow still functions in repositories that
116169
# were initialised with --no-git.
@@ -133,48 +186,6 @@ cd "$REPO_ROOT"
133186
SPECS_DIR="$REPO_ROOT/specs"
134187
mkdir -p "$SPECS_DIR"
135188

136-
# Get highest number from specs directory
137-
HIGHEST_FROM_SPECS=0
138-
if [ -d "$SPECS_DIR" ]; then
139-
for dir in "$SPECS_DIR"/*; do
140-
[ -d "$dir" ] || continue
141-
dirname=$(basename "$dir")
142-
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
143-
number=$((10#$number))
144-
if [ "$number" -gt "$HIGHEST_FROM_SPECS" ]; then HIGHEST_FROM_SPECS=$number; fi
145-
done
146-
fi
147-
148-
# Get highest number from branch names (both local and remote)
149-
HIGHEST_FROM_BRANCHES=0
150-
if [ "$HAS_GIT" = true ]; then
151-
# Get all branches (local and remote)
152-
branches=$(git branch -a 2>/dev/null || echo "")
153-
154-
if [ -n "$branches" ]; then
155-
while IFS= read -r branch; do
156-
# Clean branch name: remove leading markers and remote prefixes
157-
clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||')
158-
159-
# Extract feature number if branch matches pattern ###-*
160-
if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then
161-
number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0")
162-
number=$((10#$number))
163-
if [ "$number" -gt "$HIGHEST_FROM_BRANCHES" ]; then HIGHEST_FROM_BRANCHES=$number; fi
164-
fi
165-
done <<< "$branches"
166-
fi
167-
fi
168-
169-
# Use the highest number from either source
170-
HIGHEST=$HIGHEST_FROM_SPECS
171-
if [ "$HIGHEST_FROM_BRANCHES" -gt "$HIGHEST" ]; then
172-
HIGHEST=$HIGHEST_FROM_BRANCHES
173-
fi
174-
175-
NEXT=$((HIGHEST + 1))
176-
FEATURE_NUM=$(printf "%03d" "$NEXT")
177-
178189
# Function to generate branch name with stop word filtering and length filtering
179190
generate_branch_name() {
180191
local description="$1"
@@ -218,14 +229,15 @@ generate_branch_name() {
218229
echo "$result"
219230
else
220231
# Fallback to original logic if no meaningful words found
221-
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/-$//'
232+
local cleaned=$(clean_branch_name "$description")
233+
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
222234
fi
223235
}
224236

225237
# Generate branch name
226238
if [ -n "$SHORT_NAME" ]; then
227239
# Use provided short name, just clean it up
228-
BRANCH_SUFFIX=$(echo "$SHORT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
240+
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME")
229241
else
230242
# Generate from description with smart filtering
231243
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
@@ -235,19 +247,10 @@ fi
235247
if [ -z "$BRANCH_NUMBER" ]; then
236248
if [ "$HAS_GIT" = true ]; then
237249
# Check existing branches on remotes
238-
BRANCH_NUMBER=$(check_existing_branches "$BRANCH_SUFFIX")
250+
BRANCH_NUMBER=$(check_existing_branches "$BRANCH_SUFFIX" "$SPECS_DIR")
239251
else
240252
# Fall back to local directory check
241-
HIGHEST=0
242-
if [ -d "$SPECS_DIR" ]; then
243-
for dir in "$SPECS_DIR"/*; do
244-
[ -d "$dir" ] || continue
245-
dirname=$(basename "$dir")
246-
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
247-
number=$((10#$number))
248-
if [ "$number" -gt "$HIGHEST" ]; then HIGHEST=$number; fi
249-
done
250-
fi
253+
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
251254
BRANCH_NUMBER=$((HIGHEST + 1))
252255
fi
253256
fi

scripts/bash/update-agent-context.sh

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ AGENT_TYPE="${1:-}"
6262
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
6363
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
6464
COPILOT_FILE="$REPO_ROOT/.github/agents/copilot-instructions.md"
65-
COPILOT_AGENTS_FILE="$REPO_ROOT/AGENTS.md"
6665
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
6766
QWEN_FILE="$REPO_ROOT/QWEN.md"
6867
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
@@ -588,7 +587,6 @@ update_specific_agent() {
588587
;;
589588
copilot)
590589
update_agent_file "$COPILOT_FILE" "GitHub Copilot"
591-
update_agent_file "$COPILOT_AGENTS_FILE" "GitHub Copilot (AGENTS.md)"
592590
;;
593591
cursor-agent)
594592
update_agent_file "$CURSOR_FILE" "Cursor IDE"
@@ -650,12 +648,6 @@ update_all_existing_agents() {
650648
found_agent=true
651649
fi
652650

653-
# Also update AGENTS.md for Copilot if the Copilot directory exists
654-
if [[ -d "$REPO_ROOT/.github/agents" ]]; then
655-
update_agent_file "$COPILOT_AGENTS_FILE" "GitHub Copilot (AGENTS.md)"
656-
found_agent=true
657-
fi
658-
659651
if [[ -f "$CURSOR_FILE" ]]; then
660652
update_agent_file "$CURSOR_FILE" "Cursor IDE"
661653
found_agent=true

scripts/powershell/create-new-feature.ps1

Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,46 @@ function Find-RepositoryRoot {
5959
}
6060
}
6161

62+
function Get-HighestNumberFromSpecs {
63+
param([string]$SpecsDir)
64+
65+
$highest = 0
66+
if (Test-Path $SpecsDir) {
67+
Get-ChildItem -Path $SpecsDir -Directory | ForEach-Object {
68+
if ($_.Name -match '^(\d+)') {
69+
$num = [int]$matches[1]
70+
if ($num -gt $highest) { $highest = $num }
71+
}
72+
}
73+
}
74+
return $highest
75+
}
76+
77+
function Get-HighestNumberFromBranches {
78+
param()
79+
80+
$highest = 0
81+
try {
82+
$branches = git branch -a 2>$null
83+
if ($LASTEXITCODE -eq 0) {
84+
foreach ($branch in $branches) {
85+
# Clean branch name: remove leading markers and remote prefixes
86+
$cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
87+
88+
# Extract feature number if branch matches pattern ###-*
89+
if ($cleanBranch -match '^(\d+)-') {
90+
$num = [int]$matches[1]
91+
if ($num -gt $highest) { $highest = $num }
92+
}
93+
}
94+
}
95+
} catch {
96+
# If git command fails, return 0
97+
Write-Verbose "Could not check Git branches: $_"
98+
}
99+
return $highest
100+
}
101+
62102
function Get-NextBranchNumber {
63103
param(
64104
[string]$ShortName,
@@ -127,6 +167,12 @@ function Get-NextBranchNumber {
127167
# Return next number
128168
return $maxNum + 1
129169
}
170+
171+
function ConvertTo-CleanBranchName {
172+
param([string]$Name)
173+
174+
return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
175+
}
130176
$fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
131177
if (-not $fallbackRoot) {
132178
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
@@ -150,46 +196,6 @@ Set-Location $repoRoot
150196
$specsDir = Join-Path $repoRoot 'specs'
151197
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
152198

153-
# Get highest number from specs directory
154-
$highestFromSpecs = 0
155-
if (Test-Path $specsDir) {
156-
Get-ChildItem -Path $specsDir -Directory | ForEach-Object {
157-
if ($_.Name -match '^(\d{3})') {
158-
$num = [int]$matches[1]
159-
if ($num -gt $highestFromSpecs) { $highestFromSpecs = $num }
160-
}
161-
}
162-
}
163-
164-
# Get highest number from branch names (both local and remote)
165-
$highestFromBranches = 0
166-
try {
167-
$branches = git branch -a 2>$null
168-
if ($LASTEXITCODE -eq 0) {
169-
foreach ($branch in $branches) {
170-
# Clean branch name: remove leading markers and remote prefixes
171-
# The following regex removes:
172-
# - Git's current branch marker ('*') and leading whitespace (e.g., '* main')
173-
# - Remote prefixes (e.g., 'remotes/origin/')
174-
$cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
175-
176-
# Extract feature number if branch matches pattern ###-*
177-
if ($cleanBranch -match '^(\d{3})-') {
178-
$num = [int]$matches[1]
179-
if ($num -gt $highestFromBranches) { $highestFromBranches = $num }
180-
}
181-
}
182-
}
183-
} catch {
184-
# If git command fails, just continue with specs-only check
185-
Write-Verbose "Could not check Git branches: $_"
186-
}
187-
188-
# Use the highest number from either source
189-
$highest = [Math]::Max($highestFromSpecs, $highestFromBranches)
190-
$next = $highest + 1
191-
$featureNum = ('{0:000}' -f $next)
192-
193199
# Function to generate branch name with stop word filtering and length filtering
194200
function Get-BranchName {
195201
param([string]$Description)
@@ -229,7 +235,7 @@ function Get-BranchName {
229235
return $result
230236
} else {
231237
# Fallback to original logic if no meaningful words found
232-
$result = $Description.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
238+
$result = ConvertTo-CleanBranchName -Name $Description
233239
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
234240
return [string]::Join('-', $fallbackWords)
235241
}
@@ -238,7 +244,7 @@ function Get-BranchName {
238244
# Generate branch name
239245
if ($ShortName) {
240246
# Use provided short name, just clean it up
241-
$branchSuffix = $ShortName.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
247+
$branchSuffix = ConvertTo-CleanBranchName -Name $ShortName
242248
} else {
243249
# Generate from description with smart filtering
244250
$branchSuffix = Get-BranchName -Description $featureDesc
@@ -251,16 +257,7 @@ if ($Number -eq 0) {
251257
$Number = Get-NextBranchNumber -ShortName $branchSuffix -SpecsDir $specsDir
252258
} else {
253259
# Fall back to local directory check
254-
$highest = 0
255-
if (Test-Path $specsDir) {
256-
Get-ChildItem -Path $specsDir -Directory | ForEach-Object {
257-
if ($_.Name -match '^(\d{3})') {
258-
$num = [int]$matches[1]
259-
if ($num -gt $highest) { $highest = $num }
260-
}
261-
}
262-
}
263-
$Number = $highest + 1
260+
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
264261
}
265262
}
266263

scripts/powershell/update-agent-context.ps1

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ $NEW_PLAN = $IMPL_PLAN
4747
$CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
4848
$GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
4949
$COPILOT_FILE = Join-Path $REPO_ROOT '.github/agents/copilot-instructions.md'
50-
$COPILOT_AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
5150
$CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc'
5251
$QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
5352
$AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
@@ -371,10 +370,7 @@ function Update-SpecificAgent {
371370
switch ($Type) {
372371
'claude' { Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code' }
373372
'gemini' { Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI' }
374-
'copilot' {
375-
Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot'
376-
Update-AgentFile -TargetFile $COPILOT_AGENTS_FILE -AgentName 'GitHub Copilot (AGENTS.md)'
377-
}
373+
'copilot' { Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' }
378374
'cursor-agent' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' }
379375
'qwen' { Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code' }
380376
'opencode' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'opencode' }
@@ -396,8 +392,6 @@ function Update-AllExistingAgents {
396392
if (Test-Path $CLAUDE_FILE) { if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }; $found = $true }
397393
if (Test-Path $GEMINI_FILE) { if (-not (Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI')) { $ok = $false }; $found = $true }
398394
if (Test-Path $COPILOT_FILE) { if (-not (Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot')) { $ok = $false }; $found = $true }
399-
# Also update AGENTS.md for Copilot if the Copilot directory exists
400-
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 }
401395
if (Test-Path $CURSOR_FILE) { if (-not (Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE')) { $ok = $false }; $found = $true }
402396
if (Test-Path $QWEN_FILE) { if (-not (Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code')) { $ok = $false }; $found = $true }
403397
if (Test-Path $AGENTS_FILE) { if (-not (Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex/opencode')) { $ok = $false }; $found = $true }

0 commit comments

Comments
 (0)