chore: template 1.16.0——wiki 讀取兩支 hook(查詢即搜尋 wiki+subagent 自動注入)
leo 2026-07-20:「花很多力氣去產生 wiki,最重要的就是要可以查詢, 結果要查的時候就跳過,那就白寫了」 - wiki-first-search.sh(Grep|Glob|Read):查 code 的當下用同組關鍵字 grep wiki,只推命中行 - subagent-wiki-guard.sh(Task):查證類任務自動注入「先查 wiki」給 subagent - update.sh/install.sh 來源改指 Gitea(原指 GitHub uncle6me-web 已 suspend=自動更新早就死了) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
# /wiki-extract — vault 增量萃取(Logseq / Obsidian → system-dev/wiki)
|
||||
|
||||
把**筆記 vault**(Logseq graph 如 `notes`/`kb`、或 Obsidian)的原始筆記,**增量、冪等**地
|
||||
萃成 `system-dev/wiki/` 的精耕卡+`[[wikilink]]`。這是知識一庫 ingest 的**前段**:
|
||||
AI 只產卡片檔,下游 Arcrun ingest 再從 wikilink 機械拉三元組進 KBDB。
|
||||
|
||||
> **跟 `/wiki-init` 的分工**:
|
||||
> - `/wiki-init` 是**首次**建結構 + 全庫首萃(一次性)。
|
||||
> - `/wiki-extract` 是**之後每次**的增量重萃——vault 會被 Syncthing/cron 持續灌新筆記,
|
||||
> 這支負責「只萃變動的、沒變的不碰、不浪費 AI run」。給 Routine / cloud-worker 反覆跑。
|
||||
> - **跑它的是你(CC / Routine)=LLM 本人,不需任何 token**。
|
||||
|
||||
> **邊界(硬規矩,別越界)**
|
||||
> - 只往 `system-dev/wiki/` 寫。**絕不寫入 KBDB、絕不拉三元組紀錄**——三元組是下游
|
||||
> Arcrun 從你產的 `[[wikilink]]` + `## 關聯` 機械映射(另一張 issue),不是這支的事。
|
||||
> - **原始筆記唯讀**:`journals/`、`pages/`、Obsidian 根 `.md` 是 leo 的手寫真身,
|
||||
> 改了會被 Syncthing 推回他手機污染筆記 App。萃取=只讀原文、只寫 wiki。
|
||||
> - **D16 精耕非 RAG**:萃「知識點」成自包含原子卡 + 建 wikilink,**不地毯灌原文全文**。
|
||||
|
||||
---
|
||||
|
||||
## 執行流程
|
||||
|
||||
### 第一步:確認這是 vault repo,定位 raw source
|
||||
|
||||
偵測邏輯**同 install.sh / wiki-init**:
|
||||
|
||||
| 偵測到 | 型態 | raw source(要掃的原文) |
|
||||
|--------|------|--------------------------|
|
||||
| 根目錄有 `logseq/` | Logseq vault | `journals/*.md` + `pages/*.md` |
|
||||
| 根目錄有 `.obsidian/` | Obsidian vault | vault 根下所有 `.md` |
|
||||
| 都沒有 | **不是 vault** | → 停手。這支只處理 vault;一般 dev repo 開發時就手寫 `.claude`/`system-dev/wiki`,不需萃取 |
|
||||
|
||||
沒有 `system-dev/wiki/`?→ 先跑 `/wiki-init`(首次建結構+首萃),再回來用這支做增量。
|
||||
|
||||
### 第二步:content_hash 冪等 —— 決定哪些檔要萃(省 run 的核心)
|
||||
|
||||
讀萃取 manifest:`system-dev/wiki/.extract-manifest.json`(不存在=首次,視同全部要萃)。
|
||||
格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"algo": "sha256",
|
||||
"sources": {
|
||||
"journals/2026_07_01.md": {
|
||||
"content_hash": "<sha256 of file bytes>",
|
||||
"extracted_at": "2026-07-06",
|
||||
"cards": ["Prompt能力即拆解自己邏輯的能力", "程式化邏輯可圖解任何主題不限AI"],
|
||||
"skipped_reason": null
|
||||
},
|
||||
"journals/2026_06_25.md": {
|
||||
"content_hash": "<sha256>",
|
||||
"extracted_at": "2026-07-06",
|
||||
"cards": [],
|
||||
"skipped_reason": "空檔/訊息量不足,無可萃知識點"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
對每個 raw source 檔:
|
||||
|
||||
1. 算目前 `content_hash`(`sha256sum <file>`,取檔案 bytes 的 hash)。
|
||||
2. 跟 manifest 裡該檔的 `content_hash` 比:
|
||||
- **相同 → skip,不讀不萃、不呼叫任何 AI 推理**(就算它上次 `cards: []` 也 skip——空檔沒變還是空)。
|
||||
- **不同或不在 manifest → 這檔要(重)萃**。
|
||||
3. manifest 有、但檔已不存在 → 該檔被刪,把它的 entry 從 manifest 移除(卡片是否連帶處理見第五步)。
|
||||
|
||||
> **這一步是「省 run」的重點**:vault 每天可能只動 1~2 個 journal,其餘幾十個檔 hash 沒變
|
||||
> 就整批跳過,AI 只對真正變動的檔動腦。**重跑一個沒變動的 vault = 零 AI 呼叫、零 diff。**
|
||||
|
||||
### 第三步:對「要萃」的檔,抓知識點 + 任務
|
||||
|
||||
逐個變動檔讀原文,分兩類抽取:
|
||||
|
||||
**(a) 知識點 → 概念原子卡**
|
||||
判準與卡片格式**完全依 `/wiki-init` 第五步**(frontmatter `tags:`/`gloss:`、H1、麵包屑
|
||||
`← [[<bucket>/00-INDEX]]`、`**來源**`、`## 摘要`、`## 重點`、`## 實體`、`## 關聯` 的
|
||||
typed-edge 三元組、TAXONOMY 受控標籤、硬自檢等)——**不在這裡重寫格式,一律回去讀那份**。
|
||||
廢話/訊息量薄的段落略過(在 manifest 記 `skipped_reason`,誠實留痕、不留卡)。
|
||||
|
||||
**(b) Logseq 任務 marker → 任務卡(task_status)**
|
||||
解析**完全依** `system-dev/docs/4-guides/logseq-markers.md`(單一真相源,與 template#4
|
||||
tasks 投影共用同一套;**別自己另寫 mapping**)。摘要:
|
||||
|
||||
- 任務行 regex:`^\s*- (TODO|DOING|NOW|LATER|WAITING|DONE|CANCELED|CANCELLED)\s+`
|
||||
- 狀態正規化:TODO/LATER→`todo`、DOING/NOW→`in-progress`、WAITING→`blocked`、
|
||||
DONE→`done`、CANCELED/CANCELLED→`closed`。
|
||||
- 跳過 `:LOGBOOK:…:END:` 區塊與 `key:: value` 屬性行(`collapsed::`、`id::`、
|
||||
`SCHEDULED::`、`DEADLINE::`…),**別把 marker 或屬性當任務內文**。
|
||||
|
||||
有實質內容的任務 → 產一張任務卡進 `cards/tasks/` bucket,frontmatter 帶 `task_status`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
tags: [<領域標籤,依 TAXONOMY>]
|
||||
task_status: todo # ← 依上表正規名;這是任務卡才有的欄位
|
||||
gloss: 一句話定義這個任務要達成什麼(供下游 normalize)
|
||||
---
|
||||
# <任務一句話標題(marker 後的內文,去掉 marker)>
|
||||
|
||||
← [[tasks/00-INDEX]]
|
||||
|
||||
**來源**:`journals/2026_07_01.md`(TODO block)
|
||||
**最後更新**:YYYY-MM-DD
|
||||
|
||||
## 摘要
|
||||
[任務要做什麼、脈絡]
|
||||
|
||||
## 實體
|
||||
- **<關鍵實體正規名>**(<同義詞>)— <一句描述>
|
||||
|
||||
## 關聯
|
||||
### 內文知識關係(端點=上方 `## 實體` 正規名,一字不差)
|
||||
- <實體A> >> <謂詞> >> <實體B>
|
||||
### 卡片關係(卡對卡)
|
||||
- [[本任務卡]] >> 涉及 >> [[相關概念卡]]
|
||||
```
|
||||
|
||||
> 純瑣事任務(「買菜」這種無知識量)不必成獨立卡——可在 `cards/tasks/00-INDEX.md`
|
||||
> 列一行帶狀態即可,避免灌垃圾卡。判準同 D16:有沒有知識/專案價值。
|
||||
|
||||
### 第四步:更新桶索引與 INDEX
|
||||
|
||||
- 每個動到的 bucket(如 `cards/notes/`、`cards/tasks/`)更新其 `00-INDEX.md`
|
||||
(容器:只連不重寫,H2/H3 分節)。
|
||||
- 更新 `system-dev/wiki/INDEX.md` 的標籤視圖與卡片清單。
|
||||
- 任務卡可在 INDEX 開一個「任務視圖」按 `task_status` 聚類。
|
||||
|
||||
### 第五步:寫回 manifest + 驗證原文 0 動
|
||||
|
||||
1. 把這次萃過的每個檔的**新 `content_hash`**、`extracted_at`、產出的 `cards`、
|
||||
(或 `skipped_reason`)寫回 `system-dev/wiki/.extract-manifest.json`。
|
||||
**沒動到的檔的 entry 原樣保留**(別整檔重寫掉別人的 hash)。
|
||||
2. 驗證原文零異動(踩過的坑):
|
||||
```
|
||||
git status --short journals/ pages/ # Obsidian 則看根目錄 .md ——須 0 新增 0 修改
|
||||
```
|
||||
有任何原文變動 → 你誤寫了 raw source,回滾。
|
||||
|
||||
### 第六步:完成報告
|
||||
|
||||
```
|
||||
✅ wiki-extract 完成(增量)
|
||||
掃描:N 個 raw source 檔
|
||||
萃取:M 個(content_hash 變動)→ 產出 X 張概念卡 + Y 張任務卡
|
||||
跳過:K 個(hash 未變,零 AI 呼叫)
|
||||
任務狀態分布:todo A / in-progress B / done C / …
|
||||
原文驗證:journals/ pages/ git status 0 異動 ✅
|
||||
manifest:system-dev/wiki/.extract-manifest.json 已更新
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 冪等自檢(Routine 反覆跑必守)
|
||||
|
||||
- [ ] 跑之前先讀 manifest,hash 相同的檔**完全不進 AI**(不是「讀了才發現一樣」,是靠 hash 先擋)。
|
||||
- [ ] 對「同一個沒變動的 vault」連跑兩次:第二次應是**零萃取、零卡片 diff、零 manifest 變化**。
|
||||
- [ ] 只有 `system-dev/wiki/` 有寫入;`journals/`、`pages/` git status 全乾淨。
|
||||
- [ ] 任務狀態用正規名,marker/屬性沒混進內文(照 `logseq-markers.md` 自檢)。
|
||||
@@ -228,3 +228,8 @@ git status --short pages/ journals/ # 或一般專案的 docs/ ——須 0
|
||||
原文驗證:pages/ journals/ git status 0 異動 ✅
|
||||
下一步:用 /wiki-capture 把重要決策存進 wiki
|
||||
```
|
||||
|
||||
> **vault repo 首萃後的增量重萃**:Logseq / Obsidian vault 會被持續灌新筆記。首萃(本命令)
|
||||
> 之後,改用 **`/wiki-extract`** 做增量——它靠 content_hash 只萃變動的檔(沒變=零 AI 呼叫),
|
||||
> 並解析 Logseq 大寫任務 marker(TODO/DOING/DONE…→ `task_status`,見
|
||||
> `system-dev/docs/4-guides/logseq-markers.md`)。適合掛給 Routine / cloud-worker 反覆跑。
|
||||
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
# subagent-wiki-guard.sh — PreToolUse(Task) hook:subagent 聽到「查」就自己先查 wiki
|
||||
#
|
||||
# 病根(2026-07-20):總管兩次派 agent 查 ENCRYPTION_KEY,prompt 都只叫它「去查 repo 程式碼」。
|
||||
# agent 於是從**稿子**推論出「這東西還活著、不能動」,總管照單全收去擋 leo 三輪。
|
||||
#
|
||||
# 🔑 設計轉向(leo 2026-07-21):
|
||||
# 第一版是「上游沒交代讀 wiki 就擋下」——但那**還是依賴上游記得寫**,
|
||||
# 跟「我記得讀 wiki」是同一個病。leo 點破:
|
||||
# 「subagent 的問題跟你一樣。你叫它去查,就算你沒說要先查 wiki,
|
||||
# 但它**只要聽到查,就應該主動查 wiki**,因為每個 repo 都有維護自己的 wiki。」
|
||||
# → 改成 **注入式**:不擋、不要求上游改 prompt,直接把「先查 wiki」這條
|
||||
# 以 additionalContext 注入給 subagent,讓它自己做。零依賴任何人記得。
|
||||
#
|
||||
# 行為:偵測到查證/實作類任務 → exit 0 並用 hookSpecificOutput 注入指示。
|
||||
# 已含 wiki 指示、或非查證類任務 → 靜默放行(不重複注入)。
|
||||
set -euo pipefail
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
PROMPT=$(printf '%s' "$INPUT" | python3 -c "
|
||||
import json,sys
|
||||
try:
|
||||
d=json.load(sys.stdin)
|
||||
print(d.get('tool_input',{}).get('prompt',''))
|
||||
except Exception: print('')
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
[ -z "$PROMPT" ] && exit 0
|
||||
|
||||
# 上游已經交代了 → 不必重複注入
|
||||
if printf '%s' "$PROMPT" | grep -qiE "wiki|agent-memory|mistakes\.md|decisions-summary"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 只對「查證/實作」類任務注入(純寫作、計算、潤稿等不需要)
|
||||
if ! printf '%s' "$PROMPT" | grep -qiE "查|盤點|核實|確認|調查|研究|找出|repo|程式碼|原始碼|source|實作|移除|刪除|重構|修|grep|codebase|\.ts|\.go|src/"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
|
||||
guidance = """【自動注入:查任何東西之前,先查 wiki】
|
||||
|
||||
你所在的 repo 有維護自己的 wiki(通常在 `system-dev/wiki/`,舊結構在 `.claude/wiki/`)。
|
||||
**接到「查/盤點/核實/實作」類任務時,第一個動作是搜尋 wiki,不是翻程式碼。**
|
||||
|
||||
做法(30 秒,省下大量白工):
|
||||
grep -rin "<本題關鍵字>" system-dev/wiki/ 2>/dev/null || grep -rin "<關鍵字>" .claude/wiki/
|
||||
|
||||
為什麼這是划算的:
|
||||
• wiki 是前人已經查過、驗證過、被負責人糾正過的結論——**判準**。
|
||||
• 程式碼與歷史文件是**稿子**:它反映「還沒清乾淨」,不等於「還在用」。
|
||||
從稿子推論會系統性得出過時結論。
|
||||
• wiki 沒記載,才值得花力氣翻原文。
|
||||
|
||||
三條硬規則:
|
||||
1. **wiki 與程式碼衝突 → 以 wiki 為準**,並在回報中明確指出衝突,
|
||||
不要自行用 code 推翻 wiki。
|
||||
2. wiki 寫「不可動/待廢除/進行中」→ **讀它的解除條件並逐條核對**。
|
||||
那是當時狀態,不是永久禁令;條件已滿足就是可動。
|
||||
(2026-07-20 實際事故:agent 只看到「不可動」就回報不能動,
|
||||
實際上解除條件早已滿足,害負責人被擋三輪。)
|
||||
3. 翻原文後若得到**新結論**,回報時明講「wiki 該更新」——wiki 過時是債,要還。
|
||||
"""
|
||||
|
||||
print(json.dumps({
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"additionalContext": guidance
|
||||
}
|
||||
}, ensure_ascii=False))
|
||||
PY
|
||||
|
||||
exit 0
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# wiki-first-search.sh — PreToolUse hook:要去翻原文/程式碼前,先把 wiki 命中結果推到眼前
|
||||
#
|
||||
# 病根(2026-07-20 leo 點破,mistakes 第一鐵律):
|
||||
# 總管 session 開頭讀了 agent-memory 前 50 行就開工,關鍵那條在第 56 行 → 拿過期記憶擋了 leo 三輪。
|
||||
# leo:「如果你不是讀而是**搜尋** wiki 就不會只讀 50 行就下定論,
|
||||
# 而是就像我直接在頁面 cmd+F,那些都會高亮。」
|
||||
#
|
||||
# 設計要點(為什麼是這個形狀):
|
||||
# 1. **搜尋 ≠ 通讀**:開場 push 全文(session-start-recall.sh)解決不了這題——量大必然只讀開頭。
|
||||
# 這支反過來:在「你正要去查 code/原文」的當下,用你自己的關鍵字 grep wiki,只推命中行。
|
||||
# 2. **時機是關鍵**:不是開場推、不是寫入時擋,而是**查詢動作發生的那一刻**介入。
|
||||
# 3. **提醒不阻擋**(exit 0):wiki 沒記載時本來就該去翻原文,擋下來反而礙事。
|
||||
# 唯一目的是消滅「不知道 wiki 有寫」這件事。
|
||||
#
|
||||
# 觸發:Grep / Glob / Read 打向 code 或 docs 時(見下方 should_check)。
|
||||
# 輸出:stdout 注入 context(命中的 wiki 行 + 檔名:行號)。
|
||||
set -euo pipefail
|
||||
|
||||
INPUT=$(cat)
|
||||
TOOL=$(printf '%s' "$INPUT" | python3 -c "import json,sys;print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null || echo "")
|
||||
|
||||
# 取出這次查詢的關鍵字:Grep 用 pattern,Glob/Read 用路徑的檔名部分
|
||||
QUERY=$(printf '%s' "$INPUT" | python3 -c "
|
||||
import json,sys,os,re
|
||||
try:
|
||||
d=json.load(sys.stdin); ti=d.get('tool_input',{})
|
||||
q = ti.get('pattern') or ''
|
||||
if not q:
|
||||
p = ti.get('file_path') or ti.get('path') or ''
|
||||
q = os.path.splitext(os.path.basename(p))[0] if p else ''
|
||||
# grep pattern 常含 regex 元字元;取最長的英數/底線詞當搜尋詞
|
||||
words = re.findall(r'[A-Za-z_][A-Za-z0-9_]{3,}', q)
|
||||
print(max(words, key=len) if words else '')
|
||||
except Exception:
|
||||
print('')
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
[ -z "$QUERY" ] && exit 0
|
||||
|
||||
WIKI_DIR="system-dev/wiki"
|
||||
[ -d "$WIKI_DIR" ] || exit 0
|
||||
|
||||
# 只在「查程式碼/文件」時提醒;查 wiki 本身就不用了(已經在讀了)
|
||||
TARGET=$(printf '%s' "$INPUT" | python3 -c "
|
||||
import json,sys
|
||||
try:
|
||||
d=json.load(sys.stdin); ti=d.get('tool_input',{})
|
||||
print(ti.get('file_path') or ti.get('path') or '')
|
||||
except Exception: print('')
|
||||
" 2>/dev/null || echo "")
|
||||
case "$TARGET" in
|
||||
*system-dev/wiki*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# grep wiki(不分大小寫、含行號),最多 12 行避免洗版
|
||||
HITS=$(grep -rin --include="*.md" -- "$QUERY" "$WIKI_DIR" 2>/dev/null | head -12 || true)
|
||||
[ -z "$HITS" ] && exit 0
|
||||
|
||||
COUNT=$(printf '%s\n' "$HITS" | wc -l | tr -d ' ')
|
||||
|
||||
echo "════════════════════════════════════════════════"
|
||||
printf '📚 wiki 已有「%s」的記載(%s 處,先看這裡再翻原文)\n' "$QUERY" "$COUNT"
|
||||
echo "════════════════════════════════════════════════"
|
||||
printf '%s\n' "$HITS" | sed 's|^system-dev/wiki/| |'
|
||||
echo ""
|
||||
echo "⚠️ wiki 是判準,程式碼與歷史文件只是稿子(mistakes 第一鐵律)。"
|
||||
echo " • 上面若與你將要查的原文衝突 → **以 wiki 為準**,別用 code 推翻 wiki。"
|
||||
echo " • 看到「不可動/待廢除/進行中」→ 先讀它的**解除條件**並逐條核對,"
|
||||
echo " 那是當時狀態不是永久禁令;條件已滿足就是可動。"
|
||||
echo " • wiki 沒答案才值得翻原文——翻完若得到新結論,**回頭更新 wiki**。"
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
+19
-1
@@ -42,6 +42,24 @@
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Grep|Glob|Read",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/wiki-first-search.sh"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Task",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/subagent-wiki-guard.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
@@ -68,4 +86,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.12.0
|
||||
1.16.0
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
---
|
||||
status: draft # active | draft | paused | closed(生命週期鐵律見 ../SDD-LIFECYCLE.md)
|
||||
superseded_by: "" # closed 且被取代時填接替的 SDD 資料夾名
|
||||
---
|
||||
|
||||
# [子系統名稱] — Design
|
||||
|
||||
> 狀態:[草稿 / 審核中 / 已採納 / 已廢棄]
|
||||
> 建立:[YYYY-MM-DD] | 最後更新:[YYYY-MM-DD]
|
||||
> 負責人:[名稱]
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# Logseq 任務 marker 解析(單一真相源)
|
||||
|
||||
> **這是「Logseq 原生任務語法」解析的唯一權威規格。** 任何要從 Logseq graph
|
||||
> 抓任務狀態的功能,一律 import 這份、不得各寫一份自己的 mapping。
|
||||
>
|
||||
> **已知兩個消費者**(共用同一套解析,見各自 issue):
|
||||
> 1. **vault 萃取**(`/wiki-extract`,template#5):marker → 卡片 frontmatter `task_status`。
|
||||
> 2. **tasks→Project 投影**(`system-dev/workflows/tasks-project-sync.*`,template#4):
|
||||
> 當投影來源是 Logseq graph(notes/kb)時,用這份判斷任務與狀態。
|
||||
>
|
||||
> 兩者**只共用「怎麼 parse」**(哪幾行是任務、marker 是什麼、正規狀態是什麼、跳過什麼);
|
||||
> parse 完各自要「拿狀態做什麼」(寫卡 vs 投影 issue)不同,那部分各管各的。
|
||||
|
||||
---
|
||||
|
||||
## 為什麼不是 GFM checkbox(規格更正,leo 2026-07-04 發現)
|
||||
|
||||
Logseq 的原生任務**不是** GFM 的 `- [ ]` / `- [x]`,而是**大寫 marker 開頭的 block**:
|
||||
|
||||
```
|
||||
- TODO AI 查看 leo21c 內所有 Repo,找到本地 Repo 搬到 Gitea
|
||||
- DOING 建立知識總庫,可查所有子庫
|
||||
- DONE 手機和電腦 Logseq 可以被放進知識總庫
|
||||
```
|
||||
|
||||
若照舊規格只抓 `- [ ]` checkbox,**notes / kb 兩個 Logseq graph 的任務會全數漏抓**。
|
||||
|
||||
> 兩種源、兩套語法、同一條下游管線:
|
||||
> - **SDD `tasks.md`**(各 repo `system-dev/docs/3-specs/**`)→ GFM checkbox(現行不變)。
|
||||
> - **Logseq graph(notes / kb)** → 本檔的大寫 marker。
|
||||
|
||||
---
|
||||
|
||||
## 解析規格
|
||||
|
||||
### 1. 任務行辨識(regex)
|
||||
|
||||
```
|
||||
^\s*- (TODO|DOING|NOW|LATER|WAITING|DONE|CANCELED|CANCELLED)\s+
|
||||
```
|
||||
|
||||
- marker 必須是 block(`-` bullet)的**開頭第一個 token**、全大寫、後接空白。
|
||||
- `CANCELED` 與英式 `CANCELLED` 皆收(Logseq 兩種都產)。
|
||||
- marker 後面到行尾(或到子 bullet 之前)是**任務內文**。
|
||||
|
||||
### 2. marker → 正規狀態(task_status)
|
||||
|
||||
| Logseq marker | 正規 task_status |
|
||||
|---------------|------------------|
|
||||
| `TODO`、`LATER` | `todo` |
|
||||
| `DOING`、`NOW` | `in-progress` |
|
||||
| `WAITING` | `blocked` |
|
||||
| `DONE` | `done` |
|
||||
| `CANCELED`、`CANCELLED` | `closed` |
|
||||
|
||||
> `LATER`/`NOW` 是 Logseq「排程視圖」用的同義 marker(LATER≈TODO、NOW≈DOING),
|
||||
> 正規化後與 TODO/DOING 併軌,下游不必區分。
|
||||
|
||||
### 3. 必須跳過的東西(別當任務內文)
|
||||
|
||||
Logseq 的任務 block 底下常掛時間戳與屬性行,這些**不是內文**,解析時整段略過:
|
||||
|
||||
- **`:LOGBOOK:` … `:END:` 區塊**:marker 被點擊計時產生的時間戳紀錄。
|
||||
遇到 `:LOGBOOK:` 那行起、到 `:END:` 那行止(含兩端),整塊丟掉。
|
||||
- **屬性行 `key:: value`**:如 `collapsed:: true`、`id:: 65a...`、`SCHEDULED:: <...>`、
|
||||
`DEADLINE:: <...>`。凡符合 `^\s*[\w-]+:: ` 的行都是屬性,不是內文。
|
||||
(`SCHEDULED`/`DEADLINE` 的日期若下游要用可另抓,但**不得當任務描述文字**。)
|
||||
|
||||
### 4. 巢狀子 bullet
|
||||
|
||||
任務 block 底下縮排的子 bullet 是該任務的補充說明(非獨立任務,除非子 bullet 自己也帶 marker)。
|
||||
萃取時可併入該任務的描述脈絡;投影時只取母 block 那行當任務標題。
|
||||
|
||||
---
|
||||
|
||||
## 自檢(實作或 LLM 執行前跑一遍)
|
||||
|
||||
- [ ] 用的是大寫 marker regex,**不是** `- [ ]` checkbox。
|
||||
- [ ] 八個 marker 全部覆蓋(含 `LATER`/`NOW`/`CANCELLED` 別漏)。
|
||||
- [ ] `:LOGBOOK:...:END:` 與 `key:: value` 屬性行有跳過,沒混進任務文字。
|
||||
- [ ] 狀態用上表**正規名**(`todo`/`in-progress`/`blocked`/`done`/`closed`),不是原始 marker 字面。
|
||||
@@ -26,9 +26,9 @@ t() { if [ "$IS_ZH" = "yes" ]; then printf '%s\n' "$1"; else printf '%s\n' "$2";
|
||||
# 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"
|
||||
REPO_URL="https://git.uncle6.me/Leo/system-dev-template/raw/branch/main/template"
|
||||
# install.sh / update.sh 住在 main/scripts/(不在 template/)。
|
||||
SCRIPTS_URL="https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/scripts"
|
||||
SCRIPTS_URL="https://git.uncle6.me/Leo/system-dev-template/raw/branch/main/scripts"
|
||||
CREATED=()
|
||||
SKIPPED=()
|
||||
|
||||
@@ -115,7 +115,7 @@ if [ -d "system-dev" ] || [ -d ".claude/wiki" ] || [ -f ".claude/VERSION" ]; the
|
||||
t " 後續的更新、遷移、補新檔,一律由「更新腳本」處理(不要重跑 install):" \
|
||||
" All updates, migrations, and new-file additions are handled by the UPDATER (don't re-run install):"
|
||||
echo ""
|
||||
echo " curl -sSL https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/scripts/update.sh | bash"
|
||||
echo " curl -sSL https://git.uncle6.me/Leo/system-dev-template/raw/branch/main/scripts/update.sh | bash"
|
||||
echo ""
|
||||
t " (重跑 install 可能建出空白範本、跟你的真資料並存,故在此停止。)" \
|
||||
" (Re-running install could create empty templates alongside your real data, so it stops here.)"
|
||||
@@ -254,6 +254,10 @@ create_dir ".claude/commands"
|
||||
create_dir ".claude/hooks"
|
||||
download_if_missing "system-dev/docs/README.md" "$REPO_URL/system-dev/docs/README.md"
|
||||
|
||||
# Logseq 任務 marker 解析(單一真相源):vault 萃取(/wiki-extract)與 tasks→Project
|
||||
# 投影(tasks-project-sync)共用同一套解析,別各寫一份。放共用區,兩模組都拿得到。
|
||||
download_if_missing "system-dev/docs/4-guides/logseq-markers.md" "$REPO_URL/system-dev/docs/4-guides/logseq-markers.md"
|
||||
|
||||
# 工具版號:放 system-dev/,不寄生 .claude/。
|
||||
download_if_missing "system-dev/VERSION" "$REPO_URL/system-dev/VERSION"
|
||||
|
||||
@@ -277,6 +281,8 @@ if $WANT_WIKI; then
|
||||
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"
|
||||
# vault 增量萃取(Logseq/Obsidian → system-dev/wiki,冪等):給 Routine 反覆跑。
|
||||
download_if_missing ".claude/commands/wiki-extract.md" "$REPO_URL/.claude/commands/wiki-extract.md"
|
||||
|
||||
# wiki 相關 hooks:接關 + 機敏掃描
|
||||
download_if_missing ".claude/hooks/session-start-recall.sh" "$REPO_URL/.claude/hooks/session-start-recall.sh"
|
||||
@@ -296,6 +302,18 @@ if $WANT_SDD; then
|
||||
|
||||
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"
|
||||
|
||||
# SDD 生命週期鐵律(1.14,issue #6):規則真相源 + 規格變更緩衝區 + 獨立單一活性檢查
|
||||
download_if_missing "system-dev/docs/3-specs/SDD-LIFECYCLE.md" "$REPO_URL/system-dev/docs/3-specs/SDD-LIFECYCLE.md"
|
||||
download_if_missing "system-dev/docs/3-specs/pending-changes.md" "$REPO_URL/system-dev/docs/3-specs/pending-changes.md"
|
||||
download_if_missing "system-dev/scripts/sdd-active-check.sh" "$REPO_URL/scripts/sdd-active-check.sh"
|
||||
|
||||
# ── tasks⇄Project 投影(optional,issue #16)──────────────────
|
||||
# 帶檔 ≠ 啟用:workflow yaml 只是「留記錄+手動啟用素材」,啟用=對話答好且 acr push。
|
||||
# 投影邏輯依附 tasks.md(住 3-specs),故隨 SDD 模組帶下來;裝了不代表開。
|
||||
create_dir "system-dev/workflows"
|
||||
download_if_missing "system-dev/workflows/tasks-project-sync.yaml" "$REPO_URL/system-dev/workflows/tasks-project-sync.yaml"
|
||||
download_if_missing "system-dev/workflows/tasks-project-sync.local.sh" "$REPO_URL/system-dev/workflows/tasks-project-sync.local.sh"
|
||||
fi
|
||||
|
||||
# ── 安裝/更新腳本:一開始就放進 system-dev/scripts/ ──
|
||||
@@ -312,6 +330,8 @@ download_if_missing ".claude/hooks/pre-write-guard.sh" "$REPO_URL/.claude/hooks/
|
||||
download_if_missing ".claude/commands/issue-handle.md" "$REPO_URL/.claude/commands/issue-handle.md"
|
||||
|
||||
chmod +x .claude/hooks/*.sh 2>/dev/null || true
|
||||
chmod +x system-dev/workflows/*.sh 2>/dev/null || true
|
||||
chmod +x system-dev/scripts/*.sh 2>/dev/null || true
|
||||
|
||||
# ── 依模組產生 settings.json 的 hooks 區塊 ────────
|
||||
# settings.json 因模組而異,不能直接下載單一靜態檔,改條件組裝。
|
||||
@@ -466,6 +486,19 @@ fi
|
||||
if $WANT_SDD; then
|
||||
t " 動 code 前先在 system-dev/docs/3-specs/[子系統]/ 建 design.md(可用 /sdd-check 協助)" \
|
||||
" Before touching code, create design.md under system-dev/docs/3-specs/[subsystem]/ (use /sdd-check to help)"
|
||||
# ── tasks⇄Project 投影:裝/init 對話 + 一次性廣告(issue #16)──
|
||||
# 判準=能力(arcrun 裝了沒)+意願,不掃檔。落地成「CC 問一句」,install 只是交代 CC 去問。
|
||||
echo ""
|
||||
t " ❓ 待辦同步(optional,需 Arcrun):請你的 CC 問你一句——" \
|
||||
" ❓ Task sync (optional, needs Arcrun): have your CC ask you once —"
|
||||
t " 「您需要把本專案的待辦事項(tasks.md)同步到 GitHub 嗎?」" \
|
||||
" \"Do you want this project's tasks (tasks.md) mirrored to GitHub?\""
|
||||
t " 答「好」→ CC 查環境有沒有 Arcrun(mcp / acr 在 PATH):有就設定同步、沒有就一次性告知" \
|
||||
" Yes → CC checks for Arcrun (mcp / acr in PATH): set it up if present, otherwise inform you once"
|
||||
t " 「Arcrun 是免費的 AI-friendly 工作流套件,想裝跟 Claude 說就行;之後也可手動啟用」。" \
|
||||
" \"Arcrun is a free AI-friendly workflow toolkit — ask Claude to install it; you can also enable sync later.\""
|
||||
t " 答「不好」→ 不做、不再追問。投影 workflow 在 system-dev/workflows/(帶檔≠啟用)。" \
|
||||
" No → nothing happens, no nagging. The projection workflow sits in system-dev/workflows/ (shipped ≠ enabled)."
|
||||
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)"
|
||||
|
||||
@@ -23,7 +23,7 @@ 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"
|
||||
REPO_RAW="https://git.uncle6.me/Leo/system-dev-template/raw/branch/main"
|
||||
TEMPLATE_URL="$REPO_RAW/template"
|
||||
|
||||
UPDATED=()
|
||||
@@ -217,15 +217,26 @@ keep_with_template ".claude/hooks/pre-write-guard.sh" "$TEMPLATE_URL/.claude/hoo
|
||||
update_file ".claude/commands/issue-handle.md" "$TEMPLATE_URL/.claude/commands/issue-handle.md"
|
||||
update_file "system-dev/VERSION" "$TEMPLATE_URL/system-dev/VERSION"
|
||||
|
||||
# Logseq 任務 marker 解析(單一真相源):vault 萃取(/wiki-extract)與 tasks→Project
|
||||
# 投影共用同一套;任一模組在用就補/更新(邏輯檔,可覆蓋)。
|
||||
if $HAS_WIKI || $HAS_SDD; then
|
||||
update_file "system-dev/docs/4-guides/logseq-markers.md" "$TEMPLATE_URL/system-dev/docs/4-guides/logseq-markers.md"
|
||||
fi
|
||||
|
||||
if $HAS_WIKI; then
|
||||
# wiki 的「邏輯檔」:導航與 hooks,可覆蓋。wiki 資料在 system-dev/,hooks/commands 留 .claude/。
|
||||
update_file "system-dev/wiki/INDEX.md" "$TEMPLATE_URL/system-dev/wiki/INDEX.md"
|
||||
update_file ".claude/hooks/session-start-recall.sh" "$TEMPLATE_URL/.claude/hooks/session-start-recall.sh"
|
||||
update_file ".claude/hooks/wiki-secret-scan.sh" "$TEMPLATE_URL/.claude/hooks/wiki-secret-scan.sh"
|
||||
# 1.16.0:讓 wiki 真的被讀到的兩支(開場 push 全文解決不了「只讀開頭」,見 CHANGELOG)
|
||||
update_file ".claude/hooks/wiki-first-search.sh" "$TEMPLATE_URL/.claude/hooks/wiki-first-search.sh"
|
||||
update_file ".claude/hooks/subagent-wiki-guard.sh" "$TEMPLATE_URL/.claude/hooks/subagent-wiki-guard.sh"
|
||||
update_file ".claude/commands/wiki-init.md" "$TEMPLATE_URL/.claude/commands/wiki-init.md"
|
||||
update_file ".claude/commands/wiki-capture.md" "$TEMPLATE_URL/.claude/commands/wiki-capture.md"
|
||||
update_file ".claude/commands/wiki-update.md" "$TEMPLATE_URL/.claude/commands/wiki-update.md"
|
||||
update_file ".claude/commands/wiki-recall.md" "$TEMPLATE_URL/.claude/commands/wiki-recall.md"
|
||||
# vault 增量萃取(Logseq/Obsidian → system-dev/wiki,冪等):邏輯檔,可覆蓋。舊版沒有 → 當新檔補。
|
||||
update_file ".claude/commands/wiki-extract.md" "$TEMPLATE_URL/.claude/commands/wiki-extract.md"
|
||||
# Cowork(claude.ai)的 wiki 整理 skill:規則檔,可覆蓋
|
||||
update_file "system-dev/docs/SKILL.md" "$TEMPLATE_URL/system-dev/docs/SKILL.md"
|
||||
|
||||
@@ -246,6 +257,18 @@ if $HAS_SDD; then
|
||||
update_file "system-dev/docs/2-architecture/decisions/TEMPLATE-adr.md" "$TEMPLATE_URL/system-dev/docs/2-architecture/decisions/TEMPLATE-adr.md"
|
||||
update_file ".claude/commands/sdd-check.md" "$TEMPLATE_URL/.claude/commands/sdd-check.md"
|
||||
update_file ".claude/hooks/sdd-guard.sh" "$TEMPLATE_URL/.claude/hooks/sdd-guard.sh"
|
||||
|
||||
# SDD 生命週期鐵律(1.14,issue #6):規則檔+獨立檢查腳本=邏輯檔可覆蓋;
|
||||
# pending-changes.md 裝著用戶的 proposal=用戶資料,只補不覆蓋。
|
||||
# (issue #13 教訓:update 不補新檔會造成結構斷層,新檔必須在這裡鋪。)
|
||||
update_file "system-dev/docs/3-specs/SDD-LIFECYCLE.md" "$TEMPLATE_URL/system-dev/docs/3-specs/SDD-LIFECYCLE.md"
|
||||
add_if_missing "system-dev/docs/3-specs/pending-changes.md" "$TEMPLATE_URL/system-dev/docs/3-specs/pending-changes.md"
|
||||
update_file "system-dev/scripts/sdd-active-check.sh" "$TEMPLATE_URL/scripts/sdd-active-check.sh"
|
||||
|
||||
# tasks⇄Project 投影(issue #16):邏輯檔,可覆蓋。舊版沒有 → add_if_missing 補。
|
||||
# 啟用狀態存遠端(acr push),不在這些檔裡,覆蓋不會關掉誰的同步。
|
||||
add_if_missing "system-dev/workflows/tasks-project-sync.yaml" "$TEMPLATE_URL/system-dev/workflows/tasks-project-sync.yaml"
|
||||
add_if_missing "system-dev/workflows/tasks-project-sync.local.sh" "$TEMPLATE_URL/system-dev/workflows/tasks-project-sync.local.sh"
|
||||
fi
|
||||
|
||||
# ── 自我更新:把最新的 update.sh / install.sh 抓到 system-dev/scripts/ ──
|
||||
@@ -253,7 +276,7 @@ fi
|
||||
update_file "system-dev/scripts/update.sh" "$REPO_RAW/scripts/update.sh"
|
||||
update_file "system-dev/scripts/install.sh" "$REPO_RAW/scripts/install.sh"
|
||||
|
||||
chmod +x .claude/hooks/*.sh system-dev/scripts/*.sh 2>/dev/null || true
|
||||
chmod +x .claude/hooks/*.sh system-dev/scripts/*.sh system-dev/workflows/*.sh 2>/dev/null || true
|
||||
|
||||
# ── 使用者資料檔:絕不碰,但提醒「設定可能有新欄位要手動補」──
|
||||
keep_file ".claude/settings.json"
|
||||
@@ -317,6 +340,34 @@ if [ -f ".claude/settings.json" ]; then
|
||||
$HAS_WIKI && ! grep -q "session-start-recall.sh" .claude/settings.json && MISSING+=("SessionStart: session-start-recall.sh")
|
||||
$HAS_WIKI && ! grep -q "wiki-secret-scan.sh" .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): wiki-secret-scan.sh")
|
||||
$HAS_SDD && ! grep -q "sdd-guard.sh" .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): sdd-guard.sh")
|
||||
|
||||
# ── 1.16.0:兩支 wiki 讀取 hook 自動註冊(不只提醒)──
|
||||
# 理由:這兩支的整個存在意義就是「不依賴任何人記得」。
|
||||
# 若靠人看提醒去手動補 settings.json,等於把同一個病搬到安裝環節。
|
||||
if $HAS_WIKI && command -v python3 >/dev/null 2>&1; then
|
||||
python3 - <<'PYEOF' 2>/dev/null || true
|
||||
import json, os
|
||||
p = ".claude/settings.json"
|
||||
try:
|
||||
with open(p) as f: d = json.load(f)
|
||||
except Exception:
|
||||
raise SystemExit(0) # 壞掉的 settings 不碰,交給下方提醒
|
||||
pre = d.setdefault("hooks", {}).setdefault("PreToolUse", [])
|
||||
blob = json.dumps(pre)
|
||||
added = []
|
||||
if "wiki-first-search" not in blob:
|
||||
pre.append({"matcher": "Grep|Glob|Read", "hooks": [
|
||||
{"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/wiki-first-search.sh"}]})
|
||||
added.append("wiki-first-search")
|
||||
if "subagent-wiki-guard" not in blob:
|
||||
pre.append({"matcher": "Task", "hooks": [
|
||||
{"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/subagent-wiki-guard.sh"}]})
|
||||
added.append("subagent-wiki-guard")
|
||||
if added:
|
||||
with open(p, "w") as f: json.dump(d, f, ensure_ascii=False, indent=2)
|
||||
print(" ✅ 已自動註冊 wiki 讀取 hook:" + "、".join(added))
|
||||
PYEOF
|
||||
fi
|
||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
t "📌 settings.json 是你的設定(沒動),但偵測到缺以下 hook,請手動補上:" \
|
||||
|
||||
+10
-12
@@ -39,23 +39,21 @@
|
||||
INDEX 是**所有檢索角度的入口**,不只標籤。原文是唯讀 SSoT,wiki 是改寫過的記憶。
|
||||
新增角度只要在這裡加一節(如「決策角度」「原則角度」),指向對應 cards 或 push 檔——**不必新增實體特殊檔**。
|
||||
|
||||
### 子系統角度(按 `TAXONOMY.md` 的軸聚類,指向桶子索引)
|
||||
### 標籤角度(按 `TAXONOMY.md` 的軸聚類,指向桶子索引)
|
||||
|
||||
#### 架構決策(跨子系統)
|
||||
- [[decisions/00-INDEX]] — arcrun 架構決策(13 卡,已從 decisions-summary 全量改寫成原子卡)
|
||||
```markdown
|
||||
#### 知識管理
|
||||
- [[pkm/00-INDEX]] — PKM 知識管理(N 卡)
|
||||
|
||||
> 子系統卡桶(零件架構 / cypher / credential / recipe / kbdb / 薄殼 / 部署)尚未獨立建——
|
||||
> 決策卡已用 frontmatter `tags:` 掛子系統軸,可從 INDEX 標籤過濾;純子系統知識卡由 /wiki-capture 依需開桶。
|
||||
#### AI 協作
|
||||
- [[ai/00-INDEX]] — AI 協作(M 卡)
|
||||
```
|
||||
|
||||
### 決策角度(取代舊 decisions-summary.md 的視圖)
|
||||
|
||||
> `decisions-summary.md`(全文)保留為相容;13 條決策已逐條改寫成 `cards/decisions/` 原子卡,入口見 [[decisions/00-INDEX]]。
|
||||
> 按子系統分群的清單在桶子索引內,這裡只放跨子系統最常查的:
|
||||
|
||||
- [[工作流是default零件是例外]] — 自用→工作流,全生態重用→才零件(mindset §1)
|
||||
- [[薄殼原則-能力長在API]] — 能力只實作一次放 API,介面全薄殼(rule 07)
|
||||
- [[自力救濟階梯-缺能力怎麼補]] — 缺能力分流:自家補API/第三方 workflow 補丁(issue #4)
|
||||
- [[service-binding-vs-cypher-binding]] — 零件串接走 HTTP URL,禁 service binding(rule 03)
|
||||
```markdown
|
||||
- [[某決策卡]] — 一句話結論(YYYY-MM-DD)
|
||||
```
|
||||
|
||||
> 結構:INDEX(多角度入口)→ `cards/<bucket>/00-INDEX.md`(桶子索引,固定名)→ 概念原子卡。
|
||||
> 指 `00-INDEX` **一律帶路徑** `[[bucket/00-INDEX]]`(固定名跨桶撞名);卡片間用裸 `[[卡名]]`。
|
||||
|
||||
Reference in New Issue
Block a user