453dec60b1
scripts/install.sh 與 scripts/update.sh 的更新來源指向已被 GitHub suspend 的 uncle6me-web 帳號,實測全部 404 ⇒ 自動更新靜默失效。改成與 system-dev-template 本體、以及本 repo 現行世代 system-dev/scripts/ 一致的正解:預設公開 GitHub youlinhsieh/system-dev-template,可用 TEMPLATE_SOURCE 環境變數覆寫,不改檔。 同時補上 looks_like_error_page 判斷(沿用 template repo 已修好的寫法):curl 對 404 常回傳非空的錯誤頁內容,只判斷「非空」會把它當成正常檔案寫入且不報錯。 現在錯誤頁會被偵測、檔案不寫入、FAILED 清單於結尾列出、腳本以非零狀態碼結束。 實測: - 修正後來源多個路徑回真實 HTTP 200(CLAUDE.md/VERSION/self-update 兩支腳本)。 - 刻意把 TEMPLATE_SOURCE 指回死帳號重跑兩支腳本:全部項目進 FAILED 清單、 無任何檔案被錯誤頁污染、exit code 1。 - 兩支腳本 bash -n 語法檢查通過。 已知缺口(不在本次範圍,留待另決):template repo 內部檔案佈局已從 .claude/wiki/、docs/ 搬到 system-dev/ 前綴,這兩支舊世代腳本仍用舊路徑, 其中一部分檔案(wiki 範本、docs/README、SDD 範本等)在新帳號上仍 404—— 現在會清楚回報失敗而不是靜默吞掉,但尚未逐一改點新路徑。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
471 lines
21 KiB
Bash
Executable File
471 lines
21 KiB
Bash
Executable File
#!/bin/bash
|
||
# system-dev-template installer
|
||
# 已有專案接入腳本——只建立缺少的東西,已有的一律不動。
|
||
#
|
||
# 模組化安裝:
|
||
# --wiki 只裝 LLM Wiki(記憶系統 + 機敏防護)
|
||
# --sdd 只裝 SDD 系統(動 code 前必須有 design.md)
|
||
# --all 兩個都裝(預設)
|
||
# 無參數 互動式詢問
|
||
#
|
||
# 為什麼留在同一個 repo 用參數選,而不是 fork:
|
||
# 使用者多半非專業,最怕「我要去哪個 repo」。一個入口 + 選單最友善。
|
||
# 等未來功能多到 3+ 個再演進成「模板組合器」。模組邊界先在這裡劃好。
|
||
|
||
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; }
|
||
|
||
# 來源預設=公開 GitHub(leo 2026-07-21:「要發佈的正稿,從頭就不要用奇怪的網址,
|
||
# 以免改來改去」)。舊的 uncle6me-web 帳號已被 suspend(讀 404)=自動更新靜默失效,
|
||
# 這顆雷已在 system-dev-template 本體修過(正解=改指 youlinhsieh 這個公開帳號),
|
||
# 這裡只是把同一個修法補進 Arcrun 這份沒跟到的 copy。
|
||
# 內部要指私有草稿源時用環境變數覆寫,不改檔:
|
||
# TEMPLATE_SOURCE=https://<私有 raw base> bash scripts/install.sh
|
||
TEMPLATE_SOURCE="${TEMPLATE_SOURCE:-https://raw.githubusercontent.com/youlinhsieh/system-dev-template/main}"
|
||
REPO_URL="$TEMPLATE_SOURCE/template"
|
||
CREATED=()
|
||
SKIPPED=()
|
||
FAILED=()
|
||
|
||
# 404 頁面常常「非空」(GitHub raw 對不存在的路徑回 "404: Not Found"),
|
||
# 只判斷 [ -s file ] 會把錯誤頁內容當成檔案寫進去且完全不報錯(template issue #13 撞過的雷)。
|
||
# 這裡量身做一個輕量判斷:檔案很短又長得像錯誤訊息 → 當失敗。
|
||
looks_like_error_page() {
|
||
local f="$1"
|
||
[ -s "$f" ] || return 0
|
||
if [ "$(wc -l < "$f" | tr -d ' ')" -le 2 ] && head -c 200 "$f" | grep -qiE '40[0-9]|not found|<html'; then
|
||
return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
# ── 解析模組參數 ──────────────────────────────────
|
||
MODULE=""
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--wiki|--wiki-only) MODULE="wiki" ;;
|
||
--sdd|--sdd-only) MODULE="sdd" ;;
|
||
--all) MODULE="all" ;;
|
||
-h|--help)
|
||
if [ "$IS_ZH" = "yes" ]; then
|
||
cat <<'HELP'
|
||
用法:install.sh [--wiki | --sdd | --all]
|
||
--wiki 只裝 LLM Wiki(CC 記憶系統 + 機敏防護)
|
||
--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
|
||
|
||
echo ""
|
||
echo "🔧 system-dev-template installer"
|
||
echo "================================="
|
||
t "只建立缺少的目錄和檔案,已有的不動。" \
|
||
"Only creates missing dirs and files; never touches what already exists."
|
||
echo ""
|
||
|
||
# ── 無參數 → 互動式詢問(給非專業使用者)──────────
|
||
if [ -z "$MODULE" ]; then
|
||
if [ -t 0 ]; then
|
||
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 ""
|
||
tn "請輸入 1 / 2 / 3 [預設 3]:" "Enter 1 / 2 / 3 [default 3]: "
|
||
read -r choice || choice=3
|
||
case "$choice" in
|
||
1) MODULE="wiki" ;;
|
||
2) MODULE="sdd" ;;
|
||
*) MODULE="all" ;;
|
||
esac
|
||
else
|
||
# 非互動環境(如 curl | bash 無 tty)→ 預設全裝
|
||
MODULE="all"
|
||
fi
|
||
fi
|
||
|
||
WANT_WIKI=false
|
||
WANT_SDD=false
|
||
case "$MODULE" in
|
||
wiki) WANT_WIKI=true ;;
|
||
sdd) WANT_SDD=true ;;
|
||
all) WANT_WIKI=true; WANT_SDD=true ;;
|
||
esac
|
||
|
||
echo ""
|
||
t "📦 安裝模組:$MODULE" "📦 Module: $MODULE"
|
||
echo ""
|
||
|
||
# ── 偵測 vault 類型 → 決定 raw source(原始文件)路徑 ──────────
|
||
# 為什麼:這個模板原本假設「原始文件在 docs/」,但 Logseq / Obsidian
|
||
# 這種 PKM vault 有自己的目錄慣例,整理時不能照 docs/ 那套搬動,
|
||
# 否則會破壞 vault 結構、讓筆記變不可讀。
|
||
# 偵測結果寫進 CLAUDE.md,讓 CC 和未來的 Cowork skill 都知道
|
||
# 「該讀/該整理哪裡」而不是亂動。
|
||
# 必須在建立 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="$(tn './ (整個 vault 根目錄的 .md)' './ (all .md under the vault root)')"
|
||
IS_VAULT="yes"
|
||
else
|
||
VAULT_TYPE="docs"
|
||
RAW_SOURCE="docs/"
|
||
fi
|
||
# 偵測到是筆記 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() {
|
||
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)
|
||
|
||
> 安裝時偵測到的來源型態:**${source_kind}**
|
||
> CC 與 Cowork 整理/讀取「人寫的原始文件」時,**只在這裡找、只在這裡動**。
|
||
|
||
| 項目 | 值 |
|
||
|------|----|
|
||
| 來源型態 | \`${source_kind}\` |
|
||
| raw source | \`${RAW_SOURCE}\` |
|
||
|
||
**約束(CC 與 Cowork 都必須遵守)**
|
||
|
||
- 整理 wiki/知識時,原始文件**一律從上方 raw source 路徑讀取**,不要假設是 \`docs/\`。
|
||
BLOCK
|
||
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
|
||
}
|
||
|
||
# ── 工具函式 ──────────────────────────────────────
|
||
create_dir() {
|
||
if [ ! -d "$1" ]; then
|
||
mkdir -p "$1"
|
||
CREATED+=("$1/")
|
||
else
|
||
SKIPPED+=("$1/ $(tn '(已存在)' '(already exists)')")
|
||
fi
|
||
}
|
||
|
||
download_if_missing() {
|
||
local dest="$1" src="$2"
|
||
if [ ! -f "$dest" ]; then
|
||
mkdir -p "$(dirname "$dest")"
|
||
if curl -sSL "$src" -o "$dest" 2>/dev/null && ! looks_like_error_page "$dest"; then
|
||
CREATED+=("$dest")
|
||
else
|
||
rm -f "$dest"
|
||
FAILED+=("$dest $(tn "(來源抓不到:$src)" "(source unreachable: $src)")")
|
||
fi
|
||
else
|
||
SKIPPED+=("$dest $(tn '(已存在,跳過)' '(already exists, skipped)')")
|
||
fi
|
||
}
|
||
|
||
# ── 共用結構(兩個模組都需要 docs 分類 + .claude)──
|
||
create_dir "docs/1-vision"
|
||
create_dir "docs/2-architecture/decisions"
|
||
create_dir "docs/4-guides"
|
||
create_dir "docs/5-records/incidents"
|
||
create_dir "docs/5-records/test-reports"
|
||
create_dir "docs/6-user"
|
||
create_dir ".claude/commands"
|
||
create_dir ".claude/hooks"
|
||
download_if_missing "docs/README.md" "$REPO_URL/docs/README.md"
|
||
|
||
# ── WIKI 模組 ─────────────────────────────────────
|
||
if $WANT_WIKI; then
|
||
create_dir ".claude/wiki"
|
||
download_if_missing ".claude/wiki/INDEX.md" "$REPO_URL/.claude/wiki/INDEX.md"
|
||
download_if_missing ".claude/wiki/TAXONOMY.md" "$REPO_URL/.claude/wiki/TAXONOMY.md"
|
||
download_if_missing ".claude/wiki/status.md" "$REPO_URL/.claude/wiki/status.md"
|
||
download_if_missing ".claude/wiki/mistakes.md" "$REPO_URL/.claude/wiki/mistakes.md"
|
||
download_if_missing ".claude/wiki/decisions-summary.md" "$REPO_URL/.claude/wiki/decisions-summary.md"
|
||
download_if_missing ".claude/wiki/.wikiignore" "$REPO_URL/.claude/wiki/.wikiignore"
|
||
|
||
download_if_missing ".claude/commands/wiki-init.md" "$REPO_URL/.claude/commands/wiki-init.md"
|
||
download_if_missing ".claude/commands/wiki-capture.md" "$REPO_URL/.claude/commands/wiki-capture.md"
|
||
download_if_missing ".claude/commands/wiki-update.md" "$REPO_URL/.claude/commands/wiki-update.md"
|
||
download_if_missing ".claude/commands/wiki-recall.md" "$REPO_URL/.claude/commands/wiki-recall.md"
|
||
|
||
# wiki 相關 hooks:接關 + 機敏掃描
|
||
download_if_missing ".claude/hooks/session-start-recall.sh" "$REPO_URL/.claude/hooks/session-start-recall.sh"
|
||
download_if_missing ".claude/hooks/wiki-secret-scan.sh" "$REPO_URL/.claude/hooks/wiki-secret-scan.sh"
|
||
|
||
# Cowork(claude.ai)整理 wiki 用的 skill:與 CC 的 /wiki-init 共用同一套規則
|
||
# (含 typed-edge、frontmatter 標籤、gloss)。沒這支 → claude.ai 來掃時身上沒規則。
|
||
download_if_missing "docs/SKILL.md" "$REPO_URL/docs/SKILL.md"
|
||
fi
|
||
|
||
# ── SDD 模組 ──────────────────────────────────────
|
||
if $WANT_SDD; then
|
||
create_dir "docs/3-specs"
|
||
download_if_missing "docs/3-specs/TEMPLATE-sdd/design.md" "$REPO_URL/docs/3-specs/TEMPLATE-sdd/design.md"
|
||
download_if_missing "docs/3-specs/TEMPLATE-sdd/tasks.md" "$REPO_URL/docs/3-specs/TEMPLATE-sdd/tasks.md"
|
||
download_if_missing "docs/2-architecture/decisions/TEMPLATE-adr.md" "$REPO_URL/docs/2-architecture/decisions/TEMPLATE-adr.md"
|
||
|
||
download_if_missing ".claude/commands/sdd-check.md" "$REPO_URL/.claude/commands/sdd-check.md"
|
||
download_if_missing ".claude/hooks/sdd-guard.sh" "$REPO_URL/.claude/hooks/sdd-guard.sh"
|
||
fi
|
||
|
||
# ── 共用 hook:專案自訂禁令骨架(預設停用)────────
|
||
download_if_missing ".claude/hooks/pre-write-guard.sh" "$REPO_URL/.claude/hooks/pre-write-guard.sh"
|
||
|
||
# ── 共用指引:GitHub issue 處理(讀/回普世,跨 repo 發要先問,禁自動輪詢)──
|
||
download_if_missing ".claude/commands/issue-handle.md" "$REPO_URL/.claude/commands/issue-handle.md"
|
||
|
||
chmod +x .claude/hooks/*.sh 2>/dev/null || true
|
||
|
||
# ── 依模組產生 settings.json 的 hooks 區塊 ────────
|
||
# settings.json 因模組而異,不能直接下載單一靜態檔,改條件組裝。
|
||
build_hooks_json() {
|
||
local session_hooks="" pretool_hooks=""
|
||
|
||
if $WANT_WIKI; then
|
||
session_hooks='{ "type": "command", "command": ".claude/hooks/session-start-recall.sh" }'
|
||
fi
|
||
|
||
# PreToolUse 依模組疊加
|
||
local pt=()
|
||
$WANT_SDD && pt+=('{ "type": "command", "command": ".claude/hooks/sdd-guard.sh" }')
|
||
pt+=('{ "type": "command", "command": ".claude/hooks/pre-write-guard.sh" }')
|
||
$WANT_WIKI && pt+=('{ "type": "command", "command": ".claude/hooks/wiki-secret-scan.sh" }')
|
||
local IFS=,
|
||
pretool_hooks="${pt[*]}"
|
||
|
||
printf '{\n "hooks": {\n'
|
||
if [ -n "$session_hooks" ]; then
|
||
printf ' "SessionStart": [\n { "matcher": "startup|resume|clear",\n "hooks": [ %s ] }\n ],\n' "$session_hooks"
|
||
fi
|
||
printf ' "PreToolUse": [\n { "matcher": "Write|Edit",\n "hooks": [ %s ] }\n ]\n' "$pretool_hooks"
|
||
printf ' }\n}\n'
|
||
}
|
||
|
||
if [ ! -f ".claude/settings.json" ]; then
|
||
build_hooks_json > .claude/settings.json
|
||
CREATED+=(".claude/settings.json $(tn "(依 $MODULE 模組產生)" "(generated for module: $MODULE)")")
|
||
else
|
||
SKIPPED+=(".claude/settings.json $(tn '(已存在,請手動合併 hooks)' '(already exists — merge hooks manually)')")
|
||
fi
|
||
|
||
# ── CLAUDE.md:只在完全不存在時建立 ────────────────
|
||
# 新建時把偵測到的 raw source 宣告 append 進去(在建立的當下寫入,
|
||
# 不回頭改使用者既有的 CLAUDE.md,維持「已有不覆蓋」原則)。
|
||
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 $(tn "← 已寫入 raw source 宣告(${VAULT_TYPE})" "← raw source declaration written (${VAULT_TYPE})")")
|
||
fi
|
||
else
|
||
SKIPPED+=("CLAUDE.md $(tn '(已存在,請手動加入對應區塊)' '(already exists — add the block manually)')")
|
||
fi
|
||
|
||
# ── 輸出結果 ──────────────────────────────────────
|
||
echo ""
|
||
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 ""
|
||
t "⚠️ 跳過(已存在):" "⚠️ Skipped (already exists):"
|
||
for item in "${SKIPPED[@]}"; do echo " - $item"; done
|
||
fi
|
||
|
||
# 來源抓不到要出聲,不能安靜吞掉——404 內容不會被寫進檔案(已在 download_if_missing 擋掉),
|
||
# 但使用者必須知道「這幾個檔沒裝到」,不然會誤以為裝完整了。
|
||
if [ ${#FAILED[@]} -gt 0 ]; then
|
||
echo ""
|
||
t "❌ 抓取失敗(來源不可達,未寫入任何檔案):" "❌ Fetch failed (source unreachable, no file was written):"
|
||
for item in "${FAILED[@]}"; do echo " x $item"; done
|
||
t " 請檢查網路,或用 TEMPLATE_SOURCE=<其他來源> 重跑。" \
|
||
" Check your network, or rerun with TEMPLATE_SOURCE=<alternate source>."
|
||
fi
|
||
|
||
echo ""
|
||
echo "─────────────────────────────────"
|
||
|
||
# CLAUDE.md 已存在 → 依模組提醒手動加區塊
|
||
if [ -f "CLAUDE.md" ]; then
|
||
if ! grep -q "raw source" CLAUDE.md; then
|
||
echo ""
|
||
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 ""
|
||
t "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:" \
|
||
"📌 CLAUDE.md exists but lacks the wiki reading order — please add it manually:"
|
||
echo ""
|
||
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 ""
|
||
t "📌 CLAUDE.md 已存在但缺少 SDD 鐵律,請手動加入:" \
|
||
"📌 CLAUDE.md exists but lacks the SDD iron rule — please add it manually:"
|
||
echo ""
|
||
if [ "$IS_ZH" = "yes" ]; then
|
||
cat <<'SNIP'
|
||
## 絕對鐵律
|
||
1. 任何 code 變動前必須有對應 SDD(docs/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" ]; then
|
||
MISSING_HOOKS=()
|
||
$WANT_WIKI && ! grep -q "session-start-recall.sh" .claude/settings.json && MISSING_HOOKS+=("SessionStart: session-start-recall.sh")
|
||
$WANT_WIKI && ! grep -q "wiki-secret-scan.sh" .claude/settings.json && MISSING_HOOKS+=("PreToolUse(Write|Edit): wiki-secret-scan.sh")
|
||
$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 ""
|
||
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 ""
|
||
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 ""
|
||
t "🚀 下一步:" "🚀 Next steps:"
|
||
if $WANT_WIKI; then
|
||
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
|
||
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
|
||
t " GitHub issue:CC 可直接 /issue-handle 讀回自己 repo 的 issue(禁自動輪詢)" \
|
||
" GitHub issues: CC can use /issue-handle to read issues from its own repo (no auto-polling)"
|
||
echo ""
|
||
|
||
# 有任何來源抓取失敗 → 用非零 exit code 出聲,不能靜靜地當「裝完了」收工。
|
||
if [ ${#FAILED[@]} -gt 0 ]; then
|
||
exit 1
|
||
fi
|