diff --git a/interfaces/admin/static/chat.html b/interfaces/admin/static/chat.html
index a6c2d4be..e682796c 100644
--- a/interfaces/admin/static/chat.html
+++ b/interfaces/admin/static/chat.html
@@ -574,7 +574,7 @@
diff --git a/interfaces/admin/static/chat.js b/interfaces/admin/static/chat.js
index 8808b99f..4d8d75b1 100644
--- a/interfaces/admin/static/chat.js
+++ b/interfaces/admin/static/chat.js
@@ -77,6 +77,7 @@ const I18N = {
"theme.catppuccin": "Catppuccin Mocha",
"theme.light": "浅色",
"lang.switch": "English",
+ "chat.langSwitchedWhileStreaming": "语言已切换。当前会话仍在输出,刷新页面可更新全部界面文案。",
"chat.imageViewerClose": "关闭",
"chat.imageViewerHint": "点击查看大图,空白处或 Esc 关闭",
"chat.imageViewerDownload": "下载图片",
@@ -256,6 +257,7 @@ const I18N = {
"theme.catppuccin": "Catppuccin Mocha",
"theme.light": "Light",
"lang.switch": "中文",
+ "chat.langSwitchedWhileStreaming": "Language updated. Session is still streaming; refresh the page to reload all labels.",
"chat.imageViewerClose": "Close",
"chat.imageViewerHint": "Click image to enlarge; click outside or Esc to close",
"chat.imageViewerDownload": "Download image",
@@ -873,8 +875,8 @@ async function openDispatchLabelsEditor(statusEl) {
? JSON.stringify(cfg.overrides, null, 2)
: "";
const backdrop = el("div", {
- style:
- "position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:9999;display:flex;align-items:center;justify-content:center;padding:16px;",
+ class: "chat-confirm-backdrop",
+ style: "z-index:9999;",
});
const card = el("div", {
class: "card",
@@ -1180,6 +1182,57 @@ function closeChatImageLightbox() {
document.body.style.overflow = _chatLightboxPrevOverflow || "";
}
+function dismissChatMenus() {
+ document.querySelectorAll(".chat-sess-menu-pop, .chat-menu-scrim").forEach((n) => {
+ try {
+ n.remove();
+ } catch (_) {}
+ });
+}
+
+function isChatStreaming() {
+ return !!document.querySelector(".chat-composer-shell--busy");
+}
+
+/** Remove full-screen layers on ``document.body`` that survive ``#app`` remounts (boot/lang/popstate). */
+function clearChatPageBlockers() {
+ closeChatImageLightbox();
+ dismissChatMenus();
+ document.body.style.overflow = "";
+ document.querySelectorAll(".chat-confirm-backdrop").forEach((n) => {
+ try {
+ n.remove();
+ } catch (_) {}
+ });
+}
+
+function attachChatMenuDismiss(menu) {
+ const scrim = el("div", {
+ class: "chat-menu-scrim",
+ style: "position:fixed;inset:0;z-index:250;background:transparent;",
+ });
+ const closeAll = () => {
+ dismissChatMenus();
+ document.removeEventListener("click", onDocClick, true);
+ document.removeEventListener("keydown", onKey, true);
+ };
+ const onDocClick = (e) => {
+ if (menu.contains(e.target)) return;
+ closeAll();
+ };
+ const onKey = (e) => {
+ if (e.key === "Escape") closeAll();
+ };
+ scrim.addEventListener("click", closeAll);
+ document.body.appendChild(scrim);
+ document.body.appendChild(menu);
+ setTimeout(() => {
+ document.addEventListener("click", onDocClick, true);
+ document.addEventListener("keydown", onKey, true);
+ }, 0);
+ return closeAll;
+}
+
function openChatImageLightbox(src, alt) {
if (!src || !String(src).trim()) return;
closeChatImageLightbox();
@@ -2504,6 +2557,7 @@ async function appendMessageRow(messagesEl, m, options = {}) {
}
function mount(node) {
+ clearChatPageBlockers();
const c = document.getElementById("app");
c.innerHTML = "";
c.appendChild(node);
@@ -2566,7 +2620,7 @@ function syncAuthUserLabel() {
title: t("chat.sessionMenu"),
onclick: (ev) => {
ev.stopPropagation();
- document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove());
+ clearChatPageBlockers();
const items = [
el("button", {
type: "button",
@@ -2695,10 +2749,12 @@ function syncAuthUserLabel() {
text: t("auth.logout"),
}),
);
- const menu = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;min-width:220px;" }, items);
+ const menu = el("div", {
+ class: "chat-sess-menu-pop",
+ style: "position:fixed;min-width:220px;z-index:300;",
+ }, items);
const rect = moreBtn.getBoundingClientRect();
- menu.style.position = "fixed";
- document.body.appendChild(menu);
+ attachChatMenuDismiss(menu);
const mrect = menu.getBoundingClientRect();
const pad = 8;
let left = rect.left;
@@ -2710,13 +2766,6 @@ function syncAuthUserLabel() {
top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height));
menu.style.left = `${left}px`;
menu.style.top = `${top}px`;
- const close = (e) => {
- if (!menu.contains(e.target)) {
- menu.remove();
- document.removeEventListener("click", close);
- }
- };
- setTimeout(() => document.addEventListener("click", close), 0);
},
});
const row = el("div", { class: "chat-user-row" }, [nameBtn, moreBtn]);
@@ -3684,12 +3733,12 @@ async function renderChatUi() {
title: t("chat.sessionMenu"),
onclick: (ev) => {
ev.stopPropagation();
- document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove());
+ clearChatPageBlockers();
const menu = openSessionMenu(sid, title);
- const rect = more.getBoundingClientRect();
menu.style.position = "fixed";
- document.body.appendChild(menu);
- // Clamp into viewport; flip above if near bottom.
+ menu.style.zIndex = "300";
+ const rect = more.getBoundingClientRect();
+ attachChatMenuDismiss(menu);
const mrect = menu.getBoundingClientRect();
const pad = 8;
let left = rect.left;
@@ -3701,13 +3750,6 @@ async function renderChatUi() {
top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height));
menu.style.left = `${left}px`;
menu.style.top = `${top}px`;
- const close = (e) => {
- if (!menu.contains(e.target)) {
- menu.remove();
- document.removeEventListener("click", close);
- }
- };
- setTimeout(() => document.addEventListener("click", close), 0);
},
});
row.appendChild(btn);
@@ -5374,6 +5416,7 @@ async function syncLangFromServer() {
}
async function boot() {
+ clearChatPageBlockers();
applyI18nStatic();
if (forceReloginRequested()) {
clearAuthAndReloginFlagFromUrl();
@@ -5385,10 +5428,6 @@ async function boot() {
}
const tok = String(localStorage.getItem(AUTH_TOKEN_KEY) || "").trim();
if (!authSession || !tok) {
- // Ensure stale overlays from previous chat UI never block login inputs.
- closeChatImageLightbox();
- document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove());
- document.body.style.overflow = "";
localStorage.removeItem(AUTH_TOKEN_KEY);
localStorage.removeItem(AUTH_SESSION_KEY);
authSession = null;
@@ -5446,7 +5485,14 @@ document.body.addEventListener("click", async (e) => {
try {
await apiPost("/admin/api/chat/settings/ui-lang", { lang: currentLang });
} catch (_) {}
- await boot();
+ clearChatPageBlockers();
+ if (isChatStreaming()) {
+ applyI18nStatic();
+ syncAuthUserLabel();
+ showToast(t("chat.langSwitchedWhileStreaming"), { kind: "info", ttlMs: 6500 });
+ } else {
+ await boot();
+ }
return;
}
}
@@ -5457,6 +5503,14 @@ window.addEventListener("popstate", () => {
if (authSession) boot().catch(() => {});
});
+document.addEventListener("keydown", (ev) => {
+ if (ev.key !== "Escape" || ev.defaultPrevented) return;
+ if (!document.querySelector(".chat-confirm-backdrop, .chat-sess-menu-pop, .chat-img-lightbox, .chat-menu-scrim")) {
+ return;
+ }
+ clearChatPageBlockers();
+});
+
boot().catch((err) => {
mount(
el("div", { class: "chat-app--login" }, [
diff --git a/runtime/operations/mcp_env.py b/runtime/operations/mcp_env.py
index 4ea8face..6fb9936a 100644
--- a/runtime/operations/mcp_env.py
+++ b/runtime/operations/mcp_env.py
@@ -85,11 +85,24 @@ def _mcp_local_env_paths_in_load_order() -> list[Path]:
]
+def _apply_trilium_env_aliases(vals: dict[str, str]) -> None:
+ """Map legacy TRILIUM_URL / TRILIUM_TOKEN to names expected by triliumnext-mcp."""
+ if not str(vals.get("TRILIUM_API_URL") or "").strip():
+ legacy = str(vals.get("TRILIUM_URL") or "").strip()
+ if legacy:
+ vals["TRILIUM_API_URL"] = legacy
+ if not str(vals.get("TRILIUM_API_TOKEN") or "").strip():
+ legacy = str(vals.get("TRILIUM_TOKEN") or "").strip()
+ if legacy:
+ vals["TRILIUM_API_TOKEN"] = legacy
+
+
def mcp_local_env_merged() -> dict[str, str]:
out: dict[str, str] = {}
for p in _mcp_local_env_paths_in_load_order():
if p.is_file():
out.update(_parse_env_file(p))
+ _apply_trilium_env_aliases(out)
return out
@@ -102,7 +115,8 @@ def gateway_mcp_env_extras() -> dict[str, str]:
has_aia = str(os.getenv("AIA_MCP_ENV_ALLOWLIST") or "").strip()
if not has_aia:
extra["AIA_MCP_ENV_ALLOWLIST"] = _DEFAULT_ALLOWLIST
- file_vals = mcp_local_env_merged()
+ file_vals = dict(mcp_local_env_merged())
+ _apply_trilium_env_aliases(file_vals)
for k, v in file_vals.items():
if not v.strip():
continue
diff --git a/skills/_workspace/generalist/knowledge-base-manager/SKILL.md b/skills/_workspace/generalist/knowledge-base-manager/SKILL.md
new file mode 100644
index 00000000..e344a90b
--- /dev/null
+++ b/skills/_workspace/generalist/knowledge-base-manager/SKILL.md
@@ -0,0 +1,450 @@
+---
+name: knowledge-base-manager
+description: 智能知识库管理助手,自动将新信息归类到正确的 PARA 分类(Projects/Areas/Resources/Archives),确保笔记结构规范化。带严格目录限制和完整 ID 映射。
+user-invocable: true
+disable-model-invocation: false
+metadata: {"oclaw": {}}
+---
+
+# knowledge-base-manager
+
+**Description**: 智能知识库管理助手,自动将新信息归类到正确的 PARA 分类(Projects/Areas/Resources/Archives),确保笔记结构规范化。
+
+**Version**: 1.1 (带目录限制)
+**Author**: Oliver
+**Created**: 2026-04-11
+**Last Updated**: 2026-04-11
+
+---
+
+## ⚠️ 核心约束(必须遵守)
+
+### 🚫 禁止操作
+1. **严禁移动现有笔记** - 除非用户明确要求"移动 XXX 到 YYY"
+2. **严禁删除任何笔记** - 除非用户明确要求"删除 XXX"
+3. **严禁修改非授权目录** - 只能在以下允许的子目录下创建笔记
+4. **严禁在根目录或其他区域创建笔记** - 必须遵循目录结构
+
+### ✅ 允许操作的目录
+
+**只能**在以下父节点的**子目录**下创建新笔记:
+
+| 允许创建的父节点 | Note ID | 用途 |
+|-----------------|---------|------|
+| `00-Inbox` | `h9skuCU9czep` | 临时收集箱(默认) |
+| `01-Projects` | `8C1UOi0hfib5` | 项目相关笔记 |
+| `02-Areas/基础设施` | `pS6clLY4g5sd` | 基础设施领域 |
+| `02-Areas/开发环境` | `b7lkhRneQk2a` | 开发环境领域 |
+| `03-Resources` | `1AbShK4WDw38` | 参考资源 |
+| `04-Archives` | `6rQ5acaYa7fZ` | 归档内容 |
+| `99-Templates` | `RVjlkQmiHxaw` | 模板笔记 |
+
+**重要**:
+- 创建新笔记时,`parentNoteId` **必须**是上述 ID 之一或其子节点
+- 如果用户要求的位置不在上述列表中,**必须拒绝**并提示用户
+- 不确定时,默认使用 `h9skuCU9czep` (Inbox)
+
+---
+
+## 📋 完整笔记 ID 映射表
+
+### 🌟 知识库总览
+```
+🌟 知识库总览 → XTNCDr5hq9bG
+📖 知识库使用指南 → hgILpr4yRkcS
+📝 笔记记录规范 → [见使用指南子节点]
+✅ 迁移完成报告 → [见使用指南子节点]
+🚀 快速记录指南 → U4FnimoXwG1j
+📋 ID 速查表 → Aa2aRAGLWuu4
+```
+
+### 📥 Inbox (临时收集箱)
+```
+00-Inbox → h9skuCU9czep ← 默认存放位置
+```
+
+### 🚀 Projects (进行中项目)
+```
+01-Projects → 8C1UOi0hfib5
+├── Oclaw 项目 → [待确认]
+├── NETX 项目 → [待确认]
+└── 计划 → THJbELUQ2Ecm
+```
+
+### 🏗️ Areas (持续责任)
+```
+02-Areas → HFsgsHfH4nKN
+├── 基础设施 → pS6clLY4g5sd
+│ ├── QNAP-NAS → 6CC2lab7vPDA
+│ └── QNAP → 9TRXB88ODggT
+└── 开发环境 → b7lkhRneQk2a
+ ├── API 密钥管理 → [待确认]
+ ├── PG 数据库 → zdscNfnBnoqq
+ ├── 启动命令 → 1tCnm2jbiJPW
+ └── skill → JRYSKzNWQaCp
+```
+
+### 📚 Resources (参考资源)
+```
+03-Resources → 1AbShK4WDw38
+├── Python → M5wP7D0FZ819
+├── PostgreSQL → NZS3aOMlNJcp
+├── Skills 开发指南 → yPold3qhllN1
+│ └── Note-Taking Skill → TNyFX4lADEk5
+├── Trilium Next 使用指南 → 2s26WHNqv0Q8
+└── 学习 → l6OfBhJ804qL
+```
+
+### 🗄️ Archives (归档)
+```
+04-Archives → 6rQ5acaYa7fZ
+├── API (空) → MLXD4mGL5mzh
+└── 生活 → Gvn9zydADEYl
+```
+
+### 📝 Templates (模板)
+```
+99-Templates → RVjlkQmiHxaw
+```
+
+---
+
+## 🎯 功能说明
+
+本 Skill 帮助用户在记录信息时自动判断最佳存放位置,遵循 PARA 知识管理方法,并**严格遵守目录限制**。
+
+### 核心能力
+1. **智能分类** - 根据内容自动推荐 Projects/Areas/Resources/Archives
+2. **快速创建** - 一键在正确位置创建结构化笔记
+3. **目录验证** - 强制验证父节点是否在允许列表中
+4. **Inbox 管理** - 临时存储 + 定期整理提醒
+5. **标签建议** - 自动添加合适的状态/优先级/类型标签
+6. **关联推荐** - 推荐相关笔记建立链接
+
+---
+
+## 📋 触发条件
+
+当用户提到以下关键词时触发:
+- "记一下"、"记录"、"保存"、"添加笔记"
+- "新建笔记"、"创建文档"、"写个笔记"
+- "放在哪里"、"归到哪类"、"怎么分类"
+- "inbox"、"整理笔记"、"归档"
+- "项目笔记"、"技术文档"、"参考资料"
+
+---
+
+## 🔄 工作流程
+
+### 1. 接收信息
+用户提供要记录的内容或想法
+
+### 2. 分析分类
+根据内容特征判断所属类别:
+
+| 特征 | 分类 | 示例 |
+|------|------|------|
+| 有明确目标/截止日期 | **Projects** | "Oclaw 新功能开发计划" |
+| 持续责任/维护领域 | **Areas** | "NAS 备份策略"、"API 密钥更新" |
+| 知识点/参考资料 | **Resources** | "Python 异步编程笔记"、"PostgreSQL 优化技巧" |
+| 已完成/过时内容 | **Archives** | "2023 年项目总结" |
+| 不确定/临时信息 | **Inbox** | "稍后整理的会议记录" |
+
+### 3. 目录验证(关键步骤)
+```javascript
+// 验证父节点是否在允许列表中
+const allowedParents = [
+ 'h9skuCU9czep', // Inbox
+ '8C1UOi0hfib5', // Projects
+ 'pS6clLY4g5sd', // Areas/基础设施
+ 'b7lkhRneQk2a', // Areas/开发环境
+ '1AbShK4WDw38', // Resources
+ '6rQ5acaYa7fZ', // Archives
+ 'RVjlkQmiHxaw' // Templates
+];
+
+if (!allowedParents.includes(parentNoteId)) {
+ throw new Error("❌ 无法在指定位置创建笔记");
+}
+```
+
+### 4. 创建笔记
+- 在确定的父节点下创建新笔记
+- 使用描述性标题
+- 应用标准模板(如适用)
+- 添加基础标签
+
+### 5. 确认反馈
+向用户展示:
+- ✅ 已创建笔记的位置
+- 📝 笔记标题和预览
+- 🏷️ 应用的标签
+- 🔗 推荐的相关笔记
+- ⚠️ 如有目录限制问题,说明原因
+
+---
+
+## 📂 分类决策树
+
+```
+用户提供信息
+ ↓
+是否有明确的项目目标?
+├─ 是 → Projects/[项目名称] (8C1UOi0hfib5)
+│ └─ 如果项目不存在 → 先询问是否创建新项目
+└─ 否 → 是否是持续责任领域?
+ ├─ 是 → Areas/[领域名称]
+ │ ├─ 基础设施 → pS6clLY4g5sd
+ │ └─ 开发环境 → b7lkhRneQk2a
+ └─ 否 → 是否是参考知识?
+ ├─ 是 → Resources/[主题] (1AbShK4WDw38)
+ │ └─ 技术栈、教程、文档、最佳实践
+ └─ 否 → 是否已过时?
+ ├─ 是 → Archives/ (6rQ5acaYa7fZ)
+ └─ 否 → Inbox/ (h9skuCU9czep) ← 默认
+```
+
+---
+
+## 🏷️ 标签系统
+
+### 自动应用的标签
+
+#### 状态标签
+- `#status/active` - 进行中项目/任务
+- `#status/pending` - 待处理事项
+- `#status/completed` - 已完成
+- `#status/archived` - 已归档
+
+#### 优先级标签
+- `#priority/high` - 高优先级(包含"紧急"、"重要"、"尽快"等词)
+- `#priority/medium` - 中优先级(默认)
+- `#priority/low` - 低优先级(包含"有空时"、"参考"等词)
+
+#### 类型标签
+- `#type/note` - 普通笔记
+- `#type/task` - 任务清单(包含"待办"、"要做"等词)
+- `#type/reference` - 参考资料
+- `#type/meeting` - 会议记录
+- `#type/idea` - 想法/灵感
+
+#### 领域标签
+- `#domain/tech` - 技术相关(代码、工具、框架等)
+- `#domain/business` - 业务相关
+- `#domain/personal` - 个人生活
+- `#domain/learning` - 学习内容
+
+---
+
+## 💬 使用示例
+
+### 示例 1: 记录项目任务
+**用户**: "记一下,明天要完成 Oclaw 的搜索功能优化"
+
+**助手**:
+```
+✅ 已创建笔记
+
+📍 位置:Projects/Oclaw 项目/搜索功能优化
+🏷️ 标签:#status/active #priority/high #type/task #domain/tech
+📅 截止:明天
+
+是否需要设置提醒或添加到待办清单?
+```
+
+### 示例 2: 保存技术知识
+**用户**: "记录一个 PostgreSQL 的性能优化技巧:使用 EXPLAIN ANALYZE 分析慢查询"
+
+**助手**:
+```
+✅ 已创建笔记
+
+📍 位置:Resources/PostgreSQL/性能优化技巧
+🏷️ 标签:#type/reference #domain/tech #database
+🔗 相关:PostgreSQL 使用指南
+
+需要补充更多细节或示例吗?
+```
+
+### 示例 3: 临时想法
+**用户**: "先记下来,周末再整理:关于技能市场化的想法"
+
+**助手**:
+```
+✅ 已放入 Inbox
+
+📍 位置:00-Inbox/技能市场化想法
+🏷️ 标签:#status/pending #type/idea #domain/business
+⏰ 提醒:周末整理
+
+需要在周末提醒你整理这条笔记吗?
+```
+
+### 示例 4: 尝试在非授权目录创建(应拒绝)
+**用户**: "把这个笔记放到根目录下"
+
+**助手**:
+```
+❌ 抱歉,我无法在指定位置创建笔记。
+
+⚠️ 目录限制规则:
+为确保知识库结构稳定,我只能在规定目录下创建新笔记。
+
+✅ 允许的目录:
+- Inbox (h9skuCU9czep) - 临时收集
+- Projects (8C1UOi0hfib5) - 项目相关
+- Areas/基础设施 (pS6clLY4g5sd)
+- Areas/开发环境 (b7lkhRneQk2a)
+- Resources (1AbShK4WDw38) - 参考资料
+- Archives (6rQ5acaYa7fZ) - 归档内容
+- Templates (RVjlkQmiHxaw) - 模板
+
+💡 建议:先放入 Inbox,之后可以手动移动。
+
+是否要放入 Inbox?[是] [取消]
+```
+
+### 示例 5: 询问分类
+**用户**: "这个 NAS 维护文档应该放在哪里?"
+
+**助手**:
+```
+📂 建议分类:Areas/基础设施/QNAP-NAS
+
+理由:
+- NAS 维护是持续责任领域(Area)
+- 属于基础设施范畴
+- 已有 QNAP-NAS 子分类
+- 父节点 ID: pS6clLY4g5sd
+
+是否立即在此位置创建笔记?
+```
+
+---
+
+## 🛠️ 工具调用
+
+### Trilium Next API
+- `create_note` - 创建新笔记(必须先验证父节点)
+- `move_note` - 移动笔记到正确位置(仅当用户明确要求)
+- `search_notes` - 查找相关笔记建立关联
+- `read_attributes` - 读取现有标签
+- `manage_attributes` - 添加/更新标签
+
+### 可选集成
+- Todo 系统 - 同步任务类笔记
+- 日历系统 - 设置时间相关提醒
+- Git - 备份重要笔记
+
+---
+
+## 📊 统计与报告
+
+### 每周自动生成
+- 新增笔记数量按分类统计
+- Inbox 整理情况
+- 最活跃的项目/领域
+- 标签使用分布
+
+### 每月回顾
+- 完成项目归档
+- 过时资源清理
+- 分类体系优化建议
+
+---
+
+## ⚙️ 配置选项
+
+### 用户偏好
+```yaml
+default_inbox_review: weekly # Inbox 整理频率
+auto_tag_priority: true # 自动识别优先级
+suggest_related: true # 推荐相关笔记
+backup_enabled: true # 启用 Git 备份
+```
+
+### 目录限制(强制)
+```yaml
+directory_restrictions:
+ enabled: true # ⚠️ 必须为 true
+ allowed_parents:
+ - h9skuCU9czep # Inbox
+ - 8C1UOi0hfib5 # Projects
+ - pS6clLY4g5sd # Areas/基础设施
+ - b7lkhRneQk2a # Areas/开发环境
+ - 1AbShK4WDw38 # Resources
+ - 6rQ5acaYa7fZ # Archives
+ - RVjlkQmiHxaw # Templates
+ default_fallback: h9skuCU9czep # 不确定时默认放入 Inbox
+ strict_mode: true # 严格模式,禁止任何越界操作
+```
+
+### 自定义分类
+用户可以扩展默认分类:
+- 新增 Projects
+- 自定义 Areas
+- 扩展 Resources 子分类
+
+---
+
+## 🚨 错误处理
+
+### 常见问题
+
+**Q: 找不到合适的分类?**
+A: 默认放入 Inbox (`h9skuCU9czep`),并提示用户手动选择
+
+**Q: 笔记标题重复?**
+A: 自动添加时间戳或序号区分
+
+**Q: 父节点不存在?**
+A: 询问用户是否创建新分类或直接放入 Inbox
+
+**Q: 尝试在非授权目录创建?**
+A: **拒绝**并显示允许目录列表,建议使用 Inbox
+
+---
+
+## 📈 最佳实践
+
+### 给用户的建议
+1. **及时记录** - 想法出现时立即记下
+2. **信任系统** - 让 Skill 自动分类,事后可以调整
+3. **定期整理** - 每周花 10 分钟整理 Inbox
+4. **善用标签** - 不要过度标签化(3-5 个为宜)
+5. **建立关联** - 主动链接相关笔记
+
+### Skill 行为准则
+1. **优先确认** - 不确定时询问用户而非猜测
+2. **保持一致** - 同类内容使用相同分类逻辑
+3. **最小干扰** - 快速创建,减少打断
+4. **主动提醒** - 定期提示整理 Inbox 和归档
+5. **严格遵守** - 绝不违反目录限制规则
+
+---
+
+## 🔮 未来增强
+
+- [ ] AI 辅助摘要生成
+- [ ] 自动提取关键词作为标签
+- [ ] 智能关联推荐(基于内容相似度)
+- [ ] 语音输入支持
+- [ ] 多语言笔记支持
+- [ ] 团队协作功能
+- [ ] 自动审计目录合规性
+
+---
+
+## 📚 参考资源
+
+- [PARA 方法](https://fortelabs.com/blog/para/)
+- [Trilium Next 文档](https://triliumnext.org/)
+- [知识管理最佳实践](Resources/Knowledge Management)
+- [笔记记录规范](hgILpr4yRkcS)
+- [知识库 ID 速查表](Aa2aRAGLWuu4)
+
+---
+
+**最后更新**: 2026-04-11
+**版本**: 1.1 (带目录限制)
+**⚠️ 重要**: 严格遵守目录限制,禁止在非授权位置创建笔记!
diff --git a/skills/playwright-cli/SKILL.md b/skills/playwright-cli/SKILL.md
new file mode 100644
index 00000000..ab2f610e
--- /dev/null
+++ b/skills/playwright-cli/SKILL.md
@@ -0,0 +1,128 @@
+---
+name: playwright-cli
+description: 官方Microsoft Playwright CLI网页自动化工具,支持所有主流浏览器的无头/有头自动化操作,包括页面导航、元素交互、截图、录制、测试等功能。当用户提到网页自动化、浏览器操作、爬虫、截图、录制用户操作、E2E测试时触发。
+---
+
+# Playwright CLI 技能
+
+Playwright CLI是微软官方的浏览器自动化工具,支持Chromium、Firefox、WebKit三大主流浏览器,提供强大的网页自动化能力。
+
+## 安装步骤
+
+### 1. 安装Playwright CLI
+```bash
+npm install -g @playwright/test
+# 或者
+pip install playwright
+```
+
+### 2. 安装浏览器
+```bash
+playwright install
+# 仅安装特定浏览器
+playwright install chromium firefox
+# 安装系统依赖(Linux环境)
+playwright install-deps
+```
+
+## 核心常用命令
+
+### 页面操作
+```bash
+# 打开指定网页
+playwright open https://example.com
+
+# 无头模式打开网页并截图
+playwright screenshot https://example.com example.png
+# 全屏截图
+playwright screenshot https://example.com --full-page full.png
+# 指定视口大小
+playwright screenshot https://example.com --viewport-size=1920,1080 desktop.png
+
+# 生成PDF
+playwright pdf https://example.com example.pdf
+# 自定义PDF格式
+playwright pdf https://example.com --format=A4 --landscape report.pdf
+```
+
+### 录制用户操作
+```bash
+# 录制用户操作并生成代码
+playwright codegen https://example.com
+# 保存录制结果到文件
+playwright codegen https://example.com --output script.py
+# 生成指定语言的代码(python, javascript, java, csharp)
+playwright codegen https://example.com --target python
+```
+
+### 测试相关
+```bash
+# 运行测试
+playwright test
+# 运行特定测试文件
+playwright test tests/example.spec.js
+# 有头模式运行测试(显示浏览器界面)
+playwright test --headed
+# 调试模式运行
+playwright test --debug
+# 生成测试报告
+playwright show-report
+```
+
+### 浏览器管理
+```bash
+# 列出已安装的浏览器
+playwright list-browsers
+# 更新浏览器到最新版本
+playwright install --force
+# 卸载浏览器
+playwright uninstall chromium
+```
+
+## 使用示例
+
+### 示例1:批量截图多个网页
+```bash
+# 对多个网页进行全屏截图
+for url in "https://google.com" "https://github.com" "https://stackoverflow.com"; do
+ name=$(echo $url | sed 's/https\?:\/\///' | sed 's/\//_/g')
+ playwright screenshot $url --full-page "${name}.png"
+done
+```
+
+### 示例2:自动填写表单
+```python
+# 录制生成的自动登录脚本示例
+from playwright.sync_api import Playwright, sync_playwright, expect
+
+def run(playwright: Playwright) -> None:
+ browser = playwright.chromium.launch(headless=False)
+ context = browser.new_context()
+ page = context.new_page()
+ page.goto("https://example.com/login")
+ page.get_by_label("用户名").fill("your_username")
+ page.get_by_label("密码").fill("your_password")
+ page.get_by_role("button", name="登录").click()
+ # 截图登录后的页面
+ page.screenshot(path="logged_in.png")
+ context.close()
+ browser.close()
+
+with sync_playwright() as playwright:
+ run(playwright)
+```
+
+### 示例3:模拟移动端访问
+```bash
+# 模拟iPhone 14访问网页并截图
+playwright screenshot https://example.com --device="iPhone 14" mobile.png
+# 模拟安卓设备
+playwright screenshot https://example.com --device="Galaxy S23" android.png
+```
+
+## 最佳实践
+1. 优先使用无头模式执行自动化任务,性能更高
+2. 复杂操作优先使用`codegen`录制生成代码,再手动调整
+3. 执行长时间任务时添加`--slowmo=1000`参数减慢操作速度,避免被反爬
+4. 保存登录状态使用`--save-storage=auth.json`,下次可以直接`--load-storage=auth.json`跳过登录
+5. 网络不稳定时添加`--timeout=60000`延长超时时间
diff --git a/skills/playwright-cli/_meta.json b/skills/playwright-cli/_meta.json
new file mode 100644
index 00000000..13fcfdd6
--- /dev/null
+++ b/skills/playwright-cli/_meta.json
@@ -0,0 +1,6 @@
+{
+ "ownerId": "kn7ez7a31k0sqt7ad9wk2hq31n82qea1",
+ "slug": "playwright-cli-openclaw",
+ "version": "1.0.0",
+ "publishedAt": 1773287492717
+}
\ No newline at end of file
diff --git a/skills/self-improvement/README.md b/skills/self-improvement/README.md
index 89508174..5016d08c 100644
--- a/skills/self-improvement/README.md
+++ b/skills/self-improvement/README.md
@@ -1,14 +1,5 @@
# self-improvement
-Self-improvement skill for Oclaw. It captures learnings, errors, and feature requests to support continuous improvement across sessions.
+Self-improvement skill for Oclaw. Captures learnings, errors, and feature requests to support continuous improvement across sessions.
-## Attribution
-
-Remade for Oclaw from the original repo:
-
-- https://github.com/pskoett/pskoett-ai-skills
-- https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement
-
-## Main File
-
-- `SKILL.md`
+Based on the original [pskoett/pskoett-ai-skills](https://github.com/pskoett/pskoett-ai-skills) — adapted for Oclaw with `memory_wiki_*` tools.
diff --git a/skills/self-improvement/SKILL.md b/skills/self-improvement/SKILL.md
index ad253d92..ae4a6da3 100644
--- a/skills/self-improvement/SKILL.md
+++ b/skills/self-improvement/SKILL.md
@@ -1,83 +1,91 @@
----
-name: self-improvement
-description: 将纠错、错误与能力缺口写入 Wiki 以持续改进。适用于操作失败、用户反馈纠正、问题复发、或需要沉淀并推动的新能力需求场景。
-metadata:
+# self-improvement — 自我改进 (Self-Improvement)
+
+> 将纠错、错误与能力缺口写入 Wiki 以持续改进,形成"犯错→记录→不再犯"的闭环。
+> 本技能完全基于 `memory_wiki_*` 工具,不依赖本地文件或外部脚本。
+
---
-# 自我改进(仅 Wiki)
+## 触发条件 (Triggers)
-本技能仅使用 Wiki,不使用本地 `.learnings/` 文件。
+出现以下情况时启用:
-## 存储路径
+1. 命令或操作出现非预期失败
+2. 用户对回答进行纠正
+3. 用户提出缺失能力需求
+4. 同类问题再次复发
+5. 发现更优且可复用的方法
+6. 每轮对话结束时(强制三问反思)
-- `improvement/learnings.md`
-- `improvement/errors.md`
-- `improvement/feature-requests.md`
+## 必要流程 (Required Workflow)
-## 触发条件
+```
+1. memory_wiki_search → 检索 improvement/* 看是否已有记录
+2. memory_wiki_get → 读取目标文件上下文
+3. memory_wiki_apply → 追加结构化条目 (action=append)
+4. memory_wiki_lint → 质量检查
+5. lint 报错 → 立即修复
+```
-出现以下情况时启用本技能:
+## 条目路由 (Entry Routing)
-1. 命令或操作出现非预期失败。
-2. 用户对错误回答进行纠正。
-3. 用户提出缺失能力需求。
-4. 同类问题再次复发。
-5. 发现更优且可复用的方法。
+| 内容类型 | 目标文件 | 示例 |
+|---------|---------|------|
+| 纠错/洞见/最佳实践 | `improvement/learnings.md` | 用户纠正了某个认知错误 |
+| 运行时/工具/API 失败 | `improvement/errors.md` | 某个工具调用报错 |
+| 能力缺口/功能请求 | `improvement/feature-requests.md` | 用户问"你能做 X 吗?" |
+| 长篇经验沉淀 (>300 字) | `improvement/learnings/.md` | 一次完整的技能安装流程 |
-## 必要流程
-
-每次触发都执行以下流程:
-
-1. 用 `memory_wiki_search` 检索历史相关记录。
-2. 用 `memory_wiki_get` 读取目标文件上下文。
-3. 用 `memory_wiki_apply`(`action=append`)追加结构化条目。
-4. 对目标文件执行 `memory_wiki_lint`。
-5. 若 lint 报错,立即用 `memory_wiki_apply` 修复。
-
-## 条目路由
-
-- 纠错 / 洞见 / 最佳实践 -> `improvement/learnings.md`
-- 运行时 / 工具 / API 失败 -> `improvement/errors.md`
-- 能力请求 / 缺失功能 -> `improvement/feature-requests.md`
-
-## 条目模板
+## 条目模板 (Entry Template)
```markdown
-## [ID] <标题>
-**Logged**: ISO-8601 时间戳
+## [ID] 标题 (Title)
+**Logged**: ISO-8601 时间戳 (Timestamp)
**Priority**: low | medium | high | critical
-**Status**: pending
+**Status**: pending | resolved | promoted_to_skill
**Area**: frontend | backend | infra | tests | docs | config
-### Summary
+### 摘要 (Summary)
一句话摘要。
-### Details
+### 详情 (Details)
发生了什么、为什么重要、可执行改进建议。
-### Metadata
-- Source: conversation | error | user_feedback
+### 元数据 (Metadata)
+- Source: conversation | error | user_feedback | reflection
- Related Files: path/to/file.ext
- See Also:
```
-ID 格式:
+### ID 格式 (ID Format)
- Learning: `LRN-YYYYMMDD-XXX`
- Error: `ERR-YYYYMMDD-XXX`
- Feature request: `FEAT-YYYYMMDD-XXX`
-## 提升目标
+XXX = 当天序号,从 001 开始。
-当条目已具备广泛复用价值时,将精炼规则提升到:
+## 提升路径 (Promotion Path)
-- `AGENTS.md`(工作流模式)
-- `SOUL.md`(行为模式)
-- `TOOLS.md`(工具易错点)
-- `.github/copilot-instructions.md`(共享编码约定)
+当某个条目被验证有广泛复用价值时:
-## 安全规则
+| 到达条件 | 提升目标 |
+|---------|---------|
+| 可用作行为约定 | `core/principles.md` |
+| 可用作维护规则 | `core/maintenance.md` |
+| 可提炼为独立技能 | 使用 `skill-creator` 创建新技能 |
+| 某个工具易错点 | 标注在 SKILL.md 或 REFERENCES.md |
-- 不记录密钥、令牌、凭据或原始敏感信息。
-- 敏感输出使用脱敏摘要,不保留完整原文。
-- 未验证事实不得当作已确认规则持久化。
+## 会话结束三问 (End-of-Session Reflection)
+
+每轮对话结束前执行:
+
+1. **学到了什么?** → `improvement/learnings.md`
+2. **有什么可优化?** → `improvement/feature-requests.md` 或 `core/principles.md`
+3. **用户画像有更新吗?** → `users/current.md`
+
+## 安全规则 (Security Rules)
+
+- 不记录密钥、令牌、凭据或原始敏感信息
+- 敏感输出使用脱敏摘要,不保留完整原文
+- 未验证事实不得当作已确认规则持久化
+- 涉及用户偏好写入时需有明确的对话证据,不推测
diff --git a/skills/self-improvement/assets/ERRORS.md b/skills/self-improvement/assets/ERRORS.md
deleted file mode 100644
index 6bce3929..00000000
--- a/skills/self-improvement/assets/ERRORS.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Errors Log
-
-Command failures, exceptions, and unexpected behaviors.
-
----
diff --git a/skills/self-improvement/assets/FEATURE_REQUESTS.md b/skills/self-improvement/assets/FEATURE_REQUESTS.md
deleted file mode 100644
index 35272774..00000000
--- a/skills/self-improvement/assets/FEATURE_REQUESTS.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Feature Requests
-
-Capabilities requested by user that don't currently exist.
-
----
diff --git a/skills/self-improvement/assets/LEARNINGS.md b/skills/self-improvement/assets/LEARNINGS.md
deleted file mode 100644
index 6993f9b2..00000000
--- a/skills/self-improvement/assets/LEARNINGS.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# 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 CLAUDE.md, AGENTS.md, or copilot-instructions.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
-...
-```
-
----
-
diff --git a/skills/self-improvement/assets/SKILL-TEMPLATE.md b/skills/self-improvement/assets/SKILL-TEMPLATE.md
deleted file mode 100644
index 0162134b..00000000
--- a/skills/self-improvement/assets/SKILL-TEMPLATE.md
+++ /dev/null
@@ -1,177 +0,0 @@
-# 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
diff --git a/skills/self-improvement/hooks/runtime/HOOK.md b/skills/self-improvement/hooks/runtime/HOOK.md
deleted file mode 100644
index d095cd94..00000000
--- a/skills/self-improvement/hooks/runtime/HOOK.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-name: self-improvement
-description: "在智能体启动阶段注入自我改进提醒"
-metadata: {"oclaw":{"emoji":"🧠","events":["agent:bootstrap"]}}
----
-
-# 自我改进 Hook
-
-在 `agent:bootstrap` 阶段注入“学习沉淀提醒”。
-
-## 功能说明
-
-- 在 `agent:bootstrap` 触发(工作区文件注入前)
-- 注入提醒块,引导将学习写入 Wiki 路径
-- 提示智能体记录纠错、错误与新发现
-
-## 配置方式
-
-无需额外配置,启用命令:
-
-```bash
-oclaw hooks enable self-improvement
-```
diff --git a/skills/self-improvement/hooks/runtime/handler.py b/skills/self-improvement/hooks/runtime/handler.py
deleted file mode 100644
index 56e58388..00000000
--- a/skills/self-improvement/hooks/runtime/handler.py
+++ /dev/null
@@ -1,85 +0,0 @@
-from __future__ import annotations
-
-from typing import Any
-
-REMINDER_NAME = "SELF_IMPROVEMENT_REMINDER.md"
-REMINDER_PATH = REMINDER_NAME
-
-REMINDER_CONTENT = """## 自我改进提醒
-
-任务完成后,请评估是否产生可沉淀学习。
-
-仅在当前仓库/工作区启用 self-improvement 技能时记录。
-
-记录前:
-- 使用 memory_wiki_* 工具写入 `improvement/` 下的 Wiki 笔记
-- 不记录密钥、令牌、私钥、环境变量或原始对话全文
-- 优先使用简短摘要或脱敏片段,避免完整命令输出
-
-**以下情况应记录:**
-- 用户纠正你 → `improvement/learnings.md`
-- 命令/操作失败 → `improvement/errors.md`
-- 用户提出缺失能力 → `improvement/feature-requests.md`
-- 发现认知错误 → `improvement/learnings.md`
-- 发现更优做法 → `improvement/learnings.md`
-
-**当模式被验证后进行提升:**
-- 行为模式 → `SOUL.md`
-- 工作流改进 → `AGENTS.md`
-- 工具易错点 → `TOOLS.md`
-
-条目保持简洁:时间、标题、发生了什么、后续应如何做。"""
-
-
-def _is_record(value: object) -> bool:
- return isinstance(value, dict)
-
-
-def _is_injected_reminder_file(value: object) -> bool:
- if not _is_record(value) or str(value.get("path")) != REMINDER_PATH: # type: ignore[union-attr]
- return False
- v = value # type: ignore[assignment]
- return v.get("virtual") is True or v.get("content") == REMINDER_CONTENT
-
-
-def handle(event: object) -> None:
- if getattr(event, "type", None) != "agent" or getattr(event, "action", None) != "bootstrap":
- return
- ctx = getattr(event, "context", None)
- if not isinstance(ctx, dict):
- return
- session_key = str(getattr(event, "sessionKey", "") or "")
- if ":subagent:" in session_key:
- return
-
- if not isinstance(ctx.get("bootstrapFiles"), list):
- return
- files: list[object] = list(ctx.get("bootstrapFiles") or [])
-
- occupied = any(
- _is_record(f) and str(f.get("path")) == REMINDER_PATH and not _is_injected_reminder_file(f) # type: ignore[union-attr]
- for f in files
- )
- if occupied:
- return
-
- cleaned: list[object] = [
- f
- for i, f in enumerate(files)
- if (not _is_injected_reminder_file(f))
- or (next((j for j, c in enumerate(files) if _is_injected_reminder_file(c)), -1) == i)
- ]
-
- reminder_file: dict[str, Any] = {
- "name": REMINDER_NAME,
- "path": REMINDER_PATH,
- "content": REMINDER_CONTENT,
- "missing": False,
- "virtual": True,
- }
- existing_idx = next((i for i, f in enumerate(cleaned) if _is_injected_reminder_file(f)), -1)
- if existing_idx == -1:
- cleaned.append(reminder_file)
- else:
- cleaned[existing_idx] = reminder_file
- ctx["bootstrapFiles"] = cleaned
diff --git a/skills/self-improvement/references/examples.md b/skills/self-improvement/references/examples.md
deleted file mode 100644
index e1d0c195..00000000
--- a/skills/self-improvement/references/examples.md
+++ /dev/null
@@ -1,374 +0,0 @@
-# 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 CLAUDE.md for future reference
-
----
-```
-
-## Learning: Promoted to CLAUDE.md
-
-```markdown
-## [LRN-20250115-003] best_practice
-
-**Logged**: 2025-01-15T16:00:00Z
-**Priority**: high
-**Status**: promoted
-**Promoted**: CLAUDE.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: oclaw/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, oclaw/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: oclaw/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
-```
diff --git a/skills/self-improvement/references/hooks-setup.md b/skills/self-improvement/references/hooks-setup.md
deleted file mode 100644
index fd7319e7..00000000
--- a/skills/self-improvement/references/hooks-setup.md
+++ /dev/null
@@ -1,225 +0,0 @@
-# Hook Setup Guide
-
-Configure automatic self-improvement triggers for AI coding agents.
-
-## 概览
-
-Hooks enable proactive learning capture by injecting reminders at key moments:
-- **UserPromptSubmit**: Reminder after each prompt to evaluate learnings
-- **PostToolUse (Bash)**: Error detection when commands fail
-
-## Claude Code Setup
-
-### Option 1: Project-Level Configuration
-
-Create `.claude/settings.json` in your project root:
-
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- {
- "matcher": "",
- "hooks": [
- {
- "type": "command",
- "command": "./skills/self-improvement/scripts/activator.sh"
- }
- ]
- }
- ],
- "PostToolUse": [
- {
- "matcher": "Bash",
- "hooks": [
- {
- "type": "command",
- "command": "./skills/self-improvement/scripts/error-detector.sh"
- }
- ]
- }
- ]
- }
-}
-```
-
-### Option 2: User-Level Configuration
-
-Add to `~/.claude/settings.json` for global activation:
-
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- {
- "matcher": "",
- "hooks": [
- {
- "type": "command",
- "command": "~/.claude/skills/self-improvement/scripts/activator.sh"
- }
- ]
- }
- ]
- }
-}
-```
-
-### Minimal Setup (Activator Only)
-
-For lower overhead, use only the UserPromptSubmit hook:
-
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- {
- "matcher": "",
- "hooks": [
- {
- "type": "command",
- "command": "./skills/self-improvement/scripts/activator.sh"
- }
- ]
- }
- ]
- }
-}
-```
-
-## Codex CLI Setup
-
-Codex uses the same hook system as Claude Code. Create `.codex/settings.json`:
-
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- {
- "matcher": "",
- "hooks": [
- {
- "type": "command",
- "command": "./skills/self-improvement/scripts/activator.sh"
- }
- ]
- }
- ]
- }
-}
-```
-
-## GitHub Copilot Setup
-
-Copilot doesn't support hooks directly. Instead, add guidance to `.github/copilot-instructions.md`:
-
-```markdown
-## 自我改进
-
-After completing tasks that involved:
-- Debugging non-obvious issues
-- Discovering workarounds
-- Learning project-specific patterns
-- Resolving unexpected errors
-
-Consider logging the learning to Wiki (`improvement/learnings.md`, `improvement/errors.md`, `improvement/feature-requests.md`) using the format from the self-improvement skill.
-
-For high-value learnings that would benefit other sessions, consider skill extraction.
-```
-
-## Verification
-
-### Test Activator Hook
-
-1. Enable the hook configuration
-2. Start a new Claude Code session
-3. Send any prompt
-4. Verify you see `` in the context
-
-### Test Error Detector Hook
-
-1. Enable PostToolUse hook for Bash
-2. Run a command that fails: `ls /nonexistent/path`
-3. Verify you see `` reminder
-
-### Dry Run Extract Script
-
-```bash
-./skills/self-improvement/scripts/extract-skill.sh test-skill --dry-run
-```
-
-Expected output shows the skill scaffold that would be created.
-
-## Troubleshooting
-
-### Hook Not Triggering
-
-1. **Check script permissions**: `chmod +x scripts/*.sh`
-2. **Verify path**: Use absolute paths or paths relative to project root
-3. **Check settings location**: Project vs user-level settings
-4. **Restart session**: Hooks are loaded at session start
-
-### Permission Denied
-
-```bash
-chmod +x ./skills/self-improvement/scripts/activator.sh
-chmod +x ./skills/self-improvement/scripts/error-detector.sh
-chmod +x ./skills/self-improvement/scripts/extract-skill.sh
-```
-
-### Script Not Found
-
-If using relative paths, ensure you're in the correct directory or use absolute paths:
-
-```json
-{
- "command": "/absolute/path/to/skills/self-improvement/scripts/activator.sh"
-}
-```
-
-### Too Much Overhead
-
-If the activator feels intrusive:
-
-1. **Use minimal setup**: Only UserPromptSubmit, skip PostToolUse
-2. **Add matcher filter**: Only trigger for certain prompts:
-
-```json
-{
- "matcher": "fix|debug|error|issue",
- "hooks": [...]
-}
-```
-
-## Hook Output Budget
-
-The activator is designed to be lightweight:
-- **Target**: ~50-100 tokens per activation
-- **Content**: Structured reminder, not verbose instructions
-- **Format**: XML tags for easy parsing
-
-If you need to reduce overhead further, you can edit `activator.sh` to output less text.
-
-## Security Considerations
-
-- Hook scripts run with the same permissions as Claude Code
-- Scripts only output text; they don't modify files or run commands
-- Error detector reads `CLAUDE_TOOL_OUTPUT` environment variable
-- Treat `CLAUDE_TOOL_OUTPUT` as potentially sensitive; do not log or forward it verbatim unless the user explicitly wants that detail
-- All scripts are opt-in (you must configure them explicitly)
-- Recommended default: enable `UserPromptSubmit` only, and add `PostToolUse` only when you want error-pattern reminders from command output
-
-## Disabling Hooks
-
-To temporarily disable without removing configuration:
-
-1. **Comment out in settings**:
-```json
-{
- "hooks": {
- // "UserPromptSubmit": [...]
- }
-}
-```
-
-2. **Or delete the settings file**: Hooks won't run without configuration
diff --git a/skills/self-improvement/references/oclaw-integration.md b/skills/self-improvement/references/oclaw-integration.md
deleted file mode 100644
index bab225bd..00000000
--- a/skills/self-improvement/references/oclaw-integration.md
+++ /dev/null
@@ -1,248 +0,0 @@
-# Oclaw Integration
-
-Complete setup and usage guide for integrating the self-improvement skill with Oclaw.
-
-## 概览
-
-Oclaw 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
-
-```
-~/.oclaw/
-├── 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.md
-└── hooks/ # Custom hooks
- └── /
- ├── HOOK.md
- └── handler.py
-```
-
-## 快速配置
-
-### 1. Install the Skill
-
-```bash
-clawdhub install self-improving-agent
-```
-
-Or copy manually:
-
-```bash
-cp -r self-improving-agent ~/.oclaw/skills/
-```
-
-### 2. Install the Hook (Optional)
-
-Copy the hook to Oclaw's hooks directory:
-
-```bash
-cp -r hooks/oclaw ~/.oclaw/hooks/self-improvement
-```
-
-Enable the hook:
-
-```bash
-oclaw hooks enable self-improvement
-```
-
-### 3. Ensure Wiki Improvement Notes Exist
-
-Create or initialize these Wiki notes under your configured wiki root:
-
-- `improvement/learnings.md`
-- `improvement/errors.md`
-- `improvement/feature-requests.md`
-
-## 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
-
-## 自我改进技能
-Log learnings to Wiki `improvement/*.md` notes 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 Wiki improvement notes (`improvement/*.md`)
-2. **Cross-session**: Promote to workspace files
-
-### Promotion Decision Tree
-
-```
-Is the learning project-specific?
-├── Yes → Keep in improvement/learnings.md
-└── 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
-
-Oclaw 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 |
-
-## Detection Triggers
-
-### Standard Triggers
-- User corrections ("No, that's wrong...")
-- Command failures (non-zero exit codes)
-- API errors
-- Knowledge gaps
-
-### Oclaw-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 `improvement/*.md` or report upstream |
-
-## Verification
-
-Check hook is registered:
-
-```bash
-oclaw hooks list
-```
-
-Check skill is loaded:
-
-```bash
-oclaw 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 wiki `improvement/*.md` notes exist
-2. Check file permissions
-3. Ensure workspace path is configured correctly
-
-### Skill not loading
-
-1. Check skill is in skills directory
-2. Verify SKILL.md has correct frontmatter
-3. Run `oclaw status` to see loaded skills
diff --git a/skills/self-improvement/scripts/activator.sh b/skills/self-improvement/scripts/activator.sh
deleted file mode 100644
index 7886b469..00000000
--- a/skills/self-improvement/scripts/activator.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-# 自我改进激活 Hook
-# 在 UserPromptSubmit 触发,用于提醒记录学习沉淀
-# 输出保持精简(约 50-100 tokens)以降低上下文负担
-
-set -e
-
-# 以系统上下文形式输出提醒
-cat << 'EOF'
-
-本任务完成后,请判断是否产出可沉淀知识:
-- 是否通过排查得到非显而易见的解法?
-- 是否形成了异常行为的可复用绕过方案?
-- 是否识别出项目特有模式?
-- 是否有需要调试才能解决的错误?
-
-若是,请写入 Wiki:
-- improvement/learnings.md
-- improvement/errors.md
-- improvement/feature-requests.md
-若价值较高(复发、可广泛复用),请考虑提炼为独立技能。
-
-EOF
diff --git a/skills/self-improvement/scripts/error-detector.sh b/skills/self-improvement/scripts/error-detector.sh
deleted file mode 100644
index 72a61c56..00000000
--- a/skills/self-improvement/scripts/error-detector.sh
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/bin/bash
-# 自我改进错误检测 Hook
-# 在 Bash 的 PostToolUse 触发,用于检测命令失败
-# 读取 CLAUDE_TOOL_OUTPUT 环境变量
-
-set -e
-
-# 检查工具输出是否包含错误信号
-# CLAUDE_TOOL_OUTPUT 为工具执行结果
-OUTPUT="${CLAUDE_TOOL_OUTPUT:-}"
-
-# 错误模式(大小写不敏感匹配)
-ERROR_PATTERNS=(
- "error:"
- "Error:"
- "ERROR:"
- "failed"
- "FAILED"
- "command not found"
- "No such file"
- "Permission denied"
- "fatal:"
- "Exception"
- "Traceback"
- "npm ERR!"
- "ModuleNotFoundError"
- "SyntaxError"
- "TypeError"
- "exit code"
- "non-zero"
-)
-
-# 检查输出是否匹配任一错误模式
-contains_error=false
-for pattern in "${ERROR_PATTERNS[@]}"; do
- if [[ "$OUTPUT" == *"$pattern"* ]]; then
- contains_error=true
- break
- fi
-done
-
-# 仅在检测到错误时输出提醒
-if [ "$contains_error" = true ]; then
- cat << 'EOF'
-
-检测到命令错误。若满足以下任一条件,请记录到 improvement/errors.md:
-- 错误出乎预期或并不直观
-- 需要排查才能解决
-- 可能在相似场景复发
-- 解决方案对后续会话有复用价值
-
-记录时请使用 self-improvement 技能格式:[ERR-YYYYMMDD-XXX]
-
-EOF
-fi
diff --git a/skills/self-improvement/scripts/extract-skill.sh b/skills/self-improvement/scripts/extract-skill.sh
deleted file mode 100644
index da000a3d..00000000
--- a/skills/self-improvement/scripts/extract-skill.sh
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/bin/bash
-# Skill Extraction Helper
-# Creates a new skill from a learning entry
-# 用法: ./extract-skill.sh [--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
-用法: $(basename "$0") [options]
-
-根据学习条目创建新技能。
-
-参数:
- skill-name 技能名称(小写,空格使用连字符)
-
-选项:
- --dry-run 仅预览将创建的内容,不落盘
- --output-dir 当前路径下的相对输出目录(默认: ./skills)
- -h, --help 显示帮助信息
-
-示例:
- $(basename "$0") docker-m1-fixes
- $(basename "$0") api-timeout-patterns --dry-run
- $(basename "$0") pnpm-setup --output-dir ./skills/custom
-
-技能将创建在: \$SKILLS_DIR//
-EOF
-}
-
-log_info() {
- echo -e "${GREEN}[信息]${NC} $1"
-}
-
-log_warn() {
- echo -e "${YELLOW}[警告]${NC} $1"
-}
-
-log_error() {
- echo -e "${RED}[错误]${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 需要提供相对路径参数"
- usage
- exit 1
- fi
- SKILLS_DIR="$2"
- shift 2
- ;;
- -h|--help)
- usage
- exit 0
- ;;
- -*)
- log_error "未知选项: $1"
- usage
- exit 1
- ;;
- *)
- if [ -z "$SKILL_NAME" ]; then
- SKILL_NAME="$1"
- else
- log_error "意外参数: $1"
- usage
- exit 1
- fi
- shift
- ;;
- esac
-done
-
-# Validate skill name
-if [ -z "$SKILL_NAME" ]; then
- log_error "必须提供技能名称"
- usage
- exit 1
-fi
-
-# Validate skill name format (lowercase, hyphens, no spaces)
-if ! [[ "$SKILL_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
- log_error "技能名称格式无效。仅允许小写字母、数字和连字符。"
- log_error "示例: 'docker-fixes', 'api-patterns', 'pnpm-setup'"
- exit 1
-fi
-
-# Validate output path to avoid writes outside current workspace.
-if [[ "$SKILLS_DIR" = /* ]]; then
- log_error "输出目录必须是当前目录下的相对路径。"
- exit 1
-fi
-
-if [[ "$SKILLS_DIR" =~ (^|/)\.\.(/|$) ]]; then
- log_error "输出目录不能包含 '..' 路径段。"
- exit 1
-fi
-
-SKILLS_DIR="${SKILLS_DIR#./}"
-SKILLS_DIR="./$SKILLS_DIR"
-
-SKILL_PATH="$SKILLS_DIR/$SKILL_NAME"
-
-# Check if skill already exists
-if [ -d "$SKILL_PATH" ] && [ "$DRY_RUN" = false ]; then
- log_error "技能已存在: $SKILL_PATH"
- log_error "请更换名称或先删除已有技能目录。"
- exit 1
-fi
-
-# Dry run output
-if [ "$DRY_RUN" = true ]; then
- log_info "预览模式 - 将会创建:"
- echo " $SKILL_PATH/"
- echo " $SKILL_PATH/SKILL.md"
- echo ""
- echo "模板内容预览:"
- echo "---"
- cat << TEMPLATE
-name: $SKILL_NAME
-description: "[TODO: 用一句话说明技能作用与触发场景]"
----
-
-# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
-
-[TODO: 简要说明技能目的]
-
-## Quick Reference
-
-| Situation | Action |
-|-----------|--------|
-| [触发条件] | [执行动作] |
-
-## Usage
-
-[TODO: 详细使用说明]
-
-## Examples
-
-[TODO: 补充具体示例]
-
-## Source Learning
-
-本技能由学习条目提炼生成。
-- Learning ID: [TODO: 填写原始学习条目 ID]
-- Original File: improvement/learnings.md
-TEMPLATE
- echo "---"
- exit 0
-fi
-
-# Create skill directory structure
-log_info "正在创建技能: $SKILL_NAME"
-
-mkdir -p "$SKILL_PATH"
-
-# Create SKILL.md from template
-cat > "$SKILL_PATH/SKILL.md" << TEMPLATE
----
-name: $SKILL_NAME
-description: "[TODO: 用一句话说明技能作用与触发场景]"
----
-
-# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
-
-[TODO: 简要说明技能目的]
-
-## Quick Reference
-
-| Situation | Action |
-|-----------|--------|
-| [触发条件] | [执行动作] |
-
-## Usage
-
-[TODO: 详细使用说明]
-
-## Examples
-
-[TODO: 补充具体示例]
-
-## Source Learning
-
-本技能由学习条目提炼生成。
-- Learning ID: [TODO: 填写原始学习条目 ID]
-- Original File: improvement/learnings.md
-TEMPLATE
-
-log_info "已创建: $SKILL_PATH/SKILL.md"
-
-# Suggest next steps
-echo ""
-log_info "技能脚手架创建成功!"
-echo ""
-echo "下一步建议:"
-echo " 1. 编辑 $SKILL_PATH/SKILL.md"
-echo " 2. 用你的学习内容填写 TODO 区块"
-echo " 3. 若有详细文档,新增 references/ 目录"
-echo " 4. 若有可执行脚本,新增 scripts/ 目录"
-echo " 5. 在原学习条目中更新:"
-echo " **Status**: promoted_to_skill"
-echo " **Skill-Path**: skills/$SKILL_NAME"
diff --git a/tests/test_mcp_adapter.py b/tests/test_mcp_adapter.py
index 267bb242..65bf44a7 100644
--- a/tests/test_mcp_adapter.py
+++ b/tests/test_mcp_adapter.py
@@ -155,6 +155,14 @@ class McpAdapterTests(unittest.TestCase):
self.assertIn("oclaw", parts)
self.assertIn("_local", parts)
+ def test_trilium_env_aliases_from_legacy_names(self) -> None:
+ from runtime.operations.mcp_env import _apply_trilium_env_aliases
+
+ vals = {"TRILIUM_URL": "http://127.0.0.1:37840/etapi", "TRILIUM_TOKEN": "secret"}
+ _apply_trilium_env_aliases(vals)
+ self.assertEqual(vals["TRILIUM_API_URL"], "http://127.0.0.1:37840/etapi")
+ self.assertEqual(vals["TRILIUM_API_TOKEN"], "secret")
+
def test_mcp_env_allowlist_keys_default_when_unset(self) -> None:
from runtime.operations import mcp_env