feat: 接關 hook + SDD 強制 hook(實作 wishlist 兩項)
§1 接關機制(雙保險): - session-start-recall.sh:SessionStart 自動注入 status 重點 + 快照核實提醒 - /wiki-recall:fallback 命令,hook 失效時手動接關 §2 軟規範 → 硬攔截: - sdd-guard.sh:動 code 檔但無 SDD → exit 2 擋(/sdd-check 自動版) - pre-write-guard.sh:專案自訂禁令骨架(預設停用) - settings.json:掛 SessionStart + PreToolUse 配套:install.sh 下載 hooks/settings(settings 比照 CLAUDE.md 不覆蓋); README/CLAUDE.md 補文件 + 誠實限制聲明。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# /wiki-recall — Session 開始,手動接關
|
||||
|
||||
開新對話時接上次進度。**Fallback 命令**:SessionStart hook 沒啟動時手動接關;要完整脈絡時也用。
|
||||
|
||||
> 主路徑是 SessionStart hook 自動注入 status 重點,不靠你打命令。
|
||||
> 這支命令應對 hook 失效,以及需要比「status 重點」更完整脈絡的時候。
|
||||
|
||||
---
|
||||
|
||||
## 命名閉環
|
||||
|
||||
init(建) → update(存,session 末) ↔ **recall(接,session 初)** → capture(隨時存結論)
|
||||
|
||||
---
|
||||
|
||||
## 執行流程
|
||||
|
||||
### 第一步:讀 status.md(當前進度)
|
||||
|
||||
讀 `.claude/wiki/status.md`,掌握:
|
||||
- 正在做什麼、阻擋點
|
||||
- 下次 session 第一件事
|
||||
- 待負責人確認、已知問題
|
||||
|
||||
### 第二步:讀 decisions-summary.md(為什麼這樣做)
|
||||
|
||||
讀 `.claude/wiki/decisions-summary.md`,掌握相關的架構決策——避免重新討論已定案的事。
|
||||
|
||||
### 第三步:讀 mistakes.md(別重犯)
|
||||
|
||||
讀 `.claude/wiki/mistakes.md`,掌握已知誤解 + 快速檢查清單。
|
||||
|
||||
### 第四步:掃 wishlist / HANDOFF(如果有)
|
||||
|
||||
- `docs/wishlist.md`:待補功能
|
||||
- 任何 `HANDOFF.md` / 交接note:上一棒留下的脈絡
|
||||
|
||||
### 第五步:回報接關結果
|
||||
|
||||
```
|
||||
📍 接關完成
|
||||
🔄 上次正在做:[status 的「正在做」]
|
||||
🎯 下次第一件事:[status 的「下次 session 第一件事」]
|
||||
⚠️ 待確認:[如有]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 鐵律:快照非即時狀態
|
||||
|
||||
status / wiki 是 **point-in-time 快照,不是即時狀態**。
|
||||
|
||||
接關 = 讀快照 **+ 核實快照**,**不盲信**。
|
||||
|
||||
> 實例:某專案 status 曾寫「待 A 收尾 X」,實際 X 早已完成。
|
||||
> 照舊資訊行動會去催一件已完成的事。
|
||||
|
||||
動手前,先用當前 code / git / 檔案核實快照寫的事項是否仍成立。發現落差 → 先更新 status,再動手。
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# PreToolUse hook 範本骨架 —— 專案自訂禁令
|
||||
# wishlist §2 可選:讓使用者自訂專案禁令(例:「KBDB 禁動表」「某目錄唯讀」)。
|
||||
#
|
||||
# 預設不啟用。要用時:
|
||||
# 1. 在下面 FORBIDDEN_PATTERNS 填入禁改的路徑/檔名 pattern
|
||||
# 2. 到 .claude/settings.json 的 PreToolUse 加掛這支
|
||||
#
|
||||
# 掛在 PreToolUse(matcher: Write|Edit)。stdin 收到 JSON:{ tool_name, tool_input: { file_path } }
|
||||
# 命中禁令 → exit 2 擋。
|
||||
#
|
||||
# 誠實限制:只擋直接寫檔。bash 繞道、helper 間接改動擋不到。留痕可審 ≠ 技術防偽。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── 專案自訂:禁改的 pattern(一行一個,case glob 語法)──────
|
||||
# 範例(已註解,啟用前請改成自己的):
|
||||
# "*/db/schema.sql" # 禁手改 schema
|
||||
# "*/migrations/*" # migration 一旦建立不可改
|
||||
FORBIDDEN_PATTERNS=(
|
||||
# "*/your/protected/path/*"
|
||||
)
|
||||
|
||||
# 沒設任何禁令 → 直接放行(骨架預設狀態)
|
||||
[ ${#FORBIDDEN_PATTERNS[@]} -eq 0 ] && exit 0
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
|
||||
else
|
||||
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
||||
fi
|
||||
|
||||
[ -z "$FILE_PATH" ] && exit 0
|
||||
|
||||
for pattern in "${FORBIDDEN_PATTERNS[@]}"; do
|
||||
# shellcheck disable=SC2254
|
||||
case "$FILE_PATH" in
|
||||
$pattern)
|
||||
cat >&2 <<EOF
|
||||
🚫 專案禁令攔截:$FILE_PATH 命中禁改規則($pattern)。
|
||||
|
||||
這是本專案 .claude/hooks/pre-write-guard.sh 設定的硬底線。
|
||||
要動 → 先和負責人確認,並更新禁令設定。
|
||||
EOF
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
exit 0
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
# PreToolUse hook — 動 code 前檢查有沒有對應 SDD
|
||||
# wishlist §2:把 /sdd-check 從「命令要人打」升級成「hook 自動攔」。
|
||||
#
|
||||
# 掛在 settings.json 的 PreToolUse(matcher: Write|Edit)。
|
||||
# stdin 收到 JSON:{ tool_name, tool_input: { file_path, ... } }
|
||||
# 行為:動到 code 檔(.ts/.go/...)但 docs/3-specs/ 下沒有任何 SDD → 警告(exit 2 擋)。
|
||||
#
|
||||
# 誠實限制(抄 arcrun):只擋語法層明顯違規(直接寫 code 檔)。
|
||||
# 藏在 helper 裡、用 bash 繞道的改動擋不到。
|
||||
# 價值是「想跳過會被抓到 + 留痕可審」,不是技術防偽。絕不聲稱「不可能繞過」。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
# 解析 file_path。優先用 jq,沒有 jq 退回 grep(容錯)。
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
|
||||
else
|
||||
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
||||
fi
|
||||
|
||||
# 拿不到路徑 → 不擋(容錯,寧可放過也不誤殺)
|
||||
[ -z "$FILE_PATH" ] && exit 0
|
||||
|
||||
# 只管 code 檔。docs/markdown/設定檔等放行。
|
||||
case "$FILE_PATH" in
|
||||
*.ts|*.tsx|*.js|*.jsx|*.go|*.py|*.rs|*.java|*.rb|*.php|*.c|*.cpp|*.h|*.hpp|*.swift|*.kt) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# 改 SDD 自己 / 測試檔 → 放行
|
||||
case "$FILE_PATH" in
|
||||
*docs/3-specs/*) exit 0 ;;
|
||||
*_test.*|*.test.*|*.spec.*|*/tests/*|*/test/*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# docs/3-specs/ 下完全沒有 design.md → 攔
|
||||
SDD_COUNT=0
|
||||
if [ -d "docs/3-specs" ]; then
|
||||
SDD_COUNT=$(find docs/3-specs -name 'design.md' -not -path '*TEMPLATE*' 2>/dev/null | wc -l | tr -d ' ')
|
||||
fi
|
||||
|
||||
if [ "$SDD_COUNT" -eq 0 ]; then
|
||||
cat >&2 <<EOF
|
||||
🚫 SDD 協議攔截:要動 code 檔 ($FILE_PATH),但 docs/3-specs/ 下找不到任何 SDD。
|
||||
|
||||
絕對鐵律:任何 code 變動前必須有對應 SDD(design.md)。
|
||||
|
||||
請先:
|
||||
1. 確認這個改動屬於哪個子系統
|
||||
2. 在 docs/3-specs/[子系統]/ 建立 design.md(可用 /sdd-check 協助)
|
||||
3. 在回覆開頭宣告已讀 SDD + 對應 task
|
||||
|
||||
小修改(修 bug、改文字)若確定豁免,請明確說明範圍後由人放行。
|
||||
EOF
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# 有 SDD:放行,但留痕提醒要宣告(stderr 警告,不擋)
|
||||
echo "📋 提醒:docs/3-specs/ 下有 SDD。動手前請確認已讀對應 design.md 並在回覆宣告。" >&2
|
||||
exit 0
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
# SessionStart hook — 開 session 自動注入 status.md 重點
|
||||
# wishlist §1 主路徑:不靠 CC 自覺、不用人說,開 session 就把進度推到眼前。
|
||||
#
|
||||
# 掛在 settings.json 的 SessionStart(matcher: startup|resume|clear)。
|
||||
# stdout 會被當成 context 注入給 CC。
|
||||
#
|
||||
# 鐵律:status 是 point-in-time 快照,非即時狀態。
|
||||
# 這個 hook 只負責「把快照推到眼前」,核實快照是 CC 的責任——下面的提醒就是要它別盲信。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
STATUS_FILE=".claude/wiki/status.md"
|
||||
|
||||
# 沒有 wiki 就安靜退出(exit 0),不干擾還沒 /wiki-init 的專案
|
||||
if [ ! -f "$STATUS_FILE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "════════════════════════════════════════════════"
|
||||
echo "📍 接關:上次進度(來自 $STATUS_FILE 快照)"
|
||||
echo "════════════════════════════════════════════════"
|
||||
echo ""
|
||||
cat "$STATUS_FILE"
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────────"
|
||||
echo "⚠️ 以上是 point-in-time 快照,非即時狀態。"
|
||||
echo " 動手前先核實:快照寫的事項是否真的還沒做完?"
|
||||
echo " 需要完整脈絡(decisions / mistakes / SDD)→ 執行 /wiki-recall"
|
||||
echo "════════════════════════════════════════════════"
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "startup|resume|clear",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/hooks/session-start-recall.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/hooks/sdd-guard.sh"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/hooks/pre-write-guard.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user