mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-13 01:03:17 +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/admin-theme.js"></script>
|
||||||
<script
|
<script
|
||||||
defer
|
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 可访问。';}})()"
|
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>
|
></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ const I18N = {
|
||||||
"theme.catppuccin": "Catppuccin Mocha",
|
"theme.catppuccin": "Catppuccin Mocha",
|
||||||
"theme.light": "浅色",
|
"theme.light": "浅色",
|
||||||
"lang.switch": "English",
|
"lang.switch": "English",
|
||||||
|
"chat.langSwitchedWhileStreaming": "语言已切换。当前会话仍在输出,刷新页面可更新全部界面文案。",
|
||||||
"chat.imageViewerClose": "关闭",
|
"chat.imageViewerClose": "关闭",
|
||||||
"chat.imageViewerHint": "点击查看大图,空白处或 Esc 关闭",
|
"chat.imageViewerHint": "点击查看大图,空白处或 Esc 关闭",
|
||||||
"chat.imageViewerDownload": "下载图片",
|
"chat.imageViewerDownload": "下载图片",
|
||||||
|
|
@ -256,6 +257,7 @@ const I18N = {
|
||||||
"theme.catppuccin": "Catppuccin Mocha",
|
"theme.catppuccin": "Catppuccin Mocha",
|
||||||
"theme.light": "Light",
|
"theme.light": "Light",
|
||||||
"lang.switch": "中文",
|
"lang.switch": "中文",
|
||||||
|
"chat.langSwitchedWhileStreaming": "Language updated. Session is still streaming; refresh the page to reload all labels.",
|
||||||
"chat.imageViewerClose": "Close",
|
"chat.imageViewerClose": "Close",
|
||||||
"chat.imageViewerHint": "Click image to enlarge; click outside or Esc to close",
|
"chat.imageViewerHint": "Click image to enlarge; click outside or Esc to close",
|
||||||
"chat.imageViewerDownload": "Download image",
|
"chat.imageViewerDownload": "Download image",
|
||||||
|
|
@ -873,8 +875,8 @@ async function openDispatchLabelsEditor(statusEl) {
|
||||||
? JSON.stringify(cfg.overrides, null, 2)
|
? JSON.stringify(cfg.overrides, null, 2)
|
||||||
: "";
|
: "";
|
||||||
const backdrop = el("div", {
|
const backdrop = el("div", {
|
||||||
style:
|
class: "chat-confirm-backdrop",
|
||||||
"position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:9999;display:flex;align-items:center;justify-content:center;padding:16px;",
|
style: "z-index:9999;",
|
||||||
});
|
});
|
||||||
const card = el("div", {
|
const card = el("div", {
|
||||||
class: "card",
|
class: "card",
|
||||||
|
|
@ -1180,6 +1182,57 @@ function closeChatImageLightbox() {
|
||||||
document.body.style.overflow = _chatLightboxPrevOverflow || "";
|
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) {
|
function openChatImageLightbox(src, alt) {
|
||||||
if (!src || !String(src).trim()) return;
|
if (!src || !String(src).trim()) return;
|
||||||
closeChatImageLightbox();
|
closeChatImageLightbox();
|
||||||
|
|
@ -2504,6 +2557,7 @@ async function appendMessageRow(messagesEl, m, options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mount(node) {
|
function mount(node) {
|
||||||
|
clearChatPageBlockers();
|
||||||
const c = document.getElementById("app");
|
const c = document.getElementById("app");
|
||||||
c.innerHTML = "";
|
c.innerHTML = "";
|
||||||
c.appendChild(node);
|
c.appendChild(node);
|
||||||
|
|
@ -2566,7 +2620,7 @@ function syncAuthUserLabel() {
|
||||||
title: t("chat.sessionMenu"),
|
title: t("chat.sessionMenu"),
|
||||||
onclick: (ev) => {
|
onclick: (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove());
|
clearChatPageBlockers();
|
||||||
const items = [
|
const items = [
|
||||||
el("button", {
|
el("button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
|
|
@ -2695,10 +2749,12 @@ function syncAuthUserLabel() {
|
||||||
text: t("auth.logout"),
|
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();
|
const rect = moreBtn.getBoundingClientRect();
|
||||||
menu.style.position = "fixed";
|
attachChatMenuDismiss(menu);
|
||||||
document.body.appendChild(menu);
|
|
||||||
const mrect = menu.getBoundingClientRect();
|
const mrect = menu.getBoundingClientRect();
|
||||||
const pad = 8;
|
const pad = 8;
|
||||||
let left = rect.left;
|
let left = rect.left;
|
||||||
|
|
@ -2710,13 +2766,6 @@ function syncAuthUserLabel() {
|
||||||
top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height));
|
top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height));
|
||||||
menu.style.left = `${left}px`;
|
menu.style.left = `${left}px`;
|
||||||
menu.style.top = `${top}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]);
|
const row = el("div", { class: "chat-user-row" }, [nameBtn, moreBtn]);
|
||||||
|
|
@ -3684,12 +3733,12 @@ async function renderChatUi() {
|
||||||
title: t("chat.sessionMenu"),
|
title: t("chat.sessionMenu"),
|
||||||
onclick: (ev) => {
|
onclick: (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove());
|
clearChatPageBlockers();
|
||||||
const menu = openSessionMenu(sid, title);
|
const menu = openSessionMenu(sid, title);
|
||||||
const rect = more.getBoundingClientRect();
|
|
||||||
menu.style.position = "fixed";
|
menu.style.position = "fixed";
|
||||||
document.body.appendChild(menu);
|
menu.style.zIndex = "300";
|
||||||
// Clamp into viewport; flip above if near bottom.
|
const rect = more.getBoundingClientRect();
|
||||||
|
attachChatMenuDismiss(menu);
|
||||||
const mrect = menu.getBoundingClientRect();
|
const mrect = menu.getBoundingClientRect();
|
||||||
const pad = 8;
|
const pad = 8;
|
||||||
let left = rect.left;
|
let left = rect.left;
|
||||||
|
|
@ -3701,13 +3750,6 @@ async function renderChatUi() {
|
||||||
top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height));
|
top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height));
|
||||||
menu.style.left = `${left}px`;
|
menu.style.left = `${left}px`;
|
||||||
menu.style.top = `${top}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);
|
row.appendChild(btn);
|
||||||
|
|
@ -5374,6 +5416,7 @@ async function syncLangFromServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function boot() {
|
async function boot() {
|
||||||
|
clearChatPageBlockers();
|
||||||
applyI18nStatic();
|
applyI18nStatic();
|
||||||
if (forceReloginRequested()) {
|
if (forceReloginRequested()) {
|
||||||
clearAuthAndReloginFlagFromUrl();
|
clearAuthAndReloginFlagFromUrl();
|
||||||
|
|
@ -5385,10 +5428,6 @@ async function boot() {
|
||||||
}
|
}
|
||||||
const tok = String(localStorage.getItem(AUTH_TOKEN_KEY) || "").trim();
|
const tok = String(localStorage.getItem(AUTH_TOKEN_KEY) || "").trim();
|
||||||
if (!authSession || !tok) {
|
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_TOKEN_KEY);
|
||||||
localStorage.removeItem(AUTH_SESSION_KEY);
|
localStorage.removeItem(AUTH_SESSION_KEY);
|
||||||
authSession = null;
|
authSession = null;
|
||||||
|
|
@ -5446,7 +5485,14 @@ document.body.addEventListener("click", async (e) => {
|
||||||
try {
|
try {
|
||||||
await apiPost("/admin/api/chat/settings/ui-lang", { lang: currentLang });
|
await apiPost("/admin/api/chat/settings/ui-lang", { lang: currentLang });
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
await boot();
|
clearChatPageBlockers();
|
||||||
|
if (isChatStreaming()) {
|
||||||
|
applyI18nStatic();
|
||||||
|
syncAuthUserLabel();
|
||||||
|
showToast(t("chat.langSwitchedWhileStreaming"), { kind: "info", ttlMs: 6500 });
|
||||||
|
} else {
|
||||||
|
await boot();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5457,6 +5503,14 @@ window.addEventListener("popstate", () => {
|
||||||
if (authSession) boot().catch(() => {});
|
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) => {
|
boot().catch((err) => {
|
||||||
mount(
|
mount(
|
||||||
el("div", { class: "chat-app--login" }, [
|
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]:
|
def mcp_local_env_merged() -> dict[str, str]:
|
||||||
out: dict[str, str] = {}
|
out: dict[str, str] = {}
|
||||||
for p in _mcp_local_env_paths_in_load_order():
|
for p in _mcp_local_env_paths_in_load_order():
|
||||||
if p.is_file():
|
if p.is_file():
|
||||||
out.update(_parse_env_file(p))
|
out.update(_parse_env_file(p))
|
||||||
|
_apply_trilium_env_aliases(out)
|
||||||
return 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()
|
has_aia = str(os.getenv("AIA_MCP_ENV_ALLOWLIST") or "").strip()
|
||||||
if not has_aia:
|
if not has_aia:
|
||||||
extra["AIA_MCP_ENV_ALLOWLIST"] = _DEFAULT_ALLOWLIST
|
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():
|
for k, v in file_vals.items():
|
||||||
if not v.strip():
|
if not v.strip():
|
||||||
continue
|
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
|
||||||
|
|
||||||
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
|
Based on the original [pskoett/pskoett-ai-skills](https://github.com/pskoett/pskoett-ai-skills) — adapted for Oclaw with `memory_wiki_*` tools.
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
|
||||||
|
|
@ -1,83 +1,91 @@
|
||||||
---
|
# self-improvement — 自我改进 (Self-Improvement)
|
||||||
name: self-improvement
|
|
||||||
description: 将纠错、错误与能力缺口写入 Wiki 以持续改进。适用于操作失败、用户反馈纠正、问题复发、或需要沉淀并推动的新能力需求场景。
|
> 将纠错、错误与能力缺口写入 Wiki 以持续改进,形成"犯错→记录→不再犯"的闭环。
|
||||||
metadata:
|
> 本技能完全基于 `memory_wiki_*` 工具,不依赖本地文件或外部脚本。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 自我改进(仅 Wiki)
|
## 触发条件 (Triggers)
|
||||||
|
|
||||||
本技能仅使用 Wiki,不使用本地 `.learnings/` 文件。
|
出现以下情况时启用:
|
||||||
|
|
||||||
## 存储路径
|
1. 命令或操作出现非预期失败
|
||||||
|
2. 用户对回答进行纠正
|
||||||
|
3. 用户提出缺失能力需求
|
||||||
|
4. 同类问题再次复发
|
||||||
|
5. 发现更优且可复用的方法
|
||||||
|
6. 每轮对话结束时(强制三问反思)
|
||||||
|
|
||||||
- `improvement/learnings.md`
|
## 必要流程 (Required Workflow)
|
||||||
- `improvement/errors.md`
|
|
||||||
- `improvement/feature-requests.md`
|
|
||||||
|
|
||||||
## 触发条件
|
```
|
||||||
|
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. 用户提出缺失能力需求。
|
| 纠错/洞见/最佳实践 | `improvement/learnings.md` | 用户纠正了某个认知错误 |
|
||||||
4. 同类问题再次复发。
|
| 运行时/工具/API 失败 | `improvement/errors.md` | 某个工具调用报错 |
|
||||||
5. 发现更优且可复用的方法。
|
| 能力缺口/功能请求 | `improvement/feature-requests.md` | 用户问"你能做 X 吗?" |
|
||||||
|
| 长篇经验沉淀 (>300 字) | `improvement/learnings/<topic>.md` | 一次完整的技能安装流程 |
|
||||||
|
|
||||||
## 必要流程
|
## 条目模板 (Entry Template)
|
||||||
|
|
||||||
每次触发都执行以下流程:
|
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
||||||
## 条目模板
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## [ID] <标题>
|
## [ID] 标题 (Title)
|
||||||
**Logged**: ISO-8601 时间戳
|
**Logged**: ISO-8601 时间戳 (Timestamp)
|
||||||
**Priority**: low | medium | high | critical
|
**Priority**: low | medium | high | critical
|
||||||
**Status**: pending
|
**Status**: pending | resolved | promoted_to_skill
|
||||||
**Area**: frontend | backend | infra | tests | docs | config
|
**Area**: frontend | backend | infra | tests | docs | config
|
||||||
|
|
||||||
### Summary
|
### 摘要 (Summary)
|
||||||
一句话摘要。
|
一句话摘要。
|
||||||
|
|
||||||
### Details
|
### 详情 (Details)
|
||||||
发生了什么、为什么重要、可执行改进建议。
|
发生了什么、为什么重要、可执行改进建议。
|
||||||
|
|
||||||
### Metadata
|
### 元数据 (Metadata)
|
||||||
- Source: conversation | error | user_feedback
|
- Source: conversation | error | user_feedback | reflection
|
||||||
- Related Files: path/to/file.ext
|
- Related Files: path/to/file.ext
|
||||||
- See Also: <optional-id>
|
- See Also: <optional-id>
|
||||||
```
|
```
|
||||||
|
|
||||||
ID 格式:
|
### ID 格式 (ID Format)
|
||||||
|
|
||||||
- Learning: `LRN-YYYYMMDD-XXX`
|
- Learning: `LRN-YYYYMMDD-XXX`
|
||||||
- Error: `ERR-YYYYMMDD-XXX`
|
- Error: `ERR-YYYYMMDD-XXX`
|
||||||
- Feature request: `FEAT-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("oclaw", parts)
|
||||||
self.assertIn("_local", 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:
|
def test_mcp_env_allowlist_keys_default_when_unset(self) -> None:
|
||||||
from runtime.operations import mcp_env
|
from runtime.operations import mcp_env
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue