Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enable an AI agent to iteratively improve its own skills and instructions based on task feedback.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
hooks/openclaw/handler.js
1/**2* Self-Improvement Hook for OpenClaw3*4* Injects a reminder to evaluate learnings during agent bootstrap.5* Fires on agent:bootstrap event before workspace files are injected.6*/78const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';9const REMINDER_PATH = REMINDER_NAME;1011const REMINDER_CONTENT = `12## Self-Improvement Reminder1314After completing tasks, evaluate whether any learnings should be captured.1516Only log if this repo or workspace is using the self-improvement skill.1718Before logging:19- Create only missing \`.learnings/\` files; never overwrite existing content20- Do not log secrets, tokens, private keys, environment variables, or raw transcripts21- Prefer short summaries or redacted excerpts over full command output2223**Log when:**24- User corrects you → \`.learnings/LEARNINGS.md\`25- Command/operation fails → \`.learnings/ERRORS.md\`26- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`27- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`28- You find a better approach → \`.learnings/LEARNINGS.md\`2930**Promote when pattern is proven:**31- Behavioral patterns → \`SOUL.md\`32- Workflow improvements → \`AGENTS.md\`33- Tool gotchas → \`TOOLS.md\`3435Keep entries simple: date, title, what happened, and what to do differently.36`.trim();3738function isObject(value) {39return !!value && typeof value === 'object';40}4142function isInjectedReminderFile(value) {43if (!isObject(value) || value.path !== REMINDER_PATH) {44return false;45}4647return (48value.virtual === true ||49value.content === REMINDER_CONTENT50);51}5253const handler = async (event) => {54// Safety checks for event structure55if (!event || typeof event !== 'object') {56return;57}5859// Only handle agent:bootstrap events60if (event.type !== 'agent' || event.action !== 'bootstrap') {61return;62}6364// Safety check for context65if (!event.context || typeof event.context !== 'object') {66return;67}6869// Skip sub-agent sessions to avoid bootstrap issues70// Sub-agents have sessionKey patterns like "agent:main:subagent:..."71const sessionKey = event.sessionKey || '';72if (sessionKey.includes(':subagent:')) {73return;74}7576// Inject the reminder as a virtual bootstrap file77// Check that bootstrapFiles is an array before pushing78if (Array.isArray(event.context.bootstrapFiles)) {79const occupiedByOtherFile = event.context.bootstrapFiles.some(80(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),81);82if (occupiedByOtherFile) {83return;84}8586const cleanedBootstrapFiles = event.context.bootstrapFiles.filter(87(file, index, files) =>88!isInjectedReminderFile(file) ||89files.findIndex((candidate) => isInjectedReminderFile(candidate)) === index,90);9192const reminderFile = {93name: REMINDER_NAME,94path: REMINDER_PATH,95content: REMINDER_CONTENT,96missing: false,97virtual: true,98};99100const existingIndex = cleanedBootstrapFiles.findIndex((file) => isInjectedReminderFile(file));101if (existingIndex === -1) {102cleanedBootstrapFiles.push(reminderFile);103} else {104cleanedBootstrapFiles[existingIndex] = reminderFile;105}106107event.context.bootstrapFiles = cleanedBootstrapFiles;108}109};110111module.exports = handler;112module.exports.default = handler;113