feat: install.sh 模組化安裝(--wiki / --sdd / --all)
有時只需要 wiki 不需要 SDD。不 fork,改用同一入口模組選單: - --wiki / --sdd / --all(預設),無參數 + 有 tty 則互動詢問 - curl|bash 無 tty 安全預設 --all - settings.json 的 hooks 依選的模組自動組裝(不再下載單一靜態檔) 不 fork 的理由:使用者多半非工程背景,一個入口最友善;維護成本不翻倍。 模組邊界先劃好,未來功能達 3+ 個再演進成「模板組合器」。 同步 wishlist:§1 接關 / §2 hook 標記已完成(commit 39783cc), 新增 §3 機敏防護 / §4 模組化安裝並標完成。README 補三層防護與模組安裝說明。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+174
-72
@@ -1,21 +1,82 @@
|
||||
#!/bin/bash
|
||||
# system-dev-template installer
|
||||
# 已有專案接入腳本——只建立缺少的東西,已有的一律不動
|
||||
# 已有專案接入腳本——只建立缺少的東西,已有的一律不動。
|
||||
#
|
||||
# 模組化安裝:
|
||||
# --wiki 只裝 LLM Wiki(記憶系統 + 機敏防護)
|
||||
# --sdd 只裝 SDD 系統(動 code 前必須有 design.md)
|
||||
# --all 兩個都裝(預設)
|
||||
# 無參數 互動式詢問
|
||||
#
|
||||
# 為什麼留在同一個 repo 用參數選,而不是 fork:
|
||||
# 使用者多半非專業,最怕「我要去哪個 repo」。一個入口 + 選單最友善。
|
||||
# 等未來功能多到 3+ 個再演進成「模板組合器」。模組邊界先在這裡劃好。
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
REPO_URL="https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/template"
|
||||
CREATED=()
|
||||
SKIPPED=()
|
||||
|
||||
# ── 解析模組參數 ──────────────────────────────────
|
||||
MODULE=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--wiki|--wiki-only) MODULE="wiki" ;;
|
||||
--sdd|--sdd-only) MODULE="sdd" ;;
|
||||
--all) MODULE="all" ;;
|
||||
-h|--help)
|
||||
cat <<'HELP'
|
||||
用法:install.sh [--wiki | --sdd | --all]
|
||||
--wiki 只裝 LLM Wiki(CC 記憶系統 + 機敏防護)
|
||||
--sdd 只裝 SDD 系統(動 code 前強制要有設計文件)
|
||||
--all 兩個都裝(預設)
|
||||
無參數 互動式詢問要裝哪個
|
||||
HELP
|
||||
exit 0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "🔧 system-dev-template installer"
|
||||
echo "================================="
|
||||
echo "只建立缺少的目錄和檔案,已有的不動。"
|
||||
echo ""
|
||||
|
||||
# ── 目錄 ──────────────────────────────────────────
|
||||
# ── 無參數 → 互動式詢問(給非專業使用者)──────────
|
||||
if [ -z "$MODULE" ]; then
|
||||
if [ -t 0 ]; then
|
||||
echo "要安裝哪一塊?"
|
||||
echo " 1) LLM Wiki —— 讓 CC 記住決策、不重複犯錯(含機敏防護)"
|
||||
echo " 2) SDD —— 動 code 前強制先有設計文件"
|
||||
echo " 3) 兩個都裝(推薦)"
|
||||
echo ""
|
||||
printf "請輸入 1 / 2 / 3 [預設 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 ""
|
||||
echo "📦 安裝模組:$MODULE"
|
||||
echo ""
|
||||
|
||||
# ── 工具函式 ──────────────────────────────────────
|
||||
create_dir() {
|
||||
if [ ! -d "$1" ]; then
|
||||
mkdir -p "$1"
|
||||
@@ -25,23 +86,10 @@ create_dir() {
|
||||
fi
|
||||
}
|
||||
|
||||
create_dir "docs/1-vision"
|
||||
create_dir "docs/2-architecture/decisions"
|
||||
create_dir "docs/3-specs"
|
||||
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/wiki"
|
||||
create_dir ".claude/commands"
|
||||
create_dir ".claude/hooks"
|
||||
|
||||
# ── 檔案(從 repo 下載,只在不存在時)──────────────
|
||||
|
||||
download_if_missing() {
|
||||
local dest="$1"
|
||||
local src="$2"
|
||||
local dest="$1" src="$2"
|
||||
if [ ! -f "$dest" ]; then
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
curl -sSL "$src" -o "$dest"
|
||||
CREATED+=("$dest")
|
||||
else
|
||||
@@ -49,67 +97,110 @@ download_if_missing() {
|
||||
fi
|
||||
}
|
||||
|
||||
# wiki 核心檔案
|
||||
download_if_missing ".claude/wiki/INDEX.md" "$REPO_URL/.claude/wiki/INDEX.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"
|
||||
|
||||
# slash commands
|
||||
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"
|
||||
download_if_missing ".claude/commands/sdd-check.md" "$REPO_URL/.claude/commands/sdd-check.md"
|
||||
|
||||
# hooks(軟規範 → 硬攔截。下載後補執行權限)
|
||||
download_if_missing ".claude/hooks/session-start-recall.sh" "$REPO_URL/.claude/hooks/session-start-recall.sh"
|
||||
download_if_missing ".claude/hooks/sdd-guard.sh" "$REPO_URL/.claude/hooks/sdd-guard.sh"
|
||||
download_if_missing ".claude/hooks/pre-write-guard.sh" "$REPO_URL/.claude/hooks/pre-write-guard.sh"
|
||||
chmod +x .claude/hooks/*.sh 2>/dev/null || true
|
||||
|
||||
# docs/README.md(分類地圖)
|
||||
# ── 共用結構(兩個模組都需要 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"
|
||||
|
||||
# CLAUDE.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/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"
|
||||
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"
|
||||
|
||||
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 (依 $MODULE 模組產生)")
|
||||
else
|
||||
SKIPPED+=(".claude/settings.json (已存在,請手動合併 hooks)")
|
||||
fi
|
||||
|
||||
# ── CLAUDE.md:只在完全不存在時建立 ────────────────
|
||||
if [ ! -f "CLAUDE.md" ]; then
|
||||
download_if_missing "CLAUDE.md" "$REPO_URL/CLAUDE.md"
|
||||
else
|
||||
SKIPPED+=("CLAUDE.md (已存在,請手動加入 wiki 讀取順序區塊)")
|
||||
fi
|
||||
|
||||
# .claude/settings.json:只在完全不存在時建立(比照 CLAUDE.md,不覆蓋既有設定)
|
||||
if [ ! -f ".claude/settings.json" ]; then
|
||||
download_if_missing ".claude/settings.json" "$REPO_URL/.claude/settings.json"
|
||||
else
|
||||
SKIPPED+=(".claude/settings.json (已存在,請手動加入 hooks 區塊)")
|
||||
SKIPPED+=("CLAUDE.md (已存在,請手動加入對應區塊)")
|
||||
fi
|
||||
|
||||
# ── 輸出結果 ──────────────────────────────────────
|
||||
|
||||
echo ""
|
||||
echo "✅ 建立了:"
|
||||
for item in "${CREATED[@]}"; do
|
||||
echo " + $item"
|
||||
done
|
||||
for item in "${CREATED[@]}"; do echo " + $item"; done
|
||||
|
||||
if [ ${#SKIPPED[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
echo "⚠️ 跳過(已存在):"
|
||||
for item in "${SKIPPED[@]}"; do
|
||||
echo " - $item"
|
||||
done
|
||||
for item in "${SKIPPED[@]}"; do echo " - $item"; done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "─────────────────────────────────"
|
||||
|
||||
# 如果 CLAUDE.md 已存在,提醒手動加入 wiki 區塊
|
||||
# CLAUDE.md 已存在 → 依模組提醒手動加區塊
|
||||
if [ -f "CLAUDE.md" ]; then
|
||||
if ! grep -q "wiki/status.md" CLAUDE.md; then
|
||||
if $WANT_WIKI && ! grep -q "wiki/status.md" CLAUDE.md; then
|
||||
echo ""
|
||||
echo "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序。"
|
||||
echo " 請手動加入以下區塊:"
|
||||
echo "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:"
|
||||
echo ""
|
||||
echo ' ## Wiki 讀取順序'
|
||||
echo ' | 檔案 | 時機 | 用途 |'
|
||||
@@ -118,26 +209,37 @@ if [ -f "CLAUDE.md" ]; then
|
||||
echo ' | `.claude/wiki/mistakes.md` | 做新功能前 | 已知誤解 |'
|
||||
echo ' | `.claude/wiki/decisions-summary.md` | 設計判斷時 | 架構決策 |'
|
||||
fi
|
||||
if $WANT_SDD && ! grep -q "docs/3-specs" CLAUDE.md; then
|
||||
echo ""
|
||||
echo "📌 CLAUDE.md 已存在但缺少 SDD 鐵律,請手動加入:"
|
||||
echo ""
|
||||
echo ' ## 絕對鐵律'
|
||||
echo ' 1. 任何 code 變動前必須有對應 SDD(docs/3-specs/[子系統]/design.md)'
|
||||
echo ' 找不到 → 停手問負責人,不要自行建立。'
|
||||
fi
|
||||
fi
|
||||
|
||||
# 如果 settings.json 已存在,提醒手動加入 hooks 區塊
|
||||
# settings.json 已存在 → 依模組提醒要合併哪些 hook
|
||||
if [ -f ".claude/settings.json" ] && grep -q '"已存在"' <<<"${SKIPPED[*]}" 2>/dev/null; then :; fi
|
||||
if [ -f ".claude/settings.json" ]; then
|
||||
if ! grep -q "session-start-recall.sh" .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 ""
|
||||
echo "📌 .claude/settings.json 已存在但缺少 hooks。"
|
||||
echo " 請手動把以下 hooks 合併進去(已有設定請保留):"
|
||||
echo ""
|
||||
echo ' "SessionStart": [{ "matcher": "startup|resume|clear",'
|
||||
echo ' "hooks": [{ "type": "command", "command": ".claude/hooks/session-start-recall.sh" }] }],'
|
||||
echo ' "PreToolUse": [{ "matcher": "Write|Edit",'
|
||||
echo ' "hooks": [{ "type": "command", "command": ".claude/hooks/sdd-guard.sh" },'
|
||||
echo ' { "type": "command", "command": ".claude/hooks/pre-write-guard.sh" }] }]'
|
||||
echo "📌 .claude/settings.json 已存在,請手動把以下 hooks 合併進去(保留既有設定):"
|
||||
for h in "${MISSING_HOOKS[@]}"; do echo " • $h"; done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🚀 下一步:在 Claude Code 對話裡執行:"
|
||||
echo " /wiki-init"
|
||||
echo ""
|
||||
echo " CC 會掃描現有文件、建立 wiki、整理 docs 結構。"
|
||||
echo "🚀 下一步:"
|
||||
if $WANT_WIKI; then
|
||||
echo " 在 Claude Code 對話裡執行 /wiki-init"
|
||||
echo " CC 會掃描現有文件、套用 .wikiignore、建立 wiki。"
|
||||
fi
|
||||
if $WANT_SDD; then
|
||||
echo " 動 code 前先在 docs/3-specs/[子系統]/ 建 design.md(可用 /sdd-check 協助)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user