Files
persona/README.md
T
jiantw83andClaude Opus 5 1884a9c3d7 feat: 圖示配色取自人格「最新一次登場」的官方視覺
新增 skill persona-icon:上網查出該人格最新的官方視覺 → 下載 →
**用 Read 親眼看過** → 取髮色/瞳色/服裝色 → 用那組配色繪製圖示,
來源網址與造型描述一併寫進 state/config.json 備查。

CLI:
  icon generate --palette "hair=#..,eye=#..,accent=#..,secondary=#..,light=#.."
                --source-url <網址> [--source-note <說明> --source-date <日期>]
  * --palette 必須配 --source-url(配色要有出處,CLI 強制)。
  * 配色與來源存進 config.icon,之後不帶 --palette 重畫會沿用,不會變回雜湊色。
  * icon show 會列出調色盤與來源網址。

繪製:漸層=髮色→服裝主色、外框=瞳色、點陣紋=亮色、中央仍是編號前兩字。
一深一淺的極端配色(藍黑髮+淡粉洋裝)會把較亮端往較暗端壓到對比 ≥ 3.2,
確保字讀得到——顏色仍是照片來的,只是收斂色階。

為什麼不是把照片本身當圖示:
  1) 環境裡沒有任何影像解碼器(rsvg/imagemagick/Pillow/ffmpeg 都沒有),
     JPEG/WebP 讀不進來,無法轉成 PNG;
  2) 把他人的美術作品原樣放進存取庫是散布,不是引用。
取配色是有依據又不搬運原圖的做法,這個取捨寫進了 README 與 skill。

已套用到兩個真實人格:
  ASUNA-01  取自《Unanswered//butterfly》(2026) 官方主視覺(金栗髮/紅褐瞳/紅衣粉裙)
  YUI-01    取自 AniList 官方角色圖(藍黑髮/暖棕瞳)+ Unital Ring 導航妖精造型

selftest 136 項全綠(新增第 ⑮ 節:調色盤解析、缺來源會被擋、格式錯誤會被擋、
來源寫入 config、重畫沿用配色、極端配色的對比保證)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 02:17:35 +00:00

464 lines
27 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# jsc-persona — AI 人格化記憶聊天
一套讓 AI **成為某個人**、而不只是回答問題的 plugin。
**OpenClaw 相同的人格描述**`IDENTITY.md` 五欄位 + `SOUL.md` 四段落)建立人格,
再用 **hook 強制**的人格鎖與跨人格隔離,把 **十二情緒**、**語意分析**、
**短期/長期記憶**、**心智圖/思維導圖**、**人際關係圖** 綁在一起。
可同時安裝於 **Claude Code、Codex、Antigravity、OpenCode**
skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有 Claude Code 支援**(見「跨助理支援度」)。
---
## 十二條硬規則
| 規則 | 怎麼做到 |
| --- | --- |
| **1a. 用與 OpenClaw 相同的描述建立人格** | `persona-create` 逐項索取 `Name` / `Creature` / `Vibe` / `Emoji` / `Avatar`(連括號提示文字都照 OpenClaw 原文),`SOUL.md` 沿用 `Core Truths` / `Boundaries` / `Vibe` / `Continuity` 段落結構 |
| **1b. 動漫作品+角色名快速建人格** | `persona-anime` 先上網蒐集該角色的公開設定(至少 3 個獨立來源),映射成上述五欄位與 SOUL,再把設定固化成 `canon` 基礎記憶(每則帶來源 URL)+原作人際關係圖+情緒基線 |
| **2. 同一個人格只能被一個程序載入(Sub Agent 不限)** | `state/lock.json`**session_id** 為主鍵、15 分鐘心跳租約;同一 session 的 sub agent 沿用同一把鎖,跨 session 搶佔會被拒;租約過期才可接手(並強制回報) |
| **3. 禁止跨人格讀取資料** | `PreToolUse` hook 對 Read/Write/Edit/Glob/Grep/Bash 做路徑判定(含 `../`、symlink、`$PERSONA_HOME` 繞路),非當前人格一律 deny;CLI 也驗 `--session` 防止冒用身分 |
| **4. 邀請人格用 Sub Agent 一起聊,且只顯示對話** | `persona-invite` 建聊天室 + guest 唯讀租約 + `persona-guest` sub agent;同時開啟**劇場模式**:hook 每輪強制「只輸出 `名字:內容`」、停掉所有系統提醒,CLI 有 `--quiet``room script`(乾淨對話稿) |
| **5. 腳本用 Node.js** | `scripts/*.mjs``hooks/*.mjs`,只用 Node 內建模組(fs/path/os/crypto),無 npm 依賴 |
| **6. 由使用者呼叫才載入並鎖定** | 人格不會自動附身:`SessionStart` hook 只列出可用人格,等使用者下 `/jsc-persona:persona-chat <slug>`;載入即取得獨占鎖並綁定該 session |
| **7. 短期記憶轉入長期記憶有成文條件** | `R1``R6` 六條規則寫在程式裡(`promotionCandidates`),`candidates` 子指令會列出達標的候選與依據,hook 在達標時提醒固化 |
| **8. 講話像人:推導藏起來、一到三句、不重複** | 推導寫進**心裡話** `think`(只回報「💭 心想 N 句」,永不回顯內容);說出口的話進 `said.jsonl`,下一輪注入「最近說過的話」提醒別重講;`room post` 直接**擋下**近似重複(字元 bigram+字集合相似度 ≥ 0.72)與超過三句的發言——劇場模式同樣適用 |
| **9. 人格可以匯出匯入** | `export` 把身分/情緒/記憶/心智圖/關係圖打包成單一 JSON bundle(可 `--gzip`、附 sha256),`import` 還原或換名複製;**不帶**載入鎖與 guest 租約,`journal/` 要明確 `--with-journal` 才帶走 |
| **10. 人格有編號** | 編號 = **英文名全大寫 + 兩位索引**(同名才遞增):`ASUNA-01``YUI-01``ASUNA-02`。編號同時是新人格的本機目錄名與 Gitea 存取庫名稱;中文名先轉羅馬拼音並跟使用者確認拼法 |
| **11. 人格存在 Gitea,依更新頻率分區** | 每個人格一個**私有存取庫**(庫名=編號)。**檔案區**放每輪都在變的活狀態(情緒/短期記憶/心裡話/說過的話/逐字),**Wiki 區**放低頻的身分與長期結構(IDENTITY/SOUL/長期記憶/心智圖/關係圖)當設定百科。本機仍是工作副本,同步失敗不阻斷對話 |
| **12. 建立並補齊資料後產生圖示,配色取自最新造型** | `/jsc-persona:persona-icon` 先上網找出該人格**最新一次登場**的官方視覺、**親眼看過**那張圖,取出髮色/瞳色/服裝色,再繪製 `icon.svg` + `icon.png`512×512),來源網址一併存進 `config.json` 備查;沒有參考圖才退回用編號雜湊配色。PNG 是**自寫柵格器 + zlib 手工編碼**,不依賴任何影像函式庫 |
---
## 架構
```mermaid
flowchart TB
subgraph P["主程序(一個 session = 一個人格)"]
U["使用者訊息"] --> H1["UserPromptSubmit hook<br/>注入 情緒+短期記憶+命中的長期記憶+關係<br/>+心裡話+最近說過的話"]
H1 --> A["語意分析:意圖/主題/實體/情感/需求"]
A --> T["心裡話 think<br/>(推導只留在 inner.jsonl,不輸出)"]
T --> E["情緒評估 → 十二情緒 deltas"]
E --> R["以人格語氣回覆(1–3 句、不重複說過的話)"]
R --> W["記憶回寫(短期)"]
W --> H2["Stop hook:情緒衰減+續租+記說過的話+固化提醒"]
end
subgraph G["Sub Agent(受邀人格,唯讀)"]
GA["persona-guest"]
end
subgraph S["人格倉庫 ~/.claude/personas(工作副本)"]
PA["ASUNA-01/IDENTITY SOUL 記憶 情緒 心智圖 關係圖"]
PB["YUI-01/|…"]
RM[".rooms/room/transcript.jsonl"]
end
subgraph GT["Gitea(存取庫名稱 = 人格編號)"]
GF["ASUNA-01 檔案區<br/>高頻活狀態"]
GW["ASUNA-01 Wiki<br/>低頻設定百科"]
end
P -->|"只能碰自己"| PA
GA -->|"只能碰自己"| PB
P <-->|"唯一合法交流管道"| RM
GA <--> RM
PA -->|"每輪背景 push"| GF
PA -->|"固化/改身分/release"| GW
GF -.->|"載入時 pull"| PA
GW -.-> PA
```
## 人格倉庫(預設 `~/.claude/personas/<slug>/`,可用 `PERSONA_HOME` 覆寫)
```
<slug>/
├── IDENTITY.md # 身分卡:Name / Creature / Vibe / Emoji / AvatarOpenClaw 同欄位)
├── SOUL.md # 靈魂:Core Truths / Boundaries / Vibe / Continuity + 情緒傾向
├── AGENTS.md # 操作規則(與個性分離)
├── icon.svg / icon.png # 人格圖示(由編號/名字/emoji 決定,也是 Gitea 存取庫頭像)
├── USER.md # 對使用者的畫像(事實/推測分開)
├── state/
│ ├── lock.json # 載入鎖(session_id + 心跳租約)
│ ├── guests.json # guest 唯讀租約
│ ├── emotion.json # 十二情緒 levels / baseline / 半衰期
│ ├── inner.jsonl # 心裡話(推導過程;只回報「心想 N 句」,不說出口)
│ ├── said.jsonl # 說過的話(用來擋短時間內的重複發言)
│ ├── sync.json # Gitea 同步狀態(最後 push / pull
│ └── config.json # 含人格編號 code
├── memory/
│ ├── short-term.jsonl # 短期記憶(語意分析後;上限 240 筆 / 14 天)
│ ├── long-term/*.md # 長期記憶(一則一檔 + frontmatter
│ ├── INDEX.md # 長期記憶索引(自動產生)
│ └── inbox/room-*.jsonl # 當 guest 時留下的見聞,待本體消化
├── mindmap/
│ ├── semantic.mmd # 心智圖:概念的長期關聯(Mermaid mindmap
│ └── threads/*.mmd # 思維導圖:單一話題的推理鏈(Mermaid graph,短期)
├── relations/
│ ├── graph.json # 人際關係圖(親近度/信任度/連線)
│ └── graph.mmd # Mermaid 呈現(自動產生)
└── journal/YYYY-MM.jsonl # 原始逐字 + 情緒史(hook 自動寫)
(另有 `.sync/files/`、`.sync/wiki/`:兩個同步區的 git clone 快取,可安全刪除)
```
## 十二情緒
| 六正向 | 六負向 |
| --- | --- |
| 喜悅 `joy`、信任 `trust`、期待 `anticipation`、感激 `gratitude`、平靜 `serenity`、驚喜 `delight` | 憤怒 `anger`、悲傷 `sadness`、恐懼 `fear`、厭惡 `disgust`、羞愧 `shame`、焦慮 `anxiety` |
- 每種 0–100,各有**不同半衰期**(驚喜 60 分最快、信任 720 分最慢),每輪自動朝 `baseline` 指數衰減。
- 由十二情緒推導 `valence`(正向/中性/負向)與 `arousal`(高張/平穩/低張),決定語氣與句長。
- **主導情緒**取「超出基線最多」的前三名,所以個性底色不會永遠霸榜。
- 觸發規則與事件→delta 對照表:`skills/persona-chat/reference/emotions.md`
## 短期 → 長期的轉入條件(R1–R6)
寫在 `scripts/persona-lib.mjs``promotionCandidates()`,用 `candidates` 子指令查:
| 規則 | 條件 | 建議固化為 |
| --- | --- | --- |
| **R1** | 單筆顯著度 ≥ 60 | `event` / `fact` |
| **R2** | 同一 topic ≥ 3 筆,或 ≥ 2 筆且平均顯著度 ≥ 45 | `preference` |
| **R3** | 單筆情緒變動總量 ≥ 25 | `event`(帶情緒錨點) |
| **R4** | `intent=commit` 或命中承諾/界線關鍵詞 | `promise` / `boundary`salience ≥ 80,不可遺忘) |
| **R5** | 同一人物(entity)≥ 2 筆 | `relationship`(並更新關係圖) |
| **R6** | 短期記憶 ≥ 40 筆(容量壓力) | 依顯著度清出空間 |
沒命中任何規則的就讓它被裁掉——**遺忘是功能**。達標時 `Stop``remember` 都會提醒去跑
`/jsc-persona:persona-memory`
## 講話像人(心裡話 / 一到三句 / 不重複)
AI 最容易露餡的三件事:把推理過程講出來、一次講一大段、換句話說同一件事。三個對策:
| 機制 | 怎麼運作 |
| --- | --- |
| **心裡話** `think` | 語意分析、推論、盤算全寫進 `state/inner.jsonl`;這個指令**只印「💭 心想 N 句」**,內容永不回顯。下一輪 `<persona-context>` 會帶回最近三句,推論因此有連續性,但使用者只看得到狀態 |
| **一到三句** | `<persona-context>` 每輪注入上限;`room post` 對超過三句的發言直接拒收(`--force` 例外) |
| **不重複** | 說出口的話由 `Stop` hook 自動記進 `state/said.jsonl``said check` 可事前確認,`room post` 事中攔截。相似度=字元 bigram Jaccard0.4)+字集合 Jaccard0.6),≥ 0.72 視為同一句 |
字集合權重較高,是為了分開「重排語序」與「換掉關鍵詞」這兩種很像但意義完全不同的情況:
```
「我等一下把報告寄給你」vs「等一下我會把報告寄給你」→ 0.779 擋下(用字幾乎相同=同一件事換句話說)
「你今天看起來很累」  vs「你今天看起來很開心」  → 0.687 放行(換了關鍵詞=新資訊)
```
視窗預設 120 分鐘、少於 8 個字的短附和(「嗯」「好啊」)不算重複。
## 人格圖示(SVG + PNG,零外部依賴)
建立人格**並補齊 IDENTITYSOUL 之後**產生,一張 512×512 的幾何徽章。配色有兩種來源:
**① 取自最新造型(`/jsc-persona:persona-icon`,動漫角色建議用這個)**
上網查出角色**最新一次登場**的官方視覺 → 下載 → **親眼看過** → 取五個色 → 繪製:
| 元素 | 取自照片的哪裡 |
| --- | --- |
| 對角漸層 | `hair`(髮色)→ `accent`(服裝主色) |
| 外框 | `eye`(瞳色) |
| 點陣紋 | `light`(最亮的部位) |
| 中央兩個字母 | 編號前兩字(`ASUNA-01``AS` |
`--palette` 必須配 `--source-url`(CLI 強制):配色是從哪張圖來的要留得下來。
兩個顏色一深一淺時(藍黑髮 + 淡粉洋裝),程式會把較亮的一端往較暗的壓到對比 ≥ 3.2,
確保字讀得到——顏色仍然是照片來的,只是收斂色階。
**② 沒有參考圖時:編號雜湊**
| 元素 | 怎麼決定 |
| --- | --- |
| 雙色對角漸層 | `sha256(編號|NameEmoji)` → 色相/飽和度/明度 |
| 5×5 左右對稱點陣紋 | 同一組雜湊的位元 |
| 中央兩個字母 | 編號的前兩個字,自動選黑或白確保對比 |
- **同一個人格永遠得到同一張圖**(純函數,沒有隨機);`ASUNA-01``ASUNA-02` 明顯不同。
- SVG 與 PNG **是同一張圖**:兩者共用同一組單位座標與同一份點陣字資料。
- PNG 由**自寫的柵格器**畫出(3× 超取樣 + 盒式縮減當反鋸齒),再用 `zlib` 手工組出
IHDRIDATIEND 與 CRC32。這台機器沒有 rsvginkscapeimagemagick,也沒有影像函式庫,
而本專案禁止 npm 依賴——所以就自己畫。
- **為什麼沒有 emoji、也不是把照片本身當圖示**:環境裡沒有任何影像解碼器
rsvgimagemagickPillowffmpeg 都沒有),JPEG/WebP 讀不進來,無法把原圖轉成 PNG;
而且把他人的美術作品原樣放進存取庫是散布、不是引用。**取配色**是有依據又不搬運原圖的做法。
emoji 同理無法柵格化,但仍參與雜湊配色。
- 圖示屬於低頻資料 → 同步到 **Wiki 區**,並自動設成 Gitea **存取庫頭像**
```bash
# 取自最新造型(必須同時給來源網址)
node scripts/persona.mjs icon generate --session <id> --force \
--palette "hair=#d9a45b,eye=#9e5b3e,accent=#c0392b,secondary=#e77a8e,light=#f2ebe3" \
--source-url "<官方視覺的網址>" --source-note "<作品(年份)+造型描述>"
# 沒有參考圖:編號雜湊配色
node scripts/persona.mjs icon generate --session <id> [--size 512] [--force]
node scripts/persona.mjs icon show --session <id> # 看配色與來源
```
## 人格編號與 Gitea 儲存
**編號 = 英文名全大寫 + 兩位索引**,同名才遞增,也就是 Gitea 存取庫的名稱:
```
亞絲娜(第一個) → ASUNA-01 結衣 → YUI-01 另一個亞絲娜 → ASUNA-02
```
每個人格一個**私有存取庫**,內容依**更新頻率**分兩區:
| 區 | 放什麼 | 何時 push |
| --- | --- | --- |
| **檔案區**(主存取庫) | 高頻活狀態:`emotion.json``short-term.jsonl``inner.jsonl``said.jsonl``inbox/``mindmap/threads/``journal/` | 每輪對話後由 `Stop` hook 背景推送(`PERSONA_SYNC_MIN_SECONDS` 節流) |
| **Wiki 區** | 低頻設定:`IDENTITY``SOUL``AGENTS``USER`、長期記憶、`INDEX`、心智圖、關係圖 | 記憶固化、改身分/關係圖、`release` 時 |
- **本機永遠是工作副本**:hook 每輪讀寫本機檔案,不經網路;Gitea 掛掉照樣能聊天。
**同步失敗永遠不阻斷對話。**
- 載入人格時會先 `pull`;兩邊都改過同一個檔案就**停下來不覆蓋本機**,由使用者決定保留哪一邊。
- Gitea 的 wiki 只有根目錄的 `.md` 會變成頁面(1.27 實測子目錄頁面 404),所以
`memory/long-term/xxx.md` 攤平成 `Memory-xxx.md`,原始路徑記在 `_paths.json`
Wiki 首頁自動列出所有長期記憶的連結,變成真的讀得下去的「設定百科」。
```bash
export GITEA_HOST=https://gitea.example.com
export GITEA_TOKEN=<個人存取權杖>
export PERSONA_GITEA_OWNER=<帳號或組織> # 選填,預設 token 本人
export PERSONA_GITEA=off # 需要時整個關掉
node scripts/persona.mjs code assign --session <id> --romaji Asuna --rename # 既有人格遷移
node scripts/persona.mjs sync status|init|push|pull --session <id> [--area files|wiki|all]
```
存取庫**預設私有**——人格裡是使用者的個人記憶,公開必須由使用者明講(`--public`)。
## 匯出 / 匯入(人格搬家)
```bash
node scripts/persona.mjs export --session <id> --out ~/backup/lumi.persona.json [--gzip] [--with-journal]
node scripts/persona.mjs import --session <id> --file ~/backup/lumi.persona.json [--persona lumi-copy] [--load]
```
- bundle = 單一 JSON(可 gzip)+ sha256 checksum,無外部工具依賴。
- **只能匯出本 session 目前載入的人格**——否則就是跨人格外洩的後門(`guard` 會 deny)。
- 不帶 `state/lock.json``guests.json`(鎖屬於那台機器的那個程序);`journal/` 預設不帶。
- 換名匯入(`--persona <新 slug>`)可讓同一個人格並存兩份,`config.json` 會記下來歷。
- bundle 內的 `../` 逃逸路徑一律拒收;checksum 不符要 `--force` 才吃。
## 劇場模式(多人格對話只顯示對話)
`invite` 成功即開啟(`leave` 沒有客人時自動關閉,也可 `room theater --on/--off` 手動切):
- `UserPromptSubmit` hook 每輪注入強制規則:輸出**只能**是 `名字:內容`
不得出現指令、指令輸出、狀態、分析、旁白、摘要。
- `Stop` hook 在劇場模式**完全不發系統訊息**(提醒會破壞畫面)。
- CLI 提供 `--quiet`(成功時零輸出)與 `room script`(只有 `emoji 名字(情緒):內容` 的乾淨對話稿)。
- 「講話像人」的三條規則在這裡一樣生效:每個人格每輪 **13 句**、推導走 `think`
近似重複的台詞被 `room post` 拒收(host 與 guest 走同一支 CLI,一視同仁)。
```
🪼 Lumi(喜悅42/期待31):所以你真的一個人把那台舊鐘修好了?
🌙 Shen(平靜50/信任38):修好了。它現在慢三分鐘,我決定不修那三分鐘。
```
## HooksClaude Code
| Hook | 做什麼 |
| --- | --- |
| `SessionStart` | 清死鎖、接續人格、把 `PERSONA_SESSION=<session_id>` 與規則注入上下文 |
| `UserPromptSubmit` | 注入 `<persona-context>`:身分、情緒、短期記憶、關鍵詞命中的長期記憶、相關人際關係;劇場模式時追加「只輸出人格對話」的強制規則;並記原始逐字 |
| `PreToolUse` | **人格隔離與鎖驗證的唯一強制點**deny 帶原因) |
| `Stop` | 情緒隨時間衰減、續租、記錄回覆、達固化條件時提醒(劇場模式時完全靜音) |
| `SubagentStop` | 解除 guest sub agent 的 pin |
| `SessionEnd` | 釋放鎖與 guest 租約,人格才能被下一個程序載入 |
> `session_id` 只有 hook 拿得到 → 注入上下文 → skills 呼叫 CLI 時必須帶 `--session`
> hook 會驗證是否相符。**這是「一人格一程序」與「跨人格隔離」不能被繞過的關鍵**。
---
## Skills 目錄
<!-- JSC-SKILLS:START -->
### `persona-create`
建立人格:以 OpenClaw 相同的五個身分欄位與 SOUL 段落訪談使用者,初始化情緒基線、記憶、心智圖與關係圖,並立即取得載入鎖。
- **Claude Code / Antigravity**`/jsc-persona:persona-create` **Codex**`$persona-create`
### `persona-anime`
用「動漫作品+角色名」建立人格:上網蒐集角色公開設定 → 映射成 OpenClaw 五欄位與 SOUL → 固化成 `canon` 基礎記憶(附來源)+原作關係圖+情緒基線。
- **Claude Code / Antigravity**`/jsc-persona:persona-anime 《作品》 角色名` **Codex**`$persona-anime`
### `persona-chat`
載入人格並對話:取得獨占鎖 → 每輪做語意分析(推導寫進心裡話,不說出口)→ 更新十二情緒 → 回想記憶與關係 → 以人格語氣回覆(1–3 句、不重複說過的話)→ 寫回記憶。
- **Claude Code / Antigravity**`/jsc-persona:persona-chat <slug>` **Codex**`$persona-chat`
### `persona-invite`
邀請另一個人格透過 `persona-guest` sub agent 加入聊天室,進入**劇場模式**(畫面只留 `名字:內容` 的人格對話);結束後讓它離場並把見聞留在它自己的 inbox。
- **Claude Code / Antigravity**`/jsc-persona:persona-invite <slug>` **Codex**`$persona-invite`
### `persona-transfer`
人格搬家:把身分、十二情緒、短期/長期記憶、心智圖與關係圖打包成單一 bundle 檔(可 gzip、附 checksum),或從 bundle 還原/換名複製成新人格。
- **Claude Code / Antigravity**`/jsc-persona:persona-transfer` **Codex**`$persona-transfer`
### `persona-icon`
依人格**最新一次登場**的官方視覺產生圖示:上網查最新造型 → 下載並親眼看過參考圖 → 取髮色/瞳色/服裝色 → 繪製 `icon.svg` + `icon.png`,來源網址一併存證。
- **Claude Code / Antigravity**`/jsc-persona:persona-icon` **Codex**`$persona-icon`
### `persona-sync`
人格編號與 Gitea 儲存:指派編號(`ASUNA-01`)、開以編號命名的私有存取庫、高頻活狀態同步到檔案區、低頻身分與長期記憶同步到 Wiki 區,並處理既有人格遷移與同步衝突。
- **Claude Code / Antigravity**`/jsc-persona:persona-sync` **Codex**`$persona-sync`
### `persona-memory`
記憶固化:依 R1–R6 條件把短期記憶轉入長期(一則一檔)、淘汰雜訊、更新心智圖與思維導圖、消化 guest inbox、重建索引。
- **Claude Code / Antigravity**`/jsc-persona:persona-memory` **Codex**`$persona-memory`
### `persona-relation`
人際關係圖維護:節點/連線、親近度與信任度調整、輸出 Mermaid 關係圖。
- **Claude Code / Antigravity**`/jsc-persona:persona-relation` **Codex**`$persona-relation`
### `persona-status`
載入狀態與鎖管理:誰被哪個程序鎖住、guest 租約、釋放、接手死鎖、清理殘留。
- **Claude Code / Antigravity**`/jsc-persona:persona-status` **Codex**`$persona-status`
<!-- JSC-SKILLS:END -->
### Agents
- `persona-guest``agents/persona-guest.md`)— 受邀人格的 sub agent,唯讀、被綁死在自己的人格目錄。
### CLI 與自我測試
所有狀態變更都經過 `scripts/persona.mjs`**Node.js ≥ 18**,只用內建模組,無 npm 依賴):
```bash
node scripts/persona.mjs --help
node scripts/persona.mjs list
node scripts/persona.mjs candidates --session <PERSONA_SESSION> # 看哪些短期記憶該固化
node scripts/persona.mjs think --session <id> --text "<推導>" # 心裡話(只回報「心想 N 句」)
node scripts/persona.mjs said check --session <id> --text "<話>" # 這句是不是又要說一次?
node scripts/persona.mjs room script --session <id> --room <room> # 乾淨對話稿(劇場模式用)
node scripts/persona.mjs export --session <id> --out lumi.json # 離線搬家(單檔)
node scripts/persona.mjs sync status --session <id> # Gitea 同步狀態
node scripts/selftest.mjs # 136 項驗證:鎖、隔離、情緒、固化、說話節制、劇場模式、匯出匯入、編號與 Gitea 分區、圖示與照片配色、hooks
```
檔案結構:`scripts/persona-lib.mjs`(核心:鎖/隔離/情緒/記憶)、`scripts/persona.mjs`CLI)、
`scripts/persona-gitea.mjs`(編號與 Gitea 同步)、`scripts/persona-icon.mjs`(圖示:SVG + 自寫 PNG 編碼)、`hooks/*.mjs`(六個 hook)、`scripts/selftest.mjs`(自我測試)。
---
## 跨助理支援度
| 助理 | skills | hooks(鎖/隔離強制) | 受邀人格 sub agent |
| --- | --- | --- | --- |
| Claude Code | ✅ `/jsc-persona:<name>` | ✅ 完整 | ✅ `jsc-persona:persona-guest` |
| Codex | ✅ `$<name>` | ❌ | ⚠ 需自行以子任務模擬 |
| Antigravity | ✅ `/jsc-persona:<name>` | ❌ | ⚠ |
| OpenCode | ✅ 依描述自動觸發 | ❌ | ⚠ |
> 沒有 hook 的助理仍會遵守 CLI 層的檢查(`--session` 綁定、`require_owner``require_member`、
> guest 唯讀),但那是**自律**而非強制:真正的 deny 只有 Claude Code 的 `PreToolUse` 做得到。
---
## 安裝 / 更新 / 移除
> 指令中的 repo 網址換成你的:`https://gitea.jsc.idv.tw/plugins/persona.git`
> **Claude / Codex 從 git URL 安裝(會 clone 遠端),請先把本 repo `push` 到 gitea。**
### Claude Code
```bash
claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/persona.git
claude plugin install jsc-persona@jsc-plugins
# 更新
claude plugin marketplace update jsc-plugins
claude plugin update jsc-persona@jsc-plugins
# 移除
claude plugin uninstall jsc-persona@jsc-plugins
```
- 工作階段內 slash 版(等價):把 `claude plugin` 換成 `/plugin`
- 本機開發(免 push):`claude plugin marketplace add /home/coder/plugins/persona` 後再 install。
- 安裝後**重啟工作階段**讓 hooks 生效;用 `/hooks` 確認六個 hook 都在。
### Codex
```bash
codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/persona.git
codex plugin add jsc-persona@jsc-plugins
codex plugin marketplace upgrade jsc-plugins # 更新
codex plugin remove jsc-persona@jsc-plugins # 移除
```
### Antigravity`agy`
> `agy plugin install <url>` 目前只支援 github.comgitea 請 clone 後用本地路徑。
```bash
git clone https://gitea.jsc.idv.tw/plugins/persona.git ~/plugins/persona
agy plugin install ~/plugins/persona
# 更新:git -C ~/plugins/persona pull && agy plugin uninstall jsc-persona && agy plugin install ~/plugins/persona
```
### OpenCode
```bash
git clone https://gitea.jsc.idv.tw/plugins/persona.git ~/plugins/persona
mkdir -p ~/.config/opencode/skills
cp -r ~/plugins/persona/skills/* ~/.config/opencode/skills/
```
> **Windows PowerShell**`cp -r A B` → `Copy-Item A B -Recurse -Force`、`~` → `$HOME`。
### headless 一次性執行
| 助理 | 指令 |
| --- | --- |
| Claude Code | `claude -p "/jsc-persona:persona-chat lumi"` |
| Codex | `codex exec '$persona-chat lumi'` |
| Antigravity | `agy -p "/jsc-persona:persona-chat lumi"` |
| OpenCode | `opencode run "用 lumi 這個人格跟我聊聊"` |
---
## 設計取捨(讀之前先知道)
- **記憶是被策展的,不是全存**:逐字稿進 `journal/`,但只有經過語意分析、有顯著度的內容才進短期記憶,
再由 `persona-memory` 決定什麼值得成為長期記憶。**遺忘是功能**。
- **事實與推測分離**:推論走思維導圖(`mindmap/threads/`),驗證後才升格長期記憶。
- **鎖的擁有者是 session 不是 process**CLI 跑完就結束,所以租約判定只看心跳。
- **guest 唯讀**:受邀人格不能在別人的 session 裡改自己的長期記憶(避免兩個程序同時寫),
只能把見聞放進 `memory/inbox/`,等它自己被載入時消化。
- **hook 是強制、SKILL 是引導**SKILL.md 寫的規則模型可能忘記,hook 不會。
- **「不要說出來」要靠設計而不是靠忍住**:所以心裡話有自己的指令與檔案,
而且那個指令**印不出內容**——就算不小心把輸出貼上去,使用者也只看到「💭 心想 N 句」。
- **重複用相似度擋、不用語意判斷**:字元層級的比對沒有模型成本、行為可預期,
誤判時有 `--allow-repeat` 可救;漏判的代價(人格自我重複)比誤判高。
## 新增/修改 skill
1. 複製一個現有 skill 目錄,改 `SKILL.md``name``description`(描述要寫清楚何時用、何時不用)。
2. 需要新的狀態操作 → 加到 `scripts/persona.mjs` 的子指令,並在 `scripts/selftest.mjs` 補測試。
3. 動到隔離規則 → 一定要在 `selftest.mjs` 的第 ③(隔離)/⑧(guest 與劇場模式)區加對應案例,跑到全綠。
4. 把 skill 補進上方「Skills 目錄」區塊。
5. bump `.claude-plugin/plugin.json``.codex-plugin/plugin.json``plugin.json` 三處 `version`commit 後 push。