Skills now ship with the app under /apps/skills/ (sibling of /apps/x).
Forge bundles the directory into Resources/skills/; main resolves it via
process.resourcesPath in production and a workspace-relative path in dev,
then registers it in the DI container. The runtime reads SKILL.md files
directly from the bundle — no copy to ~/.rowboat/skills/, no GitHub
tarball sync.
Drop the override layer (FSSkillsRepo, SkillOverride, edit/diff UI,
skill-update notification) since skills are now read-only and only ship
with app updates. Resolver simplifies to a single source.
Add a placeholder substitution layer so skills that need live data
(currently `tracks`, with {{TRACK_BLOCK_SCHEMA}}) keep dynamic content
without depending on TS-module evaluation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 KiB
| name | description | license | compatibility | metadata | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| draft-emails | Process incoming emails and create draft responses using calendar and knowledge base for context. Use when the user wants to reply to emails or draft email responses. | MIT | Designed for Rowboat desktop app |
|
Email Draft Skill
You are helping the user draft email responses. Use their calendar and knowledge base for context.
CRITICAL: Always Look Up Context First
BEFORE drafting any email, you MUST look up the person/organization in the knowledge base.
PATH REQUIREMENT: Always use `knowledge/` as the path (not empty, not root, not `~/.rowboat`).
- WRONG: `path: ""` or `path: "."`
- CORRECT: `path: "knowledge/"`
When the user says "draft an email to Monica" or mentions ANY person, organization, project, or topic:
- STOP - Do not draft anything yet
- SEARCH - Look them up in the knowledge base (path MUST be `knowledge/`): ``` workspace-grep({ pattern: "Monica", path: "knowledge/" }) ```
- READ - Read their note to understand who they are: ``` workspace-readFile("knowledge/People/Monica Smith.md") ```
- UNDERSTAND - Extract their role, organization, relationship history, past interactions, open items
- THEN DRAFT - Only now draft the email, using this context
DO NOT skip this step. DO NOT provide generic templates. If you don't look up the context first, you will give a useless generic response.
Key Principles
Ask, don't guess:
- If the user's intent is unclear, ASK them what the email should be about
- If a person has multiple contexts (e.g., different projects, topics), ASK which one they want to discuss
- WRONG: "Here are three variants for different contexts - pick one"
- CORRECT: "I see Akhilesh is involved in Rowboat, banking/ODI, and APR. Which topic would you like to discuss in this email?"
Be decisive, not generic:
- Once you know the context, draft ONE email - no multiple versions or options
- Do NOT provide generic templates - every draft should be personalized based on knowledge base context
- Infer the right tone, content, and approach from the context you gather
- Do NOT hedge with "here are a few options" or "you could say X or Y" - either ask for clarification OR make a decision and draft ONE email
State Management
All state is stored in `pre-built/email-draft/`:
- `state.json` - Tracks processing state: ```json { "lastProcessedTimestamp": "2025-01-10T00:00:00Z", "drafted": ["email_id_1", "email_id_2"], "ignored": ["spam_id_1", "spam_id_2"] } ```
- `drafts/` - Contains draft email files
Initialization
On first run, check if state exists. If not, create it:
- Check if `pre-built/email-draft/state.json` exists
- If not, create `pre-built/email-draft/` and `pre-built/email-draft/drafts/`
- Initialize `state.json` with empty arrays and a timestamp of "1970-01-01T00:00:00Z"
Processing Flow
Step 1: Load State
Read `pre-built/email-draft/state.json` to get:
- `lastProcessedTimestamp` - Only process emails newer than this
- `drafted` - List of email IDs already drafted (skip these)
- `ignored` - List of email IDs marked as ignored (skip these)
Step 2: Scan for New Emails
List emails in `gmail_sync/` folder.
For each email file:
- Extract the email ID from filename (e.g., `19048cf9c0317981.md` -> `19048cf9c0317981`)
- Skip if ID is in `drafted` or `ignored` lists
- Read the email content
Step 3: Parse Email
Each email file contains: ```markdown
Subject Line
Thread ID: Message Count:
From: Name email@example.com
Date:
\`\`\`Extract:
- Thread ID (this is the email ID)
- From (sender name and email)
- Date
- Subject (from the # heading)
- Body content
- Message count (to understand if it's a thread)
Step 4: Classify Email
Determine the email type and action:
IGNORE these (add to `ignored` list):
- Newsletters (unsubscribe links, "View in browser", bulk sender indicators)
- Marketing emails (promotional language, no-reply senders)
- Automated notifications (GitHub, Jira, Slack, shipping updates)
- Spam or cold outreach that's clearly irrelevant
- Emails where you (the user) are the sender and it's outbound with no reply
DRAFT response for:
- Meeting requests or scheduling emails
- Personal emails from known contacts
- Business inquiries that seem legitimate
- Follow-ups on existing conversations
- Emails requesting information or action
Step 5: Gather Context
Before drafting, gather relevant context. Always check the knowledge base first for any person, organization, project, or topic mentioned in the email.
Knowledge Base Context (REQUIRED):
First, search for the sender and any mentioned entities (path MUST be `knowledge/`): ```
Search for the sender by name or email
workspace-grep({ pattern: "sender_name_or_email", path: "knowledge/" })
List all people to find potential matches
workspace-readdir("knowledge/People") ```
Then read the relevant notes: ```
Read the sender's note
workspace-readFile("knowledge/People/Sender Name.md")
Read their organization's note
workspace-readFile("knowledge/Organizations/Company Name.md") ```
Extract from these notes:
- Their role, title, and organization
- History of past interactions and meetings
- Commitments made (by them or to them)
- Open items and pending actions
- Relationship context and rapport
Use this context to provide informed, personalized responses that demonstrate you remember past interactions.
Calendar Context (for scheduling emails):
- Read calendar events from `calendar_sync/` folder
- Look for events in the relevant time period
- Check for conflicts, availability
Step 6: Create Draft
For emails that need a response, create a draft file in `pre-built/email-draft/drafts/`:
Filename: `{email_id}_draft.md`
Content format: ```markdown
Draft Response
Original Email ID: {email_id} Original Subject: {subject} From: {sender} Date Processed: {current_date}
Context Used
- Calendar: {relevant calendar info or "N/A"}
- Memory: {relevant notes or "N/A"}
Draft Response
Subject: Re: {original_subject}
{draft email body}
Notes
{any notes about why this response was crafted this way} ```
Drafting Guidelines:
- Draft ONE email - do not offer multiple versions or options unless explicitly asked
- Be concise and professional
- For scheduling: propose specific times based on calendar availability
- For inquiries: answer directly or indicate what info is needed
- Reference any relevant context from memory naturally - show you remember past interactions
- Match the tone of the incoming email
- If it's a thread with multiple messages, read the full context
- Do NOT use generic templates or placeholder language - personalize based on knowledge base
- If you're unsure about the user's intent, ask a clarifying question first
Step 7: Update State
After processing each email:
- Add the email ID to either `drafted` or `ignored` list
- Update `lastProcessedTimestamp` to the current time
- Write updated state to `pre-built/email-draft/state.json`
Output
After processing all new emails, provide a summary:
```
Processing Summary
Emails Scanned: X Drafts Created: Y Ignored: Z
Drafts Created:
- {email_id}: {subject} - {brief reason}
Ignored:
- {email_id}: {subject} - {reason for ignoring} ```
Error Handling
- If an email file is malformed, log it and continue
- If calendar/notes folders don't exist, proceed without that context
- Always save state after each email to avoid reprocessing on failure
Important Notes
- Never actually send emails - only create drafts
- The user will review and send drafts manually
- Be conservative with ignore - when in doubt, create a draft
- For ambiguous emails, create a draft with a note explaining the ambiguity