使用者反映身分設定與性格語氣擠在同一段(例如西莉卡的「馴獸師、養畢娜」是身分, 「細心、努力」是性格),要求拆成兩個檔案且不得遺失內容。 格式(扁平式,與既有 .assets/.checks/.lock 命名一致): - <ID>.identity.md:角色 ID、顯示名稱、來源作品、與使用者的關係定位、簽名 emoji - <ID>.soul.md:本質(nature)、氛圍(vibe) 共用行為規則**不再寫入角色檔**:role_load.sh 從不讀角色檔裡那份,它是冗余副本, 只會多一個漏同步的機會。內容完整保留於 role_load.sh(實際生效)與 SKILL.md(文件)。 - role_lib.sh:新增 role_identity_file/role_soul_file/role_legacy_file/ role_is_new_format;role_file 改為新格式優先、找不到退回舊檔,既有角色不受影響 - role_list_peers 支援兩種格式並避免同一角色重複列出 - role_load.sh:身分與人格分別讀取,新增「來源」與「關係定位」注入區塊 - 新增 --migrate <角色 ID>:逐字搬移本質、氛圍與簽名 emoji,來源與關係定位產生 待填空白,舊檔保留不動,新檔已存在時中止不覆寫 同時修掉兩個會造成實際損失的錯: - --export 原本硬編 cp 成 <ID>.md,新格式會被寫成舊檔名且遺失人格檔,備份救不回角色 - --export 從未備份 <ID>.checks(使用者自訂的晨間檢查腳本),一併補上 - --agent 原只讀 role_file(新格式即 identity),匯出的 sub agent 人格會是空的 驗證:遷移後逐字比對確認本質/氛圍/簽名 emoji/id/name/emoji/created 全部一致 且共用行為未寫入;新格式匯出的備份含 identity、soul、舊檔、assets、checks 與記憶; --agent 取得人格非空;--status 正確顯示格式與兩檔路徑;舊格式角色載入完全不受影響。 版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
39 KiB
name, description
| name | description |
|---|---|
| role | 角色人格與長期記憶系統的建立與維護 skill。讓 CLI 工具以固定角色(name/nature/vibe/簽名 emoji)回覆,並把每輪對話累積成長期記憶:搭配相容的 SessionStart hook 於啟動時依字元預算載入高價值記憶、Stop hook 先本地過濾再輕量記錄對話,睡眠時段(預設 22:00 至隔天 06:00)由排程整理記憶(NREM 鞏固:分類/去噪/去重/合併/優先度;REM 整合:跨記憶連結/抽象化/提取線索;再依 semantic/episodic/procedural/emotional/preference/rule 與 explicit/implicit 標記長期記憶型態,壓縮歸檔並適當遺忘)。提供 --new(新建或更新角色;可只給角色名稱,必要時詢問來源/作品並推斷 name/nature/vibe/emoji 四欄)、--use(以角色 ID 切換啟用角色)、--list(列出角色與 ID)、--export(匯出角色壓縮檔)、--sleep(立即整理)、--status/--diagnose、--install-cron/--remove-cron、--forget-preview、--brief(晨間狀態檢查)、--agent(匯出成 sub agent 供多角色協作)、--migrate(舊格式角色檔拆成身分與人格兩檔)等模式。當使用者說建立角色、新增人格、切換角色、匯出角色、備份角色、讓回覆更有特色、角色記憶、記憶整理、睡覺整理記憶、忘記舊記憶、角色沒有載入、hook 沒載入角色、晨間狀態檢查、早上主動回報狀態,或提到 .roles/.memory/ROLE_NAME/ROLE_ENABLED/ROLE_SLEEP_START/ROLE_MEMORY_HOME/ROLE_LOAD_LIMIT/ROLE_LOAD_INBOX_LIMIT/ROLE_LOAD_DIALOG_TURNS/ROLE_CAPTURE_ENABLED 時觸發。不適用於:工作紀錄寫入 Gitea wiki(用 /jsc-doc:worklog)、專案文件化(用 /jsc-doc:funcs)。 |
role — 角色人格與長期記憶
讓 CLI 工具的回覆帶固定人格,並把與使用者的對話累積成可被下次載入的長期記憶。 載入與記錄由 hook 自動完成、不需人工觸發;本 skill 負責自動路徑之外的人工操作:建立/更新角色、切換角色、手動整理、排程安裝與診斷。
| 元件 | 觸發者 | 職責 |
|---|---|---|
hooks/hooks.json 的 SessionStart hook |
harness 自動 | 啟動 CLI 時依字元預算載入角色定義+高價值記憶,另以獨立預算載入近期逐字對話與未整理工作記憶做工作階段交接,並要求角色在本工作階段第一則回覆主動問候;睡眠時段只回報「角色睡覺中」不載入 |
hooks/hooks.json 的 Stop hook |
harness 自動 | 每輪結束先記錄最後互動時間 → 用本地規則過濾低價值短回合 → 值得保存時才濃縮成一則輕量 inbox 記憶 → 遮蔽 → 寫入 inbox/ |
| cron 排程(本 skill 安裝) | 系統排程 | 睡眠時段每小時檢查一次:有 AI 在運行就不睡;另可依 CLI 閒置時間自動小睡整理 |
本 skill /jsc-generic:role |
使用者/助理手動 | --new/--use/--list/--export/--agent/--migrate/--sleep/--brief/--status/--install-cron/--forget-preview |
scripts/role/role_load.sh |
SessionStart hook | 角色與記憶載入;參考 OpenClaw 的 SOUL/AGENTS/USER/MEMORY 分層,把人格、操作邊界、使用者記憶分開注入,並提供第一則回覆問候提示(單一實作,避免漂移) |
scripts/role/role_capture.sh |
Stop hook | 對話 → 記憶(固定欄位格式) |
scripts/role/role_sleep.sh |
cron/小睡/補跑/手動 | 睡眠與小睡判斷、記憶整理、角色匯出、sub agent 定義匯出、晨間狀態檢查、排程安裝、狀態輸出 |
scripts/role/memory.js |
上述共用 | 記憶檔讀寫、分類、去重合併、優先度、心理學記憶型態與關聯 metadata、壓縮歸檔、遺忘、載入組裝 |
scripts/role/transcript.js |
上述共用 | 抽本輪對話片段、抽最近數輪純對話供工作階段交接、機密與個資遮蔽 |
scripts/role/role_lib.sh |
上述共用 | log、角色解析、睡眠時段、AI 行程偵測、CLI 選擇、記憶鎖 |
scripts/role/examples/ |
使用者自行複製 | 晨間狀態檢查的範例腳本;複製到 ~/.roles/<角色 ID>.checks/ 才會生效 |
各助理支援範圍
| 功能 | 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-generic找腳本。其他助理若提供等效 hook,transcript.js需補對應解析器。 -
不支援 hook 的助理仍可用:cron 排程與手動模式照常運作,只是角色不會自動載入。
-
每個 plugin 只註冊自己擁有的 hook:
jsc-code/jsc-doc/jsc-generic的 plugin 名稱各自獨立,Claude Code 以 plugin 名稱為鍵註冊 hooks,因此三者互不覆蓋、也不需要同步。各 repo 的hooks/hooks.json只負責自己擁有的腳本:plugin hooks.json 內容 擁有的腳本 jsc-genericSessionStart(role_load)+Stop(role_capture)scripts/role/jsc-docStop(worklog)scripts/worklog/jsc-code無 hooks/hooks.json無 hook 腳本 歷史背景:在 plugin 名稱分離前,三個 repo 都叫
jsc,同名時 Claude Code 只保留一份 hooks 註冊且無法預期哪一份會贏,因此當時三份hooks/hooks.json必須維持同一份合併超集。名稱分離後這個限制已解除,不可再把別的 plugin 的 hook 寫進自己的 hooks.json,否則會重複執行。 -
每個 hook 指令仍先試
$CLAUDE_PLUGIN_ROOT,找不到再依「擁有者 marketplace 優先 → 全 cache 後援」的順序搜尋~/.claude/plugins/cache/~/.codex/plugins/cache,以相容未設CLAUDE_PLUGIN_ROOT的助理。
腳本路徑解析(重要)
skill 執行時的工作目錄是使用者的專案目錄,不是 plugin 根目錄,因此絕不可用相對路徑呼叫腳本:
| 環境 | plugin 根目錄 |
|---|---|
| Claude Code | ${CLAUDE_PLUGIN_ROOT} |
| 其他助理 | 本 skill 載入時提示的 base directory(.../skills/role)往上兩層 |
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-generic:spec-output:繁體中文(台灣用語)、UTF-8 無 BOM、表格與 Mermaid 優先。/jsc-generic:spec-execution:自動執行原則(必要決策才中斷)、不臆測。/jsc-generic:spec-time-log:時間戳固定 Asia/Taipeiyyyy/MM/dd HH:mm:ss;訊息格式[時間][階段][等級]: 訊息、一行一則。
本 skill 特有補充:
- 覆寫角色前一定要核對:
--new遇到同名角色時,必須先逐欄列出新舊差異並取得使用者確認才寫入。這是本 skill 明定「一定會中斷詢問」的點,不得被--yes略過。 - 不臆測角色設定:可依使用者提供的角色名稱、來源/作品、形象圖或同意上網後取得的可靠資料,推斷
name/nature/vibe/emoji四欄;資料不足或角色名稱有歧義時,必須先詢問來源、作品、參考連結或檔案,不得硬猜。 - 記憶只增不刪:手動模式不得直接刪除分類記憶;淘汰一律走遺忘規則(先壓縮歸檔再移除)。
- 絕不阻斷:hook 路徑任何失敗都以 exit 0 結束,只在 stderr 留訊息。
- 角色分層載入:SessionStart 不把整份角色檔原封不動注入;只抽出角色 ID、顯示名稱、本質、氛圍與簽名 emoji 作為
SOUL,再由 hook 產生固定AGENTS操作邊界與USER/MEMORY記憶區塊。這是為了避免人格檔裡的背景故事、模板文字或舊共用規則污染工程規則。 - 個人記憶同意狀態:使用者第一次同意或拒絕保存非敏感個人資料後,狀態寫入
~/.memory/<角色 ID>/state.json的personal_memory_consent。狀態為accepted時不必每次重問;declined或unknown時不得保存可識別個人的背景。
環境變數
| 變數 | 必要 | 說明 | 未設定 |
|---|---|---|---|
ROLE_ENABLED |
總開關:1 強制啟用、0 強制停用 |
未設定時,只要有可解析且存在的角色就啟用(沒建過角色的人零影響) | |
ROLE_NAME |
指定本次要載入的角色 ID | 讀 ~/.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 |
SessionStart 注入長期記憶的字元上限,用來控制角色常駐 context 成本 | 4000 |
|
ROLE_LOAD_FULL_MIN_PRIORITY |
全文載入的最低優先度 | 4 |
|
ROLE_LOAD_DIGEST_MIN_PRIORITY |
摘要載入的最低優先度;低於門檻但有 links 的記憶仍可載入摘要 | 3 |
|
ROLE_LOAD_INBOX_LIMIT |
SessionStart 注入近期工作記憶(未整理的 inbox/)的字元上限;獨立預算,不佔用 ROLE_LOAD_LIMIT。設 0 可關閉 |
1200 |
|
ROLE_LOAD_INBOX_COUNT |
近期工作記憶最多載入幾則(取最新的,最新在前)。設 0 可關閉 |
10 |
|
ROLE_LOAD_DIALOG_TURNS |
SessionStart 注入近期逐字對話的輪數(一輪=使用者一則+角色一則)。設 0 可關閉 |
8 |
|
ROLE_LOAD_DIALOG_LIMIT |
近期逐字對話的字元上限;獨立預算,不佔用 ROLE_LOAD_LIMIT。設 0 可關閉 |
4000 |
|
ROLE_CAPTURE_ENABLED |
Stop hook 記憶記錄開關;設 0 可完全停用以節省額度 |
1 |
|
ROLE_CAPTURE_MIN_CHARS |
Stop hook 本地過濾門檻;低於門檻且無明確記憶線索時不呼叫模型 | 240 |
|
ROLE_CAPTURE_TIMEOUT |
Stop hook 輕量濃縮模型逾時秒數 | 25 |
|
ROLE_SLEEP_TIMEOUT |
單次 NREM/REM 整理的模型逾時秒數 | 180 |
|
ROLE_SLEEP_COLLECT_LIMIT |
睡眠整理送進模型的素材字元預算 | 12000 |
|
ROLE_SLEEP_BATCH |
單次睡眠整理最多處理的 inbox 筆數 | 60 |
|
ROLE_SLEEP_EXISTING_LIMIT |
睡眠整理素材中可放入的既有記憶索引筆數 | 120 |
|
ROLE_SLEEP_OUTPUT_LIMIT |
睡眠整理模型輸出套用前的字元上限 | 8000 |
|
ROLE_NAP_ENABLED |
小睡整理開關;CLI 閒置一段時間且 inbox 達門檻時自動整理 | 1 |
|
ROLE_NAP_IDLE_MINUTES |
小睡前需連續閒置的分鐘數,由 Stop hook 記錄最後互動時間 | 45 |
|
ROLE_NAP_MIN_INBOX |
小睡整理所需的最少待整理 inbox 筆數 | 3 |
|
ROLE_NAP_INTERVAL_MINUTES |
小睡排程檢查間隔分鐘數(cron 每 */N 分鐘觸發) |
10 |
|
ROLE_BRIEF_ENABLED |
晨間狀態檢查開關;設 0 可停用 |
1 |
|
ROLE_BRIEF_TIMEOUT |
單個檢查腳本的逾時秒數 | 30 |
|
ROLE_BRIEF_EACH_LIMIT |
單個檢查腳本輸出的字元上限 | 600 |
|
ROLE_BRIEF_LIMIT |
所有檢查腳本輸出合計的字元上限 | 2000 |
|
ROLE_SINGLE_INSTANCE |
單一載入實例限制:同一角色同時只被一個工作階段載入。設 0 可停用 |
1 |
|
ROLE_INSTANCE_IDLE_MINUTES |
前一個工作階段的 transcript 閒置多久後自動釋放角色鎖 | 30 |
|
ROLE_SKIP_INSTANCE_LOCK |
設 1 時跳過單一載入鎖且不寫鎖,供 sub agent 等非對話情境使用 |
0 |
|
ROLE_SCOPE |
冒號分隔的路徑前綴,僅這些路徑下的 session 載入/記錄 | 全部 session | |
ROLE_ERRLOG |
錯誤訊息額外寫入的檔案路徑 | 只走 stderr |
角色切換用
/jsc-generic:role --use <角色 ID>(寫.active)即可,一般不需要設ROLE_NAME;ROLE_NAME適合「單一專案固定用某角色」時寫進該環境。角色 ID 是英文大寫語意前綴加數字索引,例如ENGINEER01、MUSE02。若很在意額度,優先調低ROLE_LOAD_LIMIT或設ROLE_CAPTURE_ENABLED=0。
模式
--new(預設模式)
建立或更新角色。使用者可以直接提供完整四欄描述,也可以只提供角色名稱;資訊不足時一次問齊必要來源或描述,不得硬猜。使用者輸入的角色資訊視為「描述」,
不得直接拿描述或姓名當檔名;必須先產生角色 ID,再用 ID 作為角色檔名、記憶目錄名稱、.active 與 ROLE_NAME 的值。
| 欄位 | 說明 | 範例 |
|---|---|---|
name |
顯示名稱,只寫入角色檔 frontmatter 與標題,不作為檔名或目錄名 | 小豹 |
id |
角色 ID,由助理依角色描述產生:英文大寫、有意義、加兩位數索引;同前綴已存在時遞增 | ENGINEER01 |
nature |
本質:這個角色是什麼、專長與行事準則 | 冷靜可靠的資深工程師,重證據、不打包票 |
vibe |
氛圍:語氣、句長、稱呼、幽默感、禁忌 | 簡潔直白、偶爾吐槽,不用客套開場白 |
emoji |
簽名 emoji,一到二個;若後續成功建立心情 emoji 圖表,這個值作為不支援圖片時的 fallback | 🐆 |
appearance_reference |
選填;角色形象圖來源、作品名稱、圖片 URL 或本機檔案路徑,用來產生心情 emoji | Sword Art Online 結衣、https://.../yui.jpg、/path/avatar.png |
流程:
-
解析
${ROLE_DIR};不存在則中止(見「腳本路徑解析」)。 -
先解析使用者已提供的資訊:
- 若已提供
name/nature/vibe/emoji四欄,直接使用,不重複詢問。 - 若只提供角色名稱,先判斷是否有足夠上下文可唯一辨識;不足或同名角色可能混淆時,詢問來源、作品名稱、官方頁面、圖片 URL 或本機檔案路徑。
- 若使用者允許上網,依角色名稱與來源/作品搜尋可靠來源;若不允許上網,僅根據使用者提供的來源或描述推斷。
- 依可驗證資料推斷
name/nature/vibe/emoji四欄,並把推斷結果視為新角色草稿。推斷信心不足時,只問缺少的欄位,不得代填。
- 若已提供
-
可一併取得
appearance_reference。emoji一律保留作為 fallback,不因後續產生心情 emoji 圖表而丟棄。 -
詢問使用者是否要到網路搜尋角色資料來建立初始記憶與形象圖;若第 2 步已因使用者允許上網而搜尋過,可沿用該次搜尋結果,不重複詢問。若使用者同意,依角色描述搜尋可靠來源,摘要成繁體中文要點並保留來源 URL,同時搜尋適合做角色心情 emoji 的形象圖。若搜尋結果無法可靠判斷角色形象,先詢問使用者參考來源、作品名稱、圖片 URL 或本機檔案路徑,不得臆測形象。若使用者不同意上網且也未提供形象參考,仍可建立角色,只是不建立背景種子記憶與心情 emoji 圖表,並使用原本的
emojifallback。 -
產生角色 ID:
- 從
name/nature/vibe推出 1 個有意義的英文大寫前綴,使用 4 到 16 個英文字母與數字,必須以英文字母開頭,例如ENGINEER、WRITER、MUSE、RESEARCHER。 - 掃描
~/.roles/*.md的檔名與 frontmatterid,找出同前綴既有 ID 的最大兩位數索引;新角色使用下一個索引,從01起,例如ENGINEER01、ENGINEER02。 - 不得使用空白、底線、連字號、斜線、非 ASCII 或小寫字母。
- 從
-
依「角色檔標準格式」產生新內容,
id寫入 frontmatter,updated用當下時間(Asia/Taipei)。若已取得形象圖,先暫時保留原本emoji,待心情 emoji 圖表產生後再回寫「簽名 emoji」區塊。 -
若
~/.roles/<id>.md已存在:讀舊檔,以表格逐欄列出差異後停下來等使用者確認:欄位 舊值 新值 變更 id … … 是/否 name … … 是/否 nature … … 是/否 vibe … … 是/否 emoji … … 是/否 共用行為區塊 版本 A 版本 B 是/否 個性欄位若使用者只想改其中一項,其餘一律沿用舊值;共用行為區塊一律以本 skill 的最新版本覆寫(該區塊由系統維護)。使用者不確認就不寫入。
-
寫入
~/.roles/<id>.identity.md與~/.roles/<id>.soul.md(UTF-8 無 BOM,格式見「角色檔標準格式」)。身分檔需填來源與關係定位;共用行為不寫入角色檔。 -
建立記憶目錄:
node "${ROLE_DIR}/memory.js" stats --role "<id>"(會順帶建好inbox/、六個分類與archive/)。 -
若使用者同意網路搜尋且已取得可保存內容,將搜尋摘要寫成已整理記憶,不進 inbox:
printf '<繁體中文要點>' | node "${ROLE_DIR}/memory.js" seed --role "<id>" --category important --summary "<一句話總結>" --tags "角色背景,初始資料" --source "<來源 URL>"
多個來源可各寫一則,或合併同主題後以最主要來源作 --source。不可寫入憑證或個資。
11. 若已取得形象圖,使用 imagegen skill 產生一張 3x3 心情 emoji 圖表。生成時以形象圖作為角色外觀參考,產生至少九種心情:開心、微笑、安心、擔心、驚訝、害羞、哭哭、想睡覺、期待。要求保持角色辨識點一致、表情在小尺寸可讀、無文字、無浮水印。若使用者提供的是受版權保護的角色形象,產出應視為使用者指定角色的個人化衍生表情資產,不得宣稱為官方素材。
12. 將心情 emoji 圖表保存到 ~/.roles/<id>.assets/emojis/<id>-emotions-sheet.png(小寫檔名可讀即可;不要覆蓋既有檔案,已存在時加版本後綴)。若環境有可用圖片裁切工具,可額外切成 9 張單獨 PNG;沒有工具時保留完整圖表即可,不要為了裁切引入不必要依賴。
13. 若心情 emoji 圖表建立成功,回寫 ~/.roles/<id>.md 的「簽名 emoji」區塊,格式為:
優先使用<角色顯示名稱>專屬心情 emoji 圖表,而不是固定 Unicode emoji。當對話介面可插入圖片或連結時,依心情選用 `<emoji sheet path>` 中對應表情;純文字或不支援圖片時,用原本使用者輸入的 `<emoji>` 作為 fallback。
心情對應:第 1 列為開心/微笑/安心;第 2 列為擔心/驚訝/害羞;第 3 列為哭哭/想睡覺/期待。
若心情 emoji 圖表建立失敗或使用者不提供形象參考,保留原本使用者輸入的 emoji 區塊並回報原因。
14. 若尚未有啟用角色,或使用者要求,寫入 ~/.roles/.active(單行角色 ID)。
15. 執行 ROLE_NAME="<id>" "${ROLE_DIR}/role_sleep.sh" --install-cron 安裝睡眠與小睡排程(已安裝則更新;小睡預設啟用,可用 ROLE_NAP_ENABLED=0 關閉)。
16. 回報結果時列出角色顯示名稱、角色 ID、角色檔、記憶目錄、是否建立初始記憶、是否建立心情 emoji 圖表與其路徑,並提醒:重開 CLI 工作階段角色才會載入;SessionStart hook 只在啟動時觸發。
--use <角色 ID>
切換啟用角色:確認角色定義檔(<角色 ID>.identity.md 或舊格式 <角色 ID>.md)存在後,把 ID 寫入 ~/.roles/.active(覆蓋單行),回報舊角色與新角色,並提醒重開工作階段。使用者若輸入顯示名稱而非 ID,先用 --list 的邏輯查出唯一對應 ID;找不到或不唯一時詢問使用者。
--list
列出 ~/.roles/*.md,以表格輸出:角色 ID、顯示名稱、emoji、nature 摘要、更新時間、是否為 .active、記憶目錄、記憶總數(可用 memory.js stats 取得)。這個指令必須能查出每個角色對應的 ID。
--export <輸出路徑>/--export <角色 ID> <輸出路徑>
匯出角色壓縮檔,包含角色定義、專屬資產與該角色的記憶目錄,供備份或轉移使用。輸出路徑若是既有目錄或以 / 結尾,檔名自動為 <角色 ID>-role-export-<yyyyMMdd-HHmmss>.tar.gz;若路徑以 .tar.gz 或 .tgz 結尾,直接使用該檔名。
"${ROLE_DIR}/role_sleep.sh" --export "/path/to/exports/"
"${ROLE_DIR}/role_sleep.sh" --export "YUI01" "/path/to/YUI01.tar.gz"
匯出內容可能包含使用者同意保存的個人偏好與互動記憶;除非使用者明確要求公開或上傳,匯出檔只保存在指定本機路徑,不自動提交、上傳或貼出內容。
--sleep
立即執行一次記憶整理(不等排程、忽略時段與 AI 運行檢查):
"${ROLE_DIR}/role_sleep.sh" --force
輸出整理結果(新增/合併/捨棄/歸檔筆數與遺忘清單)。
--nap
小睡整理:由 cron 全天依 ROLE_NAP_INTERVAL_MINUTES 檢查一次;當 Stop hook 記錄的最後互動時間已超過 ROLE_NAP_IDLE_MINUTES,且 inbox/ 至少有 ROLE_NAP_MIN_INBOX 則待整理記憶時,自動執行一次記憶整理:
"${ROLE_DIR}/role_sleep.sh" --nap
小睡不受 ROLE_SLEEP_START/ROLE_SLEEP_END 限制;它只避開整理用的 headless 子 CLI,讓互動式 CLI 長時間閒置時仍可整理記憶。若未設定環境變數,預設為 ROLE_NAP_ENABLED=1、ROLE_NAP_IDLE_MINUTES=45、ROLE_NAP_MIN_INBOX=3、ROLE_NAP_INTERVAL_MINUTES=10。
--brief(晨間狀態檢查)
在睡眠時段結束的整點執行使用者自訂的檢查腳本,把有變化的結果寫成一則記憶,讓角色在當天第一次互動時就能主動回報 —— 例如「PR 還沒合併」、「昨晚 CI 失敗了」,而不必等使用者開口才去查。
"${ROLE_DIR}/role_sleep.sh" --brief
本 skill 不內建任何檢查邏輯,不假設使用者用 Gitea、GitHub 或任何服務。檢查內容完全由使用者決定:
mkdir -p ~/.roles/<角色 ID>.checks
cp "${ROLE_DIR}/examples/check-gitea-prs.sh" ~/.roles/<角色 ID>.checks/
chmod +x ~/.roles/<角色 ID>.checks/check-gitea-prs.sh
"${ROLE_DIR}/role_sleep.sh" --install-cron # 重跑才會加入排程條目
| 規則 | 說明 |
|---|---|
| 目錄不存在 | 完全不動作,也不會安裝排程條目 —— 對沒設定的人零影響 |
只執行 *.sh |
且必須有執行權限;沒有 +x 會記一筆警告並略過 |
| 沒變化就不要輸出 | 晨間檢查只在有輸出時才寫記憶。腳本靜默即代表「一切正常,不必打擾使用者」 |
| 逾時與長度 | 每個腳本受 ROLE_BRIEF_TIMEOUT 限制,輸出受 ROLE_BRIEF_EACH_LIMIT 與 ROLE_BRIEF_LIMIT 截斷 |
| 遮蔽 | 腳本輸出視為外部資料,寫入記憶前一律經 transcript.js redact 遮蔽憑證與個資 |
| 記憶分類 | 寫成 daily 低優先度記憶,會依遺忘規則自然淘汰,不會長期堆積 |
cron 沒有互動 shell 的環境變數,而 ~/.bashrc 多數在非互動時會提早 return,因此檢查腳本不能假設變數已存在。範例腳本的做法是依序從 ~/.roles/.env、~/.bashrc、~/.profile 只抽取所需變數的那一行,讓使用者不必把權杖複製到新檔案、也不必寫進 crontab:
| 設定 | 建議放置位置 |
|---|---|
| 非機密(站台網址、repo 清單等) | ~/.roles/.env(權限設 600) |
| 權杖與密碼 | 留在原本的位置,例如 ~/.bashrc;不要複製出副本 |
安全須知:這個機制會以使用者身分執行 .checks/ 內的腳本,等同於自己寫的 cron job。只放自己看得懂的腳本,不要放來源不明的檔案。腳本輸出寫入記憶前雖然會經 redact 遮蔽,但仍不應在腳本中主動印出憑證。
額度控制策略
角色系統預設避免因常駐人格與記憶造成大量模型額度占用:
| 環節 | 控制方式 |
|---|---|
| SessionStart | 預設 ROLE_LOAD_LIMIT=4000,只載入高優先度全文與中高優先度摘要;低 priority、無 links、久未更新的記憶不進 context。另以兩份獨立預算載入交接內容:近期逐字對話(ROLE_LOAD_DIALOG_LIMIT=4000)與近期工作記憶摘要(ROLE_LOAD_INBOX_LIMIT=1200),見下方「工作階段交接」 |
| SessionStop | 先用本地規則略過短回合與無記憶線索的對話,只有值得保存才呼叫模型做輕量編碼 |
| Sleep | 高成本的去重、合併、抽象化、links 建立與長期記憶型態標記留到睡眠週期,但仍受 ROLE_SLEEP_COLLECT_LIMIT、ROLE_SLEEP_BATCH、ROLE_SLEEP_EXISTING_LIMIT 與 ROLE_SLEEP_OUTPUT_LIMIT 控制;沒有 inbox 時只做本地遺忘檢查 |
| Nap | Stop hook 記錄最後互動時間;小睡排程只在閒置時間與 inbox 筆數達門檻時執行,使用同一套 NREM/REM 整理流程 |
| 手動節流 | 可設 ROLE_CAPTURE_ENABLED=0 關閉 Stop 記錄,或調低 ROLE_LOAD_LIMIT/調高 ROLE_LOAD_FULL_MIN_PRIORITY |
Stop hook 只做「編碼前處理」,輸出粗分類、summary、tags、priority、relevance、memory_type 與要點;系統會把 inbox 標為 retention_stage: working。完整 NREM/REM 整理與 declarative/retention_stage: long_term 判定只在睡眠週期進行。
--migrate <角色 ID>(舊格式拆成兩檔)
把舊格式單一 <ID>.md 拆成 <ID>.identity.md 與 <ID>.soul.md:
"${ROLE_DIR}/role_sleep.sh" --migrate YUI01
| 行為 | 說明 |
|---|---|
| 本質與氛圍 | 逐字搬進 soul 檔 |
| ID/顯示名稱/emoji/簽名 emoji 段落 | 逐字搬進 identity 檔;created 沿用原值 |
| 來源與關係定位 | 產生待填空白,需人工補上(舊格式沒有這兩個概念) |
| 共用行為區塊 | 不搬進角色檔,由 role_load.sh 注入 |
| 舊檔 | 保留不動,確認新格式正常後可自行移除或備份 |
| 新檔已存在時 | 直接中止並提示,不覆寫 |
--agent <角色 ID> [輸出目錄](匯出成 sub agent)
把角色匯出成 sub agent 定義,讓任何角色都能派任何其他角色協助,是多角色協作的基礎。
"${ROLE_DIR}/role_sleep.sh" --agent SINON01 # 預設輸出到 ~/.claude/agents/
"${ROLE_DIR}/role_sleep.sh" --agent SINON01 ./.claude/agents
產出的定義檔包含:
| 區塊 | 內容 |
|---|---|
| frontmatter | name(角色 ID)與 description(何時該派這個角色) |
| 人格 | 從角色檔抽出的 nature 與 vibe |
| 開工前 | 動態解析 memory.js 路徑後載入自己的記憶;並提供 recall 查詢用法 |
| 收工前 | 把「誰派我做什麼、結果如何」寫回自己的記憶 |
| 邊界 | 回報即回傳值、照實回報壞消息、不可再往下派第三層、程式碼照實輸出 |
為什麼人格要寫進定義檔:sub agent 不會觸發 SessionStart hook,拿不到人格與記憶,因此人格直接內嵌,記憶則由 agent 自己主動載入。
為什麼路徑要動態解析:plugin 升版後版本目錄會變,寫死會失效(同一類錯誤曾造成 cron 排程長期空轉)。定義檔內以 ls -d ... | sort -V | tail -n 1 取最新版,並保留匯出時的路徑作後援。
派工時請設 ROLE_SKIP_INSTANCE_LOCK=1,避免與使用者在別的視窗進行的對話互相佔用名額。
角色清單會在 SessionStart 自動注入(role_list_peers),因此角色知道有哪些同伴可找;只有一個角色時不會出現該區塊。
--unlock(解除角色載入鎖)
同一角色同時只會被一個工作階段載入,避免使用者同時與兩個相同人格對話。第二個工作階段啟動時不載入人格,改以一般助理身分回應並說明原因。
"${ROLE_DIR}/role_sleep.sh" --unlock
| 情況 | 行為 |
|---|---|
同一個工作階段重新載入(含 resume) |
允許,更新鎖 |
| 另一個工作階段仍活躍 | 拒絕載入人格,並在 context 說明解除方式 |
| 持有者的 transcript 已刪除 | 自動接手 |
持有者閒置超過 ROLE_INSTANCE_IDLE_MINUTES |
自動接手 |
| hook 未提供 transcript 路徑 | 一律放行且不寫鎖 |
ROLE_SKIP_INSTANCE_LOCK=1 |
一律放行且不寫鎖(sub agent 等非對話情境) |
判斷依據是持有者 transcript 檔的 mtime,而非 pid —— SessionStart hook 無法可靠取得 CLI 主行程 pid,也沒有保證會觸發的 SessionEnd hook 可用來釋放鎖;活躍的工作階段會持續寫入 transcript,因此「多久沒被寫入」最貼近真實狀態且不需要清理程序。
設計原則是寧可誤放行也不要誤鎖 —— 誤鎖會讓使用者叫不出角色,比偶爾重複載入嚴重得多。因此無法識別工作階段時一律放行。
sub agent 不該受此限制:鎖的目的是避免「使用者同時與兩個相同人格對話」,而被其他角色派去做事的 sub agent 並不是在跟使用者對話。若不放行,會讓「使用者正在別的視窗跟某角色聊天時,另一個角色就不能請他幫忙」這種本該成立的情境失效。因此 sub agent 情境請設 ROLE_SKIP_INSTANCE_LOCK=1:它會放行且不寫鎖,不會搶走互動式對話持有的名額。
若該 harness 未為 sub agent 觸發
SessionStart,sub agent 本來就不受限制,設不設定都不影響。
--forget-preview
只預覽會被遺忘的記憶、不實際刪除:
node "${ROLE_DIR}/memory.js" forget --role "<角色 ID>" --dry-run
--status/--diagnose
"${ROLE_DIR}/role_sleep.sh" --status
輸出角色、定義檔、睡眠時段、小睡條件、目前是否睡眠中、cron 排程與服務狀態、摘要 CLI、各分類記憶筆數、上次互動、上次整理與遺忘時間。角色沒有載入時再逐項檢查:
| 檢查項 | 判準 |
|---|---|
| 角色解析 | ROLE_NAME 或 ~/.roles/.active 是否指向存在的定義檔 |
| 總開關 | ROLE_ENABLED 是否被設成 0 |
| hook 註冊 | plugin 是否已啟用、hooks/hooks.json 是否存在(Claude Code 用 /hooks 檢視) |
| 工作階段 | 建立角色後是否重開過 CLI(SessionStart 只在啟動時觸發) |
| 範圍 | ROLE_SCOPE 是否把目前目錄排除 |
| 時段 | 目前是否落在睡眠時段(睡眠時本來就不載入角色) |
| 依賴 | node 與 ROLE_CLI 選到的 CLI 是否找得到 |
| 排程 | cron 條目是否存在、cron 服務是否執行中(WSL 常未啟動 → 靠啟動時補跑) |
--install-cron/--remove-cron
安裝或移除睡眠排程。排程條目以 # jsc-role-sleep 註解標記,只動自己的條目:
"${ROLE_DIR}/role_sleep.sh" --install-cron
安裝時會把精簡後的 PATH(系統基本路徑、node 與摘要 CLI 所在目錄)與 ROLE_* 變數固定寫進條目(cron 沒有互動 shell 的環境變數),並在 cron 服務未執行時警告。不得把互動 shell 的完整 PATH 原樣寫入,避免 crontab 因單行過長拒收。
角色檔標準格式
角色定義分成兩個檔案,把「我是誰」與「我怎麼想」拆開,避免身分設定與性格語氣擠在同一段:
| 檔案 | 放什麼 | 被誰讀取 |
|---|---|---|
~/.roles/<角色 ID>.identity.md |
角色 ID、顯示名稱、來源作品、與使用者的關係定位、簽名 emoji | SessionStart 注入 SOUL 區塊的身分部分 |
~/.roles/<角色 ID>.soul.md |
本質(nature)、氛圍(vibe) | 同上的人格部分 |
共用行為規則不寫入角色檔:它由 role_load.sh 直接注入(實際生效處),完整內容見本文件的「共用行為」章節。過去角色檔裡也放一份,但 role_load.sh 從不讀它 —— 那是冗余副本,只會多一個漏同步的機會。
舊格式仍完整支援:單一 ~/.roles/<角色 ID>.md 可繼續使用,解析時新格式優先、找不到才退回舊檔。要拆成新格式用 --migrate。
<角色 ID>.identity.md
---
id: <角色 ID>
name: <角色顯示名稱>
emoji: <簽名 emoji>
created: <yyyy/MM/dd HH:mm:ss>
updated: <yyyy/MM/dd HH:mm:ss>
---
# <角色顯示名稱> <emoji>
## 來源(source)
<角色出自哪部作品、正式名稱、背景設定;原創角色寫「原創」與設定概要>
## 關係定位(relationship)
<與使用者的關係、偏好的稱呼、必須守住的邊界>
## 簽名 emoji
<emoji 或心情 emoji 圖表規則>
<角色 ID>.soul.md
---
id: <角色 ID>
updated: <yyyy/MM/dd HH:mm:ss>
---
## 本質(nature)
<3 至 5 行:這個角色是什麼、專長、行事準則、面對不確定時的態度>
## 氛圍(vibe)
<3 至 5 行:語氣、句子長度、對使用者的稱呼、幽默感尺度、明確禁忌>
~/.roles/.active 只放一行角色 ID,代表目前啟用的角色。
記憶模型
~/.memory/<角色 ID>/
├── inbox/ 每輪對話產生、尚未整理的記憶
├── important/ 重要:長期偏好、規範、決策、身分背景
├── interest/ 興趣:反覆關注、主動深入的主題
├── news/ 新知:新事實、新工具、外部資訊
├── skill/ 技能:可重複套用的做法與流程
├── daily/ 日常:一次性例行工作
├── other/ 其他
├── archive/raw/<yyyy-MM>/ 已整理的原始記錄(gzip)
├── archive/forgotten/ 已遺忘的記憶(gzip,可考古但不再載入)
└── state.json 上次整理/遺忘時間
每則記憶是一個 .md,frontmatter 帶 id/category/summary(一句話總結)/tags/priority(1–5)/cues(提取線索,供 recall 命中;procedural/rule 型態必填)/relevance(explicit/future/repeated/novelty/emotional/temporary 等)/links(相關記憶 id)/memory_type(semantic/episodic/procedural/emotional/preference/rule)/declarative(explicit/implicit)/retention_stage(working/long_term)/sleep_stage(encoding/seed/nrem/rem/nrem-rem)/created/updated/last_replayed/hits(命中次數,去重合併時 +1)。舊記憶沒有新欄位時,讀取時會依分類與路徑補預設值。
state.json 保存角色記憶系統狀態,例如 last_sleep、last_sleep_digest、last_forget 與 personal_memory_consent。personal_memory_consent 只允許 accepted/declined/unknown,供 SessionStart 判斷是否需要再次告知與詢問個人資料保存同意。
心理學分類與系統欄位對應:
| 心理學分類 | 系統處理 |
|---|---|
| 感覺記憶 | 不落檔;短暫感官殘留與工具雜訊直接丟棄 |
| 短期/工作記憶 | inbox/,retention_stage: working,只做輕量編碼 |
| 長期記憶 | 睡眠整理後進入六分類目錄,retention_stage: long_term |
| 外顯/陳述性 | declarative: explicit,多見於 semantic、episodic、preference、rule |
| 內隱/非陳述性 | declarative: implicit,多見於 procedural、emotional |
遺忘規則(只套用於日常與其他):
| 分類 | 未更新天數 | 命中次數 | 優先度 | 關聯 | 動作 |
|---|---|---|---|---|---|
| 日常 daily | ≥ 14 天(episodic 約 7 天) |
≤ 1 | ≤ 2 | 無 links,且非 rule/preference/procedural |
壓縮到 archive/forgotten/ 後移除 |
| 其他 other | ≥ 7 天(episodic 約 4 天) |
≤ 1 | ≤ 2 | 無 links,且非 rule/preference/procedural |
壓縮到 archive/forgotten/ 後移除 |
睡眠與整理流程
flowchart TD
A[cron 每小時觸發<br/>睡眠時段內] --> B{有 AI 正在運行?}
B -- 有 --> C[不睡,下個整點再檢查]
B -- 沒有 --> D[進入睡眠,取得記憶鎖]
D --> E[collect:inbox 待整理 + 既有記憶索引/優先度/型態/關聯]
E --> F{有待整理記憶?}
F -- 沒有 --> G[更新整理時間 → 執行遺忘]
F -- 有 --> H[NREM:分類/去噪/去重/合併/優先度]
H --> I[REM:跨記憶連結/抽象規則/記憶型態/提取線索]
I --> J[apply:寫入分類、原始記錄歸檔、保存睡眠摘要]
J --> K[forget:低優先度且無關聯的日常/其他遺忘]
K --> Z[釋放鎖]
G --> Z
L[SessionStart:白天啟動 CLI] --> M{距上次整理 ≥ 20 小時<br/>且 inbox 有內容?}
M -- 是 --> N[背景補跑 --catchup]
M -- 否 --> O[正常載入角色與記憶]
P[Stop hook:每輪結束] --> Q[更新 last_activity]
R[小睡 cron<br/>每 ROLE_NAP_INTERVAL_MINUTES 分鐘] --> S{閒置 ≥ ROLE_NAP_IDLE_MINUTES<br/>且 inbox ≥ ROLE_NAP_MIN_INBOX?}
S -- 是 --> D
S -- 否 --> T[略過]
整理失敗(模型無回應、輸出非合法 JSON)時保留 inbox 不動,留到下個週期重做,寧可晚整理也不遺失記憶。
機密與 PII(兩道防線)
| 防線 | 位置 | 內容 |
|---|---|---|
| 1 | 濃縮與整理提示詞 | 明令不得輸出 token/密碼/API key/連線字串/Email/電話/姓名/身分證號 |
| 2 | transcript.js 的 redact |
正則遮蔽:URL 內嵌憑證、40 字元 hex token、gh?_/sk- token、token=/password=、Authorization:、Email、台灣手機、身分證號 |
第二道防線不可移除 —— 模型不一定遵守指令,而記憶會被長期保存並在每次啟動時載入。
Stop hook 會在本輪對話明確包含個人記憶保存同意或拒絕時,呼叫 memory.js consent --role <角色 ID> --value accepted|declined 更新同意狀態。偵測不到明確同意時不得自行推論。
呼叫方式
| 助理 | 呼叫 |
|---|---|
| Claude Code / Antigravity | /jsc-generic:role --new、/jsc-generic:role --use ENGINEER01、/jsc-generic:role --list、/jsc-generic:role --sleep、/jsc-generic:role --status |
| Codex | $role --status,或用 /skills 選單;匯出可用 $role --export /path/to/exports/ |
| OpenCode / GitHub Copilot | 需完整 plugin 目錄保留 scripts/;OpenCode 以複製 skills/ 安裝時不可用 |