Files
system-dev-template/scripts/install.sh
T
Leo d10659bd2d feat: install.sh 偵測 vault 類型寫入 raw source + Cowork 整理 skill
- install.sh:建立 CLAUDE.md 前偵測資料夾類型,寫入對應 raw source
  - logseq/ → pages/, journals/
  - .obsidian/ → 根目錄 .md
  - 都沒有 → docs/(維持原行為)
  宣告區塊對 vault 明令不得搬動/改名 .md,整理只寫進 .claude/wiki/。
  已有 CLAUDE.md 一律不覆蓋,改列提醒手動補。
- docs/SKILL.md:給 claude.ai Cowork 的 wiki-cowork-scan skill,
  與 CC 共用同一套偵測與整理規則。
- README:新增 vault 支援、Cowork 整理兩節,更新目錄樹。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:01:21 +08:00

320 lines
13 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# system-dev-template installer
# 已有專案接入腳本——只建立缺少的東西,已有的一律不動。
#
# 模組化安裝:
# --wiki 只裝 LLM Wiki(記憶系統 + 機敏防護)
# --sdd 只裝 SDD 系統(動 code 前必須有 design.md
# --all 兩個都裝(預設)
# 無參數 互動式詢問
#
# 為什麼留在同一個 repo 用參數選,而不是 fork:
# 使用者多半非專業,最怕「我要去哪個 repo」。一個入口 + 選單最友善。
# 等未來功能多到 3+ 個再演進成「模板組合器」。模組邊界先在這裡劃好。
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 WikiCC 記憶系統 + 機敏防護)
--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 ""
# ── 偵測 vault 類型 → 決定 raw source(原始文件)路徑 ──────────
# 為什麼:這個模板原本假設「原始文件在 docs/」,但 Logseq / Obsidian
# 這種 PKM vault 有自己的目錄慣例,整理時不能照 docs/ 那套搬動,
# 否則會破壞 vault 結構、讓筆記變不可讀。
# 偵測結果寫進 CLAUDE.md,讓 CC 和未來的 Cowork skill 都知道
# 「該讀/該整理哪裡」而不是亂動。
# 必須在建立 CLAUDE.md 之前跑完。
VAULT_TYPE=""
RAW_SOURCE=""
if [ -d "logseq" ]; then
VAULT_TYPE="logseq"
RAW_SOURCE="pages/, journals/"
elif [ -d ".obsidian" ]; then
VAULT_TYPE="obsidian"
RAW_SOURCE="./ (整個 vault 根目錄的 .md"
else
VAULT_TYPE="docs"
RAW_SOURCE="docs/"
fi
echo "🗂️ 偵測到 vault 類型:$VAULT_TYPE → raw source$RAW_SOURCE"
echo ""
# 把「raw source 宣告區塊」吐出來,給新建的 CLAUDE.md append 或
# 給已存在的 CLAUDE.md 當手動補貼的提示。內容對 CC / Cowork 都是
# 機器可讀的指令(明確路徑 + 不可破壞 vault 結構的約束)。
emit_raw_source_block() {
cat <<BLOCK
---
## 原始文件空間(raw source
> 安裝時偵測到的 vault 類型:**$VAULT_TYPE**
> CC 與 Cowork 整理/讀取「人寫的原始文件」時,**只在這裡找、只在這裡動**。
| 項目 | 值 |
|------|----|
| vault 類型 | \`$VAULT_TYPE\` |
| 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 本身**。
BLOCK
fi
}
# ── 工具函式 ──────────────────────────────────────
create_dir() {
if [ ! -d "$1" ]; then
mkdir -p "$1"
CREATED+=("$1/")
else
SKIPPED+=("$1/ (已存在)")
fi
}
download_if_missing() {
local dest="$1" src="$2"
if [ ! -f "$dest" ]; then
mkdir -p "$(dirname "$dest")"
curl -sSL "$src" -o "$dest"
CREATED+=("$dest")
else
SKIPPED+=("$dest (已存在,跳過)")
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/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"
# ── 共用指引: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 (依 $MODULE 模組產生)")
else
SKIPPED+=(".claude/settings.json (已存在,請手動合併 hooks)")
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 ← 已寫入 raw source 宣告($VAULT_TYPE")
fi
else
SKIPPED+=("CLAUDE.md (已存在,請手動加入對應區塊)")
fi
# ── 輸出結果 ──────────────────────────────────────
echo ""
echo "✅ 建立了:"
for item in "${CREATED[@]}"; do echo " + $item"; done
if [ ${#SKIPPED[@]} -gt 0 ]; then
echo ""
echo "⚠️ 跳過(已存在):"
for item in "${SKIPPED[@]}"; do echo " - $item"; done
fi
echo ""
echo "─────────────────────────────────"
# CLAUDE.md 已存在 → 依模組提醒手動加區塊
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:"
emit_raw_source_block | sed 's/^/ /'
fi
if $WANT_WIKI && ! grep -q "wiki/status.md" CLAUDE.md; then
echo ""
echo "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:"
echo ""
echo ' ## Wiki 讀取順序'
echo ' | 檔案 | 時機 | 用途 |'
echo ' |------|------|------|'
echo ' | `.claude/wiki/status.md` | session 開始第一件事 | 當前進度 |'
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 變動前必須有對應 SDDdocs/3-specs/[子系統]/design.md'
echo ' 找不到 → 停手問負責人,不要自行建立。'
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")
$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 合併進去(保留既有設定):"
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 掛上才會生效。"
echo ""
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 " GitHub issueCC 可直接 /issue-handle 讀回自己 repo 的 issue(禁自動輪詢)"
echo ""