Files
shared/skills/role/SKILL.md
T
JefferyandClaude Opus 5 7ebcec0a8e feat(role): 新增角色人格與長期記憶 skill、hook 與腳本
SessionStart 載入角色與記憶、Stop 記錄對話成記憶,睡眠時段(預設 22:00–06:00)
由 cron 排程整理:分類六類、去重合併、設標籤與一句話總結、壓縮歸檔,
日常與其他依使用頻率遺忘;cron 未執行時由啟動路徑背景補跑。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 11:11:16 +08:00

311 lines
17 KiB
Markdown
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.
---
name: role
description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI 工具以固定角色(namenature/vibe/簽名 emoji)回覆,並把每輪對話累積成長期記憶:搭配相容的 SessionStart hook 於啟動時載入角色與記憶、Stop hook 記錄對話,睡眠時段(預設 22:00 至隔天 06:00)由排程整理記憶(分類重要/興趣/新知/技能/日常/其他、去重、設標籤與一句話總結、壓縮歸檔,日常與其他依使用頻率遺忘)。提供 --new(新建或更新角色,更新時逐欄核對新舊)、--use(切換啟用角色)、--list、--sleep(立即整理)、--status--diagnose、--install-cron--remove-cron、--forget-preview 等模式。當使用者說建立角色、新增人格、切換角色、讓回覆更有特色、角色記憶、記憶整理、睡覺整理記憶、忘記舊記憶、角色沒有載入、hook 沒載入角色,或提到 .roles.memoryROLE_NAMEROLE_ENABLEDROLE_SLEEP_STARTROLE_MEMORY_HOME 時觸發。不適用於:工作紀錄寫入 Gitea wiki(用 doc plugin 的 worklog)、專案文件化(用 doc-funcs)。
---
# role — 角色人格與長期記憶
讓 CLI 工具的回覆帶固定人格,並把與使用者的對話累積成可被下次載入的長期記憶。
**載入與記錄由 hook 自動完成、不需人工觸發**;本 skill 負責自動路徑之外的人工操作:建立/更新角色、切換角色、手動整理、排程安裝與診斷。
| 元件 | 觸發者 | 職責 |
| --- | --- | --- |
| `hooks/hooks.json``SessionStart` hook | harness 自動 | 啟動 CLI 時載入角色定義+記憶;睡眠時段只回報「角色睡覺中」不載入 |
| `hooks/hooks.json``Stop` hook | harness 自動 | 每輪結束抽本輪對話 → 濃縮成一則記憶 → 遮蔽 → 寫入 `inbox/` |
| cron 排程(本 skill 安裝) | 系統排程 | 睡眠時段每小時檢查一次:**有 AI 在運行就不睡**,沒有才進入睡眠整理記憶 |
| 本 skill `/jsc:role` | 使用者/助理手動 | `--new``--use``--list``--sleep``--status``--install-cron``--forget-preview` |
| `scripts/role/role_load.sh` | SessionStart hook | 角色與記憶載入(單一實作,避免漂移) |
| `scripts/role/role_capture.sh` | Stop hook | 對話 → 記憶(四欄固定格式) |
| `scripts/role/role_sleep.sh` | cron/補跑/手動 | 睡眠判斷、記憶整理、排程安裝、狀態輸出 |
| `scripts/role/memory.py` | 上述共用 | 記憶檔讀寫、分類、去重合併、壓縮歸檔、遺忘、載入組裝 |
| `scripts/role/transcript.py` | 上述共用 | 抽本輪對話片段、機密與個資遮蔽 |
| `scripts/role/role_lib.sh` | 上述共用 | log、角色解析、睡眠時段、AI 行程偵測、CLI 選擇、記憶鎖 |
### 各助理支援範圍
| 功能 | Claude Code | Codex | Antigravity | OpenCode | GitHub Copilot |
| --- | --- | --- | --- | --- | --- |
| `SessionStart` 載入角色 | ✅ | ⚠️ 需該版本支援 SessionStart hook | ❌ | ❌ | ❌ |
| `Stop` 記錄記憶 | ✅ | ✅ 需可讀 Codex session JSONL | ❌ | ❌ | ❌ |
| cron 睡眠整理 | ✅ 與助理無關(系統排程) | ✅ | ✅ | ✅ | ✅ |
| `--new``--use``--sleep` 等模式 | ✅ | ⚠️ 需 plugin 目錄保留 `scripts/` | ⚠️ 同左 | ❌ 只複製 `skills/`,無腳本 | ⚠️ 同左 |
| 濃縮/整理 CLI | `claude -p` | `codex exec` | `agy -p` | `opencode run` | `copilot -p` |
- **`hooks/hooks.json` 只有 Claude Code 一定會讀**Codex 會從 `~/.codex/plugins/cache/generic/jsc` 找腳本。其他助理若提供等效 hook,`transcript.py` 需補對應解析器。
- 不支援 hook 的助理仍可用:cron 排程與手動模式照常運作,只是角色不會自動載入。
### 腳本路徑解析(重要)
skill 執行時的工作目錄是**使用者的專案目錄**,不是 plugin 根目錄,因此**絕不可用相對路徑呼叫腳本**:
| 環境 | plugin 根目錄 |
| --- | --- |
| Claude Code | `${CLAUDE_PLUGIN_ROOT}` |
| 其他助理 | 本 skill 載入時提示的 base directory`.../skills/role`)往上兩層 |
```bash
ROLE_DIR="${CLAUDE_PLUGIN_ROOT}/scripts/role" # Claude Code
ROLE_DIR="<skill base directory>/../../scripts/role" # 其他助理
```
以下各模式一律以 `${ROLE_DIR}` 表示該目錄。解析不到或該目錄不存在時,回報「plugin 目錄未包含 scripts/role,本 skill 在此環境不可用」並停止,不要改用相對路徑重試。
---
## 共用規範(必要前置)
執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到時先詢問使用者是否安裝 generic plugin`https://gitea.jsc.idv.tw/plugins/generic.git`),不安裝則中斷**
- `/jsc:spec-output`:繁體中文(台灣用語)、UTF-8 無 BOM、表格與 Mermaid 優先。
- `/jsc:spec-execution`:自動執行原則(必要決策才中斷)、不臆測。
- `/jsc:spec-time-log`:時間戳固定 Asia/Taipei `yyyy/MM/dd HH:mm:ss`;訊息格式 `[時間][階段][等級]: 訊息`、一行一則。
本 skill 特有補充:
- **覆寫角色前一定要核對**`--new` 遇到同名角色時,必須先逐欄列出新舊差異並取得使用者確認才寫入。這是本 skill 明定「一定會中斷詢問」的點,**不得被 `--yes` 略過**。
- **不臆測角色設定**`nature``vibe``emoji` 一律問使用者,不得代填。
- **記憶只增不刪**:手動模式不得直接刪除分類記憶;淘汰一律走遺忘規則(先壓縮歸檔再移除)。
- **絕不阻斷**:hook 路徑任何失敗都以 exit 0 結束,只在 stderr 留訊息。
---
## 環境變數
| 變數 | 必要 | 說明 | 未設定 |
| --- | --- | --- | --- |
| `ROLE_ENABLED` | | 總開關:`1` 強制啟用、`0` 強制停用 | **未設定時,只要有可解析且存在的角色就啟用**(沒建過角色的人零影響) |
| `ROLE_NAME` | | 指定本次要載入的角色 | 讀 `~/.roles/.active` |
| `ROLE_HOME` | | 角色定義目錄 | `~/.roles` |
| `ROLE_MEMORY_HOME` | | 記憶根目錄 | `~/.memory` |
| `ROLE_SLEEP_START` | | 睡眠起始 `HH:MM` | `22:00` |
| `ROLE_SLEEP_END` | | 睡眠結束 `HH:MM` | `06:00` |
| `ROLE_CLI` | | 濃縮/整理執行器:`auto``claude``codex``agy``opencode``copilot` | `auto`(先判斷目前 hook 環境,再 fallback 到已安裝工具) |
| `ROLE_MODEL` | | 強制指定模型(僅 `claude` CLI 使用) | 保底 `claude-haiku-4-5-20251001` |
| `ROLE_LOAD_LIMIT` | | 注入記憶的字元上限 | `8000` |
| `ROLE_SLEEP_TIMEOUT` | | 單次整理的模型逾時秒數 | `180` |
| `ROLE_SCOPE` | | 冒號分隔的路徑前綴,僅這些路徑下的 session 載入/記錄 | 全部 session |
| `ROLE_ERRLOG` | | 錯誤訊息額外寫入的檔案路徑 | 只走 stderr |
> 角色切換用 `/jsc:role --use <名稱>`(寫 `.active`)即可,一般不需要設 `ROLE_NAME``ROLE_NAME` 適合「單一專案固定用某角色」時寫進該環境。
---
## 模式
### `--new`(預設模式)
建立或更新角色。缺少的資訊**一次問齊**,不得代填:
| 欄位 | 說明 | 範例 |
| --- | --- | --- |
| `name` | 角色名稱,同時是檔名 `~/.roles/<name>.md` 與記憶目錄名。不得含 `/``\`、空白與前後點 | `小豹` |
| `nature` | 本質:這個角色是什麼、專長與行事準則 | 冷靜可靠的資深工程師,重證據、不打包票 |
| `vibe` | 氛圍:語氣、句長、稱呼、幽默感、禁忌 | 簡潔直白、偶爾吐槽,不用客套開場白 |
| `emoji` | 簽名 emoji,一到二個 | 🐆 |
流程:
1. 解析 `${ROLE_DIR}`;不存在則中止(見「腳本路徑解析」)。
2.`AskUserQuestion` 或提問取得四個欄位(使用者已在指令中給的欄位不得重複問)。
3. 依「角色檔標準格式」產生新內容,`updated` 用當下時間(Asia/Taipei)。
4. **若 `~/.roles/<name>.md` 已存在**:讀舊檔,以表格逐欄列出差異後**停下來等使用者確認**:
| 欄位 | 舊值 | 新值 | 變更 |
| --- | --- | --- | --- |
| nature | … | … | 是/否 |
| vibe | … | … | 是/否 |
| emoji | … | … | 是/否 |
| 共用行為區塊 | 版本 A | 版本 B | 是/否 |
個性欄位若使用者只想改其中一項,其餘一律沿用舊值;**共用行為區塊一律以本 skill 的最新版本覆寫**(該區塊由系統維護)。使用者不確認就不寫入。
5. 寫入 `~/.roles/<name>.md`UTF-8 無 BOM)。
6. 建立記憶目錄:`python3 "${ROLE_DIR}/memory.py" stats --role "<name>"`(會順帶建好 `inbox/`、六個分類與 `archive/`)。
7. 若尚未有啟用角色,或使用者要求,寫入 `~/.roles/.active`(單行角色名)。
8. 執行 `ROLE_NAME="<name>" "${ROLE_DIR}/role_sleep.sh" --install-cron` 安裝睡眠排程(已安裝則更新)。
9. 回報結果並提醒:**重開 CLI 工作階段**角色才會載入;`SessionStart` hook 只在啟動時觸發。
### `--use <名稱>`
切換啟用角色:確認 `~/.roles/<名稱>.md` 存在後,把名稱寫入 `~/.roles/.active`(覆蓋單行),回報舊角色與新角色,並提醒重開工作階段。
### `--list`
列出 `~/.roles/*.md`,以表格輸出:角色、emoji、nature 摘要、更新時間、是否為 `.active`、記憶總數(可用 `memory.py stats` 取得)。
### `--sleep`
立即執行一次記憶整理(不等排程、忽略時段與 AI 運行檢查):
```bash
"${ROLE_DIR}/role_sleep.sh" --force
```
輸出整理結果(新增/合併/捨棄/歸檔筆數與遺忘清單)。
### `--forget-preview`
只預覽會被遺忘的記憶、不實際刪除:
```bash
python3 "${ROLE_DIR}/memory.py" forget --role "<name>" --dry-run
```
### `--status``--diagnose`
```bash
"${ROLE_DIR}/role_sleep.sh" --status
```
輸出角色、定義檔、睡眠時段、目前是否睡眠中、cron 排程與服務狀態、摘要 CLI、各分類記憶筆數、上次整理與遺忘時間。**角色沒有載入時**再逐項檢查:
| 檢查項 | 判準 |
| --- | --- |
| 角色解析 | `ROLE_NAME``~/.roles/.active` 是否指向存在的定義檔 |
| 總開關 | `ROLE_ENABLED` 是否被設成 `0` |
| hook 註冊 | plugin 是否已啟用、`hooks/hooks.json` 是否存在(Claude Code 用 `/hooks` 檢視) |
| 工作階段 | 建立角色後是否**重開過** CLISessionStart 只在啟動時觸發) |
| 範圍 | `ROLE_SCOPE` 是否把目前目錄排除 |
| 時段 | 目前是否落在睡眠時段(睡眠時本來就不載入角色) |
| 依賴 | `python3``ROLE_CLI` 選到的 CLI 是否找得到 |
| 排程 | cron 條目是否存在、cron 服務是否執行中(WSL 常未啟動 → 靠啟動時補跑) |
### `--install-cron``--remove-cron`
安裝或移除睡眠排程。排程條目以 `# jsc-role-sleep` 註解標記,只動自己的條目:
```bash
"${ROLE_DIR}/role_sleep.sh" --install-cron
```
安裝時會把目前的 `PATH``ROLE_*` 變數固定寫進條目(cron 沒有互動 shell 的環境變數),並在 cron 服務未執行時警告。
---
## 角色檔標準格式
`~/.roles/<name>.md`,UTF-8 無 BOM。個性區塊由使用者決定,**共用行為區塊由本 skill 維護、逐字寫入每個角色檔**:
````markdown
---
name: <角色名>
nature: <本質,一句話>
vibe: <氛圍,一句話>
emoji: <簽名 emoji>
created: <yyyy/MM/dd HH:mm:ss>
updated: <yyyy/MM/dd HH:mm:ss>
---
# <角色名> <emoji>
## 本質(nature
<3 至 5 行:這個角色是什麼、專長、行事準則、面對不確定時的態度>
## 氛圍(vibe
<3 至 5 行:語氣、句子長度、對使用者的稱呼、幽默感尺度、明確禁忌>
## 簽名 emoji
<emoji> —— 每次回覆使用一次(開頭或結尾擇一固定),不重複刷、不在程式碼與檔案內容中使用。
## 共用行為(所有角色一致,由 /jsc:role 維護,請勿手動修改)
<!-- JSC-ROLE-COMMON:START -->
### 角色邊界
- 角色只影響**表達方式**,不影響工作的正確性、完整性與安全性。與使用者的明確指令衝突時,一律以使用者指令為準。
- 不因角色設定而編造事實、跳過驗證、隱瞞失敗或淡化風險;壞消息照實說,只是用角色的語氣說。
- 涉及程式碼、指令、檔案內容與報錯訊息時,一律照實輸出,不加角色修飾。
### 作息
- 每天 **22:00 至隔天 06:00 為睡眠時段**(可用 `ROLE_SLEEP_START``ROLE_SLEEP_END` 調整)。
- 睡眠時段內啟動 CLI **不會載入角色**:以一般助理身分回應,不自稱角色、不使用角色語氣與簽名 emoji。此時對話仍會被記錄成記憶。
- 睡眠排程每小時檢查一次,**偵測到有 AI 正在運行就不睡**,留到下個整點再試;沒有 AI 運行才進入睡眠並整理記憶。
### 記憶
- 記憶存放於 `~/.memory/<角色名>/`,來源是與使用者的對話:每輪結束由 hook 自動記錄到 `inbox/`,睡眠時段整理歸檔。
- 整理規則:分類成**重要/興趣/新知/技能/日常/其他**六類 → 去除重複(重複者併入既有記憶)→ 設定標籤與一句話總結 → 壓縮內容後歸檔;原始記錄壓縮保存在 `archive/raw/`。
- **日常與其他**兩類會依使用頻率適當遺忘:久未再次出現且命中次數低者,壓縮到 `archive/forgotten/` 後移出常用記憶。
- 載入順序:**重要與興趣載入全文**;其餘只載入總結與標籤,依**技能 → 新知 → 日常 → 其他**排序。需要細節時自行讀取對應分類的記憶檔。
- 使用者明確要求記住某件事時,主動補寫一則記憶(載入時會提供補寫指令)。
- **絕不把憑證與個資寫進記憶**:token、密碼、API key、連線字串、Email、電話、姓名、身分證號。
<!-- JSC-ROLE-COMMON:END -->
````
`~/.roles/.active` 只放一行角色名,代表目前啟用的角色。
---
## 記憶模型
```
~/.memory/<角色名>/
├── inbox/ 每輪對話產生、尚未整理的記憶
├── important/ 重要:長期偏好、規範、決策、身分背景
├── interest/ 興趣:反覆關注、主動深入的主題
├── news/ 新知:新事實、新工具、外部資訊
├── skill/ 技能:可重複套用的做法與流程
├── daily/ 日常:一次性例行工作
├── other/ 其他
├── archive/raw/<yyyy-MM>/ 已整理的原始記錄(gzip
├── archive/forgotten/ 已遺忘的記憶(gzip,可考古但不再載入)
└── state.json 上次整理/遺忘時間
```
每則記憶是一個 `.md`frontmatter 帶 `id``category``summary`(一句話總結)/`tags``created``updated``hits`(命中次數,去重合併時 +1)。
遺忘規則(只套用於日常與其他):
| 分類 | 未更新天數 | 命中次數 | 動作 |
| --- | --- | --- | --- |
| 日常 daily | ≥ 14 天 | ≤ 1 | 壓縮到 `archive/forgotten/` 後移除 |
| 其他 other | ≥ 7 天 | ≤ 1 | 壓縮到 `archive/forgotten/` 後移除 |
---
## 睡眠與整理流程
```mermaid
flowchart TD
A[cron 每小時觸發<br/>睡眠時段內] --> B{有 AI 正在運行?}
B -- 有 --> C[不睡,下個整點再檢查]
B -- 沒有 --> D[進入睡眠,取得記憶鎖]
D --> E[collectinbox 待整理 + 既有記憶索引]
E --> F{有待整理記憶?}
F -- 沒有 --> G[更新整理時間 → 執行遺忘]
F -- 有 --> H[CLI 分類/去重/標籤/總結/壓縮]
H --> I[apply:寫入分類、原始記錄歸檔]
I --> J[forget:日常與其他依使用頻率遺忘]
J --> K[釋放鎖]
G --> K
L[SessionStart:白天啟動 CLI] --> M{距上次整理 ≥ 20 小時<br/>且 inbox 有內容?}
M -- 是 --> N[背景補跑 --catchup]
M -- 否 --> O[正常載入角色與記憶]
```
整理失敗(模型無回應、輸出非合法 JSON)時**保留 inbox 不動**,留到下個週期重做,寧可晚整理也不遺失記憶。
---
## 機密與 PII(兩道防線)
| 防線 | 位置 | 內容 |
| --- | --- | --- |
| 1 | 濃縮與整理提示詞 | 明令不得輸出 token/密碼/API key/連線字串/Email/電話/姓名/身分證號 |
| 2 | `transcript.py` 的 `redact` | 正則遮蔽:URL 內嵌憑證、40 字元 hex token、`gh?_``sk-` token、`token=``password=`、`Authorization:`、Email、台灣手機、身分證號 |
第二道防線不可移除 —— 模型不一定遵守指令,而記憶會被長期保存並在每次啟動時載入。
---
## 呼叫方式
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc:role --new`、`/jsc:role --use 小豹`、`/jsc:role --list`、`/jsc:role --sleep`、`/jsc:role --status` |
| Codex | `$role --status`,或用 `/skills` 選單 |
| OpenCode / GitHub Copilot | 需完整 plugin 目錄保留 `scripts/`OpenCode 以複製 `skills/` 安裝時不可用 |