create-new-feature.sh 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #!/usr/bin/env bash
  2. set -e
  3. JSON_MODE=false
  4. ARGS=()
  5. for arg in "$@"; do
  6. case "$arg" in
  7. --json) JSON_MODE=true ;;
  8. --help|-h) echo "Usage: $0 [--json] <feature_description>"; exit 0 ;;
  9. *) ARGS+=("$arg") ;;
  10. esac
  11. done
  12. FEATURE_DESCRIPTION="${ARGS[*]}"
  13. if [ -z "$FEATURE_DESCRIPTION" ]; then
  14. echo "Usage: $0 [--json] <feature_description>" >&2
  15. exit 1
  16. fi
  17. # Function to find the repository root by searching for existing project markers
  18. find_repo_root() {
  19. local dir="$1"
  20. while [ "$dir" != "/" ]; do
  21. if [ -d "$dir/.git" ] || [ -d "$dir/.specify" ]; then
  22. echo "$dir"
  23. return 0
  24. fi
  25. dir="$(dirname "$dir")"
  26. done
  27. return 1
  28. }
  29. # Resolve repository root. Prefer git information when available, but fall back
  30. # to searching for repository markers so the workflow still functions in repositories that
  31. # were initialised with --no-git.
  32. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  33. if git rev-parse --show-toplevel >/dev/null 2>&1; then
  34. REPO_ROOT=$(git rev-parse --show-toplevel)
  35. HAS_GIT=true
  36. else
  37. REPO_ROOT="$(find_repo_root "$SCRIPT_DIR")"
  38. if [ -z "$REPO_ROOT" ]; then
  39. echo "Error: Could not determine repository root. Please run this script from within the repository." >&2
  40. exit 1
  41. fi
  42. HAS_GIT=false
  43. fi
  44. cd "$REPO_ROOT"
  45. SPECS_DIR="$REPO_ROOT/specs"
  46. mkdir -p "$SPECS_DIR"
  47. HIGHEST=0
  48. if [ -d "$SPECS_DIR" ]; then
  49. for dir in "$SPECS_DIR"/*; do
  50. [ -d "$dir" ] || continue
  51. dirname=$(basename "$dir")
  52. number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
  53. number=$((10#$number))
  54. if [ "$number" -gt "$HIGHEST" ]; then HIGHEST=$number; fi
  55. done
  56. fi
  57. NEXT=$((HIGHEST + 1))
  58. FEATURE_NUM=$(printf "%03d" "$NEXT")
  59. BRANCH_NAME=$(echo "$FEATURE_DESCRIPTION" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
  60. WORDS=$(echo "$BRANCH_NAME" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//')
  61. BRANCH_NAME="${FEATURE_NUM}-${WORDS}"
  62. if [ "$HAS_GIT" = true ]; then
  63. git checkout -b "$BRANCH_NAME"
  64. else
  65. >&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
  66. fi
  67. FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
  68. mkdir -p "$FEATURE_DIR"
  69. TEMPLATE="$REPO_ROOT/.specify/templates/spec-template.md"
  70. SPEC_FILE="$FEATURE_DIR/spec.md"
  71. if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
  72. # Set the SPECIFY_FEATURE environment variable for the current session
  73. export SPECIFY_FEATURE="$BRANCH_NAME"
  74. if $JSON_MODE; then
  75. printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM"
  76. else
  77. echo "BRANCH_NAME: $BRANCH_NAME"
  78. echo "SPEC_FILE: $SPEC_FILE"
  79. echo "FEATURE_NUM: $FEATURE_NUM"
  80. echo "SPECIFY_FEATURE environment variable set to: $BRANCH_NAME"
  81. fi