From 807679aaaa64f4e53da7907ce8afa8bfc5112cbe Mon Sep 17 00:00:00 2001 From: oliver Date: Sat, 16 May 2026 22:27:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20knowledge-base-man?= =?UTF-8?q?ager=20skill=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=AE=8C=E6=95=B4=20ID?= =?UTF-8?q?=20=E6=98=A0=E5=B0=84=E5=92=8C=E4=B8=A5=E6=A0=BC=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interfaces/admin/static/chat.html | 2 +- interfaces/admin/static/chat.js | 112 +++-- runtime/operations/mcp_env.py | 16 +- .../knowledge-base-manager/SKILL.md | 450 ++++++++++++++++++ skills/playwright-cli/SKILL.md | 128 +++++ skills/playwright-cli/_meta.json | 6 + skills/self-improvement/README.md | 13 +- skills/self-improvement/SKILL.md | 112 +++-- skills/self-improvement/assets/ERRORS.md | 5 - .../assets/FEATURE_REQUESTS.md | 5 - skills/self-improvement/assets/LEARNINGS.md | 45 -- .../self-improvement/assets/SKILL-TEMPLATE.md | 177 ------- skills/self-improvement/hooks/runtime/HOOK.md | 23 - .../self-improvement/hooks/runtime/handler.py | 85 ---- .../self-improvement/references/examples.md | 374 --------------- .../references/hooks-setup.md | 225 --------- .../references/oclaw-integration.md | 248 ---------- skills/self-improvement/scripts/activator.sh | 23 - .../scripts/error-detector.sh | 55 --- .../self-improvement/scripts/extract-skill.sh | 221 --------- tests/test_mcp_adapter.py | 8 + 21 files changed, 753 insertions(+), 1580 deletions(-) create mode 100644 skills/_workspace/generalist/knowledge-base-manager/SKILL.md create mode 100644 skills/playwright-cli/SKILL.md create mode 100644 skills/playwright-cli/_meta.json delete mode 100644 skills/self-improvement/assets/ERRORS.md delete mode 100644 skills/self-improvement/assets/FEATURE_REQUESTS.md delete mode 100644 skills/self-improvement/assets/LEARNINGS.md delete mode 100644 skills/self-improvement/assets/SKILL-TEMPLATE.md delete mode 100644 skills/self-improvement/hooks/runtime/HOOK.md delete mode 100644 skills/self-improvement/hooks/runtime/handler.py delete mode 100644 skills/self-improvement/references/examples.md delete mode 100644 skills/self-improvement/references/hooks-setup.md delete mode 100644 skills/self-improvement/references/oclaw-integration.md delete mode 100644 skills/self-improvement/scripts/activator.sh delete mode 100644 skills/self-improvement/scripts/error-detector.sh delete mode 100644 skills/self-improvement/scripts/extract-skill.sh diff --git a/interfaces/admin/static/chat.html b/interfaces/admin/static/chat.html index a6c2d4be..e682796c 100644 --- a/interfaces/admin/static/chat.html +++ b/interfaces/admin/static/chat.html @@ -574,7 +574,7 @@ diff --git a/interfaces/admin/static/chat.js b/interfaces/admin/static/chat.js index 8808b99f..4d8d75b1 100644 --- a/interfaces/admin/static/chat.js +++ b/interfaces/admin/static/chat.js @@ -77,6 +77,7 @@ const I18N = { "theme.catppuccin": "Catppuccin Mocha", "theme.light": "浅色", "lang.switch": "English", + "chat.langSwitchedWhileStreaming": "语言已切换。当前会话仍在输出,刷新页面可更新全部界面文案。", "chat.imageViewerClose": "关闭", "chat.imageViewerHint": "点击查看大图,空白处或 Esc 关闭", "chat.imageViewerDownload": "下载图片", @@ -256,6 +257,7 @@ const I18N = { "theme.catppuccin": "Catppuccin Mocha", "theme.light": "Light", "lang.switch": "中文", + "chat.langSwitchedWhileStreaming": "Language updated. Session is still streaming; refresh the page to reload all labels.", "chat.imageViewerClose": "Close", "chat.imageViewerHint": "Click image to enlarge; click outside or Esc to close", "chat.imageViewerDownload": "Download image", @@ -873,8 +875,8 @@ async function openDispatchLabelsEditor(statusEl) { ? JSON.stringify(cfg.overrides, null, 2) : ""; const backdrop = el("div", { - style: - "position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:9999;display:flex;align-items:center;justify-content:center;padding:16px;", + class: "chat-confirm-backdrop", + style: "z-index:9999;", }); const card = el("div", { class: "card", @@ -1180,6 +1182,57 @@ function closeChatImageLightbox() { document.body.style.overflow = _chatLightboxPrevOverflow || ""; } +function dismissChatMenus() { + document.querySelectorAll(".chat-sess-menu-pop, .chat-menu-scrim").forEach((n) => { + try { + n.remove(); + } catch (_) {} + }); +} + +function isChatStreaming() { + return !!document.querySelector(".chat-composer-shell--busy"); +} + +/** Remove full-screen layers on ``document.body`` that survive ``#app`` remounts (boot/lang/popstate). */ +function clearChatPageBlockers() { + closeChatImageLightbox(); + dismissChatMenus(); + document.body.style.overflow = ""; + document.querySelectorAll(".chat-confirm-backdrop").forEach((n) => { + try { + n.remove(); + } catch (_) {} + }); +} + +function attachChatMenuDismiss(menu) { + const scrim = el("div", { + class: "chat-menu-scrim", + style: "position:fixed;inset:0;z-index:250;background:transparent;", + }); + const closeAll = () => { + dismissChatMenus(); + document.removeEventListener("click", onDocClick, true); + document.removeEventListener("keydown", onKey, true); + }; + const onDocClick = (e) => { + if (menu.contains(e.target)) return; + closeAll(); + }; + const onKey = (e) => { + if (e.key === "Escape") closeAll(); + }; + scrim.addEventListener("click", closeAll); + document.body.appendChild(scrim); + document.body.appendChild(menu); + setTimeout(() => { + document.addEventListener("click", onDocClick, true); + document.addEventListener("keydown", onKey, true); + }, 0); + return closeAll; +} + function openChatImageLightbox(src, alt) { if (!src || !String(src).trim()) return; closeChatImageLightbox(); @@ -2504,6 +2557,7 @@ async function appendMessageRow(messagesEl, m, options = {}) { } function mount(node) { + clearChatPageBlockers(); const c = document.getElementById("app"); c.innerHTML = ""; c.appendChild(node); @@ -2566,7 +2620,7 @@ function syncAuthUserLabel() { title: t("chat.sessionMenu"), onclick: (ev) => { ev.stopPropagation(); - document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove()); + clearChatPageBlockers(); const items = [ el("button", { type: "button", @@ -2695,10 +2749,12 @@ function syncAuthUserLabel() { text: t("auth.logout"), }), ); - const menu = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;min-width:220px;" }, items); + const menu = el("div", { + class: "chat-sess-menu-pop", + style: "position:fixed;min-width:220px;z-index:300;", + }, items); const rect = moreBtn.getBoundingClientRect(); - menu.style.position = "fixed"; - document.body.appendChild(menu); + attachChatMenuDismiss(menu); const mrect = menu.getBoundingClientRect(); const pad = 8; let left = rect.left; @@ -2710,13 +2766,6 @@ function syncAuthUserLabel() { top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height)); menu.style.left = `${left}px`; menu.style.top = `${top}px`; - const close = (e) => { - if (!menu.contains(e.target)) { - menu.remove(); - document.removeEventListener("click", close); - } - }; - setTimeout(() => document.addEventListener("click", close), 0); }, }); const row = el("div", { class: "chat-user-row" }, [nameBtn, moreBtn]); @@ -3684,12 +3733,12 @@ async function renderChatUi() { title: t("chat.sessionMenu"), onclick: (ev) => { ev.stopPropagation(); - document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove()); + clearChatPageBlockers(); const menu = openSessionMenu(sid, title); - const rect = more.getBoundingClientRect(); menu.style.position = "fixed"; - document.body.appendChild(menu); - // Clamp into viewport; flip above if near bottom. + menu.style.zIndex = "300"; + const rect = more.getBoundingClientRect(); + attachChatMenuDismiss(menu); const mrect = menu.getBoundingClientRect(); const pad = 8; let left = rect.left; @@ -3701,13 +3750,6 @@ async function renderChatUi() { top = Math.max(pad, Math.min(top, window.innerHeight - pad - mrect.height)); menu.style.left = `${left}px`; menu.style.top = `${top}px`; - const close = (e) => { - if (!menu.contains(e.target)) { - menu.remove(); - document.removeEventListener("click", close); - } - }; - setTimeout(() => document.addEventListener("click", close), 0); }, }); row.appendChild(btn); @@ -5374,6 +5416,7 @@ async function syncLangFromServer() { } async function boot() { + clearChatPageBlockers(); applyI18nStatic(); if (forceReloginRequested()) { clearAuthAndReloginFlagFromUrl(); @@ -5385,10 +5428,6 @@ async function boot() { } const tok = String(localStorage.getItem(AUTH_TOKEN_KEY) || "").trim(); if (!authSession || !tok) { - // Ensure stale overlays from previous chat UI never block login inputs. - closeChatImageLightbox(); - document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove()); - document.body.style.overflow = ""; localStorage.removeItem(AUTH_TOKEN_KEY); localStorage.removeItem(AUTH_SESSION_KEY); authSession = null; @@ -5446,7 +5485,14 @@ document.body.addEventListener("click", async (e) => { try { await apiPost("/admin/api/chat/settings/ui-lang", { lang: currentLang }); } catch (_) {} - await boot(); + clearChatPageBlockers(); + if (isChatStreaming()) { + applyI18nStatic(); + syncAuthUserLabel(); + showToast(t("chat.langSwitchedWhileStreaming"), { kind: "info", ttlMs: 6500 }); + } else { + await boot(); + } return; } } @@ -5457,6 +5503,14 @@ window.addEventListener("popstate", () => { if (authSession) boot().catch(() => {}); }); +document.addEventListener("keydown", (ev) => { + if (ev.key !== "Escape" || ev.defaultPrevented) return; + if (!document.querySelector(".chat-confirm-backdrop, .chat-sess-menu-pop, .chat-img-lightbox, .chat-menu-scrim")) { + return; + } + clearChatPageBlockers(); +}); + boot().catch((err) => { mount( el("div", { class: "chat-app--login" }, [ diff --git a/runtime/operations/mcp_env.py b/runtime/operations/mcp_env.py index 4ea8face..6fb9936a 100644 --- a/runtime/operations/mcp_env.py +++ b/runtime/operations/mcp_env.py @@ -85,11 +85,24 @@ def _mcp_local_env_paths_in_load_order() -> list[Path]: ] +def _apply_trilium_env_aliases(vals: dict[str, str]) -> None: + """Map legacy TRILIUM_URL / TRILIUM_TOKEN to names expected by triliumnext-mcp.""" + if not str(vals.get("TRILIUM_API_URL") or "").strip(): + legacy = str(vals.get("TRILIUM_URL") or "").strip() + if legacy: + vals["TRILIUM_API_URL"] = legacy + if not str(vals.get("TRILIUM_API_TOKEN") or "").strip(): + legacy = str(vals.get("TRILIUM_TOKEN") or "").strip() + if legacy: + vals["TRILIUM_API_TOKEN"] = legacy + + def mcp_local_env_merged() -> dict[str, str]: out: dict[str, str] = {} for p in _mcp_local_env_paths_in_load_order(): if p.is_file(): out.update(_parse_env_file(p)) + _apply_trilium_env_aliases(out) return out @@ -102,7 +115,8 @@ def gateway_mcp_env_extras() -> dict[str, str]: has_aia = str(os.getenv("AIA_MCP_ENV_ALLOWLIST") or "").strip() if not has_aia: extra["AIA_MCP_ENV_ALLOWLIST"] = _DEFAULT_ALLOWLIST - file_vals = mcp_local_env_merged() + file_vals = dict(mcp_local_env_merged()) + _apply_trilium_env_aliases(file_vals) for k, v in file_vals.items(): if not v.strip(): continue diff --git a/skills/_workspace/generalist/knowledge-base-manager/SKILL.md b/skills/_workspace/generalist/knowledge-base-manager/SKILL.md new file mode 100644 index 00000000..e344a90b --- /dev/null +++ b/skills/_workspace/generalist/knowledge-base-manager/SKILL.md @@ -0,0 +1,450 @@ +--- +name: knowledge-base-manager +description: 智能知识库管理助手,自动将新信息归类到正确的 PARA 分类(Projects/Areas/Resources/Archives),确保笔记结构规范化。带严格目录限制和完整 ID 映射。 +user-invocable: true +disable-model-invocation: false +metadata: {"oclaw": {}} +--- + +# knowledge-base-manager + +**Description**: 智能知识库管理助手,自动将新信息归类到正确的 PARA 分类(Projects/Areas/Resources/Archives),确保笔记结构规范化。 + +**Version**: 1.1 (带目录限制) +**Author**: Oliver +**Created**: 2026-04-11 +**Last Updated**: 2026-04-11 + +--- + +## ⚠️ 核心约束(必须遵守) + +### 🚫 禁止操作 +1. **严禁移动现有笔记** - 除非用户明确要求"移动 XXX 到 YYY" +2. **严禁删除任何笔记** - 除非用户明确要求"删除 XXX" +3. **严禁修改非授权目录** - 只能在以下允许的子目录下创建笔记 +4. **严禁在根目录或其他区域创建笔记** - 必须遵循目录结构 + +### ✅ 允许操作的目录 + +**只能**在以下父节点的**子目录**下创建新笔记: + +| 允许创建的父节点 | Note ID | 用途 | +|-----------------|---------|------| +| `00-Inbox` | `h9skuCU9czep` | 临时收集箱(默认) | +| `01-Projects` | `8C1UOi0hfib5` | 项目相关笔记 | +| `02-Areas/基础设施` | `pS6clLY4g5sd` | 基础设施领域 | +| `02-Areas/开发环境` | `b7lkhRneQk2a` | 开发环境领域 | +| `03-Resources` | `1AbShK4WDw38` | 参考资源 | +| `04-Archives` | `6rQ5acaYa7fZ` | 归档内容 | +| `99-Templates` | `RVjlkQmiHxaw` | 模板笔记 | + +**重要**: +- 创建新笔记时,`parentNoteId` **必须**是上述 ID 之一或其子节点 +- 如果用户要求的位置不在上述列表中,**必须拒绝**并提示用户 +- 不确定时,默认使用 `h9skuCU9czep` (Inbox) + +--- + +## 📋 完整笔记 ID 映射表 + +### 🌟 知识库总览 +``` +🌟 知识库总览 → XTNCDr5hq9bG +📖 知识库使用指南 → hgILpr4yRkcS +📝 笔记记录规范 → [见使用指南子节点] +✅ 迁移完成报告 → [见使用指南子节点] +🚀 快速记录指南 → U4FnimoXwG1j +📋 ID 速查表 → Aa2aRAGLWuu4 +``` + +### 📥 Inbox (临时收集箱) +``` +00-Inbox → h9skuCU9czep ← 默认存放位置 +``` + +### 🚀 Projects (进行中项目) +``` +01-Projects → 8C1UOi0hfib5 +├── Oclaw 项目 → [待确认] +├── NETX 项目 → [待确认] +└── 计划 → THJbELUQ2Ecm +``` + +### 🏗️ Areas (持续责任) +``` +02-Areas → HFsgsHfH4nKN +├── 基础设施 → pS6clLY4g5sd +│ ├── QNAP-NAS → 6CC2lab7vPDA +│ └── QNAP → 9TRXB88ODggT +└── 开发环境 → b7lkhRneQk2a + ├── API 密钥管理 → [待确认] + ├── PG 数据库 → zdscNfnBnoqq + ├── 启动命令 → 1tCnm2jbiJPW + └── skill → JRYSKzNWQaCp +``` + +### 📚 Resources (参考资源) +``` +03-Resources → 1AbShK4WDw38 +├── Python → M5wP7D0FZ819 +├── PostgreSQL → NZS3aOMlNJcp +├── Skills 开发指南 → yPold3qhllN1 +│ └── Note-Taking Skill → TNyFX4lADEk5 +├── Trilium Next 使用指南 → 2s26WHNqv0Q8 +└── 学习 → l6OfBhJ804qL +``` + +### 🗄️ Archives (归档) +``` +04-Archives → 6rQ5acaYa7fZ +├── API (空) → MLXD4mGL5mzh +└── 生活 → Gvn9zydADEYl +``` + +### 📝 Templates (模板) +``` +99-Templates → RVjlkQmiHxaw +``` + +--- + +## 🎯 功能说明 + +本 Skill 帮助用户在记录信息时自动判断最佳存放位置,遵循 PARA 知识管理方法,并**严格遵守目录限制**。 + +### 核心能力 +1. **智能分类** - 根据内容自动推荐 Projects/Areas/Resources/Archives +2. **快速创建** - 一键在正确位置创建结构化笔记 +3. **目录验证** - 强制验证父节点是否在允许列表中 +4. **Inbox 管理** - 临时存储 + 定期整理提醒 +5. **标签建议** - 自动添加合适的状态/优先级/类型标签 +6. **关联推荐** - 推荐相关笔记建立链接 + +--- + +## 📋 触发条件 + +当用户提到以下关键词时触发: +- "记一下"、"记录"、"保存"、"添加笔记" +- "新建笔记"、"创建文档"、"写个笔记" +- "放在哪里"、"归到哪类"、"怎么分类" +- "inbox"、"整理笔记"、"归档" +- "项目笔记"、"技术文档"、"参考资料" + +--- + +## 🔄 工作流程 + +### 1. 接收信息 +用户提供要记录的内容或想法 + +### 2. 分析分类 +根据内容特征判断所属类别: + +| 特征 | 分类 | 示例 | +|------|------|------| +| 有明确目标/截止日期 | **Projects** | "Oclaw 新功能开发计划" | +| 持续责任/维护领域 | **Areas** | "NAS 备份策略"、"API 密钥更新" | +| 知识点/参考资料 | **Resources** | "Python 异步编程笔记"、"PostgreSQL 优化技巧" | +| 已完成/过时内容 | **Archives** | "2023 年项目总结" | +| 不确定/临时信息 | **Inbox** | "稍后整理的会议记录" | + +### 3. 目录验证(关键步骤) +```javascript +// 验证父节点是否在允许列表中 +const allowedParents = [ + 'h9skuCU9czep', // Inbox + '8C1UOi0hfib5', // Projects + 'pS6clLY4g5sd', // Areas/基础设施 + 'b7lkhRneQk2a', // Areas/开发环境 + '1AbShK4WDw38', // Resources + '6rQ5acaYa7fZ', // Archives + 'RVjlkQmiHxaw' // Templates +]; + +if (!allowedParents.includes(parentNoteId)) { + throw new Error("❌ 无法在指定位置创建笔记"); +} +``` + +### 4. 创建笔记 +- 在确定的父节点下创建新笔记 +- 使用描述性标题 +- 应用标准模板(如适用) +- 添加基础标签 + +### 5. 确认反馈 +向用户展示: +- ✅ 已创建笔记的位置 +- 📝 笔记标题和预览 +- 🏷️ 应用的标签 +- 🔗 推荐的相关笔记 +- ⚠️ 如有目录限制问题,说明原因 + +--- + +## 📂 分类决策树 + +``` +用户提供信息 + ↓ +是否有明确的项目目标? +├─ 是 → Projects/[项目名称] (8C1UOi0hfib5) +│ └─ 如果项目不存在 → 先询问是否创建新项目 +└─ 否 → 是否是持续责任领域? + ├─ 是 → Areas/[领域名称] + │ ├─ 基础设施 → pS6clLY4g5sd + │ └─ 开发环境 → b7lkhRneQk2a + └─ 否 → 是否是参考知识? + ├─ 是 → Resources/[主题] (1AbShK4WDw38) + │ └─ 技术栈、教程、文档、最佳实践 + └─ 否 → 是否已过时? + ├─ 是 → Archives/ (6rQ5acaYa7fZ) + └─ 否 → Inbox/ (h9skuCU9czep) ← 默认 +``` + +--- + +## 🏷️ 标签系统 + +### 自动应用的标签 + +#### 状态标签 +- `#status/active` - 进行中项目/任务 +- `#status/pending` - 待处理事项 +- `#status/completed` - 已完成 +- `#status/archived` - 已归档 + +#### 优先级标签 +- `#priority/high` - 高优先级(包含"紧急"、"重要"、"尽快"等词) +- `#priority/medium` - 中优先级(默认) +- `#priority/low` - 低优先级(包含"有空时"、"参考"等词) + +#### 类型标签 +- `#type/note` - 普通笔记 +- `#type/task` - 任务清单(包含"待办"、"要做"等词) +- `#type/reference` - 参考资料 +- `#type/meeting` - 会议记录 +- `#type/idea` - 想法/灵感 + +#### 领域标签 +- `#domain/tech` - 技术相关(代码、工具、框架等) +- `#domain/business` - 业务相关 +- `#domain/personal` - 个人生活 +- `#domain/learning` - 学习内容 + +--- + +## 💬 使用示例 + +### 示例 1: 记录项目任务 +**用户**: "记一下,明天要完成 Oclaw 的搜索功能优化" + +**助手**: +``` +✅ 已创建笔记 + +📍 位置:Projects/Oclaw 项目/搜索功能优化 +🏷️ 标签:#status/active #priority/high #type/task #domain/tech +📅 截止:明天 + +是否需要设置提醒或添加到待办清单? +``` + +### 示例 2: 保存技术知识 +**用户**: "记录一个 PostgreSQL 的性能优化技巧:使用 EXPLAIN ANALYZE 分析慢查询" + +**助手**: +``` +✅ 已创建笔记 + +📍 位置:Resources/PostgreSQL/性能优化技巧 +🏷️ 标签:#type/reference #domain/tech #database +🔗 相关:PostgreSQL 使用指南 + +需要补充更多细节或示例吗? +``` + +### 示例 3: 临时想法 +**用户**: "先记下来,周末再整理:关于技能市场化的想法" + +**助手**: +``` +✅ 已放入 Inbox + +📍 位置:00-Inbox/技能市场化想法 +🏷️ 标签:#status/pending #type/idea #domain/business +⏰ 提醒:周末整理 + +需要在周末提醒你整理这条笔记吗? +``` + +### 示例 4: 尝试在非授权目录创建(应拒绝) +**用户**: "把这个笔记放到根目录下" + +**助手**: +``` +❌ 抱歉,我无法在指定位置创建笔记。 + +⚠️ 目录限制规则: +为确保知识库结构稳定,我只能在规定目录下创建新笔记。 + +✅ 允许的目录: +- Inbox (h9skuCU9czep) - 临时收集 +- Projects (8C1UOi0hfib5) - 项目相关 +- Areas/基础设施 (pS6clLY4g5sd) +- Areas/开发环境 (b7lkhRneQk2a) +- Resources (1AbShK4WDw38) - 参考资料 +- Archives (6rQ5acaYa7fZ) - 归档内容 +- Templates (RVjlkQmiHxaw) - 模板 + +💡 建议:先放入 Inbox,之后可以手动移动。 + +是否要放入 Inbox?[是] [取消] +``` + +### 示例 5: 询问分类 +**用户**: "这个 NAS 维护文档应该放在哪里?" + +**助手**: +``` +📂 建议分类:Areas/基础设施/QNAP-NAS + +理由: +- NAS 维护是持续责任领域(Area) +- 属于基础设施范畴 +- 已有 QNAP-NAS 子分类 +- 父节点 ID: pS6clLY4g5sd + +是否立即在此位置创建笔记? +``` + +--- + +## 🛠️ 工具调用 + +### Trilium Next API +- `create_note` - 创建新笔记(必须先验证父节点) +- `move_note` - 移动笔记到正确位置(仅当用户明确要求) +- `search_notes` - 查找相关笔记建立关联 +- `read_attributes` - 读取现有标签 +- `manage_attributes` - 添加/更新标签 + +### 可选集成 +- Todo 系统 - 同步任务类笔记 +- 日历系统 - 设置时间相关提醒 +- Git - 备份重要笔记 + +--- + +## 📊 统计与报告 + +### 每周自动生成 +- 新增笔记数量按分类统计 +- Inbox 整理情况 +- 最活跃的项目/领域 +- 标签使用分布 + +### 每月回顾 +- 完成项目归档 +- 过时资源清理 +- 分类体系优化建议 + +--- + +## ⚙️ 配置选项 + +### 用户偏好 +```yaml +default_inbox_review: weekly # Inbox 整理频率 +auto_tag_priority: true # 自动识别优先级 +suggest_related: true # 推荐相关笔记 +backup_enabled: true # 启用 Git 备份 +``` + +### 目录限制(强制) +```yaml +directory_restrictions: + enabled: true # ⚠️ 必须为 true + allowed_parents: + - h9skuCU9czep # Inbox + - 8C1UOi0hfib5 # Projects + - pS6clLY4g5sd # Areas/基础设施 + - b7lkhRneQk2a # Areas/开发环境 + - 1AbShK4WDw38 # Resources + - 6rQ5acaYa7fZ # Archives + - RVjlkQmiHxaw # Templates + default_fallback: h9skuCU9czep # 不确定时默认放入 Inbox + strict_mode: true # 严格模式,禁止任何越界操作 +``` + +### 自定义分类 +用户可以扩展默认分类: +- 新增 Projects +- 自定义 Areas +- 扩展 Resources 子分类 + +--- + +## 🚨 错误处理 + +### 常见问题 + +**Q: 找不到合适的分类?** +A: 默认放入 Inbox (`h9skuCU9czep`),并提示用户手动选择 + +**Q: 笔记标题重复?** +A: 自动添加时间戳或序号区分 + +**Q: 父节点不存在?** +A: 询问用户是否创建新分类或直接放入 Inbox + +**Q: 尝试在非授权目录创建?** +A: **拒绝**并显示允许目录列表,建议使用 Inbox + +--- + +## 📈 最佳实践 + +### 给用户的建议 +1. **及时记录** - 想法出现时立即记下 +2. **信任系统** - 让 Skill 自动分类,事后可以调整 +3. **定期整理** - 每周花 10 分钟整理 Inbox +4. **善用标签** - 不要过度标签化(3-5 个为宜) +5. **建立关联** - 主动链接相关笔记 + +### Skill 行为准则 +1. **优先确认** - 不确定时询问用户而非猜测 +2. **保持一致** - 同类内容使用相同分类逻辑 +3. **最小干扰** - 快速创建,减少打断 +4. **主动提醒** - 定期提示整理 Inbox 和归档 +5. **严格遵守** - 绝不违反目录限制规则 + +--- + +## 🔮 未来增强 + +- [ ] AI 辅助摘要生成 +- [ ] 自动提取关键词作为标签 +- [ ] 智能关联推荐(基于内容相似度) +- [ ] 语音输入支持 +- [ ] 多语言笔记支持 +- [ ] 团队协作功能 +- [ ] 自动审计目录合规性 + +--- + +## 📚 参考资源 + +- [PARA 方法](https://fortelabs.com/blog/para/) +- [Trilium Next 文档](https://triliumnext.org/) +- [知识管理最佳实践](Resources/Knowledge Management) +- [笔记记录规范](hgILpr4yRkcS) +- [知识库 ID 速查表](Aa2aRAGLWuu4) + +--- + +**最后更新**: 2026-04-11 +**版本**: 1.1 (带目录限制) +**⚠️ 重要**: 严格遵守目录限制,禁止在非授权位置创建笔记! diff --git a/skills/playwright-cli/SKILL.md b/skills/playwright-cli/SKILL.md new file mode 100644 index 00000000..ab2f610e --- /dev/null +++ b/skills/playwright-cli/SKILL.md @@ -0,0 +1,128 @@ +--- +name: playwright-cli +description: 官方Microsoft Playwright CLI网页自动化工具,支持所有主流浏览器的无头/有头自动化操作,包括页面导航、元素交互、截图、录制、测试等功能。当用户提到网页自动化、浏览器操作、爬虫、截图、录制用户操作、E2E测试时触发。 +--- + +# Playwright CLI 技能 + +Playwright CLI是微软官方的浏览器自动化工具,支持Chromium、Firefox、WebKit三大主流浏览器,提供强大的网页自动化能力。 + +## 安装步骤 + +### 1. 安装Playwright CLI +```bash +npm install -g @playwright/test +# 或者 +pip install playwright +``` + +### 2. 安装浏览器 +```bash +playwright install +# 仅安装特定浏览器 +playwright install chromium firefox +# 安装系统依赖(Linux环境) +playwright install-deps +``` + +## 核心常用命令 + +### 页面操作 +```bash +# 打开指定网页 +playwright open https://example.com + +# 无头模式打开网页并截图 +playwright screenshot https://example.com example.png +# 全屏截图 +playwright screenshot https://example.com --full-page full.png +# 指定视口大小 +playwright screenshot https://example.com --viewport-size=1920,1080 desktop.png + +# 生成PDF +playwright pdf https://example.com example.pdf +# 自定义PDF格式 +playwright pdf https://example.com --format=A4 --landscape report.pdf +``` + +### 录制用户操作 +```bash +# 录制用户操作并生成代码 +playwright codegen https://example.com +# 保存录制结果到文件 +playwright codegen https://example.com --output script.py +# 生成指定语言的代码(python, javascript, java, csharp) +playwright codegen https://example.com --target python +``` + +### 测试相关 +```bash +# 运行测试 +playwright test +# 运行特定测试文件 +playwright test tests/example.spec.js +# 有头模式运行测试(显示浏览器界面) +playwright test --headed +# 调试模式运行 +playwright test --debug +# 生成测试报告 +playwright show-report +``` + +### 浏览器管理 +```bash +# 列出已安装的浏览器 +playwright list-browsers +# 更新浏览器到最新版本 +playwright install --force +# 卸载浏览器 +playwright uninstall chromium +``` + +## 使用示例 + +### 示例1:批量截图多个网页 +```bash +# 对多个网页进行全屏截图 +for url in "https://google.com" "https://github.com" "https://stackoverflow.com"; do + name=$(echo $url | sed 's/https\?:\/\///' | sed 's/\//_/g') + playwright screenshot $url --full-page "${name}.png" +done +``` + +### 示例2:自动填写表单 +```python +# 录制生成的自动登录脚本示例 +from playwright.sync_api import Playwright, sync_playwright, expect + +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + context = browser.new_context() + page = context.new_page() + page.goto("https://example.com/login") + page.get_by_label("用户名").fill("your_username") + page.get_by_label("密码").fill("your_password") + page.get_by_role("button", name="登录").click() + # 截图登录后的页面 + page.screenshot(path="logged_in.png") + context.close() + browser.close() + +with sync_playwright() as playwright: + run(playwright) +``` + +### 示例3:模拟移动端访问 +```bash +# 模拟iPhone 14访问网页并截图 +playwright screenshot https://example.com --device="iPhone 14" mobile.png +# 模拟安卓设备 +playwright screenshot https://example.com --device="Galaxy S23" android.png +``` + +## 最佳实践 +1. 优先使用无头模式执行自动化任务,性能更高 +2. 复杂操作优先使用`codegen`录制生成代码,再手动调整 +3. 执行长时间任务时添加`--slowmo=1000`参数减慢操作速度,避免被反爬 +4. 保存登录状态使用`--save-storage=auth.json`,下次可以直接`--load-storage=auth.json`跳过登录 +5. 网络不稳定时添加`--timeout=60000`延长超时时间 diff --git a/skills/playwright-cli/_meta.json b/skills/playwright-cli/_meta.json new file mode 100644 index 00000000..13fcfdd6 --- /dev/null +++ b/skills/playwright-cli/_meta.json @@ -0,0 +1,6 @@ +{ + "ownerId": "kn7ez7a31k0sqt7ad9wk2hq31n82qea1", + "slug": "playwright-cli-openclaw", + "version": "1.0.0", + "publishedAt": 1773287492717 +} \ No newline at end of file diff --git a/skills/self-improvement/README.md b/skills/self-improvement/README.md index 89508174..5016d08c 100644 --- a/skills/self-improvement/README.md +++ b/skills/self-improvement/README.md @@ -1,14 +1,5 @@ # self-improvement -Self-improvement skill for Oclaw. It captures learnings, errors, and feature requests to support continuous improvement across sessions. +Self-improvement skill for Oclaw. Captures learnings, errors, and feature requests to support continuous improvement across sessions. -## Attribution - -Remade for Oclaw from the original repo: - -- https://github.com/pskoett/pskoett-ai-skills -- https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement - -## Main File - -- `SKILL.md` +Based on the original [pskoett/pskoett-ai-skills](https://github.com/pskoett/pskoett-ai-skills) — adapted for Oclaw with `memory_wiki_*` tools. diff --git a/skills/self-improvement/SKILL.md b/skills/self-improvement/SKILL.md index ad253d92..ae4a6da3 100644 --- a/skills/self-improvement/SKILL.md +++ b/skills/self-improvement/SKILL.md @@ -1,83 +1,91 @@ ---- -name: self-improvement -description: 将纠错、错误与能力缺口写入 Wiki 以持续改进。适用于操作失败、用户反馈纠正、问题复发、或需要沉淀并推动的新能力需求场景。 -metadata: +# self-improvement — 自我改进 (Self-Improvement) + +> 将纠错、错误与能力缺口写入 Wiki 以持续改进,形成"犯错→记录→不再犯"的闭环。 +> 本技能完全基于 `memory_wiki_*` 工具,不依赖本地文件或外部脚本。 + --- -# 自我改进(仅 Wiki) +## 触发条件 (Triggers) -本技能仅使用 Wiki,不使用本地 `.learnings/` 文件。 +出现以下情况时启用: -## 存储路径 +1. 命令或操作出现非预期失败 +2. 用户对回答进行纠正 +3. 用户提出缺失能力需求 +4. 同类问题再次复发 +5. 发现更优且可复用的方法 +6. 每轮对话结束时(强制三问反思) -- `improvement/learnings.md` -- `improvement/errors.md` -- `improvement/feature-requests.md` +## 必要流程 (Required Workflow) -## 触发条件 +``` +1. memory_wiki_search → 检索 improvement/* 看是否已有记录 +2. memory_wiki_get → 读取目标文件上下文 +3. memory_wiki_apply → 追加结构化条目 (action=append) +4. memory_wiki_lint → 质量检查 +5. lint 报错 → 立即修复 +``` -出现以下情况时启用本技能: +## 条目路由 (Entry Routing) -1. 命令或操作出现非预期失败。 -2. 用户对错误回答进行纠正。 -3. 用户提出缺失能力需求。 -4. 同类问题再次复发。 -5. 发现更优且可复用的方法。 +| 内容类型 | 目标文件 | 示例 | +|---------|---------|------| +| 纠错/洞见/最佳实践 | `improvement/learnings.md` | 用户纠正了某个认知错误 | +| 运行时/工具/API 失败 | `improvement/errors.md` | 某个工具调用报错 | +| 能力缺口/功能请求 | `improvement/feature-requests.md` | 用户问"你能做 X 吗?" | +| 长篇经验沉淀 (>300 字) | `improvement/learnings/.md` | 一次完整的技能安装流程 | -## 必要流程 - -每次触发都执行以下流程: - -1. 用 `memory_wiki_search` 检索历史相关记录。 -2. 用 `memory_wiki_get` 读取目标文件上下文。 -3. 用 `memory_wiki_apply`(`action=append`)追加结构化条目。 -4. 对目标文件执行 `memory_wiki_lint`。 -5. 若 lint 报错,立即用 `memory_wiki_apply` 修复。 - -## 条目路由 - -- 纠错 / 洞见 / 最佳实践 -> `improvement/learnings.md` -- 运行时 / 工具 / API 失败 -> `improvement/errors.md` -- 能力请求 / 缺失功能 -> `improvement/feature-requests.md` - -## 条目模板 +## 条目模板 (Entry Template) ```markdown -## [ID] <标题> -**Logged**: ISO-8601 时间戳 +## [ID] 标题 (Title) +**Logged**: ISO-8601 时间戳 (Timestamp) **Priority**: low | medium | high | critical -**Status**: pending +**Status**: pending | resolved | promoted_to_skill **Area**: frontend | backend | infra | tests | docs | config -### Summary +### 摘要 (Summary) 一句话摘要。 -### Details +### 详情 (Details) 发生了什么、为什么重要、可执行改进建议。 -### Metadata -- Source: conversation | error | user_feedback +### 元数据 (Metadata) +- Source: conversation | error | user_feedback | reflection - Related Files: path/to/file.ext - See Also: ``` -ID 格式: +### ID 格式 (ID Format) - Learning: `LRN-YYYYMMDD-XXX` - Error: `ERR-YYYYMMDD-XXX` - Feature request: `FEAT-YYYYMMDD-XXX` -## 提升目标 +XXX = 当天序号,从 001 开始。 -当条目已具备广泛复用价值时,将精炼规则提升到: +## 提升路径 (Promotion Path) -- `AGENTS.md`(工作流模式) -- `SOUL.md`(行为模式) -- `TOOLS.md`(工具易错点) -- `.github/copilot-instructions.md`(共享编码约定) +当某个条目被验证有广泛复用价值时: -## 安全规则 +| 到达条件 | 提升目标 | +|---------|---------| +| 可用作行为约定 | `core/principles.md` | +| 可用作维护规则 | `core/maintenance.md` | +| 可提炼为独立技能 | 使用 `skill-creator` 创建新技能 | +| 某个工具易错点 | 标注在 SKILL.md 或 REFERENCES.md | -- 不记录密钥、令牌、凭据或原始敏感信息。 -- 敏感输出使用脱敏摘要,不保留完整原文。 -- 未验证事实不得当作已确认规则持久化。 +## 会话结束三问 (End-of-Session Reflection) + +每轮对话结束前执行: + +1. **学到了什么?** → `improvement/learnings.md` +2. **有什么可优化?** → `improvement/feature-requests.md` 或 `core/principles.md` +3. **用户画像有更新吗?** → `users/current.md` + +## 安全规则 (Security Rules) + +- 不记录密钥、令牌、凭据或原始敏感信息 +- 敏感输出使用脱敏摘要,不保留完整原文 +- 未验证事实不得当作已确认规则持久化 +- 涉及用户偏好写入时需有明确的对话证据,不推测 diff --git a/skills/self-improvement/assets/ERRORS.md b/skills/self-improvement/assets/ERRORS.md deleted file mode 100644 index 6bce3929..00000000 --- a/skills/self-improvement/assets/ERRORS.md +++ /dev/null @@ -1,5 +0,0 @@ -# Errors Log - -Command failures, exceptions, and unexpected behaviors. - ---- diff --git a/skills/self-improvement/assets/FEATURE_REQUESTS.md b/skills/self-improvement/assets/FEATURE_REQUESTS.md deleted file mode 100644 index 35272774..00000000 --- a/skills/self-improvement/assets/FEATURE_REQUESTS.md +++ /dev/null @@ -1,5 +0,0 @@ -# Feature Requests - -Capabilities requested by user that don't currently exist. - ---- diff --git a/skills/self-improvement/assets/LEARNINGS.md b/skills/self-improvement/assets/LEARNINGS.md deleted file mode 100644 index 6993f9b2..00000000 --- a/skills/self-improvement/assets/LEARNINGS.md +++ /dev/null @@ -1,45 +0,0 @@ -# Learnings - -Corrections, insights, and knowledge gaps captured during development. - -**Categories**: correction | insight | knowledge_gap | best_practice -**Areas**: frontend | backend | infra | tests | docs | config -**Statuses**: pending | in_progress | resolved | wont_fix | promoted | promoted_to_skill - -## Status Definitions - -| Status | Meaning | -|--------|---------| -| `pending` | Not yet addressed | -| `in_progress` | Actively being worked on | -| `resolved` | Issue fixed or knowledge integrated | -| `wont_fix` | Decided not to address (reason in Resolution) | -| `promoted` | Elevated to CLAUDE.md, AGENTS.md, or copilot-instructions.md | -| `promoted_to_skill` | Extracted as a reusable skill | - -## Skill Extraction Fields - -When a learning is promoted to a skill, add these fields: - -```markdown -**Status**: promoted_to_skill -**Skill-Path**: skills/skill-name -``` - -Example: -```markdown -## [LRN-20250115-001] best_practice - -**Logged**: 2025-01-15T10:00:00Z -**Priority**: high -**Status**: promoted_to_skill -**Skill-Path**: skills/docker-m1-fixes -**Area**: infra - -### Summary -Docker build fails on Apple Silicon due to platform mismatch -... -``` - ---- - diff --git a/skills/self-improvement/assets/SKILL-TEMPLATE.md b/skills/self-improvement/assets/SKILL-TEMPLATE.md deleted file mode 100644 index 0162134b..00000000 --- a/skills/self-improvement/assets/SKILL-TEMPLATE.md +++ /dev/null @@ -1,177 +0,0 @@ -# Skill Template - -Template for creating skills extracted from learnings. Copy and customize. - ---- - -## SKILL.md Template - -```markdown ---- -name: skill-name-here -description: "Concise description of when and why to use this skill. Include trigger conditions." ---- - -# Skill Name - -Brief introduction explaining the problem this skill solves and its origin. - -## Quick Reference - -| Situation | Action | -|-----------|--------| -| [Trigger 1] | [Action 1] | -| [Trigger 2] | [Action 2] | - -## Background - -Why this knowledge matters. What problems it prevents. Context from the original learning. - -## Solution - -### Step-by-Step - -1. First step with code or command -2. Second step -3. Verification step - -### Code Example - -\`\`\`language -// Example code demonstrating the solution -\`\`\` - -## Common Variations - -- **Variation A**: Description and how to handle -- **Variation B**: Description and how to handle - -## Gotchas - -- Warning or common mistake #1 -- Warning or common mistake #2 - -## Related - -- Link to related documentation -- Link to related skill - -## Source - -Extracted from learning entry. -- **Learning ID**: LRN-YYYYMMDD-XXX -- **Original Category**: correction | insight | knowledge_gap | best_practice -- **Extraction Date**: YYYY-MM-DD -``` - ---- - -## Minimal Template - -For simple skills that don't need all sections: - -```markdown ---- -name: skill-name-here -description: "What this skill does and when to use it." ---- - -# Skill Name - -[Problem statement in one sentence] - -## Solution - -[Direct solution with code/commands] - -## Source - -- Learning ID: LRN-YYYYMMDD-XXX -``` - ---- - -## Template with Scripts - -For skills that include executable helpers: - -```markdown ---- -name: skill-name-here -description: "What this skill does and when to use it." ---- - -# Skill Name - -[Introduction] - -## Quick Reference - -| Command | Purpose | -|---------|---------| -| `./scripts/helper.sh` | [What it does] | -| `./scripts/validate.sh` | [What it does] | - -## Usage - -### Automated (Recommended) - -\`\`\`bash -./skills/skill-name/scripts/helper.sh [args] -\`\`\` - -### Manual Steps - -1. Step one -2. Step two - -## Scripts - -| Script | Description | -|--------|-------------| -| `scripts/helper.sh` | Main utility | -| `scripts/validate.sh` | Validation checker | - -## Source - -- Learning ID: LRN-YYYYMMDD-XXX -``` - ---- - -## Naming Conventions - -- **Skill name**: lowercase, hyphens for spaces - - Good: `docker-m1-fixes`, `api-timeout-patterns` - - Bad: `Docker_M1_Fixes`, `APITimeoutPatterns` - -- **Description**: Start with action verb, mention trigger - - Good: "Handles Docker build failures on Apple Silicon. Use when builds fail with platform mismatch." - - Bad: "Docker stuff" - -- **Files**: - - `SKILL.md` - Required, main documentation - - `scripts/` - Optional, executable code - - `references/` - Optional, detailed docs - - `assets/` - Optional, templates - ---- - -## Extraction Checklist - -Before creating a skill from a learning: - -- [ ] Learning is verified (status: resolved) -- [ ] Solution is broadly applicable (not one-off) -- [ ] Content is complete (has all needed context) -- [ ] Name follows conventions -- [ ] Description is concise but informative -- [ ] Quick Reference table is actionable -- [ ] Code examples are tested -- [ ] Source learning ID is recorded - -After creating: - -- [ ] Update original learning with `promoted_to_skill` status -- [ ] Add `Skill-Path: skills/skill-name` to learning metadata -- [ ] Test skill by reading it in a fresh session diff --git a/skills/self-improvement/hooks/runtime/HOOK.md b/skills/self-improvement/hooks/runtime/HOOK.md deleted file mode 100644 index d095cd94..00000000 --- a/skills/self-improvement/hooks/runtime/HOOK.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: self-improvement -description: "在智能体启动阶段注入自我改进提醒" -metadata: {"oclaw":{"emoji":"🧠","events":["agent:bootstrap"]}} ---- - -# 自我改进 Hook - -在 `agent:bootstrap` 阶段注入“学习沉淀提醒”。 - -## 功能说明 - -- 在 `agent:bootstrap` 触发(工作区文件注入前) -- 注入提醒块,引导将学习写入 Wiki 路径 -- 提示智能体记录纠错、错误与新发现 - -## 配置方式 - -无需额外配置,启用命令: - -```bash -oclaw hooks enable self-improvement -``` diff --git a/skills/self-improvement/hooks/runtime/handler.py b/skills/self-improvement/hooks/runtime/handler.py deleted file mode 100644 index 56e58388..00000000 --- a/skills/self-improvement/hooks/runtime/handler.py +++ /dev/null @@ -1,85 +0,0 @@ -from __future__ import annotations - -from typing import Any - -REMINDER_NAME = "SELF_IMPROVEMENT_REMINDER.md" -REMINDER_PATH = REMINDER_NAME - -REMINDER_CONTENT = """## 自我改进提醒 - -任务完成后,请评估是否产生可沉淀学习。 - -仅在当前仓库/工作区启用 self-improvement 技能时记录。 - -记录前: -- 使用 memory_wiki_* 工具写入 `improvement/` 下的 Wiki 笔记 -- 不记录密钥、令牌、私钥、环境变量或原始对话全文 -- 优先使用简短摘要或脱敏片段,避免完整命令输出 - -**以下情况应记录:** -- 用户纠正你 → `improvement/learnings.md` -- 命令/操作失败 → `improvement/errors.md` -- 用户提出缺失能力 → `improvement/feature-requests.md` -- 发现认知错误 → `improvement/learnings.md` -- 发现更优做法 → `improvement/learnings.md` - -**当模式被验证后进行提升:** -- 行为模式 → `SOUL.md` -- 工作流改进 → `AGENTS.md` -- 工具易错点 → `TOOLS.md` - -条目保持简洁:时间、标题、发生了什么、后续应如何做。""" - - -def _is_record(value: object) -> bool: - return isinstance(value, dict) - - -def _is_injected_reminder_file(value: object) -> bool: - if not _is_record(value) or str(value.get("path")) != REMINDER_PATH: # type: ignore[union-attr] - return False - v = value # type: ignore[assignment] - return v.get("virtual") is True or v.get("content") == REMINDER_CONTENT - - -def handle(event: object) -> None: - if getattr(event, "type", None) != "agent" or getattr(event, "action", None) != "bootstrap": - return - ctx = getattr(event, "context", None) - if not isinstance(ctx, dict): - return - session_key = str(getattr(event, "sessionKey", "") or "") - if ":subagent:" in session_key: - return - - if not isinstance(ctx.get("bootstrapFiles"), list): - return - files: list[object] = list(ctx.get("bootstrapFiles") or []) - - occupied = any( - _is_record(f) and str(f.get("path")) == REMINDER_PATH and not _is_injected_reminder_file(f) # type: ignore[union-attr] - for f in files - ) - if occupied: - return - - cleaned: list[object] = [ - f - for i, f in enumerate(files) - if (not _is_injected_reminder_file(f)) - or (next((j for j, c in enumerate(files) if _is_injected_reminder_file(c)), -1) == i) - ] - - reminder_file: dict[str, Any] = { - "name": REMINDER_NAME, - "path": REMINDER_PATH, - "content": REMINDER_CONTENT, - "missing": False, - "virtual": True, - } - existing_idx = next((i for i, f in enumerate(cleaned) if _is_injected_reminder_file(f)), -1) - if existing_idx == -1: - cleaned.append(reminder_file) - else: - cleaned[existing_idx] = reminder_file - ctx["bootstrapFiles"] = cleaned diff --git a/skills/self-improvement/references/examples.md b/skills/self-improvement/references/examples.md deleted file mode 100644 index e1d0c195..00000000 --- a/skills/self-improvement/references/examples.md +++ /dev/null @@ -1,374 +0,0 @@ -# Entry Examples - -Concrete examples of well-formatted entries with all fields. - -## Learning: Correction - -```markdown -## [LRN-20250115-001] correction - -**Logged**: 2025-01-15T10:30:00Z -**Priority**: high -**Status**: pending -**Area**: tests - -### Summary -Incorrectly assumed pytest fixtures are scoped to function by default - -### Details -When writing test fixtures, I assumed all fixtures were function-scoped. -User corrected that while function scope is the default, the codebase -convention uses module-scoped fixtures for database connections to -improve test performance. - -### Suggested Action -When creating fixtures that involve expensive setup (DB, network), -check existing fixtures for scope patterns before defaulting to function scope. - -### Metadata -- Source: user_feedback -- Related Files: tests/conftest.py -- Tags: pytest, testing, fixtures - ---- -``` - -## Learning: Knowledge Gap (Resolved) - -```markdown -## [LRN-20250115-002] knowledge_gap - -**Logged**: 2025-01-15T14:22:00Z -**Priority**: medium -**Status**: resolved -**Area**: config - -### Summary -Project uses pnpm not npm for package management - -### Details -Attempted to run `npm install` but project uses pnpm workspaces. -Lock file is `pnpm-lock.yaml`, not `package-lock.json`. - -### Suggested Action -Check for `pnpm-lock.yaml` or `pnpm-workspace.yaml` before assuming npm. -Use `pnpm install` for this project. - -### Metadata -- Source: error -- Related Files: pnpm-lock.yaml, pnpm-workspace.yaml -- Tags: package-manager, pnpm, setup - -### Resolution -- **Resolved**: 2025-01-15T14:30:00Z -- **Commit/PR**: N/A - knowledge update -- **Notes**: Added to CLAUDE.md for future reference - ---- -``` - -## Learning: Promoted to CLAUDE.md - -```markdown -## [LRN-20250115-003] best_practice - -**Logged**: 2025-01-15T16:00:00Z -**Priority**: high -**Status**: promoted -**Promoted**: CLAUDE.md -**Area**: backend - -### Summary -API responses must include correlation ID from request headers - -### Details -All API responses should echo back the X-Correlation-ID header from -the request. This is required for distributed tracing. Responses -without this header break the observability pipeline. - -### Suggested Action -Always include correlation ID passthrough in API handlers. - -### Metadata -- Source: user_feedback -- Related Files: oclaw/middleware/correlation.ts -- Tags: api, observability, tracing - ---- -``` - -## Learning: Promoted to AGENTS.md - -```markdown -## [LRN-20250116-001] best_practice - -**Logged**: 2025-01-16T09:00:00Z -**Priority**: high -**Status**: promoted -**Promoted**: AGENTS.md -**Area**: backend - -### Summary -Must regenerate API client after OpenAPI spec changes - -### Details -When modifying API endpoints, the TypeScript client must be regenerated. -Forgetting this causes type mismatches that only appear at runtime. -The generate script also runs validation. - -### Suggested Action -Add to agent workflow: after any API changes, run `pnpm run generate:api`. - -### Metadata -- Source: error -- Related Files: openapi.yaml, oclaw/client/api.ts -- Tags: api, codegen, typescript - ---- -``` - -## Error Entry - -```markdown -## [ERR-20250115-A3F] docker_build - -**Logged**: 2025-01-15T09:15:00Z -**Priority**: high -**Status**: pending -**Area**: infra - -### Summary -Docker build fails on M1 Mac due to platform mismatch - -### Error -``` -error: failed to solve: python:3.11-slim: no match for platform linux/arm64 -``` - -### Context -- Command: `docker build -t myapp .` -- Dockerfile uses `FROM python:3.11-slim` -- Running on Apple Silicon (M1/M2) - -### Suggested Fix -Add platform flag: `docker build --platform linux/amd64 -t myapp .` -Or update Dockerfile: `FROM --platform=linux/amd64 python:3.11-slim` - -### Metadata -- Reproducible: yes -- Related Files: Dockerfile - ---- -``` - -## Error Entry: Recurring Issue - -```markdown -## [ERR-20250120-B2C] api_timeout - -**Logged**: 2025-01-20T11:30:00Z -**Priority**: critical -**Status**: pending -**Area**: backend - -### Summary -Third-party API timeout during request processing - -### Error -``` -TimeoutError: Request to api.example.com timed out after 30000ms -``` - -### Context -- Command: POST /api/process -- Timeout set to 30s -- Occurs during peak hours (lunch, evening) - -### Suggested Fix -Implement retry with exponential backoff. Consider circuit breaker pattern. - -### Metadata -- Reproducible: yes (during peak hours) -- Related Files: oclaw/services/api-client.ts -- See Also: ERR-20250115-X1Y, ERR-20250118-Z3W - ---- -``` - -## Feature Request - -```markdown -## [FEAT-20250115-001] export_to_csv - -**Logged**: 2025-01-15T16:45:00Z -**Priority**: medium -**Status**: pending -**Area**: backend - -### Requested Capability -Export analysis results to CSV format - -### User Context -User runs weekly reports and needs to share results with non-technical -stakeholders in Excel. Currently copies output manually. - -### Complexity Estimate -simple - -### Suggested Implementation -Add `--output csv` flag to the analyze command. Use standard csv module. -Could extend existing `--output json` pattern. - -### Metadata -- Frequency: recurring -- Related Features: analyze command, json output - ---- -``` - -## Feature Request: Resolved - -```markdown -## [FEAT-20250110-002] dark_mode - -**Logged**: 2025-01-10T14:00:00Z -**Priority**: low -**Status**: resolved -**Area**: frontend - -### Requested Capability -Dark mode support for the dashboard - -### User Context -User works late hours and finds the bright interface straining. -Several other users have mentioned this informally. - -### Complexity Estimate -medium - -### Suggested Implementation -Use CSS variables for colors. Add toggle in user settings. -Consider system preference detection. - -### Metadata -- Frequency: recurring -- Related Features: user settings, theme system - -### Resolution -- **Resolved**: 2025-01-18T16:00:00Z -- **Commit/PR**: #142 -- **Notes**: Implemented with system preference detection and manual toggle - ---- -``` - -## Learning: Promoted to Skill - -```markdown -## [LRN-20250118-001] best_practice - -**Logged**: 2025-01-18T11:00:00Z -**Priority**: high -**Status**: promoted_to_skill -**Skill-Path**: skills/docker-m1-fixes -**Area**: infra - -### Summary -Docker build fails on Apple Silicon due to platform mismatch - -### Details -When building Docker images on M1/M2 Macs, the build fails because -the base image doesn't have an ARM64 variant. This is a common issue -that affects many developers. - -### Suggested Action -Add `--platform linux/amd64` to docker build command, or use -`FROM --platform=linux/amd64` in Dockerfile. - -### Metadata -- Source: error -- Related Files: Dockerfile -- Tags: docker, arm64, m1, apple-silicon -- See Also: ERR-20250115-A3F, ERR-20250117-B2D - ---- -``` - -## Extracted Skill Example - -When the above learning is extracted as a skill, it becomes: - -**File**: `skills/docker-m1-fixes/SKILL.md` - -```markdown ---- -name: docker-m1-fixes -description: "Fixes Docker build failures on Apple Silicon (M1/M2). Use when docker build fails with platform mismatch errors." ---- - -# Docker M1 Fixes - -Solutions for Docker build issues on Apple Silicon Macs. - -## Quick Reference - -| Error | Fix | -|-------|-----| -| `no match for platform linux/arm64` | Add `--platform linux/amd64` to build | -| Image runs but crashes | Use emulation or find ARM-compatible base | - -## The Problem - -Many Docker base images don't have ARM64 variants. When building on -Apple Silicon (M1/M2/M3), Docker attempts to pull ARM64 images by -default, causing platform mismatch errors. - -## Solutions - -### Option 1: Build Flag (Recommended) - -Add platform flag to your build command: - -\`\`\`bash -docker build --platform linux/amd64 -t myapp . -\`\`\` - -### Option 2: Dockerfile Modification - -Specify platform in the FROM instruction: - -\`\`\`dockerfile -FROM --platform=linux/amd64 python:3.11-slim -\`\`\` - -### Option 3: Docker Compose - -Add platform to your service: - -\`\`\`yaml -services: - app: - platform: linux/amd64 - build: . -\`\`\` - -## Trade-offs - -| Approach | Pros | Cons | -|----------|------|------| -| Build flag | No file changes | Must remember flag | -| Dockerfile | Explicit, versioned | Affects all builds | -| Compose | Convenient for dev | Requires compose | - -## Performance Note - -Running AMD64 images on ARM64 uses Rosetta 2 emulation. This works -for development but may be slower. For production, find ARM-native -alternatives when possible. - -## Source - -- Learning ID: LRN-20250118-001 -- Category: best_practice -- Extraction Date: 2025-01-18 -``` diff --git a/skills/self-improvement/references/hooks-setup.md b/skills/self-improvement/references/hooks-setup.md deleted file mode 100644 index fd7319e7..00000000 --- a/skills/self-improvement/references/hooks-setup.md +++ /dev/null @@ -1,225 +0,0 @@ -# Hook Setup Guide - -Configure automatic self-improvement triggers for AI coding agents. - -## 概览 - -Hooks enable proactive learning capture by injecting reminders at key moments: -- **UserPromptSubmit**: Reminder after each prompt to evaluate learnings -- **PostToolUse (Bash)**: Error detection when commands fail - -## Claude Code Setup - -### Option 1: Project-Level Configuration - -Create `.claude/settings.json` in your project root: - -```json -{ - "hooks": { - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" - } - ] - } - ], - "PostToolUse": [ - { - "matcher": "Bash", - "hooks": [ - { - "type": "command", - "command": "./skills/self-improvement/scripts/error-detector.sh" - } - ] - } - ] - } -} -``` - -### Option 2: User-Level Configuration - -Add to `~/.claude/settings.json` for global activation: - -```json -{ - "hooks": { - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "~/.claude/skills/self-improvement/scripts/activator.sh" - } - ] - } - ] - } -} -``` - -### Minimal Setup (Activator Only) - -For lower overhead, use only the UserPromptSubmit hook: - -```json -{ - "hooks": { - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" - } - ] - } - ] - } -} -``` - -## Codex CLI Setup - -Codex uses the same hook system as Claude Code. Create `.codex/settings.json`: - -```json -{ - "hooks": { - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" - } - ] - } - ] - } -} -``` - -## GitHub Copilot Setup - -Copilot doesn't support hooks directly. Instead, add guidance to `.github/copilot-instructions.md`: - -```markdown -## 自我改进 - -After completing tasks that involved: -- Debugging non-obvious issues -- Discovering workarounds -- Learning project-specific patterns -- Resolving unexpected errors - -Consider logging the learning to Wiki (`improvement/learnings.md`, `improvement/errors.md`, `improvement/feature-requests.md`) using the format from the self-improvement skill. - -For high-value learnings that would benefit other sessions, consider skill extraction. -``` - -## Verification - -### Test Activator Hook - -1. Enable the hook configuration -2. Start a new Claude Code session -3. Send any prompt -4. Verify you see `` in the context - -### Test Error Detector Hook - -1. Enable PostToolUse hook for Bash -2. Run a command that fails: `ls /nonexistent/path` -3. Verify you see `` reminder - -### Dry Run Extract Script - -```bash -./skills/self-improvement/scripts/extract-skill.sh test-skill --dry-run -``` - -Expected output shows the skill scaffold that would be created. - -## Troubleshooting - -### Hook Not Triggering - -1. **Check script permissions**: `chmod +x scripts/*.sh` -2. **Verify path**: Use absolute paths or paths relative to project root -3. **Check settings location**: Project vs user-level settings -4. **Restart session**: Hooks are loaded at session start - -### Permission Denied - -```bash -chmod +x ./skills/self-improvement/scripts/activator.sh -chmod +x ./skills/self-improvement/scripts/error-detector.sh -chmod +x ./skills/self-improvement/scripts/extract-skill.sh -``` - -### Script Not Found - -If using relative paths, ensure you're in the correct directory or use absolute paths: - -```json -{ - "command": "/absolute/path/to/skills/self-improvement/scripts/activator.sh" -} -``` - -### Too Much Overhead - -If the activator feels intrusive: - -1. **Use minimal setup**: Only UserPromptSubmit, skip PostToolUse -2. **Add matcher filter**: Only trigger for certain prompts: - -```json -{ - "matcher": "fix|debug|error|issue", - "hooks": [...] -} -``` - -## Hook Output Budget - -The activator is designed to be lightweight: -- **Target**: ~50-100 tokens per activation -- **Content**: Structured reminder, not verbose instructions -- **Format**: XML tags for easy parsing - -If you need to reduce overhead further, you can edit `activator.sh` to output less text. - -## Security Considerations - -- Hook scripts run with the same permissions as Claude Code -- Scripts only output text; they don't modify files or run commands -- Error detector reads `CLAUDE_TOOL_OUTPUT` environment variable -- Treat `CLAUDE_TOOL_OUTPUT` as potentially sensitive; do not log or forward it verbatim unless the user explicitly wants that detail -- All scripts are opt-in (you must configure them explicitly) -- Recommended default: enable `UserPromptSubmit` only, and add `PostToolUse` only when you want error-pattern reminders from command output - -## Disabling Hooks - -To temporarily disable without removing configuration: - -1. **Comment out in settings**: -```json -{ - "hooks": { - // "UserPromptSubmit": [...] - } -} -``` - -2. **Or delete the settings file**: Hooks won't run without configuration diff --git a/skills/self-improvement/references/oclaw-integration.md b/skills/self-improvement/references/oclaw-integration.md deleted file mode 100644 index bab225bd..00000000 --- a/skills/self-improvement/references/oclaw-integration.md +++ /dev/null @@ -1,248 +0,0 @@ -# Oclaw Integration - -Complete setup and usage guide for integrating the self-improvement skill with Oclaw. - -## 概览 - -Oclaw uses workspace-based prompt injection combined with event-driven hooks. Context is injected from workspace files at session start, and hooks can trigger on lifecycle events. - -## Workspace Structure - -``` -~/.oclaw/ -├── workspace/ # Working directory -│ ├── AGENTS.md # Multi-agent coordination patterns -│ ├── SOUL.md # Behavioral guidelines and personality -│ ├── TOOLS.md # Tool capabilities and gotchas -│ ├── MEMORY.md # Long-term memory (main session only) -│ └── memory/ # Daily memory files -│ └── YYYY-MM-DD.md -├── skills/ # Installed skills -│ └── / -│ └── SKILL.md -└── hooks/ # Custom hooks - └── / - ├── HOOK.md - └── handler.py -``` - -## 快速配置 - -### 1. Install the Skill - -```bash -clawdhub install self-improving-agent -``` - -Or copy manually: - -```bash -cp -r self-improving-agent ~/.oclaw/skills/ -``` - -### 2. Install the Hook (Optional) - -Copy the hook to Oclaw's hooks directory: - -```bash -cp -r hooks/oclaw ~/.oclaw/hooks/self-improvement -``` - -Enable the hook: - -```bash -oclaw hooks enable self-improvement -``` - -### 3. Ensure Wiki Improvement Notes Exist - -Create or initialize these Wiki notes under your configured wiki root: - -- `improvement/learnings.md` -- `improvement/errors.md` -- `improvement/feature-requests.md` - -## Injected Prompt Files - -### AGENTS.md - -Purpose: Multi-agent workflows and delegation patterns. - -```markdown -# Agent Coordination - -## Delegation Rules -- Use explore agent for open-ended codebase questions -- Spawn sub-agents for long-running tasks -- Use sessions_send for cross-session communication - -## Session Handoff -When delegating to another session: -1. Provide full context in the handoff message -2. Include relevant file paths -3. Specify expected output format -``` - -### SOUL.md - -Purpose: Behavioral guidelines and communication style. - -```markdown -# Behavioral Guidelines - -## Communication Style -- Be direct and concise -- Avoid unnecessary caveats and disclaimers -- Use technical language appropriate to context - -## Error Handling -- Admit mistakes promptly -- Provide corrected information immediately -- Log significant errors to learnings -``` - -### TOOLS.md - -Purpose: Tool capabilities, integration gotchas, local configuration. - -```markdown -# Tool Knowledge - -## 自我改进技能 -Log learnings to Wiki `improvement/*.md` notes for continuous improvement. - -## Local Tools -- Document tool-specific gotchas here -- Note authentication requirements -- Track integration quirks -``` - -## Learning Workflow - -### Capturing Learnings - -1. **In-session**: Log to Wiki improvement notes (`improvement/*.md`) -2. **Cross-session**: Promote to workspace files - -### Promotion Decision Tree - -``` -Is the learning project-specific? -├── Yes → Keep in improvement/learnings.md -└── No → Is it behavioral/style-related? - ├── Yes → Promote to SOUL.md - └── No → Is it tool-related? - ├── Yes → Promote to TOOLS.md - └── No → Promote to AGENTS.md (workflow) -``` - -### Promotion Format Examples - -**From learning:** -> Git push to GitHub fails without auth configured - triggers desktop prompt - -**To TOOLS.md:** -```markdown -## Git -- Don't push without confirming auth is configured -- Use `gh auth status` to check GitHub CLI auth -``` - -## Inter-Agent Communication - -Oclaw provides tools for cross-session communication: - -Use these only when cross-session sharing is explicitly needed and the environment is trusted. Prefer short sanitized summaries over raw transcripts, command output, or secret-bearing content. - -### sessions_list - -View active and recent sessions: -``` -sessions_list(activeMinutes=30, messageLimit=3) -``` - -### sessions_history - -Read transcript from another session: -``` -sessions_history(sessionKey="session-id", limit=50) -``` - -Only read another session's transcript when the user explicitly wants shared context or continuation across sessions. - -### sessions_send - -Send message to another session: -``` -sessions_send(sessionKey="session-id", message="Learning: API requires X-Custom-Header") -``` - -Prefer sending a concise learning summary plus relevant paths rather than forwarding raw transcript content. - -### sessions_spawn - -Spawn a background sub-agent: -``` -sessions_spawn(task="Research X and report back", label="research") -``` - -## Available Hook Events - -| Event | When It Fires | -|-------|---------------| -| `agent:bootstrap` | Before workspace files inject | -| `command:new` | When `/new` command issued | -| `command:reset` | When `/reset` command issued | -| `command:stop` | When `/stop` command issued | -| `gateway:startup` | When gateway starts | - -## Detection Triggers - -### Standard Triggers -- User corrections ("No, that's wrong...") -- Command failures (non-zero exit codes) -- API errors -- Knowledge gaps - -### Oclaw-Specific Triggers - -| Trigger | Action | -|---------|--------| -| Tool call error | Log to TOOLS.md with tool name | -| Session handoff confusion | Log to AGENTS.md with delegation pattern | -| Model behavior surprise | Log to SOUL.md with expected vs actual | -| Skill issue | Log to `improvement/*.md` or report upstream | - -## Verification - -Check hook is registered: - -```bash -oclaw hooks list -``` - -Check skill is loaded: - -```bash -oclaw status -``` - -## Troubleshooting - -### Hook not firing - -1. Ensure hooks enabled in config -2. Restart gateway after config changes -3. Check gateway logs for errors - -### Learnings not persisting - -1. Verify wiki `improvement/*.md` notes exist -2. Check file permissions -3. Ensure workspace path is configured correctly - -### Skill not loading - -1. Check skill is in skills directory -2. Verify SKILL.md has correct frontmatter -3. Run `oclaw status` to see loaded skills diff --git a/skills/self-improvement/scripts/activator.sh b/skills/self-improvement/scripts/activator.sh deleted file mode 100644 index 7886b469..00000000 --- a/skills/self-improvement/scripts/activator.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# 自我改进激活 Hook -# 在 UserPromptSubmit 触发,用于提醒记录学习沉淀 -# 输出保持精简(约 50-100 tokens)以降低上下文负担 - -set -e - -# 以系统上下文形式输出提醒 -cat << 'EOF' - -本任务完成后,请判断是否产出可沉淀知识: -- 是否通过排查得到非显而易见的解法? -- 是否形成了异常行为的可复用绕过方案? -- 是否识别出项目特有模式? -- 是否有需要调试才能解决的错误? - -若是,请写入 Wiki: -- improvement/learnings.md -- improvement/errors.md -- improvement/feature-requests.md -若价值较高(复发、可广泛复用),请考虑提炼为独立技能。 - -EOF diff --git a/skills/self-improvement/scripts/error-detector.sh b/skills/self-improvement/scripts/error-detector.sh deleted file mode 100644 index 72a61c56..00000000 --- a/skills/self-improvement/scripts/error-detector.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -# 自我改进错误检测 Hook -# 在 Bash 的 PostToolUse 触发,用于检测命令失败 -# 读取 CLAUDE_TOOL_OUTPUT 环境变量 - -set -e - -# 检查工具输出是否包含错误信号 -# CLAUDE_TOOL_OUTPUT 为工具执行结果 -OUTPUT="${CLAUDE_TOOL_OUTPUT:-}" - -# 错误模式(大小写不敏感匹配) -ERROR_PATTERNS=( - "error:" - "Error:" - "ERROR:" - "failed" - "FAILED" - "command not found" - "No such file" - "Permission denied" - "fatal:" - "Exception" - "Traceback" - "npm ERR!" - "ModuleNotFoundError" - "SyntaxError" - "TypeError" - "exit code" - "non-zero" -) - -# 检查输出是否匹配任一错误模式 -contains_error=false -for pattern in "${ERROR_PATTERNS[@]}"; do - if [[ "$OUTPUT" == *"$pattern"* ]]; then - contains_error=true - break - fi -done - -# 仅在检测到错误时输出提醒 -if [ "$contains_error" = true ]; then - cat << 'EOF' - -检测到命令错误。若满足以下任一条件,请记录到 improvement/errors.md: -- 错误出乎预期或并不直观 -- 需要排查才能解决 -- 可能在相似场景复发 -- 解决方案对后续会话有复用价值 - -记录时请使用 self-improvement 技能格式:[ERR-YYYYMMDD-XXX] - -EOF -fi diff --git a/skills/self-improvement/scripts/extract-skill.sh b/skills/self-improvement/scripts/extract-skill.sh deleted file mode 100644 index da000a3d..00000000 --- a/skills/self-improvement/scripts/extract-skill.sh +++ /dev/null @@ -1,221 +0,0 @@ -#!/bin/bash -# Skill Extraction Helper -# Creates a new skill from a learning entry -# 用法: ./extract-skill.sh [--dry-run] - -set -e - -# Configuration -SKILLS_DIR="./skills" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -usage() { - cat << EOF -用法: $(basename "$0") [options] - -根据学习条目创建新技能。 - -参数: - skill-name 技能名称(小写,空格使用连字符) - -选项: - --dry-run 仅预览将创建的内容,不落盘 - --output-dir 当前路径下的相对输出目录(默认: ./skills) - -h, --help 显示帮助信息 - -示例: - $(basename "$0") docker-m1-fixes - $(basename "$0") api-timeout-patterns --dry-run - $(basename "$0") pnpm-setup --output-dir ./skills/custom - -技能将创建在: \$SKILLS_DIR// -EOF -} - -log_info() { - echo -e "${GREEN}[信息]${NC} $1" -} - -log_warn() { - echo -e "${YELLOW}[警告]${NC} $1" -} - -log_error() { - echo -e "${RED}[错误]${NC} $1" >&2 -} - -# Parse arguments -SKILL_NAME="" -DRY_RUN=false - -while [[ $# -gt 0 ]]; do - case $1 in - --dry-run) - DRY_RUN=true - shift - ;; - --output-dir) - if [ -z "${2:-}" ] || [[ "${2:-}" == -* ]]; then - log_error "--output-dir 需要提供相对路径参数" - usage - exit 1 - fi - SKILLS_DIR="$2" - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - -*) - log_error "未知选项: $1" - usage - exit 1 - ;; - *) - if [ -z "$SKILL_NAME" ]; then - SKILL_NAME="$1" - else - log_error "意外参数: $1" - usage - exit 1 - fi - shift - ;; - esac -done - -# Validate skill name -if [ -z "$SKILL_NAME" ]; then - log_error "必须提供技能名称" - usage - exit 1 -fi - -# Validate skill name format (lowercase, hyphens, no spaces) -if ! [[ "$SKILL_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then - log_error "技能名称格式无效。仅允许小写字母、数字和连字符。" - log_error "示例: 'docker-fixes', 'api-patterns', 'pnpm-setup'" - exit 1 -fi - -# Validate output path to avoid writes outside current workspace. -if [[ "$SKILLS_DIR" = /* ]]; then - log_error "输出目录必须是当前目录下的相对路径。" - exit 1 -fi - -if [[ "$SKILLS_DIR" =~ (^|/)\.\.(/|$) ]]; then - log_error "输出目录不能包含 '..' 路径段。" - exit 1 -fi - -SKILLS_DIR="${SKILLS_DIR#./}" -SKILLS_DIR="./$SKILLS_DIR" - -SKILL_PATH="$SKILLS_DIR/$SKILL_NAME" - -# Check if skill already exists -if [ -d "$SKILL_PATH" ] && [ "$DRY_RUN" = false ]; then - log_error "技能已存在: $SKILL_PATH" - log_error "请更换名称或先删除已有技能目录。" - exit 1 -fi - -# Dry run output -if [ "$DRY_RUN" = true ]; then - log_info "预览模式 - 将会创建:" - echo " $SKILL_PATH/" - echo " $SKILL_PATH/SKILL.md" - echo "" - echo "模板内容预览:" - echo "---" - cat << TEMPLATE -name: $SKILL_NAME -description: "[TODO: 用一句话说明技能作用与触发场景]" ---- - -# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') - -[TODO: 简要说明技能目的] - -## Quick Reference - -| Situation | Action | -|-----------|--------| -| [触发条件] | [执行动作] | - -## Usage - -[TODO: 详细使用说明] - -## Examples - -[TODO: 补充具体示例] - -## Source Learning - -本技能由学习条目提炼生成。 -- Learning ID: [TODO: 填写原始学习条目 ID] -- Original File: improvement/learnings.md -TEMPLATE - echo "---" - exit 0 -fi - -# Create skill directory structure -log_info "正在创建技能: $SKILL_NAME" - -mkdir -p "$SKILL_PATH" - -# Create SKILL.md from template -cat > "$SKILL_PATH/SKILL.md" << TEMPLATE ---- -name: $SKILL_NAME -description: "[TODO: 用一句话说明技能作用与触发场景]" ---- - -# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') - -[TODO: 简要说明技能目的] - -## Quick Reference - -| Situation | Action | -|-----------|--------| -| [触发条件] | [执行动作] | - -## Usage - -[TODO: 详细使用说明] - -## Examples - -[TODO: 补充具体示例] - -## Source Learning - -本技能由学习条目提炼生成。 -- Learning ID: [TODO: 填写原始学习条目 ID] -- Original File: improvement/learnings.md -TEMPLATE - -log_info "已创建: $SKILL_PATH/SKILL.md" - -# Suggest next steps -echo "" -log_info "技能脚手架创建成功!" -echo "" -echo "下一步建议:" -echo " 1. 编辑 $SKILL_PATH/SKILL.md" -echo " 2. 用你的学习内容填写 TODO 区块" -echo " 3. 若有详细文档,新增 references/ 目录" -echo " 4. 若有可执行脚本,新增 scripts/ 目录" -echo " 5. 在原学习条目中更新:" -echo " **Status**: promoted_to_skill" -echo " **Skill-Path**: skills/$SKILL_NAME" diff --git a/tests/test_mcp_adapter.py b/tests/test_mcp_adapter.py index 267bb242..65bf44a7 100644 --- a/tests/test_mcp_adapter.py +++ b/tests/test_mcp_adapter.py @@ -155,6 +155,14 @@ class McpAdapterTests(unittest.TestCase): self.assertIn("oclaw", parts) self.assertIn("_local", parts) + def test_trilium_env_aliases_from_legacy_names(self) -> None: + from runtime.operations.mcp_env import _apply_trilium_env_aliases + + vals = {"TRILIUM_URL": "http://127.0.0.1:37840/etapi", "TRILIUM_TOKEN": "secret"} + _apply_trilium_env_aliases(vals) + self.assertEqual(vals["TRILIUM_API_URL"], "http://127.0.0.1:37840/etapi") + self.assertEqual(vals["TRILIUM_API_TOKEN"], "secret") + def test_mcp_env_allowlist_keys_default_when_unset(self) -> None: from runtime.operations import mcp_env