diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6b8710a --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.git diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2fe0ce0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[Makefile] +indent_style = tab diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml new file mode 100644 index 0000000..cf47c93 --- /dev/null +++ b/.gitea/workflows/check.yml @@ -0,0 +1,9 @@ +name: check +on: [push] +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Build and check + run: docker build . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5a0882 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# OS +.DS_Store +Thumbs.db + +# Editors +*.swp +*.swo +*~ +*.bak +.idea/ +.vscode/ +*.sublime-* + +# Node +node_modules/ + +# Environment / secrets +.env +.env.* +*.pem +*.key diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6b1d0bf --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +LICENSE diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..7b2a067 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "proseWrap": "always", + "tabWidth": 4 +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bccda7b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +# v22.22.0 2025-05-27 +FROM node:22-slim@sha256:2f3571cb4deb87a2e2992eaa5ed75d99ad4d348eb7cabe6a62f8f0a3bc9b29c7 + +WORKDIR /app +COPY . . +RUN npm install -g prettier && make check diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..34edefe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 sneak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..686d11c --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +.PHONY: fmt fmt-check lint test check hooks + +fmt: + npx prettier --write . + +fmt-check: + npx prettier --check . + +lint: + @echo "No linter configured for docs-only repo" + +test: + @echo "No tests for docs-only repo" + +check: fmt-check + @echo "All checks passed" + +hooks: + echo '#!/bin/sh\nmake check' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit + +docker: + docker build . diff --git a/OPENCLAW_TRICKS.md b/OPENCLAW_TRICKS.md index e2b3a68..dd820d4 100644 --- a/OPENCLAW_TRICKS.md +++ b/OPENCLAW_TRICKS.md @@ -1,7 +1,7 @@ # OPENCLAW_TRICKS.md — Configuration Recipes for OpenClaw Agents -A collection of tested patterns, prompts, and file structures for configuring -an OpenClaw agent as a proactive personal assistant. These are extracted from a +A collection of tested patterns, prompts, and file structures for configuring an +OpenClaw agent as a proactive personal assistant. These are extracted from a production setup that's been running since early 2026. --- @@ -14,11 +14,11 @@ the big picture so you understand the design decisions. ### The Starting Point -OpenClaw gives you a persistent workspace directory and the ability to run -tools (shell, web, browser, messaging). Out of the box, the agent wakes up -fresh every session with no memory of what happened before. The core challenge -is: **how do you turn a stateless LLM into a stateful personal assistant that -knows who you are, where you are, what you need, and what it was working on?** +OpenClaw gives you a persistent workspace directory and the ability to run tools +(shell, web, browser, messaging). Out of the box, the agent wakes up fresh every +session with no memory of what happened before. The core challenge is: **how do +you turn a stateless LLM into a stateful personal assistant that knows who you +are, where you are, what you need, and what it was working on?** The answer is files. The workspace IS the agent's brain. Every piece of state, every rule, every memory lives in files that the agent reads on startup and @@ -33,12 +33,11 @@ instructions the agent didn't need for most tasks. The solution was **factoring out rulesets into focused files:** -- **AGENTS.md** — the master file. Responsibilities, session startup - procedure, high-level rules. Think of it as the table of contents that - points to everything else. -- **SOUL.md** — personality, tone, values. Separated because it's - philosophical, not procedural. Also: it's fun to let the agent evolve this - one over time. +- **AGENTS.md** — the master file. Responsibilities, session startup procedure, + high-level rules. Think of it as the table of contents that points to + everything else. +- **SOUL.md** — personality, tone, values. Separated because it's philosophical, + not procedural. Also: it's fun to let the agent evolve this one over time. - **USER.md** — info about the human. Timezone, communication preferences, basics the agent needs every session. - **MEMORY.md** — curated long-term memory. Only loaded in private sessions @@ -83,8 +82,8 @@ on-demand. This was probably the single most impactful addition. It's a JSON file (`memory/daily-context.json`) that every session reads on every message. It -tracks the current state of the human: where they are, what timezone they're -in, whether they're sleeping, when they last took meds, when they last sent a +tracks the current state of the human: where they are, what timezone they're in, +whether they're sleeping, when they last took meds, when they last sent a message. Why JSON instead of markdown? Because it's machine-readable. The agent can @@ -92,8 +91,8 @@ update individual fields programmatically, and the structure is unambiguous. Markdown is great for instructions; JSON is great for state. The daily context file means: -- The agent always knows the human's timezone (critical for a frequent - traveler) + +- The agent always knows the human's timezone (critical for a frequent traveler) - Medication reminders fire based on actual timestamps, not guesses - Sleep predictions inform when to send alerts vs stay quiet - Location tracking happens automatically @@ -108,13 +107,13 @@ key facts, lessons learned. Think of this as what a human would "just know" about their life. During heartbeats, the agent periodically reviews recent daily files and -promotes significant items to MEMORY.md, and removes stale info. It's -explicitly modeled on how human memory works: raw experience gets processed -into lasting knowledge, and irrelevant details fade. +promotes significant items to MEMORY.md, and removes stale info. It's explicitly +modeled on how human memory works: raw experience gets processed into lasting +knowledge, and irrelevant details fade. -The security model: MEMORY.md is only loaded in private (1:1 DM) sessions. -In group chats, the agent works from daily files and daily-context.json only. -This prevents personal context from leaking into shared channels. +The security model: MEMORY.md is only loaded in private (1:1 DM) sessions. In +group chats, the agent works from daily files and daily-context.json only. This +prevents personal context from leaking into shared channels. ### Medication Tracking @@ -123,18 +122,18 @@ This is a safety-critical system. The design is deliberately paranoid: - **CSV as source of truth.** Not the daily-context boolean, not the agent's memory — the CSV log file is authoritative. - **Double-verification before any action.** The daily context has a - `hasTakenDailyMedsToday` boolean AND a `dailyMedsTimestamp`. A midnight - cron resets the boolean. But if the human is in a timezone ahead of the - server, the reset happens at the wrong time. So the rule is: always verify - the timestamp falls on today's date in the human's timezone. The boolean - is a convenience hint, never the source of truth. + `hasTakenDailyMedsToday` boolean AND a `dailyMedsTimestamp`. A midnight cron + resets the boolean. But if the human is in a timezone ahead of the server, the + reset happens at the wrong time. So the rule is: always verify the timestamp + falls on today's date in the human's timezone. The boolean is a convenience + hint, never the source of truth. - **Interval medications anchored to actual doses.** Some meds are "every 5 days" or "every 14 days." The next dose date is calculated from the last actual dose timestamp, not from the intended schedule. If a dose is missed, the next date is unknown until the missed dose is taken and logged. - **Overdose prevention.** The agent blocks logging if it detects a same-day - duplicate batch. This is the highest-priority safety rule — a duplicate - daily batch of certain medications could cause cardiac arrest. + duplicate batch. This is the highest-priority safety rule — a duplicate daily + batch of certain medications could cause cardiac arrest. - **Escalating alerts.** If daily meds are >26h since last dose, the agent escalates aggressively — ntfy push notification if chat messages go unacknowledged. @@ -144,17 +143,18 @@ keeping context costs low for non-medication conversations. ### Sleep Tracking -The agent infers sleep from activity gaps rather than requiring explicit -"I'm going to sleep" statements. On every message, it checks: was there a -gap since the last message that overlaps with the predicted sleep window? If -so, log sleep start = last activity before gap, wake = first activity after. +The agent infers sleep from activity gaps rather than requiring explicit "I'm +going to sleep" statements. On every message, it checks: was there a gap since +the last message that overlaps with the predicted sleep window? If so, log sleep +start = last activity before gap, wake = first activity after. Activity isn't just chat messages — it includes Gitea commits, comments, scheduled flight departures, and any other observable actions. -Sleep predictions drift over time (the human in this setup tends to sleep -~30min later each day), so the agent tracks the trend and extrapolates. -Caffeine intake adjusts predictions forward. The predictions feed into: +Sleep predictions drift over time (the human in this setup tends to sleep ~30min +later each day), so the agent tracks the trend and extrapolates. Caffeine intake +adjusts predictions forward. The predictions feed into: + - When to send alerts vs stay quiet - Sitrep "sleep conflict" warnings (appointment during predicted sleep) - General awareness of the human's schedule @@ -162,31 +162,32 @@ Caffeine intake adjusts predictions forward. The predictions feed into: ### Location & Timezone Tracking The agent always knows where the human is. This matters because: -- Times must always be displayed in the human's local timezone, not the - server's timezone + +- Times must always be displayed in the human's local timezone, not the server's + timezone - "Today" and "tomorrow" mean different things in different timezones - Medication timing needs to account for timezone changes - Flight prep needs local airport info The landing checklist (triggered automatically after every flight) updates -location, timezone, nearest airport, and lodging in the daily context file. -It also checks if any cron jobs have hardcoded timezones that need updating. +location, timezone, nearest airport, and lodging in the daily context file. It +also checks if any cron jobs have hardcoded timezones that need updating. ### The Gitea Notification Poller -OpenClaw has heartbeats, but those are periodic (every ~30min). For Gitea -issues and PRs, we wanted near-realtime response. The solution: a tiny Python -script that polls the Gitea notifications API every 2 seconds and wakes the -agent via OpenClaw's `/hooks/wake` endpoint when new notifications arrive. +OpenClaw has heartbeats, but those are periodic (every ~30min). For Gitea issues +and PRs, we wanted near-realtime response. The solution: a tiny Python script +that polls the Gitea notifications API every 2 seconds and wakes the agent via +OpenClaw's `/hooks/wake` endpoint when new notifications arrive. Key design decisions: -- **The poller never marks notifications as read.** That's the agent's job - after it processes them. This prevents the poller and agent from racing. + +- **The poller never marks notifications as read.** That's the agent's job after + it processes them. This prevents the poller and agent from racing. - **It tracks notification IDs, not counts.** This way it only fires on genuinely new notifications, not re-reads of existing ones. -- **The wake message tells the agent to route output to Gitea/Mattermost, - not to DM.** This prevents chatty notification processing from disturbing - the human. +- **The wake message tells the agent to route output to Gitea/Mattermost, not to + DM.** This prevents chatty notification processing from disturbing the human. - **Zero dependencies.** Just Python stdlib (`urllib`, `json`, `time`). Runs anywhere. @@ -328,30 +329,30 @@ if __name__ == "__main__": main() ``` -Run it as a background service (launchd on macOS, systemd on Linux) with the -env vars set. It's intentionally simple — no frameworks, no async, no -dependencies. +Run it as a background service (launchd on macOS, systemd on Linux) with the env +vars set. It's intentionally simple — no frameworks, no async, no dependencies. ### The Daily Diary -Every day gets a `memory/YYYY-MM-DD.md` file. The agent appends a topic -summary to a table after every meaningful conversation: +Every day gets a `memory/YYYY-MM-DD.md` file. The agent appends a topic summary +to a table after every meaningful conversation: ```markdown ## Topics -| Time (TZ) | Topic | -| ---------- | ------------------------------------------------- | -| 14:30 | Discussed PR review workflow | -| 16:00 | Medication logged | -| 18:45 | Flight prep blocks created for tomorrow's flight | +| Time (TZ) | Topic | +| --------- | ------------------------------------------------ | +| 14:30 | Discussed PR review workflow | +| 16:00 | Medication logged | +| 18:45 | Flight prep blocks created for tomorrow's flight | ``` This serves multiple purposes: + - Any session can see what's been discussed today without loading full conversation history -- The agent can use `sessions_history` to get details on a specific topic - if needed +- The agent can use `sessions_history` to get details on a specific topic if + needed - During memory maintenance, the agent reviews these to decide what's worth promoting to MEMORY.md - It's a simple audit trail of what happened @@ -361,8 +362,7 @@ This serves multiple purposes: One of the most important rules: **every single requirement, preference, or instruction the human provides MUST be captured in a file immediately.** Not "mentally noted" — written to disk. Because the agent wakes up fresh every -session, a "mental note" is worthless. If it's not in a file, it didn't -happen. +session, a "mental note" is worthless. If it's not in a file, it didn't happen. This applies to everything: project rules ("no mocks in tests"), workflow preferences ("fewer PRs, don't over-split"), corrections, new policies. @@ -370,13 +370,14 @@ Immediate write to the daily file, and to MEMORY.md if it's a standing rule. ### PII-Aware Output Routing -A lesson learned the hard way: **the audience determines what you can say, -not who asked.** If the human asks for a medication status report in a group +A lesson learned the hard way: **the audience determines what you can say, not +who asked.** If the human asks for a medication status report in a group channel, the agent can't just dump it there — other people can read it. The -rule: if the output would contain PII and the channel isn't private, redirect -to DM and reply in-channel with "sent privately." +rule: if the output would contain PII and the channel isn't private, redirect to +DM and reply in-channel with "sent privately." This is enforced at multiple levels: + - AGENTS.md has a warning banner at the top - The checklist system catches it before action - Channel-specific rule files (like our `memory/JAMES_CHAT.md`) define what's @@ -386,28 +387,29 @@ This is enforced at multiple levels: When spawning coding sub-agents for PR work, each one MUST clone to a fresh temporary directory. Never share git clones between agents — dirty working -directories cause false CI results, merge conflicts, and wrong file state. -The rule: `cd $(mktemp -d) && git clone . && ...` +directories cause false CI results, merge conflicts, and wrong file state. The +rule: `cd $(mktemp -d) && git clone . && ...` ### The Heartbeat System OpenClaw polls the agent periodically with a configurable prompt. The agent -reads HEARTBEAT.md and decides what to do. We keep HEARTBEAT.md small -(focused checklist) to minimize token burn on these frequent checks. +reads HEARTBEAT.md and decides what to do. We keep HEARTBEAT.md small (focused +checklist) to minimize token burn on these frequent checks. The heartbeat handles: + - Gitea inbox triage (check for new assignments) - Flight prep block creation (look ahead 7 days) - Open project review (can I take a step on anything?) - Workspace sync (commit and push changes) - Periodic memory maintenance -State tracking in `memory/heartbeat-state.json` prevents redundant checks -(e.g., don't re-check email if you checked 10 minutes ago). +State tracking in `memory/heartbeat-state.json` prevents redundant checks (e.g., +don't re-check email if you checked 10 minutes ago). -The key output rule: heartbeats should either be `HEARTBEAT_OK` (nothing to -do) or a direct alert. Work narration goes to a designated status channel, -never to the human's DM. +The key output rule: heartbeats should either be `HEARTBEAT_OK` (nothing to do) +or a direct alert. Work narration goes to a designated status channel, never to +the human's DM. ### Putting It All Together @@ -417,8 +419,8 @@ The system works as a loop: 2. **Message arrives** → check daily-context for state changes (sleep gap? location change? meds overdue?), respond to the message, update state 3. **Heartbeat fires** → check inbox, projects, flights, sync workspace -4. **External event** (Gitea poller wake) → process notification, respond - via appropriate channel +4. **External event** (Gitea poller wake) → process notification, respond via + appropriate channel 5. **Session ends** → state persists in files for next session The files are the continuity. The agent is stateless; the workspace is not. @@ -481,7 +483,8 @@ Before doing anything else: 1. Read `SOUL.md` — this is who you are 2. Read `USER.md` — this is who you're helping -3. Read `memory/daily-context.json` — current state (location, timezone, sleep, meds) +3. Read `memory/daily-context.json` — current state (location, timezone, sleep, + meds) 4. Read `memory/YYYY-MM-DD.md` (today + yesterday) for recent context 5. **If in MAIN SESSION** (direct chat with your human): Also read `MEMORY.md` @@ -502,23 +505,23 @@ reads on every message. It tracks the current state of your human. ```json { - "isSleeping": false, - "lastKnownWakeTime": "2026-02-28T12:30:00+07:00", - "predictedSleepTime": "2026-03-01T05:30:00+07:00", - "predictedWakeTime": "2026-03-01T12:45:00+07:00", - "hasTakenDailyMedsToday": false, - "dailyMedsTimestamp": "2026-02-27T11:43:00+07:00", - "lastCaffeineTimestamp": null, - "currentLocation": "City (IATA)", - "currentTimezone": "Asia/Bangkok", - "currentLodging": "Hotel Name", - "travelLeg": "Description of current travel phase", - "nearestAirport": "ICAO code", - "lastMessageFromUser": "2026-02-28T05:20:00+07:00", - "lastMessageChannel": "mattermost", - "isAvailable": true, - "lastUpdated": "2026-02-28T06:00:00+07:00", - "lastSleep": "actual: 2026-02-28 05:15-12:30 ICT (~7h15m). Stable pattern." + "isSleeping": false, + "lastKnownWakeTime": "2026-02-28T12:30:00+07:00", + "predictedSleepTime": "2026-03-01T05:30:00+07:00", + "predictedWakeTime": "2026-03-01T12:45:00+07:00", + "hasTakenDailyMedsToday": false, + "dailyMedsTimestamp": "2026-02-27T11:43:00+07:00", + "lastCaffeineTimestamp": null, + "currentLocation": "City (IATA)", + "currentTimezone": "Asia/Bangkok", + "currentLodging": "Hotel Name", + "travelLeg": "Description of current travel phase", + "nearestAirport": "ICAO code", + "lastMessageFromUser": "2026-02-28T05:20:00+07:00", + "lastMessageChannel": "mattermost", + "isAvailable": true, + "lastUpdated": "2026-02-28T06:00:00+07:00", + "lastSleep": "actual: 2026-02-28 05:15-12:30 ICT (~7h15m). Stable pattern." } ``` @@ -533,6 +536,7 @@ directly. ``` ### Why it works: + - Every session reads this first, so the agent always knows where you are - Sleep predictions help the agent decide when to alert vs stay quiet - Medication timestamps enable overdue detection @@ -555,33 +559,32 @@ dynamically** based on what it thinks you should know. When the user says "sitrep", provide a concise summary covering: 1. **Daily meds** — taken today or not -2. **Interval meds** — any due/overdue or upcoming in next 48h. - ALWAYS include "X days from now" countdown. ALWAYS include day of week. +2. **Interval meds** — any due/overdue or upcoming in next 48h. ALWAYS include + "X days from now" countdown. ALWAYS include day of week. 3. **Open issues assigned to me** — count 4. **Open issues assigned to user** — count + brief summary -5. **Upcoming travel** — flights in next 72h with times/airports. - ALWAYS also include the next known flight even if beyond 72h. +5. **Upcoming travel** — flights in next 72h with times/airports. ALWAYS also + include the next known flight even if beyond 72h. 6. **Upcoming appointments** — next 48h 7. **Todo list** — overdue, due today, due tomorrow (separate categories) 8. **Unbooked travel** — flights that need booking. Flag deadlines. -9. **Sleep conflicts** — appointments in next 48h that fall within - predicted sleep window +9. **Sleep conflicts** — appointments in next 48h that fall within predicted + sleep window 10. **Sleep** — one-line summary of last sleep + drift trend + concerns -11. **Unanswered questions** — questions the agent asked that haven't - been answered +11. **Unanswered questions** — questions the agent asked that haven't been + answered 12. **Weather alerts** — only if significant or extreme. Use METAR from aviationweather.gov. Omit if nothing notable. 13. **Overdue reminders** — anything pending 14. **Open projects** — one-line status per project -Before generating a sitrep, review the last 3 days of daily memory files -for context. **If anything notable isn't covered by the items above — -recent lessons, pending decisions, things you think the user should know -about — add additional sections as needed.** +Before generating a sitrep, review the last 3 days of daily memory files for +context. **If anything notable isn't covered by the items above — recent +lessons, pending decisions, things you think the user should know about — add +additional sections as needed.** -Omit any item that is "none" or zero. Keep it scannable. Use bullet -points, not prose. Only surface things the user might not know about or -needs to act on. +Omit any item that is "none" or zero. Keep it scannable. Use bullet points, not +prose. Only surface things the user might not know about or needs to act on. ``` ### Key design decisions: @@ -600,8 +603,8 @@ needs to act on. ### Time display tip: ```markdown -Include current time in local timezone, UTC, and any other relevant timezone -at the top. +Include current time in local timezone, UTC, and any other relevant timezone at +the top. ``` This anchors the reader and makes all the relative times unambiguous. @@ -626,28 +629,28 @@ Date,SleepTime,WakeTime,TimeZone,Duration,Status,Notes ```markdown ### Sleep Tracking -- **`memory/sleep-log.csv`** — columns: Date, SleepTime, WakeTime, - TimeZone, Duration, Status, Notes +- **`memory/sleep-log.csv`** — columns: Date, SleepTime, WakeTime, TimeZone, + Duration, Status, Notes - Infer sleep/wake times from message activity and explicit statements - User's sleep pattern drifts later each day; typical duration 4–8 hours - Update daily-context.json isSleeping field accordingly -- **On every message from user:** if there was a communication gap - overlapping predicted sleep time, infer a sleep window (sleep start = - last activity before gap, wake = first activity after gap). Activity - includes direct messages, git commits/comments, scheduled flight - departures, and any other observable actions — not just chat messages. - Log based on observed gaps only, never from mathematical predictions. +- **On every message from user:** if there was a communication gap overlapping + predicted sleep time, infer a sleep window (sleep start = last activity before + gap, wake = first activity after gap). Activity includes direct messages, git + commits/comments, scheduled flight departures, and any other observable + actions — not just chat messages. Log based on observed gaps only, never from + mathematical predictions. ``` ### Daily context fields for sleep: ```json { - "isSleeping": false, - "lastKnownWakeTime": "2026-02-28T12:30:00+07:00", - "predictedSleepTime": "2026-03-01T05:30:00+07:00", - "predictedWakeTime": "2026-03-01T12:45:00+07:00", - "lastSleep": "actual: 2026-02-28 05:15-12:30 ICT (~7h15m). Stable pattern." + "isSleeping": false, + "lastKnownWakeTime": "2026-02-28T12:30:00+07:00", + "predictedSleepTime": "2026-03-01T05:30:00+07:00", + "predictedWakeTime": "2026-03-01T12:45:00+07:00", + "lastSleep": "actual: 2026-02-28 05:15-12:30 ICT (~7h15m). Stable pattern." } ``` @@ -665,7 +668,7 @@ uses predictions to: ```json { - "lastCaffeineTimestamp": "2026-02-10T00:45:00-08:00" + "lastCaffeineTimestamp": "2026-02-10T00:45:00-08:00" } ``` @@ -702,6 +705,7 @@ Date,City,Country,Timezone OpenClaw runs on a server (probably in a fixed timezone). Your human travels. Without explicit timezone tracking: + - "Take your meds" fires at 3am local time - "Today" means the wrong day - Sleep predictions are off by hours @@ -742,14 +746,14 @@ Date,Time,TimeZone,Medication,Dosage - Maintain cumulative CSV logs split by month as the authoritative record - CSV columns (fixed order): Date,Time,TimeZone,Medication,Dosage -- "Today"/"yesterday"/"tomorrow" always mean relative to currentTimezone - in daily-context.json — NEVER relative to system clock +- "Today"/"yesterday"/"tomorrow" always mean relative to currentTimezone in + daily-context.json — NEVER relative to system clock - Always spell medication names correctly; silently correct transcription errors ## Daily Medications -One batch per calendar day unless explicitly confirmed otherwise. "Log my -daily meds" = log each as a separate row. +One batch per calendar day unless explicitly confirmed otherwise. "Log my daily +meds" = log each as a separate row. **CRITICAL: Never trust hasTakenDailyMedsToday without verifying dailyMedsTimestamp.** @@ -761,9 +765,9 @@ dailyMedsTimestamp.** **Ideal dosing window:** every 24h ±4h. Hard minimum: 14h between doses. -| Medication | Dosage | -| ------------ | ------ | -| (your meds) | (dose) | +| Medication | Dosage | +| ----------- | ------ | +| (your meds) | (dose) | ## Interval-Based Medications @@ -774,6 +778,7 @@ Scheduling anchored to last actual dose, NOT intended dates. | (med name) | (dose) | Every X days | **Missed dose rules:** + - If missed, next dose date is unknown until the missed dose is taken and logged - Interval restarts from actual ingestion timestamp - Missed doses block future scheduling @@ -781,9 +786,9 @@ Scheduling anchored to last actual dose, NOT intended dates. ## Safety Rules (HIGHEST PRIORITY) 1. Always triple-check before instructing any medication: - - Last actual dose taken - - Required dosing interval - - Current eligibility without overdose risk + - Last actual dose taken + - Required dosing interval + - Current eligibility without overdose risk 2. If any ambiguity exists, **do not instruct dosing** 3. Immediately block logging if same-day duplicate daily-med batch or other overdose pattern appears @@ -812,20 +817,20 @@ Scheduling anchored to last actual dose, NOT intended dates. ### Overdue escalation: ```markdown -**CRITICAL: If daily meds are >26h since last dose, escalate aggressively.** -Use urgent notification channel if chat messages go unacknowledged. -Do not let this slide silently. Always include hours since last dose in -reminders (e.g. "daily meds overdue — last dose was 27h ago"). +**CRITICAL: If daily meds are >26h since last dose, escalate aggressively.** Use +urgent notification channel if chat messages go unacknowledged. Do not let this +slide silently. Always include hours since last dose in reminders (e.g. "daily +meds overdue — last dose was 27h ago"). ``` ### Why the double-check on the boolean: The daily-context.json has a `hasTakenDailyMedsToday` boolean AND a -`dailyMedsTimestamp`. A midnight-reset cron flips the boolean to false. But -if the user is in a timezone ahead of the server, the boolean may reset -before their actual "today" ends — or after it began. The rule: **always -verify the timestamp falls on today's date in the user's timezone.** The -boolean is a convenience hint, not the source of truth. +`dailyMedsTimestamp`. A midnight-reset cron flips the boolean to false. But if +the user is in a timezone ahead of the server, the boolean may reset before +their actual "today" ends — or after it began. The rule: **always verify the +timestamp falls on today's date in the user's timezone.** The boolean is a +convenience hint, not the source of truth. --- @@ -851,8 +856,8 @@ Date,FlightNumber,Origin,Destination,Duration,Alliance ```markdown ## Flight Prep Blocks (daily) -Run `khal list today 7d`. For flights missing "shower and dress" / -"travel to airport" blocks, create them: +Run `khal list today 7d`. For flights missing "shower and dress" / "travel to +airport" blocks, create them: - shower_start = flight_departure - airport_buffer - travel_buffer - 1h - Airport buffer: 2h domestic, 2.5h international @@ -927,10 +932,10 @@ completed. Every session can read these. ## Topics -| Time | Topic | Session | -|------|-------|---------| -| 14:30 | Discussed PR review workflow | main | -| 16:00 | Medication logged | main | +| Time | Topic | Session | +| ----- | ---------------------------- | ------- | +| 14:30 | Discussed PR review workflow | main | +| 16:00 | Medication logged | main | ## Notes @@ -993,15 +998,19 @@ stay quiet. # HEARTBEAT.md ## Inbox Check (PRIORITY) + (check notifications, issues, emails — whatever applies) ## Flight Prep Blocks (daily) + (create calendar prep blocks for upcoming flights) ## Open Projects Review + (check project status, take next steps, or ask if blocked) ## Workspace Sync + (commit and push workspace changes) ## Rules @@ -1013,6 +1022,7 @@ stay quiet. ## Output Rules Never send internal thinking or status narration to user's DM. Output should be: + - HEARTBEAT_OK (if nothing needs attention) - A direct question or alert (if action needed) - Work narration → send to a status channel via message tool @@ -1022,13 +1032,13 @@ Never send internal thinking or status narration to user's DM. Output should be: ```json { - "lastChecks": { - "email": 1703275200, - "calendar": 1703260800, - "weather": null, - "gitea": 1703280000 - }, - "lastWeeklyDocsReview": "2026-02-24" + "lastChecks": { + "email": 1703275200, + "calendar": 1703260800, + "weather": null, + "gitea": 1703280000 + }, + "lastWeeklyDocsReview": "2026-02-24" } ``` @@ -1036,12 +1046,14 @@ Never send internal thinking or status narration to user's DM. Output should be: ```markdown **Use heartbeat when:** + - Multiple checks can batch together - You need conversational context from recent messages - Timing can drift slightly (every ~30 min is fine) - You want to reduce API calls by combining periodic checks **Use cron when:** + - Exact timing matters ("9:00 AM sharp every Monday") - Task needs isolation from main session history - You want a different model or thinking level for the task @@ -1068,8 +1080,8 @@ Find issues/PRs assigned to me or where I'm @-mentioned → do the work. - When nothing is assigned to me, my Gitea work is done - PRs assigned to user = their queue, not my backlog -Workflow: issue → branch → PR "(closes #X)" → review/rework → assign user -when all checks pass + reviewed. +Workflow: issue → branch → PR "(closes #X)" → review/rework → assign user when +all checks pass + reviewed. ### Rules @@ -1082,10 +1094,10 @@ when all checks pass + reviewed. ### Notification poller: -A Python script that polls the Gitea notifications API and injects events -into OpenClaw sessions. It runs via launchd/systemd. Ask me for the full -source code if you want to set this up — I can share it (it's a -general-purpose tool with no PII). +A Python script that polls the Gitea notifications API and injects events into +OpenClaw sessions. It runs via launchd/systemd. Ask me for the full source code +if you want to set this up — I can share it (it's a general-purpose tool with no +PII). --- @@ -1099,8 +1111,7 @@ For complex coding tasks, spawn isolated sub-agents. ### Sub-Agent Git Isolation (MANDATORY) - NEVER let multiple sub-agents share the same git clone -- Each sub-agent MUST clone to a unique temporary directory - (e.g. `mktemp -d`) +- Each sub-agent MUST clone to a unique temporary directory (e.g. `mktemp -d`) - When spawning, always instruct: `cd $(mktemp -d) && git clone . && ...` - Dirty working directories cause false CI results @@ -1146,12 +1157,14 @@ Rules for when the agent is in group chats with multiple people: ### Know When to Speak **Respond when:** + - Directly mentioned or asked a question - You can add genuine value (info, insight, help) - Something witty/funny fits naturally - Correcting important misinformation **Stay silent when:** + - Just casual banter between humans - Someone already answered the question - Your response would just be "yeah" or "nice" @@ -1163,6 +1176,7 @@ Quality > quantity. ### React Like a Human Use emoji reactions naturally: + - Appreciate something but don't need to reply → 👍, ❤️ - Something made you laugh → 😂 - Interesting/thought-provoking → 🤔, 💡 @@ -1180,12 +1194,12 @@ Never lose a rule or preference your human states: ```markdown ### Requirement Capture (MANDATORY) -- **Every single requirement, rule, preference, or instruction the user - provides MUST be captured in the daily memory file immediately** -- This includes: project rules, workflow preferences, corrections, new - policies, technical decisions -- If the user says something should be done a certain way, write it down - in memory/YYYY-MM-DD.md AND in MEMORY.md if it's a standing rule +- **Every single requirement, rule, preference, or instruction the user provides + MUST be captured in the daily memory file immediately** +- This includes: project rules, workflow preferences, corrections, new policies, + technical decisions +- If the user says something should be done a certain way, write it down in + memory/YYYY-MM-DD.md AND in MEMORY.md if it's a standing rule - Nothing is to be missed. If in doubt, log it. ``` @@ -1193,9 +1207,9 @@ Never lose a rule or preference your human states: ## PII Output Routing — Audience-Aware Responses -A critical security pattern: **the audience determines what you can say, not -who asked.** If your human asks for a sitrep (or any PII-containing info) in a -group channel, you can't just dump it there — other people can read it. +A critical security pattern: **the audience determines what you can say, not who +asked.** If your human asks for a sitrep (or any PII-containing info) in a group +channel, you can't just dump it there — other people can read it. ### AGENTS.md / checklist prompt: @@ -1213,26 +1227,29 @@ group channel, you can't just dump it there — other people can read it. ### Why this matters: -This is a real failure mode. If someone asks "sitrep" in a group channel and -you respond with medication names, partner details, travel dates, and hotel -names — you just leaked all of that to everyone in the channel. The human -asking is authorized to see it; the channel audience is not. Always check -WHERE you're responding, not just WHO asked. +This is a real failure mode. If someone asks "sitrep" in a group channel and you +respond with medication names, partner details, travel dates, and hotel names — +you just leaked all of that to everyone in the channel. The human asking is +authorized to see it; the channel audience is not. Always check WHERE you're +responding, not just WHO asked. --- ## General Tips ### Bias toward action -Don't ask blocking questions with obvious answers. Figure it out. Come back -with results, not requests for permission. + +Don't ask blocking questions with obvious answers. Figure it out. Come back with +results, not requests for permission. ### Checklists over trust + For safety-critical operations (meds, deployments, external communications), maintain checklists in `memory/checklist-*.md` and reference them from AGENTS.md. The agent reads the checklist before acting. ### State repo + Keep your workspace in a git repo that auto-syncs. This gives you version history and recovery: @@ -1250,6 +1267,7 @@ history and recovery: ## On Errors When something goes wrong: + 1. Identify what input caused the wrong output 2. Fix the input (rule, checklist, prompt, automation) 3. Move on @@ -1262,9 +1280,9 @@ No apologies. No promises. Fix the system. ```markdown ## On Integrity -Never cheat. When a check fails, fix the code — don't weaken the check. -When a test fails, fix the bug — don't loosen the assertion. When a linter -flags something, fix the finding — don't suppress the rule. +Never cheat. When a check fails, fix the code — don't weaken the check. When a +test fails, fix the bug — don't loosen the assertion. When a linter flags +something, fix the finding — don't suppress the rule. ``` --- diff --git a/README.md b/README.md index ef0cd1b..f2c80a4 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ agent) under the direction of [@sneak](https://sneak.berlin). for OpenClaw agents: workspace bootstrapping, daily context state files, sitrep (situation report) prompts, sleep tracking, medication tracking, timezone-aware location tracking, flight/travel logging, memory architecture, - heartbeat configuration, Gitea integration, sub-agent management, and - security patterns. + heartbeat configuration, Gitea integration, sub-agent management, and security + patterns. ## Purpose @@ -43,11 +43,11 @@ This repository is **public to the entire internet.** The publishing agent - **Credentials:** API keys, tokens, passwords, SSH keys, webhook URLs with secrets, ntfy topic IDs - **Actual log data:** medication logs, sleep logs, flight logs, location logs, - daily memory files — only the *format/schema* is shared, never the data + daily memory files — only the _format/schema_ is shared, never the data - **Infrastructure details:** specific hostnames, IP addresses, Tailscale/VPN configurations, server names -- **Private repository contents:** code from private repos, issue contents, - PR discussions +- **Private repository contents:** code from private repos, issue contents, PR + discussions - **Financial information:** account numbers, transaction details ### Pre-Publish Checklist @@ -56,8 +56,8 @@ Before every commit to this repo, the agent: 1. Scans the diff for PII patterns (names, emails, addresses, phone numbers, coordinates, hostnames, API keys, tokens) -2. Verifies all examples use placeholder values (`YOUR-TOPIC-ID`, - `example.com`, `(your meds)`, etc.) +2. Verifies all examples use placeholder values (`YOUR-TOPIC-ID`, `example.com`, + `(your meds)`, etc.) 3. Confirms no real log data is included — only format specifications 4. Reviews for infrastructure leaks (internal hostnames, IP ranges, VPN details) 5. Gets explicit approval from the repo owner for new document types