fix: install/update 雙語化(預設英文) + 修 macOS bash 3.2 安裝崩潰 + bump 1.7.0
- 修崩潰:bash 3.2 + set -u 展開空陣列會炸 unbound variable;移除多餘
${SKIPPED[*]} no-op;多位元組字串旁變數改用 ${VAR} 防 curl|bash 串流切斷
- install/update 訊息依 locale 雙語,預設英文(curl|bash 常 LANG=C),zh_TW 切繁中
- 寫進 CLAUDE.md 的 raw source 宣告依 locale 只寫一種語言(不撐爆 context)
- 修措辭:一般開發案不再顯示誤導的「偵測到 vault 類型:docs」
- 新增 README.en.md(繁中↔English 切換)
- gitignore dogfooding 自裝產物(CLAUDE.md/docs 子目錄/.claude/settings*)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
@@ -18,3 +18,17 @@ skills/editorial-image/
|
|||||||
.claude/hooks/
|
.claude/hooks/
|
||||||
.claude/commands/
|
.claude/commands/
|
||||||
.claude/VERSION
|
.claude/VERSION
|
||||||
|
.claude/settings.json
|
||||||
|
.claude/settings.local.json
|
||||||
|
|
||||||
|
# Dogfooding:我們把 system-dev-template 也裝進自己的 repo 自用(產生這些自裝產物)。
|
||||||
|
# 真正的範本本體在 template/ 底下,這些根目錄自裝副本不推雲端,用戶不需要。
|
||||||
|
# 公開要推的 repo 自身文件是:docs/why.md、docs/wishlist.md、docs/SKILL.md(已追蹤)。
|
||||||
|
/CLAUDE.md
|
||||||
|
/docs/README.md
|
||||||
|
/docs/1-vision/
|
||||||
|
/docs/2-architecture/
|
||||||
|
/docs/3-specs/
|
||||||
|
/docs/4-guides/
|
||||||
|
/docs/5-records/
|
||||||
|
/docs/6-user/
|
||||||
|
|||||||
@@ -10,6 +10,16 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 1.7.0 — install/update 雙語化 + 修 macOS bash 3.2 安裝崩潰
|
||||||
|
|
||||||
|
非台灣使用者看不懂中文安裝訊息(`curl | bash` 出來整片中文),加上 macOS 內建 bash 3.2 在 `set -u` 下會崩。這版一起修:
|
||||||
|
|
||||||
|
- **修崩潰**:`install.sh` 在 macOS bash 3.2 下會噴 `VAULT_TYPE…: unbound variable` / `SKIPPED[*]: unbound variable`。根因是 bash 3.2 + `set -u` 展開「空陣列」會炸,加上一行多餘的 `${SKIPPED[*]}` no-op。修法:空陣列展開前先檢查、移除廢行、多位元組字串旁的變數一律用 `${VAR}` 包好(避免 `curl | bash` 串流在多位元組字元邊界被切斷而誤判變數名)。
|
||||||
|
- **install/update 訊息雙語**:依 locale 自動選語言,**預設英文**(`curl | bash` 常是 `LANG=C`,外國人預設就看得懂),`zh_TW` 自動切回繁中。
|
||||||
|
- **寫進 CLAUDE.md 的 raw source 宣告也雙語**:依 locale **只寫一種語言**進 CLAUDE.md(雙語會讓每個 session 的 context 更滿)。
|
||||||
|
- **修措辭**:一般開發案不再顯示誤導的「偵測到 vault 類型:docs」。偵測到 logseq/obsidian 筆記庫 → 出聲「會保留你的筆記結構」;不是筆記 → 默默裝完。
|
||||||
|
- **README 多語**:新增 `README.en.md`,頂部繁中 ↔ English 切換連結,利於被搜尋到。
|
||||||
|
|
||||||
## 1.6.1 — 釐清 taxonomy 是「受控擴充」非「凍結」
|
## 1.6.1 — 釐清 taxonomy 是「受控擴充」非「凍結」
|
||||||
|
|
||||||
1.6.0 把標籤字典寫成「禁止自創」,措辭過嚴:碰到現有軸裝不下的新內容時,會逼 AI 硬塞錯標籤或偷偷自創,兩者都比受控擴充差。改成正確的機制:
|
1.6.0 把標籤字典寫成「禁止自創」,措辭過嚴:碰到現有軸裝不下的新內容時,會逼 AI 硬塞錯標籤或偷偷自創,兩者都比受控擴充差。改成正確的機制:
|
||||||
|
|||||||
+236
@@ -0,0 +1,236 @@
|
|||||||
|
# system-dev-template
|
||||||
|
|
||||||
|
[繁體中文](README.md) | **English**
|
||||||
|
|
||||||
|
> Turn Claude Code from a "sprinting engineer" into a "disciplined engineer."
|
||||||
|
|
||||||
|
CC is an excellent engineer, but not a good project manager. It sprints to get work done — great for small projects, but on large ones it tends to:
|
||||||
|
- **Fix one thing, break another**: no global view, no SDD constraints
|
||||||
|
- **Let docs rot**: its built-in memory is unreliable, knowledge vanishes with the conversation, and it loves writing docs it never reads back — scattering them everywhere
|
||||||
|
|
||||||
|
This template solves both problems with two systems:
|
||||||
|
|
||||||
|
| System | What it solves | Core mechanism |
|
||||||
|
|--------|----------------|----------------|
|
||||||
|
| **SDD system** | Global view, think before doing | No code change without a `design.md` + `tasks.md` first |
|
||||||
|
| **LLM Wiki** | Memory that compounds, ordered docs | Two spaces: raw source (human-written) + `.claude/wiki/` (AI-curated) |
|
||||||
|
|
||||||
|
> **Not just code projects, not just Claude Code.** LLM Wiki now also recognizes **Logseq / Obsidian vaults** —
|
||||||
|
> at install time it auto-detects which kind of folder you're in, writes the matching "raw source" into `CLAUDE.md`,
|
||||||
|
> reads only from there when curating the wiki, and **never moves your vault structure**
|
||||||
|
> (see "Not just code — Logseq / Obsidian vaults too" below).
|
||||||
|
> The curator isn't limited to Claude Code either — **claude.ai's Cowork** can use the same rules to scan and
|
||||||
|
> curate every wiki on your machine (see "Cowork can curate wikis too" below).
|
||||||
|
|
||||||
|
- SDD: already popular. The most thorough implementation is probably Amazon Kiro, but it's hard to pay for in Taiwan. This approach beats any flashier method — it forces CC not to do whatever it pleases.
|
||||||
|
- LLM Wiki: the latest RAG idea from the legendary Karpathy. It's a *pre-compile* approach — better and less fiddly than the embedding-and-chunk-into-vectors method your company bought, and far easier to run.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Two ways to use it
|
||||||
|
|
||||||
|
### Option 1: New project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/uncle6me-web/system-dev-template
|
||||||
|
cp -r system-dev-template/template/. your-new-project/
|
||||||
|
cd your-new-project
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in a CC conversation:
|
||||||
|
```
|
||||||
|
/wiki-init
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Existing project (retrofit)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd your-existing-project
|
||||||
|
curl -sSL https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/scripts/install.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
The script only creates what's missing — **it never touches files you already have**.
|
||||||
|
|
||||||
|
**Just one piece?** The two systems install separately (sometimes you only need the wiki, not SDD):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download, then run with a flag
|
||||||
|
curl -sSL .../install.sh -o install.sh && bash install.sh --wiki # LLM Wiki only
|
||||||
|
bash install.sh --sdd # SDD only
|
||||||
|
bash install.sh --all # both (default)
|
||||||
|
```
|
||||||
|
|
||||||
|
When run directly in a terminal (with a tty), running it without a flag **asks interactively** which piece to install — friendliest for non-engineers. With no tty, `curl | bash` safely defaults to `--all`. The hooks in `settings.json` are assembled automatically based on the modules you choose.
|
||||||
|
|
||||||
|
After installing, in a CC conversation:
|
||||||
|
```
|
||||||
|
/wiki-init
|
||||||
|
```
|
||||||
|
|
||||||
|
CC will scan your existing docs, build the wiki, and tidy up the docs structure.
|
||||||
|
|
||||||
|
> CC loves writing docs and scattering them everywhere; the bigger and older a project gets, the messier its folders — yet it's afraid to move anything. With the folder conventions written down, it can file everything for you in one pass.
|
||||||
|
> LLM Wiki won't touch your original files, but it builds a separate wiki and fixes your entry point — which is `CLAUDE.md`. So even though things get reorganized, your documents won't disappear.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Already on an older version? Update with one line
|
||||||
|
|
||||||
|
Heard there are new features and want the latest? Just run this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd your-project
|
||||||
|
curl -sSL https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/scripts/update.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
It first compares your version with the latest, tells you **which new features you'd gain**, and then:
|
||||||
|
|
||||||
|
| Action | Target |
|
||||||
|
|--------|--------|
|
||||||
|
| 🆕 **Add** new features (hooks / commands / templates the old version lacked) | Template files |
|
||||||
|
| ⬆️ **Update** template logic (hooks, commands, `TEMPLATE-*` swapped for new versions) | Template files |
|
||||||
|
| 🔒 **Never touch** your content and settings | `wiki/status.md`, `mistakes.md`, `decisions-summary.md`, `.wikiignore`, `settings.json`, `CLAUDE.md` |
|
||||||
|
|
||||||
|
> **Why use curl the first time?** Old installs don't have `update.sh` locally yet, so the first run has to fetch it from the remote.
|
||||||
|
> After running, it installs itself to `scripts/update.sh`, so **next time just run** `bash scripts/update.sh` — no more curl.
|
||||||
|
|
||||||
|
Updates only touch "installed modules" (wiki installed → wiki updated; SDD installed → SDD updated), detected automatically.
|
||||||
|
If `settings.json` is missing a hook that only the new version has, it won't change your settings for you — but it will **list them at the end** as a reminder to add them manually (it never edits your settings without asking).
|
||||||
|
|
||||||
|
📜 Want to know what changed each time and which version you're on? See the **[CHANGELOG](CHANGELOG.md)**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Not just code — Logseq / Obsidian vaults too
|
||||||
|
|
||||||
|
LLM Wiki originally assumed "raw source lives in `docs/`", but note vaults like Logseq and Obsidian have their own directory conventions.
|
||||||
|
Now `install.sh` **auto-detects the folder type** before creating `CLAUDE.md`, and writes the matching "raw source" into `CLAUDE.md`:
|
||||||
|
|
||||||
|
| Detected | Type | Raw source |
|
||||||
|
|----------|------|------------|
|
||||||
|
| `logseq/` folder | Logseq | `pages/`, `journals/` |
|
||||||
|
| `.obsidian/` folder | Obsidian | all `.md` under the root |
|
||||||
|
| Neither | Regular project | `docs/` (original behavior) |
|
||||||
|
|
||||||
|
The declaration written into `CLAUDE.md` is **an instruction for the AI** — it tells the curator "here's where the raw source is, read only from there when curating the wiki",
|
||||||
|
and for vaults it **explicitly forbids moving, renaming, or re-classifying `.md` files**; curation output always goes only into `.claude/wiki/`.
|
||||||
|
This way, even with a note vault, the original structure stays intact and your notes never become unreadable.
|
||||||
|
|
||||||
|
> **Already have a `CLAUDE.md`?** It's never overwritten (the "don't touch what exists" principle). Instead, the declaration you should add is **listed at the end** of the install for you to paste manually.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cowork can curate wikis too
|
||||||
|
|
||||||
|
The wiki curator is no longer limited to Claude Code in the terminal — **claude.ai's Cowork** can do it too.
|
||||||
|
`docs/SKILL.md` provides a skill for Cowork (`wiki-cowork-scan`) that **shares the same rules** as CC's `/wiki-init` and `/wiki-capture`:
|
||||||
|
|
||||||
|
- Scans every folder under `~/Documents` that has system-dev-template installed (i.e. has a `.claude/wiki/`)
|
||||||
|
- Uses the **same detection logic as `install.sh`** to decide whether each folder is a regular project / Logseq / Obsidian
|
||||||
|
- Reads only the raw source and only appends to `.claude/wiki/` (no overwrite, no delete); **never touches** raw source, `CLAUDE.md`, `logseq/`, `.obsidian/`, `assets/`
|
||||||
|
|
||||||
|
**CC and Cowork share an identical output format**, so whatever one curates, the other sees and skips or supplements — no duplication, no overwriting.
|
||||||
|
Great for a scheduled Cowork run that keeps every wiki on your machine up to date automatically.
|
||||||
|
|
||||||
|
> To use it: give `docs/SKILL.md` to your Cowork as a skill reference, then tell it "curate the wiki".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Directory guide
|
||||||
|
|
||||||
|
```
|
||||||
|
system-dev-template/
|
||||||
|
├── template/ ← skeleton copied into new projects
|
||||||
|
│ ├── CLAUDE.md ← fill-in-the-blanks navigation sign
|
||||||
|
│ ├── docs/ ← docs structure (six-layer taxonomy)
|
||||||
|
│ └── .claude/
|
||||||
|
│ ├── wiki/ ← CC's memory space (incl. .wikiignore for sensitive exclusions)
|
||||||
|
│ ├── commands/ ← slash commands
|
||||||
|
│ ├── hooks/ ← hard interceptors (recall + SDD protocol + wiki secret scan)
|
||||||
|
│ └── settings.json ← wires up hooks (install.sh assembles by module)
|
||||||
|
│
|
||||||
|
├── skills/
|
||||||
|
│ └── llm-wiki/ ← copy into Legacy-Workspace/.claude/skills/
|
||||||
|
│
|
||||||
|
├── scripts/
|
||||||
|
│ ├── install.sh ← retrofit script for existing projects
|
||||||
|
│ └── update.sh ← one-line update for old versions (swaps templates only, never your data)
|
||||||
|
│
|
||||||
|
└── docs/ ← this repo's own documentation
|
||||||
|
├── why.md ← design philosophy
|
||||||
|
├── wishlist.md ← pending features and completed records
|
||||||
|
└── SKILL.md ← wiki-curation skill for claude.ai Cowork (wiki-cowork-scan)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slash Commands
|
||||||
|
|
||||||
|
Available in any CC conversation after install:
|
||||||
|
|
||||||
|
| Command | What it does |
|
||||||
|
|---------|--------------|
|
||||||
|
| `/wiki-init` | Initialize the wiki (new project or retrofit) |
|
||||||
|
| `/wiki-recall` | At session start, manually resume (fallback when the hook doesn't fire) |
|
||||||
|
| `/wiki-capture` | Save this conversation's conclusions into the wiki |
|
||||||
|
| `/wiki-update` | At session end, update `status.md` |
|
||||||
|
| `/sdd-check` | Check whether the current task has a matching SDD |
|
||||||
|
| `/issue-handle` | Handle GitHub issues (read back from your own repo / cross-repo posting asks first / no auto-polling) |
|
||||||
|
|
||||||
|
The naming loop: init (create) → update (save, end of session) ↔ recall (resume, start of session) → capture (save conclusions anytime).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hooks (soft norms → hard interception)
|
||||||
|
|
||||||
|
Norms are no longer just soft reminders in `CLAUDE.md`; there's a backstop:
|
||||||
|
|
||||||
|
| Hook | Role |
|
||||||
|
|------|------|
|
||||||
|
| `session-start-recall.sh` | Auto-injects the key points of `status` at session start — doesn't rely on CC remembering |
|
||||||
|
| `sdd-guard.sh` | Touching a code file with no SDD → block (exit 2); the automated version of `/sdd-check` |
|
||||||
|
| `wiki-secret-scan.sh` | A sensitive value about to be written into `.claude/wiki/` → block (exit 2); a mechanical backstop for passwords/keys/PII |
|
||||||
|
| `pre-write-guard.sh` | Skeleton for project-specific bans (disabled by default; takes effect once you fill in a pattern) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wiki secret protection (content you don't want indexed)
|
||||||
|
|
||||||
|
Like `.gitignore`, it keeps content you don't want in the wiki (passwords, keys, PII) from being indexed. Three layers:
|
||||||
|
|
||||||
|
| Layer | Mechanism | What it blocks | Nature |
|
||||||
|
|-------|-----------|----------------|--------|
|
||||||
|
| **L1** | `.claude/wiki/.wikiignore` (glob) | An entire sensitive file is not indexed | Protocol (CC complies) |
|
||||||
|
| **L2** | Inline markers `<!-- wiki:ignore -->`…`<!-- wiki:end -->` | A section within a file is not indexed | Protocol (CC complies) |
|
||||||
|
| **L3** | `wiki-secret-scan.sh` hook | A sensitive value actually being written into the wiki → exit 2 blocks it | **Hard interception (mechanical detection)** |
|
||||||
|
|
||||||
|
L1/L2 are markers you set proactively; L3 is the automatic backstop — if CC misses the first two, the moment a secret is written into the wiki it's caught by regex (password assignments, PEM private keys, AWS/GitHub/Slack keys, JWTs, connection-string credentials, national IDs, credit-card numbers).
|
||||||
|
|
||||||
|
> Honest limits: L1/L2 rely on CC's self-discipline; L3 relies on pattern matching (with false positives/negatives).
|
||||||
|
> This is a mechanism to reduce **accidental** leaks, not a vault — real secrets shouldn't be in version control in the first place.
|
||||||
|
> On a false positive: add `wiki-secret-ok` at the end of the line to allow it; for a whole sensitive file, add it to `.wikiignore`.
|
||||||
|
|
||||||
|
> Honest limits: the hook blocks syntactically obvious violations (writing directly to a file); it can't catch detours hidden in helpers / bash.
|
||||||
|
> Its value is "trying to skip it gets caught + leaves an auditable trace", not technical tamper-proofing — the docs (mindset) and the hook (backstop) are both indispensable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design principles
|
||||||
|
|
||||||
|
- **Additive, not destructive**: when retrofitting an existing project, your existing norms stay; the wiki system layers on top
|
||||||
|
- **Structure is protocol**: CC and you share the same taxonomy, no need to re-explain every time
|
||||||
|
- **`CLAUDE.md` doesn't grow**: past 100 lines is an architecture problem, not a reason to add more content
|
||||||
|
- **Capture conclusions on the spot**: after a discussion, use `/wiki-capture` so knowledge stops vanishing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
This template stands on the shoulders of giants:
|
||||||
|
|
||||||
|
- **[Andrej Karpathy](https://gist.github.com/karpathy)** — originator of the LLM Wiki concept. This template's two-space memory system comes directly from his April 2026 idea: using an LLM to "pre-compile" knowledge into interlinked markdown, rather than re-running embedding retrieval on every query. One line of his captures the spirit: *"Obsidian is the IDE, the LLM is the programmer, the wiki is the codebase."*
|
||||||
|
- Further reading: [The Andrej Karpathy LLM Wiki Idea](https://reliabilitywhisperer.substack.com/p/the-andrej-karpathy-llm-wiki-idea), [Beyond RAG (Level Up Coding)](https://levelup.gitconnected.com/beyond-rag-how-andrej-karpathys-llm-wiki-pattern-builds-knowledge-that-actually-compounds-31a08528665e)
|
||||||
|
|
||||||
|
- **[Amazon Kiro](https://aws.amazon.com/documentation-overview/kiro/)** — this template's SDD (Spec-Driven Development) approach is learned from Kiro. It takes "have a structured spec before you start" to the furthest extreme; paying for it is inconvenient in Taiwan, but its methodology deserves respect and borrowing.
|
||||||
|
|
||||||
|
- **Claude (Claude Code / Anthropic)** — this template's hooks, secret protection, modular install, and this very README were all pair-developed with Claude. The template itself exists to turn Claude Code "from a sprinting engineer into a disciplined engineer" — it's both the tool and a co-author.
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# system-dev-template
|
# system-dev-template
|
||||||
|
|
||||||
|
**繁體中文** | [English](README.en.md)
|
||||||
|
|
||||||
> 讓 Claude Code 從「猛衝的工程師」變成「有紀律的工程師」。
|
> 讓 Claude Code 從「猛衝的工程師」變成「有紀律的工程師」。
|
||||||
|
|
||||||
CC 是個優秀的工程師,但不是個好的專案經理。它會猛衝完成工作——小專案很好,大專案就會:
|
CC 是個優秀的工程師,但不是個好的專案經理。它會猛衝完成工作——小專案很好,大專案就會:
|
||||||
|
|||||||
+153
-46
@@ -14,6 +14,18 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
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; }
|
||||||
|
|
||||||
REPO_URL="https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/template"
|
REPO_URL="https://raw.githubusercontent.com/uncle6me-web/system-dev-template/main/template"
|
||||||
CREATED=()
|
CREATED=()
|
||||||
SKIPPED=()
|
SKIPPED=()
|
||||||
@@ -26,6 +38,7 @@ for arg in "$@"; do
|
|||||||
--sdd|--sdd-only) MODULE="sdd" ;;
|
--sdd|--sdd-only) MODULE="sdd" ;;
|
||||||
--all) MODULE="all" ;;
|
--all) MODULE="all" ;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
|
if [ "$IS_ZH" = "yes" ]; then
|
||||||
cat <<'HELP'
|
cat <<'HELP'
|
||||||
用法:install.sh [--wiki | --sdd | --all]
|
用法:install.sh [--wiki | --sdd | --all]
|
||||||
--wiki 只裝 LLM Wiki(CC 記憶系統 + 機敏防護)
|
--wiki 只裝 LLM Wiki(CC 記憶系統 + 機敏防護)
|
||||||
@@ -33,6 +46,15 @@ for arg in "$@"; do
|
|||||||
--all 兩個都裝(預設)
|
--all 兩個都裝(預設)
|
||||||
無參數 互動式詢問要裝哪個
|
無參數 互動式詢問要裝哪個
|
||||||
HELP
|
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 ;;
|
exit 0 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
@@ -40,18 +62,21 @@ done
|
|||||||
echo ""
|
echo ""
|
||||||
echo "🔧 system-dev-template installer"
|
echo "🔧 system-dev-template installer"
|
||||||
echo "================================="
|
echo "================================="
|
||||||
echo "只建立缺少的目錄和檔案,已有的不動。"
|
t "只建立缺少的目錄和檔案,已有的不動。" \
|
||||||
|
"Only creates missing dirs and files; never touches what already exists."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 無參數 → 互動式詢問(給非專業使用者)──────────
|
# ── 無參數 → 互動式詢問(給非專業使用者)──────────
|
||||||
if [ -z "$MODULE" ]; then
|
if [ -z "$MODULE" ]; then
|
||||||
if [ -t 0 ]; then
|
if [ -t 0 ]; then
|
||||||
echo "要安裝哪一塊?"
|
t "要安裝哪一塊?" "Which part do you want to install?"
|
||||||
echo " 1) LLM Wiki —— 讓 CC 記住決策、不重複犯錯(含機敏防護)"
|
t " 1) LLM Wiki —— 讓 CC 記住決策、不重複犯錯(含機敏防護)" \
|
||||||
echo " 2) SDD —— 動 code 前強制先有設計文件"
|
" 1) LLM Wiki — let CC remember decisions and avoid repeating mistakes (with secret protection)"
|
||||||
echo " 3) 兩個都裝(推薦)"
|
t " 2) SDD —— 動 code 前強制先有設計文件" \
|
||||||
|
" 2) SDD — require a design doc before touching code"
|
||||||
|
t " 3) 兩個都裝(推薦)" " 3) Install both (recommended)"
|
||||||
echo ""
|
echo ""
|
||||||
printf "請輸入 1 / 2 / 3 [預設 3]:"
|
tn "請輸入 1 / 2 / 3 [預設 3]:" "Enter 1 / 2 / 3 [default 3]: "
|
||||||
read -r choice || choice=3
|
read -r choice || choice=3
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1) MODULE="wiki" ;;
|
1) MODULE="wiki" ;;
|
||||||
@@ -73,7 +98,7 @@ case "$MODULE" in
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "📦 安裝模組:$MODULE"
|
t "📦 安裝模組:$MODULE" "📦 Module: $MODULE"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 偵測 vault 類型 → 決定 raw source(原始文件)路徑 ──────────
|
# ── 偵測 vault 類型 → 決定 raw source(原始文件)路徑 ──────────
|
||||||
@@ -85,47 +110,91 @@ echo ""
|
|||||||
# 必須在建立 CLAUDE.md 之前跑完。
|
# 必須在建立 CLAUDE.md 之前跑完。
|
||||||
VAULT_TYPE=""
|
VAULT_TYPE=""
|
||||||
RAW_SOURCE=""
|
RAW_SOURCE=""
|
||||||
|
IS_VAULT="no" # 只有 logseq/obsidian 這種「筆記軟體 vault」才算 yes
|
||||||
if [ -d "logseq" ]; then
|
if [ -d "logseq" ]; then
|
||||||
VAULT_TYPE="logseq"
|
VAULT_TYPE="logseq"
|
||||||
RAW_SOURCE="pages/, journals/"
|
RAW_SOURCE="pages/, journals/"
|
||||||
|
IS_VAULT="yes"
|
||||||
elif [ -d ".obsidian" ]; then
|
elif [ -d ".obsidian" ]; then
|
||||||
VAULT_TYPE="obsidian"
|
VAULT_TYPE="obsidian"
|
||||||
RAW_SOURCE="./ (整個 vault 根目錄的 .md)"
|
RAW_SOURCE="$(tn './ (整個 vault 根目錄的 .md)' './ (all .md under the vault root)')"
|
||||||
|
IS_VAULT="yes"
|
||||||
else
|
else
|
||||||
VAULT_TYPE="docs"
|
VAULT_TYPE="docs"
|
||||||
RAW_SOURCE="docs/"
|
RAW_SOURCE="docs/"
|
||||||
fi
|
fi
|
||||||
echo "🗂️ 偵測到 vault 類型:$VAULT_TYPE → raw source:$RAW_SOURCE"
|
# 偵測到是筆記 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 ""
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
# 把「raw source 宣告區塊」吐出來,給新建的 CLAUDE.md append 或
|
# 把「raw source 宣告區塊」吐出來,給新建的 CLAUDE.md append 或
|
||||||
# 給已存在的 CLAUDE.md 當手動補貼的提示。內容對 CC / Cowork 都是
|
# 給已存在的 CLAUDE.md 當手動補貼的提示。內容對 CC / Cowork 都是
|
||||||
# 機器可讀的指令(明確路徑 + 不可破壞 vault 結構的約束)。
|
# 機器可讀的指令(明確路徑 + 不可破壞 vault 結構的約束)。
|
||||||
|
# 寫進 CLAUDE.md 的 raw source 宣告區塊。給人也給 AI 看:
|
||||||
|
# 依 locale 只寫「一種語言」進 CLAUDE.md(雙語會讓每個 session 的 context 更滿)。
|
||||||
emit_raw_source_block() {
|
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
|
cat <<BLOCK
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 原始文件空間(raw source)
|
## 原始文件空間(raw source)
|
||||||
|
|
||||||
> 安裝時偵測到的 vault 類型:**$VAULT_TYPE**
|
> 安裝時偵測到的來源型態:**${source_kind}**
|
||||||
> CC 與 Cowork 整理/讀取「人寫的原始文件」時,**只在這裡找、只在這裡動**。
|
> CC 與 Cowork 整理/讀取「人寫的原始文件」時,**只在這裡找、只在這裡動**。
|
||||||
|
|
||||||
| 項目 | 值 |
|
| 項目 | 值 |
|
||||||
|------|----|
|
|------|----|
|
||||||
| vault 類型 | \`$VAULT_TYPE\` |
|
| 來源型態 | \`${source_kind}\` |
|
||||||
| raw source | \`$RAW_SOURCE\` |
|
| raw source | \`${RAW_SOURCE}\` |
|
||||||
|
|
||||||
**約束(CC 與 Cowork 都必須遵守)**
|
**約束(CC 與 Cowork 都必須遵守)**
|
||||||
|
|
||||||
- 整理 wiki/知識時,原始文件**一律從上方 raw source 路徑讀取**,不要假設是 \`docs/\`。
|
- 整理 wiki/知識時,原始文件**一律從上方 raw source 路徑讀取**,不要假設是 \`docs/\`。
|
||||||
BLOCK
|
BLOCK
|
||||||
if [ "$VAULT_TYPE" != "docs" ]; then
|
if [ "$IS_VAULT" = "yes" ]; then
|
||||||
cat <<BLOCK
|
cat <<BLOCK
|
||||||
- 這是 **$VAULT_TYPE vault**:保留它原本的目錄與檔名慣例,**不得搬動、改名、重新分類** \`.md\` 檔,
|
- 這是 **${VAULT_TYPE} 筆記庫**:保留它原本的目錄與檔名慣例,**不得搬動、改名、重新分類** \`.md\` 檔,
|
||||||
以免破壞 vault 結構造成筆記不可讀。整理只在 \`.claude/wiki/\` 產出,**不動 raw source 本身**。
|
以免破壞筆記軟體結構造成筆記不可讀。整理只在 \`.claude/wiki/\` 產出,**不動 raw source 本身**。
|
||||||
BLOCK
|
BLOCK
|
||||||
fi
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── 工具函式 ──────────────────────────────────────
|
# ── 工具函式 ──────────────────────────────────────
|
||||||
@@ -134,7 +203,7 @@ create_dir() {
|
|||||||
mkdir -p "$1"
|
mkdir -p "$1"
|
||||||
CREATED+=("$1/")
|
CREATED+=("$1/")
|
||||||
else
|
else
|
||||||
SKIPPED+=("$1/ (已存在)")
|
SKIPPED+=("$1/ $(tn '(已存在)' '(already exists)')")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +214,7 @@ download_if_missing() {
|
|||||||
curl -sSL "$src" -o "$dest"
|
curl -sSL "$src" -o "$dest"
|
||||||
CREATED+=("$dest")
|
CREATED+=("$dest")
|
||||||
else
|
else
|
||||||
SKIPPED+=("$dest (已存在,跳過)")
|
SKIPPED+=("$dest $(tn '(已存在,跳過)' '(already exists, skipped)')")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,9 +295,9 @@ build_hooks_json() {
|
|||||||
|
|
||||||
if [ ! -f ".claude/settings.json" ]; then
|
if [ ! -f ".claude/settings.json" ]; then
|
||||||
build_hooks_json > .claude/settings.json
|
build_hooks_json > .claude/settings.json
|
||||||
CREATED+=(".claude/settings.json (依 $MODULE 模組產生)")
|
CREATED+=(".claude/settings.json $(tn "(依 $MODULE 模組產生)" "(generated for module: $MODULE)")")
|
||||||
else
|
else
|
||||||
SKIPPED+=(".claude/settings.json (已存在,請手動合併 hooks)")
|
SKIPPED+=(".claude/settings.json $(tn '(已存在,請手動合併 hooks)' '(already exists — merge hooks manually)')")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── CLAUDE.md:只在完全不存在時建立 ────────────────
|
# ── CLAUDE.md:只在完全不存在時建立 ────────────────
|
||||||
@@ -238,20 +307,24 @@ if [ ! -f "CLAUDE.md" ]; then
|
|||||||
download_if_missing "CLAUDE.md" "$REPO_URL/CLAUDE.md"
|
download_if_missing "CLAUDE.md" "$REPO_URL/CLAUDE.md"
|
||||||
if [ -f "CLAUDE.md" ]; then
|
if [ -f "CLAUDE.md" ]; then
|
||||||
emit_raw_source_block >> CLAUDE.md
|
emit_raw_source_block >> CLAUDE.md
|
||||||
CREATED+=("CLAUDE.md ← 已寫入 raw source 宣告($VAULT_TYPE)")
|
CREATED+=("CLAUDE.md $(tn "← 已寫入 raw source 宣告(${VAULT_TYPE})" "← raw source declaration written (${VAULT_TYPE})")")
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
SKIPPED+=("CLAUDE.md (已存在,請手動加入對應區塊)")
|
SKIPPED+=("CLAUDE.md $(tn '(已存在,請手動加入對應區塊)' '(already exists — add the block manually)')")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 輸出結果 ──────────────────────────────────────
|
# ── 輸出結果 ──────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
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
|
for item in "${CREATED[@]}"; do echo " + $item"; done
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ${#SKIPPED[@]} -gt 0 ]; then
|
if [ ${#SKIPPED[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "⚠️ 跳過(已存在):"
|
t "⚠️ 跳過(已存在):" "⚠️ Skipped (already exists):"
|
||||||
for item in "${SKIPPED[@]}"; do echo " - $item"; done
|
for item in "${SKIPPED[@]}"; do echo " - $item"; done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -262,33 +335,59 @@ echo "────────────────────────
|
|||||||
if [ -f "CLAUDE.md" ]; then
|
if [ -f "CLAUDE.md" ]; then
|
||||||
if ! grep -q "raw source" CLAUDE.md; then
|
if ! grep -q "raw source" CLAUDE.md; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "📌 CLAUDE.md 已存在但缺少 raw source 宣告(偵測到 vault 類型:$VAULT_TYPE)。"
|
t "📌 CLAUDE.md 已存在但缺少 raw source 宣告。" \
|
||||||
echo " 請手動把以下區塊貼進去,讓 CC 與 Cowork 知道原始文件在哪、不要亂動 vault:"
|
"📌 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/^/ /'
|
emit_raw_source_block | sed 's/^/ /'
|
||||||
fi
|
fi
|
||||||
if $WANT_WIKI && ! grep -q "wiki/status.md" CLAUDE.md; then
|
if $WANT_WIKI && ! grep -q "wiki/status.md" CLAUDE.md; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:"
|
t "📌 CLAUDE.md 已存在但缺少 wiki 讀取順序,請手動加入:" \
|
||||||
|
"📌 CLAUDE.md exists but lacks the wiki reading order — please add it manually:"
|
||||||
echo ""
|
echo ""
|
||||||
echo ' ## Wiki 讀取順序'
|
if [ "$IS_ZH" = "yes" ]; then
|
||||||
echo ' | 檔案 | 時機 | 用途 |'
|
cat <<'SNIP'
|
||||||
echo ' |------|------|------|'
|
## Wiki 讀取順序
|
||||||
echo ' | `.claude/wiki/status.md` | session 開始第一件事 | 當前進度 |'
|
| 檔案 | 時機 | 用途 |
|
||||||
echo ' | `.claude/wiki/mistakes.md` | 做新功能前 | 已知誤解 |'
|
|------|------|------|
|
||||||
echo ' | `.claude/wiki/decisions-summary.md` | 設計判斷時 | 架構決策 |'
|
| `.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
|
fi
|
||||||
if $WANT_SDD && ! grep -q "docs/3-specs" CLAUDE.md; then
|
if $WANT_SDD && ! grep -q "docs/3-specs" CLAUDE.md; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "📌 CLAUDE.md 已存在但缺少 SDD 鐵律,請手動加入:"
|
t "📌 CLAUDE.md 已存在但缺少 SDD 鐵律,請手動加入:" \
|
||||||
|
"📌 CLAUDE.md exists but lacks the SDD iron rule — please add it manually:"
|
||||||
echo ""
|
echo ""
|
||||||
echo ' ## 絕對鐵律'
|
if [ "$IS_ZH" = "yes" ]; then
|
||||||
echo ' 1. 任何 code 變動前必須有對應 SDD(docs/3-specs/[子系統]/design.md)'
|
cat <<'SNIP'
|
||||||
echo ' 找不到 → 停手問負責人,不要自行建立。'
|
## 絕對鐵律
|
||||||
|
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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# settings.json 已存在 → 依模組提醒要合併哪些 hook
|
# settings.json 已存在 → 依模組提醒要合併哪些 hook
|
||||||
if [ -f ".claude/settings.json" ] && grep -q '"已存在"' <<<"${SKIPPED[*]}" 2>/dev/null; then :; fi
|
|
||||||
if [ -f ".claude/settings.json" ]; then
|
if [ -f ".claude/settings.json" ]; then
|
||||||
MISSING_HOOKS=()
|
MISSING_HOOKS=()
|
||||||
$WANT_WIKI && ! grep -q "session-start-recall.sh" .claude/settings.json && MISSING_HOOKS+=("SessionStart: session-start-recall.sh")
|
$WANT_WIKI && ! grep -q "session-start-recall.sh" .claude/settings.json && MISSING_HOOKS+=("SessionStart: session-start-recall.sh")
|
||||||
@@ -296,25 +395,33 @@ if [ -f ".claude/settings.json" ]; then
|
|||||||
$WANT_SDD && ! grep -q "sdd-guard.sh" .claude/settings.json && MISSING_HOOKS+=("PreToolUse(Write|Edit): sdd-guard.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
|
if [ ${#MISSING_HOOKS[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "📌 .claude/settings.json 已存在,請手動把以下 hooks 合併進去(保留既有設定):"
|
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
|
for h in "${MISSING_HOOKS[@]}"; do echo " • $h"; done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# pre-write-guard 是空殼,提醒它預設不攔(避免「以為有保護其實沒有」的安全錯覺)
|
# pre-write-guard 是空殼,提醒它預設不攔(避免「以為有保護其實沒有」的安全錯覺)
|
||||||
echo ""
|
echo ""
|
||||||
echo "ℹ️ .claude/hooks/pre-write-guard.sh 是「按需手填的空插槽」,預設不攔任何東西。"
|
t "ℹ️ .claude/hooks/pre-write-guard.sh 是「按需手填的空插槽」,預設不攔任何東西。" \
|
||||||
echo " 需要專案禁令?最簡單是叫你的 CC 寫一支貼合的 guard hook(比範本表達力強);"
|
"ℹ️ .claude/hooks/pre-write-guard.sh is an empty slot to fill on demand — by default it blocks nothing."
|
||||||
echo " 或自己填 FORBIDDEN_PATTERNS 並到 settings.json 掛上才會生效。"
|
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 ""
|
echo ""
|
||||||
echo "🚀 下一步:"
|
t "🚀 下一步:" "🚀 Next steps:"
|
||||||
if $WANT_WIKI; then
|
if $WANT_WIKI; then
|
||||||
echo " 在 Claude Code 對話裡執行 /wiki-init"
|
t " 在 Claude Code 對話裡執行 /wiki-init" \
|
||||||
echo " CC 會掃描現有文件、套用 .wikiignore、建立 wiki。"
|
" 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
|
fi
|
||||||
if $WANT_SDD; then
|
if $WANT_SDD; then
|
||||||
echo " 動 code 前先在 docs/3-specs/[子系統]/ 建 design.md(可用 /sdd-check 協助)"
|
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
|
fi
|
||||||
echo " GitHub issue:CC 可直接 /issue-handle 讀回自己 repo 的 issue(禁自動輪詢)"
|
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 ""
|
echo ""
|
||||||
|
|||||||
+39
-20
@@ -15,6 +15,14 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── i18n:依 locale 選語言,預設英文(curl | bash 常為 LANG=C)──
|
||||||
|
case "${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" in
|
||||||
|
zh*|*Hant*|*Hans*) IS_ZH="yes" ;;
|
||||||
|
*) IS_ZH="no" ;;
|
||||||
|
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://raw.githubusercontent.com/uncle6me-web/system-dev-template/main"
|
||||||
TEMPLATE_URL="$REPO_RAW/template"
|
TEMPLATE_URL="$REPO_RAW/template"
|
||||||
|
|
||||||
@@ -24,25 +32,29 @@ NEW=()
|
|||||||
TEMPLATED=()
|
TEMPLATED=()
|
||||||
|
|
||||||
# ── 版本比對:先看本機 vs 遠端,給使用者「值不值得更新」的判斷 ──
|
# ── 版本比對:先看本機 vs 遠端,給使用者「值不值得更新」的判斷 ──
|
||||||
LOCAL_VER="(未知)"
|
LOCAL_VER="$(tn '(未知)' '(unknown)')"
|
||||||
[ -f ".claude/VERSION" ] && LOCAL_VER="$(tr -d '[:space:]' < .claude/VERSION)"
|
[ -f ".claude/VERSION" ] && LOCAL_VER="$(tr -d '[:space:]' < .claude/VERSION)"
|
||||||
REMOTE_VER="$(curl -sSL "$TEMPLATE_URL/.claude/VERSION" 2>/dev/null | tr -d '[:space:]' || echo '')"
|
REMOTE_VER="$(curl -sSL "$TEMPLATE_URL/.claude/VERSION" 2>/dev/null | tr -d '[:space:]' || echo '')"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔄 system-dev-template updater"
|
echo "🔄 system-dev-template updater"
|
||||||
echo "================================="
|
echo "================================="
|
||||||
echo " 本機版本:${LOCAL_VER:-未知}"
|
t " 本機版本:${LOCAL_VER}" " Local version: ${LOCAL_VER}"
|
||||||
echo " 最新版本:${REMOTE_VER:-取不到(檢查網路)}"
|
t " 最新版本:${REMOTE_VER:-取不到(檢查網路)}" \
|
||||||
|
" Latest version: ${REMOTE_VER:-unavailable (check network)}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [ -z "$REMOTE_VER" ]; then
|
if [ -z "$REMOTE_VER" ]; then
|
||||||
echo "❌ 取不到遠端版本,可能是網路問題。請稍後再試。"
|
t "❌ 取不到遠端版本,可能是網路問題。請稍後再試。" \
|
||||||
|
"❌ Could not fetch the remote version (likely a network issue). Please try again later."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$LOCAL_VER" = "$REMOTE_VER" ]; then
|
if [ "$LOCAL_VER" = "$REMOTE_VER" ]; then
|
||||||
echo "✅ 已是最新版($LOCAL_VER),不需更新。"
|
t "✅ 已是最新版($LOCAL_VER),不需更新。" \
|
||||||
echo " (仍會同步模板邏輯檔,確保 hooks/commands 與最新一致。)"
|
"✅ Already up to date ($LOCAL_VER), nothing to update."
|
||||||
|
t " (仍會同步模板邏輯檔,確保 hooks/commands 與最新一致。)" \
|
||||||
|
" (Template logic files will still be synced to keep hooks/commands in line with the latest.)"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -61,14 +73,14 @@ update_file() {
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
rm -f "$dest.tmp"
|
rm -f "$dest.tmp"
|
||||||
echo " ⚠️ 抓取失敗,保留原檔:$dest"
|
t " ⚠️ 抓取失敗,保留原檔:$dest" " ⚠️ Download failed, keeping the original: $dest"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if curl -sSL "$src" -o "$dest" 2>/dev/null && [ -s "$dest" ]; then
|
if curl -sSL "$src" -o "$dest" 2>/dev/null && [ -s "$dest" ]; then
|
||||||
NEW+=("$dest") # 新功能:舊版沒有的檔
|
NEW+=("$dest") # 新功能:舊版沒有的檔
|
||||||
else
|
else
|
||||||
rm -f "$dest"
|
rm -f "$dest"
|
||||||
echo " ⚠️ 抓取失敗:$dest"
|
t " ⚠️ 抓取失敗:$dest" " ⚠️ Download failed: $dest"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -107,10 +119,12 @@ HAS_SDD=false
|
|||||||
[ -d ".claude/wiki" ] && HAS_WIKI=true
|
[ -d ".claude/wiki" ] && HAS_WIKI=true
|
||||||
if [ -f ".claude/hooks/sdd-guard.sh" ] || [ -d "docs/3-specs/TEMPLATE-sdd" ]; then HAS_SDD=true; fi
|
if [ -f ".claude/hooks/sdd-guard.sh" ] || [ -d "docs/3-specs/TEMPLATE-sdd" ]; then HAS_SDD=true; fi
|
||||||
|
|
||||||
echo "📦 偵測到已安裝模組:"
|
t "📦 偵測到已安裝模組:" "📦 Detected installed modules:"
|
||||||
$HAS_WIKI && echo " • LLM Wiki"
|
$HAS_WIKI && echo " • LLM Wiki"
|
||||||
$HAS_SDD && echo " • SDD"
|
$HAS_SDD && echo " • SDD"
|
||||||
{ $HAS_WIKI || $HAS_SDD; } || echo " (未偵測到任何模組——這裡可能還沒安裝,請改跑 install.sh)"
|
{ $HAS_WIKI || $HAS_SDD; } || \
|
||||||
|
t " (未偵測到任何模組——這裡可能還沒安裝,請改跑 install.sh)" \
|
||||||
|
" (No modules detected — nothing installed here yet; run install.sh instead.)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 客製檔:使用者手填的 guardrail,永不覆蓋(issue #3)──
|
# ── 客製檔:使用者手填的 guardrail,永不覆蓋(issue #3)──
|
||||||
@@ -167,29 +181,33 @@ echo ""
|
|||||||
echo "─────────────────────────────────"
|
echo "─────────────────────────────────"
|
||||||
if [ ${#NEW[@]} -gt 0 ]; then
|
if [ ${#NEW[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "🆕 新功能(舊版沒有,已加入):"
|
t "🆕 新功能(舊版沒有,已加入):" "🆕 New features (absent in the old version, now added):"
|
||||||
for f in "${NEW[@]}"; do echo " + $f"; done
|
for f in "${NEW[@]}"; do echo " + $f"; done
|
||||||
fi
|
fi
|
||||||
if [ ${#UPDATED[@]} -gt 0 ]; then
|
if [ ${#UPDATED[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "⬆️ 已更新(覆蓋成新版):"
|
t "⬆️ 已更新(覆蓋成新版):" "⬆️ Updated (overwritten with the new version):"
|
||||||
for f in "${UPDATED[@]}"; do echo " ~ $f"; done
|
for f in "${UPDATED[@]}"; do echo " ~ $f"; done
|
||||||
fi
|
fi
|
||||||
if [ ${#NEW[@]} -eq 0 ] && [ ${#UPDATED[@]} -eq 0 ]; then
|
if [ ${#NEW[@]} -eq 0 ] && [ ${#UPDATED[@]} -eq 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "✨ 模板邏輯檔已全部最新,無需變動。"
|
t "✨ 模板邏輯檔已全部最新,無需變動。" \
|
||||||
|
"✨ All template logic files are already up to date — no changes needed."
|
||||||
fi
|
fi
|
||||||
if [ ${#KEPT[@]} -gt 0 ]; then
|
if [ ${#KEPT[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔒 完整保留(你的內容/設定,從未碰過):"
|
t "🔒 完整保留(你的內容/設定,從未碰過):" \
|
||||||
|
"🔒 Fully preserved (your content/settings, never touched):"
|
||||||
for f in "${KEPT[@]}"; do echo " = $f"; done
|
for f in "${KEPT[@]}"; do echo " = $f"; done
|
||||||
fi
|
fi
|
||||||
if [ ${#TEMPLATED[@]} -gt 0 ]; then
|
if [ ${#TEMPLATED[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "📋 客製檔有新版範本(你的原檔沒動,新版另存旁邊,請自行 diff 採納):"
|
t "📋 客製檔有新版範本(你的原檔沒動,新版另存旁邊,請自行 diff 採納):" \
|
||||||
|
"📋 Custom files have a new template version (your original is untouched; the new one is saved alongside — diff and adopt as you like):"
|
||||||
for f in "${TEMPLATED[@]}"; do
|
for f in "${TEMPLATED[@]}"; do
|
||||||
echo " → $f"
|
echo " → $f"
|
||||||
echo " 比對:diff \"${f%.template.sh}.sh\" \"$f\""
|
t " 比對:diff \"${f%.template.sh}.sh\" \"$f\"" \
|
||||||
|
" compare: diff \"${f%.template.sh}.sh\" \"$f\""
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -201,13 +219,14 @@ if [ -f ".claude/settings.json" ]; then
|
|||||||
$HAS_SDD && ! grep -q "sdd-guard.sh" .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): sdd-guard.sh")
|
$HAS_SDD && ! grep -q "sdd-guard.sh" .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): sdd-guard.sh")
|
||||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "📌 settings.json 是你的設定(沒動),但偵測到缺以下 hook,請手動補上:"
|
t "📌 settings.json 是你的設定(沒動),但偵測到缺以下 hook,請手動補上:" \
|
||||||
|
"📌 settings.json is yours (untouched), but these hooks are missing — please add them manually:"
|
||||||
for h in "${MISSING[@]}"; do echo " • $h"; done
|
for h in "${MISSING[@]}"; do echo " • $h"; done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🚀 更新完成:${LOCAL_VER} → ${REMOTE_VER}"
|
t "🚀 更新完成:${LOCAL_VER} → ${REMOTE_VER}" "🚀 Update complete: ${LOCAL_VER} → ${REMOTE_VER}"
|
||||||
echo " 下次更新直接跑:bash scripts/update.sh"
|
t " 下次更新直接跑:bash scripts/update.sh" " Next time, just run: bash scripts/update.sh"
|
||||||
echo " 改了什麼看:CHANGELOG.md"
|
t " 改了什麼看:CHANGELOG.md" " See what changed: CHANGELOG.md"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.6.1
|
1.7.0
|
||||||
|
|||||||
Reference in New Issue
Block a user