auto: sync OpenClaw config 2026-09-16 16:13

This commit is contained in:
2026-09-16 16:13:36 +08:00
parent c51006a57d
commit 5ac4289f66
35 changed files with 4827 additions and 65 deletions
+111
View File
@@ -70,6 +70,117 @@
"status": "unsigned"
}
}
},
"multi-search-engine": {
"version": "2.1.3",
"registry": "https://clawhub.ai",
"ownerHandle": "gpyangyoujun",
"installedAt": 1789545766661,
"artifact": {
"kind": "archive",
"sha256": "c2dc9053c07bb6bbc569727b8fee823119b664026b58587d15e1858d190df8f0",
"integrity": "sha256-wtyQU8B7trvFaXJ7j+6CMRm2ZAJrWFh9FeGFjRkN+PA="
},
"skillFile": {
"path": "SKILL.md",
"sha256": "a089b7928eeff57cc50dfa87cfe6f630f227dafc3ed7f92af043b7edb0866796"
},
"fileTreeSha256": "sha256:be51286be5077a1857a94d6578780746af03b6ed9ec0f9630efb846966086aa3",
"verification": {
"schema": "clawhub.skill.verify.v1",
"ok": false,
"decision": "fail",
"reasons": [
"security.status_not_clean"
],
"card": {
"available": true,
"path": "skill-card.md",
"url": "https://wry-manatee-359.convex.site/api/v1/skills/multi-search-engine/card?ownerHandle=gpyangyoujun&version=2.1.3",
"sha256": "cbc87481998be68209542ef2e280a0066366046f9a4f100b92cc261413d71682",
"size": 1981,
"contentType": "text/markdown; charset=utf-8"
},
"artifact": {
"sourceFingerprint": "844be0440619bddf240f394c6cbd1c6ad9591305c57019d877dafac2abc4cfce",
"bundleFingerprints": [
"327434e13d0409ecd6f0f0114e832820ddeaadd0adba7eaee98ce074c2bd4257",
"201f9a4c4e604a372f0cb8f47bb51022a00b902d2730ae3e639f3f41a176c06a",
"b6fad8d918cbdf3ce7050a5afd1901186051ed577f04c842bc2e5aa934180f01"
],
"files": [
{
"path": "_meta.json",
"size": 138,
"sha256": "c519005e390529c64cea5e920e579f2c840dfa6fe3c51d01a752f7cd3a469830",
"contentType": "application/json"
},
{
"path": "CHANGELOG.md",
"size": 424,
"sha256": "5514ba8a17f4dd66e6a7bfaa9a093a1be944bea0c2a33b6733696caf4cea4e2f",
"contentType": "text/markdown"
},
{
"path": "config.json",
"size": 1966,
"sha256": "e134d2a4543fefb4f422c2a98d12aa5e9910359fae2d6030e72af6e7b66ad6a9",
"contentType": "application/json"
},
{
"path": "metadata.json",
"size": 238,
"sha256": "b5986a0ccad610875beac1dbecf932a127e878092ffd8a7b1a9c8a9fd8e451e7",
"contentType": "application/json"
},
{
"path": "CHANNELLOG.md",
"size": 1146,
"sha256": "e5a45aa16ad78fb26606ed1d27c496d1ea6b51fad63f87110a44f05c77042f67",
"contentType": "text/markdown"
},
{
"path": "SKILL.md",
"size": 6203,
"sha256": "a089b7928eeff57cc50dfa87cfe6f630f227dafc3ed7f92af043b7edb0866796",
"contentType": "text/markdown"
},
{
"path": "references/advanced-search.md",
"size": 4018,
"sha256": "e01db27db827d9d0a490bfa95f0dd76aa5c8fe5a382a967340d0abe7e1e28a02",
"contentType": "text/markdown"
},
{
"path": "references/international-search.md",
"size": 14766,
"sha256": "4f16b64f43678943d28cfcecb1092d7b567517595f528debd441b9976813423a",
"contentType": "text/markdown"
}
]
},
"provenance": {
"source": "unavailable",
"reason": "No server-resolved GitHub import provenance is stored for this version."
},
"security": {
"status": "suspicious",
"passed": false,
"rawStatus": "suspicious",
"verdict": "suspicious",
"confidence": "high",
"summary": "The skill is a real multi-search helper, but its privacy notice falsely says there is no external data transmission even though searches are sent to third-party engines.",
"model": null,
"checkedAt": 1789032879048,
"scannerReports": {
"aig": null,
"skillspector": null
}
},
"signature": {
"status": "unsigned"
}
}
}
}
}
+2 -63
View File
@@ -56,65 +56,6 @@
}
}
]
},
"newapi": {
"baseUrl": "http://100.115.195.188:3000/v1",
"apiKey": "${NEW_API_KEY}",
"api": "openai-completions",
"models": [
{
"id": "qwen3.5-plus",
"name": "Qwen 3.5 Plus",
"reasoning": false,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 131072,
"maxTokens": 8192,
"api": "openai-completions"
},
{
"id": "deepseek-v4-flash",
"name": "DeepSeek V4 Flash",
"reasoning": false,
"compat": {
"supportsTools": false
},
"input": [
"text"
],
"cost": {
"input": 1,
"output": 2,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1000000,
"maxTokens": 384000
},
{
"id": "qwen3.7-plus",
"name": "Qwen 3.7 Plus",
"reasoning": false,
"input": [
"text"
],
"cost": {
"input": 1,
"output": 2,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1000000,
"maxTokens": 384000
}
]
}
}
},
@@ -139,9 +80,7 @@
},
"modelPolicy": {
"allow": [
"deepseek/deepseek-flash",
"newapi/deepseek-flash",
"newapi/qwen3.5-plus"
"deepseek/deepseek-flash"
]
},
"workspace": "/home/yangxuan/.openclaw/workspace",
@@ -269,7 +208,7 @@
"workspace": "/home/yangxuan/.openclaw/workspace-sql",
"agentDir": "/home/yangxuan/.openclaw/agents/sql/agent",
"model": {
"primary": "newapi/qwen3.5-plus"
"primary": "deepseek/deepseek-flash"
},
"identity": {
"name": "SQL",
@@ -0,0 +1,18 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "multi-search-engine",
"ownerHandle": "gpyangyoujun",
"installedAt": 1789545766661,
"artifact": {
"kind": "archive",
"sha256": "c2dc9053c07bb6bbc569727b8fee823119b664026b58587d15e1858d190df8f0",
"integrity": "sha256-wtyQU8B7trvFaXJ7j+6CMRm2ZAJrWFh9FeGFjRkN+PA="
},
"skillFile": {
"path": "SKILL.md",
"sha256": "a089b7928eeff57cc50dfa87cfe6f630f227dafc3ed7f92af043b7edb0866796"
},
"fileTreeSha256": "sha256:be51286be5077a1857a94d6578780746af03b6ed9ec0f9630efb846966086aa3",
"installedVersion": "2.1.3"
}
+18
View File
@@ -0,0 +1,18 @@
# Changelog
## v2.1.0 (2026-04-11)
- Version bump to 2.1.0
## v2.0.1 (2026-02-06)
- Simplified documentation
- Removed gov-related content
- Optimized for ClawHub publishing
## v2.0.0 (2026-02-06)
- Added 9 international search engines
- Enhanced advanced search capabilities
- Added DuckDuckGo Bangs support
- Added WolframAlpha knowledge queries
## v1.0.0 (2026-02-04)
- Initial release with 8 domestic search engines
+48
View File
@@ -0,0 +1,48 @@
# Multi Search Engine
## 基本信息
- **名称**: multi-search-engine
- **版本**: v2.0.1
- **描述**: 集成16个搜索引擎(7国内+9国际),支持高级搜索语法
- **发布时间**: 2026-02-06
## 搜索引擎
**国内(7个)**: 百度、必应CN、必应INT、360、搜狗、微信、神马
**国际(9个)**: Google、Google HK、DuckDuckGo、Yahoo、Startpage、Brave、Ecosia、Qwant、WolframAlpha
## 核心功能
- 高级搜索操作符(site:, filetype:, intitle:等)
- DuckDuckGo Bangs快捷命令
- 时间筛选(小时/天/周/月/年)
- 隐私保护搜索
- WolframAlpha知识计算
## 更新记录
### v2.0.1 (2026-02-06)
- 精简文档,优化发布
### v2.0.0 (2026-02-06)
- 新增9个国际搜索引擎
- 强化深度搜索能力
### v1.0.0 (2026-02-04)
- 初始版本:8个国内搜索引擎
## 使用示例
```javascript
// Google搜索
web_fetch({"url": "https://www.google.com/search?q=python"})
// 隐私搜索
web_fetch({"url": "https://duckduckgo.com/html/?q=privacy"})
// 站内搜索
web_fetch({"url": "https://www.google.com/search?q=site:github.com+python"})
```
MIT License
+154
View File
@@ -0,0 +1,154 @@
---
name: "multi-search-engine"
description: "Multi search engine integration with 16 engines (7 CN + 9 Global). Supports advanced search operators, time filters, site search, privacy engines, and WolframAlpha knowledge queries. No API keys required."
---
# Multi Search Engine
Integration of 16 search engines for web crawling without API keys.
## Workflow
1. **Preparation**: AI Agent initializes an empty in-memory cookie store. Cookies are only acquired dynamically during search operations when access is denied
2. **Language Evaluation**: Detect the language attribute of the search query. If the query is in Chinese, use Domestic search engines (Baidu, Bing CN, Bing INT, 360, Sogou, WeChat, Shenma). If the query is non-Chinese, use International search engines (Google, Google HK, DuckDuckGo, Yahoo, Startpage, Brave, Ecosia, Qwant, WolframAlpha). Select engines based on query relevance and availability.
3. **Controlled Search**: Use web_fetch to execute search requests with rate limiting:
- Add 1-2 second delay between requests to respect server load
- Batch requests in groups of 3-4 engines with sequential execution between batches
- Include standard browser headers to identify as legitimate user agent
- If access is denied (403/429), fetch engine homepage to obtain fresh session cookies
4. **Cookie Management**:
- Cookies are stored ONLY in memory during runtime
- Cookies are acquired on-demand when search requests fail
- No cookies are read from or written to config.json or any file
- Cookies are cleared after search session completes
- Only session cookies from search engine domains are captured
5. **Retry Mechanism**: If a search fails due to cookie/session issues, retry once with freshly acquired cookies after a 2-second delay
6. **Result Aggregation**: Consolidate successful results from search engines, organize and summarize them to output a core search report
## Search Engines
### Domestic (7)
- **Baidu**: `https://www.baidu.com/s?wd={keyword}`
- **Bing CN**: `https://cn.bing.com/search?q={keyword}&ensearch=0`
- **Bing INT**: `https://cn.bing.com/search?q={keyword}&ensearch=1`
- **360**: `https://www.so.com/s?q={keyword}`
- **Sogou**: `https://sogou.com/web?query={keyword}`
- **WeChat**: `https://wx.sogou.com/weixin?type=2&query={keyword}`
- **Shenma**: `https://m.sm.cn/s?q={keyword}`
### International (9)
- **Google**: `https://www.google.com/search?q={keyword}`
- **Google HK**: `https://www.google.com.hk/search?q={keyword}`
- **DuckDuckGo**: `https://duckduckgo.com/html/?q={keyword}`
- **Yahoo**: `https://search.yahoo.com/search?p={keyword}`
- **Startpage**: `https://www.startpage.com/sp/search?query={keyword}`
- **Brave**: `https://search.brave.com/search?q={keyword}`
- **Ecosia**: `https://www.ecosia.org/search?q={keyword}`
- **Qwant**: `https://www.qwant.com/?q={keyword}`
- **WolframAlpha**: `https://www.wolframalpha.com/input?i={keyword}`
## Quick Examples
```javascript
// Basic search
web_fetch({"url": "https://www.google.com/search?q=python+tutorial"})
// Site-specific
web_fetch({"url": "https://www.google.com/search?q=site:github.com+react"})
// File type
web_fetch({"url": "https://www.google.com/search?q=machine+learning+filetype:pdf"})
// Time filter (past week)
web_fetch({"url": "https://www.google.com/search?q=ai+news&tbs=qdr:w"})
// Privacy search
web_fetch({"url": "https://duckduckgo.com/html/?q=privacy+tools"})
// DuckDuckGo Bangs
web_fetch({"url": "https://duckduckgo.com/html/?q=!gh+tensorflow"})
// Knowledge calculation
web_fetch({"url": "https://www.wolframalpha.com/input?i=100+USD+to+CNY"})
```
## Advanced Operators
| Operator | Example | Description |
|----------|---------|-------------|
| `site:` | `site:github.com python` | Search within site |
| `filetype:` | `filetype:pdf report` | Specific file type |
| `""` | `"machine learning"` | Exact match |
| `-` | `python -snake` | Exclude term |
| `OR` | `cat OR dog` | Either term |
## Time Filters
| Parameter | Description |
|-----------|-------------|
| `tbs=qdr:h` | Past hour |
| `tbs=qdr:d` | Past day |
| `tbs=qdr:w` | Past week |
| `tbs=qdr:m` | Past month |
| `tbs=qdr:y` | Past year |
## Privacy Engines
- **DuckDuckGo**: No tracking
- **Startpage**: Google results + privacy
- **Brave**: Independent index
- **Qwant**: EU GDPR compliant
## Bangs Shortcuts (DuckDuckGo)
| Bang | Destination |
|------|-------------|
| `!g` | Google |
| `!gh` | GitHub |
| `!so` | Stack Overflow |
| `!w` | Wikipedia |
| `!yt` | YouTube |
## WolframAlpha Queries
- Math: `integrate x^2 dx`
- Conversion: `100 USD to CNY`
- Stocks: `AAPL stock`
- Weather: `weather in Beijing`
## Documentation
- `references/advanced-search.md` - Domestic search guide
- `references/international-search.md` - International search guide
- `CHANGELOG.md` - Version history
## License
MIT
## Security & Privacy Notice
### Cookie Handling
- **Purpose**: Cookies are used ONLY to maintain search session state when access is denied (403/429 errors)
- **Storage**: Cookies are kept STRICTLY in memory during runtime - NEVER persisted to disk or config files
- **Acquisition**: Cookies are acquired on-demand from search engine homepages only when search requests fail
- **Scope**: Only session cookies from the specific search engine domain are captured
- **Lifecycle**: Cookies are cleared immediately after the search session completes
- **No Pre-configuration**: No cookies are loaded from config.json or any external file at startup
- **No API Keys**: This tool uses standard web search URLs, no authentication required
### Crawling Ethics
- **Rate Limiting**: Implement reasonable delays between requests (recommend 1-2 seconds)
- **Respect robots.txt**: Honor search engine crawling policies
- **Terms of Service**: Users are responsible for complying with search engine ToS
- **Purpose**: Designed for legitimate search aggregation, not mass data scraping
### Data Handling
- **No Personal Data**: Tool does not collect or transmit user personal information
- **Local Execution**: All operations run locally, no external data transmission
- **Session Isolation**: Cookies are session-specific and cleared after use
+6
View File
@@ -0,0 +1,6 @@
{
"ownerId": "kn79j8kk7fb9w10jh83803j7f180a44m",
"slug": "multi-search-engine",
"version": "2.1.3",
"publishedAt": 1775879953831
}
+85
View File
@@ -0,0 +1,85 @@
{
"name": "multi-search-engine",
"engines": [
{
"name": "Baidu",
"url": "https://www.baidu.com/s?wd={keyword}",
"region": "cn"
},
{
"name": "Bing CN",
"url": "https://cn.bing.com/search?q={keyword}&ensearch=0",
"region": "cn"
},
{
"name": "Bing INT",
"url": "https://cn.bing.com/search?q={keyword}&ensearch=1",
"region": "cn"
},
{
"name": "360",
"url": "https://www.so.com/s?q={keyword}",
"region": "cn"
},
{
"name": "Sogou",
"url": "https://sogou.com/web?query={keyword}",
"region": "cn"
},
{
"name": "WeChat",
"url": "https://wx.sogou.com/weixin?type=2&query={keyword}",
"region": "cn"
},
{
"name": "Shenma",
"url": "https://m.sm.cn/s?q={keyword}",
"region": "cn"
},
{
"name": "Google",
"url": "https://www.google.com/search?q={keyword}",
"region": "global"
},
{
"name": "Google HK",
"url": "https://www.google.com.hk/search?q={keyword}",
"region": "global"
},
{
"name": "DuckDuckGo",
"url": "https://duckduckgo.com/html/?q={keyword}",
"region": "global"
},
{
"name": "Yahoo",
"url": "https://search.yahoo.com/search?p={keyword}",
"region": "global"
},
{
"name": "Startpage",
"url": "https://www.startpage.com/sp/search?query={keyword}",
"region": "global"
},
{
"name": "Brave",
"url": "https://search.brave.com/search?q={keyword}",
"region": "global"
},
{
"name": "Ecosia",
"url": "https://www.ecosia.org/search?q={keyword}",
"region": "global"
},
{
"name": "Qwant",
"url": "https://www.qwant.com/?q={keyword}",
"region": "global"
},
{
"name": "WolframAlpha",
"url": "https://www.wolframalpha.com/input?i={keyword}",
"region": "global"
}
]
}
+7
View File
@@ -0,0 +1,7 @@
{
"name": "multi-search-engine",
"version": "2.1.0",
"description": "Multi search engine with 16 engines (7 CN + 9 Global). Supports advanced operators, time filters, privacy engines.",
"engines": 16,
"requires_api_key": false
}
@@ -0,0 +1,146 @@
# 国内搜索引擎深度搜索指南
## 🔍 百度 (Baidu)
### 特色功能
| 功能 | 说明 | URL |
|------|------|-----|
| **中文优化** | 中文内容索引最全 | `https://www.baidu.com/s?wd={keyword}` |
| **百度学术** | 学术资源搜索 | `https://xueshu.baidu.com/s?wd={keyword}` |
| **百度新闻** | 新闻聚合 | `https://news.baidu.com/` |
### 搜索示例
```javascript
// 1. 基础搜索
web_fetch({"url": "https://www.baidu.com/s?wd=Python教程"})
// 2. 站内搜索
web_fetch({"url": "https://www.baidu.com/s?wd=site:github.com+python"})
// 3. 文件类型搜索
web_fetch({"url": "https://www.baidu.com/s?wd=机器学习+filetype:pdf"})
// 4. 学术搜索
web_fetch({"url": "https://xueshu.baidu.com/s?wd=深度学习+图像识别"})
```
---
## 🔎 必应中国版 (Bing CN/INT)
### 特色功能
| 功能 | 说明 | URL |
|------|------|-----|
| **中文优化** | `ensearch=0` 中文结果 | `https://cn.bing.com/search?q={keyword}&ensearch=0` |
| **国际版** | `ensearch=1` 英文结果 | `https://cn.bing.com/search?q={keyword}&ensearch=1` |
| **学术搜索** | 学术资源 | `https://cn.bing.com/academic/search?q={keyword}` |
### 搜索示例
```javascript
// 1. 中文搜索结果
web_fetch({"url": "https://cn.bing.com/search?q=人工智能技术&ensearch=0"})
// 2. 英文搜索结果(使用中国服务器)
web_fetch({"url": "https://cn.bing.com/search?q=artificial+intelligence&ensearch=1"})
// 3. 学术搜索
web_fetch({"url": "https://cn.bing.com/academic/search?q=机器学习算法"})
```
---
## 🔍 360搜索
### 特色功能
| 功能 | 说明 | URL |
|------|------|-----|
| **安全搜索** | 内置安全防护 | 默认开启 |
| **基础搜索** | 网页搜索 | `https://www.so.com/s?q={keyword}` |
### 搜索示例
```javascript
// 1. 基础搜索
web_fetch({"url": "https://www.so.com/s?q=网络安全"})
// 2. 站内搜索
web_fetch({"url": "https://www.so.com/s?q=site:zhihu.com+python"})
```
---
## 🔍 搜狗 (Sogou) + 微信搜索
### 特色功能
| 功能 | 说明 | URL |
|------|------|-----|
| **网页搜索** | 通用搜索 | `https://sogou.com/web?query={keyword}` |
| **微信公众号** | 搜公众号文章(唯一渠道) | `https://wx.sogou.com/weixin?type=2&query={keyword}` |
| **知乎优化** | 知乎内容索引好 | `site:zhihu.com` 配合使用 |
### 搜索示例
```javascript
// 1. 网页搜索
web_fetch({"url": "https://sogou.com/web?query=python教程"})
// 2. 微信公众号文章搜索
web_fetch({"url": "https://wx.sogou.com/weixin?type=2&query=Python编程"})
// 3. 搜索特定公众号
web_fetch({"url": "https://wx.sogou.com/weixin?type=2&query=公众号:机器之心"})
// 4. 知乎内容搜索
web_fetch({"url": "https://www.sogou.com/web?query=site:zhihu.com+机器学习"})
```
---
## 📱 神马搜索 (Shenma)
### 特色功能
| 功能 | 说明 | URL |
|------|------|-----|
| **移动优化** | 专注移动端搜索 | `https://m.sm.cn/s?q={keyword}` |
| **阿里生态** | 整合阿里系内容 | UC浏览器默认搜索 |
### 搜索示例
```javascript
// 1. 移动端搜索
web_fetch({"url": "https://m.sm.cn/s?q=python入门教程"})
// 2. 移动网站点搜索
web_fetch({"url": "https://m.sm.cn/s?q=site:zhuanlan.zhihu.com+AI"})
```
---
## 🌍 国内搜索策略
### 按搜索目标选择引擎
| 搜索目标 | 首选引擎 | 原因 |
|---------|---------|------|
| **综合中文内容** | 百度 | 中文索引最全 |
| **微信公众号** | 搜狗微信 | 唯一支持公众号搜索 |
| **知乎内容** | 搜狗 | 知乎优化好 |
| **移动端内容** | 神马 | 移动端优化 |
| **学术资源** | 必应学术 | 学术索引 |
| **中英文双语** | 必应中国/国际版 | enswitch切换 |
| **新闻资讯** | 百度新闻 | 新闻聚合 |
---
## 📚 参考资料
- [百度搜索高级语法](https://baike.baidu.com/item/搜索语法)
- [必应搜索技巧](https://cn.bing.com/tips)
- [搜狗搜索帮助](https://help.sogou.com/)
@@ -0,0 +1,398 @@
# 国际搜索引擎深度搜索指南
## 🔍 Google 深度搜索
### 1.1 基础高级搜索操作符
| 操作符 | 功能 | 示例 | URL |
|--------|------|------|-----|
| `""` | 精确匹配 | `"machine learning"` | `https://www.google.com/search?q=%22machine+learning%22` |
| `-` | 排除关键词 | `python -snake` | `https://www.google.com/search?q=python+-snake` |
| `OR` | 或运算 | `machine learning OR deep learning` | `https://www.google.com/search?q=machine+learning+OR+deep+learning` |
| `*` | 通配符 | `machine * algorithms` | `https://www.google.com/search?q=machine+*+algorithms` |
| `()` | 分组 | `(apple OR microsoft) phones` | `https://www.google.com/search?q=(apple+OR+microsoft)+phones` |
| `..` | 数字范围 | `laptop $500..$1000` | `https://www.google.com/search?q=laptop+%24500..%241000` |
### 1.2 站点与文件搜索
| 操作符 | 功能 | 示例 |
|--------|------|------|
| `site:` | 站内搜索 | `site:github.com python projects` |
| `filetype:` | 文件类型 | `filetype:pdf annual report` |
| `inurl:` | URL包含 | `inurl:login admin` |
| `intitle:` | 标题包含 | `intitle:"index of" mp3` |
| `intext:` | 正文包含 | `intext:password filetype:txt` |
| `cache:` | 查看缓存 | `cache:example.com` |
| `related:` | 相关网站 | `related:github.com` |
| `info:` | 网站信息 | `info:example.com` |
### 1.3 时间筛选参数
| 参数 | 含义 | URL示例 |
|------|------|---------|
| `tbs=qdr:h` | 过去1小时 | `https://www.google.com/search?q=news&tbs=qdr:h` |
| `tbs=qdr:d` | 过去24小时 | `https://www.google.com/search?q=news&tbs=qdr:d` |
| `tbs=qdr:w` | 过去1周 | `https://www.google.com/search?q=news&tbs=qdr:w` |
| `tbs=qdr:m` | 过去1月 | `https://www.google.com/search?q=news&tbs=qdr:m` |
| `tbs=qdr:y` | 过去1年 | `https://www.google.com/search?q=news&tbs=qdr:y` |
| `tbs=cdr:1,cd_min:1/1/2024,cd_max:12/31/2024` | 自定义日期范围 | 2024年全年 |
### 1.4 语言和地区筛选
| 参数 | 功能 | 示例 |
|------|------|------|
| `hl=en` | 界面语言 | `https://www.google.com/search?q=test&hl=en` |
| `lr=lang_zh-CN` | 搜索结果语言 | `https://www.google.com/search?q=test&lr=lang_zh-CN` |
| `cr=countryCN` | 国家/地区 | `https://www.google.com/search?q=test&cr=countryCN` |
| `gl=us` | 地理位置 | `https://www.google.com/search?q=test&gl=us` |
### 1.5 特殊搜索类型
| 类型 | URL | 说明 |
|------|-----|------|
| 图片搜索 | `https://www.google.com/search?q={keyword}&tbm=isch` | `tbm=isch` 表示图片 |
| 新闻搜索 | `https://www.google.com/search?q={keyword}&tbm=nws` | `tbm=nws` 表示新闻 |
| 视频搜索 | `https://www.google.com/search?q={keyword}&tbm=vid` | `tbm=vid` 表示视频 |
| 地图搜索 | `https://www.google.com/search?q={keyword}&tbm=map` | `tbm=map` 表示地图 |
| 购物搜索 | `https://www.google.com/search?q={keyword}&tbm=shop` | `tbm=shop` 表示购物 |
| 图书搜索 | `https://www.google.com/search?q={keyword}&tbm=bks` | `tbm=bks` 表示图书 |
| 学术搜索 | `https://scholar.google.com/scholar?q={keyword}` | Google Scholar |
### 1.6 Google 深度搜索示例
```javascript
// 1. 搜索GitHub上的Python机器学习项目
web_fetch({"url": "https://www.google.com/search?q=site:github.com+python+machine+learning"})
// 2. 搜索2024年的PDF格式机器学习教程
web_fetch({"url": "https://www.google.com/search?q=machine+learning+tutorial+filetype:pdf&tbs=cdr:1,cd_min:1/1/2024"})
// 3. 搜索标题包含"tutorial"的Python相关页面
web_fetch({"url": "https://www.google.com/search?q=intitle:tutorial+python"})
// 4. 搜索过去一周的新闻
web_fetch({"url": "https://www.google.com/search?q=AI+breakthrough&tbs=qdr:w&tbm=nws"})
// 5. 搜索中文内容(界面英文,结果中文)
web_fetch({"url": "https://www.google.com/search?q=人工智能&lr=lang_zh-CN&hl=en"})
// 6. 搜索特定价格范围的笔记本电脑
web_fetch({"url": "https://www.google.com/search?q=laptop+%241000..%242000+best+rating"})
// 7. 搜索排除Wikipedia的结果
web_fetch({"url": "https://www.google.com/search?q=python+programming+-wikipedia"})
// 8. 搜索学术文献
web_fetch({"url": "https://scholar.google.com/scholar?q=deep+learning+optimization"})
// 9. 搜索缓存页面(查看已删除内容)
web_fetch({"url": "https://webcache.googleusercontent.com/search?q=cache:example.com"})
// 10. 搜索相关网站
web_fetch({"url": "https://www.google.com/search?q=related:stackoverflow.com"})
```
---
## 🦆 DuckDuckGo 深度搜索
### 2.1 DuckDuckGo 特色功能
| 功能 | 语法 | 示例 |
|------|------|------|
| **Bangs 快捷** | `!缩写` | `!g python` → Google搜索 |
| **密码生成** | `password` | `https://duckduckgo.com/?q=password+20` |
| **颜色转换** | `color` | `https://duckduckgo.com/?q=+%23FF5733` |
| **短链接** | `shorten` | `https://duckduckgo.com/?q=shorten+example.com` |
| **二维码生成** | `qr` | `https://duckduckgo.com/?q=qr+hello+world` |
| **生成UUID** | `uuid` | `https://duckduckgo.com/?q=uuid` |
| **Base64编解码** | `base64` | `https://duckduckgo.com/?q=base64+hello` |
### 2.2 DuckDuckGo Bangs 完整列表
#### 搜索引擎
| Bang | 跳转目标 | 示例 |
|------|---------|------|
| `!g` | Google | `!g python tutorial` |
| `!b` | Bing | `!b weather` |
| `!y` | Yahoo | `!y finance` |
| `!sp` | Startpage | `!sp privacy` |
| `!brave` | Brave Search | `!brave tech` |
#### 编程开发
| Bang | 跳转目标 | 示例 |
|------|---------|------|
| `!gh` | GitHub | `!gh tensorflow` |
| `!so` | Stack Overflow | `!so javascript error` |
| `!npm` | npmjs.com | `!npm express` |
| `!pypi` | PyPI | `!pypi requests` |
| `!mdn` | MDN Web Docs | `!mdn fetch api` |
| `!docs` | DevDocs | `!docs python` |
| `!docker` | Docker Hub | `!docker nginx` |
#### 知识百科
| Bang | 跳转目标 | 示例 |
|------|---------|------|
| `!w` | Wikipedia | `!w machine learning` |
| `!wen` | Wikipedia英文 | `!wen artificial intelligence` |
| `!wt` | Wiktionary | `!wt serendipity` |
| `!imdb` | IMDb | `!imdb inception` |
#### 购物价格
| Bang | 跳转目标 | 示例 |
|------|---------|------|
| `!a` | Amazon | `!a wireless headphones` |
| `!e` | eBay | `!e vintage watch` |
| `!ali` | AliExpress | `!ali phone case` |
#### 地图位置
| Bang | 跳转目标 | 示例 |
|------|---------|------|
| `!m` | Google Maps | `!m Beijing` |
| `!maps` | OpenStreetMap | `!maps Paris` |
### 2.3 DuckDuckGo 搜索参数
| 参数 | 功能 | 示例 |
|------|------|------|
| `kp=1` | 严格安全搜索 | `https://duckduckgo.com/html/?q=test&kp=1` |
| `kp=-1` | 关闭安全搜索 | `https://duckduckgo.com/html/?q=test&kp=-1` |
| `kl=cn` | 中国区域 | `https://duckduckgo.com/html/?q=news&kl=cn` |
| `kl=us-en` | 美国英文 | `https://duckduckgo.com/html/?q=news&kl=us-en` |
| `ia=web` | 网页结果 | `https://duckduckgo.com/?q=test&ia=web` |
| `ia=images` | 图片结果 | `https://duckduckgo.com/?q=test&ia=images` |
| `ia=news` | 新闻结果 | `https://duckduckgo.com/?q=test&ia=news` |
| `ia=videos` | 视频结果 | `https://duckduckgo.com/?q=test&ia=videos` |
### 2.4 DuckDuckGo 深度搜索示例
```javascript
// 1. 使用Bang跳转到Google搜索
web_fetch({"url": "https://duckduckgo.com/html/?q=!g+machine+learning"})
// 2. 直接搜索GitHub上的项目
web_fetch({"url": "https://duckduckgo.com/html/?q=!gh+react"})
// 3. 查找Stack Overflow答案
web_fetch({"url": "https://duckduckgo.com/html/?q=!so+python+list+comprehension"})
// 4. 生成密码
web_fetch({"url": "https://duckduckgo.com/?q=password+16"})
// 5. Base64编码
web_fetch({"url": "https://duckduckgo.com/?q=base64+hello+world"})
// 6. 颜色代码转换
web_fetch({"url": "https://duckduckgo.com/?q=%23FF5733"})
// 7. 搜索YouTube视频
web_fetch({"url": "https://duckduckgo.com/html/?q=!yt+python+tutorial"})
// 8. 查看Wikipedia
web_fetch({"url": "https://duckduckgo.com/html/?q=!w+artificial+intelligence"})
// 9. 亚马逊商品搜索
web_fetch({"url": "https://duckduckgo.com/html/?q=!a+laptop"})
// 10. 生成二维码
web_fetch({"url": "https://duckduckgo.com/?q=qr+https://github.com"})
```
---
## 🔎 Brave Search 深度搜索
### 3.1 Brave Search 特色功能
| 功能 | 参数 | 示例 |
|------|------|------|
| **独立索引** | 无依赖Google/Bing | 自有爬虫索引 |
| **Goggles** | 自定义搜索规则 | 创建个性化过滤器 |
| **Discussions** | 论坛讨论搜索 | 聚合Reddit等论坛 |
| **News** | 新闻聚合 | 独立新闻索引 |
### 3.2 Brave Search 参数
| 参数 | 功能 | 示例 |
|------|------|------|
| `tf=pw` | 本周 | `https://search.brave.com/search?q=news&tf=pw` |
| `tf=pm` | 本月 | `https://search.brave.com/search?q=tech&tf=pm` |
| `tf=py` | 本年 | `https://search.brave.com/search?q=AI&tf=py` |
| `safesearch=strict` | 严格安全 | `https://search.brave.com/search?q=test&safesearch=strict` |
| `source=web` | 网页搜索 | 默认 |
| `source=news` | 新闻搜索 | `https://search.brave.com/search?q=tech&source=news` |
| `source=images` | 图片搜索 | `https://search.brave.com/search?q=cat&source=images` |
| `source=videos` | 视频搜索 | `https://search.brave.com/search?q=music&source=videos` |
### 3.3 Brave Search Goggles(自定义过滤器)
Goggles 允许创建自定义搜索规则:
```
$discard // 丢弃所有
$boost,site=stackoverflow.com // 提升Stack Overflow
$boost,site=github.com // 提升GitHub
$boost,site=docs.python.org // 提升Python文档
```
### 3.4 Brave Search 深度搜索示例
```javascript
// 1. 本周科技新闻
web_fetch({"url": "https://search.brave.com/search?q=technology&tf=pw&source=news"})
// 2. 本月AI发展
web_fetch({"url": "https://search.brave.com/search?q=artificial+intelligence&tf=pm"})
// 3. 图片搜索
web_fetch({"url": "https://search.brave.com/search?q=machine+learning&source=images"})
// 4. 视频教程
web_fetch({"url": "https://search.brave.com/search?q=python+tutorial&source=videos"})
// 5. 使用独立索引搜索
web_fetch({"url": "https://search.brave.com/search?q=privacy+tools"})
```
---
## 📊 WolframAlpha 知识计算搜索
### 4.1 WolframAlpha 数据类型
| 类型 | 查询示例 | URL |
|------|---------|-----|
| **数学计算** | `integrate x^2 dx` | `https://www.wolframalpha.com/input?i=integrate+x%5E2+dx` |
| **单位换算** | `100 miles to km` | `https://www.wolframalpha.com/input?i=100+miles+to+km` |
| **货币转换** | `100 USD to CNY` | `https://www.wolframalpha.com/input?i=100+USD+to+CNY` |
| **股票数据** | `AAPL stock` | `https://www.wolframalpha.com/input?i=AAPL+stock` |
| **天气查询** | `weather in Beijing` | `https://www.wolframalpha.com/input?i=weather+in+Beijing` |
| **人口数据** | `population of China` | `https://www.wolframalpha.com/input?i=population+of+China` |
| **化学元素** | `properties of gold` | `https://www.wolframalpha.com/input?i=properties+of+gold` |
| **营养成分** | `nutrition of apple` | `https://www.wolframalpha.com/input?i=nutrition+of+apple` |
| **日期计算** | `days between Jan 1 2020 and Dec 31 2024` | 日期间隔计算 |
| **时区转换** | `10am Beijing to New York` | 时区转换 |
| **IP地址** | `8.8.8.8` | IP信息查询 |
| **条形码** | `scan barcode 123456789` | 条码信息 |
| **飞机航班** | `flight AA123` | 航班信息 |
### 4.2 WolframAlpha 深度搜索示例
```javascript
// 1. 计算积分
web_fetch({"url": "https://www.wolframalpha.com/input?i=integrate+sin%28x%29+from+0+to+pi"})
// 2. 解方程
web_fetch({"url": "https://www.wolframalpha.com/input?i=solve+x%5E2-5x%2B6%3D0"})
// 3. 货币实时汇率
web_fetch({"url": "https://www.wolframalpha.com/input?i=100+USD+to+CNY"})
// 4. 股票实时数据
web_fetch({"url": "https://www.wolframalpha.com/input?i=Apple+stock+price"})
// 5. 城市天气
web_fetch({"url": "https://www.wolframalpha.com/input?i=weather+in+Shanghai+tomorrow"})
// 6. 国家统计信息
web_fetch({"url": "https://www.wolframalpha.com/input?i=GDP+of+China+vs+USA"})
// 7. 化学计算
web_fetch({"url": "https://www.wolframalpha.com/input?i=molar+mass+of+H2SO4"})
// 8. 物理常数
web_fetch({"url": "https://www.wolframalpha.com/input?i=speed+of+light"})
// 9. 营养信息
web_fetch({"url": "https://www.wolframalpha.com/input?i=calories+in+banana"})
// 10. 历史日期
web_fetch({"url": "https://www.wolframalpha.com/input?i=events+on+July+20+1969"})
```
---
## 🔧 Startpage 隐私搜索
### 5.1 Startpage 特色功能
| 功能 | 说明 | URL |
|------|------|-----|
| **代理浏览** | 匿名访问搜索结果 | 点击"匿名查看" |
| **无追踪** | 不记录搜索历史 | 默认开启 |
| **EU服务器** | 受欧盟隐私法保护 | 数据在欧洲 |
| **代理图片** | 图片代理加载 | 隐藏IP |
### 5.2 Startpage 参数
| 参数 | 功能 | 示例 |
|------|------|------|
| `cat=web` | 网页搜索 | 默认 |
| `cat=images` | 图片搜索 | `...&cat=images` |
| `cat=video` | 视频搜索 | `...&cat=video` |
| `cat=news` | 新闻搜索 | `...&cat=news` |
| `language=english` | 英文结果 | `...&language=english` |
| `time=day` | 过去24小时 | `...&time=day` |
| `time=week` | 过去一周 | `...&time=week` |
| `time=month` | 过去一月 | `...&time=month` |
| `time=year` | 过去一年 | `...&time=year` |
| `nj=0` | 关闭 family filter | `...&nj=0` |
### 5.3 Startpage 深度搜索示例
```javascript
// 1. 隐私搜索
web_fetch({"url": "https://www.startpage.com/sp/search?query=privacy+tools"})
// 2. 图片隐私搜索
web_fetch({"url": "https://www.startpage.com/sp/search?query=nature&cat=images"})
// 3. 本周新闻(隐私模式)
web_fetch({"url": "https://www.startpage.com/sp/search?query=tech+news&time=week&cat=news"})
// 4. 英文结果搜索
web_fetch({"url": "https://www.startpage.com/sp/search?query=machine+learning&language=english"})
```
---
## 🌐 其他国际搜索引擎
### Yahoo
```javascript
web_fetch({"url": "https://search.yahoo.com/search?p={keyword}"})
```
### Ecosia(环保搜索)
```javascript
web_fetch({"url": "https://www.ecosia.org/search?q={keyword}"})
```
### Qwant(欧盟隐私搜索)
```javascript
web_fetch({"url": "https://www.qwant.com/?q={keyword}"})
```
---
## 🌍 国际搜索策略
### 按搜索目标选择引擎
| 搜索目标 | 首选引擎 | 原因 |
|---------|---------|------|
| **学术研究** | Google Scholar | 学术资源索引最全 |
| **编程开发** | Google + DuckDuckGo Bangs | 技术文档全面 |
| **隐私敏感** | DuckDuckGo / Brave | 不追踪用户 |
| **实时新闻** | Brave News | 独立新闻索引 |
| **知识计算** | WolframAlpha | 结构化数据计算 |
| **隐私+Google结果** | Startpage | Google结果+隐私保护 |
+51
View File
@@ -0,0 +1,51 @@
## Description:
Multi Search Engine helps agents query and summarize results from 16 Chinese and global search engines with advanced operators, time filters, site search, privacy engines, and WolframAlpha knowledge queries without API keys.
This skill is ready for commercial/non-commercial use.
## Publisher:
[gpyangyoujun](https://clawhub.ai/user/gpyangyoujun)
### License/Terms of Use:
MIT-0
## Use Case:
Developers and external users use this skill to route web search requests across Chinese and global search engines, apply search operators and time filters, and aggregate results into a concise search report.
### Deployment Geography for Use:
Global
## Known Risks and Mitigations:
Risk: Search terms are sent to third-party search engines, which may expose private, internal, regulated, or identifying information.
Mitigation: Review before installing for sensitive use cases and avoid searching secrets, credentials, private customer data, incident details, or confidential project names unless that disclosure is acceptable.
## Reference(s):
- [Domestic Search Engine Deep Search Guide](artifact/references/advanced-search.md)
- [International Search Engine Deep Search Guide](artifact/references/international-search.md)
- [Multi Search Engine on ClawHub](https://clawhub.ai/gpyangyoujun/skills/multi-search-engine)
## Skill Output:
**Output Type(s):** [Text, Markdown, Shell commands, Guidance]
**Output Format:** [Markdown search report with inline web_fetch examples]
**Output Parameters:** [1D]
**Other Properties Related to Output:** [No API keys required; search requests are sent to third-party search engines.]
## Skill Version(s):
2.1.3 (source: server release metadata)
## Ethical Considerations:
Users should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.
+152
View File
@@ -0,0 +1,152 @@
{
"version": 1,
"skills": {
"self-improving-agent": {
"version": "4.0.2",
"registry": "https://clawhub.ai",
"ownerHandle": "pskoett",
"installedAt": 1789545691162,
"artifact": {
"kind": "archive",
"sha256": "89f2a239f9d675c4c5787cf61c17f709cd38cb9c7d395f9dc50f487a59a47291",
"integrity": "sha256-ifKiOfnWdcTFeHz2HBf3Cc04y5x9OV+dxQ9IelmkcpE="
},
"skillFile": {
"path": "SKILL.md",
"sha256": "eff889950f23bb8a3de90cd617481bbf2557f9495f884bd5b1e9b7604a20817c"
},
"fileTreeSha256": "sha256:cd72f5c1910da8f21f4e326cc4f3b39de8b5bd371411f6a026f1f72b6747de8d",
"verification": {
"schema": "clawhub.skill.verify.v1",
"ok": false,
"decision": "fail",
"reasons": [
"security.status_not_clean"
],
"card": {
"available": true,
"path": "skill-card.md",
"url": "https://wry-manatee-359.convex.site/api/v1/skills/self-improving-agent/card?ownerHandle=pskoett&version=4.0.2",
"sha256": "6f9beece207f7b423e9a20c1b804b664741ee8d76cc77ade7240766b32d4916d",
"size": 2633,
"contentType": "text/markdown; charset=utf-8"
},
"artifact": {
"sourceFingerprint": "c75aa847057f2210ef7136d8456b1f91b7d7cd0352962c9f920679cce045adf2",
"bundleFingerprints": [
"a0b59e786c352370b6031b7f2b956eaade2dec1467933d0f97e104aa28220214",
"3bb2a005cd20f56524be2d5e379f14b0f4cadc5ae2a6801179d46a4530385612",
"ca25cab698e926de89a5b4548f7c090dcf5f1ca70558c537b3f998f1e95ad346"
],
"files": [
{
"path": "CHANGELOG.md",
"size": 7598,
"sha256": "8d8e946a3e1482470068773fa62482af3440431b3aa400df23b8c781906ba934",
"contentType": "text/markdown"
},
{
"path": "references/uninstall.md",
"size": 1891,
"sha256": "c569a88473ac0187e0d9dde1686eb24efe4666d84af2a149b5fb8e99b7a99828",
"contentType": "text/markdown"
},
{
"path": "references/examples.md",
"size": 8287,
"sha256": "ba521451abf0f1cc774b061894c94689390dafa3ce3905f63d5fa6f860a5e3b8",
"contentType": "text/markdown"
},
{
"path": "references/openclaw-integration.md",
"size": 9814,
"sha256": "517a40ddec886f07cf66cb7c98e61ea464af953b505610722ed46a297741708a",
"contentType": "text/markdown"
},
{
"path": "hooks/openclaw/handler.test.js",
"size": 8921,
"sha256": "3047f4239bc3eadcb8f12cc3515f946e86b16cb482de82ae900e5b994c403e69",
"contentType": "text/javascript"
},
{
"path": "hooks/openclaw/handler.js",
"size": 13741,
"sha256": "b94261dce2de54ba6bd1d6aca48d9ef11236cbe7396bd37354daa218e0ceecf3",
"contentType": "text/javascript"
},
{
"path": "hooks/openclaw/HOOK.md",
"size": 2465,
"sha256": "2ae0820cc5cad75d9ecce8cfc6a9c4c8baafcb4c96f820bf85f3a0e7ff2364f5",
"contentType": "text/markdown"
},
{
"path": "hooks/openclaw/handler.ts",
"size": 14615,
"sha256": "35f8ccd18d6979b4bacfbf1841f4f0965193ed694e46fa6cee67994f3ac838ba",
"contentType": "application/octet-stream"
},
{
"path": "scripts/extract-skill.sh",
"size": 5293,
"sha256": "d9af3d6f2cde0eeb3c0a85b898828656c1c6d9d51f61ba38f9801030424f5147",
"contentType": "text/x-sh"
},
{
"path": "SKILL.md",
"size": 21361,
"sha256": "eff889950f23bb8a3de90cd617481bbf2557f9495f884bd5b1e9b7604a20817c",
"contentType": "text/markdown"
},
{
"path": "assets/FEATURE_REQUESTS.md",
"size": 84,
"sha256": "a6291573936034efaad57b43b6f57bdb35ed627f01ce9309b2c781da63563c37",
"contentType": "text/markdown"
},
{
"path": "assets/ERRORS.md",
"size": 75,
"sha256": "731f4a26fee7074d11e999920e5c3ca63b0e025b75a4d9966429ee88a2cb5988",
"contentType": "text/markdown"
},
{
"path": "assets/SKILL-TEMPLATE.md",
"size": 3407,
"sha256": "3ee008f80d53e0e576f7e747ac4c4e040ff659f02d3f5689a95524a3696e4881",
"contentType": "text/markdown"
},
{
"path": "assets/LEARNINGS.md",
"size": 1135,
"sha256": "58b31d4cda7d1f2a6fdaf1aff06f45288e6cb0161dd1ed1bf2152c7f8c3c88da",
"contentType": "text/markdown"
}
]
},
"provenance": {
"source": "unavailable",
"reason": "No server-resolved GitHub import provenance is stored for this version."
},
"security": {
"status": "suspicious",
"passed": false,
"rawStatus": "suspicious",
"verdict": "suspicious",
"confidence": "high",
"summary": "This skill is mostly purpose-aligned, but its optional hook can automatically read session transcripts and persist excerpts with insufficient path scoping and only best-effort redaction.",
"model": null,
"checkedAt": 1789015140848,
"scannerReports": {
"aig": null,
"skillspector": null
}
},
"signature": {
"status": "unsigned"
}
}
}
}
}
+48
View File
@@ -0,0 +1,48 @@
# Errors
Command failures and integration errors.
---
## [ERR-20260916-001] python3-run-tmp-script
**Logged**: 2026-09-16T08:10:00+08:00 (观测于 2026-08-28,回溯补录)
**Priority**: medium
**Status**: resolved
**Area**: backend
### Summary
`/tmp` 运行周报生成脚本时,无法导入同目录的 `send_weekly_report.py`,脚本直接崩溃,周报未生成。
### Error
```
Traceback (most recent call last):
File "/tmp/gen_g6_report.py", line 1, in <module>
from send_weekly_report import send_to_drafts
ModuleNotFoundError: No module named 'send_weekly_report'
```
### Context
- Command/operation attempted: `python3 /tmp/gen_g6_report.py`(脚本首行 `from send_weekly_report import send_to_drafts`
- Input or parameters used: 临时脚本放在 `/tmp`,模块 `send_weekly_report.py``/home/yangxuan/.openclaw/workspace-resume/`
- Environment details: cwd 已是 workspace-resume,但 Python 只把**脚本所在目录**`/tmp`)加入 `sys.path`cwd 不在其中
- Summary of relevant output: 退出码 1,无其他副作用(未写草稿、未归档、未入库)
### Suggested Fix
两条任选其一:
1. 运行前显式指定模块搜索路径:`PYTHONPATH=/home/yangxuan/.openclaw/workspace-resume python3 /tmp/gen_g6_report.py`
2. 直接把临时生成脚本写在 workspace 目录内再运行(`python3 gen_report.py`
### Resolution
- **Resolved**: 2026-08-28T05:34:00Z
- **Notes**: 采用方案 1PYTHONPATH),周报随即成功归档并同步 MySQL。
### Metadata
- Reproducible: yes
- Related Files: send_weekly_report.py
- Pattern-Key: deps.module-not-found
- Recurrence-Count: 1
- First-Seen: 2026-08-28
- Last-Seen: 2026-08-28
---
@@ -0,0 +1,5 @@
# Feature Requests
Capabilities requested by the user.
---
+173
View File
@@ -0,0 +1,173 @@
# Learnings
Corrections, insights, and knowledge gaps captured during development.
**Categories**: correction | insight | knowledge_gap | best_practice
---
## [LRN-20260916-001] correction
**Logged**: 2026-09-16T08:10:00+08:00 (观测于 2026-08-27,回溯补录)
**Priority**: high
**Status**: promoted
**Area**: docs
### Summary
`TOOLS.md` / `MEMORY.md` 曾把 `send_weekly_report.py` 描述为「会同步元数据到 db」,实际该脚本从不写 SQLite。
### Details
盘点归档链路时逐条核对脚本行为,确认 `send_to_drafts()` 只有两条落盘动作:
- `archive_report()` → 写 Markdown 到 `weekly-reports/YYYY/`
- `sync_to_mysql()` → 写 MySQL `resume`
**没有任何 SQLite 写入**。原描述会让后续会话误判「SQLite 已自动入库」,从而漏跑 `sync_reports_db.py` / `import_md_to_db.py`,造成本地索引与 Markdown 静默不一致。
### Suggested Action
已修正 `TOOLS.md``MEMORY.md` 的表述,明确「脚本不写 SQLite,入库需手动执行」。
### Metadata
- Source: conversation
- Related Files: TOOLS.md, MEMORY.md, send_weekly_report.py
- Tags: 文档, 归档, 周报, sqlite
- Pattern-Key: docs.inaccurate-description
- Recurrence-Count: 1
- First-Seen: 2026-08-27
- Last-Seen: 2026-08-27
- **Promoted**: TOOLS.md, MEMORY.md
---
## [LRN-20260916-002] correction
**Logged**: 2026-09-16T08:10:00+08:00
**Priority**: medium
**Status**: resolved
**Area**: docs
### Summary
`AGENTS.md` 的技能小节指向 `skills/weekly-report-g5/SKILL.md`,该路径不存在;在用技能实际是 `skills/weekly-report/SKILL.md`
### Details
2026-09-16 按 AGENTS.md 指引直接 `read` 该路径,返回 `ENOENT: no such file or directory`。核实后确认:
- 实际在用:`skills/weekly-report/SKILL.md`description 已覆盖 G5/G6
- `weekly-report-g5` 只存在于 Skill Workshop 的 proposal 目录,从未成为 live 技能
AGENTS.md 是每次会话必读文件,错误路径既浪费工具调用,也可能让后续会话判定「技能不存在」而重复造轮子。
### Suggested Action
把 AGENTS.md 技能小节的路径改为 `skills/weekly-report/SKILL.md`
### Resolution
- **Resolved**: 2026-09-16T08:15:00Z
- **Notes**: 已修正 `AGENTS.md` 技能小节路径,并补登自我改进技能条目;同时清理了同文件第 252 行 OKR 小节与「## 技能」标题粘连的格式残留。
### Metadata
- Source: conversation
- Related Files: AGENTS.md, skills/weekly-report/SKILL.md
- Tags: 文档, 技能路径, 漂移
- Pattern-Key: docs.stale-path
- Recurrence-Count: 1
- First-Seen: 2026-09-16
- Last-Seen: 2026-09-16
- See Also: LRN-20260916-004
---
## [LRN-20260916-003] knowledge_gap
**Logged**: 2026-09-16T08:10:00+08:00
**Priority**: medium
**Status**: pending
**Area**: config
### Summary
GNU date 的 `monday this week` 返回的是**下周一**,不是本周一;用它推算周报日期范围会算出一周之后的区间。
### Details
2026-09-16(周三)实测:
- `date -d 'monday this week' +%F``2026-09-21`(下周一,错的)
- `date -d 'last monday' +%F``2026-09-14`(本周一,对的)
周期语义在 GNU date 里不直观,且不会报错——属于「静默算错」,比报错更危险。
### Suggested Action
周报日期范围以**用户提供的任务日期**为准,不用表达式推导;确需推导时只用 `last monday`
### Metadata
- Source: conversation
- Related Files: send_weekly_report.py
- Tags: shell, date, 周报, 静默错误
- Pattern-Key: shell.date-week-start
- Recurrence-Count: 1
- First-Seen: 2026-09-16
- Last-Seen: 2026-09-16
---
## [LRN-20260916-004] correction
**Logged**: 2026-09-16T08:10:00+08:00
**Updated**: 2026-09-16T08:20:00+08:00
**Priority**: medium
**Status**: resolved
**Area**: config
### Summary
(本条已更正)我曾误判 Skill Workshop 中 `weekly-report-g5` 的两个提案为 pending;实际状态是 `stale``applied`**均非待处理**。
### Details
首次复盘时,我只凭提案目录里 `PROPOSAL.md` 头部自带的 `status: proposal` 就断定二者「仍待处理」,并把「待处理提案数虚高」写进了改进周报。随后用权威入口核实:
- `skill_workshop list``[stale, create, clean]`v1)、`[applied, update, clean]`v2
- `skill_workshop list --status pending`**No skill proposals matched**
即:v1 已 stale(基线变动,无法再应用),v2 已 applied(内容确已并入在用技能)。**「虚高待办数」这一结论不成立**,是读错来源所致。
附带发现:这两个目录现存仅 `PROPOSAL.md`,原先的 `proposal.json` / `rollback.json` 已不在(2026-08-28 时还在),说明其间有自动化(skill-collection-review)做过迁移/清理。
### Suggested Action
判断提案状态**只用 `skill_workshop list`**,不要读 `PROPOSAL.md` 头部的 `status` 字段——那是提案正文自带的静态标记,不代表当前生命周期状态。同理,任何「状态类」结论都应以对应的工具/接口为准,而非文件表面内容。
### Resolution
- **Resolved**: 2026-09-16T08:20:00Z
- **Notes**: 已更正 `.learnings` 本条与 `2026-W38-改进周报.md`;经复核**无需清理提案**(本就不存在 pending,删除反而会丢掉 v2 已应用的历史记录)。
### Metadata
- Source: conversation
- Related Files: skill-workshop/proposals/, weekly-reports/2026/2026-W38-改进周报.md
- Tags: skill-workshop, 提案, 误判, 来源可靠性
- Pattern-Key: tooling.wrong-status-source
- Recurrence-Count: 1
- First-Seen: 2026-09-16
- Last-Seen: 2026-09-16
- See Also: LRN-20260916-002
---
## [LRN-20260916-005] knowledge_gap
**Logged**: 2026-09-16T08:10:00+08:00
**Priority**: low
**Status**: pending
**Area**: docs
### Summary
`sessions_search` 用中文错误关键词查询返回空,不适合当作「错误发现」入口。
### Details
2026-09-16 连续两次中文查询(`错误 失败 修正` / `报错 失败 修正 不是这样 应该 重新`)均返回 `results: []`,但同一会话 transcript 中确实存在对应内容(`sessions_history` 可正常读到)。说明该检索路径对中文召回不可靠,会给出「什么都没发生」的假信号。
### Suggested Action
错误复盘以 `.learnings/` 记录为主数据源,配合 transcript / 日志直读;`sessions_search` 仅作辅助,空结果不当作「无此事」。
### Metadata
- Source: conversation
- Related Files: .learnings/
- Tags: 检索, 召回, 中文
- Pattern-Key: tooling.empty-search-result
- Recurrence-Count: 1
- First-Seen: 2026-09-16
- Last-Seen: 2026-09-16
---
+3 -2
View File
@@ -249,11 +249,12 @@
- **支持新旧两种模板**(自动识别):
- 旧模板(202601 等):多 sheet,每个 sheet 一个考核月;表头行4,目标行5起 6 项,月度评价在 J12/B12
- 新模板(8月起):单 sheet `OKR考核`;表头行5-6,目标行8起(跳过行7示例)5 项,B序号/C任务/D关键结果/E完成/G评分/I权重/J得分;人员信息在 B3;跨月周期取考核期末(如 7月27日-8月27日→202608);月度评价字段上级可能未填(NULL)
- **Excel 结构(旧模板)**:一个文件多 sheet202511/202512/202601/...),每个 sheet 一个考核月## 技能
- **Excel 结构(旧模板)**:一个文件多 sheet202511/202512/202601/...),每个 sheet 一个考核月
## 技能
- **周报技能**`skills/weekly-report-g5/SKILL.md`
- **周报技能**`skills/weekly-report/SKILL.md`G5/G6 通用)
- **自我改进技能**`skills/self-improving-agent/SKILL.md`(日志在 `.learnings/`
## 配置文件位置(红线)
@@ -0,0 +1,18 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "self-improving-agent",
"ownerHandle": "pskoett",
"installedAt": 1789545691162,
"artifact": {
"kind": "archive",
"sha256": "89f2a239f9d675c4c5787cf61c17f709cd38cb9c7d395f9dc50f487a59a47291",
"integrity": "sha256-ifKiOfnWdcTFeHz2HBf3Cc04y5x9OV+dxQ9IelmkcpE="
},
"skillFile": {
"path": "SKILL.md",
"sha256": "eff889950f23bb8a3de90cd617481bbf2557f9495f884bd5b1e9b7604a20817c"
},
"fileTreeSha256": "sha256:cd72f5c1910da8f21f4e326cc4f3b39de8b5bd371411f6a026f1f72b6747de8d",
"installedVersion": "4.0.2"
}
@@ -0,0 +1,175 @@
# Changelog
All notable changes to this skill are documented here, including anything an
agent or user must do when upgrading. Format follows
[Keep a Changelog](https://keepachangelog.com/); versions follow
[SemVer](https://semver.org/) and match `version` in `SKILL.md` frontmatter.
Read this before upgrading. General upgrade rules:
- **OpenClaw hook changes only take effect after re-copying and restarting.**
The hook runs from a copy: re-run
`cp -r ~/.openclaw/skills/self-improving-agent/hooks/openclaw ~/.openclaw/hooks/self-improvement`
and restart the gateway after upgrading.
- `.learnings/` files are user data and are never migrated or overwritten by
upgrades; first-use initialisation is idempotent and only creates missing
files.
## [Unreleased]
## [4.0.2] - 2026-08-06
> 4.0.1 is skipped: that version number was already used by a build
> published to ClawHub, so it is not reusable here.
### Fixed
- **Install commands and clone URL in the docs were wrong** (#24). The
advertised `clawdhub install` command does not exist. The documented
commands are now `openclaw skills install @pskoett/self-improving-agent`
(built-in, installs into the active workspace) and
`clawhub install @pskoett/self-improving-agent` (ClawHub CLI,
`npm i -g clawhub`, installs into `./skills` under the current working
directory). Both take the `@owner/slug` form — a bare slug is only
accepted for already-installed or unambiguous skills. Fixed in
`README.md`, `SKILL.md`, and `references/openclaw-integration.md`.
- The manual `git clone` URL used the wrong owner handle
(`peterskoett``pskoett`). The old handle only resolved via a web
redirect, which `git clone` does not follow, so manual installs failed on
a fresh machine.
### Added
- GitHub Actions CI (`.github/workflows/ci.yml` at the repo root): runs the
hook test suite, type-checks `handler.ts` (strict mode, against a CI-only
type stub), and syntax-checks `extract-skill.sh` on every push to master
and every PR.
### Changed
- **Repo restructured for ClawHub publishing**: the skill package now lives
in the repo's `self-improving-agent/` subfolder (SKILL.md, assets, hooks,
references, scripts, this changelog), keeping repo-level files (README,
`.github/`) out of the published skill. Install by copying the subfolder,
not the repo root — install/upgrade commands in the docs are updated.
- `SKILL.md` frontmatter `name` corrected from `self-improvement` to
`self-improving-agent` to match the skill folder name, as the Agent Skills
spec requires. The OpenClaw *hook* keeps its `self-improvement` name, so
existing hook installs and `openclaw hooks enable self-improvement` are
unaffected.
### Upgrade notes (4.0.x → 4.0.2)
Documentation only — no skill behavior, hook, or `.learnings/` changes. No
action needed beyond reinstalling if you want the corrected install docs.
## [4.0.0] - 2026-07-04
### Removed
- **This distribution is now OpenClaw-only.** Removed the Claude Code /
Codex / Copilot integrations: `scripts/activator.sh`,
`scripts/error-detector.sh`, `references/hooks-setup.md`, and the
multi-agent setup/support sections in `SKILL.md` and
`references/uninstall.md`. For other agents, use the original multi-agent
version: https://github.com/pskoett/pskoett-ai-skills.
### Changed
- Promotion targets are now the OpenClaw workspace files (`SOUL.md`,
`TOOLS.md`, `AGENTS.md`), with the project's own agent file as the target
for project-specific patterns.
- Hook docs and code comments describe the session-end sweep on its own
terms (OpenClaw has no per-tool-call event) instead of contrasting with
other platforms.
### Upgrade notes (0.3.0 → 4.0.0)
1. If you configured the Claude Code/Codex hooks from earlier versions,
remove the stale `.claude/settings.json` / `.codex/settings.json` entries
pointing at `activator.sh` / `error-detector.sh`, and switch to the
original multi-agent skill for those platforms.
2. Nothing changes for OpenClaw installs beyond the usual re-copy of the
hook directory; `.learnings/` data and entry formats are unaffected.
## [0.3.0] - 2026-07-04
### Added
- **Pattern-Key generalized to all three log files**: the `ERRORS.md` and
`FEATURE_REQUESTS.md` entry formats now carry a `Pattern-Key` field
(recommended for errors, optional for features), joining `LEARNINGS.md`
where it was previously limited to the simplify-and-harden feed.
- **Pattern-Key Taxonomy** section in `SKILL.md`: controlled `area.symptom`
namespaces (`api`, `auth`, `build`, `config`, `deps`, `fs`, `net`,
`runtime`, `shell`, `vcs`, plus `simplify`/`harden`), with reuse-before-mint
and one-key-per-entry rules.
- The OpenClaw session-end error sweep stamps deterministic `Pattern-Key`
values on auto-detected entries (e.g. `ModuleNotFoundError`
`deps.module-not-found`), making them recurrence-countable with no agent
discipline required.
### Changed
- Grep-by-`Pattern-Key` is now the documented **default dedup step** when
logging; keyword grep is the fallback. Recurrences are folded into the
existing entry (`Recurrence-Count`, `Last-Seen`, `See Also`) instead of
duplicated.
- Tightened `SKILL.md` (hook sections, taxonomy, duplicated setup blocks) to
keep the always-loaded skill prompt compact.
### Fixed
- `scripts/extract-skill.sh` is now committed with the executable bit set —
previously every install needed a manual `chmod +x`.
### Upgrade notes (0.2.0 → 0.3.0)
1. Re-copy the OpenClaw hook and restart the gateway (see general rules
above) to get Pattern-Key stamping on swept entries.
2. Existing `.learnings/` entries without `Pattern-Key` remain valid — the
field is additive. Add keys opportunistically when touching old entries.
## [0.2.0] - 2026-07-04
### Added
- **OpenClaw session-end error sweep** (`hooks/openclaw/`): the hook now also
fires on `command:new` / `command:reset`, scans the ended session's
transcript for the same error patterns as `scripts/error-detector.sh`, and
appends pending entries (`Source: openclaw-error-sweep`) to
`<workspace>/.learnings/ERRORS.md`. Opt-in: runs only when
`<workspace>/.learnings/` exists. Excerpts are capped, truncated, redacted,
and deduplicated.
- Pending-triage note in the bootstrap reminder when auto-detected error
entries await review.
- Test suite: `node --test hooks/openclaw/handler.test.js` (no dependencies).
- Docs: "Error Detection on OpenClaw" section and platform support matrix in
`references/openclaw-integration.md`; uninstall guide in
`references/uninstall.md`; this changelog.
### Changed
- `hooks/openclaw/HOOK.md` events metadata is now
`["agent:bootstrap", "command:new", "command:reset"]`.
- Docs now state explicitly that `scripts/error-detector.sh` is Claude Code
only (OpenClaw has no `PostToolUse` equivalent).
### Upgrade notes (0.1.x → 0.2.0)
1. Re-copy the hook and restart the gateway (see general rules above).
2. To enable the new error sweep: `mkdir -p ~/.openclaw/workspace/.learnings`.
Without that directory, behavior is identical to 0.1.0.
3. No breaking changes; no `.learnings/` migration needed.
## [0.1.0] - 2026-01-31
### Added
- Initial release: `SKILL.md` with logging formats (`LEARNINGS.md`,
`ERRORS.md`, `FEATURE_REQUESTS.md`), promotion workflow, and skill
extraction.
- OpenClaw bootstrap-reminder hook (`agent:bootstrap`).
- Claude Code hook scripts: `activator.sh` (UserPromptSubmit) and
`error-detector.sh` (PostToolUse).
- Reference guides for hooks setup and OpenClaw integration.
@@ -0,0 +1,594 @@
---
name: self-improving-agent
description: "Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects Claude ('No, that's wrong...', 'Actually...'), (3) User requests a capability that doesn't exist, (4) An external API or tool fails, (5) Claude realizes its knowledge is outdated or incorrect, (6) A better approach is discovered for a recurring task. Also review learnings before major tasks."
version: "4.0.2"
metadata:
---
# Self-Improvement Skill
Log learnings and errors to markdown files for continuous improvement. Agents can later process these into fixes, and important learnings get promoted to workspace memory. This version of the skill is built for OpenClaw only — for other agents, see the original multi-agent version at https://github.com/pskoett/pskoett-ai-skills.
## First-Use Initialisation
Before logging anything, ensure the `.learnings/` directory and files exist in the project or workspace root. If any are missing, create them:
```bash
mkdir -p .learnings
[ -f .learnings/LEARNINGS.md ] || printf "# Learnings\n\nCorrections, insights, and knowledge gaps captured during development.\n\n**Categories**: correction | insight | knowledge_gap | best_practice\n\n---\n" > .learnings/LEARNINGS.md
[ -f .learnings/ERRORS.md ] || printf "# Errors\n\nCommand failures and integration errors.\n\n---\n" > .learnings/ERRORS.md
[ -f .learnings/FEATURE_REQUESTS.md ] || printf "# Feature Requests\n\nCapabilities requested by the user.\n\n---\n" > .learnings/FEATURE_REQUESTS.md
```
Never overwrite existing files. This is a no-op if `.learnings/` is already initialised.
Do not log secrets, tokens, private keys, environment variables, or full source/config files unless the user explicitly asks for that level of detail. Prefer short summaries or redacted excerpts over raw command output or full transcripts.
If you want automatic reminders and session-end error detection, enable the opt-in hook described in [Optional: Enable Hook](#optional-enable-hook).
## Quick Reference
| Situation | Action |
|-----------|--------|
| Command/operation fails | Log to `.learnings/ERRORS.md` |
| User corrects you | Log to `.learnings/LEARNINGS.md` with category `correction` |
| User wants missing feature | Log to `.learnings/FEATURE_REQUESTS.md` |
| API/external tool fails | Log to `.learnings/ERRORS.md` with integration details |
| Knowledge was outdated | Log to `.learnings/LEARNINGS.md` with category `knowledge_gap` |
| Found better approach | Log to `.learnings/LEARNINGS.md` with category `best_practice` |
| Simplify/Harden recurring patterns | Log/update `.learnings/LEARNINGS.md` with `Source: simplify-and-harden` and a stable `Pattern-Key` |
| Similar to existing entry | Grep by `Pattern-Key` first, link with `**See Also**`, bump `Recurrence-Count` |
| Workflow improvements | Promote to `AGENTS.md` (workspace) |
| Tool gotchas | Promote to `TOOLS.md` (workspace) |
| Behavioral patterns | Promote to `SOUL.md` (workspace) |
## OpenClaw Setup
OpenClaw uses workspace-based prompt injection with automatic skill loading.
### Installation
**Via OpenClaw's built-in installer (recommended)** — installs into the
active OpenClaw workspace:
```bash
openclaw skills install @pskoett/self-improving-agent
```
**Via the ClawHub CLI** (`npm i -g clawhub`) — installs into `./skills`
under the current working directory, not the workspace:
```bash
clawhub install @pskoett/self-improving-agent
```
**Manual** (the skill lives in the repo's `self-improving-agent/` subfolder;
copy that folder, not the repo root):
```bash
git clone https://github.com/pskoett/self-improving-agent.git /tmp/self-improving-agent-repo
cp -r /tmp/self-improving-agent-repo/self-improving-agent ~/.openclaw/skills/self-improving-agent
```
Remade for openclaw from original repo : https://github.com/pskoett/pskoett-ai-skills - https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement
### Workspace Structure
OpenClaw injects these files into every session:
```
~/.openclaw/workspace/
├── AGENTS.md # Multi-agent workflows, delegation patterns
├── SOUL.md # Behavioral guidelines, personality, principles
├── TOOLS.md # Tool capabilities, integration gotchas
├── MEMORY.md # Long-term memory (main session only)
├── memory/ # Daily memory files
│ └── YYYY-MM-DD.md
└── .learnings/ # This skill's log files
├── LEARNINGS.md
├── ERRORS.md
└── FEATURE_REQUESTS.md
```
### Create Learning Files
```bash
mkdir -p ~/.openclaw/workspace/.learnings
```
Then create the log files (or copy from `assets/`):
- `LEARNINGS.md` — corrections, knowledge gaps, best practices
- `ERRORS.md` — command failures, exceptions
- `FEATURE_REQUESTS.md` — user-requested capabilities
### Promotion Targets
When learnings prove broadly applicable, promote them to workspace files:
| Learning Type | Promote To | Example |
|---------------|------------|---------|
| Behavioral patterns | `SOUL.md` | "Be concise, avoid disclaimers" |
| Workflow improvements | `AGENTS.md` | "Spawn sub-agents for long tasks" |
| Tool gotchas | `TOOLS.md` | "Git push needs auth configured first" |
### Inter-Session Communication
OpenClaw provides tools to share learnings across sessions:
- **sessions_list** — View active/recent sessions
- **sessions_history** — Read another session's transcript
- **sessions_send** — Send a learning to another session
- **sessions_spawn** — Spawn a sub-agent for background work
Use these only in trusted environments and only when the user explicitly wants cross-session sharing. Prefer sending a short sanitized summary and relevant file paths, not raw transcripts, secrets, or full command output.
### Optional: Enable Hook
For automatic reminders at session start and error detection at session end:
```bash
cp -r ~/.openclaw/skills/self-improving-agent/hooks/openclaw ~/.openclaw/hooks/self-improvement
openclaw hooks enable self-improvement
```
Fires on `agent:bootstrap` (injects the reminder, plus a pending-triage note
when auto-detected errors await review) and on `command:new`/`command:reset`
(sweeps the ended session's transcript for error patterns into
`<workspace>/.learnings/ERRORS.md`; opt-in — runs only when `.learnings/`
exists). OpenClaw has no per-tool-call hook event, so error detection happens
at session end. See `references/openclaw-integration.md` for details and
sweep limitations.
## Logging Format
### Learning Entry
Append to `.learnings/LEARNINGS.md`:
```markdown
## [LRN-YYYYMMDD-XXX] category
**Logged**: ISO-8601 timestamp
**Priority**: low | medium | high | critical
**Status**: pending
**Area**: frontend | backend | infra | tests | docs | config
### Summary
One-line description of what was learned
### Details
Full context: what happened, what was wrong, what's correct
### Suggested Action
Specific fix or improvement to make
### Metadata
- Source: conversation | error | user_feedback
- Related Files: path/to/file.ext
- Tags: tag1, tag2
- See Also: LRN-20250110-001 (if related to existing entry)
- Pattern-Key: area.symptom (recommended; e.g. deps.module-not-found, simplify.dead_code — see Pattern-Key Taxonomy)
- Recurrence-Count: 1 (optional)
- First-Seen: 2025-01-15 (optional)
- Last-Seen: 2025-01-15 (optional)
---
```
### Error Entry
Append to `.learnings/ERRORS.md`:
```markdown
## [ERR-YYYYMMDD-XXX] skill_or_command_name
**Logged**: ISO-8601 timestamp
**Priority**: high
**Status**: pending
**Area**: frontend | backend | infra | tests | docs | config
### Summary
Brief description of what failed
### Error
```
Actual error message or output
```
### Context
- Command/operation attempted
- Input or parameters used
- Environment details if relevant
- Summary or redacted excerpt of relevant output (avoid full transcripts and secret-bearing data by default)
### Suggested Fix
If identifiable, what might resolve this
### Metadata
- Reproducible: yes | no | unknown
- Related Files: path/to/file.ext
- See Also: ERR-20250110-001 (if recurring)
- Pattern-Key: area.symptom (recommended; e.g. net.connection-refused — see Pattern-Key Taxonomy)
- Recurrence-Count: 1 (optional)
- First-Seen: 2025-01-15 (optional)
- Last-Seen: 2025-01-15 (optional)
---
```
### Feature Request Entry
Append to `.learnings/FEATURE_REQUESTS.md`:
```markdown
## [FEAT-YYYYMMDD-XXX] capability_name
**Logged**: ISO-8601 timestamp
**Priority**: medium
**Status**: pending
**Area**: frontend | backend | infra | tests | docs | config
### Requested Capability
What the user wanted to do
### User Context
Why they needed it, what problem they're solving
### Complexity Estimate
simple | medium | complex
### Suggested Implementation
How this could be built, what it might extend
### Metadata
- Frequency: first_time | recurring
- Related Features: existing_feature_name
- Pattern-Key: area.symptom (optional — features usually dedupe by capability name; use a key only for recurring themes, e.g. api.missing-endpoint)
---
```
## ID Generation
Format: `TYPE-YYYYMMDD-XXX`
- TYPE: `LRN` (learning), `ERR` (error), `FEAT` (feature)
- YYYYMMDD: Current date
- XXX: Sequential number or random 3 chars (e.g., `001`, `A7B`)
Examples: `LRN-20250115-001`, `ERR-20250115-A3F`, `FEAT-20250115-002`
## Resolving Entries
When an issue is fixed, update the entry:
1. Change `**Status**: pending``**Status**: resolved`
2. Add resolution block after Metadata:
```markdown
### Resolution
- **Resolved**: 2025-01-16T09:00:00Z
- **Commit/PR**: abc123 or #42
- **Notes**: Brief description of what was done
```
Other status values:
- `in_progress` - Actively being worked on
- `wont_fix` - Decided not to address (add reason in Resolution notes)
- `promoted` - Elevated to a workspace file (`SOUL.md`, `TOOLS.md`, `AGENTS.md`)
## Promoting to Workspace Memory
When a learning is broadly applicable (not a one-off fix), promote it to a workspace file so every session inherits it.
### When to Promote
- Learning applies across multiple files/features
- Knowledge any contributor (human or AI) should know
- Prevents recurring mistakes
- Documents project-specific conventions
### Promotion Targets
| Target | What Belongs There |
|--------|-------------------|
| `SOUL.md` | Behavioral guidelines, communication style, principles |
| `TOOLS.md` | Tool capabilities, usage patterns, integration gotchas |
| `AGENTS.md` | Workflows, delegation patterns, automation rules |
When the learning is specific to a project repo you work in (not the
workspace), promote to that project's own agent file (e.g. its `AGENTS.md`)
instead.
### How to Promote
1. **Distill** the learning into a concise rule or fact
2. **Add** to appropriate section in target file (create file if needed)
3. **Update** original entry:
- Change `**Status**: pending``**Status**: promoted`
- Add `**Promoted**: SOUL.md`, `TOOLS.md`, or `AGENTS.md`
### Promotion Examples
**Learning** (verbose):
> Project uses pnpm workspaces. Attempted `npm install` but failed.
> Lock file is `pnpm-lock.yaml`. Must use `pnpm install`.
**In TOOLS.md** (concise):
```markdown
## Build & Dependencies
- Package manager: pnpm (not npm) - use `pnpm install`
```
**Learning** (verbose):
> When modifying API endpoints, must regenerate TypeScript client.
> Forgetting this causes type mismatches at runtime.
**In AGENTS.md** (actionable):
```markdown
## After API Changes
1. Regenerate client: `pnpm run generate:api`
2. Check for type errors: `pnpm tsc --noEmit`
```
## Pattern-Key Taxonomy
`Pattern-Key` is the stable dedup and recurrence key for entries in all three
log files: keyword grep misses semantically identical but differently-worded
entries, a shared key does not — and reliable keys are what make
`Recurrence-Count` and the promotion rule work.
**Format**: `area.symptom` — exactly two levels, lowercase, hyphenated
(e.g. `deps.module-not-found`). Keep symptoms generic enough to recur: no
file names, versions, or hostnames in keys.
| Area | Scope | Example Keys |
|------|-------|--------------|
| `api` | External API/service behavior | `api.rate-limit`, `api.schema-mismatch`, `api.missing-endpoint` |
| `auth` | Credentials, tokens, scopes | `auth.token-expired`, `auth.missing-scope` |
| `build` | Compilation, bundling, CI | `build.type-error`, `build.missing-artifact` |
| `config` | Config files, env vars, settings | `config.missing-env`, `config.invalid-json` |
| `deps` | Package managers, dependencies | `deps.module-not-found`, `deps.npm-error`, `deps.version-conflict` |
| `fs` | Filesystem | `fs.no-such-file`, `fs.permission-denied` |
| `net` | Network connectivity | `net.connection-refused`, `net.timeout` |
| `runtime` | Language/runtime errors not covered above | `runtime.type-error`, `runtime.python-exception` |
| `shell` | Shell/CLI mechanics | `shell.command-not-found`, `shell.nonzero-exit` |
| `vcs` | Git and other version control | `vcs.fatal-error`, `vcs.merge-conflict` |
| `simplify` / `harden` | Code-quality patterns from the simplify-and-harden feed | `simplify.dead_code`, `harden.input_validation` |
**Rules:**
1. **Reuse before minting**: `grep -rh "Pattern-Key:" .learnings/ | sort -u`
a near-match beats a new key.
2. **One key per manual entry**; auto-swept OpenClaw entries may carry
several — reduce to one when triaging.
3. **Mint new areas sparingly** — only when several entries would share one.
4. **Generic sweep keys** (`runtime.error`, `runtime.failure`) mean
"unclassified" — replace with a specific key during triage.
## Recurring Pattern Detection
If logging something similar to an existing entry:
1. **Search by key first**: `grep -n "Pattern-Key: area.symptom" .learnings/*.md`
— this is the default dedup check and catches rewordings that keyword
search misses
2. **Fallback keyword search**: `grep -ri "keyword" .learnings/` for entries
logged without a key
3. **Fold, don't duplicate**: on a hit, update the existing entry — bump
`Recurrence-Count`, set `Last-Seen`, add `**See Also**` — instead of
creating a new one
4. **Bump priority** if issue keeps recurring
5. **Consider systemic fix**: Recurring issues often indicate:
- Missing knowledge (→ promote to `TOOLS.md` or `SOUL.md`)
- Missing automation (→ add to `AGENTS.md`)
- Architectural problem (→ create tech debt ticket)
## Simplify & Harden Feed
Use this workflow to ingest recurring patterns from the `simplify-and-harden`
skill and turn them into durable prompt guidance.
### Ingestion Workflow
1. Read `simplify_and_harden.learning_loop.candidates` from the task summary.
2. For each candidate, use `pattern_key` as the stable dedupe key.
3. Search `.learnings/LEARNINGS.md` for an existing entry with that key:
- `grep -n "Pattern-Key: <pattern_key>" .learnings/LEARNINGS.md`
4. If found:
- Increment `Recurrence-Count`
- Update `Last-Seen`
- Add `See Also` links to related entries/tasks
5. If not found:
- Create a new `LRN-...` entry
- Set `Source: simplify-and-harden`
- Set `Pattern-Key`, `Recurrence-Count: 1`, and `First-Seen`/`Last-Seen`
### Promotion Rule (System Prompt Feedback)
Promote recurring patterns into agent context/system prompt files when all are true:
- `Recurrence-Count >= 3`
- Seen across at least 2 distinct tasks
- Occurred within a 30-day window
Promotion targets: `SOUL.md`, `TOOLS.md`, or `AGENTS.md` (workspace), or the
project's own agent file when the pattern is project-specific.
Write promoted rules as short prevention rules (what to do before/while coding),
not long incident write-ups.
## Periodic Review
Review `.learnings/` at natural breakpoints:
### When to Review
- Before starting a new major task
- After completing a feature
- When working in an area with past learnings
- Weekly during active development
### Quick Status Check
```bash
# Count pending items
grep -h "Status\*\*: pending" .learnings/*.md | wc -l
# List pending high-priority items
grep -B5 "Priority\*\*: high" .learnings/*.md | grep "^## \["
# Find learnings for a specific area
grep -l "Area\*\*: backend" .learnings/*.md
```
### Review Actions
- Resolve fixed items
- Promote applicable learnings
- Link related entries
- Escalate recurring issues
## Detection Triggers
Automatically log when you notice:
**Corrections** (→ learning with `correction` category):
- "No, that's not right..."
- "Actually, it should be..."
- "You're wrong about..."
- "That's outdated..."
**Feature Requests** (→ feature request):
- "Can you also..."
- "I wish you could..."
- "Is there a way to..."
- "Why can't you..."
**Knowledge Gaps** (→ learning with `knowledge_gap` category):
- User provides information you didn't know
- Documentation you referenced is outdated
- API behavior differs from your understanding
**Errors** (→ error entry):
- Command returns non-zero exit code
- Exception or stack trace
- Unexpected output or behavior
- Timeout or connection failure
## Priority Guidelines
| Priority | When to Use |
|----------|-------------|
| `critical` | Blocks core functionality, data loss risk, security issue |
| `high` | Significant impact, affects common workflows, recurring issue |
| `medium` | Moderate impact, workaround exists |
| `low` | Minor inconvenience, edge case, nice-to-have |
## Area Tags
Use to filter learnings by codebase region:
| Area | Scope |
|------|-------|
| `frontend` | UI, components, client-side code |
| `backend` | API, services, server-side code |
| `infra` | CI/CD, deployment, Docker, cloud |
| `tests` | Test files, testing utilities, coverage |
| `docs` | Documentation, comments, READMEs |
| `config` | Configuration files, environment, settings |
## Best Practices
1. **Log immediately** - context is freshest right after the issue
2. **Be specific** - future agents need to understand quickly
3. **Include reproduction steps** - especially for errors
4. **Link related files** - makes fixes easier
5. **Suggest concrete fixes** - not just "investigate"
6. **Use consistent categories** - enables filtering
7. **Promote aggressively** - if in doubt, add to `TOOLS.md` or `SOUL.md`
8. **Review regularly** - stale learnings lose value
## Gitignore Options
**Keep learnings local** (per-developer):
```gitignore
.learnings/
```
This repo uses that default to avoid committing sensitive or noisy local logs by accident.
**Track learnings in repo** (team-wide):
Don't add to .gitignore - learnings become shared knowledge.
**Hybrid** (track templates, ignore entries):
```gitignore
.learnings/*.md
!.learnings/.gitkeep
```
## Upgrading & Uninstalling
Read `CHANGELOG.md` before upgrading — it carries per-version notes, and
hook changes require re-copying the hook and restarting the gateway.
To disable or remove the skill, follow `references/uninstall.md`:
`.learnings/` is user data (review before deleting), and content promoted to
`SOUL.md`/`TOOLS.md`/`AGENTS.md` stays until removed manually.
## Automatic Skill Extraction
When a learning is valuable enough to become a reusable skill, extract it using the provided helper.
### Skill Extraction Criteria
A learning qualifies for skill extraction when ANY of these apply:
| Criterion | Description |
|-----------|-------------|
| **Recurring** | Has `See Also` links to 2+ similar issues |
| **Verified** | Status is `resolved` with working fix |
| **Non-obvious** | Required actual debugging/investigation to discover |
| **Broadly applicable** | Not project-specific; useful across codebases |
| **User-flagged** | User says "save this as a skill" or similar |
### Extraction Workflow
1. **Identify candidate**: Learning meets extraction criteria
2. **Run helper** (or create manually):
```bash
~/.openclaw/skills/self-improving-agent/scripts/extract-skill.sh skill-name --dry-run
~/.openclaw/skills/self-improving-agent/scripts/extract-skill.sh skill-name
```
3. **Customize SKILL.md**: Fill in template with learning content
4. **Update learning**: Set status to `promoted_to_skill`, add `Skill-Path`
5. **Verify**: Read skill in fresh session to ensure it's self-contained
### Manual Extraction
If you prefer manual creation:
1. Create `skills/<skill-name>/SKILL.md`
2. Use template from `assets/SKILL-TEMPLATE.md`
3. Follow [Agent Skills spec](https://agentskills.io/specification):
- YAML frontmatter with `name` and `description`
- Name must match folder name
- No README.md inside skill folder
### Extraction Detection Triggers
Watch for these signals that a learning should become a skill:
**In conversation:**
- "Save this as a skill"
- "I keep running into this"
- "This would be useful for other projects"
- "Remember this pattern"
**In learning entries:**
- Multiple `See Also` links (recurring issue)
- High priority + resolved status
- Category: `best_practice` with broad applicability
- User feedback praising the solution
### Skill Quality Gates
Before extraction, verify:
- [ ] Solution is tested and working
- [ ] Description is clear without original context
- [ ] Code examples are self-contained
- [ ] No project-specific hardcoded values
- [ ] Follows skill naming conventions (lowercase, hyphens)
@@ -0,0 +1,6 @@
{
"ownerId": "kn70cjr952qdec1nx70zs6wefn7ynq2t",
"slug": "self-improving-agent",
"version": "4.0.2",
"publishedAt": 1785999527780
}
@@ -0,0 +1,5 @@
# Errors Log
Command failures, exceptions, and unexpected behaviors.
---
@@ -0,0 +1,5 @@
# Feature Requests
Capabilities requested by user that don't currently exist.
---
@@ -0,0 +1,45 @@
# Learnings
Corrections, insights, and knowledge gaps captured during development.
**Categories**: correction | insight | knowledge_gap | best_practice
**Areas**: frontend | backend | infra | tests | docs | config
**Statuses**: pending | in_progress | resolved | wont_fix | promoted | promoted_to_skill
## Status Definitions
| Status | Meaning |
|--------|---------|
| `pending` | Not yet addressed |
| `in_progress` | Actively being worked on |
| `resolved` | Issue fixed or knowledge integrated |
| `wont_fix` | Decided not to address (reason in Resolution) |
| `promoted` | Elevated to SOUL.md, TOOLS.md, or AGENTS.md |
| `promoted_to_skill` | Extracted as a reusable skill |
## Skill Extraction Fields
When a learning is promoted to a skill, add these fields:
```markdown
**Status**: promoted_to_skill
**Skill-Path**: skills/skill-name
```
Example:
```markdown
## [LRN-20250115-001] best_practice
**Logged**: 2025-01-15T10:00:00Z
**Priority**: high
**Status**: promoted_to_skill
**Skill-Path**: skills/docker-m1-fixes
**Area**: infra
### Summary
Docker build fails on Apple Silicon due to platform mismatch
...
```
---
@@ -0,0 +1,177 @@
# Skill Template
Template for creating skills extracted from learnings. Copy and customize.
---
## SKILL.md Template
```markdown
---
name: skill-name-here
description: "Concise description of when and why to use this skill. Include trigger conditions."
---
# Skill Name
Brief introduction explaining the problem this skill solves and its origin.
## Quick Reference
| Situation | Action |
|-----------|--------|
| [Trigger 1] | [Action 1] |
| [Trigger 2] | [Action 2] |
## Background
Why this knowledge matters. What problems it prevents. Context from the original learning.
## Solution
### Step-by-Step
1. First step with code or command
2. Second step
3. Verification step
### Code Example
\`\`\`language
// Example code demonstrating the solution
\`\`\`
## Common Variations
- **Variation A**: Description and how to handle
- **Variation B**: Description and how to handle
## Gotchas
- Warning or common mistake #1
- Warning or common mistake #2
## Related
- Link to related documentation
- Link to related skill
## Source
Extracted from learning entry.
- **Learning ID**: LRN-YYYYMMDD-XXX
- **Original Category**: correction | insight | knowledge_gap | best_practice
- **Extraction Date**: YYYY-MM-DD
```
---
## Minimal Template
For simple skills that don't need all sections:
```markdown
---
name: skill-name-here
description: "What this skill does and when to use it."
---
# Skill Name
[Problem statement in one sentence]
## Solution
[Direct solution with code/commands]
## Source
- Learning ID: LRN-YYYYMMDD-XXX
```
---
## Template with Scripts
For skills that include executable helpers:
```markdown
---
name: skill-name-here
description: "What this skill does and when to use it."
---
# Skill Name
[Introduction]
## Quick Reference
| Command | Purpose |
|---------|---------|
| `./scripts/helper.sh` | [What it does] |
| `./scripts/validate.sh` | [What it does] |
## Usage
### Automated (Recommended)
\`\`\`bash
./skills/skill-name/scripts/helper.sh [args]
\`\`\`
### Manual Steps
1. Step one
2. Step two
## Scripts
| Script | Description |
|--------|-------------|
| `scripts/helper.sh` | Main utility |
| `scripts/validate.sh` | Validation checker |
## Source
- Learning ID: LRN-YYYYMMDD-XXX
```
---
## Naming Conventions
- **Skill name**: lowercase, hyphens for spaces
- Good: `docker-m1-fixes`, `api-timeout-patterns`
- Bad: `Docker_M1_Fixes`, `APITimeoutPatterns`
- **Description**: Start with action verb, mention trigger
- Good: "Handles Docker build failures on Apple Silicon. Use when builds fail with platform mismatch."
- Bad: "Docker stuff"
- **Files**:
- `SKILL.md` - Required, main documentation
- `scripts/` - Optional, executable code
- `references/` - Optional, detailed docs
- `assets/` - Optional, templates
---
## Extraction Checklist
Before creating a skill from a learning:
- [ ] Learning is verified (status: resolved)
- [ ] Solution is broadly applicable (not one-off)
- [ ] Content is complete (has all needed context)
- [ ] Name follows conventions
- [ ] Description is concise but informative
- [ ] Quick Reference table is actionable
- [ ] Code examples are tested
- [ ] Source learning ID is recorded
After creating:
- [ ] Update original learning with `promoted_to_skill` status
- [ ] Add `Skill-Path: skills/skill-name` to learning metadata
- [ ] Test skill by reading it in a fresh session
@@ -0,0 +1,68 @@
---
name: self-improvement
description: "Injects self-improvement reminder at bootstrap and sweeps ended sessions for errors"
metadata: {"openclaw":{"emoji":"🧠","events":["agent:bootstrap","command:new","command:reset"]}}
---
# Self-Improvement Hook
Injects a reminder to evaluate learnings during agent bootstrap, and detects
errors from ended sessions.
OpenClaw has no per-tool-call hook event, so errors cannot be detected in
real time after each command. This hook detects them with a session-end
error sweep instead.
## What It Does
**On `agent:bootstrap`** (before workspace files are injected):
- Adds a reminder block to check `.learnings/` for relevant entries
- Prompts the agent to log corrections, errors, and discoveries
- If auto-detected errors are awaiting triage, includes a pending-triage note
**On `command:new` / `command:reset`** (session end):
- Locates the transcript of the session that just ended
(`context.previousSessionEntry.sessionFile`, falling back to
`<workspace>/sessions/<sessionId>.jsonl`)
- Scans it against a fixed error-pattern list
(`Error:`, `command not found`, `Traceback`, `npm ERR!`, …)
- Appends a `pending` entry to `<workspace>/.learnings/ERRORS.md` with short,
truncated, redacted excerpts (max 5 per sweep) for the next session to triage
- Stamps each entry with deterministic `Pattern-Key` values derived from the
matched pattern (e.g. `deps.module-not-found`, `shell.command-not-found`),
so auto-detected errors can be deduplicated and recurrence-counted by key
(see the Pattern-Key Taxonomy in `SKILL.md`)
## Opt-In and Safety
- The sweep only runs when `<workspace>/.learnings/` exists — create that
directory to enable it, delete it to disable it
- `ERRORS.md` is created only if missing and is otherwise appended to, never
overwritten
- Excerpts are truncated to 200 characters and common secret shapes (bearer
tokens, API keys, GitHub/Slack/AWS tokens, JWTs, long opaque blobs) are
redacted before writing; excerpts already present in `ERRORS.md` are skipped
- Hook failures are swallowed so the gateway is never affected; set
`SELF_IMPROVEMENT_HOOK_DEBUG=1` to log failures
## Configuration
No configuration needed. Enable with:
```bash
openclaw hooks enable self-improvement
```
Enable the error sweep by creating the learnings directory:
```bash
mkdir -p ~/.openclaw/workspace/.learnings
```
## Testing
```bash
node --test hooks/openclaw/handler.test.js
```
@@ -0,0 +1,448 @@
/**
* Self-Improvement Hook for OpenClaw
*
* OpenClaw has no per-tool-call hook event, so errors cannot be detected in
* real time after each command. This hook detects them at session end
* instead:
*
* - agent:bootstrap Injects the self-improvement reminder before
* workspace files are injected, including a note
* when auto-detected errors are awaiting triage.
* - command:new / :reset Session-end sweep: scans the transcript of the
* session that just ended for error patterns and
* appends a pending entry to
* <workspace>/.learnings/ERRORS.md.
*
* The sweep is opt-in: it only runs when <workspace>/.learnings/ exists.
* Excerpts are truncated and redacted before being written.
*/
const fs = require('node:fs/promises');
const path = require('node:path');
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
const REMINDER_PATH = REMINDER_NAME;
const REMINDER_HEADER = '## Self-Improvement Reminder';
const REMINDER_CONTENT = `
${REMINDER_HEADER}
After completing tasks, evaluate whether any learnings should be captured.
Only log if this repo or workspace is using the self-improvement skill.
Before logging:
- Create only missing \`.learnings/\` files; never overwrite existing content
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
- Prefer short summaries or redacted excerpts over full command output
**Log when:**
- User corrects you → \`.learnings/LEARNINGS.md\`
- Command/operation fails → \`.learnings/ERRORS.md\`
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
- You find a better approach → \`.learnings/LEARNINGS.md\`
**Promote when pattern is proven:**
- Behavioral patterns → \`SOUL.md\`
- Workflow improvements → \`AGENTS.md\`
- Tool gotchas → \`TOOLS.md\`
Keep entries simple: date, title, what happened, and what to do differently.
`.trim();
// Error-detection patterns. Ordered specific → generic: the first matching pattern
// supplies the Pattern-Key stamped on swept entries, which is what makes
// auto-detected errors dedup-able and recurrence-countable (see the
// "Pattern-Key Taxonomy" section in SKILL.md).
const ERROR_PATTERN_KEYS = [
['command not found', 'shell.command-not-found'],
['No such file', 'fs.no-such-file'],
['Permission denied', 'fs.permission-denied'],
['ModuleNotFoundError', 'deps.module-not-found'],
['npm ERR!', 'deps.npm-error'],
['Traceback', 'runtime.python-exception'],
['SyntaxError', 'runtime.syntax-error'],
['TypeError', 'runtime.type-error'],
['Exception', 'runtime.exception'],
['fatal:', 'vcs.fatal-error'],
['exit code', 'shell.nonzero-exit'],
['non-zero', 'shell.nonzero-exit'],
['error:', 'runtime.error'],
['Error:', 'runtime.error'],
['ERROR:', 'runtime.error'],
['failed', 'runtime.failure'],
['FAILED', 'runtime.failure'],
];
const SWEEP_SOURCE = 'openclaw-error-sweep';
const MAX_EXCERPTS = 5;
const MAX_EXCERPT_LENGTH = 200;
const ERRORS_FILE_HEADER = '# Errors\n\nCommand failures and integration errors.\n\n---\n';
// Best-effort redaction of common secret shapes before anything is written.
const REDACTION_RULES = [
[/\b(api[_-]?key|token|secret|password|passwd|authorization|credential)s?\b(\s*[=:]\s*)\S+/gi, '$1$2[REDACTED]'],
[/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [REDACTED]'],
[/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g, '[REDACTED]'],
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, '[REDACTED]'],
[/\bAKIA[0-9A-Z]{16}\b/g, '[REDACTED]'],
[/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/g, '[REDACTED-JWT]'],
[/\b[A-Za-z0-9_-]{40,}\b/g, '[REDACTED-BLOB]'],
];
function isObject(value) {
return !!value && typeof value === 'object';
}
function isInjectedReminderFile(value) {
if (!isObject(value) || value.path !== REMINDER_PATH) {
return false;
}
return (
value.virtual === true ||
(typeof value.content === 'string' && value.content.includes(REMINDER_HEADER))
);
}
function redactSensitiveText(text) {
let result = text;
for (const [pattern, replacement] of REDACTION_RULES) {
result = result.replace(pattern, replacement);
}
return result;
}
function sanitizeExcerptLine(line) {
let excerpt = redactSensitiveText(line.trim()).split('```').join("'''");
if (excerpt.length > MAX_EXCERPT_LENGTH) {
excerpt = `${excerpt.slice(0, MAX_EXCERPT_LENGTH)}`;
}
return excerpt;
}
function collectTextFragments(value, out, depth = 0) {
if (depth > 4 || out.length > 200) {
return;
}
if (typeof value === 'string') {
out.push(value);
return;
}
if (Array.isArray(value)) {
for (const item of value) {
collectTextFragments(item, out, depth + 1);
}
return;
}
if (isObject(value)) {
if (typeof value.text === 'string') {
out.push(value.text);
}
if ('content' in value) {
collectTextFragments(value.content, out, depth + 1);
}
}
}
function matchErrorPatternKey(line) {
for (const [pattern, patternKey] of ERROR_PATTERN_KEYS) {
if (line.includes(pattern)) {
return patternKey;
}
}
return null;
}
async function scanTranscriptForErrors(sessionFilePath) {
let raw;
try {
raw = await fs.readFile(sessionFilePath, 'utf-8');
} catch {
return [];
}
const excerpts = [];
const seen = new Set();
for (const jsonLine of raw.split('\n')) {
if (excerpts.length >= MAX_EXCERPTS) {
break;
}
const trimmed = jsonLine.trim();
if (!trimmed) {
continue;
}
let entry;
try {
entry = JSON.parse(trimmed);
} catch {
continue;
}
if (!isObject(entry) || !isObject(entry.message)) {
continue;
}
const fragments = [];
collectTextFragments(entry.message.content, fragments);
for (const fragment of fragments) {
for (const line of fragment.split('\n')) {
const patternKey = matchErrorPatternKey(line);
if (!patternKey) {
continue;
}
const excerpt = sanitizeExcerptLine(line);
if (!excerpt || seen.has(excerpt)) {
continue;
}
seen.add(excerpt);
excerpts.push({ excerpt, patternKey });
if (excerpts.length >= MAX_EXCERPTS) {
return excerpts;
}
}
}
}
return excerpts;
}
function resolveSessionFilePath(context, workspaceDir) {
const sessionEntry = isObject(context.previousSessionEntry)
? context.previousSessionEntry
: isObject(context.sessionEntry)
? context.sessionEntry
: {};
if (typeof sessionEntry.sessionFile === 'string' && sessionEntry.sessionFile.trim()) {
return sessionEntry.sessionFile;
}
const sessionId =
typeof sessionEntry.sessionId === 'string' ? sessionEntry.sessionId.trim() : '';
if (sessionId && workspaceDir) {
return path.join(workspaceDir, 'sessions', `${sessionId}.jsonl`);
}
return undefined;
}
function generateEntryId(timestamp) {
const yyyymmdd = timestamp.toISOString().slice(0, 10).replace(/-/g, '');
const suffix = Math.random().toString(36).slice(2, 5).toUpperCase().padEnd(3, '0');
return `ERR-${yyyymmdd}-${suffix}`;
}
function formatErrorEntry(params) {
const { excerpts, sessionKey, sessionFilePath, action, timestamp } = params;
const plural = excerpts.length === 1 ? '' : 's';
const patternKeys = [...new Set(excerpts.map((item) => item.patternKey))];
return [
`## [${generateEntryId(timestamp)}] openclaw_session_sweep`,
'',
`**Logged**: ${timestamp.toISOString()}`,
'**Priority**: medium',
'**Status**: pending',
'**Area**: config',
'',
'### Summary',
`Session-end sweep detected ${excerpts.length} possible error${plural} in the previous OpenClaw session.`,
'',
'### Error',
'```',
...excerpts.map((item) => item.excerpt),
'```',
'',
'### Context',
`- Detected by the self-improvement hook on \`/${action}\` (OpenClaw has no per-tool-call hook, so errors are swept from the session transcript at session end)`,
`- Session key: ${sessionKey || 'unknown'}`,
`- Session transcript: ${sessionFilePath}`,
'- Excerpts are truncated and redacted; check the transcript for full context',
'',
'### Suggested Fix',
'Triage this entry: if the error was real and non-obvious, keep it and fill in the fix; otherwise mark it resolved or delete it. Before keeping it, grep for its Pattern-Key(s) and fold recurrences into the existing entry (bump Recurrence-Count) instead of duplicating.',
'',
'### Metadata',
`- Source: ${SWEEP_SOURCE}`,
'- Reproducible: unknown',
...patternKeys.map((patternKey) => `- Pattern-Key: ${patternKey}`),
'',
'---',
].join('\n');
}
async function handleSessionEndSweep(event) {
const context = event.context;
const workspaceDir =
typeof context.workspaceDir === 'string' && context.workspaceDir.trim()
? context.workspaceDir
: undefined;
if (!workspaceDir) {
return;
}
// Opt-in gate: only sweep when the workspace uses the self-improvement skill.
const learningsDir = path.join(workspaceDir, '.learnings');
try {
const stats = await fs.stat(learningsDir);
if (!stats.isDirectory()) {
return;
}
} catch {
return;
}
const sessionFilePath = resolveSessionFilePath(context, workspaceDir);
if (!sessionFilePath) {
return;
}
const excerpts = await scanTranscriptForErrors(sessionFilePath);
if (excerpts.length === 0) {
return;
}
const errorsFilePath = path.join(learningsDir, 'ERRORS.md');
let existing = '';
try {
existing = await fs.readFile(errorsFilePath, 'utf-8');
} catch {
// Missing file is fine; it is created below.
}
const freshExcerpts = excerpts.filter((item) => !existing.includes(item.excerpt));
if (freshExcerpts.length === 0) {
return;
}
const entry = formatErrorEntry({
excerpts: freshExcerpts,
sessionKey: typeof event.sessionKey === 'string' ? event.sessionKey : '',
sessionFilePath,
action: event.action,
timestamp: event.timestamp instanceof Date ? event.timestamp : new Date(),
});
if (!existing) {
try {
await fs.writeFile(errorsFilePath, `${ERRORS_FILE_HEADER}\n${entry}\n`, { flag: 'wx' });
return;
} catch (err) {
if (!isObject(err) || err.code !== 'EEXIST') {
throw err;
}
}
}
await fs.appendFile(errorsFilePath, `\n${entry}\n`);
}
async function countPendingSweepEntries(workspaceDir) {
if (!workspaceDir) {
return 0;
}
let content;
try {
content = await fs.readFile(path.join(workspaceDir, '.learnings', 'ERRORS.md'), 'utf-8');
} catch {
return 0;
}
return content
.split(/^## /m)
.slice(1)
.filter(
(section) =>
section.includes(`Source: ${SWEEP_SOURCE}`) && section.includes('**Status**: pending'),
).length;
}
async function handleBootstrap(event) {
// Skip sub-agent sessions to avoid bootstrap issues
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
const sessionKey = event.sessionKey || '';
if (sessionKey.includes(':subagent:')) {
return;
}
// Inject the reminder as a virtual bootstrap file
// Check that bootstrapFiles is an array before pushing
if (!Array.isArray(event.context.bootstrapFiles)) {
return;
}
const occupiedByOtherFile = event.context.bootstrapFiles.some(
(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),
);
if (occupiedByOtherFile) {
return;
}
let reminderContent = REMINDER_CONTENT;
const workspaceDir =
typeof event.context.workspaceDir === 'string' && event.context.workspaceDir.trim()
? event.context.workspaceDir
: undefined;
const pendingSweepCount = await countPendingSweepEntries(workspaceDir);
if (pendingSweepCount > 0) {
const plural = pendingSweepCount === 1 ? 'y' : 'ies';
reminderContent +=
`\n\n**Pending triage:** ${pendingSweepCount} auto-detected error entr${plural} ` +
`(Source: ${SWEEP_SOURCE}) in \`.learnings/ERRORS.md\` await review. ` +
'Confirm, resolve, or delete them when convenient.';
}
const cleanedBootstrapFiles = event.context.bootstrapFiles.filter(
(file, index, files) =>
!isInjectedReminderFile(file) ||
files.findIndex((candidate) => isInjectedReminderFile(candidate)) === index,
);
const reminderFile = {
name: REMINDER_NAME,
path: REMINDER_PATH,
content: reminderContent,
missing: false,
virtual: true,
};
const existingIndex = cleanedBootstrapFiles.findIndex((file) => isInjectedReminderFile(file));
if (existingIndex === -1) {
cleanedBootstrapFiles.push(reminderFile);
} else {
cleanedBootstrapFiles[existingIndex] = reminderFile;
}
event.context.bootstrapFiles = cleanedBootstrapFiles;
}
const handler = async (event) => {
// Safety checks for event structure
if (!event || typeof event !== 'object') {
return;
}
if (!event.context || typeof event.context !== 'object') {
return;
}
try {
if (event.type === 'agent' && event.action === 'bootstrap') {
await handleBootstrap(event);
return;
}
if (event.type === 'command' && (event.action === 'new' || event.action === 'reset')) {
await handleSessionEndSweep(event);
}
} catch (err) {
// Never break the gateway on hook failure.
if (process.env.SELF_IMPROVEMENT_HOOK_DEBUG) {
console.error('[self-improvement] hook failed:', err);
}
}
};
module.exports = handler;
module.exports.default = handler;
@@ -0,0 +1,229 @@
/**
* Tests for the OpenClaw self-improvement hook.
*
* Run with: node --test hooks/openclaw/
* (no dependencies; uses the built-in node:test runner)
*/
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const { test, beforeEach, afterEach } = require('node:test');
const handler = require('./handler.js');
let workspaceDir;
beforeEach(async () => {
workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), 'self-improvement-hook-'));
});
afterEach(async () => {
await fs.rm(workspaceDir, { recursive: true, force: true });
});
function makeBootstrapEvent(overrides = {}) {
return {
type: 'agent',
action: 'bootstrap',
sessionKey: 'agent:main:whatsapp',
timestamp: new Date('2026-07-04T12:00:00Z'),
messages: [],
context: { workspaceDir, bootstrapFiles: [] },
...overrides,
};
}
function makeCommandEvent(action, sessionFile) {
return {
type: 'command',
action,
sessionKey: 'agent:main:whatsapp',
timestamp: new Date('2026-07-04T12:00:00Z'),
messages: [],
context: {
workspaceDir,
commandSource: 'whatsapp',
previousSessionEntry: { sessionId: 'abc123', sessionFile },
},
};
}
async function writeTranscript(lines) {
const sessionsDir = path.join(workspaceDir, 'sessions');
await fs.mkdir(sessionsDir, { recursive: true });
const sessionFile = path.join(sessionsDir, 'abc123.jsonl');
await fs.writeFile(sessionFile, lines.map((line) => JSON.stringify(line)).join('\n'));
return sessionFile;
}
function toolResultLine(text) {
return {
type: 'message',
message: { role: 'toolResult', content: [{ type: 'text', text }] },
};
}
const errorsFile = () => path.join(workspaceDir, '.learnings', 'ERRORS.md');
test('bootstrap injects the reminder as a virtual file', async () => {
const event = makeBootstrapEvent();
await handler(event);
assert.equal(event.context.bootstrapFiles.length, 1);
const injected = event.context.bootstrapFiles[0];
assert.equal(injected.path, 'SELF_IMPROVEMENT_REMINDER.md');
assert.equal(injected.virtual, true);
assert.match(injected.content, /## Self-Improvement Reminder/);
assert.doesNotMatch(injected.content, /Pending triage/);
});
test('bootstrap skips sub-agent sessions', async () => {
const event = makeBootstrapEvent({ sessionKey: 'agent:main:subagent:xyz' });
await handler(event);
assert.equal(event.context.bootstrapFiles.length, 0);
});
test('bootstrap deduplicates a previously injected reminder', async () => {
const event = makeBootstrapEvent();
event.context.bootstrapFiles.push({
name: 'SELF_IMPROVEMENT_REMINDER.md',
path: 'SELF_IMPROVEMENT_REMINDER.md',
content: '## Self-Improvement Reminder\n\nstale copy',
virtual: true,
});
await handler(event);
assert.equal(event.context.bootstrapFiles.length, 1);
});
test('session-end sweep appends detected errors to ERRORS.md', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
const sessionFile = await writeTranscript([
{ type: 'message', message: { role: 'user', content: 'run the build' } },
toolResultLine('npm ERR! missing script: build\nbash: tsc: command not found'),
{ type: 'message', message: { role: 'assistant', content: 'Build is broken.' } },
]);
await handler(makeCommandEvent('new', sessionFile));
const content = await fs.readFile(errorsFile(), 'utf-8');
assert.match(content, /^# Errors/);
assert.match(content, /## \[ERR-20260704-[A-Z0-9]{3}\] openclaw_session_sweep/);
assert.match(content, /npm ERR! missing script: build/);
assert.match(content, /command not found/);
assert.match(content, /Source: openclaw-error-sweep/);
assert.match(content, /\*\*Status\*\*: pending/);
assert.match(content, /- Pattern-Key: deps\.npm-error/);
assert.match(content, /- Pattern-Key: shell\.command-not-found/);
});
test('sweep stamps the most specific Pattern-Key and dedupes keys', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
const sessionFile = await writeTranscript([
// 'ModuleNotFoundError' must win over the generic 'Error:'/'Traceback' buckets
toolResultLine("ModuleNotFoundError: No module named 'requests'"),
toolResultLine('TypeError: cannot read properties of undefined (first)'),
toolResultLine('TypeError: cannot read properties of undefined (second)'),
]);
await handler(makeCommandEvent('new', sessionFile));
const content = await fs.readFile(errorsFile(), 'utf-8');
assert.match(content, /- Pattern-Key: deps\.module-not-found/);
const typeErrorKeys = content.split('- Pattern-Key: runtime.type-error').length - 1;
assert.equal(typeErrorKeys, 1);
assert.doesNotMatch(content, /- Pattern-Key: runtime\.error/);
});
test('sweep does nothing when .learnings/ does not exist (opt-in gate)', async () => {
const sessionFile = await writeTranscript([toolResultLine('fatal: not a git repository')]);
await handler(makeCommandEvent('new', sessionFile));
await assert.rejects(fs.access(errorsFile()));
});
test('sweep does nothing when the transcript has no errors', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
const sessionFile = await writeTranscript([
{ type: 'message', message: { role: 'assistant', content: 'All good!' } },
]);
await handler(makeCommandEvent('new', sessionFile));
await assert.rejects(fs.access(errorsFile()));
});
test('sweep never overwrites an existing ERRORS.md', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
await fs.writeFile(errorsFile(), '# Errors\n\nExisting notes.\n\n---\n');
const sessionFile = await writeTranscript([toolResultLine('Error: connection refused')]);
await handler(makeCommandEvent('reset', sessionFile));
const content = await fs.readFile(errorsFile(), 'utf-8');
assert.match(content, /Existing notes\./);
assert.match(content, /Error: connection refused/);
});
test('sweep is idempotent for already-logged excerpts', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
const sessionFile = await writeTranscript([toolResultLine('Error: connection refused')]);
await handler(makeCommandEvent('new', sessionFile));
await handler(makeCommandEvent('new', sessionFile));
const content = await fs.readFile(errorsFile(), 'utf-8');
const occurrences = content.split('Error: connection refused').length - 1;
assert.equal(occurrences, 1);
});
test('sweep redacts secrets and truncates long lines', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
const longTail = 'x'.repeat(300);
// Fixture secret is assembled at runtime so the literal never appears in
// this file and secret scanners don't flag it as an exposed credential.
const fakeKey = ['sk', 'live', '1234567890'].join('-');
const sessionFile = await writeTranscript([
toolResultLine(
`Error: request failed with api_key=${fakeKey} Bearer abc.def.ghi token: hunter2 ${longTail}`,
),
]);
await handler(makeCommandEvent('new', sessionFile));
const content = await fs.readFile(errorsFile(), 'utf-8');
assert.doesNotMatch(content, new RegExp(fakeKey));
assert.doesNotMatch(content, /hunter2/);
assert.match(content, /\[REDACTED\]/);
assert.doesNotMatch(content, /x{250}/);
});
test('sweep falls back to <workspace>/sessions/<sessionId>.jsonl', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
await writeTranscript([toolResultLine('Traceback (most recent call last):')]);
await handler(makeCommandEvent('new', undefined));
const content = await fs.readFile(errorsFile(), 'utf-8');
assert.match(content, /Traceback/);
});
test('bootstrap surfaces pending sweep entries for triage', async () => {
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
const sessionFile = await writeTranscript([toolResultLine('Permission denied (publickey)')]);
await handler(makeCommandEvent('new', sessionFile));
const event = makeBootstrapEvent();
await handler(event);
const injected = event.context.bootstrapFiles[0];
assert.match(injected.content, /\*\*Pending triage:\*\* 1 auto-detected error entry/);
});
test('handler ignores unrelated events and malformed input', async () => {
await handler(null);
await handler({ type: 'gateway', action: 'startup', context: {} });
await handler({ type: 'command', action: 'stop', context: { workspaceDir } });
// A sweep with a missing transcript must not throw.
await fs.mkdir(path.join(workspaceDir, '.learnings'), { recursive: true });
await handler(makeCommandEvent('new', path.join(workspaceDir, 'sessions', 'missing.jsonl')));
await assert.rejects(fs.access(errorsFile()));
});
@@ -0,0 +1,465 @@
/**
* Self-Improvement Hook for OpenClaw
*
* OpenClaw has no per-tool-call hook event, so errors cannot be detected in
* real time after each command. This hook detects them at session end
* instead:
*
* - agent:bootstrap Injects the self-improvement reminder before
* workspace files are injected, including a note
* when auto-detected errors are awaiting triage.
* - command:new / :reset Session-end sweep: scans the transcript of the
* session that just ended for error patterns and
* appends a pending entry to
* <workspace>/.learnings/ERRORS.md.
*
* The sweep is opt-in: it only runs when <workspace>/.learnings/ exists.
* Excerpts are truncated and redacted before being written.
*/
import fs from 'node:fs/promises';
import path from 'node:path';
import type { HookHandler } from 'openclaw/hooks';
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
const REMINDER_PATH = REMINDER_NAME;
const REMINDER_HEADER = '## Self-Improvement Reminder';
const REMINDER_CONTENT = `${REMINDER_HEADER}
After completing tasks, evaluate whether any learnings should be captured.
Only log if this repo or workspace is using the self-improvement skill.
Before logging:
- Create only missing \`.learnings/\` files; never overwrite existing content
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
- Prefer short summaries or redacted excerpts over full command output
**Log when:**
- User corrects you → \`.learnings/LEARNINGS.md\`
- Command/operation fails → \`.learnings/ERRORS.md\`
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
- You find a better approach → \`.learnings/LEARNINGS.md\`
**Promote when pattern is proven:**
- Behavioral patterns → \`SOUL.md\`
- Workflow improvements → \`AGENTS.md\`
- Tool gotchas → \`TOOLS.md\`
Keep entries simple: date, title, what happened, and what to do differently.`;
// Error-detection patterns. Ordered specific → generic: the first matching pattern
// supplies the Pattern-Key stamped on swept entries, which is what makes
// auto-detected errors dedup-able and recurrence-countable (see the
// "Pattern-Key Taxonomy" section in SKILL.md).
const ERROR_PATTERN_KEYS: Array<[string, string]> = [
['command not found', 'shell.command-not-found'],
['No such file', 'fs.no-such-file'],
['Permission denied', 'fs.permission-denied'],
['ModuleNotFoundError', 'deps.module-not-found'],
['npm ERR!', 'deps.npm-error'],
['Traceback', 'runtime.python-exception'],
['SyntaxError', 'runtime.syntax-error'],
['TypeError', 'runtime.type-error'],
['Exception', 'runtime.exception'],
['fatal:', 'vcs.fatal-error'],
['exit code', 'shell.nonzero-exit'],
['non-zero', 'shell.nonzero-exit'],
['error:', 'runtime.error'],
['Error:', 'runtime.error'],
['ERROR:', 'runtime.error'],
['failed', 'runtime.failure'],
['FAILED', 'runtime.failure'],
];
interface SweepExcerpt {
excerpt: string;
patternKey: string;
}
const SWEEP_SOURCE = 'openclaw-error-sweep';
const MAX_EXCERPTS = 5;
const MAX_EXCERPT_LENGTH = 200;
const ERRORS_FILE_HEADER = '# Errors\n\nCommand failures and integration errors.\n\n---\n';
// Best-effort redaction of common secret shapes before anything is written.
const REDACTION_RULES: Array<[RegExp, string]> = [
[/\b(api[_-]?key|token|secret|password|passwd|authorization|credential)s?\b(\s*[=:]\s*)\S+/gi, '$1$2[REDACTED]'],
[/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [REDACTED]'],
[/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g, '[REDACTED]'],
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, '[REDACTED]'],
[/\bAKIA[0-9A-Z]{16}\b/g, '[REDACTED]'],
[/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/g, '[REDACTED-JWT]'],
[/\b[A-Za-z0-9_-]{40,}\b/g, '[REDACTED-BLOB]'],
];
type HookEvent = Parameters<HookHandler>[0];
function isObject(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object';
}
function isInjectedReminderFile(value: unknown): boolean {
if (!isObject(value) || value.path !== REMINDER_PATH) {
return false;
}
return (
value.virtual === true ||
(typeof value.content === 'string' && value.content.includes(REMINDER_HEADER))
);
}
function redactSensitiveText(text: string): string {
let result = text;
for (const [pattern, replacement] of REDACTION_RULES) {
result = result.replace(pattern, replacement);
}
return result;
}
function sanitizeExcerptLine(line: string): string {
let excerpt = redactSensitiveText(line.trim()).split('```').join("'''");
if (excerpt.length > MAX_EXCERPT_LENGTH) {
excerpt = `${excerpt.slice(0, MAX_EXCERPT_LENGTH)}`;
}
return excerpt;
}
function collectTextFragments(value: unknown, out: string[], depth = 0): void {
if (depth > 4 || out.length > 200) {
return;
}
if (typeof value === 'string') {
out.push(value);
return;
}
if (Array.isArray(value)) {
for (const item of value) {
collectTextFragments(item, out, depth + 1);
}
return;
}
if (isObject(value)) {
if (typeof value.text === 'string') {
out.push(value.text);
}
if ('content' in value) {
collectTextFragments(value.content, out, depth + 1);
}
}
}
function matchErrorPatternKey(line: string): string | null {
for (const [pattern, patternKey] of ERROR_PATTERN_KEYS) {
if (line.includes(pattern)) {
return patternKey;
}
}
return null;
}
async function scanTranscriptForErrors(sessionFilePath: string): Promise<SweepExcerpt[]> {
let raw: string;
try {
raw = await fs.readFile(sessionFilePath, 'utf-8');
} catch {
return [];
}
const excerpts: SweepExcerpt[] = [];
const seen = new Set<string>();
for (const jsonLine of raw.split('\n')) {
if (excerpts.length >= MAX_EXCERPTS) {
break;
}
const trimmed = jsonLine.trim();
if (!trimmed) {
continue;
}
let entry: unknown;
try {
entry = JSON.parse(trimmed);
} catch {
continue;
}
if (!isObject(entry) || !isObject(entry.message)) {
continue;
}
const fragments: string[] = [];
collectTextFragments(entry.message.content, fragments);
for (const fragment of fragments) {
for (const line of fragment.split('\n')) {
const patternKey = matchErrorPatternKey(line);
if (!patternKey) {
continue;
}
const excerpt = sanitizeExcerptLine(line);
if (!excerpt || seen.has(excerpt)) {
continue;
}
seen.add(excerpt);
excerpts.push({ excerpt, patternKey });
if (excerpts.length >= MAX_EXCERPTS) {
return excerpts;
}
}
}
}
return excerpts;
}
function resolveSessionFilePath(
context: Record<string, unknown>,
workspaceDir: string | undefined,
): string | undefined {
const sessionEntry = isObject(context.previousSessionEntry)
? context.previousSessionEntry
: isObject(context.sessionEntry)
? context.sessionEntry
: {};
const sessionFile = (sessionEntry as Record<string, unknown>).sessionFile;
if (typeof sessionFile === 'string' && sessionFile.trim()) {
return sessionFile;
}
const sessionIdValue = (sessionEntry as Record<string, unknown>).sessionId;
const sessionId = typeof sessionIdValue === 'string' ? sessionIdValue.trim() : '';
if (sessionId && workspaceDir) {
return path.join(workspaceDir, 'sessions', `${sessionId}.jsonl`);
}
return undefined;
}
function generateEntryId(timestamp: Date): string {
const yyyymmdd = timestamp.toISOString().slice(0, 10).replace(/-/g, '');
const suffix = Math.random().toString(36).slice(2, 5).toUpperCase().padEnd(3, '0');
return `ERR-${yyyymmdd}-${suffix}`;
}
function formatErrorEntry(params: {
excerpts: SweepExcerpt[];
sessionKey: string;
sessionFilePath: string;
action: string;
timestamp: Date;
}): string {
const { excerpts, sessionKey, sessionFilePath, action, timestamp } = params;
const plural = excerpts.length === 1 ? '' : 's';
const patternKeys = [...new Set(excerpts.map((item) => item.patternKey))];
return [
`## [${generateEntryId(timestamp)}] openclaw_session_sweep`,
'',
`**Logged**: ${timestamp.toISOString()}`,
'**Priority**: medium',
'**Status**: pending',
'**Area**: config',
'',
'### Summary',
`Session-end sweep detected ${excerpts.length} possible error${plural} in the previous OpenClaw session.`,
'',
'### Error',
'```',
...excerpts.map((item) => item.excerpt),
'```',
'',
'### Context',
`- Detected by the self-improvement hook on \`/${action}\` (OpenClaw has no per-tool-call hook, so errors are swept from the session transcript at session end)`,
`- Session key: ${sessionKey || 'unknown'}`,
`- Session transcript: ${sessionFilePath}`,
'- Excerpts are truncated and redacted; check the transcript for full context',
'',
'### Suggested Fix',
'Triage this entry: if the error was real and non-obvious, keep it and fill in the fix; otherwise mark it resolved or delete it. Before keeping it, grep for its Pattern-Key(s) and fold recurrences into the existing entry (bump Recurrence-Count) instead of duplicating.',
'',
'### Metadata',
`- Source: ${SWEEP_SOURCE}`,
'- Reproducible: unknown',
...patternKeys.map((patternKey) => `- Pattern-Key: ${patternKey}`),
'',
'---',
].join('\n');
}
async function handleSessionEndSweep(event: HookEvent): Promise<void> {
const context = event.context as Record<string, unknown>;
const workspaceDir =
typeof context.workspaceDir === 'string' && context.workspaceDir.trim()
? context.workspaceDir
: undefined;
if (!workspaceDir) {
return;
}
// Opt-in gate: only sweep when the workspace uses the self-improvement skill.
const learningsDir = path.join(workspaceDir, '.learnings');
try {
const stats = await fs.stat(learningsDir);
if (!stats.isDirectory()) {
return;
}
} catch {
return;
}
const sessionFilePath = resolveSessionFilePath(context, workspaceDir);
if (!sessionFilePath) {
return;
}
const excerpts = await scanTranscriptForErrors(sessionFilePath);
if (excerpts.length === 0) {
return;
}
const errorsFilePath = path.join(learningsDir, 'ERRORS.md');
let existing = '';
try {
existing = await fs.readFile(errorsFilePath, 'utf-8');
} catch {
// Missing file is fine; it is created below.
}
const freshExcerpts = excerpts.filter((item) => !existing.includes(item.excerpt));
if (freshExcerpts.length === 0) {
return;
}
const entry = formatErrorEntry({
excerpts: freshExcerpts,
sessionKey: typeof event.sessionKey === 'string' ? event.sessionKey : '',
sessionFilePath,
action: event.action,
timestamp: event.timestamp instanceof Date ? event.timestamp : new Date(),
});
if (!existing) {
try {
await fs.writeFile(errorsFilePath, `${ERRORS_FILE_HEADER}\n${entry}\n`, { flag: 'wx' });
return;
} catch (err) {
if (!isObject(err) || (err as { code?: string }).code !== 'EEXIST') {
throw err;
}
}
}
await fs.appendFile(errorsFilePath, `\n${entry}\n`);
}
async function countPendingSweepEntries(workspaceDir: string | undefined): Promise<number> {
if (!workspaceDir) {
return 0;
}
let content: string;
try {
content = await fs.readFile(path.join(workspaceDir, '.learnings', 'ERRORS.md'), 'utf-8');
} catch {
return 0;
}
return content
.split(/^## /m)
.slice(1)
.filter(
(section) =>
section.includes(`Source: ${SWEEP_SOURCE}`) && section.includes('**Status**: pending'),
).length;
}
async function handleBootstrap(event: HookEvent): Promise<void> {
// Skip sub-agent sessions to avoid bootstrap issues
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
const sessionKey = event.sessionKey || '';
if (sessionKey.includes(':subagent:')) {
return;
}
const context = event.context as Record<string, unknown>;
// Inject the reminder as a virtual bootstrap file
// Check that bootstrapFiles is an array before pushing
if (!Array.isArray(context.bootstrapFiles)) {
return;
}
const occupiedByOtherFile = context.bootstrapFiles.some(
(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),
);
if (occupiedByOtherFile) {
return;
}
let reminderContent = REMINDER_CONTENT;
const workspaceDir =
typeof context.workspaceDir === 'string' && context.workspaceDir.trim()
? context.workspaceDir
: undefined;
const pendingSweepCount = await countPendingSweepEntries(workspaceDir);
if (pendingSweepCount > 0) {
const plural = pendingSweepCount === 1 ? 'y' : 'ies';
reminderContent +=
`\n\n**Pending triage:** ${pendingSweepCount} auto-detected error entr${plural} ` +
`(Source: ${SWEEP_SOURCE}) in \`.learnings/ERRORS.md\` await review. ` +
'Confirm, resolve, or delete them when convenient.';
}
const cleanedBootstrapFiles = context.bootstrapFiles.filter(
(file, index, files) =>
!isInjectedReminderFile(file) ||
files.findIndex((candidate) => isInjectedReminderFile(candidate)) === index,
);
const reminderFile = {
name: REMINDER_NAME,
path: REMINDER_PATH,
content: reminderContent,
missing: false,
virtual: true,
};
const existingIndex = cleanedBootstrapFiles.findIndex((file) => isInjectedReminderFile(file));
if (existingIndex === -1) {
cleanedBootstrapFiles.push(reminderFile);
} else {
cleanedBootstrapFiles[existingIndex] = reminderFile;
}
context.bootstrapFiles = cleanedBootstrapFiles;
}
const handler: HookHandler = async (event) => {
// Safety checks for event structure
if (!event || typeof event !== 'object') {
return;
}
if (!event.context || typeof event.context !== 'object') {
return;
}
try {
if (event.type === 'agent' && event.action === 'bootstrap') {
await handleBootstrap(event);
return;
}
if (event.type === 'command' && (event.action === 'new' || event.action === 'reset')) {
await handleSessionEndSweep(event);
}
} catch (err) {
// Never break the gateway on hook failure.
if (process.env.SELF_IMPROVEMENT_HOOK_DEBUG) {
console.error('[self-improvement] hook failed:', err);
}
}
};
export default handler;
@@ -0,0 +1,374 @@
# Entry Examples
Concrete examples of well-formatted entries with all fields.
## Learning: Correction
```markdown
## [LRN-20250115-001] correction
**Logged**: 2025-01-15T10:30:00Z
**Priority**: high
**Status**: pending
**Area**: tests
### Summary
Incorrectly assumed pytest fixtures are scoped to function by default
### Details
When writing test fixtures, I assumed all fixtures were function-scoped.
User corrected that while function scope is the default, the codebase
convention uses module-scoped fixtures for database connections to
improve test performance.
### Suggested Action
When creating fixtures that involve expensive setup (DB, network),
check existing fixtures for scope patterns before defaulting to function scope.
### Metadata
- Source: user_feedback
- Related Files: tests/conftest.py
- Tags: pytest, testing, fixtures
---
```
## Learning: Knowledge Gap (Resolved)
```markdown
## [LRN-20250115-002] knowledge_gap
**Logged**: 2025-01-15T14:22:00Z
**Priority**: medium
**Status**: resolved
**Area**: config
### Summary
Project uses pnpm not npm for package management
### Details
Attempted to run `npm install` but project uses pnpm workspaces.
Lock file is `pnpm-lock.yaml`, not `package-lock.json`.
### Suggested Action
Check for `pnpm-lock.yaml` or `pnpm-workspace.yaml` before assuming npm.
Use `pnpm install` for this project.
### Metadata
- Source: error
- Related Files: pnpm-lock.yaml, pnpm-workspace.yaml
- Tags: package-manager, pnpm, setup
### Resolution
- **Resolved**: 2025-01-15T14:30:00Z
- **Commit/PR**: N/A - knowledge update
- **Notes**: Added to TOOLS.md for future reference
---
```
## Learning: Promoted to TOOLS.md
```markdown
## [LRN-20250115-003] best_practice
**Logged**: 2025-01-15T16:00:00Z
**Priority**: high
**Status**: promoted
**Promoted**: TOOLS.md
**Area**: backend
### Summary
API responses must include correlation ID from request headers
### Details
All API responses should echo back the X-Correlation-ID header from
the request. This is required for distributed tracing. Responses
without this header break the observability pipeline.
### Suggested Action
Always include correlation ID passthrough in API handlers.
### Metadata
- Source: user_feedback
- Related Files: src/middleware/correlation.ts
- Tags: api, observability, tracing
---
```
## Learning: Promoted to AGENTS.md
```markdown
## [LRN-20250116-001] best_practice
**Logged**: 2025-01-16T09:00:00Z
**Priority**: high
**Status**: promoted
**Promoted**: AGENTS.md
**Area**: backend
### Summary
Must regenerate API client after OpenAPI spec changes
### Details
When modifying API endpoints, the TypeScript client must be regenerated.
Forgetting this causes type mismatches that only appear at runtime.
The generate script also runs validation.
### Suggested Action
Add to agent workflow: after any API changes, run `pnpm run generate:api`.
### Metadata
- Source: error
- Related Files: openapi.yaml, src/client/api.ts
- Tags: api, codegen, typescript
---
```
## Error Entry
```markdown
## [ERR-20250115-A3F] docker_build
**Logged**: 2025-01-15T09:15:00Z
**Priority**: high
**Status**: pending
**Area**: infra
### Summary
Docker build fails on M1 Mac due to platform mismatch
### Error
```
error: failed to solve: python:3.11-slim: no match for platform linux/arm64
```
### Context
- Command: `docker build -t myapp .`
- Dockerfile uses `FROM python:3.11-slim`
- Running on Apple Silicon (M1/M2)
### Suggested Fix
Add platform flag: `docker build --platform linux/amd64 -t myapp .`
Or update Dockerfile: `FROM --platform=linux/amd64 python:3.11-slim`
### Metadata
- Reproducible: yes
- Related Files: Dockerfile
---
```
## Error Entry: Recurring Issue
```markdown
## [ERR-20250120-B2C] api_timeout
**Logged**: 2025-01-20T11:30:00Z
**Priority**: critical
**Status**: pending
**Area**: backend
### Summary
Third-party API timeout during request processing
### Error
```
TimeoutError: Request to api.example.com timed out after 30000ms
```
### Context
- Command: POST /api/process
- Timeout set to 30s
- Occurs during peak hours (lunch, evening)
### Suggested Fix
Implement retry with exponential backoff. Consider circuit breaker pattern.
### Metadata
- Reproducible: yes (during peak hours)
- Related Files: src/services/api-client.ts
- See Also: ERR-20250115-X1Y, ERR-20250118-Z3W
---
```
## Feature Request
```markdown
## [FEAT-20250115-001] export_to_csv
**Logged**: 2025-01-15T16:45:00Z
**Priority**: medium
**Status**: pending
**Area**: backend
### Requested Capability
Export analysis results to CSV format
### User Context
User runs weekly reports and needs to share results with non-technical
stakeholders in Excel. Currently copies output manually.
### Complexity Estimate
simple
### Suggested Implementation
Add `--output csv` flag to the analyze command. Use standard csv module.
Could extend existing `--output json` pattern.
### Metadata
- Frequency: recurring
- Related Features: analyze command, json output
---
```
## Feature Request: Resolved
```markdown
## [FEAT-20250110-002] dark_mode
**Logged**: 2025-01-10T14:00:00Z
**Priority**: low
**Status**: resolved
**Area**: frontend
### Requested Capability
Dark mode support for the dashboard
### User Context
User works late hours and finds the bright interface straining.
Several other users have mentioned this informally.
### Complexity Estimate
medium
### Suggested Implementation
Use CSS variables for colors. Add toggle in user settings.
Consider system preference detection.
### Metadata
- Frequency: recurring
- Related Features: user settings, theme system
### Resolution
- **Resolved**: 2025-01-18T16:00:00Z
- **Commit/PR**: #142
- **Notes**: Implemented with system preference detection and manual toggle
---
```
## Learning: Promoted to Skill
```markdown
## [LRN-20250118-001] best_practice
**Logged**: 2025-01-18T11:00:00Z
**Priority**: high
**Status**: promoted_to_skill
**Skill-Path**: skills/docker-m1-fixes
**Area**: infra
### Summary
Docker build fails on Apple Silicon due to platform mismatch
### Details
When building Docker images on M1/M2 Macs, the build fails because
the base image doesn't have an ARM64 variant. This is a common issue
that affects many developers.
### Suggested Action
Add `--platform linux/amd64` to docker build command, or use
`FROM --platform=linux/amd64` in Dockerfile.
### Metadata
- Source: error
- Related Files: Dockerfile
- Tags: docker, arm64, m1, apple-silicon
- See Also: ERR-20250115-A3F, ERR-20250117-B2D
---
```
## Extracted Skill Example
When the above learning is extracted as a skill, it becomes:
**File**: `skills/docker-m1-fixes/SKILL.md`
```markdown
---
name: docker-m1-fixes
description: "Fixes Docker build failures on Apple Silicon (M1/M2). Use when docker build fails with platform mismatch errors."
---
# Docker M1 Fixes
Solutions for Docker build issues on Apple Silicon Macs.
## Quick Reference
| Error | Fix |
|-------|-----|
| `no match for platform linux/arm64` | Add `--platform linux/amd64` to build |
| Image runs but crashes | Use emulation or find ARM-compatible base |
## The Problem
Many Docker base images don't have ARM64 variants. When building on
Apple Silicon (M1/M2/M3), Docker attempts to pull ARM64 images by
default, causing platform mismatch errors.
## Solutions
### Option 1: Build Flag (Recommended)
Add platform flag to your build command:
\`\`\`bash
docker build --platform linux/amd64 -t myapp .
\`\`\`
### Option 2: Dockerfile Modification
Specify platform in the FROM instruction:
\`\`\`dockerfile
FROM --platform=linux/amd64 python:3.11-slim
\`\`\`
### Option 3: Docker Compose
Add platform to your service:
\`\`\`yaml
services:
app:
platform: linux/amd64
build: .
\`\`\`
## Trade-offs
| Approach | Pros | Cons |
|----------|------|------|
| Build flag | No file changes | Must remember flag |
| Dockerfile | Explicit, versioned | Affects all builds |
| Compose | Convenient for dev | Requires compose |
## Performance Note
Running AMD64 images on ARM64 uses Rosetta 2 emulation. This works
for development but may be slower. For production, find ARM-native
alternatives when possible.
## Source
- Learning ID: LRN-20250118-001
- Category: best_practice
- Extraction Date: 2025-01-18
```
@@ -0,0 +1,333 @@
# OpenClaw Integration
Complete setup and usage guide for integrating the self-improvement skill with OpenClaw.
## Overview
OpenClaw uses workspace-based prompt injection combined with event-driven hooks. Context is injected from workspace files at session start, and hooks can trigger on lifecycle events.
## Workspace Structure
```
~/.openclaw/
├── workspace/ # Working directory
│ ├── AGENTS.md # Multi-agent coordination patterns
│ ├── SOUL.md # Behavioral guidelines and personality
│ ├── TOOLS.md # Tool capabilities and gotchas
│ ├── MEMORY.md # Long-term memory (main session only)
│ └── memory/ # Daily memory files
│ └── YYYY-MM-DD.md
├── skills/ # Installed skills
│ └── <skill-name>/
│ └── SKILL.md
└── hooks/ # Custom hooks
└── <hook-name>/
├── HOOK.md
└── handler.ts
```
## Quick Setup
### 1. Install the Skill
```bash
openclaw skills install @pskoett/self-improving-agent
```
Or with the ClawHub CLI (`npm i -g clawhub`) — note this installs into
`./skills` under the current working directory, not the workspace:
```bash
clawhub install @pskoett/self-improving-agent
```
Or copy manually — the skill package is the `self-improving-agent/`
subfolder of the repo, not the repo root:
```bash
git clone https://github.com/pskoett/self-improving-agent.git /tmp/self-improving-agent-repo
cp -r /tmp/self-improving-agent-repo/self-improving-agent ~/.openclaw/skills/self-improving-agent
```
### 2. Install the Hook (Optional)
Copy the hook to OpenClaw's hooks directory:
```bash
cp -r ~/.openclaw/skills/self-improving-agent/hooks/openclaw ~/.openclaw/hooks/self-improvement
```
Enable the hook:
```bash
openclaw hooks enable self-improvement
```
The hook does two things:
- **`agent:bootstrap`** — injects the self-improvement reminder into session
context (and flags auto-detected errors awaiting triage)
- **`command:new` / `command:reset`** — sweeps the transcript of the session
that just ended for error patterns and appends pending entries to
`<workspace>/.learnings/ERRORS.md` (only if `.learnings/` exists — see
[Error Detection](#error-detection))
### 3. Create Learning Files
Create the `.learnings/` directory in your workspace:
```bash
mkdir -p ~/.openclaw/workspace/.learnings
```
Or in the skill directory:
```bash
mkdir -p ~/.openclaw/skills/self-improving-agent/.learnings
```
## Injected Prompt Files
### AGENTS.md
Purpose: Multi-agent workflows and delegation patterns.
```markdown
# Agent Coordination
## Delegation Rules
- Use explore agent for open-ended codebase questions
- Spawn sub-agents for long-running tasks
- Use sessions_send for cross-session communication
## Session Handoff
When delegating to another session:
1. Provide full context in the handoff message
2. Include relevant file paths
3. Specify expected output format
```
### SOUL.md
Purpose: Behavioral guidelines and communication style.
```markdown
# Behavioral Guidelines
## Communication Style
- Be direct and concise
- Avoid unnecessary caveats and disclaimers
- Use technical language appropriate to context
## Error Handling
- Admit mistakes promptly
- Provide corrected information immediately
- Log significant errors to learnings
```
### TOOLS.md
Purpose: Tool capabilities, integration gotchas, local configuration.
```markdown
# Tool Knowledge
## Self-Improvement Skill
Log learnings to `.learnings/` for continuous improvement.
## Local Tools
- Document tool-specific gotchas here
- Note authentication requirements
- Track integration quirks
```
## Learning Workflow
### Capturing Learnings
1. **In-session**: Log to `.learnings/` as usual
2. **Cross-session**: Promote to workspace files
### Promotion Decision Tree
```
Is the learning project-specific?
├── Yes → Keep in .learnings/
└── No → Is it behavioral/style-related?
├── Yes → Promote to SOUL.md
└── No → Is it tool-related?
├── Yes → Promote to TOOLS.md
└── No → Promote to AGENTS.md (workflow)
```
### Promotion Format Examples
**From learning:**
> Git push to GitHub fails without auth configured - triggers desktop prompt
**To TOOLS.md:**
```markdown
## Git
- Don't push without confirming auth is configured
- Use `gh auth status` to check GitHub CLI auth
```
## Inter-Agent Communication
OpenClaw provides tools for cross-session communication:
Use these only when cross-session sharing is explicitly needed and the environment is trusted. Prefer short sanitized summaries over raw transcripts, command output, or secret-bearing content.
### sessions_list
View active and recent sessions:
```
sessions_list(activeMinutes=30, messageLimit=3)
```
### sessions_history
Read transcript from another session:
```
sessions_history(sessionKey="session-id", limit=50)
```
Only read another session's transcript when the user explicitly wants shared context or continuation across sessions.
### sessions_send
Send message to another session:
```
sessions_send(sessionKey="session-id", message="Learning: API requires X-Custom-Header")
```
Prefer sending a concise learning summary plus relevant paths rather than forwarding raw transcript content.
### sessions_spawn
Spawn a background sub-agent:
```
sessions_spawn(task="Research X and report back", label="research")
```
## Available Hook Events
| Event | When It Fires |
|-------|---------------|
| `agent:bootstrap` | Before workspace files inject |
| `command:new` | When `/new` command issued |
| `command:reset` | When `/reset` command issued |
| `command:stop` | When `/stop` command issued |
| `gateway:startup` | When gateway starts |
| `gateway:shutdown` | When gateway shuts down |
| `message:received` / `message:sent` | Around message delivery |
| `session:compact:before` / `:after` | Around session compaction |
**Important:** OpenClaw has **no per-tool-call event** — nothing fires after
each individual tool call, so real-time per-command error detection is not
possible. Error detection is done at session end instead (see below).
## Error Detection
The skill's hook (`hooks/openclaw/`) implements a **session-end error
sweep**:
1. When `/new` or `/reset` ends a session, the hook resolves the ended
session's transcript from `context.previousSessionEntry` (falling back to
`<workspace>/sessions/<sessionId>.jsonl`) — the same source OpenClaw's
bundled `session-memory` hook uses.
2. The transcript is scanned against a fixed error-pattern list (`Error:`,
`command not found`, `Traceback`, `npm ERR!`, `Permission denied`, …).
3. Matches are appended to `<workspace>/.learnings/ERRORS.md` as a `pending`
entry with `Source: openclaw-error-sweep`, containing at most 5 short
excerpts (truncated to 200 chars, common secret shapes redacted,
duplicates skipped). Each entry is stamped with deterministic
`Pattern-Key` values derived from the matched pattern (for example
`ModuleNotFoundError``deps.module-not-found`), so recurrences can be
counted by key during triage — see the Pattern-Key Taxonomy in `SKILL.md`.
4. At the next `agent:bootstrap`, the injected reminder includes a
**pending triage** note so the agent reviews the auto-detected entries —
confirming real errors, filling in fixes, or deleting noise.
### Enabling / Disabling the Sweep
The sweep is opt-in and gated on the `.learnings/` directory:
```bash
# Enable
mkdir -p ~/.openclaw/workspace/.learnings
# Disable (reminder injection keeps working)
rm -r ~/.openclaw/workspace/.learnings
```
### Sweep Limitations
- Sessions that are never ended with `/new` or `/reset` are not swept.
- Detection happens at session end, not immediately after the failing
command — there is no per-tool-call event to hook.
- Pattern matching is heuristic; it can flag false positives such as prose
containing the word "failed" — that's what the triage step is for.
- Excerpts are redacted with best-effort rules; treat `.learnings/` as
potentially sensitive and keep it out of version control by default.
## Detection Triggers
### Standard Triggers
- User corrections ("No, that's wrong...")
- Command failures (non-zero exit codes)
- API errors
- Knowledge gaps
### OpenClaw-Specific Triggers
| Trigger | Action |
|---------|--------|
| Tool call error | Log to TOOLS.md with tool name |
| Session handoff confusion | Log to AGENTS.md with delegation pattern |
| Model behavior surprise | Log to SOUL.md with expected vs actual |
| Skill issue | Log to .learnings/ or report upstream |
## Verification
Check hook is registered:
```bash
openclaw hooks list
```
Check skill is loaded:
```bash
openclaw status
```
## Troubleshooting
### Hook not firing
1. Ensure hooks enabled in config
2. Restart gateway after config changes
3. Check gateway logs for errors
### Learnings not persisting
1. Verify `.learnings/` directory exists
2. Check file permissions
3. Ensure workspace path is configured correctly
### Error sweep not writing entries
1. Verify `<workspace>/.learnings/` exists (the sweep is opt-in and skips
silently without it)
2. End the session with `/new` or `/reset` — the sweep only runs on those
commands
3. Confirm a transcript exists in `<workspace>/sessions/`
4. Run the gateway with `SELF_IMPROVEMENT_HOOK_DEBUG=1` to surface hook errors
5. Remember excerpts already present in `ERRORS.md` are skipped (dedupe)
### Skill not loading
1. Check skill is in skills directory
2. Verify SKILL.md has correct frontmatter
3. Run `openclaw status` to see loaded skills
@@ -0,0 +1,55 @@
# Uninstall Guide
How to disable or fully remove the self-improvement skill. **Disabling** turns
off automatic behavior but keeps your data; **removing** deletes the skill's
files. They are different operations — do the one you mean.
> **Your learnings are data, not skill machinery.** `.learnings/` contains the
> errors, corrections, and insights the skill captured for you. Review or
> archive it before deleting anything. Content the skill *promoted* into
> `SOUL.md`, `TOOLS.md`, or `AGENTS.md` is part of those files now — removing
> the skill does not (and should not) automatically remove it.
## Disable only
```bash
# Stop the hook (reminder injection + session-end error sweep)
openclaw hooks disable self-improvement
```
To keep the bootstrap reminder but disable only the error sweep, remove the
learnings directory instead (archive it first if it has entries):
```bash
rm -r ~/.openclaw/workspace/.learnings
```
Restart the gateway after hook changes.
## Remove completely
```bash
# 1. Disable and remove the hook
openclaw hooks disable self-improvement
rm -r ~/.openclaw/hooks/self-improvement
# 2. Remove the skill
rm -r ~/.openclaw/skills/self-improving-agent
# 3. Optional — remove captured learnings (REVIEW FIRST, this is your data)
rm -r ~/.openclaw/workspace/.learnings
```
Then restart the gateway and verify with `openclaw hooks list` and
`openclaw status`.
Manually review `SOUL.md`, `TOOLS.md`, and `AGENTS.md` in
`~/.openclaw/workspace/` for sections promoted by this skill and delete the
ones you no longer want. If the skill logged `.learnings/` directories into
project repos you worked in, review and remove those separately.
## Verification
- `openclaw hooks list` no longer shows `self-improvement`
- New sessions no longer contain `SELF_IMPROVEMENT_REMINDER.md`
- No stray `.learnings/` directories remain that you didn't choose to keep
@@ -0,0 +1,221 @@
#!/bin/bash
# Skill Extraction Helper
# Creates a new skill from a learning entry
# Usage: ./extract-skill.sh <skill-name> [--dry-run]
set -e
# Configuration
SKILLS_DIR="./skills"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
usage() {
cat << EOF
Usage: $(basename "$0") <skill-name> [options]
Create a new skill from a learning entry.
Arguments:
skill-name Name of the skill (lowercase, hyphens for spaces)
Options:
--dry-run Show what would be created without creating files
--output-dir Relative output directory under current path (default: ./skills)
-h, --help Show this help message
Examples:
$(basename "$0") docker-m1-fixes
$(basename "$0") api-timeout-patterns --dry-run
$(basename "$0") pnpm-setup --output-dir ./skills/custom
The skill will be created in: \$SKILLS_DIR/<skill-name>/
EOF
}
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
# Parse arguments
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 requires a relative path argument"
usage
exit 1
fi
SKILLS_DIR="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
if [ -z "$SKILL_NAME" ]; then
SKILL_NAME="$1"
else
log_error "Unexpected argument: $1"
usage
exit 1
fi
shift
;;
esac
done
# Validate skill name
if [ -z "$SKILL_NAME" ]; then
log_error "Skill name is required"
usage
exit 1
fi
# Validate skill name format (lowercase, hyphens, no spaces)
if ! [[ "$SKILL_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
log_error "Invalid skill name format. Use lowercase letters, numbers, and hyphens only."
log_error "Examples: 'docker-fixes', 'api-patterns', 'pnpm-setup'"
exit 1
fi
# Validate output path to avoid writes outside current workspace.
if [[ "$SKILLS_DIR" = /* ]]; then
log_error "Output directory must be a relative path under the current directory."
exit 1
fi
if [[ "$SKILLS_DIR" =~ (^|/)\.\.(/|$) ]]; then
log_error "Output directory cannot include '..' path segments."
exit 1
fi
SKILLS_DIR="${SKILLS_DIR#./}"
SKILLS_DIR="./$SKILLS_DIR"
SKILL_PATH="$SKILLS_DIR/$SKILL_NAME"
# Check if skill already exists
if [ -d "$SKILL_PATH" ] && [ "$DRY_RUN" = false ]; then
log_error "Skill already exists: $SKILL_PATH"
log_error "Use a different name or remove the existing skill first."
exit 1
fi
# Dry run output
if [ "$DRY_RUN" = true ]; then
log_info "Dry run - would create:"
echo " $SKILL_PATH/"
echo " $SKILL_PATH/SKILL.md"
echo ""
echo "Template content would be:"
echo "---"
cat << TEMPLATE
name: $SKILL_NAME
description: "[TODO: Add a concise description of what this skill does and when to use it]"
---
# $(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: Brief introduction explaining the skill's purpose]
## Quick Reference
| Situation | Action |
|-----------|--------|
| [Trigger condition] | [What to do] |
## Usage
[TODO: Detailed usage instructions]
## Examples
[TODO: Add concrete examples]
## Source Learning
This skill was extracted from a learning entry.
- Learning ID: [TODO: Add original learning ID]
- Original File: .learnings/LEARNINGS.md
TEMPLATE
echo "---"
exit 0
fi
# Create skill directory structure
log_info "Creating skill: $SKILL_NAME"
mkdir -p "$SKILL_PATH"
# Create SKILL.md from template
cat > "$SKILL_PATH/SKILL.md" << TEMPLATE
---
name: $SKILL_NAME
description: "[TODO: Add a concise description of what this skill does and when to use it]"
---
# $(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: Brief introduction explaining the skill's purpose]
## Quick Reference
| Situation | Action |
|-----------|--------|
| [Trigger condition] | [What to do] |
## Usage
[TODO: Detailed usage instructions]
## Examples
[TODO: Add concrete examples]
## Source Learning
This skill was extracted from a learning entry.
- Learning ID: [TODO: Add original learning ID]
- Original File: .learnings/LEARNINGS.md
TEMPLATE
log_info "Created: $SKILL_PATH/SKILL.md"
# Suggest next steps
echo ""
log_info "Skill scaffold created successfully!"
echo ""
echo "Next steps:"
echo " 1. Edit $SKILL_PATH/SKILL.md"
echo " 2. Fill in the TODO sections with content from your learning"
echo " 3. Add references/ folder if you have detailed documentation"
echo " 4. Add scripts/ folder if you have executable code"
echo " 5. Update the original learning entry with:"
echo " **Status**: promoted_to_skill"
echo " **Skill-Path**: skills/$SKILL_NAME"
@@ -0,0 +1,60 @@
## Description:
Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects Claude ('No, that's wrong...', 'Actually...'), (3) User requests a capability that doesn't exist, (4) An external API or tool fails, (5) Claude realizes its knowledge is outdated or incorrect, (6) A better approach is discovered for a recurring task. Also review learnings before major tasks.
This skill is ready for commercial/non-commercial use.
## Publisher:
[pskoett](https://clawhub.ai/user/pskoett)
### License/Terms of Use:
MIT-0
## Use Case:
Developers and agent users use this skill to capture corrections, command failures, feature requests, outdated knowledge, and reusable best practices in workspace learning files. OpenClaw users can also opt into a hook that reminds agents to review learnings and sweeps ended sessions for possible errors.
### Deployment Geography for Use:
Global
## Known Risks and Mitigations:
Risk: The optional hook may read ended session transcripts and persist excerpts in .learnings, with only best-effort redaction.
Mitigation: Enable the hook only in trusted workspaces, keep .learnings out of version control, and review captured entries before sharing or promotion.
Risk: Automatically detected errors can include false positives or incomplete context.
Mitigation: Triage pending sweep entries before keeping them, promoting them, or converting them into workspace guidance.
Risk: Uninstall or cleanup commands can delete .learnings, which is user data.
Mitigation: Review or archive .learnings before running deletion commands.
## Reference(s):
- [ClawHub skill page](https://clawhub.ai/pskoett/skills/self-improving-agent)
- [OpenClaw integration guide](references/openclaw-integration.md)
- [Uninstall guide](references/uninstall.md)
- [Examples](references/examples.md)
## Skill Output:
**Output Type(s):** [Markdown, Files, Shell commands, Configuration, Guidance]
**Output Format:** [Markdown entries with inline shell commands and configuration snippets]
**Output Parameters:** [1D]
**Other Properties Related to Output:** [Creates or appends to .learnings files; the optional hook writes truncated, redacted error excerpts for later triage.]
## Skill Version(s):
4.0.2 (source: frontmatter, changelog, server evidence)
## Ethical Considerations:
Users should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.
@@ -0,0 +1,124 @@
# 改进周报 (2026-W38)
**周期:** 2026-09-14 ~ 2026-09-16(本周至今,周三)
**生成方式:** self-improving-agent 技能
**数据来源:** `.learnings/` 日志、会话 transcript、workspace 配置文件实测
---
## 一、本周概览
| 指标 | 数量 |
|------|------|
| 新增学习条目 (LRN) | 5 |
| 新增错误条目 (ERR) | 1 |
| 新增需求条目 (FEAT) | 0 |
| 已解决 | 3 |
| 已升级到配置文档 | 1 条(涉及 TOOLS.md、MEMORY.md 两个文件) |
| 待处理 | 2 |
> 说明:`.learnings/` 本周**首次启用**(此前不存在),因此本轮为回溯补录——
> 把此前散落在会话中的教训固化下来,避免下次再踩。
---
## 二、关键发现
### 🔴 1. 陈旧路径导致「技能找不到」假象
**类型:** correction **状态:** ✅ 已修复
`AGENTS.md` 指向 `skills/weekly-report-g5/SKILL.md`,该路径**不存在**(实测返回 `ENOENT`)。
在用技能实际是 `skills/weekly-report/SKILL.md``weekly-report-g5` 只存在于 Skill Workshop 提案目录。
- **影响:** AGENTS.md 是每次会话必读文件,错误路径会浪费工具调用,更可能让后续会话误判「技能不存在」而重复造轮子。
- **动作:** 已修正路径,并补登自我改进技能条目。
### 🟠 2. 文档描述与实际行为不符
**类型:** correction **状态:** ✅ 已升级
`TOOLS.md` / `MEMORY.md` 曾称 `send_weekly_report.py` 会「同步元数据到 db」。逐条核对脚本行为后确认:该脚本**从不写 SQLite**,只有两个落盘动作——
| 动作 | 目标 |
|------|------|
| `archive_report()` | Markdown → `weekly-reports/YYYY/` |
| `sync_to_mysql()` | MySQL `resume` 库 |
- **风险:** 后续会话会误以为 SQLite 已自动入库,从而漏跑 `sync_reports_db.py`,造成本地索引与 Markdown **静默不一致**
- **动作:** 已修正两份文档表述。
### 🟡 3. shell 日期推导会「静默算错」
**类型:** knowledge_gap **状态:** 待处理
2026-09-16(周三)实测:
| 表达式 | 结果 | 判定 |
|--------|------|------|
| `date -d 'monday this week'` | `2026-09-21` | ❌ 下周一 |
| `date -d 'last monday'` | `2026-09-14` | ✅ 本周一 |
- **风险:** 不报错、直接算出一周之后的区间,属「静默错误」,比崩溃更难发现。
- **对策:** 周报日期范围一律以**用户提供的任务日期**为准,不用表达式推导。
### 🟡 4. 【已更正】我曾把「非待处理」误判为「待处理」
**类型:** correction **状态:** ✅ 已更正
⚠️ **本条是本次复盘中最值得记的教训:我自己犯了一个「读错来源」的错。**
首轮我报告「Skill Workshop 有两个 pending 的 `weekly-report-g5` 提案,虚高待办数」。该结论**不成立**——我读的是提案目录里 `PROPOSAL.md` 头部自带的 `status: proposal`,那只是**静态正文标记**,不代表生命周期状态。用权威入口核实后:
| 提案 | 真实状态 |
|------|----------|
| `weekly-report-g5-20260626…` (v1) | `stale`(基线变动,无法再应用) |
| `weekly-report-g5-20260821…` (v2) | `applied`(内容确已并入在用技能) |
| `list --status pending` | **No skill proposals matched** |
- **正确做法:** 判断提案状态**只用 `skill_workshop list`**。同理,任何「状态类」结论都应以对应工具/接口为准,而非文件表面内容。
- **动作:** 已更正本条与 `.learnings` 记录。经复核**无需清理提案**——本就不存在 pending,删除反而会丢掉 v2 已应用的历史记录。
- **副产品:** 顺带确认这两个目录现仅存 `PROPOSAL.md`,原 `proposal.json`/`rollback.json` 已不在(2026-08-28 时还在),说明其间有 skill-collection-review 自动化做过迁移清理。
> 🎯 **这正是本技能的价值所在**:如果不做这轮复盘,这个误判会一直留在记录里,下次还可能照着错误结论去删东西。
### 🟢 5. 中文检索召回不可靠
**类型:** knowledge_gap **状态:** 待处理
`sessions_search` 两次中文错误关键词查询均返回空,但同一会话 transcript 中确实存在对应内容。
- **风险:** 会给出「什么都没发生」的假信号,让复盘遗漏真实错误。
- **对策:** 错误复盘以 `.learnings/` 为主数据源,`sessions_search` 空结果不当作「无此事」。
---
## 三、本周已修复的错误
| 编号 | 问题 | 结果 |
|------|------|------|
| ERR-20260916-001 | `/tmp` 下运行脚本报 `ModuleNotFoundError` | ✅ 已解决(用 `PYTHONPATH` 指定模块路径) |
**根因:** Python 只把**脚本所在目录**加入 `sys.path`cwd 不在其中。
**教训:** 临时脚本要么写在 workspace 内,要么运行前显式设 `PYTHONPATH`
---
## 四、下周改进重点
1. **已完事项复核** —— 本周两项均已当场完成:AGENTS.md 技能路径与格式残留已修正;提案核实后确认**无需清理**(不存在 pending)。
2. **沉淀状态判断约定** —— 把「状态类结论只以工具/接口为准」写入规范:提案查 `skill_workshop list`、周报日期以用户提供为准。
3. **沉淀运行约定** —— 把「PYTHONPATH / 脚本位置」、`date -d 'last monday'` 写进周报技能,避免下次再撞。
4. **持续记录** —— 本周起 `.learnings/` 已常态化启用,后续教训即时入账,不再依赖回溯补录。
---
## 五、方法论沉淀
本轮确立的复盘原则:
- **只用可核实证据** —— 每条结论都有实测输出或文件依据,不写推测性「体会」。
- **状态只看权威入口** —— 文件表面的 `status` 字段不代表真实生命周期状态(本轮实际踩坑)。
- **区分「报错」与「静默错误」** —— 静默错误优先级更高,因为它不会被发现。
- **能修就当场修** —— 陈旧路径、文档不符等确定性缺陷直接修复,不留到「下次」。
- **结论需自检** —— 把误判写进记录后再核实,是有效的纠错方式(本轮靠它推翻了自身结论)。
---
*归档时间:2026-09-16*
*日志位置:`.learnings/LEARNINGS.md`、`.learnings/ERRORS.md`*