github / github/spec-kit

[Bug] create-new-feature.sh uses ${word^^} (bash 4+) and silently drops acronyms on macOS (bash 3.2)

オープン 初心者向け
#3,927 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る
bug-assess invalid
主要言語
Python
スター
137k
フォーク
12.3k
平均マージ
2日 12時間
マージ済み PR(30日)
159

説明

## Summary

`create-new-feature.sh` (line 231) uses `${word^^}`, a **bash 4+ uppercase expansion**. macOS ships bash 3.2.57 (Apple's last GPLv2 build) and the script **fails silently** on that line: stderr is full of `bad substitution` warnings, the script keeps running, and the generated branch name silently **drops acronyms** that were short (≤2 chars) and uppercase in the original description.

Because the script still emits valid JSON and the resulting branch name is plausible, end users on macOS get a quietly truncated branch without realizing it.

## Environment

- macOS (any current version; bash 3.2.57 has been the system bash on every macOS since 2007)
- Any git repo initialised with `specify init`
- No `bash` from Homebrew installed (or installed but not first in `PATH` for the running shell)

```sh
$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin24)

$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run \
"Transparencia Art. 50 AI Act: aviso de interaccion con IA"
{"BRANCH_NAME":"001-transparencia-art-act", ...} # ← missing "AI"

$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run "AI Act transparency"
{"BRANCH_NAME":"001-act-transparency", ...} # ← missing "AI" (top-3 cutoff also affected)
bad substitution # (only visible in stderr; the JSON looks fine)
```

The same inputs against bash 5.x preserve the acronym:

```sh
$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run "AI Act transparency"
{"BRANCH_NAME":"001-ai-act-transparency", ...} # ← "ai" preserved
```

## Root cause

`create-new-feature.sh`, line 231:

```sh
elif echo "$description" | grep -q "\b${word^^}\b"; then
# Keep short words if they appear as uppercase in original (likely acronyms)
meaningful_words+=("$word")
fi
```

`${word^^}` is case-conversion syntax introduced in bash 4.0 (2009). It is not available in bash 3.2. On bash 3.2 the line errors out, `grep` runs with the literal placeholder string, the grep returns non-zero, the elif is false, and the short word is silently dropped from `meaningful_words[]`. The same `${var^^}` / `${var,,}` pattern likely exists in other vendored scripts; I have not audited them all.

The shebang `#!/usr/bin/env bash` does not protect against this — `env` picks whatever `bash` resolves to in the current `PATH`, and on a stock macOS that is the system's bash 3.2.

## Suggested fix

Two options, in order of preference:

1. **POSIX-portable** — replace `${word^^}` with a portable uppercase conversion, e.g. `echo "$word" | tr '[:lower:]' '[:upper:]'`, so the script works on bash 3.2 too. This is a one-line change per occurrence and removes the silent failure mode entirely.
2. **Bash 4+ required** — if the project decides to require bash 4+ intentionally, make the failure loud: at the top of every vendored script, add a runtime check that fails fast with a clear message and a hint to install bash from Homebrew (`brew install bash`). Currently the failure is silent and the user has no way to know their branch name is wrong.

I'd recommend option 1 (POSIX-portable) — the function is a small, isolated transformation and the project already has a `clean_branch_name` that uses `tr '[:upper:]' '[:lower:]'` to do the inverse. Symmetry argues for replacing the bash-only expansion with `tr` as well.

## Workaround on macOS today

Install bash 5.x via Homebrew and ensure it is first in `PATH` for the shell that runs Spec-Kit:

```sh
brew install bash
# ~/.zshrc or ~/.bashrc, idempotent:
case "$PATH" in
/opt/homebrew/bin:*) ;;
/opt/homebrew/bin) ;;
*) export PATH="/opt/homebrew/bin${PATH:+:$PATH}" ;;
esac
```

Note that on login shells, `/etc/zprofile` runs `path_helper` which re-derives `PATH` from `/etc/paths` + `/etc/paths.d/*` and puts `/opt/homebrew/bin` at the *end* — so the prefix must be re-asserted in a file that runs after `/etc/zprofile` (e.g. `~/.zshrc`).

## Impact

This affects every macOS developer or agent using Spec-Kit with a description containing acronyms (e.g. "AI Act", "RGPD", "GDPR", "API", "OAuth", "HIPAA", "PCI"). The "feature name" used downstream in `specs/NNN-short-name/spec.md` is the same truncated string, so the spec file itself also loses the acronym, and any tooling that relies on the branch/spec name to understand the feature (e.g. CI labels, dashboards, search) gets the wrong subject.

Discovered while piloting Spec-Kit on ada-legal-ai (PR #11 in the HOILAB org). Same root cause likely affects any other team running Spec-Kit on macOS.

コントリビューションガイド

コントリビューションガイドを開く

調査の方向性

.specify/scripts/bash/create-new-feature.sh の 231 行目付近から始めて、アクロニムのブランチ名パスを調べてください。macOS の /bin/bash 3.2 で提供されている --json --dry-run コマンドを実行し、Bash 5.x と比較してください。短い大文字のアクロニムが BRANCH_NAME に残り、stderr に不正な置換の警告がないことが完了の条件です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
bash
領域
cli, tooling
issue の種類
バグ
難易度
2/5
見積もり時間
1〜3時間
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
84/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。