mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-08 23:33:16 +08:00
feat: 更新 knowledge-base-manager skill,添加完整 ID 映射和严格目录限制
This commit is contained in:
parent
acfb2cf73b
commit
807679aaaa
21 changed files with 753 additions and 1580 deletions
|
|
@ -574,7 +574,7 @@
|
|||
<script defer src="/admin/assets/admin-theme.js"></script>
|
||||
<script
|
||||
defer
|
||||
src="/admin/assets/chat.js?v=20260511-2"
|
||||
src="/admin/assets/chat.js?v=20260515-2"
|
||||
onerror="(function(){var s=document.getElementById('chat-boot-splash');if(s){s.querySelector('.chat-boot-splash__title span:last-child').textContent='无法加载 chat.js';s.querySelector('.chat-boot-splash__muted').textContent='请确认网关已启动且 /admin/assets/chat.js 可访问。';}})()"
|
||||
></script>
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -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" }, [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
450
skills/_workspace/generalist/knowledge-base-manager/SKILL.md
Normal file
450
skills/_workspace/generalist/knowledge-base-manager/SKILL.md
Normal file
|
|
@ -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 (带目录限制)
|
||||
**⚠️ 重要**: 严格遵守目录限制,禁止在非授权位置创建笔记!
|
||||
128
skills/playwright-cli/SKILL.md
Normal file
128
skills/playwright-cli/SKILL.md
Normal file
|
|
@ -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`延长超时时间
|
||||
6
skills/playwright-cli/_meta.json
Normal file
6
skills/playwright-cli/_meta.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"ownerId": "kn7ez7a31k0sqt7ad9wk2hq31n82qea1",
|
||||
"slug": "playwright-cli-openclaw",
|
||||
"version": "1.0.0",
|
||||
"publishedAt": 1773287492717
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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/<topic>.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: <optional-id>
|
||||
```
|
||||
|
||||
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)
|
||||
|
||||
- 不记录密钥、令牌、凭据或原始敏感信息
|
||||
- 敏感输出使用脱敏摘要,不保留完整原文
|
||||
- 未验证事实不得当作已确认规则持久化
|
||||
- 涉及用户偏好写入时需有明确的对话证据,不推测
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
# Errors Log
|
||||
|
||||
Command failures, exceptions, and unexpected behaviors.
|
||||
|
||||
---
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# Feature Requests
|
||||
|
||||
Capabilities requested by user that don't currently exist.
|
||||
|
||||
---
|
||||
|
|
@ -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
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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 `<self-improvement-reminder>` 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 `<error-detected>` 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
|
||||
|
|
@ -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-name>/
|
||||
│ └── SKILL.md
|
||||
└── hooks/ # Custom hooks
|
||||
└── <hook-name>/
|
||||
├── 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
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/bash
|
||||
# 自我改进激活 Hook
|
||||
# 在 UserPromptSubmit 触发,用于提醒记录学习沉淀
|
||||
# 输出保持精简(约 50-100 tokens)以降低上下文负担
|
||||
|
||||
set -e
|
||||
|
||||
# 以系统上下文形式输出提醒
|
||||
cat << 'EOF'
|
||||
<self-improvement-reminder>
|
||||
本任务完成后,请判断是否产出可沉淀知识:
|
||||
- 是否通过排查得到非显而易见的解法?
|
||||
- 是否形成了异常行为的可复用绕过方案?
|
||||
- 是否识别出项目特有模式?
|
||||
- 是否有需要调试才能解决的错误?
|
||||
|
||||
若是,请写入 Wiki:
|
||||
- improvement/learnings.md
|
||||
- improvement/errors.md
|
||||
- improvement/feature-requests.md
|
||||
若价值较高(复发、可广泛复用),请考虑提炼为独立技能。
|
||||
</self-improvement-reminder>
|
||||
EOF
|
||||
|
|
@ -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'
|
||||
<error-detected>
|
||||
检测到命令错误。若满足以下任一条件,请记录到 improvement/errors.md:
|
||||
- 错误出乎预期或并不直观
|
||||
- 需要排查才能解决
|
||||
- 可能在相似场景复发
|
||||
- 解决方案对后续会话有复用价值
|
||||
|
||||
记录时请使用 self-improvement 技能格式:[ERR-YYYYMMDD-XXX]
|
||||
</error-detected>
|
||||
EOF
|
||||
fi
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Skill Extraction Helper
|
||||
# Creates a new skill from a learning entry
|
||||
# 用法: ./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
|
||||
用法: $(basename "$0") <skill-name> [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/<skill-name>/
|
||||
EOF
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[信息]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[警告]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[错误]${NC} $1" >&2
|
||||
}
|
||||
|
||||
# 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"
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue