Skill: cheney87/self-evolution-cn (version 2.1.1)

--- name: self-evolution-cn slug: self-evolution-cn version: 2.1.1 homepage: https://clawhub.ai/skills/self-evolution-cn description: "多 agent 自我进化系统，自动记录学习、错误和功能需求，支持多 agent 统计和自动提升" --- # Self-Evolution-CN 多 agent 自我进化系统，自动记录学习、错误和功能需求，支持多 agent 统计和自动提升。 ## 快速开始 ### 一键配置 ```bash cd ~/.openclaw/skills/self-evolution-cn ./scripts/setup.sh ``` ### 手动配置 ```bash # 设置共享目录 export SHARED_LEARNING_DIR="/root/.openclaw/shared-learning" export SHARED_AGENTS="agent1 agent2" # 创建目录和软链接 mkdir -p "$SHARED_LEARNING_DIR" cp .learnings/*.md "$SHARED_LEARNING_DIR/" ln -s "$SHARED_LEARNING_DIR" ~/.openclaw/workspace-agent1/.learnings ln -s "$SHARED_LEARNING_DIR" ~/.openclaw/workspace-agent2/.learnings # 启用 hook 和 cron openclaw hooks enable self-evolution-cn crontab -e # 添加：0 0 * * * ~/.openclaw/skills/self-evolution-cn/scripts/trigger-daily-review.sh >> ~/.openclaw/skills/self-evolution-cn/logs/heartbeat-daily.log 2>&1 ``` ## 脚本说明 | 脚本 | 功能 | |------|------| | `setup.sh` | 一键配置 | | `daily_review.sh` | 自动统计与提升（每日 00:00 执行） | | `trigger-daily-review.sh` | Cron 触发脚本 | | `activator.sh` | 任务完成后提醒 | | `error-detector.sh` | 命令失败时提醒 | | `extract-skill.sh` | 提取可重用技能 | ## Hook 集成 自动识别并记录： - **用户纠正**：检测中文关键词（"不对"、"错了"、"错误"、"不是这样"、"应该是"）和英文关键词（"No, that's wrong"、"Actually"、"should be"） - **命令失败**：检测工具执行失败（非零退出码）和系统级错误（command not found、Permission denied、fatal） - **知识缺口**：检测中文关键词（"我不知道"、"查不到"、"不知道"、"无法找到"、"找不到"）和英文关键词（"I don't know"、"can't find"、"not sure"） - **更好的方法**：检测中文关键词（"更好的方法"、"更简单"、"优化"、"改进"）和英文关键词（"better way"、"simpler"、"optimize"、"improve"） **自动生成元数据：** - **Pattern-Key**：根据类别自动生成唯一标识（user.correction、knowledge.gap、better.method） - **Area**：根据类别自动映射到对应区域（行为准则、工作流、工作流改进） **记录文件：** - LEARNINGS.md：学习记录（用户纠正、知识缺口、更好的方法） - ERRORS.md：错误记录（命令失败、系统错误） - FEATURE_REQUESTS.md：功能需求记录 启用： ```bash openclaw hooks enable self-evolution-cn ``` ### Hook 事件结构 Hook 监听 OpenClaw 事件并从正确字段读取数据： | 事件 | 读取字段 | 说明 | |------|----------|------| | `message:received` | `event.context.content` | 用户消息内容 | | `tool:after` | `event.context.output` | 工具执行输出 | | `agent:bootstrap` | `event.context.bootstrapFiles` | 引导文件注入 | **兼容性：** 同时支持 `event.message` 和 `event.toolOutput` 旧格式。 ## 常见问题 ### Q: 如何手动执行检查？ A: 直接运行： ```bash bash ~/.openclaw/skills/self-evolution-cn/scripts/daily_review.sh ``` ### Q: 如何控制是否启用自动提升？ A: 设置环境变量 `AUTO_PROMOTE_ENABLED`： ```bash # 禁用自动提升（仅统计） AUTO_PROMOTE_ENABLED=false bash ~/.openclaw/skills/self-evolution-cn/scripts/daily_review.sh # 启用自动提升（默认） AUTO_PROMOTE_ENABLED=true bash ~/.openclaw/skills/self-evolution-cn/scripts/daily_review.sh ``` ### Q: 如何修改共享目录？ A: 设置环境变量： ```bash export SHARED_LEARNING_DIR="/your/custom/path" ``` ### Q: 执行状态和日志在哪里？ A: - 状态：`$SHARED_LEARNING_DIR/heartbeat-state.json` - 日志：`$SHARED_LEARNING_DIR/logs/heartbeat-daily.log` ## 详细文档 - `references/format.md` - 记录格式 - `references/promotion.md` - 提升机制 - `references/multi-agent.md` - 多 agent 支持 - `references/hooks-setup.md` - Hook 配置 - `references/openclaw-integration.md` - OpenClaw 集成 - `hooks/openclaw/HOOK.md` - Hook 说明 ## 更新 ```bash clawdhub update self-evolution-cn ``` ## 版本 当前版本：2.1.1 ### 更新日志 **v2.1.1 (2026-04-18)** - 改进记录反馈：记录完成后自动告知记录的文件名（LEARNINGS.md、ERRORS.md、FEATURE_REQUESTS.md） - 修改 recordLearning、recordError、recordFeatureRequest 函数返回文件名 - 更新 HOOK.md 文档：说明自动回复机制 **v2.1.0 (2026-04-18)** - 添加英文关键词支持：支持 "No, that's wrong"、"Actually"、"should be"、"I don't know"、"can't find"、"not sure"、"better way"、"simpler"、"optimize"、"improve" - 添加系统级错误检测：支持 command not found、No such file、Permission denied、fatal - 添加 FEATURE_REQUESTS.md 支持：新增功能需求记录文件 - 实现 Pattern-Key 自动生成：根据类别自动生成唯一标识（user.correction、knowledge.gap、better.method） - 实现 Area 自动生成：根据类别自动映射到对应区域（行为准则、工作流、工作流改进） - 统一 handler.js 和 handler.ts 功能：确保两个文件功能完全一致 - 更新 HOOK.md 文档：添加新功能说明 - 改进记录反馈：记录完成后自动告知记录的文件名（LEARNINGS.md、ERRORS.md、FEATURE_REQUESTS.md） **v2.0.4 (2026-04-16)** - 修复 detectCorrection 函数：移除 toLowerCase()，中文关键词检测不生效 - 修复 detectKnowledgeGap 函数：移除 toLowerCase() - 修复 detectBetterMethod 函数：移除 toLowerCase() **v2.0.2 (2026-04-16)** - 修复 skills 目录下的 handler.js，确保发布到 clawhub 的版本包含正确的修复 - 从 `event.context.content` 读取消息，而非 `event.message` - 从 `event.context.output` 读取工具输出，而非 `event.toolOutput` **v2.0.1 (2026-04-16)** - 修复 Hook 事件结构读取错误：从 `event.context.content` 读取消息，而非 `event.message` - 修复 Hook 工具输出读取：从 `event.context.output` 读取，而非 `event.toolOutput` - 添加事件结构说明文档 - 向后兼容旧格式 **v2.0.0 (2026-04-07)** - 优化提升格式，去除冗余元数据 - 根据 Area 字段自动映射到对应的二级标题 - 修复 Area 字段提取逻辑 - 更新文档说明 - 精简所有说明文档 - 修复 Pattern-Key 匹配逻辑 - 添加无效 Pattern-Key 过滤 **v1.0.6 (2026-04-06)** - 初始版本

path: _meta.json
content: ownerId: kn79m0n1y3yjzjyvf0p1n5816s827str
slug: self-evolution-cn
version: 2.1.1
publishedAt: 1776323948817

path: assets/LEARNINGS.md
content: # 学习记录模板

学习记录模板，说明状态定义和技能提取字段。

## 状态定义

| 状态 | 含义 |
|------|------|
| `pending` | 未处理 |
| `in_progress` | 正在处理 |
| `resolved` | 已解决或知识已集成 |
| `wont_fix` | 决定不处理（原因在解决方案中） |
| `promoted` | 提升到 SOUL.md、AGENTS.md 或 TOOLS.md |
| `promoted_to_skill` | 提取为可重用技能 |

## 技能提取字段

当学习被提升为技能时，添加这些字段：

```markdown
**Status**: promoted_to_skill
**Skill-Path**: skills/skill-name
```

示例：
```markdown
## [LRN-20260326-001] best_practice

- Agent: agent1
- Logged: 2026-03-26T10:00:00+08:00
- Priority: high
- Status: promoted_to_skill
- Skill-Path: skills/docker-m1-fixes
- Area: infra

### 摘要
Docker 构建在 Apple Silicon 上因平台不匹配而失败

### 详情
...

### 建议行动
...
```

## 类别

**类别**：correction | insight | knowledge_gap | best_practice

**区域**：frontend | backend | infra | tests | docs | config

## 提升决策树

```
学习是否项目特定？
├── 是 → 保留在 .learnings/
└── 否 → 是否与行为/风格相关？
    ├── 是 → 提升到 SOUL.md
    └── 否 → 是否与工具相关？
        ├── 是 → 提升到 TOOLS.md
        └── 否 → 提升到 AGENTS.md（工作流）
```


path: assets/SKILL-TEMPLATE.md
content: # 技能模板

从学习记录中提取技能的模板。复制并自定义。

---

## SKILL.md 模板

```markdown
---
name: skill-name-here
description: "简洁描述何时以及为什么使用此技能。包括触发条件。"
---

# 技能名称

简要介绍解释此技能解决的问题及其起源。

## 快速参考

| 情况 | 操作 |
|------|------|
| [触发 1] | [操作 1] |
| [触发 2] | [操作 2] |

## 背景

为什么这个知识很重要。它防止什么问题。原始学习的上下文。

## 解决方案

### 分步说明

1. 第一步，包含代码或命令
2. 第二步
3. 验证步骤

### 代码示例

\`\`\`language
// 演示解决方案的示例代码
\`\`\`

## 常见变体

- **变体 A**：描述和处理方法
- **变体 B**：描述和处理方法

## 注意事项

- 警告或常见错误 #1
- 警告或常见错误 #2

## 相关

- 相关文档的链接
- 相关技能的链接

## 来源

从学习记录中提取。
- **学习 ID**：LRN-YYYYMMDD-XXX
- **原始类别**：correction | insight | knowledge_gap | best_practice
- **提取日期**：YYYY-MM-DD
```

---

## 最小模板

对于不需要所有部分的简单技能：

```markdown
---
name: skill-name-here
description: "此技能的作用以及何时使用它。"
---

# 技能名称

[一句话的问题陈述]

## 解决方案

[直接解决方案，包含代码/命令]

## 来源

- 学习 ID：LRN-YYYYMMDD-XXX
```

---

## 带脚本的模板

对于包含可执行帮助程序的技能：

```markdown
---
name: skill-name-here
description: "此技能的作用以及何时使用它。"
---

# 技能名称

[介绍]

## 快速参考

| 命令 | 目的 |
|------|------|
| `./scripts/helper.sh` | [它的作用] |
| `./scripts/validate.sh` | [它的作用] |

## 使用方法

### 自动化（推荐）

\`\`\`bash
./skills/skill-name/scripts/helper.sh [args]
\`\`\`

### 手动步骤

1. 步骤一
2. 步骤二

## 脚本

| 脚本 | 描述 |
|------|------|
| `scripts/helper.sh` | 主要工具 |
| `scripts/validate.sh` | 验证检查器 |

## 来源

- 学习 ID：LRN-YYYYMMDD-XXX
```

---

## 命名约定

- **技能名称**：小写，空格用连字符
  - 好的：`docker-m1-fixes`、`api-timeout-patterns`
  - 不好的：`Docker_M1_Fixes`、`APITimeoutPatterns`

- **描述**：以动作动词开头，提及触发条件
  - 好的："处理 Apple Silicon 上的 Docker 构建失败。当构建因平台不匹配而失败时使用。"
  - 不好的："Docker 东西"

- **文件**：
  - `SKILL.md` - 必需，主要文档
  - `scripts/` - 可选，可执行代码
  - `references/` - 可选，详细文档
  - `assets/` - 可选，模板

---

## 提取检查清单

从学习创建技能之前：

- [ ] 学习已验证（状态：resolved）
- [ ] 解决方案广泛适用（不是一次性）
- [ ] 内容完整（包含所有需要的上下文）
- [ ] 名称遵循约定
- [ ] 描述简洁但信息丰富
- [ ] 快速参考表可操作
- [ ] 代码示例已测试
- [ ] 记录了来源学习 ID

创建之后：

- [ ] 使用 `promoted_to_skill` 状态更新原始学习
- [ ] 将 `Skill-Path: skills/skill-name` 添加到学习元数据
- [ ] 通过在新会话中阅读来测试技能


path: heartbeat-state.json
content: {
  "agents": {
    "main": {
      "last_execution_date": "2026-04-16",
      "last_execution_time": "2026-04-16T00:00:19+08:00",
      "status": "completed"
    },
    "bh": {
      "last_execution_date": "2026-04-16",
      "last_execution_time": "2026-04-16T00:00:26+08:00",
      "status": "completed"
    },
    "zq": {
      "last_execution_date": "2026-04-16",
      "last_execution_time": "2026-04-16T00:00:34+08:00",
      "status": "completed"
    },
    "xw": {
      "last_execution_date": "2026-04-16",
      "last_execution_time": "2026-04-16T00:00:42+08:00",
      "status": "completed"
    }
  }
}


path: hooks/openclaw/HOOK.md
content: ---
name: self-evolution-cn
description: "自动识别并记录学习、错误和功能需求，支持多 agent 统计和自动提升"
metadata: {"openclaw":{"emoji":"🧠","events":["agent:bootstrap","message:received","tool:after"]}}
---

# Self-Evolution-CN Hook

自动识别并记录学习、错误和功能需求。

## 功能

### 1. 自动识别触发条件

**用户纠正（自动记录到 LEARNINGS.md）：**
- 检测到关键词："不对"、"错了"、"错误"、"不是这样"、"应该是"、"你为什么"、"我记得是"、"提醒你"、"更正一下"
- 检测到纠正性表达："No, that's wrong"、"Actually"、"should be"
- **动作**：自动记录到 LEARNINGS.md，类别为 correction
- **Pattern-Key**：user.correction
- **Area**：行为准则

**命令失败（自动记录到 ERRORS.md）：**
- 检测到工具执行失败（非零退出码）
- 检测到错误信息：error、Error、ERROR、failed、FAILED
- 检测到系统级错误：command not found、No such file、Permission denied、fatal
- **动作**：自动记录到 ERRORS.md

**知识缺口（自动记录到 LEARNINGS.md）：**
- 检测到用户提供新信息
- 检测到关键词："我不知道"、"查不到"、"不知道"、"无法找到"、"找不到"、"记下来"、"记住"、"你记好"、"别忘了"、"不清楚"、"不确定"
- 检测到英文表达："I don't know"、"can't find"、"not sure"
- **动作**：自动记录到 LEARNINGS.md，类别为 knowledge_gap
- **Pattern-Key**：knowledge.gap
- **Area**：工作流

**发现更好的方法（自动记录到 LEARNINGS.md）：**
- 检测到关键词："更好的方法"、"更简单"、"优化"、"改进"、"更好的"
- 检测到英文表达："better way"、"simpler"、"optimize"、"improve"
- **动作**：自动记录到 LEARNINGS.md，类别为 best_practice
- **Pattern-Key**：better.method
- **Area**：工作流改进

### 2. 自动记录格式

**学习记录：**
```markdown
## [LRN-YYYYMMDD-XXX] 类别

- Agent: <agent_id>
- Logged: 当前时间
- Priority: medium
- Status: pending
- Area: 根据上下文判断

### 摘要
一句话描述

### 详情
完整上下文

### 建议行动
具体修复或改进

### 元数据
- Source: conversation
- Pattern-Key: 自动生成
- Recurrence-Count: 1
```

**错误记录：**
```markdown
## [ERR-YYYYMMDD-XXX] 技能或命令名称

- Agent: <agent_id>
- Logged: 当前时间
- Priority: high
- Status: pending
- Area: 根据上下文判断

### 摘要
简要描述

### 错误
```
错误信息
```

### 上下文
- 尝试的命令/操作
- 使用的输入或参数

### 建议修复
如果可识别，如何解决

### 元数据
- Reproducible: yes
```

### 3. 记录后回复

记录完成后，会自动回复：
- "已记录到 LEARNINGS.md"（学习记录）
- "已记录到 ERRORS.md"（错误记录）
- "已记录到 FEATURE_REQUESTS.md"（功能需求记录）

### 4. 提升规则

**多 Agent 统计：**
- 所有 agent 共享学习目录
- 按 `Pattern-Key` 累计 `Recurrence-Count`
- 累计次数 >= 3 时自动提升到 SOUL.md

**提升格式：**
```markdown
### 一句话总结
一句话建议行动

---
```

**提升位置：**
- 根据 `Area` 字段自动映射到对应的二级标题
- 如果二级标题不存在，自动创建

**Area 映射规则：**
- `行为准则`|`行为模式`|`交互规范` → `## Core Truths（核心准则）`
- `工作流`|`工作流改进`|`任务分发` → `## 工作流程`
- `工具`|`配置`|`工具问题` → `## 工具使用`
- `边界`|`安全` → `## Boundaries（边界）`
- `风格`|`气质` → `## Vibe（风格气质）`
- `连续性`|`偏好` → `## Continuity（连续性）`
- 其他 → `## 其他`

## 事件结构

Hook 监听以下 OpenClaw 事件：

| 事件 | 读取字段 | 说明 |
|------|----------|------|
| `message:received` | `event.context.content` | 用户发送的消息内容 |
| `tool:after` | `event.context.output` | 工具执行输出 |
| `agent:bootstrap` | `event.context.bootstrapFiles` | 注入引导文件 |

**兼容性：** Hook 同时支持 `event.message` 和 `event.toolOutput` 旧格式，确保向后兼容。

## 配置

无需配置。使用以下命令启用：

```bash
openclaw hooks enable self-evolution-cn
```

## 多 Agent 支持

此 hook 支持多 agent 共享学习目录。所有 agent 的学习记录将存储在共享目录中，并按 `Pattern-Key` 累计 `Recurrence-Count`。

### 学习目录检测逻辑

Hook 会自动检测工作区的 `.learnings` 文件夹：

1. **如果工作区有 `.learnings` 软连接指向共享目录** → 使用共享目录
   - 示例：`/root/.openclaw/workspace-agent1/.learnings -> /root/.openclaw/shared-learning`
   - 学习目录：`/root/.openclaw/shared-learning`

2. **如果工作区有独立的 `.learnings` 文件夹** → 使用工作区的 `.learnings`
   - 示例：`/root/.openclaw/workspace-agent2/.learnings/`（独立文件夹）
   - 学习目录：`/root/.openclaw/workspace-agent2/.learnings`

3. **如果工作区没有 `.learnings`** → 使用共享目录
   - 学习目录：`/root/.openclaw/shared-learning`

## 环境变量

### SHARED_LEARNING_DIR

指定共享学习目录路径。

```bash
export SHARED_LEARNING_DIR="/root/.openclaw/shared-learning"
```

### AGENT_ID

指定当前 agent ID。

```bash
export AGENT_ID="main"
```

## 执行状态和日志

### 状态文件

**位置**：`$SHARED_LEARNING_DIR/heartbeat-state.json`

### 日志文件

**位置**：`$SHARED_LEARNING_DIR/logs/heartbeat-daily.log`

### 查看 Agent 状态

**查看所有 agent 状态：**
```bash
cat $SHARED_LEARNING_DIR/heartbeat-state.json
```

**查看特定 agent 状态：**
```bash
jq '.agents.agent1' $SHARED_LEARNING_DIR/heartbeat-state.json
```

**查看特定 agent 日志：**
```bash
grep 'Processing agent: agent1' $SHARED_LEARNING_DIR/logs/heartbeat-daily.log
```


path: hooks/openclaw/handler.js
content: /**
 * Self-Evolution-CN Hook for OpenClaw
 *
 * 自动识别并记录学习、错误和功能需求。
 * 支持多事件：agent:bootstrap、message:received、tool:after
 */

const fs = require('fs').promises;
const path = require('path');
const { lstatSync, realpathSync, existsSync } = require('fs');

// 获取共享学习目录
const SHARED_LEARNING_DIR = process.env.SHARED_LEARNING_DIR || '/root/.openclaw/shared-learning';

// 获取当前 agent ID
const AGENT_ID = process.env.AGENT_ID || 'main';

// 获取当前工作区路径
const WORKSPACE_DIR = process.cwd();

// 检测学习目录路径
function getLearningDir() {
  try {
    const learningsPath = path.join(WORKSPACE_DIR, '.learnings');

    // 检查 .learnings 是否存在
    if (!existsSync(learningsPath)) {
      // 不存在，使用共享目录
      return SHARED_LEARNING_DIR;
    }

    // 检查是否是软连接
    const stats = lstatSync(learningsPath);
    if (stats.isSymbolicLink()) {
      // 解析软连接目标
      const targetPath = realpathSync(learningsPath);

      // 如果软连接目标是共享目录，使用共享目录
      if (targetPath === SHARED_LEARNING_DIR) {
        return SHARED_LEARNING_DIR;
      }

      // 否则使用软连接目标
      return targetPath;
    }

    // 如果是独立文件夹，使用工作区的 .learnings
    if (stats.isDirectory()) {
      return learningsPath;
    }

    // 默认使用共享目录
    return SHARED_LEARNING_DIR;
  } catch (error) {
    console.error('检测学习目录失败:', error);
    return SHARED_LEARNING_DIR;
  }
}

// 获取学习目录
const LEARNING_DIR = getLearningDir();

// 自动识别和记录的提醒内容
const REMINDER_CONTENT = `
## 自我进化提醒

**自动识别和记录规则：**

### 1. 自动识别触发条件

**用户纠正（自动记录到 LEARNINGS.md）：**
- 检测到关键词："不对"、"错了"、"错误"、"不是这样"、"应该是"
- 检测到纠正性表达："No, that's wrong"、"Actually"、"应该是"
- **动作**：自动记录到 LEARNINGS.md，类别为 correction

**命令失败（自动记录到 ERRORS.md）：**
- 检测到工具执行失败（非零退出码）
- 检测到错误信息：error、Error、ERROR、failed、FAILED
- **动作**：自动记录到 ERRORS.md

**知识缺口（自动记录到 LEARNINGS.md）：**
- 检测到用户提供新信息
- 检测到"我不知道"、"查不到"等表达
- **动作**：自动记录到 LEARNINGS.md，类别为 knowledge_gap

**发现更好的方法（自动记录到 LEARNINGS.md）：**
- 检测到"更好的方法"、"更简单"、"优化"等表达
- **动作**：自动记录到 LEARNINGS.md，类别为 best_practice

### 2. 自动记录格式

**学习记录：**
\`\`\`markdown
## [LRN-YYYYMMDD-XXX] 类别

- Agent: ${AGENT_ID}
- Logged: 当前时间
- Priority: medium
- Status: pending
- Area: 根据上下文判断

### 摘要
一句话描述

### 详情
完整上下文

### 建议行动
具体修复或改进

### 元数据
- Source: conversation
- Pattern-Key: 自动生成
- Recurrence-Count: 1
\`\`\`

**错误记录：**
\`\`\`markdown
## [ERR-YYYYMMDD-XXX] 技能或命令名称

- Agent: ${AGENT_ID}
- Logged: 当前时间
- Priority: high
- Status: pending
- Area: 根据上下文判断

### 摘要
简要描述

### 错误
\`\`\`
错误信息
\`\`\`

### 上下文
- 尝试的命令/操作
- 使用的输入或参数

### 建议修复
如果可识别，如何解决

### 元数据
- Reproducible: yes
\`\`\`

### 3. 记录后回复

记录完成后，必须回复：
"已记录到 LEARNINGS.md" 或 "已记录到 ERRORS.md"

### 4. 提升规则

**多 Agent 统计：**
- 所有 agent 共享学习目录
- 按 \`Pattern-Key\` 累计 \`Recurrence-Count\`
- 累计次数 >= 3 时自动提升到 SOUL.md

**提升目标：**
- 行为模式 → SOUL.md
- 工作流改进 → AGENTS.md
- 工具问题 → TOOLS.md

### 5. 共享目录

共享目录：${SHARED_LEARNING_DIR}
当前 Agent：${AGENT_ID}
当前工作区：${WORKSPACE_DIR}
学习目录：${LEARNING_DIR}

**学习目录检测逻辑：**
- 如果工作区有 .learnings 软连接指向共享目录 → 使用共享目录
- 如果工作区有独立的 .learnings 文件夹 → 使用工作区的 .learnings
- 如果工作区没有 .learnings → 使用共享目录
`.trim();

// 生成唯一 ID
function generateId(prefix) {
  const now = new Date();
  const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
  const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
  return `${prefix}-${dateStr}-${random}`;
}

// 确保目录存在
async function ensureDir(dir) {
  try {
    await fs.mkdir(dir, { recursive: true });
  } catch (err) {
    // 目录已存在，忽略错误
  }
}

// 追加内容到文件
async function appendToFile(filePath, content) {
  await ensureDir(LEARNING_DIR);
  await fs.appendFile(filePath, content + '\n\n');
}

// 检测用户纠正
function detectCorrection(content) {
  const keywords = [
    '不对', '错了', '错误', '不是这样', '应该是',
    '你为什么', '我记得是', '提醒你', '更正一下',
    "No, that's wrong", 'Actually', 'should be'
  ];
  return keywords.some(keyword => content.includes(keyword));
}

// 检测知识缺口
function detectKnowledgeGap(content) {
  const keywords = [
    '我不知道', '查不到', '不知道', '无法找到', '找不到',
    '记下来', '记住', '你记好', '别忘了',
    '不清楚', '不确定',
    "I don't know", "can't find", 'not sure'
  ];
  return keywords.some(keyword => content.includes(keyword));
}

// 检测更好的方法
function detectBetterMethod(content) {
  const keywords = [
    '更好的方法', '更简单', '优化', '改进', '更好的',
    'better way', 'simpler', 'optimize', 'improve'
  ];
  return keywords.some(keyword => content.includes(keyword));
}

// 记录学习
async function recordLearning(category, summary, details, suggestedAction) {
  const id = generateId('LRN');
  const now = new Date().toISOString();
  const filePath = path.join(LEARNING_DIR, 'LEARNINGS.md');

  // 根据 category 生成 Pattern-Key 和 Area
  let patternKey = 'unknown';
  let area = '其他';

  switch (category) {
    case 'correction':
      patternKey = 'user.correction';
      area = '行为准则';
      break;
    case 'knowledge_gap':
      patternKey = 'knowledge.gap';
      area = '工作流';
      break;
    case 'best_practice':
      patternKey = 'better.method';
      area = '工作流改进';
      break;
  }

  const content = `## [${id}] ${category}

- Agent: ${AGENT_ID}
- Logged: ${now}
- Priority: medium
- Status: pending
- Area: ${area}

### 摘要
${summary}

### 详情
${details}

### 建议行动
${suggestedAction}

### 元数据
- Source: conversation
- Pattern-Key: ${patternKey}
- Recurrence-Count: 1
`;

  await appendToFile(filePath, content);
  return 'LEARNINGS.md';
}

// 记录错误
async function recordError(summary, error, context, suggestedFix) {
  const id = generateId('ERR');
  const now = new Date().toISOString();
  const filePath = path.join(LEARNING_DIR, 'ERRORS.md');

  const content = `## [${id}] ${summary}

- Agent: ${AGENT_ID}
- Logged: ${now}
- Priority: high
- Status: pending
- Area: 根据上下文判断

### 摘要
${summary}

### 错误
\`\`\`
${error}
\`\`\`

### 上下文
${context}

### 建议修复
${suggestedFix}

### 元数据
- Reproducible: yes
`;

  await appendToFile(filePath, content);
  return 'ERRORS.md';
}

// 记录功能需求
async function recordFeatureRequest(summary, details, suggestedAction) {
  const id = generateId('FEAT');
  const now = new Date().toISOString();
  const filePath = path.join(LEARNING_DIR, 'FEATURE_REQUESTS.md');

  const content = `## [${id}] ${summary}

- Agent: ${AGENT_ID}
- Logged: ${now}
- Priority: medium
- Status: pending
- Area: 根据上下文判断

### 摘要
${summary}

### 详情
${details}

### 建议行动
${suggestedAction}

### 元数据
- Source: conversation
- Pattern-Key: 自动生成
- Recurrence-Count: 1
`;

  await appendToFile(filePath, content);
  return 'FEATURE_REQUESTS.md';
}

const handler = async (event) => {
  // 事件结构的安全检查
  if (!event || typeof event !== 'object') {
    return;
  }

  // 处理 agent:bootstrap 事件
  if (event.type === 'agent' && event.action === 'bootstrap') {
    // 上下文的安全检查
    if (!event.context || typeof event.context !== 'object') {
      return;
    }

    // 将提醒作为虚拟引导文件注入
    if (Array.isArray(event.context.bootstrapFiles)) {
      event.context.bootstrapFiles.push({
        path: 'SELF_EVOLUTION_REMINDER.md',
        content: REMINDER_CONTENT,
        virtual: true,
      });
    }
  }

  // 处理 message:received 事件
  if (event.type === 'message' && event.action === 'received') {
    const content = event.context?.content || '';

    // 检测用户纠正
    if (detectCorrection(content)) {
      const fileName = await recordLearning(
        'correction',
        '用户纠正了之前的回答',
        `用户消息: ${content}`,
        '仔细分析用户的纠正意见，理解正确的做法，避免重复错误'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }

    // 检测知识缺口
    if (detectKnowledgeGap(content)) {
      const fileName = await recordLearning(
        'knowledge_gap',
        '发现知识缺口',
        `用户消息: ${content}`,
        '记录这个知识缺口，后续需要补充相关知识'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }

    // 检测更好的方法
    if (detectBetterMethod(content)) {
      const fileName = await recordLearning(
        'best_practice',
        '发现更好的方法',
        `用户消息: ${content}`,
        '学习这个更好的方法，更新工作流程'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }
  }

  // 处理 tool:after 事件
  if (event.type === 'tool' && event.action === 'after') {
    const toolName = event.context?.toolName || 'unknown';
    const result = event.context?.result || {};
    const output = event.context?.output || '';

    // 检测工具执行失败（结构化检测）
    if (result.error || result.status === 'error' || result.exitCode !== 0) {
      const errorInfo = result.error || result.message || JSON.stringify(result);
      const fileName = await recordError(
        `工具执行失败: ${toolName}`,
        errorInfo,
        `工具: ${toolName}\n参数: ${JSON.stringify(event.context?.args || {})}`,
        '检查工具使用方式，确认参数正确，或查看工具文档'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }

    // 检测系统级错误（字符串检测）
    if (output && typeof output === 'string') {
      const errorPatterns = [
        /error|Error|ERROR|failed|FAILED/g,
        /command not found|No such file|Permission denied|fatal/g
      ];
      if (errorPatterns.some(pattern => pattern.test(output))) {
        const fileName = await recordError(
          `系统错误: ${toolName}`,
          output,
          `工具: ${toolName}\n参数: ${JSON.stringify(event.context?.args || {})}`,
          '检查系统配置和权限'
        );
        event.messages?.push(`已记录到 ${fileName}`);
      }
    }
  }
};

module.exports = handler;
module.exports.default = handler;


path: hooks/openclaw/handler.ts
content: /**
 * Self-Evolution-CN Hook for OpenClaw
 *
 * 自动识别并记录学习、错误和功能需求。
 * 支持多事件：agent:bootstrap、message:received、tool:after
 */

import type { HookHandler } from 'openclaw/hooks';
import { promises as fs } from 'fs';
import { join } from 'path';
import { lstatSync, realpathSync, existsSync } from 'fs';

// 获取共享学习目录
const SHARED_LEARNING_DIR = process.env.SHARED_LEARNING_DIR || '/root/.openclaw/shared-learning';

// 获取当前 agent ID
const AGENT_ID = process.env.AGENT_ID || 'main';

// 获取当前工作区路径
const WORKSPACE_DIR = process.cwd();

// 检测学习目录路径
function getLearningDir(): string {
  try {
    const learningsPath = join(WORKSPACE_DIR, '.learnings');
    
    // 检查 .learnings 是否存在
    if (!existsSync(learningsPath)) {
      // 不存在，使用共享目录
      return SHARED_LEARNING_DIR;
    }
    
    // 检查是否是软连接
    const stats = lstatSync(learningsPath);
    if (stats.isSymbolicLink()) {
      // 解析软连接目标
      const targetPath = realpathSync(learningsPath);
      
      // 如果软连接目标是共享目录，使用共享目录
      if (targetPath === SHARED_LEARNING_DIR) {
        return SHARED_LEARNING_DIR;
      }
      
      // 否则使用软连接目标
      return targetPath;
    }
    
    // 如果是独立文件夹，使用工作区的 .learnings
    if (stats.isDirectory()) {
      return learningsPath;
    }
    
    // 默认使用共享目录
    return SHARED_LEARNING_DIR;
  } catch (error) {
    console.error('检测学习目录失败:', error);
    return SHARED_LEARNING_DIR;
  }
}

// 获取学习目录
const LEARNING_DIR = getLearningDir();

// 自动识别和记录的提醒内容
const REMINDER_CONTENT = `
## 自我进化提醒

**自动识别和记录规则：**

### 1. 自动识别触发条件

**用户纠正（自动记录到 LEARNINGS.md）：**
- 检测到关键词："不对"、"错了"、"错误"、"不是这样"、"应该是"
- 检测到纠正性表达："No, that's wrong"、"Actually"、"应该是"
- **动作**：自动记录到 LEARNINGS.md，类别为 correction

**命令失败（自动记录到 ERRORS.md）：**
- 检测到工具执行失败（非零退出码）
- 检测到错误信息：error、Error、ERROR、failed、FAILED
- **动作**：自动记录到 ERRORS.md

**知识缺口（自动记录到 LEARNINGS.md）：**
- 检测到用户提供新信息
- 检测到"我不知道"、"查不到"等表达
- **动作**：自动记录到 LEARNINGS.md，类别为 knowledge_gap

**发现更好的方法（自动记录到 LEARNINGS.md）：**
- 检测到"更好的方法"、"更简单"、"优化"等表达
- **动作**：自动记录到 LEARNINGS.md，类别为 best_practice

### 2. 自动记录格式

**学习记录：**
\`\`\`markdown
## [LRN-YYYYMMDD-XXX] 类别

- Agent: ${AGENT_ID}
- Logged: 当前时间
- Priority: medium
- Status: pending
- Area: 根据上下文判断

### 摘要
一句话描述

### 详情
完整上下文

### 建议行动
具体修复或改进

### 元数据
- Source: conversation
- Pattern-Key: 自动生成
- Recurrence-Count: 1
\`\`\`

**错误记录：**
\`\`\`markdown
## [ERR-YYYYMMDD-XXX] 技能或命令名称

- Agent: ${AGENT_ID}
- Logged: 当前时间
- Priority: high
- Status: pending
- Area: 根据上下文判断

### 摘要
简要描述

### 错误
\`\`\`
错误信息
\`\`\`

### 上下文
- 尝试的命令/操作
- 使用的输入或参数

### 建议修复
如果可识别，如何解决

### 元数据
- Reproducible: yes
\`\`\`

### 3. 记录后回复

记录完成后，必须回复：
"已记录到 LEARNINGS.md" 或 "已记录到 ERRORS.md"

### 4. 提升规则

**多 Agent 统计：**
- 所有 agent 共享学习目录
- 按 \`Pattern-Key\` 累计 \`Recurrence-Count\`
- 累计次数 >= 3 时自动提升到 SOUL.md

**提升目标：**
- 行为模式 → SOUL.md
- 工作流改进 → AGENTS.md
- 工具问题 → TOOLS.md

### 5. 共享目录

共享目录：${SHARED_LEARNING_DIR}
当前 Agent：${AGENT_ID}
当前工作区：${WORKSPACE_DIR}
学习目录：${LEARNING_DIR}

**学习目录检测逻辑：**
- 如果工作区有 .learnings 软连接指向共享目录 → 使用共享目录
- 如果工作区有独立的 .learnings 文件夹 → 使用工作区的 .learnings
- 如果工作区没有 .learnings → 使用共享目录
`.trim();

// 生成唯一 ID
function generateId(prefix: string): string {
  const now = new Date();
  const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
  const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
  return `${prefix}-${dateStr}-${random}`;
}

// 确保目录存在
async function ensureDir(dir: string): Promise<void> {
  try {
    await fs.mkdir(dir, { recursive: true });
  } catch (err) {
    // 目录已存在，忽略错误
  }
}

// 追加内容到文件
async function appendToFile(filePath: string, content: string): Promise<void> {
  await ensureDir(LEARNING_DIR);
  await fs.appendFile(filePath, content + '\n\n');
}

// 检测用户纠正
function detectCorrection(content: string): boolean {
  const keywords = [
    '不对', '错了', '错误', '不是这样', '应该是',
    '你为什么', '我记得是', '提醒你', '更正一下',
    "No, that's wrong", 'Actually', 'should be'
  ];
  return keywords.some(keyword => content.includes(keyword));
}

// 检测知识缺口
function detectKnowledgeGap(content: string): boolean {
  const keywords = [
    '我不知道', '查不到', '不知道', '无法找到', '找不到',
    '记下来', '记住', '你记好', '别忘了',
    '不清楚', '不确定',
    "I don't know", "can't find", 'not sure'
  ];
  return keywords.some(keyword => content.includes(keyword));
}

// 检测更好的方法
function detectBetterMethod(content: string): boolean {
  const keywords = [
    '更好的方法', '更简单', '优化', '改进', '更好的',
    'better way', 'simpler', 'optimize', 'improve'
  ];
  return keywords.some(keyword => content.includes(keyword));
}

// 记录学习
async function recordLearning(
  category: string,
  summary: string,
  details: string,
  suggestedAction: string
): Promise<string> {
  const id = generateId('LRN');
  const now = new Date().toISOString();
  const filePath = join(LEARNING_DIR, 'LEARNINGS.md');

  // 根据 category 生成 Pattern-Key 和 Area
  let patternKey = 'unknown';
  let area = '其他';

  switch (category) {
    case 'correction':
      patternKey = 'user.correction';
      area = '行为准则';
      break;
    case 'knowledge_gap':
      patternKey = 'knowledge.gap';
      area = '工作流';
      break;
    case 'best_practice':
      patternKey = 'better.method';
      area = '工作流改进';
      break;
  }

  const content = `## [${id}] ${category}

- Agent: ${AGENT_ID}
- Logged: ${now}
- Priority: medium
- Status: pending
- Area: ${area}

### 摘要
${summary}

### 详情
${details}

### 建议行动
${suggestedAction}

### 元数据
- Source: conversation
- Pattern-Key: ${patternKey}
- Recurrence-Count: 1
`;

  await appendToFile(filePath, content);
  return 'LEARNINGS.md';
}

// 记录错误
async function recordError(
  summary: string,
  error: string,
  context: string,
  suggestedFix: string
): Promise<string> {
  const id = generateId('ERR');
  const now = new Date().toISOString();
  const filePath = join(LEARNING_DIR, 'ERRORS.md');

  const content = `## [${id}] ${summary}

- Agent: ${AGENT_ID}
- Logged: ${now}
- Priority: high
- Status: pending
- Area: 根据上下文判断

### 摘要
${summary}

### 错误
\`\`\`
${error}
\`\`\`

### 上下文
${context}

### 建议修复
${suggestedFix}

### 元数据
- Reproducible: yes
`;

  await appendToFile(filePath, content);
  return 'ERRORS.md';
}

// 记录功能需求
async function recordFeatureRequest(
  summary: string,
  details: string,
  suggestedAction: string
): Promise<string> {
  const id = generateId('FEAT');
  const now = new Date().toISOString();
  const filePath = join(LEARNING_DIR, 'FEATURE_REQUESTS.md');

  const content = `## [${id}] ${summary}

- Agent: ${AGENT_ID}
- Logged: ${now}
- Priority: medium
- Status: pending
- Area: 根据上下文判断

### 摘要
${summary}

### 详情
${details}

### 建议行动
${suggestedAction}

### 元数据
- Source: conversation
- Pattern-Key: 自动生成
- Recurrence-Count: 1
`;

  await appendToFile(filePath, content);
  return 'FEATURE_REQUESTS.md';
}

const handler: HookHandler = async (event) => {
  // 事件结构的安全检查
  if (!event || typeof event !== 'object') {
    return;
  }

  // 处理 agent:bootstrap 事件
  if (event.type === 'agent' && event.action === 'bootstrap') {
    // 上下文的安全检查
    if (!event.context || typeof event.context !== 'object') {
      return;
    }

    // 将提醒作为虚拟引导文件注入
    if (Array.isArray(event.context.bootstrapFiles)) {
      event.context.bootstrapFiles.push({
        path: 'SELF_EVOLUTION_REMINDER.md',
        content: REMINDER_CONTENT,
        virtual: true,
      });
    }
  }

  // 处理 message:received 事件
  if (event.type === 'message' && event.action === 'received') {
    const content = event.context?.content || '';

    // 检测用户纠正
    if (detectCorrection(content)) {
      const fileName = await recordLearning(
        'correction',
        '用户纠正了之前的回答',
        `用户消息: ${content}`,
        '仔细分析用户的纠正意见，理解正确的做法，避免重复错误'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }

    // 检测知识缺口
    if (detectKnowledgeGap(content)) {
      const fileName = await recordLearning(
        'knowledge_gap',
        '发现知识缺口',
        `用户消息: ${content}`,
        '记录这个知识缺口，后续需要补充相关知识'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }

    // 检测更好的方法
    if (detectBetterMethod(content)) {
      const fileName = await recordLearning(
        'best_practice',
        '发现更好的方法',
        `用户消息: ${content}`,
        '学习这个更好的方法，更新工作流程'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }
  }

  // 处理 tool:after 事件
  if (event.type === 'tool' && event.action === 'after') {
    const toolName = event.context?.toolName || 'unknown';
    const result = event.context?.result || {};
    const output = event.context?.output || '';

    // 检测工具执行失败（结构化检测）
    if (result.error || result.status === 'error' || result.exitCode !== 0) {
      const errorInfo = result.error || result.message || JSON.stringify(result);
      const fileName = await recordError(
        `工具执行失败: ${toolName}`,
        errorInfo,
        `工具: ${toolName}\n参数: ${JSON.stringify(event.context?.args || {})}`,
        '检查工具使用方式，确认参数正确，或查看工具文档'
      );
      event.messages?.push(`已记录到 ${fileName}`);
    }

    // 检测系统级错误（字符串检测）
    if (output && typeof output === 'string') {
      const errorPatterns = [
        /error|Error|ERROR|failed|FAILED/g,
        /command not found|No such file|Permission denied|fatal/g
      ];
      if (errorPatterns.some(pattern => pattern.test(output))) {
        const fileName = await recordError(
          `系统错误: ${toolName}`,
          output,
          `工具: ${toolName}\n参数: ${JSON.stringify(event.context?.args || {})}`,
          '检查系统配置和权限'
        );
        event.messages?.push(`已记录到 ${fileName}`);
      }
    }
  }
};

export default handler;


path: references/examples.md
content: # 示例条目

## 学习：纠正

```markdown
## [LRN-20260326-001] correction

- Agent: agent1
- Logged: 2026-03-26T10:30:00+08:00
- Priority: high
- Status: pending
- Area: tests

### 摘要
错误地假设 pytest fixtures 默认为函数作用域

### 详情
编写测试 fixtures 时，我假设所有 fixtures 都是函数作用域的。用户纠正说虽然函数作用域是默认的，但代码库约定对数据库连接使用模块作用域的 fixtures 以提高测试性能。

### 建议行动
创建涉及昂贵设置（DB、网络）的 fixtures 时，在默认为函数作用域之前检查现有 fixtures 的作用域模式。

### 元数据
- Source: user_feedback
- Related Files: tests/conftest.py
- Tags: pytest, testing, fixtures

---
```

## 学习：知识缺口（已解决）

```markdown
## [LRN-20260326-002] knowledge_gap

- Agent: agent1
- Logged: 2026-03-26T14:22:00+08:00
- Priority: medium
- Status: resolved
- Area: config

### 摘要
项目使用 pnpm 而不是 npm 进行包管理

### 详情
尝试运行 `npm install` 但项目使用 pnpm workspaces。锁文件是 `pnpm-lock.yaml`，而不是 `package-lock.json`。

### 建议行动
在假设 npm 之前检查 `pnpm-lock.yaml` 或 `pnpm-workspace.yaml`。为此项目使用 `pnpm install`。

### 元数据
- Source: error
- Related Files: pnpm-lock.yaml, pnpm-workspace.yaml
- Tags: package-manager, pnpm, setup

### 解决方案
- **Resolved**: 2026-03-26T14:30:00Z
- **Commit/PR**: N/A - 知识更新
- **Notes**: 已添加到 CLAUDE.md 以供将来参考

---
```

## 学习：提升到 SOUL.md

```markdown
## [LRN-20260326-003] best_practice

- Agent: agent1
- Logged: 2026-03-26T16:00:00+08:00
- Priority: high
- Status: promoted
- Promoted: SOUL.md
- Promoted-By: agent1
- Area: backend

### 摘要
API 响应必须包含请求头中的关联 ID

### 详情
所有 API 响应应该回传请求中的 X-Correlation-ID 头。这是分布式跟踪所必需的。没有此头的响应会破坏可观察性管道。

### 建议行动
始终在 API 处理程序中包含关联 ID 传递。

### 元数据
- Source: user_feedback
- Related Files: src/middleware/correlation.ts
- Tags: api, observability, tracing

---
```

## 错误条目

```markdown
## [ERR-20260326-A3F] docker_build

- Agent: agent1
- Logged: 2026-03-26T09:15:00+08:00
- Priority: high
- Status: pending
- Area: infra

### 摘要
Docker 构建在 M1 Mac 上因平台不匹配而失败

### 错误
```
error: failed to solve: python:3.11-slim: no match for platform linux/arm64
```

### 上下文
- 命令：`docker build -t myapp .`
- Dockerfile 使用 `FROM python:3.11-slim`
- 在 Apple Silicon (M1/M2) 上运行

### 建议修复
添加平台标志：`docker build --platform linux/amd64 -t myapp .`
或更新 Dockerfile：`FROM --platform=linux/amd64 python:3.11-slim`

### 元数据
- Reproducible: yes
- Related Files: Dockerfile

---
```

## 功能需求

```markdown
## [FEAT-20260326-001] export_to_csv

- Agent: agent1
- Logged: 2026-03-26T16:45:00+08:00
- Priority: medium
- Status: pending
- Area: backend

### 请求的能力
导出分析结果为 CSV 格式

### 用户上下文
用户运行每周报告，需要与非技术利益相关者在 Excel 中共享结果。目前手动复制输出。

### 复杂度估算
simple

### 建议实现
为 analyze 命令添加 `--output csv` 标志。使用标准 csv 模块。可以扩展现有的 `--output json` 模式。

### 元数据
- Frequency: recurring
- Related Features: analyze command, json output

---
```


path: references/format.md
content: # 记录格式说明

## 学习记录格式

```markdown
## [LRN-YYYYMMDD-XXX] 类别

- Agent: <agent_id>
- Logged: 2026-03-26T10:00:00+08:00
- Priority: low|medium|high|critical
- Status: pending|in_progress|resolved|promoted|promoted:all
- Promoted: SOUL.md  # 提升后添加
- Promoted-By: agent1, agent2  # 提升后添加
- Area: frontend|backend|infra|tests|docs|config

### 摘要
一句话描述

### 详情
完整上下文

### 建议行动
具体修复或改进

### 元数据
- Source: conversation|error|user_feedback
- Related Files: path/to/file.ext
- Pattern-Key: simplify.dead_code  # 用于重复模式跟踪
- Recurrence-Count: 1
- First-Seen: 2026-03-26
- Last-Seen: 2026-03-26

---
```

## 错误记录格式

```markdown
## [ERR-YYYYMMDD-XXX] 技能或命令名称

- Agent: <agent_id>
- Logged: 2026-03-26T10:00:00+08:00
- Priority: high
- Status: pending
- Area: frontend|backend|infra|tests|docs|config

### 摘要
简要描述失败内容

### 错误
```
实际错误消息或输出
```

### 上下文
- 尝试的命令/操作
- 使用的输入或参数

### 建议修复
如果可识别，如何解决

### 元数据
- Reproducible: yes|no|unknown
- Related Files: path/to/file.ext

---
```

## 字段说明

| 字段 | 说明 | 示例 |
|------|------|------|
| Agent | 触发记录的 agent 标识 | agent1, agent2 |
| Logged | ISO-8601 时间戳 | 2026-03-26T10:00:00+08:00 |
| Priority | 优先级 | low, medium, high, critical |
| Status | 状态 | pending, in_progress, resolved, promoted, promoted:all |
| Promoted | 提升到的文件 | SOUL.md |
| Promoted-By | 已提升的 agent 列表 | agent1, agent2 |
| Area | 代码库区域 | frontend, backend, infra, tests, docs, config |
| Source | 来源 | conversation, error, user_feedback |
| Pattern-Key | 模式标识 | simplify.dead_code |
| Recurrence-Count | 重复次数 | 1, 2, 3... |
| First-Seen | 首次出现日期 | 2026-03-26 |
| Last-Seen | 最近出现日期 | 2026-03-26 |

## ID 生成

格式：`TYPE-YYYYMMDD-XXX`
- TYPE: `LRN`（学习）、`ERR`（错误）、`FEAT`（功能）
- YYYYMMDD: 当前日期
- XXX: 序号或随机 3 个字符

示例：`LRN-20260326-001`、`ERR-20260326-A3F`

## 状态说明

| 状态 | 说明 |
|------|------|
| `pending` | 待处理 |
| `in_progress` | 正在处理 |
| `resolved` | 已解决 |
| `promoted` | 已提升到 SOUL.md（部分 agent） |
| `promoted:all` | 已提升到 SOUL.md（所有 agent） |

## 解决条目

当问题被修复时，更新条目：

1. 更改 `Status: pending` → `Status: resolved`
2. 添加解决块：

```markdown
### 解决方案
- **Resolved**: 2026-03-27T09:00:00Z
- **Commit/PR**: abc123 或 #42
- **Notes**: 简要描述做了什么
```

## 提升条目

当条目被提升到 SOUL.md 时：

1. 更改 `Status: pending` → `Status: promoted`
2. 添加 `Promoted: SOUL.md`
3. 添加 `Promoted-By: <agent_id>`

**多 agent 场景：**
- 每个 agent 提升后，将自己的 ID 添加到 `Promoted-By`
- 当所有 agent 都提升后，Status 改为 `promoted:all`


path: references/hooks-setup.md
content: # Hook 配置指南

为 OpenClaw 配置自动自我改进触发器。

## 概述

Hook 通过在关键时刻注入提醒来启用主动学习捕获：
- **agent:bootstrap** - 在工作区文件注入之前
- **message:received** - 收到用户消息时
- **tool:after** - 工具执行之后

## OpenClaw 设置

### 1. 安装 Hook

复制 hook 到 OpenClaw 的 hooks 目录：

```bash
cp -r hooks/openclaw ~/.openclaw/hooks/self-evolution-cn
```

启用 hook：

```bash
openclaw hooks enable self-evolution-cn
```

### 2. 验证 Hook

检查 hook 是否已注册：

```bash
openclaw hooks list
```

### 3. 测试 Hook

1. 启用 hook 配置
2. 重启 gateway
3. 启动新的 OpenClaw 会话
4. 发送任何提示
5. 验证你在上下文中看到 `<self-improvement-reminder>`

## 验证

### 测试激活器 Hook

1. 启用 hook 配置
2. 重启 gateway
3. 启动新的 OpenClaw 会话
4. 发送任何提示
5. 验证你在上下文中看到 `<self-improvement-reminder>`

### 测试错误检测器 Hook

1. 为 Bash 启用 PostToolUse hook
2. 运行失败的命令：`ls /nonexistent/path`
3. 验证你看到 `<error-detected>` 提醒

## 故障排除

### Hook 未触发

1. **检查脚本权限**：`chmod +x scripts/*.sh`
2. **验证路径**：使用绝对路径或相对于项目根目录的路径
3. **检查设置位置**：项目级 vs 用户级设置
4. **重启 gateway**：Hook 在 gateway 启动时加载

### 权限被拒绝

```bash
chmod +x ./skills/self-evolution-cn/scripts/activator.sh
chmod +x ./skills/self-evolution-cn/scripts/error-detector.sh
chmod +x ./skills/self-evolution-cn/scripts/extract-skill.sh
```

### 脚本未找到

如果使用相对路径，确保你在正确的目录中或使用绝对路径：

```json
{
  "command": "/absolute/path/to/skills/self-evolution-cn/scripts/activator.sh"
}
```

## Hook 输出预算

激活器设计为轻量级：
- **目标**：每次激活约 50-100 个 token
- **内容**：结构化提醒，不是冗长的指令
- **格式**：XML 标签以便于解析

如果需要进一步减少开销，可以编辑 `activator.sh` 以输出更少的文本。

## 安全考虑

- Hook 脚本以与 OpenClaw 相同的权限运行
- 脚本仅输出文本；它们不修改文件或运行命令
- 错误检测器读取环境变量
- 所有脚本都是可选的（你必须显式配置它们）

## 禁用 Hook

要在不删除配置的情况下临时禁用：

```bash
openclaw hooks disable self-evolution-cn
```

## OpenClaw Hook 事件

| 事件 | 何时触发 |
|------|---------|
| `agent:bootstrap` | 在工作区文件注入之前 |
| `message:received` | 收到用户消息时 |
| `tool:after` | 工具执行之后 |

## 自动识别触发器

### 用户纠正

检测到关键词："不对"、"错了"、"错误"、"不是这样"、"应该是"

### 命令失败

检测到工具执行失败（非零退出码）

### 知识缺口

检测到用户提供新信息

### 发现更好的方法

检测到"更好的方法"、"更简单"、"优化"等表达


path: references/multi-agent.md
content: # 多 Agent 支持说明

## 概述

Self-Evolution-CN 支持多个 agent 共享学习目录，所有 agent 的学习记录存储在共享目录中，并按 `Pattern-Key` 累计 `Recurrence-Count`。

## 共享目录结构

```
/root/.openclaw/shared-learning/     # 共享目录
├── ERRORS.md                       # 所有 agent 共享
├── LEARNINGS.md                    # 所有 agent 共享
└── FEATURE_REQUESTS.md             # 所有 agent 共享

/root/.openclaw/workspace-agent1/.learnings  # 软链接 → 共享目录
/root/.openclaw/workspace-agent2/.learnings  # 软链接 → 共享目录
/root/.openclaw/workspace-agent3/.learnings  # 独立目录（可选）
```

## 配置

详见 `SKILL.md` 快速开始部分。

## 统计逻辑

### 按 Pattern-Key 累计

所有 agent 的记录按 `Pattern-Key` 累计 `Recurrence-Count`，累计次数 >= 3 时自动提升到 SOUL.md。

### 提升判断

- 累计次数 >= 3 时自动提升到 SOUL.md
- Agent 字段仅用于追溯，不影响提升判断

## 脚本支持

### daily_review.sh

自动统计与提升脚本，每日 00:00 自动执行。

```bash
# 手动执行
./scripts/daily_review.sh
```

**功能：**
- 自动统计所有记录的重复次数
- 自动提升到 SOUL.md
- 共享目录：所有使用共享目录的智能体都提升
- 独立目录：对应智能体提升到自己的 SOUL.md

### trigger-daily-review.sh

Cron 触发脚本，每日 00:00 自动执行。

```bash
# 添加到 crontab
0 0 * * * /path/to/scripts/trigger-daily-review.sh
```

## 环境变量

| 变量 | 说明 | 默认值 |
|------|------|--------|
| `SHARED_LEARNING_DIR` | 共享学习目录路径 | `/root/.openclaw/shared-learning` |
| `SHARED_AGENTS` | 需要共享学习目录的 agent 列表 | `agent1` |
| `AGENT_ID` | 当前 agent ID | `main` |
| `AUTO_PROMOTE_ENABLED` | 是否启用自动提升 | `true` |

### 控制自动提升

```bash
# 禁用自动提升（仅统计）
export AUTO_PROMOTE_ENABLED=false

# 启用自动提升（默认）
export AUTO_PROMOTE_ENABLED=true
```

## 执行状态和日志

### 状态文件

**位置**：`$SHARED_LEARNING_DIR/heartbeat-state.json`

**查看状态：**
```bash
cat $SHARED_LEARNING_DIR/heartbeat-state.json
jq '.agents.agent1' $SHARED_LEARNING_DIR/heartbeat-state.json
```

### 日志文件

**位置**：`$SHARED_LEARNING_DIR/logs/heartbeat-daily.log`

**查看日志：**
```bash
tail -f $SHARED_LEARNING_DIR/logs/heartbeat-daily.log
grep 'Processing agent: agent1' $SHARED_LEARNING_DIR/logs/heartbeat-daily.log
```


path: references/openclaw-integration.md
content: # OpenClaw 集成

## 工作区结构

```
~/.openclaw/
├── workspace-agent1/
│   ├── SOUL.md                 # 行为准则（所有 agent 加载）
│   ├── TOOLS.md                # 工具能力
│   ├── MEMORY.md               # 长期记忆（仅主会话）
│   ├── .learnings/             # 学习记录（软链接 → 共享目录）
│   └── memory/                 # 每日记忆
├── shared-learning/            # 共享学习目录
│   ├── ERRORS.md
│   ├── LEARNINGS.md
│   └── FEATURE_REQUESTS.md
├── skills/self-evolution-cn/   # 技能目录
│   ├── SKILL.md
│   ├── scripts/
│   ├── hooks/openclaw/
│   ├── references/
│   ├── logs/
│   └── heartbeat-state.json
└── hooks/self-evolution-cn/    # Hook 安装目录
    ├── HOOK.md
    └── handler.js
```

## 快速设置

详见 `SKILL.md` 快速开始部分。

## 学习工作流

### 捕获学习

1. **会话内**：记录到 `.learnings/`
2. **跨会话**：提升到 SOUL.md（所有 agent 加载）

### 提升决策树

```
学习是否项目特定？
├── 是 → 保留在 .learnings/
└── 否 → 提升到 SOUL.md
```

详见 `references/promotion.md`

## 可用的 Hook 事件

| 事件 | 何时触发 |
|------|---------|
| `agent:bootstrap` | 在工作区文件注入之前 |
| `message:received` | 收到用户消息时 |
| `tool:after` | 工具执行之后 |

## 验证

检查 hook 是否已注册：

```bash
openclaw hooks list
```

## 故障排除

### Hook 未触发

1. 确保启用了 hook：`openclaw hooks enable self-evolution-cn`
2. 配置更改后重启 gateway
3. 检查 gateway 日志

### 学习未持久化

1. 验证 `.learnings/` 目录存在
2. 检查文件权限
3. 确保工作区路径配置正确

### Cron 任务未运行

1. 检查 crontab：`crontab -l`
2. 检查日志：`cat ~/.openclaw/skills/self-evolution-cn/logs/heartbeat-daily.log`
3. 手动运行：`bash scripts/trigger-daily-review.sh`

### 软链接问题

1. 检查软链接：`ls -la ~/.openclaw/workspace-agent1/.learnings`
2. 检查共享目录：`ls -la $SHARED_LEARNING_DIR`
3. 重新运行配置脚本：`./scripts/setup.sh`

## Cron 任务

### 查看当前 Cron 任务

```bash
crontab -l | grep trigger-daily-review
```

### 手动触发检查

```bash
bash ~/.openclaw/skills/self-evolution-cn/scripts/daily_review.sh
```

### 查看执行日志

```bash
tail -f ~/.openclaw/skills/self-evolution-cn/logs/heartbeat-daily.log
```

## 最佳实践

1. **定期检查**：每天运行日检查以识别重复模式
2. **及时提升**：当学习被证明时立即提升
3. **保持简洁**：保持学习记录简洁，只包含必要信息
4. **使用模板**：使用提供的模板确保一致性

## 相关文档

- `SKILL.md` - 主要文档
- `references/format.md` - 记录格式
- `references/promotion.md` - 提升机制
- `references/multi-agent.md` - 多 agent 支持
- `references/hooks-setup.md` - Hook 配置
- `hooks/openclaw/HOOK.md` - Hook 说明


path: references/promotion.md
content: # 提升机制说明

## 何时提升

当学习广泛适用（不是一次性修复）时，提升到 SOUL.md。

### 提升条件

- 学习适用于多个文件/功能
- 任何贡献者（人类或 AI）都应该知道的知识
- 防止重复错误
- 记录项目特定的约定

## 自动提升规则

### 规则 A：重复 >= 3 次（自动）

**触发条件（每日 00:00 自动执行）：**
- 按 `Pattern-Key` 累计 `Recurrence-Count`
- 累计次数 >= 3 时触发提升

**自动操作：**
1. 提取 `### 摘要` 和 `### 建议行动`
2. 根据 `Area` 字段映射到对应的二级标题
3. 生成规则写入 `SOUL.md`
4. 更新原始记录：
   - `Status: promoted`
   - `Promoted: SOUL.md`
   - `Promoted-By: <agent_id>`

**提升格式：**
```markdown
### 一句话总结
一句话建议行动

---
```

**Area 映射规则：**
- `行为准则`|`行为模式`|`交互规范` → `## Core Truths（核心准则）`
- `工作流`|`工作流改进`|`任务分发` → `## 工作流程`
- `工具`|`配置`|`工具问题` → `## 工具使用`
- `边界`|`安全` → `## Boundaries（边界）`
- `风格`|`气质` → `## Vibe（风格气质）`
- `连续性`|`偏好` → `## Continuity（连续性）`
- 其他 → `## 其他`

**重要说明：**
- ✅ 所有 agent 的记录合并统计
- ✅ 按 `Pattern-Key` 累计 `Recurrence-Count`
- ✅ 共享目录：所有使用共享目录的智能体都提升
- ✅ 独立目录：对应智能体提升到自己的 SOUL.md

### 规则 B：高优先级（自动）

**触发条件：** `Priority: critical`

**自动操作：**
1. 立即提升到 SOUL.md
2. 标记为 `promoted`
3. 添加 `Promoted-By: <agent_id>`

### 规则 C：工作流/工具问题（自动）

**触发条件：** `Priority: high` 且 `Area: infra | config | tools`

**自动操作：**
1. 提升到 SOUL.md
2. 标记为 `promoted`
3. 添加 `Promoted-By: <agent_id>`

## 多 Agent 统计

### 统计逻辑

- 按 `Pattern-Key` 累计所有 agent 的 `Recurrence-Count`
- 累计次数 >= 3 时自动提升到 SOUL.md
- 不区分 agent，只看累计次数

### 示例

```markdown
## [ERR-20260326-001] git_push_without_pull
- Agent: agent1
- Pattern-Key: git.push.without.pull
- Recurrence-Count: 1

## [ERR-20260327-001] git_push_without_pull
- Agent: agent2
- Pattern-Key: git.push.without.pull
- Recurrence-Count: 1

## [ERR-20260328-001] git_push_without_pull
- Agent: agent3
- Pattern-Key: git.push.without.pull
- Recurrence-Count: 1
```

统计结果：`git.push.without.pull` 累计次数：3 → 触发自动提升

## 提升决策树

```
学习是否项目特定？
├── 是 → 保留在 .learnings/
└── 否 → 提升到 SOUL.md
```


path: scripts/activator.sh
content: #!/bin/bash
# Self-Evolution-CN Activator Hook
# 任务完成后提醒评估学习
# 保持输出最小化（~50-100 tokens）

set -e

# 输出提醒作为系统上下文
cat << 'EOF'
<self-evolution-reminder>
完成此任务后，评估是否需要记录学习：
- 发现了非显而易见的解决方案？
- 遇到了意外行为？
- 学到了项目特定的模式？
- 需要调试才能解决的错误？

如果是：使用 self-evolution-cn 技能格式记录到 .learnings/
如果是高价值（重复出现、广泛适用）：考虑技能提取
</self-evolution-reminder>
EOF


path: scripts/daily_review.sh
content: #!/bin/bash
# 自动统计与提升脚本
# 每日 00:00 自动执行，遍历所有智能体

set -e

# 获取共享学习目录
SHARED_LEARNING_DIR="${SHARED_LEARNING_DIR:-/root/.openclaw/shared-learning}"

# 获取当前 agent ID（用于日志记录）
CURRENT_AGENT_ID="${AGENT_ID:-main}"

# 工作区基础路径
WORKSPACE_BASE="/root/.openclaw"

# 是否启用自动提升（默认启用）
AUTO_PROMOTE_ENABLED="${AUTO_PROMOTE_ENABLED:-true}"

# 日志输出
log() {
    echo "$1"
}

# 获取所有智能体列表及其工作区
get_all_agents() {
    local agents=()
    
    if command -v openclaw &> /dev/null; then
        while IFS= read -r line; do
            if [[ $line =~ ^([a-zA-Z0-9_-]+)\ -\>\ (.+)$ ]]; then
                agents+=("${BASH_REMATCH[1]}:${BASH_REMATCH[2]}")
            fi
        done < <(openclaw config get agents.list 2>&1 | jq -r '.[] | "\(.id) -> \(.workspace)"' 2>/dev/null)
    fi
    
    # 如果没有找到，使用默认值
    if [ ${#agents[@]} -eq 0 ]; then
        agents=("main:$WORKSPACE_BASE/workspace-ql")
    fi
    
    echo "${agents[@]}"
}

# 检测学习目录路径
get_learning_dir() {
    local workspace="$1"
    local learnings_path="$workspace/.learnings"
    
    # 检查 .learnings 是否存在
    if [ ! -e "$learnings_path" ]; then
        echo "$SHARED_LEARNING_DIR"
        return
    fi
    
    # 检查是否是软连接
    if [ -L "$learnings_path" ]; then
        local target_path=$(readlink -f "$learnings_path")
        echo "$target_path"
        return
    fi
    
    # 如果是独立文件夹
    if [ -d "$learnings_path" ]; then
        echo "$learnings_path"
        return
    fi
    
    echo "$SHARED_LEARNING_DIR"
}

# 统计并更新 Recurrence-Count
update_recurrence_counts() {
    local learning_dir="$1"
    local errors_file="$learning_dir/ERRORS.md"
    local learnings_file="$learning_dir/LEARNINGS.md"
    
    # 临时文件用于存储 Pattern-Key 统计
    local temp_file=$(mktemp)
    
    # 提取所有 Pattern-Key 及其出现次数
    grep -oh "- Pattern-Key: [^$]*" "$errors_file" "$learnings_file" 2>/dev/null | \
        sed 's/- Pattern-Key: //' | \
        grep -v "^$" | \
        grep -v "^|" | \
        grep -v "^)" | \
        grep -v "^(" | \
        grep -v "^uniq" | \
        grep -v "^sort" | \
        grep -v "^grep" | \
        grep -v "^sed" | \
        grep -v "^awk" | \
        grep -v "^cut" | \
        grep -v "^自动生成" | \
        grep -v "^  " | \
        sort | uniq -c | \
        while read count key; do
            echo "$key:$count" >> "$temp_file"
        done
    
    # 对每个 Pattern-Key 更新记录
    if [ -f "$temp_file" ]; then
        while IFS=: read -r key count; do
            # 找到所有包含此 Pattern-Key 的记录
            local files=$(grep -l "- Pattern-Key: $key" "$errors_file" "$learnings_file" 2>/dev/null || true)
            
            if [ -n "$files" ]; then
                # 找到最早和最晚日期
                local first_seen=$(grep -B20 "- Pattern-Key: $key" $files 2>/dev/null | grep "Logged:" | head -1 | sed 's/.*Logged: //' | cut -d'T' -f1)
                local last_seen=$(grep -B20 "- Pattern-Key: $key" $files 2>/dev/null | grep "Logged:" | tail -1 | sed 's/.*Logged: //' | cut -d'T' -f1)
                
                # 更新所有相关记录
                for file in $files; do
                    # 使用 awk 更新特定 Pattern-Key 的 Recurrence-Count
                    awk -v key="$key" -v count="$count" -v first="$first_seen" -v last="$last_seen" '
                    BEGIN { in_record = 0; found_key = 0; updated = 0 }
                    /^## \[/ {
                        if (in_record && found_key && !updated) {
                            # 上一个记录有 Pattern-Key 但未更新，插入
                            print "- Recurrence-Count: " count
                            print "- First-Seen: " first
                            print "- Last-Seen: " last
                        }
                        in_record = 1
                        found_key = 0
                        updated = 0
                    }
                    in_record && /- Pattern-Key: / {
                        if ($0 ~ "- Pattern-Key: " key) {
                            found_key = 1
                        }
                    }
                    in_record && /Recurrence-Count:/ && found_key && !updated {
                        sub(/Recurrence-Count: [0-9]+/, "Recurrence-Count: " count)
                        updated = 1
                    }
                    in_record && /First-Seen:/ && found_key {
                        sub(/First-Seen: .*/, "First-Seen: " first)
                    }
                    in_record && /Last-Seen:/ && found_key {
                        sub(/Last-Seen: .*/, "Last-Seen: " last)
                    }
                    { print }
                    END {
                        if (in_record && found_key && !updated) {
                            print "- Recurrence-Count: " count
                            print "- First-Seen: " first
                            print "- Last-Seen: " last
                        }
                    }
                    ' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
                done
            fi
        done < "$temp_file"
        rm -f "$temp_file"
    fi
}

# 自动提升
auto_promote() {
    local learning_dir="$1"
    local agent_id="$2"
    local workspace_dir="$3"
    local soul_file="$workspace_dir/SOUL.md"
    
    local errors_file="$learning_dir/ERRORS.md"
    local learnings_file="$learning_dir/LEARNINGS.md"
    
    local promoted_count=0
    
    # 查找候选项（Recurrence-Count >= 3）
    local candidates=$(grep -l "Recurrence-Count: [3-9]" "$errors_file" "$learnings_file" 2>/dev/null || true)
    
    if [ -z "$candidates" ]; then
        return 0
    fi
    
    for file in $candidates; do
        # 提取所有候选项的 Pattern-Key（去重）
        local pattern_keys=$(awk '/^## \[/ { in_record = 1 }
 in_record && /- Pattern-Key: / { 
    match($0, /- Pattern-Key: [^$]*/)
    key = substr($0, RSTART + 15, RLENGTH - 15)
    print key
    in_record = 0
 }
 /^---/ { in_record = 0 }' "$file" | sort -u)
        
        if [ -z "$pattern_keys" ]; then
            continue
        fi
        
        # 对每个 Pattern-Key 进行提升
        for pattern_key in $pattern_keys; do
        
        # 检查是否已提升（检查该 Pattern-Key 下是否有记录已提升）
        local promoted_by=$(awk -v pattern="$pattern_key" '
        BEGIN { in_record = 0; found = 0 }
        /^## \[/ {
            if (in_record && found) {
                exit
            }
            in_record = 1
            found = 0
        }
        in_record && /- Pattern-Key: / {
            if ($0 ~ "- Pattern-Key: " pattern) {
                found = 1
            }
        }
        in_record && /Promoted-By:/ && found {
            print $0
            exit
        }
        ' "$file" | sed 's/.*Promoted-By: //')
        
        if [ -n "$promoted_by" ] && echo "$promoted_by" | grep -q "$agent_id"; then
            # 当前 agent 已提升，跳过
            continue
        fi
        
        # 提取该 Pattern-Key 下第一条记录的摘要和建议行动
        local summary=$(awk -v pattern="$pattern_key" '
        BEGIN { in_record = 0; found = 0; in_summary = 0 }
        /^## \[/ {
            if (in_record && found) {
                exit
            }
            in_record = 1
            found = 0
        }
        in_record && /- Pattern-Key: / {
            if ($0 ~ "- Pattern-Key: " pattern) {
                found = 1
            }
        }
        in_record && /### 摘要/ {
            in_summary = 1
            next
        }
        in_summary && /^###/ {
            in_summary = 0
        }
        in_summary {
            print
        }
        ' "$file")
        
        local action=$(awk -v pattern="$pattern_key" '
        BEGIN { in_record = 0; found = 0; in_action = 0 }
        /^## \[/ {
            if (in_record && found) {
                exit
            }
            in_record = 1
            found = 0
        }
        in_record && /- Pattern-Key: / {
            if ($0 ~ "- Pattern-Key: " pattern) {
                found = 1
            }
        }
        in_record && /### 建议行动/ {
            in_action = 1
            next
        }
        in_action && /^###/ {
            in_action = 0
        }
        in_action {
            print
        }
        ' "$file")
        
        # 提取 Area 字段
        local area=$(awk -v pattern="$pattern_key" '
        BEGIN { in_record = 0; found = 0 }
        /^## \[/ {
            if (in_record && found) {
                exit
            }
            in_record = 1
            found = 0
        }
        in_record && /^- Area: / {
            area = $0
        }
        in_record && /- Pattern-Key: / {
            if ($0 ~ "- Pattern-Key: " pattern) {
                found = 1
                print area
                exit
            }
        }
        ' "$file" | sed 's/.*Area: //')
        
        if [ -z "$summary" ]; then
            summary="自动提升的规则"
        fi
        
        if [ -z "$area" ]; then
            area="其他"
        fi
        
        # 检查是否启用自动提升
        if [ "$AUTO_PROMOTE_ENABLED" != "true" ]; then
            log "    跳过提升（AUTO_PROMOTE_ENABLED=$AUTO_PROMOTE_ENABLED）"
            continue
        fi
        
        # 根据 Area 映射到二级标题
        local section_header=""
        case "$area" in
            "行为准则"|"行为模式"|"交互规范")
                section_header="## Core Truths（核心准则）"
                ;;
            "工作流"|"工作流改进"|"任务分发")
                section_header="## 工作流程"
                ;;
            "工具"|"配置"|"工具问题")
                section_header="## 工具使用"
                ;;
            "边界"|"安全")
                section_header="## Boundaries（边界）"
                ;;
            "风格"|"气质")
                section_header="## Vibe（风格气质）"
                ;;
            "连续性"|"偏好")
                section_header="## Continuity（连续性）"
                ;;
            *)
                section_header="## 其他"
                ;;
        esac
        
        # 生成规则内容
        local rule_content="
### $summary
$action

---
"
        
        # 查找或创建二级标题，然后追加内容
        if grep -q "^$section_header" "$soul_file"; then
            # 找到二级标题，追加内容
            # 使用临时文件传递多行内容
            echo "$rule_content" > /tmp/rule_content.txt
            awk -v section="$section_header" '
            BEGIN { in_section = 0; found = 0 }
            /^## / {
                if (in_section) {
                    # 找到下一个二级标题，在之前插入
                    while ((getline line < "/tmp/rule_content.txt") > 0) {
                        print line
                    }
                    close("/tmp/rule_content.txt")
                    print $0
                    exit
                }
                if ($0 == section) {
                    in_section = 1
                }
            }
            { print }
            END {
                if (in_section) {
                    # 没有找到下一个二级标题，追加到文件末尾
                    while ((getline line < "/tmp/rule_content.txt") > 0) {
                        print line
                    }
                    close("/tmp/rule_content.txt")
                }
            }
            ' "$soul_file" > "$soul_file.tmp" && mv "$soul_file.tmp" "$soul_file"
            rm -f /tmp/rule_content.txt
        else
            # 没有找到二级标题，创建新的
            echo "" >> "$soul_file"
            echo "$section_header" >> "$soul_file"
            echo "$rule_content" >> "$soul_file"
        fi
        
        # 更新该 Pattern-Key 下所有记录的状态
        awk -v pattern="$pattern_key" -v agent_id="$agent_id" '
        BEGIN { in_record = 0; found = 0; in_metadata = 0 }
        /^## \[/ {
            if (in_record && found && in_metadata) {
                # 记录结束，检查是否需要添加 Promoted 字段
                if (!has_promoted) {
                    print "- Promoted: SOUL.md"
                }
                if (!has_promoted_by) {
                    print "- Promoted-By: " agent_id
                }
            }
            in_record = 1
            found = 0
            in_metadata = 0
            has_promoted = 0
            has_promoted_by = 0
        }
        in_record && /- Pattern-Key: / {
            if ($0 ~ "- Pattern-Key: " pattern) {
                found = 1
            }
        }
        in_record && found && /^###/ {
            # 进入内容部分，元数据结束
            in_metadata = 0
        }
        in_record && found && /^---/ {
            # 记录结束
            if (!has_promoted) {
                print "- Promoted: SOUL.md"
            }
            if (!has_promoted_by) {
                print "- Promoted-By: " agent_id
            }
            in_record = 0
        }
        in_record && found && /^-/ {
            # 元数据行
            in_metadata = 1
            if (/^- Status: pending/) {
                sub(/Status: pending/, "Status: promoted")
            }
            if (/^- Promoted:/) {
                has_promoted = 1
            }
            if (/^- Promoted-By:/) {
                has_promoted_by = 1
                if ($0 !~ /Promoted-By:.*'" agent_id "'/) {
                    sub(/Promoted-By: /, "Promoted-By: " agent_id ", ")
                }
            }
        }
        { print }
        END {
            if (in_record && found && in_metadata) {
                if (!has_promoted) {
                    print "- Promoted: SOUL.md"
                }
                if (!has_promoted_by) {
                    print "- Promoted-By: " agent_id
                }
            }
        }
        ' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
        
        promoted_count=$((promoted_count + 1))
        done  # 结束内层循环（for pattern_key）
    done  # 结束外层循环（for file）
    
    echo "$promoted_count"
}

# 主流程
main() {
    log "=== 自动统计与提升（所有智能体） ==="
    log ""
    
    # 获取所有智能体
    local agents=$(get_all_agents)
    
    # 分类：共享目录组和独立目录组
    declare -A shared_agents
    declare -A independent_agents
    
    for agent_entry in $agents; do
        local agent_id=$(echo "$agent_entry" | cut -d: -f1)
        local workspace=$(echo "$agent_entry" | cut -d: -f2-)
        local learning_dir=$(get_learning_dir "$workspace")
        
        if [ "$learning_dir" = "$SHARED_LEARNING_DIR" ]; then
            shared_agents["$agent_id"]="$workspace"
        else
            independent_agents["$agent_id"]="$workspace:$learning_dir"
        fi
    done
    
    # 处理共享目录
    if [ ${#shared_agents[@]} -gt 0 ]; then
        log "共享目录："
        
        # 统计共享目录
        update_recurrence_counts "$SHARED_LEARNING_DIR"
        
        # 找出候选项
        local shared_candidates=$(grep -l "Recurrence-Count: [3-9]" "$SHARED_LEARNING_DIR"/*.md 2>/dev/null || true)
        local shared_candidate_count=0
        if [ -n "$shared_candidates" ]; then
            shared_candidate_count=$(echo "$shared_candidates" | xargs grep -c "Recurrence-Count: [3-9]" 2>/dev/null | awk '{sum+=$1} END {print sum+0}')
        fi
        
        log "  发现候选项：$shared_candidate_count 条"
        log "  需要提升的智能体：${!shared_agents[*]}"
        
        # 依次提升
        for agent_id in "${!shared_agents[@]}"; do
            local workspace="${shared_agents[$agent_id]}"
            local promoted=$(auto_promote "$SHARED_LEARNING_DIR" "$agent_id" "$workspace")
            log "  提升 $agent_id：$promoted 条"
        done
        
        log ""
    fi
    
    # 处理独立目录
    if [ ${#independent_agents[@]} -gt 0 ]; then
        log "独立目录："
        
        for agent_id in "${!independent_agents[@]}"; do
            local entry="${independent_agents[$agent_id]}"
            local workspace=$(echo "$entry" | cut -d: -f1)
            local learning_dir=$(echo "$entry" | cut -d: -f2)
            
            log "  $agent_id："
            
            # 统计
            update_recurrence_counts "$learning_dir"
            
            # 找出候选项
            local ind_candidates=$(grep -l "Recurrence-Count: [3-9]" "$learning_dir"/*.md 2>/dev/null || true)
            local ind_candidate_count=0
            if [ -n "$ind_candidates" ]; then
                ind_candidate_count=$(echo "$ind_candidates" | xargs grep -c "Recurrence-Count: [3-9]" 2>/dev/null | awk '{sum+=$1} END {print sum+0}')
            fi
            
            # 提升
            local promoted=$(auto_promote "$learning_dir" "$agent_id" "$workspace")
            log "    发现候选项：$ind_candidate_count 条"
            log "    提升：$promoted 条"
        done
        
        log ""
    fi
    
    log "完成"
}

# 执行
main


path: scripts/error-detector.sh
content: #!/bin/bash
# Self-Evolution-CN Error Detector Hook
# 检测命令失败并提醒记录错误
# 读取 CLAUDE_TOOL_OUTPUT 环境变量

set -e

# 检查工具输出是否指示错误
# CLAUDE_TOOL_OUTPUT 包含工具执行的结果
OUTPUT="${CLAUDE_TOOL_OUTPUT:-}"

# 指示错误的模式（不区分大小写匹配）
ERROR_PATTERNS=(
    "error:"
    "Error:"
    "ERROR:"
    "failed"
    "FAILED"
    "command not found"
    "No such file"
    "Permission denied"
    "fatal:"
    "Exception"
    "Traceback"
    "npm ERR!"
    "ModuleNotFoundError"
    "SyntaxError"
    "TypeError"
    "exit code"
    "non-zero"
)

# 检查输出是否包含任何错误模式
contains_error=false
for pattern in "${ERROR_PATTERNS[@]}"; do
    if [[ "$OUTPUT" == *"$pattern"* ]]; then
        contains_error=true
        break
    fi
done

# 仅在检测到错误时输出提醒
if [ "$contains_error" = true ]; then
    cat << 'EOF'
<error-detected>
检测到命令错误。如果满足以下条件，考虑记录到 .learnings/ERRORS.md：
- 错误是意外或非显而易见的
- 需要调查才能解决
- 可能在类似上下文中重复出现
- 解决方案可能对未来会话有帮助

使用 self-evolution-cn 技能格式：[ERR-YYYYMMDD-XXX]
</error-detected>
EOF
fi


path: scripts/extract-skill.sh
content: #!/bin/bash
# 技能提取助手
# 从学习条目创建新技能
# 用法：./extract-skill.sh <skill-name> [--dry-run]

set -e

# 配置
SKILLS_DIR="./skills"

# 输出颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 无颜色

usage() {
    cat << EOF
用法：$(basename "$0") <skill-name> [选项]

从学习条目创建新技能。

参数：
  skill-name     技能名称（小写，空格用连字符）

选项：
  --dry-run      显示将要创建的内容而不实际创建文件
  --output-dir   当前路径下的相对输出目录（默认：./skills）
  -h, --help     显示此帮助信息

示例：
  $(basename "$0") docker-m1-fixes
  $(basename "$0") api-timeout-patterns --dry-run
  $(basename "$0") pnpm-setup --output-dir ./skills/custom

技能将创建于：\$SKILLS_DIR/<skill-name>/
EOF
}

log_info() {
    echo -e "${GREEN}[信息]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[警告]${NC} $1"
}

log_error() {
    echo -e "${RED}[错误]${NC} $1" >&2
}

# 解析参数
SKILL_NAME=""
DRY_RUN=false

while [[ $# -gt 0 ]]; do
    case $1 in
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        --output-dir)
            if [ -z "${2:-}" ] || [[ "${2:-}" == -* ]]; then
                log_error "--output-dir 需要相对路径参数"
                usage
                exit 1
            fi
            SKILLS_DIR="$2"
            shift 2
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        -*)
            log_error "未知选项：$1"
            usage
            exit 1
            ;;
        *)
            if [ -z "$SKILL_NAME" ]; then
                SKILL_NAME="$1"
            else
                log_error "意外参数：$1"
                usage
                exit 1
            fi
            shift
            ;;
    esac
done

# 验证技能名称
if [ -z "$SKILL_NAME" ]; then
    log_error "技能名称是必需的"
    usage
    exit 1
fi

# 验证技能名称格式（小写、连字符、无空格）
if ! [[ "$SKILL_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
    log_error "技能名称格式无效。仅使用小写字母、数字和连字符。"
    log_error "示例：'docker-fixes'、'api-patterns'、'pnpm-setup'"
    exit 1
fi

# 验证输出路径以避免写入当前工作区之外
if [[ "$SKILLS_DIR" = /* ]]; then
    log_error "输出目录必须是当前目录下的相对路径。"
    exit 1
fi

if [[ "$SKILLS_DIR" =~ (^|/)\.\.(/|$) ]]; then
    log_error "输出目录不能包含 '..' 路径段。"
    exit 1
fi

SKILLS_DIR="${SKILLS_DIR#./}"
SKILLS_DIR="./$SKILLS_DIR"

SKILL_PATH="$SKILLS_DIR/$SKILL_NAME"

# 检查技能是否已存在
if [ -d "$SKILL_PATH" ] && [ "$DRY_RUN" = false ]; then
    log_error "技能已存在：$SKILL_PATH"
    log_error "使用不同的名称或先删除现有技能。"
    exit 1
fi

# 试运行输出
if [ "$DRY_RUN" = true ]; then
    log_info "试运行 - 将创建："
    echo "  $SKILL_PATH/"
    echo "  $SKILL_PATH/SKILL.md"
    echo ""
    echo "模板内容将是："
    echo "---"
    cat << TEMPLATE
---
name: $SKILL_NAME
description: "[TODO：添加此技能功能的简明描述以及何时使用]"
---

# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')

[TODO：简要解释技能目的的介绍]

## 快速参考

| 情况 | 操作 |
|------|------|
| [触发条件] | [要做什么] |

## 使用方法

[TODO：详细使用说明]

## 示例

[TODO：添加具体示例]

## 来源学习

此技能从学习条目提取。
- 学习 ID：[TODO：添加原始学习 ID]
- 原始文件：.learnings/LEARNINGS.md
TEMPLATE
    echo "---"
    exit 0
fi

# 创建技能目录结构
log_info "创建技能：$SKILL_NAME"

mkdir -p "$SKILL_PATH"

# 从模板创建 SKILL.md
cat > "$SKILL_PATH/SKILL.md" << TEMPLATE
---
name: $SKILL_NAME
description: "[TODO：添加此技能功能的简明描述以及何时使用]"
---

# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')

[TODO：简要解释技能目的的介绍]

## 快速参考

| 情况 | 操作 |
|------|------|
| [触发条件] | [要做什么] |

## 使用方法

[TODO：详细使用说明]

## 示例

[TODO：添加具体示例]

## 来源学习

此技能从学习条目提取。
- 学习 ID：[TODO：添加原始学习 ID]
- 原始文件：.learnings/LEARNINGS.md
TEMPLATE

log_info "已创建：$SKILL_PATH/SKILL.md"

# 建议下一步
echo ""
log_info "技能脚手架创建成功！"
echo ""
echo "下一步："
echo "  1. 编辑 $SKILL_PATH/SKILL.md"
echo "  2. 用学习内容填充 TODO 部分"
echo "  3. 如有详细文档，添加 references/ 文件夹"
echo "  4. 如有可执行代码，添加 scripts/ 文件夹"
echo "  5. 更新原始学习条目："
echo "     **Status**: promoted_to_skill"
echo "     **Skill-Path**: skills/$SKILL_NAME"


path: scripts/setup.sh
content: #!/bin/bash
# 一键配置脚本
# 自动配置 self-evolution-cn 技能

set -e

# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # 无颜色

log_info() {
    echo -e "${GREEN}[信息]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[警告]${NC} $1"
}

log_error() {
    echo -e "${RED}[错误]${NC} $1" >&2
}

log_step() {
    echo -e "${BLUE}[步骤]${NC} $1"
}

# 获取脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"

# 默认配置
SHARED_LEARNING_DIR="${SHARED_LEARNING_DIR:-/root/.openclaw/shared-learning}"
OPENCLAW_DIR="${OPENCLAW_DIR:-/root/.openclaw}"

log_info "开始配置 self-evolution-cn 技能..."
echo ""

# 步骤 1：检查 OpenClaw 安装
log_step "1. 检查 OpenClaw 安装..."
if ! command -v openclaw &> /dev/null; then
    log_error "未找到 OpenClaw。请先安装 OpenClaw。"
    exit 1
fi
log_info "OpenClaw 已安装：$(openclaw --version)"
echo ""

# 步骤 2：创建共享目录
log_step "2. 创建共享目录..."
mkdir -p "$SHARED_LEARNING_DIR"
mkdir -p "$SKILL_DIR/logs"
log_info "共享目录：$SHARED_LEARNING_DIR"
log_info "日志目录：$SKILL_DIR/logs"
echo ""

# 步骤 2.5：设置脚本执行权限
log_step "2.5. 设置脚本执行权限..."
chmod +x "$SCRIPT_DIR"/*.sh
log_info "脚本执行权限已设置"
echo ""

# 步骤 3：初始化状态文件
log_step "3. 初始化状态文件..."
if [ ! -f "$SKILL_DIR/heartbeat-state.json" ]; then
    echo '{"agents":{}}' > "$SKILL_DIR/heartbeat-state.json"
    log_info "状态文件已创建：$SKILL_DIR/heartbeat-state.json"
else
    log_info "状态文件已存在：$SKILL_DIR/heartbeat-state.json"
fi
echo ""

# 步骤 4：复制模板文件到共享目录
log_step "4. 复制模板文件到共享目录..."
cp "$SKILL_DIR/.learnings/ERRORS.md" "$SHARED_LEARNING_DIR/"
cp "$SKILL_DIR/.learnings/LEARNINGS.md" "$SHARED_LEARNING_DIR/"
cp "$SKILL_DIR/.learnings/FEATURE_REQUESTS.md" "$SHARED_LEARNING_DIR/"
log_info "模板文件已复制到共享目录"
echo ""

# 步骤 5：遍历所有 agent 工作区
log_step "5. 配置所有 agent 工作区..."

# 默认共享的 agent（主 agent）
DEFAULT_AGENTS="workspace-agent1"

# 从环境变量获取需要共享的 agent 列表
SHARED_AGENTS="${SHARED_AGENTS:-$DEFAULT_AGENTS}"

# 将空格分隔的列表转换为数组
IFS=' ' read -ra SHARED_AGENT_ARRAY <<< "$SHARED_AGENTS"

log_info "需要共享的 agent：$SHARED_AGENTS"
echo ""

# 遍历所有 workspace-* 目录
for agent_dir in "$OPENCLAW_DIR"/workspace-*; do
    if [ -d "$agent_dir" ]; then
        agent_name=$(basename "$agent_dir")
        learnings_path="$agent_dir/.learnings"

        # 检查是否需要共享
        is_shared=false
        for shared_agent in "${SHARED_AGENT_ARRAY[@]}"; do
            if [ "$shared_agent" = "$agent_name" ]; then
                is_shared=true
                break
            fi
        done

        if [ "$is_shared" = true ]; then
            # 需要共享：创建软链接
            log_info "配置共享 agent：$agent_name"

            # 如果存在软链接，删除重建
            if [ -L "$learnings_path" ]; then
                rm -f "$learnings_path"
                log_info "  删除旧软链接"
            fi

            # 如果存在目录，先备份
            if [ -d "$learnings_path" ]; then
                backup_path="$learnings_path.backup.$(date +%Y%m%d%H%M%S)"
                mv "$learnings_path" "$backup_path"
                log_info "  备份现有目录到：$backup_path"
            fi

            # 创建软链接
            ln -s "$SHARED_LEARNING_DIR" "$learnings_path"
            log_info "  创建软链接：$learnings_path → $SHARED_LEARNING_DIR"
        else
            # 不需要共享：创建独立目录
            log_info "配置独立 agent：$agent_name"

            # 如果存在软链接，删除
            if [ -L "$learnings_path" ]; then
                rm -f "$learnings_path"
                log_info "  删除旧软链接"
            fi

            # 如果存在目录，先备份
            if [ -d "$learnings_path" ]; then
                backup_path="$learnings_path.backup.$(date +%Y%m%d%H%M%S)"
                mv "$learnings_path" "$backup_path"
                log_info "  备份现有目录到：$backup_path"
            fi

            # 创建独立目录
            mkdir -p "$learnings_path"
            log_info "  创建独立目录：$learnings_path"

            # 复制模板文件
            cp "$SKILL_DIR/.learnings/ERRORS.md" "$learnings_path/"
            cp "$SKILL_DIR/.learnings/LEARNINGS.md" "$learnings_path/"
            cp "$SKILL_DIR/.learnings/FEATURE_REQUESTS.md" "$learnings_path/"
            log_info "  复制模板文件到独立目录"
        fi
    fi
done
echo ""

# 步骤 6：配置 hook
log_step "6. 配置 hook..."
HOOK_DIR="$OPENCLAW_DIR/hooks/self-evolution-cn"
if [ -d "$HOOK_DIR" ]; then
    rm -rf "$HOOK_DIR"
    log_info "  删除旧 hook 目录"
fi
cp -r "$SKILL_DIR/hooks/openclaw" "$HOOK_DIR"
log_info "Hook 已复制到：$HOOK_DIR"

# 启用 hook
if openclaw hooks enable self-evolution-cn 2>/dev/null; then
    log_info "Hook 已启用"
else
    log_warn "Hook 启用失败，可能需要手动启用"
fi
echo ""

# 步骤 7：设置 cron 任务
log_step "7. 设置 cron 任务..."
LOG_FILE="$SKILL_DIR/logs/heartbeat-daily.log"
CRON_JOB="0 0 * * * $SCRIPT_DIR/trigger-daily-review.sh >> $LOG_FILE 2>&1"

# 删除旧的 cron 任务
crontab -l 2>/dev/null | grep -v "trigger-daily-review.sh" | crontab -
log_info "  删除旧 cron 任务"

# 添加新的 cron 任务
(crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab -
log_info "Cron 任务已添加"
echo ""

# 步骤 8：验证配置
log_step "8. 验证配置..."
echo ""

# 检查共享目录
if [ -d "$SHARED_LEARNING_DIR" ]; then
    log_info "✓ 共享目录存在：$SHARED_LEARNING_DIR"
else
    log_error "✗ 共享目录不存在：$SHARED_LEARNING_DIR"
fi

# 检查模板文件
if [ -f "$SHARED_LEARNING_DIR/ERRORS.md" ] && [ -f "$SHARED_LEARNING_DIR/LEARNINGS.md" ] && [ -f "$SHARED_LEARNING_DIR/FEATURE_REQUESTS.md" ]; then
    log_info "✓ 共享目录模板文件存在"
else
    log_error "✗ 共享目录模板文件缺失"
fi

# 检查所有 agent 的配置
for agent_dir in "$OPENCLAW_DIR"/workspace-*; do
    if [ -d "$agent_dir" ]; then
        agent_name=$(basename "$agent_dir")
        learnings_path="$agent_dir/.learnings"

        # 检查是否需要共享
        is_shared=false
        for shared_agent in "${SHARED_AGENT_ARRAY[@]}"; do
            if [ "$shared_agent" = "$agent_name" ]; then
                is_shared=true
                break
            fi
        done

        if [ "$is_shared" = true ]; then
            # 检查软链接
            if [ -L "$learnings_path" ]; then
                log_info "✓ 软链接存在：$agent_name/.learnings"
            else
                log_error "✗ 软链接不存在：$agent_name/.learnings"
            fi
        else
            # 检查独立目录
            if [ -d "$learnings_path" ]; then
                if [ -f "$learnings_path/ERRORS.md" ] && [ -f "$learnings_path/LEARNINGS.md" ] && [ -f "$learnings_path/FEATURE_REQUESTS.md" ]; then
                    log_info "✓ 独立目录存在：$agent_name/.learnings"
                else
                    log_error "✗ 独立目录文件缺失：$agent_name/.learnings"
                fi
            else
                log_error "✗ 独立目录不存在：$agent_name/.learnings"
            fi
        fi
    fi
done

# 检查 hook
if openclaw hooks list 2>/dev/null | grep -q "self-evolution-cn"; then
    log_info "✓ Hook 已启用"
else
    log_warn "✗ Hook 未启用"
fi

# 检查 cron
if crontab -l 2>/dev/null | grep -q "trigger-daily-review.sh"; then
    log_info "✓ Cron 任务已设置"
else
    log_warn "✗ Cron 任务未设置"
fi

echo ""
log_info "配置完成！"
echo ""
echo "下一步："
echo "  1. 测试技能：在对话中说'执行日检查'"
echo "  2. 查看学习记录：cat $SHARED_LEARNING_DIR/ERRORS.md"
echo "  3. 手动触发检查：bash $SCRIPT_DIR/daily_review.sh"
echo ""
echo "配置其他 agent："
echo "  export SHARED_AGENTS=\"workspace-agent1 workspace-agent2 workspace-agent3\""
echo "  ./scripts/setup.sh"
echo ""
echo "环境变量："
echo "  export SHARED_LEARNING_DIR=\"$SHARED_LEARNING_DIR\""
echo "  export SHARED_AGENTS=\"$SHARED_AGENTS\""
echo ""


path: scripts/trigger-daily-review.sh
content: #!/bin/bash
# Cron 触发脚本
# 每日 00:00 自动触发，遍历所有 agent

set -e

# 获取脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# 获取共享学习目录
SHARED_LEARNING_DIR="${SHARED_LEARNING_DIR:-/root/.openclaw/shared-learning}"

# 日志文件
LOG_FILE="$SCRIPT_DIR/../logs/heartbeat-daily.log"

# 状态文件
STATE_FILE="$SCRIPT_DIR/../heartbeat-state.json"

# 确保日志目录存在
mkdir -p "$(dirname "$LOG_FILE")"

# 记录执行
echo "=== Self-Evolution-CN Daily Review (All Agents): $(date '+%Y-%m-%d %H:%M:%S') ===" >> "$LOG_FILE"

# 获取所有 agent 列表
WORKSPACE_BASE="/root/.openclaw"
AGENTS=()

# 获取 agent ID 和 workspace 的对应关系
declare -A AGENT_WORKSPACES
if command -v openclaw &> /dev/null; then
    # 使用 OpenClaw 配置 API 获取 agent 列表
    while IFS= read -r line; do
        if [[ $line =~ ^([a-zA-Z0-9_-]+)\ -\>\ (.+)$ ]]; then
            agent_id="${BASH_REMATCH[1]}"
            workspace="${BASH_REMATCH[2]}"
            AGENTS+=("$agent_id")
            AGENT_WORKSPACES["$agent_id"]="$workspace"
        fi
    done < <(openclaw config get agents.list 2>&1 | jq -r '.[] | "\(.id) -> \(.workspace)"' 2>/dev/null)
fi

# 如果没有找到 agent，使用默认值
if [ ${#AGENTS[@]} -eq 0 ]; then
    AGENTS=("agent1")
    AGENT_WORKSPACES["agent1"]="$WORKSPACE_BASE/workspace-agent1"
fi

echo "找到 ${#AGENTS[@]} 个 agent: ${AGENTS[*]}" >> "$LOG_FILE"

# 遍历所有 agent
for AGENT_ID in "${AGENTS[@]}"; do
    echo "" >> "$LOG_FILE"
    echo "--- Processing agent: $AGENT_ID ---" >> "$LOG_FILE"

    # 检查今日是否已执行
    if [ -f "$STATE_FILE" ]; then
        LAST_DATE=$(cat "$STATE_FILE" | grep -o "\"$AGENT_ID\".*\"last_execution_date\":\"[^\"]*\"" | grep -o '"last_execution_date":"[^"]*"' | cut -d'"' -f4 | head -1)
        TODAY=$(date '+%Y-%m-%d')

        if [ "$LAST_DATE" = "$TODAY" ]; then
            echo "今日已执行日检查（$TODAY），跳过。" >> "$LOG_FILE"
            continue
        fi
    fi

    # 运行日检查脚本
    echo "执行日检查..." >> "$LOG_FILE"
    AGENT_ID="$AGENT_ID" bash "$SCRIPT_DIR/daily_review.sh" >> "$LOG_FILE" 2>&1

    # 更新状态文件
    # 如果状态文件不存在，创建空结构
    if [ ! -f "$STATE_FILE" ]; then
        echo '{"agents":{}}' > "$STATE_FILE"
    fi

    # 更新当前 agent 的状态
    CURRENT_TIME=$(date '+%Y-%m-%dT%H:%M:%S%:z')
    TODAY=$(date '+%Y-%m-%d')

    # 使用 jq 更新状态文件（如果可用），否则使用 sed
    if command -v jq &> /dev/null; then
        jq --arg agent "$AGENT_ID" --arg date "$TODAY" --arg time "$CURRENT_TIME" \
            '.agents[$agent] = {
                "last_execution_date": $date,
                "last_execution_time": $time,
                "status": "completed"
            }' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
    else
        # 使用 sed 更新（简单实现）
        # 注意：这不是完美的 JSON 更新，但足够用于基本用途
        sed -i "s/\"$AGENT_ID\":{[^}]*}/\"$AGENT_ID\":{\"last_execution_date\":\"$TODAY\",\"last_execution_time\":\"$CURRENT_TIME\",\"status\":\"completed\"}/g" "$STATE_FILE"
        # 如果 agent 不存在，添加它
        if ! grep -q "\"$AGENT_ID\":" "$STATE_FILE"; then
            sed -i "s/{/{\"$AGENT_ID\":{\"last_execution_date\":\"$TODAY\",\"last_execution_time\":\"$CURRENT_TIME\",\"status\":\"completed\"},/" "$STATE_FILE"
        fi
    fi

    echo "状态已更新：agent = $AGENT_ID, last_execution_date = $TODAY" >> "$LOG_FILE"
done

echo "" >> "$LOG_FILE"
echo "=== Self-Evolution-CN Daily Review Complete (All Agents) ===" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"

exit 0


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