-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Feature/configure branch name #1009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ce2c497
7fdc1f4
18cb593
0951aa9
3b39bf4
f891e52
a1c746b
112be12
d960d32
13e9f64
e14f0dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,27 @@ All notable changes to the Specify CLI and templates are documented here. | |||||||||||||||||||||||||||||||||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||||||||||||||||||||||||||||||||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## [Unreleased] | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## [0.0.23] - 2025-10-22 / 2025-11-23 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### Added | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - **Configurable Branch Prefixes**: Branch names can now be prefixed with custom patterns (e.g., `feature/`, `bugfix/`) | ||||||||||||||||||||||||||||||||||
| - New `.specify/config.json` configuration file with `branch.prefix` setting | ||||||||||||||||||||||||||||||||||
| - Environment variable `SPECIFY_BRANCH_PREFIX` for per-session overrides | ||||||||||||||||||||||||||||||||||
| - **Per-feature override**: `--branch-prefix` / `-BranchPrefix` parameter for `create-new-feature` scripts | ||||||||||||||||||||||||||||||||||
| - Priority order: Command-line parameter > Environment variable > Config file > Default (no prefix) | ||||||||||||||||||||||||||||||||||
| - Automatically created during project initialization via `specify init` | ||||||||||||||||||||||||||||||||||
| - Examples: | ||||||||||||||||||||||||||||||||||
| - With `"prefix": "feature/"`: `001-user-auth` → `feature/001-user-auth` | ||||||||||||||||||||||||||||||||||
| - With `"prefix": "bugfix/"`: `001-fix-login` → `bugfix/001-fix-login` | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+17
to
+24
|
||||||||||||||||||||||||||||||||||
| - New `.specify/config.json` configuration file with `branch.prefix` setting | |
| - Environment variable `SPECIFY_BRANCH_PREFIX` for per-session overrides | |
| - **Per-feature override**: `--branch-prefix` / `-BranchPrefix` parameter for `create-new-feature` scripts | |
| - Priority order: Command-line parameter > Environment variable > Config file > Default (no prefix) | |
| - Automatically created during project initialization via `specify init` | |
| - Examples: | |
| - With `"prefix": "feature/"`: `001-user-auth` → `feature/001-user-auth` | |
| - With `"prefix": "bugfix/"`: `001-fix-login` → `bugfix/001-fix-login` | |
| - New `.specify/config.json` configuration file with `branch_prefix` setting | |
| - Environment variable `SPECIFY_BRANCH_PREFIX` for per-session overrides | |
| - **Per-feature override**: `--branch-prefix` / `-BranchPrefix` parameter for `create-new-feature` scripts | |
| - Priority order: Command-line parameter > Environment variable > Config file > Default (no prefix) | |
| - Automatically created during project initialization via `specify init` | |
| - Examples: | |
| - With `"branch_prefix": "feature/"`: `001-user-auth` → `feature/001-user-auth` | |
| - With `"branch_prefix": "bugfix/"`: `001-fix-login` → `bugfix/001-fix-login` |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -256,6 +256,70 @@ Additional commands for enhanced quality and validation: | |||||
| | Variable | Description | | ||||||
| |------------------|------------------------------------------------------------------------------------------------| | ||||||
| | `SPECIFY_FEATURE` | Override feature detection for non-Git repositories. Set to the feature directory name (e.g., `001-photo-albums`) to work on a specific feature when not using Git branches.<br/>**Must be set in the context of the agent you're working with prior to using `/speckit.plan` or follow-up commands. | | ||||||
| | `SPECIFY_BRANCH_PREFIX` | Configure a prefix for git branch names (e.g., `feature/`, `bugfix/`). When set, this prefix is prepended to auto-generated branch names. Overrides the `branch.prefix` setting in `.specify/config.json`. Example: With prefix `feature/`, branch `001-user-auth` becomes `feature/001-user-auth`. | | ||||||
| | `SPECIFY_SPEC_NUMBER` | Override the auto-incremented spec number with a custom value (e.g., to match an issue tracker number). When set, the specified number is used instead of finding the next available number. Example: `SPECIFY_SPEC_NUMBER=42` creates spec `042-feature-name`. Can be overridden per-feature using the `--number` parameter in `/speckit.specify`. | | ||||||
|
|
||||||
| ### Configuration File | ||||||
|
|
||||||
| The `.specify/config.json` file allows you to configure project-specific settings. This file is automatically created when you initialize a new project with `specify init`. | ||||||
|
|
||||||
| #### Branch Prefix Configuration | ||||||
|
|
||||||
| You can configure a default branch prefix for your project that will be applied to all auto-generated branch names: | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "branch": { | ||||||
| "prefix": "feature/" | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| **Common patterns:** | ||||||
|
|
||||||
| - **Feature branches:** `"prefix": "feature/"` → Creates branches like `feature/001-user-auth` | ||||||
| - **Bugfix branches:** `"prefix": "bugfix/"` → Creates branches like `bugfix/001-fix-login` | ||||||
| - **Development branches:** `"prefix": "dev/"` → Creates branches like `dev/001-new-api` | ||||||
| - **No prefix (default):** `"prefix": ""` → Creates branches like `001-user-auth` | ||||||
|
|
||||||
| **Priority order:** | ||||||
|
|
||||||
| 1. `--branch-prefix` command-line parameter (highest priority, per-feature override) | ||||||
| 2. `SPECIFY_BRANCH_PREFIX` environment variable (per-session override) | ||||||
| 3. `.specify/config.json` file setting (project-wide default) | ||||||
| 4. Default: no prefix (empty string) | ||||||
|
|
||||||
| This allows you to set project-wide defaults in the config file, override them per-session using the environment variable, or specify them per-feature when creating a new specification. | ||||||
|
|
||||||
| **Per-feature branch prefix:** | ||||||
|
|
||||||
| When using the `/speckit.specify` command, you can specify a branch prefix for that specific feature: | ||||||
|
|
||||||
| ```text | ||||||
| /speckit.specify Add user authentication --branch-prefix feature/ | ||||||
| /speckit.specify Fix login timeout --branch-prefix bugfix/ | ||||||
| /speckit.specify Update API endpoints with prefix hotfix/ | ||||||
| ``` | ||||||
|
|
||||||
| The AI agent will recognize the prefix specification and pass it to the `create-new-feature` script. | ||||||
|
|
||||||
| **Per-feature spec number:** | ||||||
|
|
||||||
| You can also specify a custom spec number to match your issue tracker: | ||||||
|
|
||||||
| ```text | ||||||
| /speckit.specify Add user authentication --number 42 | ||||||
| /speckit.specify Fix payment bug for issue #123 --number 123 | ||||||
| /speckit.specify Implement search API --number 1234 --branch-prefix feature/ | ||||||
| ``` | ||||||
|
|
||||||
| **Spec number priority:** | ||||||
|
|
||||||
| 1. `--number` command-line parameter (highest priority, per-feature override) | ||||||
| 2. `SPECIFY_SPEC_NUMBER` environment variable (per-session override) | ||||||
| 3. Auto-increment from existing specs (default) | ||||||
|
|
||||||
| This allows you to align spec numbers with your issue tracker (GitHub Issues, Jira, Linear, etc.) while maintaining the structured workflow. | ||||||
|
|
||||||
| ## 📚 Core Philosophy | ||||||
|
|
||||||
|
|
@@ -393,6 +457,8 @@ With your project principles established, you can now create the functional spec | |||||
| >[!IMPORTANT] | ||||||
| >Be as explicit as possible about *what* you are trying to build and *why*. **Do not focus on the tech stack at this point**. | ||||||
|
|
||||||
| #### Basic Example | ||||||
|
|
||||||
| An example prompt: | ||||||
|
|
||||||
| ```text | ||||||
|
|
@@ -414,6 +480,32 @@ see yours. You can edit any comments that you make, but you can't edit comments | |||||
| delete any comments that you made, but you can't delete comments anybody else made. | ||||||
| ``` | ||||||
|
|
||||||
| #### Issue Tracker Integration | ||||||
|
|
||||||
| You can align spec numbers with your issue tracker (GitHub Issues, Jira, Linear, etc.) by specifying a custom spec number: | ||||||
|
|
||||||
| ```text | ||||||
| /speckit.specify Add user authentication system --number 42 | ||||||
| ``` | ||||||
|
|
||||||
| This creates spec `042-add-user-auth` matching issue #42 in your tracker. You can also combine with branch prefixes: | ||||||
|
|
||||||
| ```text | ||||||
| /speckit.specify Fix payment processing timeout --number 123 --branch-prefix bugfix/ | ||||||
| ``` | ||||||
|
|
||||||
| This creates: | ||||||
| - Spec directory: `specs/123-fix-payment-timeout/` | ||||||
|
||||||
| - Spec directory: `specs/123-fix-payment-timeout/` | |
| - Spec directory: `specs/bugfix/123-fix-payment-timeout/` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,8 +37,9 @@ get_current_branch() { | |
| 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]} | ||
| # Support both formats: 001-name or feature/001-name | ||
| if [[ "$dirname" =~ ^(([a-z]+/)?([0-9]{3,}))- ]]; then | ||
| local number=${BASH_REMATCH[3]} | ||
| number=$((10#$number)) | ||
| if [[ "$number" -gt "$highest" ]]; then | ||
| highest=$number | ||
|
|
@@ -72,9 +73,13 @@ check_feature_branch() { | |
| return 0 | ||
| fi | ||
|
|
||
| if [[ ! "$branch" =~ ^[0-9]{3}- ]]; then | ||
| # Support both simple format (001-name) and prefixed format (feature/001-name) | ||
| if [[ ! "$branch" =~ ^([a-z]+/)?[0-9]{3,}- ]]; then | ||
| echo "ERROR: Not on a feature branch. Current branch: $branch" >&2 | ||
| echo "Feature branches should be named like: 001-feature-name" >&2 | ||
| echo "Feature branches should be named like:" >&2 | ||
| echo " - 001-feature-name" >&2 | ||
| echo " - feature/001-feature-name" >&2 | ||
| echo " - bugfix/042-fix-name" >&2 | ||
| return 1 | ||
| fi | ||
|
|
||
|
|
@@ -85,28 +90,34 @@ get_feature_dir() { echo "$1/specs/$2"; } | |
|
|
||
| # Find feature directory by numeric prefix instead of exact branch match | ||
| # This allows multiple branches to work on the same spec (e.g., 004-fix-bug, 004-add-feature) | ||
| # Also handles branch names with prefixes like feature/004-name or bugfix/042-fix | ||
| find_feature_dir_by_prefix() { | ||
| local repo_root="$1" | ||
| local branch_name="$2" | ||
| local specs_dir="$repo_root/specs" | ||
|
|
||
| # Extract numeric prefix from branch (e.g., "004" from "004-whatever") | ||
| if [[ ! "$branch_name" =~ ^([0-9]{3})- ]]; then | ||
| # Extract numeric prefix from branch (e.g., "004" from "004-whatever" or "feature/004-whatever") | ||
| # Pattern: optional prefix (feature/, bugfix/, etc.) followed by at least 3 digits | ||
| if [[ ! "$branch_name" =~ ^(([a-z]+/)?([0-9]{3,}))- ]]; then | ||
| # If branch doesn't have numeric prefix, fall back to exact match | ||
| echo "$specs_dir/$branch_name" | ||
| return | ||
| fi | ||
|
|
||
| local prefix="${BASH_REMATCH[1]}" | ||
| local number="${BASH_REMATCH[3]}" # Just the numeric part | ||
|
|
||
| # Search for directories in specs/ that start with this prefix | ||
| # Search for directories in specs/ that contain this number | ||
| # Could be in format: 004-name or feature/004-name or bugfix/004-name | ||
| local matches=() | ||
| if [[ -d "$specs_dir" ]]; then | ||
| for dir in "$specs_dir"/"$prefix"-*; do | ||
| if [[ -d "$dir" ]]; then | ||
| matches+=("$(basename "$dir")") | ||
| # Use find to search more precisely - avoid glob matching issues | ||
| while IFS= read -r -d '' dir; do | ||
| local dirname=$(basename "$dir") | ||
| # Verify it actually matches our pattern: starts with optional prefix/ then number- | ||
| if [[ "$dirname" =~ ^(([a-z]+/)?$number)- ]]; then | ||
| matches+=("$dirname") | ||
| fi | ||
| done | ||
| done < <(find "$specs_dir" -mindepth 1 -maxdepth 1 -type d -print0) | ||
|
Comment on lines
+113
to
+120
|
||
| fi | ||
|
|
||
| # Handle results | ||
|
|
@@ -118,7 +129,7 @@ find_feature_dir_by_prefix() { | |
| echo "$specs_dir/${matches[0]}" | ||
| else | ||
| # Multiple matches - this shouldn't happen with proper naming convention | ||
| echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2 | ||
| echo "ERROR: Multiple spec directories found with number '$number': ${matches[*]}" >&2 | ||
| echo "Please ensure only one spec directory exists per numeric prefix." >&2 | ||
| echo "$specs_dir/$branch_name" # Return something to avoid breaking the script | ||
| fi | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ set -e | |||||||||
|
|
||||||||||
| JSON_MODE=false | ||||||||||
| SHORT_NAME="" | ||||||||||
| BRANCH_PREFIX_ARG="" | ||||||||||
| BRANCH_NUMBER="" | ||||||||||
| ARGS=() | ||||||||||
| i=1 | ||||||||||
|
|
@@ -27,6 +28,20 @@ while [ $i -le $# ]; do | |||||||||
| fi | ||||||||||
| SHORT_NAME="$next_arg" | ||||||||||
| ;; | ||||||||||
| --branch-prefix) | ||||||||||
| if [ $((i + 1)) -gt $# ]; then | ||||||||||
| echo 'Error: --branch-prefix requires a value' >&2 | ||||||||||
| exit 1 | ||||||||||
| fi | ||||||||||
| i=$((i + 1)) | ||||||||||
| next_arg="${!i}" | ||||||||||
| # Check if the next argument is another option (starts with --) | ||||||||||
| if [[ "$next_arg" == --* ]]; then | ||||||||||
| echo 'Error: --branch-prefix requires a value' >&2 | ||||||||||
| exit 1 | ||||||||||
| fi | ||||||||||
| BRANCH_PREFIX_ARG="$next_arg" | ||||||||||
| ;; | ||||||||||
| --number) | ||||||||||
| if [ $((i + 1)) -gt $# ]; then | ||||||||||
| echo 'Error: --number requires a value' >&2 | ||||||||||
|
|
@@ -41,17 +56,20 @@ while [ $i -le $# ]; do | |||||||||
| BRANCH_NUMBER="$next_arg" | ||||||||||
| ;; | ||||||||||
| --help|-h) | ||||||||||
| echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>" | ||||||||||
| echo "Usage: $0 [--json] [--short-name <name>] [--branch-prefix <prefix>] [--number N] <feature_description>" | ||||||||||
| echo "" | ||||||||||
| echo "Options:" | ||||||||||
| echo " --json Output in JSON format" | ||||||||||
| echo " --short-name <name> Provide a custom short name (2-4 words) for the branch" | ||||||||||
| echo " --number N Specify branch number manually (overrides auto-detection)" | ||||||||||
| echo " --help, -h Show this help message" | ||||||||||
| echo " --json Output in JSON format" | ||||||||||
| echo " --short-name <name> Provide a custom short name (2-4 words) for the branch" | ||||||||||
| echo " --branch-prefix <prefix> Override branch prefix (e.g., 'feature/', 'bugfix/')" | ||||||||||
| echo " --number N Specify branch number manually (overrides auto-detection)" | ||||||||||
| echo " --help, -h Show this help message" | ||||||||||
| echo "" | ||||||||||
| echo "Examples:" | ||||||||||
| echo " $0 'Add user authentication system' --short-name 'user-auth'" | ||||||||||
| echo " $0 'Implement OAuth2 integration for API' --number 5" | ||||||||||
| echo " $0 'Fix login bug' --branch-prefix 'bugfix/'" | ||||||||||
| echo " $0 'Add payment processing' --number 42 --branch-prefix 'feature/'" | ||||||||||
| exit 0 | ||||||||||
| ;; | ||||||||||
| *) | ||||||||||
|
|
@@ -245,7 +263,10 @@ fi | |||||||||
|
|
||||||||||
| # Determine branch number | ||||||||||
| if [ -z "$BRANCH_NUMBER" ]; then | ||||||||||
| if [ "$HAS_GIT" = true ]; then | ||||||||||
| # Check environment variable first | ||||||||||
| if [ -n "$SPECIFY_SPEC_NUMBER" ]; then | ||||||||||
| BRANCH_NUMBER="$SPECIFY_SPEC_NUMBER" | ||||||||||
| elif [ "$HAS_GIT" = true ]; then | ||||||||||
| # Check existing branches on remotes | ||||||||||
| BRANCH_NUMBER=$(check_existing_branches "$BRANCH_SUFFIX" "$SPECS_DIR") | ||||||||||
| else | ||||||||||
|
|
@@ -256,23 +277,68 @@ if [ -z "$BRANCH_NUMBER" ]; then | |||||||||
| fi | ||||||||||
|
|
||||||||||
| FEATURE_NUM=$(printf "%03d" "$BRANCH_NUMBER") | ||||||||||
| BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}" | ||||||||||
|
|
||||||||||
| # Function to determine branch prefix | ||||||||||
| get_branch_prefix() { | ||||||||||
| # Priority: CLI arg > environment variable > config file > empty string | ||||||||||
| if [ -n "$BRANCH_PREFIX_ARG" ]; then | ||||||||||
| echo "$BRANCH_PREFIX_ARG" | ||||||||||
| return | ||||||||||
| fi | ||||||||||
|
|
||||||||||
| if [ -n "$SPECIFY_BRANCH_PREFIX" ]; then | ||||||||||
| echo "$SPECIFY_BRANCH_PREFIX" | ||||||||||
| return | ||||||||||
| fi | ||||||||||
|
|
||||||||||
| # Check config file | ||||||||||
| local config_file="$REPO_ROOT/.specify/config.json" | ||||||||||
| if [ -f "$config_file" ]; then | ||||||||||
| # Extract branch_prefix from config (avoid jq dependency) | ||||||||||
| local branch_prefix=$(grep '"branch_prefix"' "$config_file" | sed 's/.*"branch_prefix"[^"]*"\([^"]*\)".*/\1/') | ||||||||||
|
Comment on lines
+297
to
+298
|
||||||||||
| # Extract branch_prefix from config (avoid jq dependency) | |
| local branch_prefix=$(grep '"branch_prefix"' "$config_file" | sed 's/.*"branch_prefix"[^"]*"\([^"]*\)".*/\1/') | |
| # Extract branch prefix from config (avoid jq dependency) | |
| local branch_prefix=$(grep -A 10 '"branch"' "$config_file" | grep '"prefix"' | sed 's/.*"prefix"[^"]*"\([^"]*\)".*/\1/') |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The local keyword is used outside a function scope. This variable declaration is in the main script body (inside an if block), not within a function. Remove the local keyword or the script will fail with a syntax error.
| local prefix_length=${#BRANCH_PREFIX} | |
| prefix_length=${#BRANCH_PREFIX} |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using local keyword outside of a function is a bash extension and may not work in POSIX-compliant shells. Since this is already at the top level of the script (not inside get_branch_prefix function), this should just be prefix_length=${#BRANCH_PREFIX} without the local keyword.
| local prefix_length=${#BRANCH_PREFIX} | |
| prefix_length=${#BRANCH_PREFIX} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version date appears to be in the future: "2025-10-22 / 2025-11-23". Given that my knowledge cutoff is January 2025 and the current date is November 2025, the first date (October 2025) would be in the past, but the dual-date format is unusual. This should likely be a single date in the format YYYY-MM-DD. Please verify the correct release date.