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 < 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` 引用時,**強制**走以下流程,不問: