From 67b91c0a9ee8915ae384682cc7b7d875703f4a01 Mon Sep 17 00:00:00 2001 From: Shinji Saito Date: Mon, 31 Aug 2026 14:07:05 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E3=83=97=E3=83=A9=E3=82=B0=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E8=BF=BD=E5=8A=A0=E7=A2=BA=E8=AA=8D=E3=81=AE=E3=83=97?= =?UTF-8?q?=E3=83=AD=E3=83=B3=E3=83=97=E3=83=88=E3=81=8C=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9A=E5=85=A5=E5=8A=9B=E5=BE=85=E3=81=A1?= =?UTF-8?q?=E3=81=A7=E5=81=9C=E6=AD=A2=E3=81=99=E3=82=8B=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=EF=BC=88v0.76.2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 対話更新で新規カタログのプラグインを 1 件ずつ確認する _offer_new_plugins_interactive に 2 つの表示不具合があった。 1. 質問が表示されない read -r -p "..." reply < /dev/tty 2>/dev/null の形で質問していた。 read -p はプロンプトを stderr に書くため、端末を開けなかった場合に 備えた 2>/dev/null が質問文そのものを破棄していた。 利用者にはプラグイン一覧だけが表示されて入力待ちになるため、停止した ように見える。先へ進めるために押した Enter は空回答として DISMISSED_PLUGINS に記録される。既定は No で一度断った項目は再提示 しない設計のため、質問を一度も見ないまま該当プラグインが恒久的に 拒否されていた。 キットの他の対話プロンプト(lib/deploy.sh:1376-1378、lib/merge.sh)と 同じ形に揃え、printf ... >&2 で質問を出力してから -p なしの read を 実行する。 2. プラグイン名に \033[1m がそのまま表示される printf " %s%s%s%s\n" "${BOLD:-}" ... と、BOLD / NC を %s の引数として 渡していた。両者はエスケープシーケンスをバックスラッシュ表記の文字列 として保持しており、printf が解釈するのは書式文字列に含まれる場合だけ なので、制御文字列が画面にそのまま出る。lib/colors.sh の他の出力と 同じく書式文字列側に置く。色出力が有効な端末でのみ発生する。 回帰テストを tests/unit/test-plugin-adoption.sh に 2 件追加した。修正前の コードでは 2 件とも失敗することを確認済み。 検証: - shellcheck -S warning: lib/plugin-adoption.sh / tests/unit/test-plugin-adoption.sh ともにクリーン - tests/unit/test-plugin-adoption.sh: 44/44 通過(修正前は 42/44) - tests/run-unit-tests.sh: 既存の fonts 失敗 1 件のみ。本変更に起因する失敗なし Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 8 +++++++ lib/plugin-adoption.sh | 17 +++++++++++--- tests/unit/test-plugin-adoption.sh | 36 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90fae83..0938362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [0.76.2] - 2026-08-31 + +### Fixed +- **新規プラグインの追加確認が、質問を表示しないまま入力待ちで止まる問題を修正**: 対話更新でカタログに追加されたプラグインを 1 件ずつ確認する `_offer_new_plugins_interactive` が、`read -r -p "..." reply < /dev/tty 2>/dev/null` の形で質問していた。`read -p` はプロンプトを stderr に書くため、端末を開けなかった場合に備えて付けていた `2>/dev/null` が質問文そのものを破棄していた。画面にはプラグイン一覧だけが出て入力待ちで停止するため停止しているように見え、先に進めるために押した Enter は空回答として `DISMISSED_PLUGINS` に記録される。既定は No で、一度断った項目は再提示されない設計のため、質問を一度も見ないまま該当プラグインが恒久的に拒否されていた。キットの他の対話プロンプト(`lib/deploy.sh` / `lib/merge.sh`)と同じ形に揃え、`printf ... >&2` で質問を出力してから `-p` なしの `read` を実行するようにした + - **既に拒否記録が残っている環境の復旧**: `~/.claude-starter-kit.conf` の `DISMISSED_PLUGINS` と `KNOWN_PLUGINS` の両方から該当エントリを削除すると、次回の対話更新で再度提示される。`_compute_new_plugins` は SELECTED / DISMISSED / KNOWN のいずれかに含まれる項目を除外するため、`DISMISSED_PLUGINS` だけを消しても再提示されない。Claude Code 内で `/plugin install @` を直接実行する方法も従来どおり使える +- **プラグイン一覧に `\033[1m` が制御文字列のまま表示される問題を修正**: 同じ関数がプラグイン名を `printf " %s%s%s%s\n" "${BOLD:-}" ...` と出力していた。`BOLD` / `NC` はエスケープシーケンスをバックスラッシュ表記の文字列として保持しており、printf が解釈するのは書式文字列に含まれる場合だけなので、`%s` の引数として渡すと画面にそのまま出る。色出力が有効な端末で発生する(`lib/colors.sh` は stdout が端末のときのみ色を設定するため、リダイレクト時は空文字列になり影響しない)。`lib/colors.sh` の他の出力と同じく書式文字列側に置くよう変更した +- 上記 2 点の回帰テストを `tests/unit/test-plugin-adoption.sh` に追加した。修正前のコードでは 2 件とも失敗する + ## [0.76.0] - 2026-08-24 ### Added diff --git a/lib/plugin-adoption.sh b/lib/plugin-adoption.sh index 9fb537a..fec617c 100644 --- a/lib/plugin-adoption.sh +++ b/lib/plugin-adoption.sh @@ -414,10 +414,21 @@ _offer_new_plugins_interactive() { [[ -n "$entry" ]] || continue local desc desc="$(_plugin_description "$entry")" - printf " %s%s%s%s\n" "${BOLD:-}" "$entry" "${NC:-}" "${desc:+ — $desc}" + # BOLD/NC carry escape sequences as literal backslash text, so they only + # render when printf interprets them in the FORMAT string. Passing them as + # %s arguments printed a raw "\033[1m" instead of bold. + # shellcheck disable=SC2059 # BOLD/NC are kit-owned escape constants + printf " ${BOLD:-}%s${NC:-}%s\n" "$entry" "${desc:+ — $desc}" + # Print the question separately: `read -p` writes its prompt to stderr, so + # the `2>/dev/null` that guards an unreadable terminal swallowed the prompt + # itself. The run then looked like a hang, and the Enter a user pressed to + # get past it was recorded as a permanent dismissal. Same shape as the + # kit's other post-wizard prompts (lib/deploy.sh, lib/merge.sh). # shellcheck disable=SC2059 # STR_* carries the %s placeholder - if ! read -r -p "$(printf "${STR_NEW_PLUGINS_ASK:-Add %s? [y/N]}" "$entry") " \ - reply < "${_TTY_INPUT:-/dev/tty}" 2>/dev/null; then + printf "${STR_NEW_PLUGINS_ASK:-Add %s? [y/N]} " "$entry" >&2 + reply="" + if ! read -r reply < "${_TTY_INPUT:-/dev/tty}" 2>/dev/null; then + printf "\n" >&2 return 2 fi case "$reply" in diff --git a/tests/unit/test-plugin-adoption.sh b/tests/unit/test-plugin-adoption.sh index 67520de..a29d1a6 100644 --- a/tests/unit/test-plugin-adoption.sh +++ b/tests/unit/test-plugin-adoption.sh @@ -280,6 +280,42 @@ else fail "plugin-adoption: blank answer must not install (got '$_pa_out')" fi +# ── prompt visibility ────────────────────────────────────────────────────── +# +# The question is what makes an offer an offer. `read -p` writes its prompt to +# stderr, so the `2>/dev/null` that guards an unreadable terminal swallowed the +# prompt itself: the run looked like a hang, and the Enter someone pressed to +# get past it was recorded as a permanent dismissal. +_pa_out="$(_pa_run ' + _MERGE_INTERACTIVE="true" + printf "n\n" > "'"$_pa_tmp"'/answer-visible" + _TTY_INPUT="'"$_pa_tmp"'/answer-visible" + SELECTED_PLUGINS="alpha" + mkdir -p "'"$_pa_tmp"'/home7" + _detect_and_offer_new_plugins "'"$_pa_tmp"'/home7" 2>&1 >/dev/null')" +if [[ "$_pa_out" == *"gamma@other-mp"*"[y/N]"* ]]; then + pass "plugin-adoption: the offer prompt reaches the user" +else + fail "plugin-adoption: the [y/N] prompt must not be swallowed (got '$_pa_out')" +fi + +# BOLD/NC carry escape sequences as literal backslash text. printf renders them +# only from the format string; passed as %s arguments they print as raw +# "\033[1m" in the middle of the plugin list. +_pa_out="$(_pa_run ' + _MERGE_INTERACTIVE="true" + BOLD="\033[1m"; NC="\033[0m" + printf "n\n" > "'"$_pa_tmp"'/answer-bold" + _TTY_INPUT="'"$_pa_tmp"'/answer-bold" + SELECTED_PLUGINS="alpha" + mkdir -p "'"$_pa_tmp"'/home8" + _detect_and_offer_new_plugins "'"$_pa_tmp"'/home8" 2>/dev/null')" +if [[ "$_pa_out" != *'\033['* ]]; then + pass "plugin-adoption: the plugin line renders escapes instead of printing them" +else + fail "plugin-adoption: BOLD/NC must not reach the terminal literally (got '$_pa_out')" +fi + # ── CSV validation ───────────────────────────────────────────────────────── # # These CSVs gate an install offer, so a corrupted or hand-edited value is From 8a0eb0cfe6b3466d2b66550341ee522fbd5a2bbe Mon Sep 17 00:00:00 2001 From: okash1n <48118431+okash1n@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:44:26 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20BOLD/NC=20=E5=9B=9E=E5=B8=B0?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=AB=E6=AD=A3=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=83=B3=E3=82=AB=E3=83=BC=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97?= =?UTF-8?q?=E7=A9=BA=E5=87=BA=E5=8A=9B=E3=81=A7=E3=81=AE=E5=81=BD=20PASS?= =?UTF-8?q?=20=E3=82=92=E9=98=B2=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/test-plugin-adoption.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-plugin-adoption.sh b/tests/unit/test-plugin-adoption.sh index a29d1a6..896a255 100644 --- a/tests/unit/test-plugin-adoption.sh +++ b/tests/unit/test-plugin-adoption.sh @@ -310,10 +310,10 @@ _pa_out="$(_pa_run ' SELECTED_PLUGINS="alpha" mkdir -p "'"$_pa_tmp"'/home8" _detect_and_offer_new_plugins "'"$_pa_tmp"'/home8" 2>/dev/null')" -if [[ "$_pa_out" != *'\033['* ]]; then +if [[ "$_pa_out" == *"gamma@other-mp"* && "$_pa_out" != *'\033['* ]]; then pass "plugin-adoption: the plugin line renders escapes instead of printing them" else - fail "plugin-adoption: BOLD/NC must not reach the terminal literally (got '$_pa_out')" + fail "plugin-adoption: the plugin line must appear without literal BOLD/NC (got '$_pa_out')" fi # ── CSV validation ─────────────────────────────────────────────────────────