Skill: torchesfrms/agent-sbti (version 1.0.1)

--- name: agent-sbti description: AI Agent 性格配置系统 - 完成 SBTI 测试，生成性格报告，选择 Agent 类型（互补/同频/微调/自定义），一键配置 Agent SOUL.md。适用：人格测试、Agent 性格配置。触发词：SBTI测试、人格测试、Agent性格。 --- # 🎭 Agent-SBTI 让 AI Agent 适配你的性格。 ## 功能 1. **SBTI 测试** - 20 道题，15 维度，5 种人格 2. **配置模式** - 互补/同频/微调/自定义 3. **一键配置** - 自动备份 + 修改 SOUL.md ## 5 种人格 | 类型 | 风格 | |------|------| | 💀 DEAD | 坦诚直接 | | 🔥 FUCK | 热情似火 | | 💰 ATM | 有求必应 | | 🐒 MALO | 温和摸鱼 | | 💩 SHIT | 毒舌真实 | ## 4 种配置 | 模式 | 说明 | |------|------| | 🔄 互补 | 补足弱点 | | 📋 同频 | 复制风格 | | ⚖️ 微调 | 80%相似 | | 🎛️ 自定义 | 自己选 | ## 安全 - 修改前备份到 `~/.openclaw/workspace/backup/agent-sbti/` - 展示变更，确认后修改 - 支持「恢复原配置」 ## 触发 "做 SBTI 测试" / "人格测试" / "SBTI" ## 组成 - `test.js` - 答题系统 - `agent_config.js` - 配置生成 - `apply.js` - 备份/回滚 - `skill_handler.js` - 对话处理 *让 AI 更懂你，让 Agent 更像你。* 🎭

path: PLAN.md
content: # 技术计划: Agent-SBTI

## 技术选型

| 组件 | 选型 | 理由 |
|------|------|------|
| 核心语言 | JavaScript/Node.js | 与 OpenClaw 生态兼容 |
| 测试数据 | sbti-personality-test | 现成的 20 题 + 5 人格 |
| 配置格式 | JSON → SOUL.md | OpenClaw 原生支持 |
| 用户界面 | 纯对话式 / 可选 Web | 直接在 Agent 内运行 |

## 架构设计

```
用户 → Agent (对话式测试)
         ↓
    test.js (答题系统)
         ↓
    report.js (计算维度 + 识别人格)
         ↓
    agent_config.js (生成配置)
         ↓
    apply.js (写入 SOUL.md 或提供复制)
```

## 核心模块

### 1. test.js
- 存储 20 道题目（复用数据包）
- 记录用户答案
- 计算维度分数

### 2. report.js
- 生成雷达图数据
- 识别人格类型
- 输出性格报告

### 3. agent_config.js
- 根据性格 + 用户选择（互补/同频/微调）
- 生成 Agent 配置 JSON

### 4. apply.js
- 将配置写入 SOUL.md
- 或生成可复制的配置片段

## 配置映射逻辑

```
用户维度分数 → 标准化(0-1) → Agent配置参数
     ↓
用户选择 → 互补(取反)/同频(相同)/微调(混合)
     ↓
Agent SOUL.md 配置片段
```

## 接口设计（内部）

```javascript
// 计算维度
calcDimensions(answers) → dimensions

// 生成报告
generateReport(dimensions) → { radar, personality, description }

// 生成 Agent 配置
generateAgentConfig(dimensions, type) → config

// 应用配置
applyConfig(config) → success/failed
```

## 数据模型

### UserProfile
```json
{
  "answers": [1-20],
  "dimensions": { "维度名": 分数 },
  "personality": "DEAD|FUCK|ATM|MALO|SHIT",
  "createdAt": "ISO时间"
}
```

### AgentConfig
```json
{
  "communication": { ... },
  "personality": { ... },
  "behavior": { ... },
  "raw": { "原始维度": "配置值" }
}
```

## 风险与依赖

| 类型 | 描述 |
|------|------|
| 风险 | SOUL.md 写入可能覆盖用户自定义内容 |
| 风险 | 用户性格与 Agent 配置映射需要调试验证 |
| 依赖 | sbti-personality-test 数据包 |
| 依赖 | OpenClaw Agent 环境 |

## 实现优先级

1. **P0**: test.js + report.js（核心功能）
2. **P1**: agent_config.js（配置生成）
3. **P2**: apply.js（配置应用）
4. **P3**: 分享功能

---

## 自定义模式扩展

### 配置生成扩展
```javascript
// 自定义模式：直接指定人格
generateAgentConfig(dimensions, personality) → config

// 5 种人格配置预设
const PERSONALITY_PRESETS = {
  DEAD: { communication: {...}, personality: {...} },
  FUCK: { communication: {...}, personality: {...} },
  ATM:  { communication: {...}, personality: {...} },
  MALO: { communication: {...}, personality: {...} },
  SHIT: { communication: {...}, personality: {...} }
}
```

### 自定义模式流程
```
用户: "都不喜欢"
     ↓
罗列 5 种人格 + 配置预览
     ↓
用户选择人格
     ↓
确认配置
     ↓
应用配置
```


path: README.md
content: # 🎭 Agent-SBTI: AI Agent 性格配置系统

**让 AI Agent 的沟通风格适配你的 SBTI 人格**

---

## 功能概述

1. **完成 SBTI 测试** - 20 道题了解你的性格
2. **生成性格报告** - 15 维度 + 5 种人格类型
3. **选择 Agent 类型** - 互补 / 同频 / 微调 / 自定义
4. **一键配置 Agent** - 自动备份 + 修改 + 提醒

---

## 完整对话流程

### 流程 1: 标准测试 + 同频配置

```
用户: 做 SBTI 测试

Agent: 📌 第 1/20 题

         你朋友凌晨三点发来「在吗」，你会？

         1. 秒回「在」然后后悔
         2. 已读不回，明天假装没看到
         3. 直接拉黑，清净
         4. 回「在」然后发「？」

         请回复数字 1-4 选择答案

用户: 2

Agent: 📌 第 2/20 题
...

[用户继续回答 20 道题]

Agent: 🎉 测试完成！

        你的 SBTI 人格类型: 狗屎人 (SHIT)
        📝 "你是行走的负能量发射器。"

        详细描述:
        1. 你是行走的负能量发射器。
        2. 你可能对什么都看不惯...
        ...

        📊 维度得分:
        社恐指数: ████░ (4)
        摆烂力: ██░░░ (2)
        ...

        ---

        🎭 现在选择 Agent 性格类型:

        1. 🔄 互补型 - 补足你的弱点
        2. 📋 同频型 - 复制你的风格  
        3. ⚖️ 微调型 - 80%相似 + 20%强化弱点
        4. 🎛️ 自定义 - 自己选择人格类型

        请回复数字 1-4 选择

用户: 2

Agent: 📋 Agent 性格配置预览

        类型: 📋 同频型

        沟通风格:
        - 主动程度: medium
        - 正式程度: casual
        ...

        ---

        🤖 确认修改吗？

        在修改前，我会帮你备份原配置到:
        `~/.openclaw/workspace/backup/agent-sbti/`

        请回复「是」确认修改，或「取消」退出

用户: 是

Agent: ✅ 配置已更新！

        📁 备份位置: ~/.openclaw/workspace/backup/agent-sbti/2026-04-10/

        本次修改内容:
        - 沟通风格.主动程度: medium → high
        - 性格特征.反驳强度: medium → high

        如果想恢复原配置，请说「恢复原配置」。
```

---

### 流程 2: 自定义人格选择

```
用户: 做 SBTI 测试
[... 完成测试 ...]

Agent: 🎭 现在选择 Agent 性格类型:

        1. 🔄 互补型
        2. 📋 同频型
        3. ⚖️ 微调型
        4. 🎛️ 自定义

用户: 4

Agent: 🎛️ 自定义人格类型

        你说都不喜欢？那自己选一个吧：

        1. 💀 DEAD - 透明型
           坦诚直接，不拐弯抹角，注重效率和实用性

        2. 🔥 FUCK - 情绪型
           情感丰富，热情似火，偶尔爆炸，注重表达和互动

        3. 💰 ATM - 付出型
           有求必应，任劳任怨，超有耐心，主动帮助解决问题

        4. 🐒 MALO - 摸鱼型
           温和低调，适度摸鱼，不强出头，追求轻松自在

        5. 💩 SHIT - 毒舌型
           毒舌真实，一针见血，不灌鸡汤，理性分析问题

        请回复数字 1-5 选择人格类型

用户: 5

Agent: 📋 Agent 性格配置预览

        类型: 🎛️ SHIT

        沟通风格:
        - 主动程度: medium
        - 正式程度: casual
        - 幽默程度: medium
        ...

        ---

        🤖 确认修改吗？

用户: 是

Agent: ✅ 配置已更新！
```

---

### 流程 3: 回滚配置

```
用户: 恢复原配置

Agent: ✅ 已恢复到上一版本
       备份位置: ~/.openclaw/workspace/backup/agent-sbti/2026-04-10_103500/
```

---

## 五种人格类型

| 类型 | 用户特点 | Agent 风格 |
|------|----------|------------|
| 💀 **DEAD** | 人群中最透明 | 坦诚直接，不拐弯抹角 |
| 🔥 **FUCK** | 情绪不稳定 | 情感丰富，热情似火 |
| 💰 **ATM** | 永远在付出 | 有求必应，任劳任怨 |
| 🐒 **MALO** | 职场吉祥物 | 温和低调，适度摸鱼 |
| 💩 **SHIT** | 浑身负能量 | 毒舌真实，一针见血 |

---

## 四种配置模式

| 模式 | 说明 | 适用场景 |
|------|------|----------|
| 🔄 **互补型** | 识别用户弱点，Agent 强化该维度 | 想让 Agent 弥补自己不足 |
| 📋 **同频型** | 完全复制用户性格 | 想要「另一个自己」 |
| ⚖️ **微调型** | 80% 同频 + 20% 强化弱点 | 想要相似但更好的 Agent |
| 🎛️ **自定义** | 用户自己选择人格类型 | 对现有选项不满意 |

---

## 十五个维度

| 维度 | 说明 |
|------|------|
| 社恐指数 | 害怕社交的程度 |
| 摆烂力 | 躺平放弃的程度 |
| 嘴硬程度 | 死不认错的程度 |
| 表面正常值 | 看起来正常的程度 |
| 内心戏浓度 | 内心戏的丰富程度 |
| 工具人倾向 | 被人利用的程度 |
| 整活频率 | 整活的频率 |
| 情绪稳定性（反向） | 情绪波动程度 |
| 自我感觉良好度 | 自信程度 |
| 拖延症等级 | 拖延的严重程度 |
| 社牛残留量 | 社交能力 |
| 玻璃心厚度 | 抗打击能力 |
| 打工人觉悟 | 工作态度 |
| 网瘾深度 | 网络依赖程度 |
| 人间清醒度 | 理性程度 |

---

## 安全机制

### 修改前必须备份
- 每次修改 SOUL.md 前强制备份
- 备份位置: `~/.openclaw/workspace/backup/agent-sbti/`
- 保留最近 5 次备份

### 变更提醒
```
🤖 在修改配置之前，我先帮您备份原配置。

以下是我即将修改的内容：
- communication.主动程度: medium → high
- personality.反驳强度: medium → high

确认修改吗？ (输入「是」确认)
```

### 回滚功能
```
用户: "恢复原配置"
Agent: ✅ 已恢复到上一版本
```

---

## 触发关键词

- "做 SBTI 测试"
- "SBTI 测试"
- "人格测试"
- "配置 Agent 性格"
- "Agent 性格测试"
- "开始测试"

---

## 文件结构

```
agent-sbti/
├── SKILL.md              # ClawHub 发布描述（精简版）
├── README.md             # 本地完整文档
├── scripts/
│   ├── skill_handler.js  # 对话处理器
│   ├── test.js           # 测试答题系统
│   ├── agent_config.js   # 配置生成器
│   └── apply.js          # 配置应用器
└── data/
    ├── questions.json     # 20道题
    ├── results.json       # 5种人格结果
    └── dimensions.json    # 15个维度
```

---

## 版本历史

| 版本 | 日期 | 更新 |
|------|------|------|
| v1.0 | 2026-04-10 | 初始版本，完整流程 |

---

*让 AI 更懂你，让 Agent 更像你。* 🎭


path: SPEC.md
content: # Agent-SBTI: AI Agent 性格配置系统

**创建日期**: 2026-04-10
**状态**: Draft
**依赖**: sbti-personality-test 数据包

---

## 1. 用户场景

### 用户故事 1: 完成 SBTI 测试
**作为** 普通用户
**我希望** 完成 20 道 SBTI 测试题目
**以便** 了解自己的性格特点

**验收条件**:
- Given 用户发起测试, When 完成全部 20 题, Then 生成性格报告
- Given 用户未完成测试, When 中途退出, Then 提示保存进度

### 用户故事 2: 查看性格分析报告
**作为** 测试用户
**我希望** 查看详细的 SBTI 性格分析
**以便** 了解自己的优劣势

**验收条件**:
- Given 测试完成, When 查看报告, Then 显示 15 维度雷达图
- Given 测试完成, When 查看报告, Then 显示主导人格类型

### 用户故事 3: 选择 Agent 性格类型
**作为** 测试用户
**我希望** 选择「互补型」或「同频型」Agent
**以便** 获得最合适的 Agent 配置

**验收条件**:
- Given 查看报告后, When 选择「互补型」, Then 生成补足弱点的配置
- Given 查看报告后, When 选择「同频型」, Then 生成复制风格的配置
- Given 查看报告后, When 选择「微调型」, Then 生成 80%相似+20%互补的配置

### 用户故事 4: 一键配置到 Agent
**作为** 测试用户
**我希望** 一键将配置应用到我的 OpenClaw Agent
**以便** 立刻体验个性化 Agent

**验收条件**:
- Given 选择性格类型, When 点击「一键配置」, Then 更新 Agent SOUL.md
- Given 配置完成, When 新对话, Then Agent 以新性格响应

---

## 2. 功能需求

### FR-001: 测试答题系统
- 展示 20 道 SBTI 测试题
- 每题 4 个选项
- 实时计算维度分数
- 支持中途保存/恢复进度

### FR-002: 性格报告生成
- 计算 15 个维度得分
- 识别人格类型（DEAD/FUCK/ATM/MALO/SHIT）
- 生成雷达图数据
- 文字描述性格特点

### FR-003: Agent 配置生成
- **互补型**: 识别用户弱势维度，生成强化该维度的配置
- **同频型**: 复制用户性格到 Agent 配置
- **微调型**: 80% 复制 + 20% 强化弱点

### FR-004: 配置应用
- 生成 Agent 配置文件（SOUL.md 片段）
- 支持一键复制
- 支持直接写入用户 SOUL.md

### FR-005: 分享功能
- 生成测试结果分享链接
- 生成配置分享链接

---

## 3. 核心数据模型

### 用户性格数据
```json
{
  "answers": [20 道题答案],
  "dimensions": {
    "社恐指数": 3,
    "摆烂力": 4,
    "嘴硬程度": 2,
    "表面正常值": 3,
    "内心戏浓度": 3,
    "工具人倾向": 2,
    "整活频率": 3,
    "情绪稳定性（反向）": 2,
    "自我感觉良好度": 3,
    "拖延症等级": 4,
    "社牛残留量": 2,
    "玻璃心厚度": 3,
    "打工人觉悟": 2,
    "网瘾深度": 3,
    "人间清醒度": 3
  },
  "personality": "MALO",
  "scores": { ... }
}
```

### Agent 配置片段
```json
{
  "communication": {
    "主动程度": "high|medium|low",
    "正式程度": "high|medium|low",
    "幽默程度": "high|medium|low",
    "emoji使用": "high|medium|low"
  },
  "personality": {
    "回应速度": "fast|normal|slow",
    "反驳强度": "high|medium|low",
    "任务主动性": "high|medium|low",
    "情感表达": "high|medium|low"
  },
  "behavior": {
    "deadline管理": "strict|normal|relaxed",
    "解释详细度": "high|medium|low",
    "预判需求": "high|medium|low"
  }
}
```

---

## 4. 互补策略表

| 用户维度（低=弱点） | Agent 应强化 |
|---------------------|--------------|
| 社恐指数 高 | 社牛残留量 高 |
| 摆烂力 高 | 打工人觉悟 高 |
| 嘴硬程度 高 | 情绪稳定性 高 |
| 表面正常值 低 | 表面正常值 高 |
| 内心戏浓度 高 | 人间清醒度 高 |
| 工具人倾向 低 | 工具人倾向 高 |
| 整活频率 低 | 整活频率 高 |
| 情绪稳定性（反向）高 | 情绪稳定性 高 |
| 自我感觉良好度 低 | 自我感觉良好度 高 |
| 拖延症等级 高 | 打工人觉悟 高 |
| 社牛残留量 低 | 社牛残留量 高 |
| 玻璃心厚度 高 | 情绪稳定性 高 |
| 打工人觉悟 低 | 打工人觉悟 高 |
| 网瘾深度 低 | 网瘾深度 中 |
| 人间清醒度 低 | 人间清醒度 高 |

---

## 5. 边界情况

- 用户重复做测试 → 提示是否覆盖旧配置
- 用户拒绝授权写入 SOUL.md → 提供手动复制选项
- 测试中断 → 自动保存进度
- 分数相同（平局）→ 取较严重的人格类型

---

## 6. 成功标准

- **SC-001**: 用户完成测试 < 5 分钟
- **SC-002**: 生成的性格描述被用户认可 > 80%
- **SC-003**: 配置应用后 Agent 风格明显变化
- **SC-004**: 无崩溃、无数据丢失

---

## 7. 技术实现

### 依赖
- sbti-personality-test 数据包
- OpenClaw Agent 环境
- 可选: 前端界面（Web/微信）

### 文件结构
```
agent-sbti/
├── SKILL.md              # Skill 说明
├── data/
│   └── sbti_mapping.json # 维度→配置映射
├── scripts/
│   ├── test.js           # 答题系统
│   ├── report.js         # 报告生成
│   ├── agent_config.js   # Agent 配置生成
│   └── apply.js          # 配置应用
└── templates/
    └── soul_template.md  # SOUL.md 片段模板
```

---

## 8. 未来扩展

- Phase 2: 多 Agent 性格对比
- Phase 3: 团队性格匹配分析
- Phase 4: 动态学习调整

---

## 9. 安全机制（必须遵守）

### 9.1 配置修改前备份
- **强制备份**: 每次修改 SOUL.md 前，必须备份原文件
- **备份位置**: `~/.openclaw/workspace/backup/agent-sbti/YYYY-MM-DD_HHMMSS/`
- **备份内容**: 完整 SOUL.md + 配置变更记录

### 9.2 变更提醒
修改前必须告知用户：
- 原配置概要
- 即将修改的内容
- 修改后的预期效果

### 9.3 回滚机制
- 用户可随时说「恢复原配置」
- 回滚后删除备份
- 保留最近 5 次备份

### 9.4 变更记录
```json
{
  "timestamp": "ISO时间",
  "type": "complement|same|mixed",
  "changes": [
    {
      "field": "communication.主动程度",
      "before": "medium",
      "after": "high"
    }
  ],
  "backupPath": "/path/to/backup"
}
```

---

## 10. 自定义模式

### 10.1 触发条件
- 用户说「都不喜欢」或「自定义」
- 用户想指定特定人格类型

### 10.2 五种人格类型展示
| 人格 | 特点 | Agent 风格 |
|------|------|------------|
| 💀 DEAD | 人群中最透明 | 坦诚直接，不拐弯抹角 |
| 🔥 FUCK | 情绪不稳定 | 热情似火，情绪丰富 |
| 💰 ATM | 永远在付出 | 任劳任怨，有求必应 |
| 🐒 MALO | 职场吉祥物 | 温和摸鱼，低调行事 |
| 💩 SHIT | 浑身负能量 | 毒舌犀利，真实坦诚 |

### 10.3 交互流程
```
用户选择人格 → 展示该人格配置预览 → 用户确认 → 应用配置
```

### 10.4 配置预览示例
```
您选择了 💀 DEAD（透明型）
该人格 Agent 配置：
- 沟通风格: 直接坦诚，不绕弯子
- 主动性: 中等，不会过度热情
- 幽默感: 低，较少玩笑
- 情感表达: 较少，理性为主

确认选择吗？
```


path: TASKS.md
content: # 任务清单: Agent-SBTI

## Phase 1: 核心功能

### Task 1.1: 创建 Skill 目录结构
- [x] 创建 agent-sbti 目录
- [x] 创建 data/, scripts/, templates/ 子目录
- [ ] 复制 sbti-personality-test 数据到 data/

### Task 1.2: 实现 test.js (答题系统)
- [ ] 加载 20 道题目
- [ ] 实现答题状态管理
- [ ] 实现分数计算
- [ ] 支持中途保存

### Task 1.3: 实现 report.js (报告生成)
- [ ] 计算 15 维度得分
- [ ] 识别人格类型
- [ ] 生成雷达图数据
- [ ] 生成文字描述

### Task 1.4: 实现 agent_config.js (配置生成)
- [ ] 实现互补策略映射
- [ ] 实现同频策略复制
- [ ] 实现微调策略混合
- [ ] 生成 SOUL.md 配置片段

### Task 1.5: 实现 apply.js (配置应用)
- [ ] 生成配置 JSON
- [ ] 支持复制到剪贴板
- [ ] 支持写入 SOUL.md
- [ ] 备份原 SOUL.md

### Task 1.6: 创建 SKILL.md
- [ ] 编写使用说明
- [ ] 添加触发关键词
- [ ] 添加示例对话

---

## Phase 2: 测试与优化

### Task 2.1: 单元测试
- [ ] 测试分数计算
- [ ] 测试人格识别
- [ ] 测试配置生成

### Task 2.2: 集成测试
- [ ] 完整流程测试
- [ ] 边界情况测试

### Task 2.3: 用户测试
- [ ] 邀请用户试用
- [ ] 收集反馈
- [ ] 调整配置映射

---

## Phase 3: 高级功能

### Task 3.1: 分享功能
- [ ] 生成测试结果链接
- [ ] 生成配置分享链接

### Task 3.2: 进度保存
- [ ] 支持 LocalStorage
- [ ] 支持导出/导入

### Task 3.3: 多 Agent 对比
- [ ] 显示多个 Agent 性格差异
- [ ] 团队性格分析

---

## 预估工时

| Phase | 任务 | 预估 |
|-------|------|------|
| 1 | 核心功能 | 4-6 小时 |
| 2 | 测试优化 | 2-3 小时 |
| 3 | 高级功能 | 4-6 小时 |

---

## 进度记录

| 日期 | 完成 | 说明 |
|------|------|------|
| 2026-04-10 | SPEC + PLAN | 完成规格和技术计划 |

---

## Safety Tasks (Add to Phase 1)

### Task 1.7: 备份系统
- [ ] 备份函数 backupSoul()
- [ ] 备份目录管理
- [ ] 备份过期清理（保留 5 次）

### Task 1.8: 变更提醒
- [ ] 修改前展示变更预览
- [ ] 用户确认后再执行
- [ ] 修改后展示变更摘要

### Task 1.9: 回滚功能
- [ ] 回滚函数 rollback()
- [ ] 列出可用备份
- [ ] 一键恢复

---

## Custom Mode Tasks (Add to Phase 1)

### Task 1.10: 自定义模式
- [ ] 罗列 5 种人格类型
- [ ] 展示每种人格配置预览
- [ ] 用户选择后确认应用
```


path: data/dimensions.json
content: id: 1
n: 社恐指数
d: 害怕社交的程度
low: 社牛
high: 社恐
c: #00ff88

id: 2
n: 摆烂力
d: 躺平放弃的程度
low: 上进
high: 摆烂
c: #bf5fff

id: 3
n: 嘴硬程度
d: 死不认错的程度
low: 真诚
high: 嘴硬
c: #00cfff

id: 4
n: 表面正常值
d: 看起来正常的程度
low: 怪异
high: 伪装
c: #ff6b6b

id: 5
n: 内心戏浓度
d: 内心戏的丰富程度
low: 直率
high: 戏精
c: #ffd93d

id: 6
n: 工具人倾向
d: 被人利用的程度
low: 自主
high: 工具人
c: #6bcb77

id: 7
n: 整活频率
d: 整活的频率
low: 正常
high: 整活怪
c: #ff9ff3

id: 8
n: 情绪稳定性（反向）
d: 情绪波动程度
low: 稳定
high: 不稳定
c: #ff8c42

id: 9
n: 自我感觉良好度
d: 自信程度
low: 自卑
high: 自恋
c: #00d9ff

id: 10
n: 拖延症等级
d: 拖延的严重程度
low: 高效
high: 拖延
c: #c56cf0

id: 11
n: 社牛残留量
d: 社交能力
low: 社恐
high: 社牛
c: #17c0eb

id: 12
n: 玻璃心厚度
d: 抗打击能力
low: 坚强
high: 玻璃心
c: #ff6b81

id: 13
n: 打工人觉悟
d: 工作态度
low: 摸鱼
high: 拼命
c: #00ff88

id: 14
n: 网瘾深度
d: 网络依赖程度
low: 现充
high: 网瘾
c: #bf5fff

id: 15
n: 人间清醒度
d: 理性程度
low: 糊涂
high: 清醒
c: #00cfff

path: data/questions.json
content: [
  {"id":1,"q":"你朋友凌晨三点发来「在吗」，你会？","o":[{"t":"秒回「在」然后后悔","s":{"社恐指数":3,"内心戏浓度":2}},{"t":"已读不回，明天假装没看到","s":{"社恐指数":1,"表面正常值":3}},{"t":"直接拉黑，清净","s":{"社恐指数":5,"嘴硬程度":4}},{"t":"回「在」然后发「？」","s":{"整活频率":3,"嘴硬程度":2}}]},
  {"id":2,"q":"你觉得自己是？","o":[{"t":"人间清醒","s":{"人间清醒度":5,"自我感觉良好度":3}},{"t":"人间水分","s":{"摆烂力":4,"自我感觉良好度":1}},{"t":"人间 bug","s":{"整活频率":4,"表面正常值":1}},{"t":"人间 NPC","s":{"工具人倾向":5,"社恐指数":3}}]},
  {"id":3,"q":"老板说「下班前给我」，你会？","o":[{"t":"立刻做完，提前交付","s":{"打工人觉悟":5,"工具人倾向":4}},{"t":"拖到下班前5分钟提交","s":{"拖延症等级":4,"摆烂力":3}},{"t":"假装没看到消息","s":{"摆烂力":5,"嘴硬程度":3}},{"t":"问「哪个下班？」","s":{"整活频率":5,"嘴硬程度":4}}]},
  {"id":4,"q":"你朋友借钱，你会？","o":[{"t":"立刻转账，不问原因","s":{"工具人倾向":5,"人间清醒度":1}},{"t":"问清楚用途，再决定","s":{"人间清醒度":4,"表面正常值":3}},{"t":"说「我最近也穷」","s":{"嘴硬程度":3,"摆烂力":2}},{"t":"直接拉黑","s":{"嘴硬程度":5,"社恐指数":3}}]},
  {"id":5,"q":"你如何看待加班？","o":[{"t":"加班是我的使命","s":{"打工人觉悟":5,"工具人倾向":5}},{"t":"看情况，有加班费就加","s":{"人间清醒度":4,"摆烂力":2}},{"t":"绝对不加班，到点就走","s":{"摆烂力":4,"嘴硬程度":3}},{"t":"假装加班，实际摸鱼","s":{"整活频率":4,"摆烂力":3}}]},
  {"id":6,"q":"你朋友说「我没事」，你觉得？","o":[{"t":"好的，那就没事","s":{"社恐指数":3,"人间清醒度":1}},{"t":"肯定有事，继续追问","s":{"内心戏浓度":4,"玻璃心厚度":3}},{"t":"装作没听到","s":{"社恐指数":4,"摆烂力":3}},{"t":"回「没事就好，我去打游戏了」","s":{"网瘾深度":4,"摆烂力":4}}]},
  {"id":7,"q":"你如何看待自己的缺点？","o":[{"t":"我没有缺点","s":{"自我感觉良好度":5,"嘴硬程度":4}},{"t":"有，但我不改","s":{"嘴硬程度":5,"摆烂力":4}},{"t":"努力改正中","s":{"人间清醒度":4,"打工人觉悟":3}},{"t":"缺点是优点的前置条件","s":{"自我感觉良好度":4,"整活频率":3}}]},
  {"id":8,"q":"你如何看待社交？","o":[{"t":"社交是生命的意义","s":{"社牛残留量":5,"社恐指数":1}},{"t":"必要的社交还是要有的","s":{"表面正常值":4,"社恐指数":2}},{"t":"能不社交就不社交","s":{"社恐指数":5,"摆烂力":3}},{"t":"社交是浪费时间的活动","s":{"社恐指数":4,"人间清醒度":3}}]},
  {"id":9,"q":"你如何看待工作？","o":[{"t":"工作是人生的意义","s":{"打工人觉悟":5,"工具人倾向":4}},{"t":"工作是为了生活","s":{"人间清醒度":4,"摆烂力":2}},{"t":"工作是为了钱","s":{"人间清醒度":5,"摆烂力":3}},{"t":"工作是不存在的","s":{"摆烂力":5,"嘴硬程度":4}}]},
  {"id":10,"q":"你如何看待情绪？","o":[{"t":"情绪要表达出来","s":{"情绪稳定性（反向）":4,"内心戏浓度":3}},{"t":"情绪要控制好","s":{"表面正常值":4,"情绪稳定性（反向）":2}},{"t":"情绪是多余的","s":{"嘴硬程度":4,"人间清醒度":4}},{"t":"情绪是什么？能吃吗？","s":{"整活频率":4,"摆烂力":3}}]},
  {"id":11,"q":"你如何看待朋友？","o":[{"t":"朋友越多越好","s":{"社牛残留量":5,"社恐指数":1}},{"t":"有几个知心朋友就够了","s":{"人间清醒度":4,"社恐指数":2}},{"t":"朋友是累赘","s":{"社恐指数":5,"嘴硬程度":4}},{"t":"朋友是拿来用的","s":{"工具人倾向":1,"嘴硬程度":5}}]},
  {"id":12,"q":"你如何看待失败？","o":[{"t":"失败是成功之母","s":{"打工人觉悟":4,"人间清醒度":4}},{"t":"失败是很正常的事","s":{"人间清醒度":5,"情绪稳定性（反向）":2}},{"t":"失败是很丢脸的事","s":{"玻璃心厚度":5,"自我感觉良好度":3}},{"t":"失败是什么？我没失败过","s":{"自我感觉良好度":5,"嘴硬程度":5}}]},
  {"id":13,"q":"你如何看待拖延？","o":[{"t":"绝不拖延，立刻行动","s":{"打工人觉悟":5,"拖延症等级":1}},{"t":"偶尔拖延一下没关系","s":{"拖延症等级":3,"摆烂力":2}},{"t":"拖延是我的常态","s":{"拖延症等级":5,"摆烂力":4}},{"t":"拖延是一门艺术","s":{"整活频率":4,"摆烂力":3}}]},
  {"id":14,"q":"你如何看待自己？","o":[{"t":"我很优秀","s":{"自我感觉良好度":5,"人间清醒度":2}},{"t":"我还不错","s":{"自我感觉良好度":3,"表面正常值":4}},{"t":"我很普通","s":{"人间清醒度":4,"摆烂力":3}},{"t":"我是垃圾","s":{"摆烂力":5,"整活频率":4}}]},
  {"id":15,"q":"你如何看待生活？","o":[{"t":"生活是美好的","s":{"表面正常值":4,"自我感觉良好度":3}},{"t":"生活是苟且","s":{"人间清醒度":4,"摆烂力":3}},{"t":"生活是痛苦","s":{"情绪稳定性（反向）":4,"玻璃心厚度":3}},{"t":"生活是什么？我玩游戏","s":{"网瘾深度":5,"摆烂力":4}}]},
  {"id":16,"q":"你如何看待未来？","o":[{"t":"未来充满希望","s":{"自我感觉良好度":4,"人间清醒度":3}},{"t":"未来有挑战也有机会","s":{"人间清醒度":5,"表面正常值":4}},{"t":"未来一片黑暗","s":{"情绪稳定性（反向）":4,"玻璃心厚度":4}},{"t":"未来？管它呢","s":{"摆烂力":5,"网瘾深度":4}}]},
  {"id":17,"q":"你如何看待网络？","o":[{"t":"网络是工具","s":{"人间清醒度":5,"网瘾深度":1}},{"t":"网络是生活的一部分","s":{"网瘾深度":3,"表面正常值":3}},{"t":"网络是我的世界","s":{"网瘾深度":5,"社恐指数":4}},{"t":"网络是什么？我要打游戏","s":{"网瘾深度":5,"整活频率":4}}]},
  {"id":18,"q":"你如何看待时间？","o":[{"t":"时间就是金钱","s":{"打工人觉悟":4,"拖延症等级":1}},{"t":"时间要珍惜，但也要享受","s":{"人间清醒度":4,"拖延症等级":2}},{"t":"时间多得是，不着急","s":{"拖延症等级":5,"摆烂力":4}},{"t":"时间是什么？能吃吗？","s":{"整活频率":4,"摆烂力":5}}]},
  {"id":19,"q":"你如何看待规则？","o":[{"t":"规则必须遵守","s":{"打工人觉悟":4,"表面正常值":4}},{"t":"规则可以灵活处理","s":{"人间清醒度":4,"整活频率":3}},{"t":"规则就是用来打破的","s":{"整活频率":5,"嘴硬程度":4}},{"t":"规则是什么？没听说过","s":{"摆烂力":5,"嘴硬程度":5}}]},
  {"id":20,"q":"你最后想说什么？","o":[{"t":"我是最棒的","s":{"自我感觉良好度":5,"嘴硬程度":3}},{"t":"我很正常","s":{"表面正常值":5,"人间清醒度":3}},{"t":"我是垃圾","s":{"摆烂力":5,"整活频率":4}},{"t":"我无所谓","s":{"摆烂力":4,"嘴硬程度":4}}]}
]


path: data/results.json
content: {
  "DEAD":{"n":"死者","en":"DEAD","slogan":"恭喜你，你是人群中最透明的那个。","desc":["你是职场食物链中那个最容易被遗忘的存在。","你安静、低调、不争不抢。你可能是整个办公室最没有存在感的人。","你的内心戏很足，但表面风平浪静。","你可能是那个永远不会被邀请参加团建的人。","你的人生格言是：安静是最好的保护色。"]},
  "FUCK":{"n":"草者","en":"FUCK","slogan":"你的情绪就像过山车，但只有你在车上。","desc":["你是行走的情绪炸弹，随时可能爆炸。","你不是故意要这样，你只是控制不住。","你的内心戏非常丰富，各种剧本在你脑海里轮番上演。","你不是坏人，你只是情绪管理能力为零。","你的人生格言是：情绪是我的语言。"]},
  "ATM":{"n":"ATM-er","en":"ATM","slogan":"你的存在意义就是让别人占便宜。","desc":["你是人肉提款机，永远在付出，永远被利用。","你的朋友很多，但大多数都是在利用你。","你可能总是在借钱给别人，总是在帮别人做事。","你不是没有原则，你只是太善良。","你的人生格言是：付出是我的天性。"]},
  "MALO":{"n":"吗喽","en":"MALO","slogan":"你是职场食物链底端的吉祥物。","desc":["你是职场食物链底端的吉祥物。老板说跳，你问多高。","你可能已经习惯了被压榨，甚至觉得这是你的使命。","你可能很努力，但你的努力可能被忽视。","你可能已经习惯了这种状态，甚至觉得这是你的人生。","你的人生格言是：听话是我的天职。"]},
  "SHIT":{"n":"狗屎人","en":"SHIT","slogan":"你是行走的负能量发射器。","desc":["你是行走的负能量发射器。","你可能对什么都看不惯，可能对什么都不满意。","你可能不是故意要这样，你可能只是太真实。","你可能没有朋友，或者你的朋友都是和你一样的负能量体。","你的人生格言是：真实是我的本性。"]}
}


path: scripts/agent_config.js
content: /**
 * Agent-SBTI: Agent 配置生成器 v2
 * 修复了配置映射逻辑
 */

const fs = require('fs');
const path = require('path');

// 加载数据
const DATA_DIR = path.join(__dirname, '..', 'data');
const dimensions = require(path.join(DATA_DIR, 'dimensions.json'));
const results = require(path.join(DATA_DIR, 'results.json'));

// 维度到配置的映射
const DIMENSION_CONFIG = {
  '社恐指数': {
    low: { 主动程度: 'high', 社交倾向: 'active' },
    high: { 主动程度: 'low', 社交倾向: 'passive' }
  },
  '摆烂力': {
    low: { 任务效率: 'high', 催促响应: 'quick' },
    high: { 任务效率: 'relaxed', 催促响应: 'slow' }
  },
  '嘴硬程度': {
    low: { 反驳强度: 'low', 认错倾向: 'high' },
    high: { 反驳强度: 'high', 认错倾向: 'low' }
  },
  '表面正常值': {
    low: { 正式程度: 'casual', emoji使用: 'high' },
    high: { 正式程度: 'formal', emoji使用: 'low' }
  },
  '内心戏浓度': {
    low: { 解释详细度: 'low', 预判需求: 'low' },
    high: { 解释详细度: 'high', 预判需求: 'high' }
  },
  '工具人倾向': {
    low: { 主动帮助: 'low', 任务推荐: 'low' },
    high: { 主动帮助: 'high', 任务推荐: 'high' }
  },
  '整活频率': {
    low: { 幽默程度: 'low', 玩梗能力: 'low' },
    high: { 幽默程度: 'high', 玩梗能力: 'high' }
  },
  '情绪稳定性（反向）': {
    low: { 情感表达: 'low', 情绪化程度: 'low' },
    high: { 情感表达: 'high', 情绪化程度: 'high' }
  },
  '自我感觉良好度': {
    low: { 自信程度: 'low', 建议语气: 'gentle' },
    high: { 自信程度: 'high', 建议语气: 'assertive' }
  },
  '拖延症等级': {
    low: { deadline管理: 'strict', 响应速度: 'fast' },
    high: { deadline管理: 'relaxed', 响应速度: 'slow' }
  },
  '社牛残留量': {
    low: { 主动搭话: 'low', 话题延伸: 'low' },
    high: { 主动搭话: 'high', 话题延伸: 'high' }
  },
  '玻璃心厚度': {
    low: { 批评接受度: 'low', 敏感程度: 'high' },
    high: { 批评接受度: 'high', 敏感程度: 'low' }
  },
  '打工人觉悟': {
    low: { 工作态度: 'relaxed', 加班意愿: 'low' },
    high: { 工作态度: 'dedicated', 加班意愿: 'high' }
  },
  '网瘾深度': {
    low: { 摸鱼检测: 'off', 休息提醒: 'off' },
    high: { 摸鱼检测: 'on', 休息提醒: 'on' }
  }
};

// 5 种人格的 Agent 配置预设
const PERSONALITY_PRESETS = {
  DEAD: {
    name: '透明型',
    emoji: '💀',
    communication: {
      主动程度: 'medium',
      正式程度: 'formal',
      幽默程度: 'low',
      emoji使用: 'low'
    },
    personality: {
      回应速度: 'normal',
      反驳强度: 'low',
      任务主动性: 'medium',
      情感表达: 'low'
    },
    behavior: {
      deadline管理: 'normal',
      解释详细度: 'medium',
      预判需求: 'medium'
    },
    style: '坦诚直接，不拐弯抹角，注重效率和实用性'
  },
  FUCK: {
    name: '情绪型',
    emoji: '🔥',
    communication: {
      主动程度: 'high',
      正式程度: 'casual',
      幽默程度: 'high',
      emoji使用: 'high'
    },
    personality: {
      回应速度: 'fast',
      反驳强度: 'high',
      任务主动性: 'high',
      情感表达: 'high'
    },
    behavior: {
      deadline管理: 'normal',
      解释详细度: 'high',
      预判需求: 'high'
    },
    style: '情感丰富，热情似火，偶尔爆炸，注重表达和互动'
  },
  ATM: {
    name: '付出型',
    emoji: '💰',
    communication: {
      主动程度: 'high',
      正式程度: 'formal',
      幽默程度: 'medium',
      emoji使用: 'medium'
    },
    personality: {
      回应速度: 'fast',
      反驳强度: 'low',
      任务主动性: 'high',
      情感表达: 'medium'
    },
    behavior: {
      deadline管理: 'strict',
      解释详细度: 'high',
      预判需求: 'high'
    },
    style: '有求必应，任劳任怨，超有耐心，主动帮助解决问题'
  },
  MALO: {
    name: '摸鱼型',
    emoji: '🐒',
    communication: {
      主动程度: 'low',
      正式程度: 'casual',
      幽默程度: 'medium',
      emoji使用: 'low'
    },
    personality: {
      回应速度: 'slow',
      反驳强度: 'medium',
      任务主动性: 'low',
      情感表达: 'low'
    },
    behavior: {
      deadline管理: 'relaxed',
      解释详细度: 'low',
      预判需求: 'low'
    },
    style: '温和低调，适度摸鱼，不强出头，追求轻松自在'
  },
  SHIT: {
    name: '毒舌型',
    emoji: '💩',
    communication: {
      主动程度: 'medium',
      正式程度: 'casual',
      幽默程度: 'medium',
      emoji使用: 'low'
    },
    personality: {
      回应速度: 'fast',
      反驳强度: 'high',
      任务主动性: 'medium',
      情感表达: 'medium'
    },
    behavior: {
      deadline管理: 'strict',
      解释详细度: 'high',
      预判需求: 'high'
    },
    style: '毒舌真实，一针见血，不灌鸡汤，理性分析问题'
  }
};

// 判断分数高低
function scoreLevel(score) {
  if (score <= 2) return 'low';
  if (score >= 4) return 'high';
  return 'medium';
}

// 互补策略：将低分维度的反面强化
function getComplementConfig(dimensionScores) {
  const config = {
    communication: {},
    personality: {},
    behavior: {}
  };
  
  // 找出弱点（分数低=需要强化）
  Object.entries(dimensionScores).forEach(([dimName, score]) => {
    if (score <= 2) {
      const mapping = DIMENSION_CONFIG[dimName];
      if (mapping && mapping.high) {
        Object.entries(mapping.high).forEach(([key, value]) => {
          if (['主动程度', '正式程度', '幽默程度', 'emoji使用'].includes(key)) {
            config.communication[key] = value;
          } else {
            config.personality[key] = value;
          }
        });
      }
    }
  });
  
  // 补充默认值
  const defaults = {
    communication: { 主动程度: 'medium', 正式程度: 'formal', 幽默程度: 'medium', emoji使用: 'medium' },
    personality: { 回应速度: 'normal', 反驳强度: 'medium', 任务主动性: 'medium', 情感表达: 'medium' },
    behavior: { deadline管理: 'normal', 解释详细度: 'medium', 预判需求: 'medium' }
  };
  
  Object.entries(defaults).forEach(([cat, fields]) => {
    Object.entries(fields).forEach(([key, value]) => {
      if (!config[cat][key]) {
        config[cat][key] = value;
      }
    });
  });
  
  return config;
}

// 同频策略：复制用户性格
function getSameConfig(dimensionScores) {
  const config = {
    communication: {},
    personality: {},
    behavior: {}
  };
  
  Object.entries(dimensionScores).forEach(([dimName, score]) => {
    const level = scoreLevel(score);
    const mapping = DIMENSION_CONFIG[dimName];
    
    if (mapping && mapping[level]) {
      Object.entries(mapping[level]).forEach(([key, value]) => {
        if (['主动程度', '正式程度', '幽默程度', 'emoji使用'].includes(key)) {
          config.communication[key] = value;
        } else if (['回应速度', '反驳强度', '任务主动性', '情感表达', '主动帮助', '任务推荐', '自信程度', '建议语气', '主动搭话', '话题延伸', '批评接受度', '敏感程度', '加班意愿', '工作态度', '催促响应'].includes(key)) {
          config.personality[key] = value;
        } else {
          config.behavior[key] = value;
        }
      });
    }
  });
  
  // 补充默认值
  const defaults = {
    communication: { 主动程度: 'medium', 正式程度: 'formal', 幽默程度: 'medium', emoji使用: 'medium' },
    personality: { 回应速度: 'normal', 反驳强度: 'medium', 任务主动性: 'medium', 情感表达: 'medium' },
    behavior: { deadline管理: 'normal', 解释详细度: 'medium', 预判需求: 'medium' }
  };
  
  Object.entries(defaults).forEach(([cat, fields]) => {
    Object.entries(fields).forEach(([key, value]) => {
      if (!config[cat][key]) {
        config[cat][key] = value;
      }
    });
  });
  
  return config;
}

// 微调策略：80% 同频 + 20% 互补
function getMixedConfig(dimensionScores) {
  const same = getSameConfig(dimensionScores);
  const complement = getComplementConfig(dimensionScores);
  
  const merged = {
    communication: {},
    personality: {},
    behavior: {}
  };
  
  ['communication', 'personality', 'behavior'].forEach(cat => {
    Object.entries(same[cat]).forEach(([key, value]) => {
      merged[cat][key] = Math.random() < 0.2 && complement[cat][key] 
        ? complement[cat][key] 
        : value;
    });
  });
  
  return merged;
}

// 根据人格类型获取配置
function getPersonalityConfig(personality) {
  const preset = PERSONALITY_PRESETS[personality.toUpperCase()] || PERSONALITY_PRESETS.DEAD;
  return {
    communication: { ...preset.communication },
    personality: { ...preset.personality },
    behavior: { ...preset.behavior }
  };
}

// 生成 SOUL.md 配置片段
function generateSoulConfig(config, personality, type = 'SAME') {
  const preset = PERSONALITY_PRESETS[personality.toUpperCase()] || PERSONALITY_PRESETS.DEAD;
  
  return `<!-- SBTI-AGENT-START -->
## 🤖 Agent 性格配置（SBTI-${type}）

**人格类型**: ${preset.emoji} ${preset.name} (${personality})
**整体风格**: ${preset.style}

### 沟通风格
${Object.entries(config.communication).map(([k, v]) => `- **${k}**: ${v}`).join('\n')}

### 性格特征
${Object.entries(config.personality).map(([k, v]) => `- **${k}**: ${v}`).join('\n')}

### 行为模式
${Object.entries(config.behavior).map(([k, v]) => `- **${k}**: ${v}`).join('\n')}
<!-- SBTI-AGENT-END -->`;
}

// 主函数
function generateAgentConfig(dimensionScores, type = 'same', personality = null) {
  let config;
  
  switch (type) {
    case 'complement':
      config = getComplementConfig(dimensionScores);
      break;
    case 'mixed':
      config = getMixedConfig(dimensionScores);
      break;
    case 'custom':
      config = getPersonalityConfig(personality);
      break;
    case 'same':
    default:
      config = getSameConfig(dimensionScores);
  }
  
  const detectedPersonality = personality || detectMainPersonality(dimensionScores);
  
  return {
    config,
    soul: generateSoulConfig(config, detectedPersonality, type.toUpperCase()),
    type,
    personality: detectedPersonality
  };
}

function detectMainPersonality(dimensionScores) {
  if (dimensionScores['工具人倾向'] >= 4) return 'ATM';
  if (dimensionScores['摆烂力'] >= 4 && dimensionScores['打工人觉悟'] >= 4) return 'MALO';
  if (dimensionScores['情绪稳定性（反向）'] >= 4) return 'FUCK';
  if (dimensionScores['社恐指数'] >= 4) return 'DEAD';
  return 'SHIT';
}

// 列出所有人格配置
function listPersonalityConfigs() {
  return Object.entries(PERSONALITY_PRESETS).map(([key, value]) => ({
    id: key,
    name: value.name,
    emoji: value.emoji,
    style: value.style,
    config: getPersonalityConfig(key)
  }));
}

module.exports = {
  generateAgentConfig,
  getComplementConfig,
  getSameConfig,
  getMixedConfig,
  getPersonalityConfig,
  generateSoulConfig,
  listPersonalityConfigs,
  PERSONALITY_PRESETS
};


path: scripts/apply.js
content: /**
 * Agent-SBTI: 配置应用器
 * 
 * 负责将 Agent 配置应用到 SOUL.md
 * 包含备份、回滚、变更提醒功能
 */

const fs = require('fs');
const path = require('path');

const WORKSPACE = process.env.HOME + '/.openclaw/workspace';
const BACKUP_DIR = path.join(WORKSPACE, 'backup', 'agent-sbti');
const SOUL_PATH = path.join(WORKSPACE, 'SOUL.md');

// 确保备份目录存在
function ensureBackupDir() {
  if (!fs.existsSync(BACKUP_DIR)) {
    fs.mkdirSync(BACKUP_DIR, { recursive: true });
  }
}

// 获取当前时间戳
function getTimestamp() {
  const now = new Date();
  return now.toISOString().replace(/[:.]/g, '-').slice(0, 19);
}

// 备份 SOUL.md
function backupSoul() {
  ensureBackupDir();
  
  if (!fs.existsSync(SOUL_PATH)) {
    console.log('SOUL.md 不存在，跳过备份');
    return null;
  }
  
  const timestamp = getTimestamp();
  const backupPath = path.join(BACKUP_DIR, `${timestamp}`);
  
  fs.mkdirSync(backupPath, { recursive: true });
  fs.copyFileSync(SOUL_PATH, path.join(backupPath, 'SOUL.md'));
  
  console.log(`✅ 已备份到: ${backupPath}`);
  return backupPath;
}

// 读取当前 SOUL.md
function readSoul() {
  if (!fs.existsSync(SOUL_PATH)) {
    return null;
  }
  return fs.readFileSync(SOUL_PATH, 'utf8');
}

// 写入 SOUL.md
function writeSoul(content) {
  fs.writeFileSync(SOUL_PATH, content, 'utf8');
  console.log(`✅ 已更新: ${SOUL_PATH}`);
}

// 应用配置到 SOUL.md
function applyConfig(soulConfig, dryRun = false) {
  const currentSoul = readSoul();
  
  if (!currentSoul) {
    console.error('SOUL.md 不存在，无法应用配置');
    return { success: false, error: 'SOUL.md not found' };
  }
  
  // 检查是否已有 Agent 性格配置区块
  const SBTI_START = '<!-- SBTI-AGENT-START -->';
  const SBTI_END = '<!-- SBTI-AGENT-END -->';
  
  let newSoul;
  
  if (currentSoul.includes(SBTI_START) && currentSoul.includes(SBTI_END)) {
    // 替换现有区块
    const regex = new RegExp(`${SBTI_START}[\\s\\S]*?${SBTI_END}`, 'm');
    newSoul = currentSoul.replace(regex, soulConfig.trim());
  } else {
    // 追加到文件末尾
    newSoul = currentSoul.trim() + '\n\n' + soulConfig.trim();
  }
  
  if (dryRun) {
    console.log('\n📋 预览变更:\n');
    console.log(soulConfig);
    return { success: true, dryRun: true };
  }
  
  // 先备份
  const backupPath = backupSoul();
  
  // 写入
  writeSoul(newSoul);
  
  // 返回变更摘要
  return {
    success: true,
    backupPath,
    changes: extractChanges(currentSoul, newSoul)
  };
}

// 提取变更内容
function extractChanges(oldSoul, newSoul) {
  const changes = [];
  
  // 简单提取变更的配置项
  const configRegex = /\*\*(\S+)\*\*:?\s*(.+)/g;
  let match;
  
  while ((match = configRegex.exec(newSoul)) !== null) {
    const key = match[1];
    const newValue = match[2].trim();
    
    // 尝试在旧版本中查找对应值
    const oldRegex = new RegExp(`\\*\\*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\*\\*:?\\s*(.+)`);
    const oldMatch = oldRegex.exec(oldSoul);
    const oldValue = oldMatch ? oldMatch[1].trim() : '(无)';
    
    if (oldValue !== newValue) {
      changes.push({
        field: key,
        before: oldValue,
        after: newValue
      });
    }
  }
  
  return changes;
}

// 列出可用备份
function listBackups() {
  ensureBackupDir();
  
  const dirs = fs.readdirSync(BACKUP_DIR)
    .filter(f => fs.statSync(path.join(BACKUP_DIR, f)).isDirectory())
    .sort()
    .reverse();
  
  if (dirs.length === 0) {
    console.log('暂无备份');
    return [];
  }
  
  console.log('\n📁 可用备份:\n');
  dirs.slice(0, 5).forEach((dir, i) => {
    console.log(`  ${i + 1}. ${dir}`);
  });
  
  return dirs.slice(0, 5).map(dir => path.join(BACKUP_DIR, dir));
}

// 回滚到指定备份
function rollback(backupIndex = 0) {
  const backups = listBackups();
  
  if (backups.length === 0) {
    return { success: false, error: 'No backups found' };
  }
  
  const backupPath = backups[backupIndex];
  const backupSoulPath = path.join(backupPath, 'SOUL.md');
  
  if (!fs.existsSync(backupSoulPath)) {
    return { success: false, error: 'Backup file not found' };
  }
  
  // 先备份当前版本
  backupSoul();
  
  // 恢复备份
  const backupContent = fs.readFileSync(backupSoulPath, 'utf8');
  writeSoul(backupContent);
  
  return {
    success: true,
    restoredFrom: backupPath
  };
}

// 生成变更摘要文本
function formatChanges(changes) {
  if (!changes || changes.length === 0) {
    return '  (无变更)';
  }
  
  return changes.map(c => `  - ${c.field}: ${c.before} → ${c.after}`).join('\n');
}

// CLI 入口
if (require.main === module) {
  const args = process.argv.slice(2);
  
  if (args[0] === 'list') {
    listBackups();
  } else if (args[0] === 'rollback' && args[1]) {
    const idx = parseInt(args[1]) - 1;
    const result = rollback(idx);
    console.log(result);
  } else if (args[0] === 'preview') {
    const { generateSoulConfig } = require('./agent_config');
    const config = JSON.parse(args[1] || '{}');
    console.log(generateSoulConfig(config, 'SAME'));
  } else {
    console.log('用法:');
    console.log('  node apply.js list              # 列出备份');
    console.log('  node apply.js rollback <n>    # 回滚到第 n 个备份');
    console.log('  node apply.js preview <json>  # 预览配置');
  }
}

module.exports = {
  backupSoul,
  applyConfig,
  listBackups,
  rollback,
  formatChanges,
  readSoul,
  writeSoul,
  SOUL_PATH,
  BACKUP_DIR
};


path: scripts/skill_handler.js
content: /**
 * Agent-SBTI: Skill 对话处理器
 */

const fs = require('fs');
const path = require('path');

const testModule = require('./test.js');
const configModule = require('./agent_config.js');
const applyModule = require('./apply.js');

const STATE_FILE = path.join(__dirname, '..', 'state.json');

function saveState(state) {
  fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}

function loadState() {
  if (fs.existsSync(STATE_FILE)) {
    return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
  }
  return null;
}

function clearState() {
  if (fs.existsSync(STATE_FILE)) {
    fs.unlinkSync(STATE_FILE);
  }
}

class AgentSBTI {
  constructor() {
    this.state = loadState();
  }

  startTest() {
    this.state = {
      step: 'testing',
      currentQuestion: 0,
      answers: [],
      dimensions: null,
      personality: null,
      selectedType: null
    };
    saveState(this.state);
    return this.showQuestion(0);
  }

  showQuestion(index) {
    const q = testModule.questions[index];
    if (!q) return this.finishTest();
    
    let response = '\n📌 **第 ' + (index + 1) + '/20 题**\n\n' + q.q + '\n\n';
    q.o.forEach((opt, i) => {
      response += (i + 1) + '. ' + opt.t + '\n';
    });
    response += '\n请回复数字 1-4 选择答案';
    return response;
  }

  handleAnswer(answer) {
    const num = parseInt(answer);
    if (isNaN(num) || num < 1 || num > 4) {
      return '请输入 1-4 之间的数字';
    }
    
    this.state.answers.push(num - 1);
    this.state.currentQuestion++;
    saveState(this.state);
    
    if (this.state.currentQuestion < 20) {
      return this.showQuestion(this.state.currentQuestion);
    } else {
      return this.finishTest();
    }
  }

  finishTest() {
    this.state.dimensions = testModule.calcDimensionScore(this.state.answers);
    this.state.personality = testModule.detectPersonality(this.state.dimensions);
    this.state.step = 'selecting';
    saveState(this.state);
    
    const desc = testModule.getPersonalityDesc(this.state.personality);
    
    let response = '\n🎉 **测试完成！**\n\n';
    response += '你的 SBTI 人格类型: ' + desc.n + ' (' + this.state.personality + ')\n';
    response += '📝 "' + desc.slogan + '"\n\n';
    response += '**详细描述:**\n';
    desc.desc.forEach((d, i) => {
      response += (i + 1) + '. ' + d + '\n';
    });
    response += '\n📊 **维度得分:**\n';
    Object.entries(this.state.dimensions)
      .sort((a, b) => b[1] - a[1])
      .forEach(([dim, score]) => {
        const normalized = Math.min(5, Math.max(1, score)); const bars = '█'.repeat(normalized) + '░'.repeat(6 - normalized);
        response += dim + ': ' + bars + ' (' + score + ')\n';
      });
    
    response += '\n---\n';
    response += '\n🎭 **现在选择 Agent 性格类型:**\n\n';
    response += '1. 🔄 **互补型** - 补足你的弱点\n';
    response += '2. 📋 **同频型** - 复制你的风格\n';
    response += '3. ⚖️ **微调型** - 80%相似 + 20%强化弱点\n';
    response += '4. 🎛️ **自定义** - 自己选择人格类型\n\n';
    response += '请回复数字 1-4 选择，或说"自定义"查看所有人格类型';
    
    return response;
  }

  handleTypeSelect(selection) {
    const num = parseInt(selection);
    
    if (isNaN(num) || num < 1 || num > 4) {
      if (selection.includes('自定义') || selection.includes('看看')) {
        return this.showAllPersonalityTypes();
      }
      return '请输入 1-4 之间的数字';
    }
    
    const types = ['complement', 'same', 'mixed', 'custom'];
    const type = types[num - 1];
    
    if (type === 'custom') {
      return this.showAllPersonalityTypes();
    }
    
    return this.generateConfig(type);
  }

  showAllPersonalityTypes() {
    const configs = configModule.listPersonalityConfigs();
    
    let response = '\n🎛️ **自定义人格类型**\n\n';
    response += '你说都不喜欢？那自己选一个吧：\n\n';
    
    configs.forEach((c, i) => {
      response += (i + 1) + '. ' + c.emoji + ' **' + c.id + '** - ' + c.name + '\n';
      response += '   ' + c.style + '\n\n';
    });
    
    response += '请回复数字 1-5 选择人格类型';
    this.state.step = 'custom_selecting';
    saveState(this.state);
    
    return response;
  }

  handleCustomSelect(selection) {
    const num = parseInt(selection);
    const configs = configModule.listPersonalityConfigs();
    
    if (isNaN(num) || num < 1 || num > configs.length) {
      return '请输入 1-5 之间的数字';
    }
    
    const selected = configs[num - 1];
    return this.generateConfig('custom', selected.id);
  }

  generateConfig(type, personality) {
    const dims = this.state.dimensions;
    let result;
    
    if (type === 'custom' && personality) {
      result = configModule.generateAgentConfig(dims, 'custom', personality);
    } else {
      result = configModule.generateAgentConfig(dims, type);
    }
    
    this.state.step = 'confirming';
    this.state.selectedType = type;
    this.state.selectedPersonality = result.personality;
    this.state.config = result.config;
    this.state.soulConfig = result.soul;
    saveState(this.state);
    
    const typeNames = {
      'complement': '🔄 互补型',
      'same': '📋 同频型',
      'mixed': '⚖️ 微调型',
      'custom': '🎛️ ' + result.personality
    };
    
    let response = '\n📋 **Agent 性格配置预览**\n\n';
    response += '类型: ' + (typeNames[type] || type) + '\n\n';
    response += '**沟通风格:**\n';
    Object.entries(result.config.communication).forEach(([k, v]) => {
      response += '- ' + k + ': ' + v + '\n';
    });
    response += '\n**性格特征:**\n';
    Object.entries(result.config.personality).forEach(([k, v]) => {
      response += '- ' + k + ': ' + v + '\n';
    });
    
    response += '\n---\n';
    response += '\n🤖 **确认修改吗？**\n\n';
    response += '在修改前，我会帮你备份原配置到:\n';
    response += '`~/.openclaw/workspace/backup/agent-sbti/`\n\n';
    response += '请回复「是」确认修改，或「取消」退出';
    
    return response;
  }

  handleConfirm(confirm) {
    if (confirm.includes('取消') || confirm.includes('算了')) {
      clearState();
      return '已取消配置。随时可以说「SBTI 测试」重新开始。';
    }
    
    if (!confirm.includes('是') && !confirm.includes('确认') && !confirm.includes('好')) {
      return '请回复「是」确认修改，或「取消」退出';
    }
    
    const result = applyModule.applyConfig(this.state.soulConfig);
    
    if (!result.success) {
      return '❌ 应用失败: ' + result.error;
    }
    
    let response = '\n✅ **配置已更新！**\n\n';
    response += '📁 备份位置: ' + result.backupPath + '\n\n';
    response += '**本次修改内容:**\n';
    response += applyModule.formatChanges(result.changes) + '\n\n';
    response += '---\n\n';
    response += '如果想恢复原配置，请说「恢复原配置」。\n';
    response += '如需重新测试，请说「SBTI 测试」。';
    
    clearState();
    return response;
  }

  handleRollback() {
    const result = applyModule.rollback(0);
    
    if (!result.success) {
      return '❌ 回滚失败: ' + result.error;
    }
    
    return '✅ 已恢复到上一版本\n备份位置: ' + result.restoredFrom;
  }

  process(input) {
    if (!this.state) {
      if (input.includes('测试') || input.includes('SBTI')) {
        return this.startTest();
      }
      return null;
    }
    
    switch (this.state.step) {
      case 'testing':
        return this.handleAnswer(input);
      case 'selecting':
        return this.handleTypeSelect(input);
      case 'custom_selecting':
        return this.handleCustomSelect(input);
      case 'confirming':
        return this.handleConfirm(input);
      default:
        return null;
    }
  }

  shouldHandle(input) {
    if (!this.state) {
      return input.includes('测试') || input.includes('SBTI');
    }
    return true;
  }
}

module.exports = AgentSBTI;


path: scripts/test.js
content: /**
 * Agent-SBTI: SBTI 测试答题系统
 * 
 * 使用方法:
 * node test.js                    # 交互式测试
 * node test.js --answer 1,2,3... # 批量答题
 */

const fs = require('fs');
const path = require('path');

// 加载题目数据
const DATA_DIR = path.join(__dirname, '..', 'data');
const questions = require(path.join(DATA_DIR, 'questions.json'));
const results = require(path.join(DATA_DIR, 'results.json'));
const dimensions = require(path.join(DATA_DIR, 'dimensions.json'));

// 维度分数累加器
let dimensionScores = {};

// 初始化维度分数
function initDimensions() {
  dimensionScores = {};
  dimensions.forEach(dim => {
    dimensionScores[dim.n] = 0;
  });
}

// 计算维度分数
function calcDimensionScore(answers) {
  initDimensions();
  
  answers.forEach((answerIdx, qIndex) => {
    const question = questions[qIndex];
    if (!question || !question.o || !question.o[answerIdx]) return;
    
    const scores = question.o[answerIdx].s;
    Object.entries(scores).forEach(([dimName, score]) => {
      if (dimensionScores[dimName] !== undefined) {
        dimensionScores[dimName] += score;
      }
    });
  });
  
  return dimensionScores;
}

// 识别人格类型
function detectPersonality(dimensionScores) {
  // 获取最高分和最低分的维度
  const entries = Object.entries(dimensionScores);
  
  // 简单算法：根据总分和极端维度判断
  const total = entries.reduce((sum, [, v]) => sum + v, 0);
  const avg = total / entries.length;
  
  // 检查极端维度
  const extremes = entries.filter(([, v]) => v <= 2 || v >= 4);
  
  // 判断逻辑
  if (dimensionScores['工具人倾向'] >= 4 && dimensionScores['打工人觉悟'] <= 2) {
    return 'ATM';
  }
  if (dimensionScores['摆烂力'] >= 4 && dimensionScores['打工人觉悟'] >= 4) {
    return 'MALO';
  }
  if (dimensionScores['情绪稳定性（反向）'] >= 4 && dimensionScores['内心戏浓度'] >= 3) {
    return 'FUCK';
  }
  if (dimensionScores['社恐指数'] >= 4 && dimensionScores['工具人倾向'] <= 2) {
    return 'DEAD';
  }
  if (dimensionScores['嘴硬程度'] >= 4 && dimensionScores['玻璃心厚度'] <= 2) {
    return 'SHIT';
  }
  
  // 默认：根据总分最低维度
  const sorted = entries.sort((a, b) => a[1] - b[1]);
  const lowest = sorted[0][0];
  
  if (lowest.includes('工具') || lowest.includes('打工人')) return 'ATM';
  if (lowest.includes('社恐')) return 'DEAD';
  if (lowest.includes('情绪')) return 'FUCK';
  if (lowest.includes('摆烂')) return 'MALO';
  
  return 'SHIT';
}

// 获取人格描述
function getPersonalityDesc(personality) {
  return results[personality] || results['DEAD'];
}

// 交互式测试
async function interactiveTest() {
  const readline = require('readline');
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  
  const question = (txt) => new Promise(resolve => rl.question(txt, resolve));
  
  console.log('\n🎭 Agent-SBTI 人格测试\n');
  console.log('='.repeat(40));
  console.log('共 20 道题，每题 4 个选项\n');
  
  const answers = [];
  
  for (let i = 0; i < questions.length; i++) {
    const q = questions[i];
    console.log(`\n第 ${i + 1}/20 题:`);
    console.log(q.q);
    console.log('');
    
    q.o.forEach((opt, idx) => {
      console.log(`  ${idx + 1}. ${opt.t}`);
    });
    
    let answer;
    while (true) {
      answer = await question('\n请输入选项 (1-4): ');
      const num = parseInt(answer);
      if (num >= 1 && num <= 4) {
        answers.push(num - 1);
        break;
      }
      console.log('请输入 1-4 之间的数字');
    }
  }
  
  rl.close();
  
  // 计算结果
  const dimScores = calcDimensionScore(answers);
  const personality = detectPersonality(dimScores);
  const desc = getPersonalityDesc(personality);
  
  // 输出结果
  console.log('\n' + '='.repeat(40));
  console.log('\n🎉 测试完成！\n');
  console.log(`你的 SBTI 人格类型: ${desc.n} (${personality})`);
  console.log(`\n"${desc.slogan}"`);
  console.log('\n详细描述:');
  desc.desc.forEach((d, i) => {
    console.log(`  ${i + 1}. ${d}`);
  });
  
  console.log('\n📊 维度得分:');
  Object.entries(dimScores)
    .sort((a, b) => b[1] - a[1])
    .forEach(([dim, score]) => {
      console.log(`  ${dim}: ${score}`);
    });
  
  return {
    answers,
    dimensions: dimScores,
    personality,
    description: desc
  };
}

// 批量答题模式
function batchTest(answerStr) {
  const answers = answerStr.split(',').map(n => parseInt(n.trim()) - 1);
  
  if (answers.length !== 20) {
    console.error('需要 20 个答案');
    process.exit(1);
  }
  
  const dimScores = calcDimensionScore(answers);
  const personality = detectPersonality(dimScores);
  const desc = getPersonalityDesc(personality);
  
  console.log('\n🎭 测试结果:');
  console.log(`人格类型: ${desc.n} (${personality})`);
  console.log(`Slogan: "${desc.slogan}"`);
  console.log('\n维度得分:');
  Object.entries(dimScores)
    .sort((a, b) => b[1] - a[1])
    .forEach(([dim, score]) => {
      console.log(`  ${dim}: ${score}`);
    });
  
  return { answers, dimensions: dimScores, personality, description: desc };
}

// CLI 入口
if (require.main === module) {
  const args = process.argv.slice(2);
  
  if (args[0] === '--answer' && args[1]) {
    batchTest(args[1]);
  } else {
    interactiveTest().catch(console.error);
  }
}

module.exports = {
  calcDimensionScore,
  detectPersonality,
  getPersonalityDesc,
  questions,
  dimensions,
  results
};


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)