# Personal Assistant Patterns

How Dobby supports personal assistant use cases without hardcoding any features.

## Design Principle

Personal assistant capabilities are NOT dedicated features. They emerge from composing Dobby's existing infrastructure:

1. **Scheduled tasks** (cron/interval) — "every morning at 8am"
2. **MCP tool access** — Slack, Gmail, Calendar, Notion, GitHub
3. **Memory/preferences system** — persistent user preferences agents respect
4. **Notification channels** — Slack messages, thread replies

A user says "every morning check my unreplied emails" and Dobby creates a cron task that uses the Gmail MCP tool, respects the user's email preferences, and posts results to Slack.

## User Preferences

User preferences tell agents HOW to behave. They are stored via the `dobby_user_prefs` MCP tool (or the REST API) and automatically injected into every agent's prompt.

### Setting Preferences

Via MCP tool (from Claude Code):
```
dobby_user_prefs(action="set", key="email-reply-policy", value="I always reply to emails from my advisor (weiyan@) myself. Never auto-reply.")
dobby_user_prefs(action="set", key="exercise-time", value="Remind me to exercise at 6pm weekdays")
dobby_user_prefs(action="set", key="meeting-notes-format", value="Use bullet points with action items at the top")
```

Via REST API:
```bash
curl -X POST http://localhost:8420/memories \
  -H 'Content-Type: application/json' \
  -d '{"key": "pref:email-reply-policy", "value": "Never auto-reply to emails", "type": "preference"}'
```

### Querying Preferences

Agents can query preferences at runtime:
```
dobby_user_prefs(action="list")                    # All preferences
dobby_user_prefs(action="get", key="email-reply-policy")  # Specific preference
dobby_user_prefs(action="search", query="email")   # Search preferences
```

### How Preferences Flow to Agents

1. When Dobby dispatches a task, it fetches all `type="preference"` memories
2. The prompt builder injects them into the agent's prompt under `# User Preferences`
3. The agent sees them as constraints and respects them during execution
4. Agents can also call `dobby_user_prefs` at runtime for additional context

## Scheduling Patterns

### Morning Digest (Cron)

"Every morning at 8am, check my unreplied emails and Slack messages":
```
dobby_add_task(
    project="personal",
    description="Check Gmail for unreplied emails from the last 24h. Check Slack DMs I haven't responded to. Summarize in a Slack message to me, grouped by urgency.",
    cron="0 8 * * *"
)
```

### Deadline Tracker (Daily Cron)

"Every Monday morning, review upcoming deadlines":
```
dobby_add_task(
    project="personal",
    description="Check my calendar and email for upcoming deadlines this week. List them by date with days remaining. Flag anything due within 3 days.",
    cron="0 9 * * 1"
)
```

### Recurring Reminder (Interval)

"Remind me to stretch every 2 hours":
```
dobby_add_task(
    project="personal",
    description="Send me a Slack DM reminder to stand up and stretch.",
    every="2h"
)
```

### Post-Meeting Notes (After-Trigger)

For meeting transcripts, set up a task that processes them:
```
dobby_add_task(
    project="personal",
    description="Process the latest meeting transcript. Extract: 1) key decisions, 2) action items with owners, 3) follow-up questions. Post summary to the meeting's Slack channel.",
    cron="0 */1 * * *"
)
```

## Example Use Cases

### Email Digest / Unreplied Tracker

**Setup:**
1. Set preferences: `pref:email-priority-contacts` = "advisor@, collaborator@, program-manager@"
2. Set preferences: `pref:email-reply-policy` = "I reply to advisor emails myself. Summarize but don't suggest replies."
3. Create scheduled task with `cron="0 8 * * *"` and description: "Check Gmail for unreplied emails, prioritize contacts from my preferences, summarize"

**How it works:**
- 8am daily: Dobby dispatches the task
- Agent receives user preferences in its prompt
- Agent uses Gmail MCP tool to fetch unreplied emails
- Agent groups by priority (using preference contacts list)
- Agent posts digest to user's Slack DM
- Agent does NOT auto-reply (respects email-reply-policy preference)

### Slack Unreplied Tracker

**Setup:**
1. Set preferences: `pref:slack-important-channels` = "#research, #paper-deadlines, DMs"
2. Create cron task: "Check Slack for unread DMs and mentions I haven't responded to in the last 24h"

### Daily Todo List Generator

**Setup:**
1. Set preferences: `pref:work-hours` = "9am-6pm, prefer deep work in morning"
2. Set preferences: `pref:active-projects` = "SPARE training, ICML paper, grant proposal"
3. Create cron task: "Generate my daily todo. Check calendar for meetings, review active project deadlines, and create a prioritized list factoring in my work-hours preference."

### Project Timeline Manager

**Setup:**
1. Store milestones in memory: `dobby_remember(key="milestone:icml-deadline", value="2026-05-01: ICML camera-ready due", type="project")`
2. Create weekly cron task: "Review all project milestones in memory. Flag anything due within 2 weeks. Check for conflicts (multiple deadlines same week)."

### Physical / Non-Automatable Reminders

**Setup:**
```
dobby_add_task(project="personal", description="Send Slack reminder: Time to exercise!", cron="0 18 * * 1-5")
dobby_add_task(project="personal", description="Send Slack reminder: Practice conference talk (15 min)", cron="0 10 * * *", until="2026-04-15")
```

## Available MCP Services

The prompt builder tells agents which external services are available:

| Service | Capabilities |
|---------|-------------|
| **Slack** | Read/send messages, list channels, search conversations |
| **Gmail** | Read emails, search inbox, list unreplied messages (read-only) |
| **Google Calendar** | Read events, check meetings and deadlines |
| **Notion** | Read/write pages and databases |
| **GitHub** | Create PRs, read issues, manage repositories |

To add new services, update `AVAILABLE_MCP_SERVICES` in `dobby/core/prompt_builder.py`.

## Architecture

```
User: "every morning check my unreplied emails"
  ↓
Dobby creates scheduled task (cron="0 8 * * *")
  ↓
Every 8am: QueueManager marks task as due
  ↓
Daemon fetches user preferences from store
  ↓
PromptBuilder injects:
  - Task description
  - User preferences (email-reply-policy, priority-contacts, etc.)
  - Available MCP services (Gmail, Slack, Calendar, etc.)
  ↓
Claude Code agent executes:
  1. Calls Gmail MCP tool → gets unreplied emails
  2. Checks preferences → groups by priority contacts
  3. Calls Slack MCP tool → posts digest to user
  4. Respects "don't auto-reply" preference
  ↓
Result stored, next_run_at computed for tomorrow 8am
```

## Key Design Decisions

1. **Read-only by default**: Email/Slack tools should default to read-only. Never auto-reply or send messages on behalf of the user unless explicitly configured.

2. **Preferences over hardcoding**: Instead of building an "email digest feature", we build a preference system + scheduled tasks. The agent figures out the rest.

3. **Composable**: Any combination of schedule + MCP tools + preferences creates a new capability. No new code needed.

4. **Graceful degradation**: If a MCP service is unavailable, the agent reports it rather than failing silently. The task result captures what happened.

5. **User control**: Preferences can be updated anytime. Adding `until` to scheduled tasks lets reminders expire naturally.
