From b6d97bcc0b9a91f8b9db8bc3c90bef7c5831635e Mon Sep 17 00:00:00 2001 From: che cheng Date: Thu, 7 May 2026 13:41:59 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20=E6=8A=BD=E5=87=BA=20check-plug?= =?UTF-8?q?in-presence.sh=20generic=20helper=20+=20check-ralph-loop.sh=20?= =?UTF-8?q?=E8=AE=8A=20wrapper=20(Refs=20#34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scripts/check-plugin-presence.sh | 72 +++++++++++++++++++ .../scripts/check-ralph-loop.sh | 58 ++++----------- 2 files changed, 86 insertions(+), 44 deletions(-) create mode 100755 plugins/issue-driven-dev/scripts/check-plugin-presence.sh diff --git a/plugins/issue-driven-dev/scripts/check-plugin-presence.sh b/plugins/issue-driven-dev/scripts/check-plugin-presence.sh new file mode 100755 index 0000000..8c0686c --- /dev/null +++ b/plugins/issue-driven-dev/scripts/check-plugin-presence.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# check-plugin-presence.sh — generic Claude Code plugin presence detector +# +# Usage: +# check-plugin-presence.sh +# +# Examples: +# check-plugin-presence.sh claude-plugins-official ralph-loop +# check-plugin-presence.sh psychquant-claude-plugins che-word-mcp +# +# Used by: +# - idd-issue Step 1 Source Type Adapter (#27 fail-fast for .docx / Telegram / Mail / Notes) +# - scripts/check-ralph-loop.sh (wrapper for backward-compat from #28) +# +# Exit: +# 0 — plugin installed (or IDD_SKIP_PLUGIN_CHECK=1) +# 1 — plugin missing +# 2 — usage error (wrong arg count) +# +# Detect path is hardcoded against Claude Code 2025-Q4 plugin cache schema. +# When schema changes upstream, see #35 (path schema watch-list). + +set -u + +# Usage check +if [ $# -ne 2 ]; then + cat >&2 < + +Examples: + $(basename "$0") claude-plugins-official ralph-loop + $(basename "$0") psychquant-claude-plugins che-word-mcp +EOF + exit 2 +fi + +MARKETPLACE="$1" +PLUGIN="$2" + +# Escape hatch — let user bypass detect (#28 risk mitigation, #27 inheritance). +# Print stderr warning so this leaves an audit trail. +if [ "${IDD_SKIP_PLUGIN_CHECK:-}" = "1" ]; then + echo "⚠ IDD_SKIP_PLUGIN_CHECK=1 set — skipping ${MARKETPLACE}/${PLUGIN} detection (advanced override)" >&2 + echo " Caller will run as if ${PLUGIN} is installed." >&2 + exit 0 +fi + +# Plugin cache layout (Claude Code 2025-Q4): +# ~/.claude/plugins/cache////.claude-plugin/plugin.json +# Match any installed version under the plugin dir. +# +# Use bash array + nullglob (NOT unquoted `for f in $GLOB`) to handle HOME with +# whitespace correctly (per #28 F3 verify finding). +shopt -s nullglob +files=( "${HOME}/.claude/plugins/cache/${MARKETPLACE}/${PLUGIN}"/*/.claude-plugin/plugin.json ) +shopt -u nullglob + +if (( ${#files[@]} > 0 )); then + exit 0 +fi + +cat >&2 </${MARKETPLACE} + claude plugin install ${PLUGIN}@${MARKETPLACE} + + Or bypass this check (advanced): export IDD_SKIP_PLUGIN_CHECK=1 +EOF +exit 1 diff --git a/plugins/issue-driven-dev/scripts/check-ralph-loop.sh b/plugins/issue-driven-dev/scripts/check-ralph-loop.sh index 784a386..8497bbd 100755 --- a/plugins/issue-driven-dev/scripts/check-ralph-loop.sh +++ b/plugins/issue-driven-dev/scripts/check-ralph-loop.sh @@ -1,52 +1,22 @@ #!/usr/bin/env bash -# check-ralph-loop.sh — detect ralph-loop plugin presence +# check-ralph-loop.sh — backward-compat wrapper around check-plugin-presence.sh # -# Used by: -# - idd-verify --loop (Step 0a fail-fast) -# - idd-all Phase 0.6 (graceful degrade gate) +# v2.54+ (#34 refactor): the actual detection logic moved to +# scripts/check-plugin-presence.sh which accepts . +# This wrapper is preserved so #28's existing callers continue to work: +# - idd-verify --loop Step 0a fail-fast +# - idd-all Phase 0.6 graceful degrade gate # -# Exit: -# 0 — ralph-loop installed (or IDD_SKIP_RALPH_CHECK=1) -# 1 — ralph-loop missing +# Both `IDD_SKIP_RALPH_CHECK=1` and `IDD_SKIP_PLUGIN_CHECK=1` are honored +# (legacy + new env var name). # -# Detect path is hardcoded against Claude Code 2025-Q4 plugin cache schema. -# When schema changes upstream, see #35 (path schema watch-list). -# Source plugin lives at anthropics/claude-plugins-official. +# Exit codes inherited from check-plugin-presence.sh: +# 0 — installed +# 1 — missing -set -u - -# Escape hatch — let user bypass detect (#28 risk mitigation). -# Print stderr warning so this leaves an audit trail (per F4 verify finding). +# Honor legacy env var name from #28 v2.53. if [ "${IDD_SKIP_RALPH_CHECK:-}" = "1" ]; then - echo "⚠ IDD_SKIP_RALPH_CHECK=1 set — skipping ralph-loop detection (advanced override)" >&2 - echo " /idd-verify --loop and /idd-all (PR, unattended) will run as if ralph-loop is installed." >&2 - exit 0 + export IDD_SKIP_PLUGIN_CHECK=1 fi -# Plugin cache layout (Claude Code 2025-Q4): -# ~/.claude/plugins/cache////.claude-plugin/plugin.json -# Match any installed version under the plugin dir. -# -# F3 fix: use bash array + nullglob, NOT unquoted `for f in $GLOB`. The latter -# word-splits when HOME contains whitespace (macOS display-name accounts, -# iCloud Mobile Documents paths) → false-negative even when ralph-loop is -# installed → silently breaks v2.40.0 backward-compat for /idd-all callers. -shopt -s nullglob -files=( "${HOME}/.claude/plugins/cache/claude-plugins-official/ralph-loop"/*/.claude-plugin/plugin.json ) -shopt -u nullglob - -if (( ${#files[@]} > 0 )); then - exit 0 -fi - -cat >&2 < Date: Thu, 7 May 2026 13:42:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20idd-issue=20Step=201=20=E5=8A=A0=20?= =?UTF-8?q?MCP=20plugin=20presence=20pre-flight=20+=20structured=20fail-fa?= =?UTF-8?q?st=20(Refs=20#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skills/idd-issue/SKILL.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md index 2a287fb..c58747e 100644 --- a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md +++ b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md @@ -276,6 +276,56 @@ UPSTREAM=$(echo "$REPO_JSON" | jq -r '.parent.nameWithOwner // empty') | 直接貼文字(無附件) | argument 直接帶文字 | n/a | | 混合(文字 + 圖片貼上) | argument 帶文字 + 使用者另外提供 path 清單 | 把使用者給的 path 全部納入 Step 4 上傳清單 | +#### MCP plugin presence pre-flight (v2.54+, #27 fail-fast) + +當 source 是 `.docx` / Telegram / Apple Mail / Apple Notes(任一需要 MCP plugin 的類型),Step 1 一開始就 invoke `check-plugin-presence.sh` detect — 缺失則 **fail-fast abort** with structured error message (per #32 absorbed acceptance criteria)。 + +```bash +# Source-type → required plugin mapping +case "$SOURCE_TYPE" in + docx|doc) + "$CLAUDE_PLUGIN_ROOT/scripts/check-plugin-presence.sh" psychquant-claude-plugins che-word-mcp || abort_source_unsupported "che-word-mcp" ".docx" "psychquant-claude-plugins" ;; + telegram) + "$CLAUDE_PLUGIN_ROOT/scripts/check-plugin-presence.sh" psychquant-claude-plugins che-telegram-mcp || abort_source_unsupported "che-telegram-mcp" "Telegram chat" "psychquant-claude-plugins" ;; + apple-mail) + "$CLAUDE_PLUGIN_ROOT/scripts/check-plugin-presence.sh" psychquant-claude-plugins che-apple-mail-mcp || abort_source_unsupported "che-apple-mail-mcp" "Apple Mail" "psychquant-claude-plugins" ;; + apple-notes) + "$CLAUDE_PLUGIN_ROOT/scripts/check-plugin-presence.sh" psychquant-claude-plugins che-apple-notes-mcp || abort_source_unsupported "che-apple-notes-mcp" "Apple Notes" "psychquant-claude-plugins" ;; + text|md|mixed) + : ;; # no plugin needed for raw text / markdown / pasted text+paths +esac +``` + +`abort_source_unsupported` 印出 structured error message(format spec from #32 absorbed acceptance criteria): + +``` +✗ Source detected as but `` MCP plugin is not installed. + +該 source type 需要對應 MCP plugin 才能讀取文字 + 抽圖。 + +Options: + 1) Install plugin (recommended): + claude plugin marketplace add PsychQuant/ + claude plugin install @ + # 不知道 marketplace? 跑: claude plugin marketplace list + # 詳見: plugins/issue-driven-dev/README.md#optional-per-source-type + + 2) Convert to another supported format: + - Save as `.md` or `.txt` then paste content directly + - Or screenshot then attach as image (如果 主要內容是 figures) + + 3) Manual fallback (not recommended for archives): + paste relevant text into prompt directly + +Aborting /idd-issue. Run again after installing or converting. +``` + +**Why fail-fast not silent fallback** (per #27 + #32 absorbed): +- Silent fallback (e.g. "讓使用者手動處理") 讓使用者誤以為 IDD **不支援**該格式,而非少裝 plugin +- Explicit error + 3 options 把責任 explicit 還給使用者,各 source-type 統一格式建立 mental model + +**Bypass**: `IDD_SKIP_PLUGIN_CHECK=1` env var 跳過 detect(同 #34 generic helper escape hatch)。 + #### Telegram source 專屬流程(最常見且最容易漏的) 當原始描述中含 `chat_id` / Telegram URL / `@username` 引用時,**強制**走以下流程,不問: