feat: 人格編號(ASUNA-01)與 Gitea 儲存,依更新頻率分檔案區/Wiki 區

1. 人格編號
   - 編號 = 英文名全大寫 + 兩位索引,同名才遞增:ASUNA-01 / YUI-01 / ASUNA-02。
   - 編號同時是新人格的本機目錄名與 Gitea 存取庫名稱(`create` 不給
     --persona 就用編號當目錄名;既有小寫 slug 仍然合法,不會被鎖在門外)。
   - 中文名由 skill 提議羅馬拼音、使用者確認後帶 `--romaji` 進來;
     CLI 只吃拉丁字母,避免拼音在程式裡亂猜。
   - 新增 `code show|assign|next`;`code assign --rename` 可把既有人格的
     目錄名一併改成編號(會轉移鎖與 session 綁定)。

2. Gitea 儲存(scripts/persona-gitea.mjs)
   - 每個人格一個私有存取庫,庫名 = 編號。本機仍是工作副本,hook 每輪
     讀寫本機檔案,**同步失敗永遠不阻斷對話**。
   - 依更新頻率分區:
       檔案區(高頻)emotion / short-term / inner / said / inbox /
                     mindmap threads / journal → 每輪由 Stop hook 背景 push
       Wiki 區(低頻)IDENTITY SOUL AGENTS USER / 長期記憶 / INDEX /
                     心智圖 / 關係圖 → 固化、改身分關係、release 時 push
   - Gitea 的 wiki 只有根目錄 .md 會變成頁面(1.27 實測子目錄頁面 404),
     所以 Wiki 區攤平成 `Memory-xxx.md`,原始路徑記在 `_paths.json`,
     pull 時還原;Home 頁自動列出所有長期記憶連結。
   - 新增 `sync status|init|push|pull`;載入時先 pull,兩邊都改過同一個
     檔案就停下來不覆蓋本機,交由使用者決定。
   - push 遇到 non-fast-forward 會對齊遠端後把工作副本重新疊上去再推。
   - 認證走 http.extraHeader(GIT_CONFIG_* 環境變數),token 不寫進
     .git/config 也不進 process 參數。

3. 順帶修正
   - guard 的 `--persona` 解析原本只吃小寫,大寫編號會漏掉整個跨人格檢查。
   - pull 原本會無差別覆蓋本機檔案,本機較新但還沒 push 的內容會被蓋掉;
     改成只寫回「遠端真的改過的」與「本機缺少的」。
   - `create` 原本吞掉 Gitea 首次推送的結果,失敗是靜默的。

新增 skill:persona-sync(編號、同步、既有人格遷移、衝突處理)。
selftest 116 項全綠(新增 ⑬,含「分區不重不漏」檢查;測試強制 PERSONA_GITEA=off)。
另對真實 Gitea 跑過端對端(建庫→兩區推送→改動→拉回→release),測試用存取庫已刪除。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 01:49:27 +00:00
co-authored by Claude Opus 5
parent 52a73e81c8
commit e986686d0e
14 changed files with 1145 additions and 29 deletions
+62 -8
View File
@@ -10,7 +10,7 @@ skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有
---
## 條硬規則
## 十一條硬規則
| 規則 | 怎麼做到 |
| --- | --- |
@@ -24,6 +24,8 @@ skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有
| **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/長期記憶/心智圖/關係圖)當設定百科。本機仍是工作副本,同步失敗不阻斷對話 |
---
@@ -43,15 +45,23 @@ flowchart TB
subgraph G["Sub Agent(受邀人格,唯讀)"]
GA["persona-guest"]
end
subgraph S["人格倉庫 ~/.claude/personas"]
PA["alpha/IDENTITY SOUL 記憶 情緒 心智圖 關係圖"]
PB["beta/|…"]
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` 覆寫)
@@ -68,7 +78,8 @@ flowchart TB
│ ├── emotion.json # 十二情緒 levels / baseline / 半衰期
│ ├── inner.jsonl # 心裡話(推導過程;只回報「心想 N 句」,不說出口)
│ ├── said.jsonl # 說過的話(用來擋短時間內的重複發言)
── config.json
── sync.json # Gitea 同步狀態(最後 push / pull
│ └── config.json # 含人格編號 code
├── memory/
│ ├── short-term.jsonl # 短期記憶(語意分析後;上限 240 筆 / 14 天)
│ ├── long-term/*.md # 長期記憶(一則一檔 + frontmatter
@@ -81,6 +92,8 @@ flowchart TB
│ ├── graph.json # 人際關係圖(親近度/信任度/連線)
│ └── graph.mmd # Mermaid 呈現(自動產生)
└── journal/YYYY-MM.jsonl # 原始逐字 + 情緒史(hook 自動寫)
(另有 `.sync/files/`、`.sync/wiki/`:兩個同步區的 git clone 快取,可安全刪除)
```
## 十二情緒
@@ -129,6 +142,40 @@ AI 最容易露餡的三件事:把推理過程講出來、一次講一大段
視窗預設 120 分鐘、少於 8 個字的短附和(「嗯」「好啊」)不算重複。
## 人格編號與 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
@@ -208,6 +255,12 @@ node scripts/persona.mjs import --session <id> --file ~/backup/lumi.persona.json
- **Claude Code / Antigravity**`/jsc-persona:persona-transfer` **Codex**`$persona-transfer`
### `persona-sync`
人格編號與 Gitea 儲存:指派編號(`ASUNA-01`)、開以編號命名的私有存取庫、高頻活狀態同步到檔案區、低頻身分與長期記憶同步到 Wiki 區,並處理既有人格遷移與同步衝突。
- **Claude Code / Antigravity**`/jsc-persona:persona-sync` **Codex**`$persona-sync`
### `persona-memory`
記憶固化:依 R1–R6 條件把短期記憶轉入長期(一則一檔)、淘汰雜訊、更新心智圖與思維導圖、消化 guest inbox、重建索引。
@@ -243,12 +296,13 @@ 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/selftest.mjs # 102 項驗證:鎖、隔離、情緒、固化條件、說話節制、劇場模式、匯出匯入、hooks
node scripts/persona.mjs export --session <id> --out lumi.json # 離線搬家(單檔)
node scripts/persona.mjs sync status --session <id> # Gitea 同步狀態
node scripts/selftest.mjs # 116 項驗證:鎖、隔離、情緒、固化、說話節制、劇場模式、匯出匯入、編號與 Gitea 分區、hooks
```
檔案結構:`scripts/persona-lib.mjs`(核心:鎖/隔離/情緒/記憶)、`scripts/persona.mjs`CLI)、
`hooks/*.mjs`(六個 hook)、`scripts/selftest.mjs`(自我測試)。
`scripts/persona-gitea.mjs`(編號與 Gitea 同步)、`hooks/*.mjs`(六個 hook)、`scripts/selftest.mjs`(自我測試)。
---