Auto-commit: 2026-05-10 12:34

This commit is contained in:
OpenClaw
2026-05-10 12:34:24 +00:00
parent a62e89ad99
commit aa10d148ec
37 changed files with 4699 additions and 52 deletions

View File

@@ -5,10 +5,20 @@
* 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 if any learnings should be captured:
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\`
@@ -22,9 +32,24 @@ After completing tasks, evaluate if any learnings should be captured:
- Workflow improvements → \`AGENTS.md\`
- Tool gotchas → \`TOOLS.md\`
Keep entries simple: date, title, what happened, what to do differently.
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') {
@@ -41,14 +66,45 @@ const handler = async (event) => {
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)) {
event.context.bootstrapFiles.push({
path: 'SELF_IMPROVEMENT_REMINDER.md',
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;
}
};

View File

@@ -7,9 +7,19 @@
import type { HookHandler } from 'openclaw/hooks';
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
const REMINDER_PATH = REMINDER_NAME;
const REMINDER_CONTENT = `## Self-Improvement Reminder
After completing tasks, evaluate if any learnings should be captured:
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\`
@@ -23,7 +33,22 @@ After completing tasks, evaluate if any learnings should be captured:
- Workflow improvements → \`AGENTS.md\`
- Tool gotchas → \`TOOLS.md\`
Keep entries simple: date, title, what happened, what to do differently.`;
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
@@ -51,11 +76,35 @@ const handler: HookHandler = async (event) => {
// Inject the reminder as a virtual bootstrap file
// Check that bootstrapFiles is an array before pushing
if (Array.isArray(event.context.bootstrapFiles)) {
event.context.bootstrapFiles.push({
path: 'SELF_IMPROVEMENT_REMINDER.md',
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;
}
};