Skip to content

Commit 0ed8a64

Browse files
air-captainclaude
andauthored
[recipes] Clean up Life Engine: add state table, harden permissions, fix skill divergence (#135)
* [recipes] Update life-engine schema: user_id TEXT, add weekly_review/cron_state types - Changed user_id from UUID to TEXT across all 5 tables (supports Telegram chat_id as identifier without UUID padding hacks) - Added weekly_review and cron_state to briefing_type check constraint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Clean up Life Engine: add state table, simplify loop timing, fix skill divergence - Add life_engine_state key-value table for runtime state (cron job ID, sleep schedule) instead of overloading briefing log with cron_state type - Remove cron_state from briefing_type CHECK constraint - Simplify Dynamic Loop Timing from 6 tiers to 4 (15m/30m/60m/one-shot) - Replace duplicate embedded skill in README with pointer to life-engine-skill.md - Add user_responded update logic to Rule 7 for self-improvement engagement tracking - Add timezone note to skill time windows - Fix platform references to include Discord alongside Telegram - Add RLS comment explaining why no row policies are needed - Update metadata.json date Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Harden Life Engine permissions: lead with settings.json allowlist, scope MCP tools - Restructure Step 6 to recommend settings.json allowlist as default (Option A) - Replace broad mcp__open-brain__* and mcp__supabase__* wildcards with specific tool names (search_thoughts, list_thoughts, execute_sql, etc.) - Include CronCreate and CronDelete in the default allowlist - Demote --dangerously-skip-permissions to Option D (testing only) - Update Quick Setup and Step 7 launch commands to use settings.json approach - Addresses HIGH finding from security audit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Add rain forecast to Life Engine morning briefing via Open-Meteo - Add Weather section to skill with Open-Meteo API call (free, no API key) - Include rain windows with time ranges and probability in morning briefing - Default coordinates: Portland, OR (45.52, -122.68), configurable via life_engine_state - Only show rain line when precipitation_probability >= 30% - Update schema comment to document latitude/longitude state keys Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Add Daily Capture, portable customizations, and manual sync rule to Life Engine Backport portable customizations from installed SKILL.md into the recipe: date anchor, database note, user identity, valid briefing types, proactive chat_id, rules 9-14. Add Daily Capture prompt in evening window with capture_thought integration. Add Rule 14 requiring manual sync between recipe and installed skill files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Fix hallucinated column name: briefings table uses 'content' not 'summary' Add explicit column reference note to prevent the LLM from hallucinating a 'summary' column on life_engine_briefings — the correct column is 'content'. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Address PR review: Discord support, migration steps, permission docs Fixes all issues from PR #135 review: - P1: Add Bash(date/curl) and capture_thought to README allowlist examples - P1: Make channel event handling platform-agnostic (Telegram + Discord) in skill Rules 7, 10, 11 and Channel Tools section - P1: Add upgrade migration steps to schema.sql for user_id UUID→TEXT - P2: Add CHECK constraint on delivered_via ('telegram', 'discord') - P2: Add single-user assumption comment on life_engine_state table - Bump version to 1.1.0, update date to 2026-04-01 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [recipes] Broaden Bash permission to Bash(*) — scoped patterns are fragile Scoped Bash patterns like Bash(date *) and Bash(curl -s *api.open-meteo.com*) break when the LLM varies its exact command syntax between runs, causing silent permission blocks during unattended operation. Replace with Bash(*) since Life Engine only uses benign read-only commands (date, curl) and Rule 11 prevents dangerous execution from external triggers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06b5b6c commit 0ed8a64

4 files changed

Lines changed: 257 additions & 240 deletions

File tree

recipes/life-engine/README.md

Lines changed: 76 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> One loop. One skill. Claude figures out what matters right now.
44
5-
A self-improving, time-aware personal assistant that runs in the background via Claude Code's `/loop` command. It checks your calendar, surfaces relevant knowledge from your Open Brain, tracks habits and health check-ins, and delivers proactive briefings via Telegram — adapting to your life over time.
5+
A self-improving, time-aware personal assistant that runs in the background via Claude Code's `/loop` command. It checks your calendar, surfaces relevant knowledge from your Open Brain, tracks habits and health check-ins, and delivers proactive briefings via Telegram or Discord — adapting to your life over time.
66

77
**This isn't a calendar tool. It's not a reminder app. It's a personal AI engine that runs your day and gets better at it every week.**
88

@@ -40,14 +40,14 @@ This guide contains everything Claude Code needs to set up your entire Life Engi
4040
5. Pause for you to complete Telegram pairing (requires your phone)
4141
6. Run a test cycle to confirm everything works
4242

43-
After setup, exit and relaunch with your channel:
43+
After setup, exit and relaunch with your channel (permissions are handled by `settings.json` — see Step 6):
4444

4545
```bash
4646
# Telegram
47-
claude --channels plugin:telegram@claude-plugins-official --dangerously-skip-permissions
47+
claude --channels plugin:telegram@claude-plugins-official
4848

4949
# Discord
50-
claude --channels plugin:discord@claude-plugins-official --dangerously-skip-permissions
50+
claude --channels plugin:discord@claude-plugins-official
5151
```
5252

5353
Then start your loop: `/loop 30m /life-engine`
@@ -474,7 +474,7 @@ The Life Engine needs its own tables to track habits, moods, check-ins, and skil
474474

475475
Run the included [`schema.sql`](schema.sql) file in your Supabase SQL Editor. It contains the full schema with CHECK constraints, table comments, GRANT statements for `service_role`, performance indexes, and an auto-update trigger.
476476

477-
**Checkpoint:** Run the verification query at the bottom of `schema.sql` — you should see 5 tables (`life_engine_habits`, `life_engine_habit_log`, `life_engine_checkins`, `life_engine_briefings`, `life_engine_evolution`).
477+
**Checkpoint:** Run the verification query at the bottom of `schema.sql` — you should see 6 tables (`life_engine_habits`, `life_engine_habit_log`, `life_engine_checkins`, `life_engine_briefings`, `life_engine_evolution`, `life_engine_state`).
478478

479479
---
480480

@@ -484,143 +484,11 @@ This is the core — a Claude Code skill that runs on every loop iteration.
484484

485485
### 5.1 Create the Skill File
486486

487-
Create `.claude/skills/life-engine/SKILL.md` in your home directory (or project directory):
487+
Create `.claude/skills/life-engine/SKILL.md` in your home directory (or project directory) and paste the full contents of [`life-engine-skill.md`](life-engine-skill.md) into it.
488488

489-
```markdown
490-
# /life-engine — Proactive Personal Assistant
491-
492-
You are a time-aware personal assistant running on a recurring loop.
493-
Every time this skill fires, determine what the user needs RIGHT NOW
494-
based on the current time, their calendar, and their Open Brain.
495-
496-
## Core Behavior
497-
498-
1. **Time check** — What time is it? What time window am I in?
499-
2. **Duplicate check** — Did I already send something this cycle/today?
500-
3. **Decide** — Based on the time window, what should I be doing?
501-
4. **External pull** — Grab live data from integrations (calendar, etc.)
502-
5. **Internal enrich** — Search Open Brain for context on what you found.
503-
You can't enrich what you haven't seen yet — always external before internal.
504-
6. **Deliver via Telegram** — only if worth it. Silence > noise.
505-
7. **Log what you did** so the next cycle knows what's been covered
506-
507-
## Time Windows
508-
509-
### Early Morning (6 AM - 8 AM)
510-
- Pull today's calendar events
511-
- Count meetings, identify first event
512-
- Check for active habits (morning jog, meditation, etc.)
513-
- Send a **morning briefing** via Telegram:
514-
- "Good morning! Here's your day..."
515-
- List key events with times
516-
- Habit reminders
517-
- Weather note if relevant
518-
519-
### Pre-Meeting (15-45 min before any calendar event)
520-
- Identify the upcoming meeting
521-
- Extract attendee names, meeting title, description
522-
- Search Open Brain for relevant context on attendees/topics
523-
- Send a **meeting prep briefing** via Telegram:
524-
- Who you're meeting with
525-
- What Open Brain knows about them
526-
- Any recent interactions or notes
527-
- Suggested talking points
528-
529-
### Midday (11 AM - 1 PM)
530-
- If no imminent meetings, send a **check-in prompt**:
531-
- "Quick check-in: How are you feeling? Reply with a quick update"
532-
- Log responses to life_engine_checkins table
533-
- Review afternoon calendar
534-
535-
### Afternoon (2 PM - 5 PM)
536-
- Pre-meeting prep (same as above) for afternoon events
537-
- If calendar is clear, review pending tasks or follow-ups
538-
- Surface any Open Brain thoughts tagged as action items
539-
540-
### Evening (5 PM - 7 PM)
541-
- Send a **day summary** via Telegram:
542-
- Meetings attended
543-
- Habits completed today
544-
- Any check-in data logged
545-
- Preview of tomorrow's calendar
546-
547-
### Off Hours (7 PM - 6 AM)
548-
- Do nothing. Respect quiet hours.
549-
- Exception: urgent calendar events in the next hour
550-
551-
## Rules
552-
553-
- **Never send the same briefing twice** — check life_engine_briefings
554-
before sending. If you already sent a morning briefing today, skip it.
555-
- **Be concise** — the user reads on their phone. Use bullet points.
556-
- **When in doubt, do nothing** — silence is better than noise.
557-
- **Respect check-in responses** — if the user replies on Telegram,
558-
log it to the appropriate table.
559-
- **Suggest improvements** — every 7 days, review your briefing log
560-
and suggest one addition or removal to make the skill more useful.
561-
Send the suggestion via Telegram and wait for approval before changing.
562-
563-
## Available Tools
564-
565-
Use these MCP tools:
566-
- `gcal_list_events` — Get calendar events for a date range
567-
- `gcal_get_event` — Get details on a specific event
568-
- Open Brain semantic search — Find relevant knowledge
569-
- `reply` — Send text or files via Telegram channel
570-
- `react` — Acknowledge messages with emoji reactions
571-
- `edit_message` — Update a previously sent message
572-
- Supabase execute_sql — Query/insert Life Engine tables
573-
574-
## Self-Improvement Protocol
575-
576-
Every 7 days (check life_engine_evolution for last suggestion date):
577-
578-
1. Review the past week's briefing log
579-
2. Identify patterns:
580-
- Which briefings did the user respond to? (high value)
581-
- Which briefings got no response? (low value / noise)
582-
- Did the user manually ask Claude for something repeatedly?
583-
(candidate for automation)
584-
3. Propose ONE change via Telegram:
585-
- "I noticed you always check your OB1 before client calls.
586-
Want me to add automatic client prep briefings?"
587-
- "You haven't responded to the midday check-ins in 2 weeks.
588-
Should I drop those?"
589-
4. Wait for user approval
590-
5. Log the change to life_engine_evolution (approved: true/false)
591-
6. If approved, update your behavior accordingly
592-
593-
## Message Format
594-
595-
Use this format for Telegram messages:
596-
597-
Morning briefing:
598-
☀️ **Good morning!**
599-
📅 You have [N] events today
600-
[Time][Event name]
601-
[Time][Event name]
602-
🏃 Habit reminder: [habit name]
603-
604-
Pre-meeting:
605-
📋 **Prep: [Meeting name] in [N] min**
606-
👥 With: [attendees]
607-
🧠 From your brain: [relevant OB1 context]
608-
💡 Suggested: [talking points]
609-
610-
Check-in:
611-
💬 **Quick check-in**
612-
How are you feeling? Reply with a quick update
613-
and I'll log it.
614-
615-
Evening summary:
616-
🌙 **Day wrap-up**
617-
📅 [N] meetings today
618-
✅ Habits: [completed] / [total]
619-
📊 Mood: [if logged]
620-
📅 Tomorrow: [first event]
621-
```
622-
623-
**Checkpoint:** The skill file exists in `.claude/skills/`.
489+
This file contains the complete Life Engine behavior: the 7-step core loop, time windows, message formats, self-improvement protocol, dynamic loop timing with automatic rescheduling, and all operational rules. It is the single source of truth for how Life Engine behaves — any customizations you make should be made there.
490+
491+
**Checkpoint:** The skill file exists at `.claude/skills/life-engine/SKILL.md` and matches the contents of `life-engine-skill.md`.
624492

625493
> **Note:** In the previous version of this recipe, a separate `/check-telegram` polling skill was required to read incoming messages. With Channels, that's no longer needed — Telegram messages are pushed directly into your Claude Code session in real time. Claude reads and responds to them inline.
626494
@@ -637,73 +505,93 @@ Life Engine runs autonomously via `/loop`. If Claude encounters a tool it doesn'
637505

638506
| Approach | Best For | Risk Level |
639507
|----------|----------|------------|
640-
| **`--dangerously-skip-permissions`** | Always-on setups on a dedicated, trusted machine | High — bypasses ALL checks |
508+
| **`settings.json` allowlist** *(recommended)* | Scoped permissions that persist across sessions | Low — scoped + persistent |
509+
| **`--allowedTools` (CLI flag)** | Same scoping, but must be re-typed each launch | Low — scoped |
641510
| **`--permission-mode auto`** | A middle ground — automatic but with some guardrails | Medium |
642-
| **`--allowedTools` (CLI flag)** | Fine-grained — approve only the tools Life Engine needs | Low — scoped |
643-
| **`settings.json` allowlist** | Same as above, but persisted in config instead of CLI | Low — scoped + persistent |
511+
| **`--dangerously-skip-permissions`** | Quick testing on a dedicated, trusted machine | High — bypasses ALL checks |
644512

645-
### 6.2 Option A: Skip Permissions (Simplest for Dedicated Machines)
513+
### 6.2 Option A: settings.json Allowlist (Recommended)
646514

647-
For a machine you fully trust (e.g., a Mac Mini running Life Engine in a persistent terminal):
515+
Pre-approve only the specific tools Life Engine needs, persisted in your config so you don't have to re-type them every session. Create or update `.claude/settings.json`:
648516

649-
```bash
650-
# Use whichever channel you set up in Step 1
651-
claude --channels plugin:telegram@claude-plugins-official --dangerously-skip-permissions
652-
claude --channels plugin:discord@claude-plugins-official --dangerously-skip-permissions
517+
```json
518+
{
519+
"permissions": {
520+
"allow": [
521+
"mcp__plugin_telegram_telegram__reply",
522+
"mcp__plugin_telegram_telegram__react",
523+
"mcp__plugin_telegram_telegram__edit_message",
524+
"mcp__google-calendar__gcal_list_events",
525+
"mcp__google-calendar__gcal_get_event",
526+
"mcp__open-brain__search_thoughts",
527+
"mcp__open-brain__list_thoughts",
528+
"mcp__open-brain__thought_stats",
529+
"mcp__open-brain__capture_thought",
530+
"mcp__supabase__execute_sql",
531+
"Bash(*)",
532+
"CronCreate",
533+
"CronDelete"
534+
]
535+
}
536+
}
653537
```
654538

655-
> [!CAUTION]
656-
> This means Claude can run any tool, any bash command, write any file — without asking. Only use this on a machine and in an environment you fully trust.
657-
658-
### 6.3 Option B: Auto Permission Mode
539+
> **Why `Bash(*)` instead of scoped patterns?** Life Engine uses `date` (date anchor) and `curl` (weather API) — both benign, read-only commands. Scoped patterns like `Bash(date *)` or `Bash(curl -s *api.open-meteo.com*)` are fragile because the LLM may vary its exact command syntax between runs, causing silent permission blocks. `Bash(*)` eliminates this fragility while MCP tools remain individually scoped above. Rule 11 (prompt injection guard) prevents dangerous Bash execution from external triggers.
659540
660-
A less extreme alternative. Claude can take actions automatically but still respects certain guardrails:
541+
Then launch with just the channel flag:
661542

662543
```bash
663-
claude --channels plugin:telegram@claude-plugins-official --permission-mode auto
544+
# Telegram
545+
claude --channels plugin:telegram@claude-plugins-official
546+
547+
# Discord
548+
claude --channels plugin:discord@claude-plugins-official
664549
```
665550

666-
(Swap `telegram` for `discord` if using Discord.)
551+
> **Note:** The exact tool names depend on how you named your MCP servers. Run `/mcp` in Claude Code to see your server names, then match them here. If you use Discord instead of Telegram, replace the `mcp__plugin_telegram_telegram__` entries with the corresponding Discord tool names.
667552
668-
### 6.4 Option C: Allowlisted Tools (Most Precise)
553+
### 6.3 Option B: --allowedTools (CLI Flag)
669554

670-
Pre-approve only the specific tools Life Engine uses. You can pass them on the CLI:
555+
Same scoping as Option A, but passed on the command line instead of persisted in config. Useful if you want different permission sets for different sessions:
671556

672557
```bash
673558
claude --channels plugin:telegram@claude-plugins-official \
674-
--allowedTools "Bash Read Write Edit \
675-
mcp__plugin_telegram_telegram__reply \
559+
--allowedTools "mcp__plugin_telegram_telegram__reply \
676560
mcp__plugin_telegram_telegram__react \
677561
mcp__plugin_telegram_telegram__edit_message \
678562
mcp__google-calendar__gcal_list_events \
679563
mcp__google-calendar__gcal_get_event \
680-
mcp__open-brain__* \
681-
mcp__supabase__*"
564+
mcp__open-brain__search_thoughts \
565+
mcp__open-brain__list_thoughts \
566+
mcp__open-brain__thought_stats \
567+
mcp__open-brain__capture_thought \
568+
mcp__supabase__execute_sql \
569+
'Bash(*)' \
570+
CronCreate CronDelete"
682571
```
683572

684-
Or persist them in `.claude/settings.json`:
573+
### 6.4 Option C: Auto Permission Mode
685574

686-
```json
687-
{
688-
"permissions": {
689-
"allow": [
690-
"mcp__plugin_telegram_telegram__reply",
691-
"mcp__plugin_telegram_telegram__react",
692-
"mcp__plugin_telegram_telegram__edit_message",
693-
"mcp__google-calendar__gcal_list_events",
694-
"mcp__google-calendar__gcal_get_event",
695-
"mcp__open-brain__*",
696-
"mcp__supabase__*"
697-
]
698-
}
699-
}
575+
A middle ground. Claude can take actions automatically but still respects certain guardrails:
576+
577+
```bash
578+
claude --channels plugin:telegram@claude-plugins-official --permission-mode auto
700579
```
701580

702-
> **Note:** The exact tool names depend on how you named your MCP servers. Run `/mcp` in Claude Code to see your server names, then match them here. The `__*` wildcard approves all tools from that server.
581+
(Swap `telegram` for `discord` if using Discord.)
703582

704-
If you're using the [Dynamic Loop Timing](#dynamic-loop-timing) feature from the skill, also add `CronCreate` and `CronDelete`.
583+
### 6.5 Option D: Skip Permissions (Testing Only)
705584

706-
### 6.5 Test Before You Walk Away
585+
For initial setup and testing on a machine you fully trust:
586+
587+
```bash
588+
claude --channels plugin:telegram@claude-plugins-official --dangerously-skip-permissions
589+
```
590+
591+
> [!CAUTION]
592+
> This means Claude can run any tool, any bash command, write any file — without asking. Use this for initial testing, then switch to Option A for daily operation.
593+
594+
### 6.6 Test Before You Walk Away
707595

708596
1. Start Claude Code with your chosen permission strategy
709597
2. Run `/life-engine` manually
@@ -721,15 +609,17 @@ If you're using the [Dynamic Loop Timing](#dynamic-loop-timing) feature from the
721609

722610
### 7.1 Start Claude Code with Channels and Permissions
723611

612+
If you configured `settings.json` in Step 6 (recommended), just launch with the channel flag:
613+
724614
```bash
725615
# Telegram
726-
claude --channels plugin:telegram@claude-plugins-official --dangerously-skip-permissions
616+
claude --channels plugin:telegram@claude-plugins-official
727617

728618
# Discord
729-
claude --channels plugin:discord@claude-plugins-official --dangerously-skip-permissions
619+
claude --channels plugin:discord@claude-plugins-official
730620
```
731621

732-
Or swap `--dangerously-skip-permissions` for your preferred permission strategy from Step 6.
622+
Or append your preferred permission flag from Step 6 if you didn't use `settings.json`.
733623

734624
### 7.2 Test the Skill Manually
735625

@@ -749,7 +639,7 @@ Once you've confirmed it works:
749639
/loop 30m /life-engine
750640
```
751641

752-
That's it. Claude will now check in every 30 minutes and decide if you need anything. When you reply on Telegram, the channel pushes your message directly into the session — Claude reads and responds inline, no separate polling loop needed.
642+
That's it. Claude will now check in every 30 minutes and decide if you need anything. When you reply on Telegram or Discord, the channel pushes your message directly into the session — Claude reads and responds inline, no separate polling loop needed.
753643

754644
> **Note:** Loop jobs and channels are session-only — they stop when Claude Code exits. For persistent operation, keep a Claude Code session running on a dedicated machine or persistent terminal, or restart when you begin your day.
755645
@@ -790,7 +680,7 @@ After 7 days of data, Claude reviews its own performance:
790680
- Which ones did you ignore?
791681
- What did you ask for manually that could be automated?
792682

793-
It sends you a suggestion via Telegram. You approve or reject. The skill evolves.
683+
It sends you a suggestion via your messaging channel. You approve or reject. The skill evolves.
794684

795685
### Beyond: It's Yours
796686
Over weeks and months, your Life Engine accumulates:

0 commit comments

Comments
 (0)