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.ts
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*/78import type { HookHandler } from 'openclaw/hooks';910const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';11const REMINDER_PATH = REMINDER_NAME;1213const REMINDER_CONTENT = `## Self-Improvement Reminder1415After completing tasks, evaluate whether any learnings should be captured.1617Only log if this repo or workspace is using the self-improvement skill.1819Before logging:20- Create only missing \`.learnings/\` files; never overwrite existing content21- Do not log secrets, tokens, private keys, environment variables, or raw transcripts22- Prefer short summaries or redacted excerpts over full command output2324**Log when:**25- User corrects you → \`.learnings/LEARNINGS.md\`26- Command/operation fails → \`.learnings/ERRORS.md\`27- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`28- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`29- You find a better approach → \`.learnings/LEARNINGS.md\`3031**Promote when pattern is proven:**32- Behavioral patterns → \`SOUL.md\`33- Workflow improvements → \`AGENTS.md\`34- Tool gotchas → \`TOOLS.md\`3536Keep entries simple: date, title, what happened, and what to do differently.`;3738function isObject(value: unknown): value is Record<string, unknown> {39return !!value && typeof value === 'object';40}4142function isInjectedReminderFile(value: unknown): boolean {43if (!isObject(value) || value.path !== REMINDER_PATH) {44return false;45}4647return (48value.virtual === true ||49value.content === REMINDER_CONTENT50);51}5253const handler: HookHandler = 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};110111export default handler;112