--- name: persona-transfer description: 匯出與匯入人格:把一個人格(身分、靈魂、十二情緒、短期與長期記憶、心智圖、思維導圖、人際關係圖)打包成單一 bundle 檔,或從 bundle 還原/複製成新人格。當使用者說要備份人格、把人格搬到另一台機器或另一個 AI 助理、把人格分享給別人、從檔案還原人格、複製一份人格來做實驗、或問「這個人格能不能帶走」時觸發。不適用於:建立全新人格(用 persona-create 或 persona-anime)、載入既有人格聊天(用 persona-chat)、整理記憶(用 persona-memory)。 --- # 📦 persona-transfer — 人格搬家(匯出/匯入) **CLI**:`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`(一律帶 `--session `) bundle 是**單一 JSON 檔**(可 gzip),不依賴任何外部工具,複製到隨身碟、貼進 git、 傳給另一台機器都可以。 > 只是要在另一台機器接續同一個人格、而且兩邊都連得到 Gitea 的話,**不必經過檔案**: > 用 `/jsc-persona:persona-sync` 的 `clone --code <編號>` 直接從存取庫拉一份回來。 > 這裡的 bundle 是給離線、換助理、或想留一份快照的情境。 --- ## bundle 帶走什麼、不帶什麼 | 帶走 | 不帶 | | --- | --- | | `IDENTITY.md` / `SOUL.md` / `AGENTS.md` / `USER.md` | `state/lock.json`、`state/guests.json`(載入鎖與 guest 租約屬於「那台機器的那個程序」) | | `state/config.json`、`state/emotion.json`(十二情緒的 levels/baseline/半衰期) | `journal/`(原始逐字稿:量大且最私密,要帶請明確加 `--with-journal`) | | `state/mood.json`(當日心情底色)、`state/loops.json`(懸著的事)、`state/probe.jsonl`(試探紀錄) | 任何**其他人格**的資料——一個 bundle 只有一個人格 | | `state/inner.jsonl`(心裡話)、`state/said.jsonl`(說過的話) | | | `memory/`(短期、長期一則一檔、INDEX、inbox) | | | `mindmap/`(心智圖與思維導圖)、`relations/`(關係圖) | | 規則很簡單:**除了鎖與租約,人格目錄裡的東西都帶走**。所以之後新增的狀態檔不必回頭改這張表—— `state/lock.json` 與 `state/guests.json` 是唯一兩個明文排除的(它們描述的是「那台機器的那個程序」, 換一台機器就是假的)。 檔案裡有 `checksum`(sha256),匯入時會驗;對不上就是被改過或損毀。 --- ## bundle 版本(目前 v2) | 版本 | 差在哪 | | --- | --- | | v1 | 長期記憶只有 `salience`;內文不分層;沒有 `mood.json`/`loops.json`/`probe.jsonl` | | **v2** | 長期記憶多了 `strength`(回想強度)與 `when`/`where`/`mood`(情境索引),內文分「主旨/細節」兩層;狀態多了當日心情底色、未完事項、試探紀錄 | - **舊的 v1 bundle 照樣吃得下**:`import` 寫完檔案會**就地跑一次 migration** 補齊欄位並切分內文, 所以匯入之後這台機器上只有一種格式,不必到處判版本。 - **遷移失敗不會讓整個匯入失敗**(INDEX 還是要重建),代價是它也**不會出現在人看的輸出裡**—— 只有 `--json` 的 `migrated` 看得到統計,是 `null` 就代表那次遷移出過錯。 匯進來卻沒升格式的人格,它的老記憶進不了衰減與模糊態,等於最重要的那批記憶反而沒效果, 所以**匯入舊 bundle 之後補跑一次**比較保險:`migrate --session --dry-run` 看還有沒有東西要動,有就跑一次不帶 `--dry-run` 的。 - **比本版新的 bundle 會被擋下**(`validateBundle` 直接判不合法):未來的欄位沒有辦法猜, 硬吃會把不認得的資料寫進人格目錄。這種時候該更新 plugin,不是加 `--force`。 --- ## 匯出 **只能匯出「本 session 目前載入的那個人格」**——這是隔離規則的一部分, 不然匯出就成了把別人的記憶讀出來的後門。所以要先載入它: ```bash # 1) 先載入(若還沒載入) node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" load --persona --session # 2) 匯出 node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" export \ --session --out ~/persona-backup/.persona.json ``` 選項: - `--with-journal`:連逐字稿一起帶(會大很多,而且很私密——**要先問使用者**)。 - `--gzip`:輸出 `.json.gz`(大人格建議加;匯入會自動辨識)。 - `--force`:覆寫已存在的輸出檔。 - 不給 `--out` 就寫到現在的工作目錄,檔名 `-<時間戳>.persona.json`。 **不要把 bundle 寫進人格倉庫裡**(`~/.claude/personas/`)——hook 會擋,而且那裡是人格的家,不是備份區。 ## 匯入 ```bash node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" import \ --session --file ~/persona-backup/.persona.json ``` | 情況 | 做法 | | --- | --- | | 這台機器還沒有這個人格 | 直接匯入,slug 沿用 bundle 內的名字 | | 想同時保留兩份(原版 + 實驗版) | `--persona <新 slug>` 換名匯入;`config.json` 會記下它來自誰 | | 要用匯入的資料**覆蓋**既有人格 | `--force`——**這會蓋掉現有記憶與情緒,先問過使用者**;且該人格不能正被其他程序載入 | | 匯入後想馬上聊 | 加 `--load`(本 session 沒有載入別的人格時才會生效) | checksum 對不上會被擋下;要硬吃得加 `--force`,並**主動告訴使用者這份檔案可能被改過**。 ## 跟使用者互動的原則 1. **匯出前確認範圍**:要不要帶 `journal/`(逐字稿)。預設不帶。 2. **匯出後回報實際路徑與大小**,還有帶了幾則長期記憶——他要拿去搬家,得知道搬了什麼。 3. **匯入前先 `list`**,看目標 slug 是否已存在:存在就給他「換名匯入 / 覆寫」兩個選擇, 不要自己決定 `--force`。 4. **匯入的是 v1 bundle 就講一句**:長期記憶已經升成新格式(補回想強度、切主旨/細節)。 這會改變那個人格「記得多清楚」的行為,使用者有權知道搬過來的不完全是原樣。 5. 匯入後告訴他 `/jsc-persona:persona-chat ` 就能開始聊。 ## 邊界 - 一次一個人格。要搬多個就跑多次(每次都要先 `release` 再 `load` 下一個)。 - bundle 內的相對路徑會被驗證,`../` 之類的逃逸路徑一律拒收。 - 匯入不會帶進鎖:新機器上的人格是空閒的,等使用者自己載入。 - 所有面向使用者的輸出使用**繁體中文(台灣用語)**、UTF-8 無亂碼。