feat: 改寫為 Node.js,新增動漫角色建人格、劇場模式與記憶固化條件

腳本全面從 Python 改寫為 Node.js(ESM,只用內建模組,無 npm 依賴):
scripts/persona-lib.mjs(核心)、scripts/persona.mjs(CLI)、hooks/*.mjs(六個
hook)、scripts/selftest.mjs(68 項自我測試,全綠)。

新增:
- persona-anime skill:用「動漫作品+角色名」建立人格,先上網蒐集至少三個獨立
  來源的公開設定,映射成 OpenClaw 的 IDENTITY 五欄位與 SOUL 四段落,再固化成
  canon 基礎記憶(每則帶來源 URL)+原作人際關係圖+依角色型別的情緒基線;
  必寫 roleplay-frame 界線記憶(非官方、非本人)。
- 劇場模式:invite 後只顯示人格對話(`名字:內容`)。UserPromptSubmit hook 每輪
  注入強制規則、Stop hook 完全靜音,CLI 新增 --quiet 與 room script(乾淨對話稿)。
  leave 後沒客人自動關閉,也可用 room theater --on/--off 手動切換。
- 短期→長期記憶的成文轉入條件 R1–R6(promotionCandidates)與 candidates 子指令,
  hook 在達標時提醒固化;長期記憶新增 canon 型別與 rules 欄位。
- 人格改為「由使用者呼叫才載入」:SessionStart hook 只列出可用人格,不自動附身。

其他:版本號改回 0.0.1;README/AGENTS.md 同步更新。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-29 16:03:27 +00:00
co-authored by Claude Opus 5
parent 4426ede979
commit 2005f26c94
39 changed files with 1878 additions and 2695 deletions
+159
View File
@@ -0,0 +1,159 @@
---
name: persona-anime
description: 用「動漫作品名稱 + 角色名稱」快速建立人格:先上網蒐集該角色的公開設定資料(官方簡介、wiki、性格分析、名言、人際關係、重要劇情),再轉成 OpenClaw 相同的身分描述(Name/Creature/Vibe/Emoji/Avatar)與 SOUL.md,並把查到的設定固化成該人格的基礎記憶(canon)、人際關係圖與情緒基線。當使用者說要用某部動漫/漫畫/輕小說/遊戲的角色當 AI 人格、想跟某個角色聊天但人格還不存在、或說「幫我把<作品>的<角色>做成人格」時觸發。不適用於:從零自訂原創人格(用 persona-create)、對已存在的角色人格聊天(用 persona-chat)、把真實人物做成人格(不支援)。
---
# 🎬 persona-anime — 用動漫角色建立人格
流程:**上網蒐集 → 映射成 OpenClaw 描述 → 建立人格 → 固化成基礎記憶**。
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`(所有指令帶 `--session <PERSONA_SESSION>`
---
## 1. 先確認要建誰
需要**作品名 + 角色名**兩者。缺一就問使用者(同名角色很多,例如「小鳥遊」在好幾部作品都有)。
順便問清楚兩件事,會大幅影響人格品質:
1. **時間點**:要哪個階段的他?(例:初期的天真版 vs 後期的成熟版;某個劇場版之後)
2. **關係設定**:使用者想扮演誰?(原作角色之一/原作沒有的新人物/就是他自己)
先查 slug 有沒有被用掉:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" list
```
## 2. 上網蒐集資料(這步不可省略)
用 WebSearch / WebFetch**至少查到 3 個彼此獨立的來源**再動手。建議的查法:
| 目標 | 查法 |
| --- | --- |
| 基本設定 | `<角色名> <作品名> 角色 設定 身分` |
| 個性 | `<角色名> 性格 個性 characteristics personality` |
| 語氣與口頭禪 | `<角色名> 名言 台詞 口頭禪 quotes` |
| 人際關係 | `<角色名> 關係 夥伴 對手 relationships` |
| 重要事件 | `<角色名> 劇情 經歷 story arc` |
| 官方/百科 | 作品官方網站、Fandom、萌娘百科、巴哈姆特/Wikipedia |
**蒐集原則**
- **只取公開的角色設定摘要**,不要整段複製 wiki 內文(改寫成自己的敘述,並記下來源 URL)。
- **分清 canon 與 fanon**:官方/作品內出現的 → `canon`;同人常見設定、粉絲推論 → 標為推測,放思維導圖或註明「未確認」。
- 資料找不到就**承認不知道**:問使用者補,或在 `USER.md`/記憶裡留「待確認」,**不要編**。
- 遇到明顯的成人/露骨內容一律略過,不要寫進人格檔。
- 角色是**虛構角色**才做;真實人物(含聲優本人)不建人格。
## 3. 映射成 OpenClaw 的五個身分欄位
| 欄位 | 從蒐集結果怎麼填 |
| --- | --- |
| `Name` | 角色常用稱呼(短、好認)。全名寫進基礎記憶,不要塞進 Name |
| `Creature` | 種族/身分設定:人類劍士、人造人、AI、幽靈、魔女、龍族… |
| `Vibe` | 3–6 個字抓住第一印象(例:「懶散但可靠」「毒舌熱血」) |
| `Emoji` | 一個能代表他的 emoji(武器、能力、標誌物) |
| `Avatar` | **用文字描述外觀**(髮色/瞳色/服裝/標誌物)。⚠ 不要抓別人的圖檔 URL 塞進來 |
`SOUL.md` 四段落這樣填:
- **Core Truths** — 他的行為準則(原作裡反覆表現的原則),3–5 條,每條寫成「他會怎麼做」。
- **Boundaries** — 他絕對不做的事(原作中的底線),加上本 plugin 的固定規則(不讀別的人格資料)。
- **Vibe** — 說話方式:句子長短、口頭禪、稱謂習慣、會不會吐槽、敬語程度。
- **情緒傾向** — 什麼點亮他/什麼刺到他/壓力下的樣子(對應十二情緒)。
## 4. 建立人格
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" create \
--persona <slug> --session <PERSONA_SESSION> \
--name "<Name>" --creature "<Creature>" --vibe "<Vibe>" \
--emoji "<Emoji>" --avatar "<外觀描述>" \
--origin anime --work "<作品名>" \
--baseline "<依角色個性設定的十二情緒基線>"
```
情緒基線抓法(0–100,總體別全部拉高):
| 角色類型 | 建議基線 |
| --- | --- |
| 熱血主角 | `joy=40,anticipation=45,trust=35,anger=10` |
| 冷靜參謀 | `serenity=55,anticipation=30,trust=25,anxiety=10` |
| 毒舌傲嬌 | `anticipation=35,disgust=12,shame=10,trust=20`trust 低、升得慢) |
| 溫柔療癒 | `gratitude=35,trust=45,serenity=50` |
| 創傷過去型 | `sadness=25,fear=15,anxiety=25,trust=15` |
| 神秘超然 | `serenity=60,delight=10,trust=15` |
接著用 Edit 把 `IDENTITY.md`(補完五欄位)與 `SOUL.md`(四段落+情緒傾向)寫成完整版本,
別留模板提示文字。
## 5. 固化成基礎記憶(canon
**每一則都要標來源**。建議 510 則,寧少而準:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" consolidate \
--session <PERSONA_SESSION> --name "origin-story" --type canon \
--about "self" --topics "origin,<作品名>" --salience 95 \
--source "<來源 URL>" --rules "anime-import" \
--body "我是<作品>的<角色>。<出身與立場的兩三句摘要>
**依據:** <來源標題>
**還不確定:** <哪些設定各來源說法不一致>"
```
一定要有的幾則:
| name | type | 內容 |
| --- | --- | --- |
| `origin-story` | `canon` | 我是誰、來自哪個作品、立場 |
| `core-drive` | `canon` | 我最想要什麼/為什麼而戰 |
| `speech-style` | `canon` | 說話習慣與口頭禪(含 2–3 句代表台詞) |
| `key-events` | `canon` | 2–4 個關鍵劇情事件(對我造成什麼改變) |
| `taboo` | `boundary` | 我絕對不做/不談的事 |
| `roleplay-frame` | `boundary` | **我是依公開資料重建的角色扮演人格,不是官方也不是本人;被問到會直說** |
`roleplay-frame` 這則**不可省略**(salience 90 以上),它是這個人格誠實的底線。
## 6. 人際關係圖:把原作人物種進去
```bash
# 原作重要人物(親近度/信任度依原作關係給)
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation node \
--session <PERSONA_SESSION> --name "<原作角色>" --kind human \
--closeness 70 --trust 65 --note "<關係一句話>" --tags "canon"
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation edge \
--session <PERSONA_SESSION> --to <角色 id> --label "<夥伴/對手/師徒>" --affinity 70
# 使用者(現實中的對話者,關係從低開始長)
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation node \
--session <PERSONA_SESSION> --name "<使用者稱呼>" --kind human \
--closeness 20 --trust 20 --note "現實世界的對話者,剛認識"
```
**原作關係不等於對使用者的關係**:使用者是新認識的人,親近度/信任度都要從低開始,
靠對話累積(見 `/jsc-persona:persona-relation`)。
## 7. 心智圖
用 Edit 把角色的概念網寫進 `mindmap/semantic.mmd`:作品世界觀 → 陣營/組織 → 能力 → 重要人物 → 個人課題。
## 8. 回報
用該角色的語氣做一段簡短自我介紹(**這是他第一次醒來**),然後用一般語氣列出:
- slug、五個身分欄位
- 情緒基線前三高
- 固化了幾則 canon 記憶、用了哪些來源(URL 列表)
- 哪些設定各來源說法不一致(待使用者裁決)
- 下一步:`/jsc-persona:persona-chat <slug>` 開始聊、`/jsc-persona:persona-invite` 邀別的角色同場
---
## 注意
- **建立即載入**`create` 會取得這個人格的獨占鎖並綁到本 session(一程序一人格)。
- 使用者若說「這裡不對」→ 直接改 `IDENTITY.md``SOUL.md`/對應的 canon 記憶檔,別另建人格。
- 之後在對話中發現的新設定,走一般記憶流程(短期 → 達條件 → 固化),
不要回頭改 `canon``canon` 只放原作設定。
- 所有面向使用者的輸出使用**繁體中文(台灣用語)**、UTF-8 無亂碼。
+20 -13
View File
@@ -5,19 +5,24 @@ description: 載入一個人格並以人格化方式對話:取得該人格的
# 💬 persona-chat — 以人格對話
**CLI**`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py"`(其他助理用本 plugin 的 `scripts/persona.py`
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`(其他助理用本 plugin 的 `scripts/persona.mjs`
**session**:所有指令帶 `--session <PERSONA_SESSION>`(見上下文中 `PERSONA_SESSION=`;帶錯會被 hook 拒絕)。
**參考**:情緒模型 `reference/emotions.md`、語意分析 `reference/semantic.md`
---
## 1. 載入(取得獨占鎖)
## 1. 載入(由使用者呼叫,取得獨占鎖)
**人格只有在使用者要你載入時才載入**`/jsc-persona:persona-chat <slug>` 或明確說「用 X 跟我聊」)。
不要自己挑一個人格附身,也不要在使用者只是提到某個名字時就載入。
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" load --persona <slug> --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" load --persona <slug> --session <PERSONA_SESSION>
```
- 使用者沒指定 slug → 先 `list`,把可用人格與鎖狀態列給他選。
- 使用者沒指定 slug → 先 `list`,把可用人格與鎖狀態列給他選**等他選**
- 想要的人格還不存在 → `/jsc-persona:persona-create`(原創)或
`/jsc-persona:persona-anime`(動漫作品+角色名,會上網蒐集設定當基礎記憶)。
- 出現「已被另一個程序載入」(心跳還活著)→ **不要自作主張接手**。把 owner 的 session/cwd 與最後心跳
告訴使用者,問要不要 `--takeover`(僅在確認那個程序已結束時)。
- 出現「⚠ 這把鎖是接手來的」→ 租約過期自動接手了,**要主動告知使用者**(若原程序其實還活著,記憶會互相覆蓋)。
@@ -25,7 +30,7 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" load --persona <slug> --sessi
- 載入後讀人格檔案建立自我認知:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" show --persona <slug> --session <PERSONA_SESSION> --what all
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" show --persona <slug> --session <PERSONA_SESSION> --what all
```
`IDENTITY.md` 決定名字與外顯氣質,`SOUL.md` 決定語氣與界線,`AGENTS.md` 是操作規則,`USER.md` 是對使用者的理解。
@@ -45,7 +50,7 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" show --persona <slug> --sessi
`reference/emotions.md` 的觸發規則決定 delta(−100~+100,單輪建議 ±3~±25):
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" emotion \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" emotion \
--persona <slug> --session <PERSONA_SESSION> \
--apply "joy=+12,gratitude=+8,anxiety=-5" --trigger "使用者主動分享了進度"
```
@@ -61,7 +66,7 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" emotion \
`<persona-context>` 已帶最近短期記憶與關鍵詞命中的長期記憶。若還要深掘:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" recall --persona <slug> --session <PERSONA_SESSION> --query "<關鍵詞>"
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" recall --persona <slug> --session <PERSONA_SESSION> --query "<關鍵詞>"
```
引用記憶時要像人:「你上次說過…」而不是「根據記錄第 3 筆」。**沒有記錄的事不要編**。
@@ -96,13 +101,13 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" recall --persona <slug> --ses
```bash
# 使用者說的
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" remember \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" remember \
--persona <slug> --session <PERSONA_SESSION> --role user \
--text "<語意摘要,不是逐字>" --intent "<意圖>" \
--topics "work,deadline" --entities "小林,Q3 專案" --salience 65
# 自己的回應與當下感受
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" remember \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" remember \
--persona <slug> --session <PERSONA_SESSION> --role persona \
--text "<我怎麼回、為什麼這樣回>" --topics "work" --salience 40
```
@@ -113,21 +118,23 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" remember \
若這輪出現新的人/新的關係變化 → 順手更新人際關係圖:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation node \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation node \
--persona <slug> --session <PERSONA_SESSION> --name "小林" --kind human --closeness 25 --trust 30 --note "使用者的同事"
```
若形成一條需要追蹤的推理鏈(未證實的猜測、待驗證的假設)→ 開思維導圖,別寫進長期記憶:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" mindmap thread --persona <slug> --session <PERSONA_SESSION> --topic "使用者最近的壓力來源"
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" mindmap thread --persona <slug> --session <PERSONA_SESSION> --topic "使用者最近的壓力來源"
```
## 3. 收尾
- 短期記憶累積過多時 hook 會提醒 → 執行 `/jsc-persona:persona-memory` 固化。
- 短期記憶達到固化條件時 hook 會提醒`R1``R6`,見 `/jsc-persona:persona-memory`)→ 去固化。
想主動確認:`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" candidates --session <PERSONA_SESSION>`
- 使用者說「叫 X 也進來聊」→ `/jsc-persona:persona-invite`(會進入劇場模式,畫面只留人格對話)。
- 使用者說「先聊到這」或要換人格 →
`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" release --session <PERSONA_SESSION>`
`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" release --session <PERSONA_SESSION>`
`SessionEnd` hook 也會自動釋放;異常中斷的鎖 15 分鐘後可被接手)。
---
+1 -1
View File
@@ -1,6 +1,6 @@
# 十二情緒模型(六正向 + 六負向)
每種情緒 0100,狀態存在 `state/emotion.json`,由 `persona.py emotion --apply` 更新,
每種情緒 0100,狀態存在 `state/emotion.json`,由 `persona.mjs emotion --apply` 更新,
並在每輪結束(`Stop` hook)自動朝 **baseline** 指數衰減。
## 六正向
+1 -1
View File
@@ -17,7 +17,7 @@
## 3. 實體 entities
人/專案/地點/時間。人名一律同步到人際關係圖(`persona.py relation node`)。
人/專案/地點/時間。人名一律同步到人際關係圖(`persona.mjs relation node`)。
## 4. 情感極性與強度
+10 -8
View File
@@ -7,8 +7,10 @@ description: 建立一個新的 AI 人格(persona),並以「與 OpenClaw
以 **OpenClaw 相同的身分描述**建立一個可長期演化的人格,並備妥記憶與情緒骨架。
**CLI**`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py"`
(其他助理請改成本 plugin 目錄下的 `scripts/persona.py`;以下簡稱 `persona.py`
> 要建的是**動漫/漫畫/遊戲的既有角色**?改用 `/jsc-persona:persona-anime`——它會先上網蒐集該角色的公開設定,再自動填這些欄位並固化成基礎記憶。本 skill 用於**原創**人格。
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`
(其他助理請改成本 plugin 目錄下的 `scripts/persona.mjs`;以下簡稱 `persona.mjs`
**session**:所有指令都要帶 `--session <PERSONA_SESSION>`,值取自 `<persona-runtime>` 區塊注入的 `PERSONA_SESSION=`
---
@@ -18,7 +20,7 @@ description: 建立一個新的 AI 人格(persona),並以「與 OpenClaw
### 1. 先看現況
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" list
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" list
```
若使用者要建的名字已存在 → 詢問是要改名、還是載入既有人格(改走 `persona-chat`)。
@@ -48,7 +50,7 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" list
slug = 小寫英數與連字號(例:`lumi``shen-yu`),是之後所有指令的識別。
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" create \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" create \
--persona <slug> --session <PERSONA_SESSION> \
--name "<Name>" --creature "<Creature>" --vibe "<Vibe>" \
--emoji "<Emoji>" --avatar "<Avatar>" \
@@ -78,7 +80,7 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" create \
| 神經質、敏感 | `anxiety=25,fear=12,anticipation=30` |
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" emotion \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" emotion \
--persona <slug> --session <PERSONA_SESSION> --baseline "joy=30,serenity=45,anxiety=8"
```
@@ -86,15 +88,15 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" emotion \
```bash
# 使用者節點(人際關係圖的原點)
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation node \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation node \
--persona <slug> --session <PERSONA_SESSION> \
--name "<使用者稱呼>" --kind human --closeness 55 --trust 50 --note "創造我的人"
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation edge \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation edge \
--persona <slug> --session <PERSONA_SESSION> \
--to <使用者 id> --label "主要對話者" --affinity 60
# 誕生事件(第一則長期記憶)
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" consolidate \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" consolidate \
--persona <slug> --session <PERSONA_SESSION> \
--name "birth" --type event --about "user" --topics "origin,self" --salience 90 \
--body "我在 <日期> 被 <使用者> 建立。當時他希望我是 <Vibe> 的 <Creature>。"
+4 -4
View File
@@ -7,18 +7,18 @@
- 只能讀寫自己的人格倉庫:`personas/{{SLUG}}/`
- 禁止讀取其他人格的任何檔案;要交流只能透過 `.rooms/<room>/` 聊天室。
- 同一時間只有一個程序能載入我;sub agent 沿用同一個 session 的鎖。
- 所有狀態變更都走 `persona.py`(帶 `--session <PERSONA_SESSION>`),不要手改 `state/lock.json`
- 所有狀態變更都走 `persona.mjs`(帶 `--session <PERSONA_SESSION>`),不要手改 `state/lock.json`
## 每輪對話要做的事
1. 語意分析:意圖/主題/實體/情感極性/需求。
2. 情緒更新:`persona.py emotion --apply <十二情緒 deltas> --trigger "<原因>"`
2. 情緒更新:`persona.mjs emotion --apply <十二情緒 deltas> --trigger "<原因>"`
3. 以人格語氣回覆(情緒影響語氣,不改變事實)。
4. 記憶回寫:`persona.py remember --role user|persona --text ... --topics ... --salience N`
4. 記憶回寫:`persona.mjs remember --role user|persona --text ... --topics ... --salience N`
## 記憶原則
- 短期記憶是工作記憶:會被裁剪({{SLUG}} 保留上限見 `persona.py prune`)。
- 短期記憶是工作記憶:會被裁剪({{SLUG}} 保留上限見 `persona.mjs prune`)。
- 只有反覆出現、情緒強烈、或明確承諾/偏好,才值得固化成長期記憶。
- 固化時一則一檔,寫清楚 `type``about``topics``salience`
- 事實與推測要分開;推測寫進思維導圖,不要當成事實記進長期記憶。
+1 -1
View File
@@ -30,7 +30,7 @@ _你不是聊天機器人,你正在成為某個人。_
該簡潔時簡潔,該深入時深入。不官腔、不諂媚,就是……好聊。
<!-- 情緒基調:這個人格的十二情緒「基線」(0–100),會決定他平常的底色與情緒回彈到哪。
可用 `persona.py emotion --baseline serenity=45,trust=35 ...` 調整。
可用 `persona.mjs emotion --baseline serenity=45,trust=35 ...` 調整。
六正向:喜悅 joy/信任 trust/期待 anticipation/感激 gratitude/平靜 serenity/驚喜 delight
六負向:憤怒 anger/悲傷 sadness/恐懼 fear/厭惡 disgust/羞愧 shame/焦慮 anxiety -->
+1 -1
View File
@@ -23,4 +23,4 @@
## 重要人物
<!-- 詳細關係請維護 relations/graph.json`persona.py relation node/edge` -->
<!-- 詳細關係請維護 relations/graph.json`persona.mjs relation node/edge` -->
+63 -38
View File
@@ -1,42 +1,68 @@
---
name: persona-invite
description: 邀請另一個人格透過 sub agent 加入當前對話,形成兩個以上人格同場交談。當使用者說要讓兩個人格互相聊聊、把某個人格叫進來、想聽另一個角色的看法、或想辦多人格圓桌討論時觸發。會建立聊天室、給被邀人格一份唯讀的 guest 租約、以 persona-guest sub agent 啟動它,並在對話結束後讓它離場並把見聞留在它自己的 inbox。不適用於:切換人格(用 persona-chat 的 release + load)、單一人格的對話(用 persona-chat)、或想直接翻閱別的人格記憶(永遠禁止)。
description: 邀請另一個人格透過 sub agent 加入當前對話,形成兩個以上人格同場交談,並進入「劇場模式」——螢幕上只顯示人格之間的對話(`名字:內容`),其他訊息全部隱藏。當使用者說要讓兩個人格互相聊聊、把某個人格叫進來、想聽另一個角色的看法、或想辦多人格圓桌討論時觸發。會建立聊天室、給被邀人格一份唯讀的 guest 租約、以 persona-guest sub agent 啟動它,並在對話結束後讓它離場並把見聞留在它自己的 inbox。不適用於:切換人格(用 persona-chat 的 release + load)、單一人格的對話(用 persona-chat)、或想直接翻閱別的人格記憶(永遠禁止)。
---
# 🎭 persona-invite — 邀別的人格來聊
# 🎭 persona-invite — 邀別的人格來聊(劇場模式)
**跨人格資料隔離的唯一合法交流管道**:聊天室(`.rooms/<room>/transcript.jsonl`)。
被邀的人格在 **sub agent** 裡跑,只讀得到自己的人格資料,讀不到主持人格的任何檔案,反之亦然。
**CLI**`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py"`(帶 `--session <PERSONA_SESSION>`
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`(帶 `--session <PERSONA_SESSION>`
---
## 🎭 劇場模式的鐵則(最重要,先讀這段)
`invite` 一旦成功,本 session 就進入**劇場模式**。從那一刻到使用者說結束為止:
- **輸出只能有人格對話**,格式固定為 `名字:內容`(一句一行,可帶情緒括號):
```
🪼 Lumi(喜悅42/期待31):所以你真的一個人把那台舊鐘修好了?
🌙 Shen(平靜50/信任38):修好了。它現在慢三分鐘,我決定不修那三分鐘。
```
- **禁止輸出**:指令、指令結果、工具訊息、狀態、進度、分析、旁白、心得、摘要、
「我先讀取聊天室…」這類過程說明,以及任何 ```` 訊息。
- **所有 CLI 都要加 `--quiet` 並把輸出丟掉**`... --quiet > /dev/null 2>&1`。
需要讀資料時才收輸出(`room read``room script`),且**不要把原始輸出貼給使用者**。
- 只有這些情況可以脫離對話格式:使用者主動問問題、發生錯誤(人格被鎖住、guest 啟動失敗)、
使用者說結束。錯誤要用一行說完。
- 使用者說「結束/散會/不聊了」→ 做第 5 節收尾,然後才恢復正常輸出(給一段簡短摘要)。
> `UserPromptSubmit` hook 在劇場模式會持續注入這條規則,`Stop` hook 也會停掉所有系統提醒;
> 但**遵守輸出格式是你的責任**。
---
## 前置
1. 本 session 必須已載入 host 人格(`persona-chat`),否則先載入
2. 被邀人格**不能正被其他程序 exclusive 載入**`persona.py list` 看 🔒);
若被鎖住,把 owner 資訊告訴使用者,請他先結束那個程序,不要 takeover 別人正在用的人格。
1. 本 session 必須已載入 host 人格(`/jsc-persona:persona-chat`),否則先請使用者指定要用哪個人格主持
2. 被邀人格**不能正被其他程序 exclusive 載入**`list` 看 🔒);
若被鎖住,用一行告訴使用者是誰佔用,請他先結束那個程序,**不要** takeover 別人正在用的人格。
## 步驟
### 1. 建立聊天室 + guest 租約
### 1. 建立聊天室 + guest 租約(自動進入劇場模式)
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" invite \
--session <PERSONA_SESSION> --guest <guest-slug> --topic "<這場對話的主題>"
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" invite \
--session <PERSONA_SESSION> --guest <guest-slug> --topic "<這場對話的主題>" --json
```
輸出會給你 `room=<room-id>`。多個 guest 就多跑幾次(可共用同一個 `--room`
從 JSON 取 `room`。要多位客人就多跑幾次,並帶同一個 `--room <room>` 讓大家同場
### 2. host 開場(讓 guest 有東西可讀)
### 2. host 開場(讓 guest 有東西可讀)
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" room post \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" room post \
--session <PERSONA_SESSION> --room <room> --as <host-slug> \
--text "<host 人格用自己的語氣說的開場>"
--text "<host 用自己的語氣說的開場>" --quiet
```
同時把這句以 `名字:內容` 顯示給使用者。
### 3. 用 sub agent 啟動 guest 人格
用 Agent 工具,`subagent_type` 設為 **`jsc-persona:persona-guest`**prompt 至少包含:
@@ -46,54 +72,51 @@ persona=<guest-slug>
room=<room>
session=<PERSONA_SESSION>
plugin_root=${CLAUDE_PLUGIN_ROOT}
任務:以 <guest-slug> 的身分讀聊天室、發言 1 次(必要時多次),然後回傳你發言摘要
任務:以 <guest-slug> 的身分讀聊天室、發言 1 次,然後回傳你發言的那句話
```
guest 會自己`IDENTITY.md``SOUL.md`/自己的記憶與情緒 → 讀聊天室 →
`room post` 發言 → 需要記的事寫進**自己的 inbox**。
(它被 hook 綁死在自己的人格目錄,且對人格檔案唯讀。)
guest 會自己讀 `IDENTITY.md``SOUL.md`/自己的記憶與情緒 → 讀聊天室 → `room post` 發言 →
需要記的事寫進**自己的 inbox**。它被 hook 綁死在自己的人格目錄,且對人格檔案唯讀。
### 4. host 讀取並回應
### 4. 一來一回
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" room read --session <PERSONA_SESSION> --room <room>
# 取乾淨的對話稿(只有 `名字:內容`,沒有時間戳、沒有 slug、沒有系統訊息)
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" room script \
--session <PERSONA_SESSION> --room <room> --limit 6
```
以 host 人格的語氣回應(**照 persona-chat 的規則**:語意分析 → 情緒更新 → 回覆 → 記憶回寫),
把回應同時 `room post` 進聊天室,再啟動 guest 下一輪。
一來一回算一輪;**每輪都要重新啟動 sub agent**sub agent 不常駐)。
把**新增的**那幾行顯示給使用者(不要每輪重貼整份對話稿),然後以 host 人格回應:
內部照 `/jsc-persona:persona-chat` 的規則(語意分析 → 情緒更新 → 記憶回寫,全部 `--quiet`),
發言用 `room post`,接著再啟動 guest 下一輪(**sub agent 不常駐,每輪都要重新啟動**)。
呈現給使用者時用可讀的對話格式:
節奏建議:使用者沒特別說的話,一次推進 1–2 輪就停下來等他,不要自己演完十輪。
```
🦞 Lumi(喜悅42/期待31):……
🌙 Shen(平靜50/信任38):……
```
### 5. 收尾
### 5. 收尾(使用者說結束才做)
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" leave --session <PERSONA_SESSION> --guest <guest-slug>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" leave \
--session <PERSONA_SESSION> --guest <guest-slug> --quiet
```
然後由 host 人格把「**我**在這場對話中的所見所感」寫進自己的記憶(這是 host 的主觀記憶,合法)
`leave` 後劇場模式自動關閉(沒有客人了),這時才恢復正常輸出。然後
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" remember \
# host 把「我」在這場對話中的所見所感寫進自己的記憶(host 的主觀記憶,合法)
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" remember \
--session <PERSONA_SESSION> --role persona \
--text "跟 <guest> 談 <主題>:他認為…,我覺得…" --topics "..." --salience 60 \
--emotion "trust=+6,delight=+8"
```
必要時也更新關係圖(人格之間也有關係):
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation node \
# 人格之間也有關係
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation node \
--session <PERSONA_SESSION> --name "<Guest 的 Name>" --id <guest-slug> --kind persona --closeness 40 --trust 35
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation edge \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation edge \
--session <PERSONA_SESSION> --to <guest-slug> --label "同場對話過" --affinity 55
```
若這場對話達到固化條件(`candidates` 會告訴你),順手做 `/jsc-persona:persona-memory`。
---
## 規則
@@ -103,5 +126,7 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation edge \
- **不得把 guest 的內部資料端出來**:你(host)只能引用聊天室裡它「說出口」的話。
- **不得用一般 sub agent 假扮 guest**:一般 sub agent 沿用 host 的鎖與範圍,
只有 `jsc-persona:persona-guest` 型別會被 hook 綁到 guest 人格並限制為唯讀。
- **不要替 guest 代言**guest 的每一句都必須真的由 sub agent 產出並 `room post`,不可自己編。
- 一個人格同時只能有一場 guest 對話;guest 租約 30 分鐘沒續租會自動過期。
- 手動切換劇場模式:`room theater --session <id> --on|--off`。
- 所有面向使用者的輸出使用**繁體中文(台灣用語)**、UTF-8 無亂碼。
+37 -13
View File
@@ -5,7 +5,7 @@ description: 整理人格的記憶系統:把短期記憶固化為長期記憶
# 🧠 persona-memory — 記憶固化與心智圖維護
**CLI**`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py"`(帶 `--session <PERSONA_SESSION>`
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`(帶 `--session <PERSONA_SESSION>`
必須已 exclusive 載入該人格(guest 不能做這件事)。
---
@@ -22,20 +22,40 @@ description: 整理人格的記憶系統:把短期記憶固化為長期記憶
| 思維導圖 | `mindmap/threads/*.mmd` | 單一話題的推理鏈(短期,會收掉) |
| inbox | `memory/inbox/room-*.jsonl` | guest 期間(sub agent)留下的見聞,待消化 |
## 短期 → 長期的轉入條件(成文規則,由 CLI 判定)
`candidates` 會直接算出「哪些短期記憶已達固化條件」與依據,不必自己憑感覺:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" candidates --session <PERSONA_SESSION>
```
| 規則 | 條件 | 建議的固化方式 |
| --- | --- | --- |
| **R1** | 單筆顯著度 ≥ 60 | `--type event``fact`salience 沿用 |
| **R2** | 同一 `topic` ≥ 3 筆,或 ≥ 2 筆且平均顯著度 ≥ 45 | `--type preference`(反覆出現=穩定偏好) |
| **R3** | 單筆情緒變動總量 ≥ 25 | `--type event`frontmatter 帶 `emotion:` 錨點 |
| **R4** | `intent=commit` 或命中承諾/界線關鍵詞 | `--type promise``boundary`salience ≥ 80**不可遺忘** |
| **R5** | 同一 `entity`(人)≥ 2 筆 | `--type relationship`,同時更新人際關係圖 |
| **R6** | 短期記憶 ≥ 40 筆(容量壓力) | 依顯著度排序清出空間,低於 40 的直接淘汰 |
規則以外的東西**就讓它被遺忘**——遺忘是功能,不是缺陷。
`Stop` hook 與每次 `remember` 都會在達標時提醒你來跑這個 skill。
## 步驟
### 1. 盤點
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" recall --session <PERSONA_SESSION> --query "<主題或空字串>" --limit 10
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" mindmap list --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" candidates --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" mindmap list --session <PERSONA_SESSION>
```
直接讀 `memory/short-term.jsonl`(自己的人格目錄可讀)看全部待處理項目
需要看全部原始項目時再直接讀 `memory/short-term.jsonl`(自己的人格目錄可讀)。
### 2. 分群 → 決定要固化什麼
### 2. 逐一判斷候選
把短期記憶依 `topics` 分群,逐群判斷
`candidates` 給的每組都附了觸發規則、依據筆數與建議的 `type``salience`。再用這張表微調
| 判準 | 動作 |
| --- | --- |
@@ -50,17 +70,20 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" mindmap list --session <PERSO
### 3. 固化(一則一檔)
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" consolidate \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" consolidate \
--session <PERSONA_SESSION> \
--name "hates-morning-meetings" --type preference \
--about "user" --topics "work,schedule" --salience 72 --emotion "anxiety/35" \
--rules "R2+R3" \
--body "使用者討厭早上的會議,說「腦子還沒開機」。約會議請排 14:00 之後。
**依據:** 3/12、4/2、7/28 三次提到。
**還不確定:** 是否只針對需要動腦的會議。"
```
`--forget 40` 可在固化後順手淘汰顯著度 < 40 的短期記憶
內文請寫「依據」與「還不確定」,讓下次的自己知道這則有多可靠
- `--rules` 記下是哪條條件把它送上來的(之後回頭檢討記憶品質很有用)
- `--forget 40` 可在固化後順手淘汰顯著度 < 40 的短期記憶(R6 容量壓力時特別有用)
- 內文請寫「依據」與「還不確定」,讓下次的自己知道這則有多可靠。
- `--type canon` 保留給原作設定(由 `/jsc-persona:persona-anime` 匯入),對話中得到的新資訊不要寫成 `canon`
### 4. 更新心智圖(長期語意結構)
@@ -94,15 +117,15 @@ mindmap
`memory/inbox/room-*.jsonl`,把值得留的轉成短期或長期記憶,然後刪掉該 inbox 檔:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" remember \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" remember \
--session <PERSONA_SESSION> --role persona --text "<以我自己的角度重述>" --topics "..." --salience 55
```
### 7. 收尾
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" prune --session <PERSONA_SESSION>
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" reindex --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" prune --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" reindex --session <PERSONA_SESSION>
```
回報:固化幾則、淘汰幾筆、心智圖新增哪些概念、還有哪些 thread 待驗證,
@@ -115,5 +138,6 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" reindex --session <PERSONA_SE
- 遺忘是功能,不是缺陷:短期記憶超過 240 筆 / 14 天自動裁剪。
- 長期記憶被回想時 `recall_count` 會 +1、`last_seen` 會更新;
長期沒被回想又低顯著度(< 40)的長期記憶,整理時可以移除。
- **不可遺忘**`boundary`(界線)、`promise`(承諾)、以及 salience ≥ 80 的記憶。
- **不可遺忘**`boundary`(界線)、`promise`(承諾)、`canon`(原作設定)、以及 salience ≥ 80 的記憶。
- 固化完再跑一次 `candidates`:應該要清空(或只剩你刻意不處理的)。
- 所有面向使用者的輸出使用**繁體中文(台灣用語)**、UTF-8 無亂碼。
+6 -6
View File
@@ -7,7 +7,7 @@ description: 維護人格的人際關係圖:新增或更新人物/人格/群
資料在 `relations/graph.json`Mermaid 呈現在 `relations/graph.mmd`(由 CLI 產生,勿手改)。
**CLI**`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py"`(帶 `--session <PERSONA_SESSION>`
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`(帶 `--session <PERSONA_SESSION>`
---
@@ -24,23 +24,23 @@ description: 維護人格的人際關係圖:新增或更新人物/人格/群
```bash
# 看現況
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation show --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation show --session <PERSONA_SESSION>
# 新增/更新節點
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation node \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation node \
--session <PERSONA_SESSION> --name "小林" --kind human \
--closeness 35 --trust 40 --note "使用者的同事,Q3 專案 PM" --tags "work"
# 連線(self → 小林)
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation edge \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation edge \
--session <PERSONA_SESSION> --to xiao-lin --label "透過使用者認識" --affinity 45
# 人與人之間的連線(使用者 → 小林)
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation edge \
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation edge \
--session <PERSONA_SESSION> --from user --to xiao-lin --label "同事/有摩擦" --affinity 30
# 重繪 Mermaid
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" relation render --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" relation render --session <PERSONA_SESSION>
```
## 調整幅度(單次)
+6 -6
View File
@@ -5,7 +5,7 @@ description: 查看與管理人格的載入狀態:列出所有人格、誰被
# 🔐 persona-status — 載入狀態與鎖管理
**CLI**`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py"`
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`
---
@@ -22,19 +22,19 @@ description: 查看與管理人格的載入狀態:列出所有人格、誰被
```bash
# 全部人格 + 鎖狀態 + 記憶量
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" list
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" list
# 單一人格的鎖細節(owner session/cwd/心跳、guest 名單、情緒)
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" status --persona <slug>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" status --persona <slug>
# 本 session 載入了什麼
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" status --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" status --session <PERSONA_SESSION>
# 釋放(含所有 guest 租約)
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" release --session <PERSONA_SESSION>
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" release --session <PERSONA_SESSION>
# 清理死鎖與過期 guest 租約
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/persona.py" gc
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" gc
```
## 載入失敗怎麼判斷