mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-14 15:03:19 +08:00
重构仓库目录为统一的 runtime 分层并清理历史 openclaw 残留。
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。 Made-with: Cursor
This commit is contained in:
parent
ba3836f00f
commit
4a23b715a2
498 changed files with 2760 additions and 2200 deletions
23
runtime/skills/self-improvement/hooks/runtime/HOOK.md
Normal file
23
runtime/skills/self-improvement/hooks/runtime/HOOK.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: self-improvement
|
||||
description: "Injects self-improvement reminder during agent bootstrap"
|
||||
metadata: {"oclaw":{"emoji":"🧠","events":["agent:bootstrap"]}}
|
||||
---
|
||||
|
||||
# Self-Improvement Hook
|
||||
|
||||
Injects a reminder to evaluate learnings during agent bootstrap.
|
||||
|
||||
## What It Does
|
||||
|
||||
- Fires on `agent:bootstrap` (before workspace files are injected)
|
||||
- Adds a reminder block to check `.learnings/` for relevant entries
|
||||
- Prompts the agent to log corrections, errors, and discoveries
|
||||
|
||||
## Configuration
|
||||
|
||||
No configuration needed. Enable with:
|
||||
|
||||
```bash
|
||||
oclaw hooks enable self-improvement
|
||||
```
|
||||
112
runtime/skills/self-improvement/hooks/runtime/handler.js
Normal file
112
runtime/skills/self-improvement/hooks/runtime/handler.js
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* Self-Improvement Hook for Oclaw
|
||||
*
|
||||
* Injects a reminder to evaluate learnings during agent bootstrap.
|
||||
* Fires on agent:bootstrap event before workspace files are injected.
|
||||
*/
|
||||
|
||||
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
|
||||
const REMINDER_PATH = REMINDER_NAME;
|
||||
|
||||
const REMINDER_CONTENT = `
|
||||
## Self-Improvement Reminder
|
||||
|
||||
After completing tasks, evaluate whether any learnings should be captured.
|
||||
|
||||
Only log if this repo or workspace is using the self-improvement skill.
|
||||
|
||||
Before logging:
|
||||
- Create only missing \`.learnings/\` files; never overwrite existing content
|
||||
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
|
||||
- Prefer short summaries or redacted excerpts over full command output
|
||||
|
||||
**Log when:**
|
||||
- User corrects you → \`.learnings/LEARNINGS.md\`
|
||||
- Command/operation fails → \`.learnings/ERRORS.md\`
|
||||
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
|
||||
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
|
||||
- You find a better approach → \`.learnings/LEARNINGS.md\`
|
||||
|
||||
**Promote when pattern is proven:**
|
||||
- Behavioral patterns → \`SOUL.md\`
|
||||
- Workflow improvements → \`AGENTS.md\`
|
||||
- Tool gotchas → \`TOOLS.md\`
|
||||
|
||||
Keep entries simple: date, title, what happened, and what to do differently.
|
||||
`.trim();
|
||||
|
||||
function isObject(value) {
|
||||
return !!value && typeof value === 'object';
|
||||
}
|
||||
|
||||
function isInjectedReminderFile(value) {
|
||||
if (!isObject(value) || value.path !== REMINDER_PATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
value.virtual === true ||
|
||||
value.content === REMINDER_CONTENT
|
||||
);
|
||||
}
|
||||
|
||||
const handler = async (event) => {
|
||||
// Safety checks for event structure
|
||||
if (!event || typeof event !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle agent:bootstrap events
|
||||
if (event.type !== 'agent' || event.action !== 'bootstrap') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safety check for context
|
||||
if (!event.context || typeof event.context !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip sub-agent sessions to avoid bootstrap issues
|
||||
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
|
||||
const sessionKey = event.sessionKey || '';
|
||||
if (sessionKey.includes(':subagent:')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the reminder as a virtual bootstrap file
|
||||
// Check that bootstrapFiles is an array before pushing
|
||||
if (Array.isArray(event.context.bootstrapFiles)) {
|
||||
const occupiedByOtherFile = event.context.bootstrapFiles.some(
|
||||
(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),
|
||||
);
|
||||
if (occupiedByOtherFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanedBootstrapFiles = event.context.bootstrapFiles.filter(
|
||||
(file, index, files) =>
|
||||
!isInjectedReminderFile(file) ||
|
||||
files.findIndex((candidate) => isInjectedReminderFile(candidate)) === index,
|
||||
);
|
||||
|
||||
const reminderFile = {
|
||||
name: REMINDER_NAME,
|
||||
path: REMINDER_PATH,
|
||||
content: REMINDER_CONTENT,
|
||||
missing: false,
|
||||
virtual: true,
|
||||
};
|
||||
|
||||
const existingIndex = cleanedBootstrapFiles.findIndex((file) => isInjectedReminderFile(file));
|
||||
if (existingIndex === -1) {
|
||||
cleanedBootstrapFiles.push(reminderFile);
|
||||
} else {
|
||||
cleanedBootstrapFiles[existingIndex] = reminderFile;
|
||||
}
|
||||
|
||||
event.context.bootstrapFiles = cleanedBootstrapFiles;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = handler;
|
||||
module.exports.default = handler;
|
||||
111
runtime/skills/self-improvement/hooks/runtime/handler.ts
Normal file
111
runtime/skills/self-improvement/hooks/runtime/handler.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* Self-Improvement Hook for Oclaw
|
||||
*
|
||||
* Injects a reminder to evaluate learnings during agent bootstrap.
|
||||
* Fires on agent:bootstrap event before workspace files are injected.
|
||||
*/
|
||||
|
||||
import type { HookHandler } from 'oclaw/hooks';
|
||||
|
||||
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
|
||||
const REMINDER_PATH = REMINDER_NAME;
|
||||
|
||||
const REMINDER_CONTENT = `## Self-Improvement Reminder
|
||||
|
||||
After completing tasks, evaluate whether any learnings should be captured.
|
||||
|
||||
Only log if this repo or workspace is using the self-improvement skill.
|
||||
|
||||
Before logging:
|
||||
- Create only missing \`.learnings/\` files; never overwrite existing content
|
||||
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
|
||||
- Prefer short summaries or redacted excerpts over full command output
|
||||
|
||||
**Log when:**
|
||||
- User corrects you → \`.learnings/LEARNINGS.md\`
|
||||
- Command/operation fails → \`.learnings/ERRORS.md\`
|
||||
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
|
||||
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
|
||||
- You find a better approach → \`.learnings/LEARNINGS.md\`
|
||||
|
||||
**Promote when pattern is proven:**
|
||||
- Behavioral patterns → \`SOUL.md\`
|
||||
- Workflow improvements → \`AGENTS.md\`
|
||||
- Tool gotchas → \`TOOLS.md\`
|
||||
|
||||
Keep entries simple: date, title, what happened, and what to do differently.`;
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === 'object';
|
||||
}
|
||||
|
||||
function isInjectedReminderFile(value: unknown): boolean {
|
||||
if (!isObject(value) || value.path !== REMINDER_PATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
value.virtual === true ||
|
||||
value.content === REMINDER_CONTENT
|
||||
);
|
||||
}
|
||||
|
||||
const handler: HookHandler = async (event) => {
|
||||
// Safety checks for event structure
|
||||
if (!event || typeof event !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle agent:bootstrap events
|
||||
if (event.type !== 'agent' || event.action !== 'bootstrap') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safety check for context
|
||||
if (!event.context || typeof event.context !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip sub-agent sessions to avoid bootstrap issues
|
||||
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
|
||||
const sessionKey = event.sessionKey || '';
|
||||
if (sessionKey.includes(':subagent:')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the reminder as a virtual bootstrap file
|
||||
// Check that bootstrapFiles is an array before pushing
|
||||
if (Array.isArray(event.context.bootstrapFiles)) {
|
||||
const occupiedByOtherFile = event.context.bootstrapFiles.some(
|
||||
(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),
|
||||
);
|
||||
if (occupiedByOtherFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanedBootstrapFiles = event.context.bootstrapFiles.filter(
|
||||
(file, index, files) =>
|
||||
!isInjectedReminderFile(file) ||
|
||||
files.findIndex((candidate) => isInjectedReminderFile(candidate)) === index,
|
||||
);
|
||||
|
||||
const reminderFile = {
|
||||
name: REMINDER_NAME,
|
||||
path: REMINDER_PATH,
|
||||
content: REMINDER_CONTENT,
|
||||
missing: false,
|
||||
virtual: true,
|
||||
};
|
||||
|
||||
const existingIndex = cleanedBootstrapFiles.findIndex((file) => isInjectedReminderFile(file));
|
||||
if (existingIndex === -1) {
|
||||
cleanedBootstrapFiles.push(reminderFile);
|
||||
} else {
|
||||
cleanedBootstrapFiles[existingIndex] = reminderFile;
|
||||
}
|
||||
|
||||
event.context.bootstrapFiles = cleanedBootstrapFiles;
|
||||
}
|
||||
};
|
||||
|
||||
export default handler;
|
||||
Loading…
Add table
Add a link
Reference in a new issue