fix: install/update 雙語化(預設英文) + 修 macOS bash 3.2 安裝崩潰 + bump 1.7.0

- 修崩潰:bash 3.2 + set -u 展開空陣列會炸 unbound variable;移除多餘
  ${SKIPPED[*]} no-op;多位元組字串旁變數改用 ${VAR} 防 curl|bash 串流切斷
- install/update 訊息依 locale 雙語,預設英文(curl|bash 常 LANG=C),zh_TW 切繁中
- 寫進 CLAUDE.md 的 raw source 宣告依 locale 只寫一種語言(不撐爆 context)
- 修措辭:一般開發案不再顯示誤導的「偵測到 vault 類型:docs」
- 新增 README.en.md(繁中↔English 切換)
- gitignore dogfooding 自裝產物(CLAUDE.md/docs 子目錄/.claude/settings*)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 12:40:28 +08:00
parent bc0fe3f01c
commit e9d6d78032
7 changed files with 460 additions and 72 deletions
+158 -51
View File
@@ -14,6 +14,18 @@
set -euo pipefail
# ── i18n:依 locale 選語言,預設英文 ──────────────────
# 為什麼預設英文:curl | bash 常是 LANG=C,外國人預設就該看得懂;
# 台灣使用者 locale 多為 zh_TW,會自動切回繁中。
case "${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" in
zh*|*Hant*|*Hans*) IS_ZH="yes" ;;
*) IS_ZH="no" ;;
esac
# t "中文" "English" → 依語系印出對應字串
t() { if [ "$IS_ZH" = "yes" ]; then printf '%s\n' "$1"; else printf '%s\n' "$2"; fi; }
# tn = 不換行版(給 prompt 用)
tn() { if [ "$IS_ZH" = "yes" ]; then printf '%s' "$1"; else printf '%s' "$2"; fi; }
REPO_URL="https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/template"
CREATED=()
SKIPPED=()
@@ -26,13 +38,23 @@ for arg in "$@"; do
--sdd|--sdd-only) MODULE="sdd" ;;
--all) MODULE="all" ;;
-h|--help)
cat <<'HELP'
if [ "$IS_ZH" = "yes" ]; then
cat <<'HELP'
用法:install.sh [--wiki | --sdd | --all]
--wiki 只裝 LLM WikiCC 記憶系統 + 機敏防護)
--sdd 只裝 SDD 系統(動 code 前強制要有設計文件)
--all 兩個都裝(預設)
無參數 互動式詢問要裝哪個
HELP
else
cat <<'HELP'
Usage: install.sh [--wiki | --sdd | --all]
--wiki Install LLM Wiki only (CC memory system + secret protection)
--sdd Install SDD system only (require a design doc before touching code)
--all Install both (default)
no flag Interactively ask which to install
HELP
fi
exit 0 ;;
esac
done
@@ -40,18 +62,21 @@ done
echo ""
echo "🔧 system-dev-template installer"
echo "================================="
echo "只建立缺少的目錄和檔案,已有的不動。"
t "只建立缺少的目錄和檔案,已有的不動。" \
"Only creates missing dirs and files; never touches what already exists."
echo ""
# ── 無參數 → 互動式詢問(給非專業使用者)──────────
if [ -z "$MODULE" ]; then
if [ -t 0 ]; then
echo "要安裝哪一塊?"
echo " 1) LLM Wiki —— 讓 CC 記住決策、不重複犯錯(含機敏防護)"
echo " 2) SDD —— 動 code 前強制先有設計文件"
echo " 3) 兩個都裝(推薦)"
t "要安裝哪一塊?" "Which part do you want to install?"
t " 1) LLM Wiki —— 讓 CC 記住決策、不重複犯錯(含機敏防護)" \
" 1) LLM Wiki — let CC remember decisions and avoid repeating mistakes (with secret protection)"
t " 2) SDD —— 動 code 前強制先有設計文件" \
" 2) SDD — require a design doc before touching code"
t " 3) 兩個都裝(推薦)" " 3) Install both (recommended)"
echo ""
printf "請輸入 1 / 2 / 3 [預設 3]"
tn "請輸入 1 / 2 / 3 [預設 3]" "Enter 1 / 2 / 3 [default 3]: "
read -r choice || choice=3
case "$choice" in
1) MODULE="wiki" ;;
@@ -73,7 +98,7 @@ case "$MODULE" in
esac
echo ""
echo "📦 安裝模組:$MODULE"
t "📦 安裝模組:$MODULE" "📦 Module: $MODULE"
echo ""
# ── 偵測 vault 類型 → 決定 raw source(原始文件)路徑 ──────────
@@ -85,46 +110,90 @@ echo ""
# 必須在建立 CLAUDE.md 之前跑完。
VAULT_TYPE=""
RAW_SOURCE=""
IS_VAULT="no" # 只有 logseq/obsidian 這種「筆記軟體 vault」才算 yes
if [ -d "logseq" ]; then
VAULT_TYPE="logseq"
RAW_SOURCE="pages/, journals/"
IS_VAULT="yes"
elif [ -d ".obsidian" ]; then
VAULT_TYPE="obsidian"
RAW_SOURCE="./ (整個 vault 根目錄的 .md"
RAW_SOURCE="$(tn './ (整個 vault 根目錄的 .md' './ (all .md under the vault root)')"
IS_VAULT="yes"
else
VAULT_TYPE="docs"
RAW_SOURCE="docs/"
fi
echo "🗂️ 偵測到 vault 類型:$VAULT_TYPE → raw source$RAW_SOURCE"
echo ""
# 偵測到是筆記 vault → 出聲告訴使用者「我看到了,會小心、不破壞你的筆記結構」。
# 不是筆記(一般開發案等)→ 不囉嗦,默默把 docs/ 當原始文件夾安裝完成。
if [ "$IS_VAULT" = "yes" ]; then
t "🗂️ 偵測到 ${VAULT_TYPE} 筆記庫 → 原始文件:${RAW_SOURCE}" \
"🗂️ Detected a ${VAULT_TYPE} note vault → raw source: ${RAW_SOURCE}"
t " (會保留你筆記軟體的目錄/檔名結構,不搬動、不改名)" \
" (your note app's directory/file structure is preserved — nothing is moved or renamed)"
echo ""
fi
# 把「raw source 宣告區塊」吐出來,給新建的 CLAUDE.md append 或
# 給已存在的 CLAUDE.md 當手動補貼的提示。內容對 CC / Cowork 都是
# 機器可讀的指令(明確路徑 + 不可破壞 vault 結構的約束)。
# 寫進 CLAUDE.md 的 raw source 宣告區塊。給人也給 AI 看:
# 依 locale 只寫「一種語言」進 CLAUDE.md(雙語會讓每個 session 的 context 更滿)。
emit_raw_source_block() {
cat <<BLOCK
local source_kind
if [ "$IS_ZH" = "yes" ]; then
if [ "$IS_VAULT" = "yes" ]; then source_kind="${VAULT_TYPE} 筆記庫"
else source_kind="一般專案(原始文件放 raw source 路徑)"; fi
cat <<BLOCK
---
## 原始文件空間(raw source
> 安裝時偵測到的 vault 類型:**$VAULT_TYPE**
> 安裝時偵測到的來源型態:**${source_kind}**
> CC 與 Cowork 整理/讀取「人寫的原始文件」時,**只在這裡找、只在這裡動**。
| 項目 | 值 |
|------|----|
| vault 類型 | \`$VAULT_TYPE\` |
| raw source | \`$RAW_SOURCE\` |
| 來源型態 | \`${source_kind}\` |
| raw source | \`${RAW_SOURCE}\` |
**約束(CC 與 Cowork 都必須遵守)**
- 整理 wiki/知識時,原始文件**一律從上方 raw source 路徑讀取**,不要假設是 \`docs/\`。
BLOCK
if [ "$VAULT_TYPE" != "docs" ]; then
cat <<BLOCK
- 這是 **$VAULT_TYPE vault**:保留它原本的目錄與檔名慣例,**不得搬動、改名、重新分類** \`.md\` 檔,
以免破壞 vault 結構造成筆記不可讀。整理只在 \`.claude/wiki/\` 產出,**不動 raw source 本身**。
if [ "$IS_VAULT" = "yes" ]; then
cat <<BLOCK
- 這是 **${VAULT_TYPE} 筆記庫**:保留它原本的目錄與檔名慣例,**不得搬動、改名、重新分類** \`.md\` 檔,
以免破壞筆記軟體結構造成筆記不可讀。整理只在 \`.claude/wiki/\` 產出,**不動 raw source 本身**。
BLOCK
fi
else
if [ "$IS_VAULT" = "yes" ]; then source_kind="${VAULT_TYPE} note vault"
else source_kind="regular project (raw source lives at the path below)"; fi
cat <<BLOCK
---
## Raw source space
> Source type detected at install time: **${source_kind}**
> When CC and Cowork curate/read human-written raw source, **look only here and act only here**.
| Item | Value |
|------|-------|
| Source type | \`${source_kind}\` |
| raw source | \`${RAW_SOURCE}\` |
**Constraints (both CC and Cowork must obey)**
- When curating the wiki/knowledge, **always read raw source from the path above** — don't assume \`docs/\`.
BLOCK
if [ "$IS_VAULT" = "yes" ]; then
cat <<BLOCK
- This is a **${VAULT_TYPE} note vault**: keep its original directory and file-naming conventions. **Do not move, rename, or re-classify** \`.md\` files,
or you'll break the note-app structure and make notes unreadable. Curation output goes only into \`.claude/wiki/\`; **never touch the raw source itself**.
BLOCK
fi
fi
}
@@ -134,7 +203,7 @@ create_dir() {
mkdir -p "$1"
CREATED+=("$1/")
else
SKIPPED+=("$1/ (已存在)")
SKIPPED+=("$1/ $(tn '(已存在)' '(already exists)')")
fi
}
@@ -145,7 +214,7 @@ download_if_missing() {
curl -sSL "$src" -o "$dest"
CREATED+=("$dest")
else
SKIPPED+=("$dest (已存在,跳過)")
SKIPPED+=("$dest $(tn '(已存在,跳過)' '(already exists, skipped)')")
fi
}
@@ -226,9 +295,9 @@ build_hooks_json() {
if [ ! -f ".claude/settings.json" ]; then
build_hooks_json > .claude/settings.json
CREATED+=(".claude/settings.json (依 $MODULE 模組產生)")
CREATED+=(".claude/settings.json $(tn "(依 $MODULE 模組產生)" "(generated for module: $MODULE)")")
else
SKIPPED+=(".claude/settings.json (已存在,請手動合併 hooks)")
SKIPPED+=(".claude/settings.json $(tn '(已存在,請手動合併 hooks)' '(already exists — merge hooks manually)')")
fi
# ── CLAUDE.md:只在完全不存在時建立 ────────────────
@@ -238,20 +307,24 @@ if [ ! -f "CLAUDE.md" ]; then
download_if_missing "CLAUDE.md" "$REPO_URL/CLAUDE.md"
if [ -f "CLAUDE.md" ]; then
emit_raw_source_block >> CLAUDE.md
CREATED+=("CLAUDE.md ← 已寫入 raw source 宣告($VAULT_TYPE")
CREATED+=("CLAUDE.md $(tn "← 已寫入 raw source 宣告(${VAULT_TYPE}" "← raw source declaration written (${VAULT_TYPE})")")
fi
else
SKIPPED+=("CLAUDE.md (已存在,請手動加入對應區塊)")
SKIPPED+=("CLAUDE.md $(tn '(已存在,請手動加入對應區塊)' '(already exists — add the block manually)')")
fi
# ── 輸出結果 ──────────────────────────────────────
echo ""
echo "✅ 建立了:"
for item in "${CREATED[@]}"; do echo " + $item"; done
t "✅ 建立了:" "✅ Created:"
# 注意:macOS bash 3.2 在 set -u 下展開「空陣列」會炸 unbound variable
# 所以這裡先確認有元素才展開(SKIPPED 區塊在下方本來就有守,CREATED 補上)。
if [ ${#CREATED[@]} -gt 0 ]; then
for item in "${CREATED[@]}"; do echo " + $item"; done
fi
if [ ${#SKIPPED[@]} -gt 0 ]; then
echo ""
echo "⚠️ 跳過(已存在):"
t "⚠️ 跳過(已存在):" "⚠️ Skipped (already exists):"
for item in "${SKIPPED[@]}"; do echo " - $item"; done
fi
@@ -262,33 +335,59 @@ echo "────────────────────────
if [ -f "CLAUDE.md" ]; then
if ! grep -q "raw source" CLAUDE.md; then
echo ""
echo "📌 CLAUDE.md 已存在但缺少 raw source 宣告(偵測到 vault 類型:$VAULT_TYPE)。"
echo " 請手動把以下區塊貼進去,讓 CC 與 Cowork 知道原始文件在哪、不要亂動 vault:"
t "📌 CLAUDE.md 已存在但缺少 raw source 宣告。" \
"📌 CLAUDE.md exists but lacks a raw source declaration."
t " 請手動把以下區塊貼進去,讓 CC 與 Cowork 知道原始文件在哪、不要亂動既有結構:" \
" Paste the block below in so CC and Cowork know where the raw source is and won't disturb your structure:"
emit_raw_source_block | sed 's/^/ /'
fi
if $WANT_WIKI && ! grep -q "wiki/status.md" CLAUDE.md; then
echo ""
echo "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:"
t "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:" \
"📌 CLAUDE.md exists but lacks the wiki reading order — please add it manually:"
echo ""
echo ' ## Wiki 讀取順序'
echo ' | 檔案 | 時機 | 用途 |'
echo ' |------|------|------|'
echo ' | `.claude/wiki/status.md` | session 開始第一件事 | 當前進度 |'
echo ' | `.claude/wiki/mistakes.md` | 做新功能前 | 已知誤解 |'
echo ' | `.claude/wiki/decisions-summary.md` | 設計判斷時 | 架構決策 |'
if [ "$IS_ZH" = "yes" ]; then
cat <<'SNIP'
## Wiki 讀取順序
| 檔案 | 時機 | 用途 |
|------|------|------|
| `.claude/wiki/status.md` | session 開始第一件事 | 當前進度 |
| `.claude/wiki/mistakes.md` | 做新功能前 | 已知誤解 |
| `.claude/wiki/decisions-summary.md` | 設計判斷時 | 架構決策 |
SNIP
else
cat <<'SNIP'
## Wiki reading order
| File | When | Purpose |
|------|------|---------|
| `.claude/wiki/status.md` | first thing at session start | current progress |
| `.claude/wiki/mistakes.md` | before building a new feature | known misconceptions |
| `.claude/wiki/decisions-summary.md` | when making design calls | architecture decisions |
SNIP
fi
fi
if $WANT_SDD && ! grep -q "docs/3-specs" CLAUDE.md; then
echo ""
echo "📌 CLAUDE.md 已存在但缺少 SDD 鐵律,請手動加入:"
t "📌 CLAUDE.md 已存在但缺少 SDD 鐵律,請手動加入:" \
"📌 CLAUDE.md exists but lacks the SDD iron rule — please add it manually:"
echo ""
echo ' ## 絕對鐵律'
echo ' 1. 任何 code 變動前必須有對應 SDDdocs/3-specs/[子系統]/design.md'
echo ' 找不到 → 停手問負責人,不要自行建立。'
if [ "$IS_ZH" = "yes" ]; then
cat <<'SNIP'
## 絕對鐵律
1. 任何 code 變動前必須有對應 SDDdocs/3-specs/[子系統]/design.md
找不到 → 停手問負責人,不要自行建立。
SNIP
else
cat <<'SNIP'
## Iron rule
1. Every code change must have a matching SDD (docs/3-specs/[subsystem]/design.md).
Not found → stop and ask the owner; do not create one on your own.
SNIP
fi
fi
fi
# settings.json 已存在 → 依模組提醒要合併哪些 hook
if [ -f ".claude/settings.json" ] && grep -q '"已存在"' <<<"${SKIPPED[*]}" 2>/dev/null; then :; fi
if [ -f ".claude/settings.json" ]; then
MISSING_HOOKS=()
$WANT_WIKI && ! grep -q "session-start-recall.sh" .claude/settings.json && MISSING_HOOKS+=("SessionStart: session-start-recall.sh")
@@ -296,25 +395,33 @@ if [ -f ".claude/settings.json" ]; then
$WANT_SDD && ! grep -q "sdd-guard.sh" .claude/settings.json && MISSING_HOOKS+=("PreToolUse(Write|Edit): sdd-guard.sh")
if [ ${#MISSING_HOOKS[@]} -gt 0 ]; then
echo ""
echo "📌 .claude/settings.json 已存在,請手動把以下 hooks 合併進去(保留既有設定):"
t "📌 .claude/settings.json 已存在,請手動把以下 hooks 合併進去(保留既有設定):" \
"📌 .claude/settings.json exists — merge the hooks below in manually (keep your existing settings):"
for h in "${MISSING_HOOKS[@]}"; do echo "$h"; done
fi
fi
# pre-write-guard 是空殼,提醒它預設不攔(避免「以為有保護其實沒有」的安全錯覺)
echo ""
echo "️ .claude/hooks/pre-write-guard.sh 是「按需手填的空插槽」,預設不攔任何東西。"
echo " 需要專案禁令?最簡單是叫你的 CC 寫一支貼合的 guard hook(比範本表達力強);"
echo " 或自己填 FORBIDDEN_PATTERNS 並到 settings.json 掛上才會生效。"
t "️ .claude/hooks/pre-write-guard.sh 是「按需手填的空插槽」,預設不攔任何東西。" \
"️ .claude/hooks/pre-write-guard.sh is an empty slot to fill on demand — by default it blocks nothing."
t " 需要專案禁令?最簡單是叫你的 CC 寫一支貼合的 guard hook(比範本表達力強);" \
" Need project-specific bans? Easiest is to ask your CC to write a tailored guard hook (more expressive than the template);"
t " 或自己填 FORBIDDEN_PATTERNS 並到 settings.json 掛上才會生效。" \
" or fill in FORBIDDEN_PATTERNS yourself and wire it into settings.json to take effect."
echo ""
echo "🚀 下一步:"
t "🚀 下一步:" "🚀 Next steps:"
if $WANT_WIKI; then
echo " 在 Claude Code 對話裡執行 /wiki-init"
echo " CC 會掃描現有文件、套用 .wikiignore、建立 wiki。"
t " 在 Claude Code 對話裡執行 /wiki-init" \
" In a Claude Code conversation, run /wiki-init"
t " CC 會掃描現有文件、套用 .wikiignore、建立 wiki。" \
" CC will scan your existing docs, apply .wikiignore, and build the wiki."
fi
if $WANT_SDD; then
echo " 動 code 前先在 docs/3-specs/[子系統]/ 建 design.md(可用 /sdd-check 協助)"
t " 動 code 前先在 docs/3-specs/[子系統]/ 建 design.md(可用 /sdd-check 協助)" \
" Before touching code, create design.md under docs/3-specs/[subsystem]/ (use /sdd-check to help)"
fi
echo " GitHub issueCC 可直接 /issue-handle 讀回自己 repo 的 issue(禁自動輪詢)"
t " GitHub issueCC 可直接 /issue-handle 讀回自己 repo 的 issue(禁自動輪詢)" \
" GitHub issues: CC can use /issue-handle to read issues from its own repo (no auto-polling)"
echo ""
+39 -20
View File
@@ -15,6 +15,14 @@
set -euo pipefail
# ── i18n:依 locale 選語言,預設英文(curl | bash 常為 LANG=C)──
case "${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" in
zh*|*Hant*|*Hans*) IS_ZH="yes" ;;
*) IS_ZH="no" ;;
esac
t() { if [ "$IS_ZH" = "yes" ]; then printf '%s\n' "$1"; else printf '%s\n' "$2"; fi; }
tn() { if [ "$IS_ZH" = "yes" ]; then printf '%s' "$1"; else printf '%s' "$2"; fi; }
REPO_RAW="https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main"
TEMPLATE_URL="$REPO_RAW/template"
@@ -24,25 +32,29 @@ NEW=()
TEMPLATED=()
# ── 版本比對:先看本機 vs 遠端,給使用者「值不值得更新」的判斷 ──
LOCAL_VER="(未知)"
LOCAL_VER="$(tn '(未知)' '(unknown)')"
[ -f ".claude/VERSION" ] && LOCAL_VER="$(tr -d '[:space:]' < .claude/VERSION)"
REMOTE_VER="$(curl -sSL "$TEMPLATE_URL/.claude/VERSION" 2>/dev/null | tr -d '[:space:]' || echo '')"
echo ""
echo "🔄 system-dev-template updater"
echo "================================="
echo " 本機版本:${LOCAL_VER:-未知}"
echo " 最新版本:${REMOTE_VER:-取不到(檢查網路)}"
t " 本機版本:${LOCAL_VER}" " Local version: ${LOCAL_VER}"
t " 最新版本:${REMOTE_VER:-取不到(檢查網路)}" \
" Latest version: ${REMOTE_VER:-unavailable (check network)}"
echo ""
if [ -z "$REMOTE_VER" ]; then
echo "❌ 取不到遠端版本,可能是網路問題。請稍後再試。"
t "❌ 取不到遠端版本,可能是網路問題。請稍後再試。" \
"❌ Could not fetch the remote version (likely a network issue). Please try again later."
exit 1
fi
if [ "$LOCAL_VER" = "$REMOTE_VER" ]; then
echo "✅ 已是最新版($LOCAL_VER),不需更新。"
echo " (仍會同步模板邏輯檔,確保 hooks/commands 與最新一致。)"
t "✅ 已是最新版($LOCAL_VER),不需更新。" \
"✅ Already up to date ($LOCAL_VER), nothing to update."
t " (仍會同步模板邏輯檔,確保 hooks/commands 與最新一致。)" \
" (Template logic files will still be synced to keep hooks/commands in line with the latest.)"
echo ""
fi
@@ -61,14 +73,14 @@ update_file() {
fi
else
rm -f "$dest.tmp"
echo " ⚠️ 抓取失敗,保留原檔:$dest"
t " ⚠️ 抓取失敗,保留原檔:$dest" " ⚠️ Download failed, keeping the original: $dest"
fi
else
if curl -sSL "$src" -o "$dest" 2>/dev/null && [ -s "$dest" ]; then
NEW+=("$dest") # 新功能:舊版沒有的檔
else
rm -f "$dest"
echo " ⚠️ 抓取失敗:$dest"
t " ⚠️ 抓取失敗:$dest" " ⚠️ Download failed: $dest"
fi
fi
}
@@ -107,10 +119,12 @@ HAS_SDD=false
[ -d ".claude/wiki" ] && HAS_WIKI=true
if [ -f ".claude/hooks/sdd-guard.sh" ] || [ -d "docs/3-specs/TEMPLATE-sdd" ]; then HAS_SDD=true; fi
echo "📦 偵測到已安裝模組:"
t "📦 偵測到已安裝模組:" "📦 Detected installed modules:"
$HAS_WIKI && echo " • LLM Wiki"
$HAS_SDD && echo " • SDD"
{ $HAS_WIKI || $HAS_SDD; } || echo " (未偵測到任何模組——這裡可能還沒安裝,請改跑 install.sh"
{ $HAS_WIKI || $HAS_SDD; } || \
t " (未偵測到任何模組——這裡可能還沒安裝,請改跑 install.sh" \
" (No modules detected — nothing installed here yet; run install.sh instead.)"
echo ""
# ── 客製檔:使用者手填的 guardrail,永不覆蓋(issue #3)──
@@ -167,29 +181,33 @@ echo ""
echo "─────────────────────────────────"
if [ ${#NEW[@]} -gt 0 ]; then
echo ""
echo "🆕 新功能(舊版沒有,已加入):"
t "🆕 新功能(舊版沒有,已加入):" "🆕 New features (absent in the old version, now added):"
for f in "${NEW[@]}"; do echo " + $f"; done
fi
if [ ${#UPDATED[@]} -gt 0 ]; then
echo ""
echo "⬆️ 已更新(覆蓋成新版):"
t "⬆️ 已更新(覆蓋成新版):" "⬆️ Updated (overwritten with the new version):"
for f in "${UPDATED[@]}"; do echo " ~ $f"; done
fi
if [ ${#NEW[@]} -eq 0 ] && [ ${#UPDATED[@]} -eq 0 ]; then
echo ""
echo "✨ 模板邏輯檔已全部最新,無需變動。"
t "✨ 模板邏輯檔已全部最新,無需變動。" \
"✨ All template logic files are already up to date — no changes needed."
fi
if [ ${#KEPT[@]} -gt 0 ]; then
echo ""
echo "🔒 完整保留(你的內容/設定,從未碰過):"
t "🔒 完整保留(你的內容/設定,從未碰過):" \
"🔒 Fully preserved (your content/settings, never touched):"
for f in "${KEPT[@]}"; do echo " = $f"; done
fi
if [ ${#TEMPLATED[@]} -gt 0 ]; then
echo ""
echo "📋 客製檔有新版範本(你的原檔沒動,新版另存旁邊,請自行 diff 採納):"
t "📋 客製檔有新版範本(你的原檔沒動,新版另存旁邊,請自行 diff 採納):" \
"📋 Custom files have a new template version (your original is untouched; the new one is saved alongside — diff and adopt as you like):"
for f in "${TEMPLATED[@]}"; do
echo "$f"
echo " 比對:diff \"${f%.template.sh}.sh\" \"$f\""
t " 比對:diff \"${f%.template.sh}.sh\" \"$f\"" \
" compare: diff \"${f%.template.sh}.sh\" \"$f\""
done
fi
@@ -201,13 +219,14 @@ if [ -f ".claude/settings.json" ]; then
$HAS_SDD && ! grep -q "sdd-guard.sh" .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): sdd-guard.sh")
if [ ${#MISSING[@]} -gt 0 ]; then
echo ""
echo "📌 settings.json 是你的設定(沒動),但偵測到缺以下 hook,請手動補上:"
t "📌 settings.json 是你的設定(沒動),但偵測到缺以下 hook,請手動補上:" \
"📌 settings.json is yours (untouched), but these hooks are missing — please add them manually:"
for h in "${MISSING[@]}"; do echo "$h"; done
fi
fi
echo ""
echo "🚀 更新完成:${LOCAL_VER}${REMOTE_VER}"
echo " 下次更新直接跑:bash scripts/update.sh"
echo " 改了什麼看:CHANGELOG.md"
t "🚀 更新完成:${LOCAL_VER}${REMOTE_VER}" "🚀 Update complete: ${LOCAL_VER}${REMOTE_VER}"
t " 下次更新直接跑:bash scripts/update.sh" " Next time, just run: bash scripts/update.sh"
t " 改了什麼看:CHANGELOG.md" " See what changed: CHANGELOG.md"
echo ""