16 Commits

Author SHA1 Message Date
KS Jannette
31618b1acf upped maximum number of emails that can be fetched for review 2026-08-11 02:12:43 -04:00
087839d2bf Merge pull request 'update README' (#2) from FEAT-connector-refinements into master
Reviewed-on: kjannette/deClawed-Assity-Kitty#2
2026-07-30 22:49:31 +00:00
b9337d1b0c Merge branch 'master' into FEAT-connector-refinements 2026-07-30 22:49:25 +00:00
KS Jannette
ee12a44ec1 update README 2026-07-30 18:48:47 -04:00
dbfa913e5e Merge pull request 'Refined connectors' (#1) from FEAT-connector-refinements into master
Reviewed-on: kjannette/deClawed-Assity-Kitty#1
2026-07-30 19:32:14 +00:00
KS Jannette
72ec89a1c6 Refined connectors 2026-07-30 15:24:46 -04:00
KS Jannette
a91c8c0009 hottie 2026-02-22 19:19:46 -05:00
KS Jannette
8874b32334 m 2026-02-20 15:36:29 -05:00
KS Jannette
f3cf4a51bd m 2026-02-20 14:12:41 -05:00
KS Jannette
065645983a more 2026-02-20 14:01:07 -05:00
KS Jannette
98585b52ad more 2026-02-20 13:58:58 -05:00
KS Jannette
ea62edbc21 Tweaker 2026-02-20 13:52:27 -05:00
KS Jannette
21b4e656f6 hotfix 2026-02-20 12:36:46 -05:00
KS Jannette
fc3223a119 more 2026-02-20 12:29:53 -05:00
KS Jannette
9adc9cc3e0 more 2026-02-20 11:55:11 -05:00
S Jannette
7a02c49bd2 Merge pull request #6 from kjannette/refine-mail-processing-prompts
Refine mail processing prompts
2026-02-20 11:53:53 -05:00
11 changed files with 478 additions and 325 deletions

10
.gitignore vendored
View File

@@ -1,11 +1,11 @@
node_modules/ node_modules/
build/ build/
# OAuth secrets # OAuth secrets (inside accountsAndCredentials/, example files are NOT ignored)
credentials.json accountsAndCredentials/credentials.json
credentials.* accountsAndCredentials/accounts.json
token*.json accountsAndCredentials/token*.json
accounts.json !accountsAndCredentials/*.example.json
# Runtime output # Runtime output
mailSummaries/ mailSummaries/

209
README.md
View File

@@ -2,119 +2,182 @@
<img src="assets/logo.png" alt="Declawed" width="120" /> <img src="assets/logo.png" alt="Declawed" width="120" />
</p> </p>
# Declawed: your kuddly assisty-kitty # Declawed: your kuddly assisty-kitty
# Inbox brimming with stank-ass algorithmically-generated turds?
# (Cat) nip that sh*t in the bud!!! Inbox brimming with stank? Wanna cut the crap and get the messaging that matters into calendars, Atlassian, Jira, Asana, Google Calendar/Sheets (etc.) ... in two clicks? (Cat) nip slop in the bud with Assisty-Kitty!!
### Declawed is a configurable, prompt purrr-fectable LLM mail management assisty-kitty.
### Puuurfect for tedious daily sorts/classification/responses
### Cuts down on the frustartion of feeling like you'll never get your paws on that catnip-stuffed mouse dangling from the string.
# Privacy # The "why":
# More than the blue-plate crustacean
A local Model Context Protocol (MCP) server integrates with an LLM API you choose -- both of which offer greater ops and privacy control and transparency. ### Eliminates that frustrating feeling you'll never get your paws around the dangling, catnip-stuffed mouse through haunch-high mail from list you're never signed up for or were re-sold to from ones you have, bot-generated marketing slop, and, of course, nigerians princes that need help claim their lottery jackpot.
# Avoid flakey (molty?) black box installs.
Billed as QUICK AND EASY! ...loaded with more Trojans than an Olympic Village. "deClawed" because: no one should be beholden to one LLM api or local model. Have you seen that baddie Kimi K3?
... Silent processes: data harvest phone-homes, behavioral analytic reporting, telemetry backdoors - all the nasties crawling the dark detritus. You know lobsters eat poop, right?
# Declawed: **actually** easy
## Stop drilling byzantine menus in mail, scehduling and sheets pltforms to 1. configure filters that don't even work 2. change every three months The platform princes are rent extractors.
## Short, declarative prompts are teh equivelent of wrapping slop, bulk-mail's feet in tin foil and tossing it in the bathtub.
### Prompts are discerning, unlike filters. For exaple, they keep the gold and bin the junk in forums/feeds where solid-gold insights are only 10% of the signal/noise. Open weights are heavy weight champions.
### With refinement, prompting runs auto: cleaning that stank litter(in)box before you get a whiff.
### Morning greets you with a fresh, spring-meadow aroma of opportunities and insights -- not an avalanche of turds -- like its f*cxing 1998 again.
# The "how"
Because It's soooo easy.
deClawed is configurable, prompt purrr-fectable, LLM-driven mail and task automation for anyone that can cut n' paste a few CLI commands and can type in short declarative sentences.
More private than other options (not purrfect *yet* - but privacy is our number one goal with every release).
More accurate actions/results (read on).
# Privacy: more than the blue-plate crustacean
Your own local Model Context Protocol (MCP) server integrates with your preferred LLM API. Each step in the install, and setup offers greater control, and transparency. Permissions are 100% configurable.
Escape black-box installs stocked with more Trojans than an Olympic Village - silent/dark processes, data harvesting phone-homes, behavioral analytics, telemetry backdoors, corporate surveillance - the nasties lurking in the dark discoverable only by secops edgelords that are masters of the the CLI.
# Simplicity
### NLP prompts are **actually** easy and **demonstrably** more effective
Stop drilling down byzantine menus in mail, scheduling and workflow to configure filters and actions that 1. only ever work 20% of the time 2. change in functionality and scope every two months because a project manager needs to justify their salary.
Write short, declarative prompts instead. It's like wrapping busywork's paws in tinfoil and tossing it in a bathtub.
Prompts
1. Use semantic grouping, organizing by message intent, purpose, and context.
2. The prompt templates in Kitty's "built in" library are evaluated using, for example, cosine similarity, ROUGE scoring and real human eval in the SDLC dev loop.
3. For example: you'll consistently keep the treasure and trash the junk when you task deClawed with reviewing forums/feeds where actionable insights are gold, but only 10% of the signal.
### Prompting: easily configured to cron: cleaning the litter(in)box before the stank wafts through the whole work house.
### Imagine your morning inbox or summaries brimming with a fresh, spring-meadow aroma of opportunity and insight -- not last night's Kitty kibble.
# -- You just might feel like it' s freakin' 1998 again.
# Wiring up your kitty
Assisty-kitty easily interfaces with mail and calendar and other app APIs (about any other service you want to plug in) ... it keeps things moving so you can go chase laser pointers or enjoy a 19-hour nap in a sunbeam.
# Kitty connex are simple
Assisty-kitty easily interfaces with mail and calendar and other app APIs (about any other service you want to plug in) ... it keeps things moving so you can go you chase your tail or take a 19-hour nap in a sunbeam.
--- ---
# Scope and Architecture # Scope and Architecture
## Current implementation requires building/using: ## Current implementation requires building/using:
-- Claude Desktop (as UI - proprietary UI coming soonish) -- Claude Desktop (as UI only - proprietary UI coming soonish)
-- Building/Connecting a Local Model Context Protocol Server -- Building/Connecting a Model API or Local Model Context Protocol Server
-- A DNS-config’d domain, with MX records pointing to: -- A DNS-config’d domain, with MX records pointing to:
-- A commercial or self-hosted SMTP Serve -- A commercial or self-hosted SMTP Server
## Coming soonish: ## Coming soonish:
-- Our own custom UI -- Proprietary, custom UI.
-- Suppost for wiring up local LLMs. -- Support for all stripes of models on HugginFFace.
- Support for local LLMs.
# Setup # Setup
### 1. Install Node.js (v16+ required). ### 1. Install Node.js (v16+ required).
```bash ```bash
node --version node --version
npm --version npm --version
``` ```
If not installed, grab it from [nodejs.org](https://nodejs.org/). If not installed, grab it from [nodejs.org](https://nodejs.org/).
### 2. Clone and Install ### 2. Clone and Install
```bash
git clone https://github.com/kjannette/deClawed-Assisty-Kitty.git
cd deClawed-Assity-Kitty
npm install
```
```bash
git clone https://git.sjdev.online/kjannette/deClawed-Assity-Kitty
cd deClawed-Assity-Kitty
npm i
```
### 3. Set Up Google Cloud Credentials ### 3. Set Up Google Cloud Credentials
#### 3a. Create a Google Cloud Project #### 3a. Create a Google Cloud Project
1. Go to [Google Cloud Console](https://console.cloud.google.com/) 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Sign in with the Google account that owns the target Gmail 2. Sign in with the Google account that owns the target Gmail
3. Click the project dropdown (top-left) > **New Project** 3. Click the project dropdown (top-left) > **New Project**
4. Name it (e.g., `assistant-mcp`) and click **Create** 4. Name it (e.g., `assistant-mcp`) and click **Create**
5. Select the new project from the dropdown 5. Select the new project from the dropdown
#### 3b. Enable APIs #### 3b. Enable APIs
In **APIs & Services > Library**, enable: In **APIs & Services > Library**, enable:
- **Gmail API** - **Gmail API**
- **Google Sheets API** (if mapping mail to sheets) - **Google Sheets API** (if mapping mail to sheets)
- **Google Calendar API** (if using calendar event creation, etc.s) - **Google Calendar API** (if using calendar event creation, etc.s)
#### 3c. Configure the OAuth Consent Screen #### 3c. Configure the OAuth Consent Screen
1. Go to **Google Auth Platform > Branding** (or **APIs & Services > OAuth consent screen**) 1. Go to **Google Auth Platform > Branding** (or **APIs & Services > OAuth consent screen**)
2. Set user type to **External**, click **Create** 2. Set user type to **External**, click **Create**
3. Fill in app name, support email, and developer contact email 3. Fill in app name, support email, and developer contact email
4. Save and continue 4. Save and continue
#### 3d. Add OAuth Scopes #### 3d. Add OAuth Scopes
In **Google Auth Platform > Data Access**, add: In **Google Auth Platform > Data Access**, add:
- `https://www.googleapis.com/auth/gmail.modify` - `https://www.googleapis.com/auth/gmail.modify`
- `https://www.googleapis.com/auth/spreadsheets` (for Sheets integration) - `https://www.googleapis.com/auth/spreadsheets` (for Sheets integration)
- `https://www.googleapis.com/auth/calendar.events` (for Calendar integration) - `https://www.googleapis.com/auth/calendar.events` (for Calendar integration)
#### 3e. Add Yourself as a Test User #### 3e. Add Yourself as a Test User
Go to **Google Auth Platform > Audience** and add each Gmail address you'll use. Go to **Google Auth Platform > Audience** and add each Gmail address you'll use.
#### 3f. Create OAuth Client Credentials #### 3f. Create OAuth Client Credentials
1. Go to **Google Auth Platform > Clients** (or **APIs & Services > Credentials**) 1. Go to **Google Auth Platform > Clients** (or **APIs & Services > Credentials**)
2. Click **Create Client** > Application type: **Desktop app** 2. Click **Create Client** > Application type: **Desktop app**
3. **Download the JSON**, rename it to `credentials.json` 3. **Download the JSON**, rename it to `credentials.json`
4. Place it in the project root 4. Place it in the project root
### 4. Configure Accounts ### 4. Configure Accounts
Create file `accounts.json` in the project root. Each key is an account alias with its own token file and optional Sheets/Calendar config: Create file `accounts.json` in the project root. Each key is an account alias with its own token file and optional Sheets/Calendar config:
```json ```json
{ {
"work": { "work": {
@@ -130,61 +193,83 @@ Create file `accounts.json` in the project root. Each key is an account alias wi
} }
``` ```
- **`label`** -- display name (typically the email address) - **`label`** -- display name (typically the email address)
- **`tokenFile`** -- per-account OAuth token (auto-generated during auth) - **`tokenFile`** -- per-account OAuth token (auto-generated during auth)
- **`spreadsheetId`** -- Google Sheets ID for recruiter contact logging (optional) - **`spreadsheetId`** -- Google Sheets ID for recruiter contact logging (optional)
- **`calendarId`** -- Google Calendar ID for event creation (optional, `"primary"` uses the default calendar) - **`calendarId`** -- Google Calendar ID for event creation (optional, `"primary"` uses the default calendar)
### 5. Authorize Gmail Accounts ### 5. Authorize Gmail Accounts
Build and run the auth script for each account: Build and run the auth script for each account:
```bash ```bash
npm run auth # authorizes the "work" account npm run auth # authorizes the "work" account
npm run auth -- secondary # authorizes the "secondary" account npm run auth -- secondary # authorizes the "secondary" account
``` ```
Each run will:
Each run required steps:
1. Print a URL -- open it in your browser 1. Print a URL -- open it in your browser
2. Sign in and click **Allow** 2. Sign in and click **Allow**
3. You'll land on a "localhost refused to connect" page (normal) 3. You'll land on a "localhost refused to connect" page (normal)
4. Copy the **entire URL** from the address bar and paste it back into the terminal 4. Copy the **entire URL** from the address bar and paste it back into the terminal
5. The script saves the token file (e.g., `token.json` or `token-secondary.json`) 5. The script saves the token file (e.g., `token.json` or `token-secondary.json`)
You only need to do this once per account. Tokens auto-refresh. You only need to do this once per account. Tokens auto-refresh.
### 6. Write the Prompts ### 6. Write the Prompts
Two plain-text prompt files in `src/prompts/` control the workflow: Two plain-text prompt files in `src/prompts/` control the workflow:
| File | Phase | Purpose | | File | Phase | Purpose |
|------|-------|---------| |------|-------|---------|
| `src/prompts/classify-emails.txt` | 1 -- Classification | Defines categories A/B/C/D and how to sort emails | | `src/prompts/classify-emails.txt` | 1 -- Classification | Defines categories A/B/C/D and how to sort emails |
| `src/prompts/take-action-on-emails.txt` | 2 -- Action | Tells the LLM what to do with each category (delete, log, schedule, etc.) | | `src/prompts/take-action-on-emails.txt` | 2 -- Action | Tell the LLM what to do with each category (delete, log, schedule, etc.) |
**Tips:** **Tips:**
- Use clear, explicit category definitions with example language - Use clear, explicit category definitions with example language
- Handle ambiguous cases (e.g., "If an email both acknowledges receipt AND requests action, classify as B") - Handle ambiguous cases (e.g., "If an email both acknowledges receipt AND requests action, classify as B")
- Prompt files are loaded at runtime -- edit them anytime, no rebuild required - Prompt files are loaded at runtime -- edit them anytime, no rebuild required
### 7. Build ### 7. Build
```bash ```bash
npm run build npm run build
``` ```
Compiles `src/**/*.ts` into `build/`. Compiles `src/**/*.ts` into `build/`.
## 8. Configure Claude Desktop ## 8. Configure Claude Desktop
Edit your Claude Desktop config: Edit your Claude Desktop config:
```bash ```bash
code ~/Library/Application\ Support/Claude/claude_desktop_config.json code ~/Library/Application\ Support/Claude/claude_desktop_config.json
``` ```
Add the server: Add the server:
```json ```json
{ {
"mcpServers": { "mcpServers": {
@@ -198,23 +283,31 @@ Add the server:
} }
``` ```
Replace paths with the output of `which node` and your actual project location. Replace paths with the output of `which node` and your actual project location.
## 9. Add/Configure API Keys, Credentials, and Other Secrets ## 9. Add/Configure API Keys, Credentials, and Other Secrets
Secret files are **gitignored** -- they never leave your machine. Example templates are provided so you know what shape each file needs to be in. Secret files are **gitignored** -- they never leave your machine. Example templates are provided so you know what shape each file needs to be in.
#### 9a. `credentials.json` #### 9a. `credentials.json`
This file holds your Google OAuth client credentials. **You do not write this by hand** -- it is downloaded from the Google Cloud Console (see Step 3f above). Copy the example and then replace it with the real download: This file holds your Google OAuth client credentials. **You do not write this by hand** -- it is downloaded from the Google Cloud Console (see Step 3f above). Copy the example and then replace it with the real download:
```bash ```bash
cp credentials.example.json credentials.json cp credentials.example.json credentials.json
# Now replace credentials.json with the file downloaded from Google Cloud Console. # Now replace credentials.json with the file downloaded from Google Cloud Console.
``` ```
The structure looks like this (the example file ships with empty values): The structure looks like this (the example file ships with empty values):
```json ```json
{ {
"installed": { "installed": {
@@ -229,14 +322,18 @@ The structure looks like this (the example file ships with empty values):
} }
``` ```
#### 9b. `accounts.json` #### 9b. `accounts.json`
Defines each Gmail account the server manages. Copy the example and fill in your values: Defines each Gmail account the server manages. Copy the example and fill in your values:
```bash ```bash
cp accounts.example.json accounts.json cp accounts.example.json accounts.json
``` ```
| Field | What to put here | | Field | What to put here |
|-------|-----------------| |-------|-----------------|
| `label` | The email address for this account (display only) | | `label` | The email address for this account (display only) |
@@ -244,28 +341,38 @@ cp accounts.example.json accounts.json
| `spreadsheetId` | The ID from your Google Sheet URL: `docs.google.com/spreadsheets/d/<THIS_PART>/edit` (optional) | | `spreadsheetId` | The ID from your Google Sheet URL: `docs.google.com/spreadsheets/d/<THIS_PART>/edit` (optional) |
| `calendarId` | `"primary"` for your default calendar, or a specific calendar ID (optional) | | `calendarId` | `"primary"` for your default calendar, or a specific calendar ID (optional) |
Add as many accounts as you need. Each key (e.g., `"work"`, `"secondary"`) becomes the account name used in tool calls and auth commands. Add as many accounts as you need. Each key (e.g., `"work"`, `"secondary"`) becomes the account name used in tool calls and auth commands.
#### 9c. `token*.json` (auto-generated) #### 9c. `token*.json` (auto-generated)
Token files are **created automatically** when you run `npm run auth` (Step 5). You do not need to create or edit them manually. A `token.example.json` is provided for reference only -- it shows the structure but the values are populated by the OAuth flow. Token files are **created automatically** when you run `npm run auth` (Step 5). You do not need to create or edit them manually. A `token.example.json` is provided for reference only -- it shows the structure but the values are populated by the OAuth flow.
#### Summary of secret files #### Summary of secret files
| Example template | Actual file (gitignored) | How to create | | Example template | Actual file (gitignored) | How to create |
|-----------------|--------------------------|---------------| |-----------------|--------------------------|---------------|
| `credentials.example.json` | `credentials.json` | Download from Google Cloud Console | | `credentials.example.json` | `credentials.json` | Download from Google Cloud Console |
| `accounts.example.json` | `accounts.json` | Copy example, fill in your email/sheet/calendar IDs | | `accounts.example.json` | `accounts.json` | Copy example, fill in your email/sheet/calendar IDs |
| `token.example.json` | `token.json`, `token-secondary.json`, etc. | Auto-generated by `npm run auth` | | `token.example.json` | `token.json`, `token-secondary.json`, etc. | Auto-generated by `npm run auth` |
### 10. Restart Claude Desktop ### 10. Restart Claude Desktop
Fully quit (**Cmd+Q**, not just close the window) and reopen. The `assistant` server should appear under **Connectors**. Fully quit (**Cmd+Q**, not just close the window) and reopen. The `assistant` server should appear under **Connectors**.
--- ---
## MCP Tools ## MCP Tools
| Tool | Description | | Tool | Description |
|------|-------------| |------|-------------|
| `fetch_new_emails` | Fetches unread emails for a given account. Classification and action prompts are automatically appended to the response. | | `fetch_new_emails` | Fetches unread emails for a given account. Classification and action prompts are automatically appended to the response. |
@@ -274,24 +381,32 @@ Fully quit (**Cmd+Q**, not just close the window) and reopen. The `assistant` se
| `log_contact` | Logs or updates contact info in a Google Sheet. Merges rows by email + role. | | `log_contact` | Logs or updates contact info in a Google Sheet. Merges rows by email + role. |
| `create_calendar_event` | Creates a Google Calendar event for scheduled calls/meetings. Includes meeting links, attendees and their contact info. | | `create_calendar_event` | Creates a Google Calendar event for scheduled calls/meetings. Includes meeting links, attendees and their contact info. |
## MCP Prompts (built in as of now-ish - add your own) ## MCP Prompts (built in as of now-ish - add your own)
### BETTER YET - DO A PR OR FORK ### BETTER YET - DO A PR OR FORK
| Prompt | Account | Description | | Prompt | Account | Description |
|--------|---------|-------------| |--------|---------|-------------|
| `review_emails` | work | Loads the classification + action prompts for the work inbox | | `review_emails` | work | Loads the classification + action prompts for the work inbox |
| `review_secondary_emails` | secondary | Same workflow, but for a second email account inbox | | `review_secondary_emails` | secondary | Same workflow, but for a second email account inbox |
Invoke these from Claude Desktop's Connectors menu, or just type "Review my inbox" / "Review my secondary inbox." Invoke these from Claude Desktop's Connectors menu, or just type "Review my inbox" / "Review my secondary inbox."
--- ---
## Two-Phase Workflow ## Two-Phase Workflow
**Phase 1 -- Classify** **Phase 1 -- Classify**
The LLM calls `fetch_new_emails`, which returns email data with the classification instructions from `classify-emails.txt` appended. Each email is sorted into: The LLM calls `fetch_new_emails`, which returns email data with the classification instructions from `classify-emails.txt` appended. Each email is sorted into:
| Category | Meaning | Action | | Category | Meaning | Action |
|----------|---------|--------| |----------|---------|--------|
| **A** | Acknowledgement / auto-reply | Delete | | **A** | Acknowledgement / auto-reply | Delete |
@@ -299,18 +414,23 @@ The LLM calls `fetch_new_emails`, which returns email data with the classificati
| **C** | Rejection | Delete | | **C** | Rejection | Delete |
| **D** | Other / uncategorized | Summarize + log | | **D** | Other / uncategorized | Summarize + log |
**Phase 2 -- Act** **Phase 2 -- Act**
Using `take-action-on-emails.txt`, the LLM: Using `take-action-on-emails.txt`, the LLM:
- Calls `delete_emails` for A + C - Calls `delete_emails` for A + C
- Calls `append_to_summary` for B + D - Calls `append_to_summary` for B + D
- Calls `log_recruiter_contact` to track contacts in Sheets (if configured) - Calls `log_recruiter_contact` to track contacts in Sheets (if configured)
- Calls `create_calendar_event` for any scheduled interviews/calls (if configured) - Calls `create_calendar_event` for any scheduled interviews/calls (if configured)
--- ---
## Key Commands ## Key Commands
| Command | Purpose | | Command | Purpose |
|---------|---------| |---------|---------|
| `npm run build` | Recompile after editing source files | | `npm run build` | Recompile after editing source files |
@@ -319,10 +439,13 @@ Using `take-action-on-emails.txt`, the LLM:
| `npm test` | Run the test suite | | `npm test` | Run the test suite |
| `npm run test:watch` | Run tests in watch mode | | `npm run test:watch` | Run tests in watch mode |
--- ---
## Project Structure ## Project Structure
``` ```
deClawed-Assity-Kitty/ deClawed-Assity-Kitty/
├── src/ ├── src/
@@ -340,6 +463,14 @@ deClawed-Assity-Kitty/
│ ├── tools-email.ts # fetch, delete, append_to_summary │ ├── tools-email.ts # fetch, delete, append_to_summary
│ ├── tools-calendar.ts # create_calendar_event │ ├── tools-calendar.ts # create_calendar_event
│ └── tools-spreadsheet.ts # log_recruiter_contact │ └── tools-spreadsheet.ts # log_recruiter_contact
├── accountsAndCredentials/
│ ├── accounts.json # Multi-account configuration (gitignored)
│ ├── accounts.example.json # Template for accounts.json
│ ├── credentials.json # Google OAuth client credentials (gitignored)
│ ├── credentials.example.json # Template for credentials.json
│ ├── token.json # OAuth token -- work account (gitignored, auto-generated)
│ ├── token-secondary.json # OAuth token -- secondary account (gitignored, auto-generated)
│ └── token.example.json # Template showing token structure
├── test/ ├── test/
│ ├── fixtures/ │ ├── fixtures/
│ │ └── mock-emails.ts # Mock Gmail API responses │ │ └── mock-emails.ts # Mock Gmail API responses
@@ -347,21 +478,13 @@ deClawed-Assity-Kitty/
│ │ └── email-workflow.test.ts # Integration tests │ │ └── email-workflow.test.ts # Integration tests
│ └── unit/ │ └── unit/
│ └── email-parsing.test.ts # Unit tests for parsing helpers │ └── email-parsing.test.ts # Unit tests for parsing helpers
├── mailSummaries/ # Per-account summary output (auto-generated) ├── mailSummaries/ # Per-account summary output (gitignored, auto-generated)
├── build/ # Compiled JS (auto-generated) ├── build/ # Compiled JS (auto-generated)
├── accounts.json # Multi-account configuration ├── assets/
├── credentials.json # Google OAuth client credentials │ └── logo.png # Project logo
├── token*.json # Per-account OAuth tokens (auto-generated)
├── package.json ├── package.json
├── tsconfig.json ├── tsconfig.json
├── vitest.config.ts ├── vitest.config.ts
├── .gitignore
└── README.md └── README.md
``` ```

View File

@@ -0,0 +1,11 @@
{
"installed": {
"client_id": "",
"project_id": "",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "",
"redirect_uris": ["http://localhost"]
}
}

View File

@@ -0,0 +1,7 @@
{
"access_token": "",
"refresh_token": "",
"scope": "https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/calendar.events",
"token_type": "Bearer",
"expiry_date": 0
}

View File

@@ -0,0 +1,7 @@
{
"access_token": "ya29.a0AT3oNZ9Db5cbvOq43MccPAw3bB6dmyKxfHkvLlHvvAnMGd-fQqyHYa0raFy5Tx0G0iJysQz_Hb9A-POPTp3_KJ7-6KpovEJPsgoTxy_jKlt81aa2CKKFWZlAClrvldH4I5XTJt6hyrDwu8fFvfBrpoqbVe0FK9yJWIMua3GAfChFjEebPMIqGF7VCwGjhXNMdOikNp_5SQaCgYKAagSARMSFQHGX2Miikn-iDccI4e7x_vlkScaaw0209",
"refresh_token": "1//0fD27YPvUVJODCgYIARAAGA8SNwF-L9IrSeT9ypacVvfAmW0K7J6M7PS4o4i4LBBLKwgm54lvNs1-VyIWnYav_88jWcP3Ugb_7Xw",
"scope": "https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/gmail.modify",
"token_type": "Bearer",
"expiry_date": 1783036437865
}

View File

@@ -5,8 +5,9 @@ import readline from "readline";
const __dirname = path.dirname(new URL(import.meta.url).pathname); const __dirname = path.dirname(new URL(import.meta.url).pathname);
const PROJECT_ROOT = path.resolve(__dirname, ".."); const PROJECT_ROOT = path.resolve(__dirname, "..");
const CREDENTIALS_PATH = path.join(PROJECT_ROOT, "credentials.json"); const SECRETS_DIR = path.join(PROJECT_ROOT, "accountsAndCredentials");
const ACCOUNTS_PATH = path.join(PROJECT_ROOT, "accounts.json"); const CREDENTIALS_PATH = path.join(SECRETS_DIR, "credentials.json");
const ACCOUNTS_PATH = path.join(SECRETS_DIR, "accounts.json");
const SCOPES = [ const SCOPES = [
"https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/gmail.modify",
@@ -26,7 +27,7 @@ function resolveTokenPath(accountKey: string): string {
`Unknown account "${accountKey}". Available: ${available}` `Unknown account "${accountKey}". Available: ${available}`
); );
} }
return path.join(PROJECT_ROOT, acct.tokenFile); return path.join(SECRETS_DIR, acct.tokenFile);
} }
async function authorize(): Promise<void> { async function authorize(): Promise<void> {
@@ -44,7 +45,7 @@ async function authorize(): Promise<void> {
"1. Go to https://console.cloud.google.com/\n" + "1. Go to https://console.cloud.google.com/\n" +
"2. Create a project and enable the Gmail, Sheets, and Calendar APIs\n" + "2. Create a project and enable the Gmail, Sheets, and Calendar APIs\n" +
"3. Create OAuth 2.0 credentials (Desktop app type)\n" + "3. Create OAuth 2.0 credentials (Desktop app type)\n" +
"4. Download the JSON and save it as credentials.json in the project root\n" "4. Download the JSON and save it as credentials.json in the accountsAndCredentials/ folder\n"
); );
process.exit(1); process.exit(1);
} }

View File

@@ -9,12 +9,12 @@ import path from "path";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const __dirname = path.dirname(new URL(import.meta.url).pathname); const __dirname = path.dirname(new URL(import.meta.url).pathname);
export const PROJECT_ROOT = path.resolve(__dirname, "..", ".."); export const PROJECT_ROOT = path.resolve(__dirname, "..", "..");
const CREDENTIALS_PATH = path.join(PROJECT_ROOT, "credentials.json"); const SECRETS_DIR = path.join(PROJECT_ROOT, "accountsAndCredentials");
const ACCOUNTS_PATH = path.join(PROJECT_ROOT, "accounts.json"); const CREDENTIALS_PATH = path.join(SECRETS_DIR, "credentials.json");
const CLASSIFY_PROMPT_PATH = path.join(PROJECT_ROOT, "classify-emails.txt"); const ACCOUNTS_PATH = path.join(SECRETS_DIR, "accounts.json");
const ACTION_PROMPT_PATH = path.join( const PROMPTS_DIR = path.join(PROJECT_ROOT, "src", "prompts");
PROJECT_ROOT, "src", "prompts", "take-action-on-emails.txt" const CLASSIFY_PROMPT_PATH = path.join(PROMPTS_DIR, "classify-emails.txt");
); const ACTION_PROMPT_PATH = path.join(PROMPTS_DIR, "take-action-on-emails.txt");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Account configuration // Account configuration
@@ -46,7 +46,7 @@ export const getTokenPath = (account: string): string => {
`Unknown account "${account}". Available accounts: ${available}` `Unknown account "${account}". Available accounts: ${available}`
); );
} }
return path.join(PROJECT_ROOT, acct.tokenFile); return path.join(SECRETS_DIR, acct.tokenFile);
}; };
export const getSummaryPath = (account: string): string => { export const getSummaryPath = (account: string): string => {

View File

@@ -1,6 +1,8 @@
You are an expert Executive Assistant AI specialized in email management and productivity. Your goal is to organize, prioritize, and summarize incoming emails to maximize user efficiency. You are an expert Executive Assistant AI specialized in email management and productivity. Your goal is to organize, prioritize, and summarize incoming emails to maximize user efficiency.
I have been applying for jobs, which generates a large volume of responses. Review my inbox and apply the following instructions to all new emails. I have been applying for jobs, which generates a large volume of responses.
Review the first 200 emails in my inbox. Apply the following instructions to those emails.
STEP 1: Constraints STEP 1: Constraints
- Do not make up information. - Do not make up information.
@@ -23,7 +25,9 @@ STEP 3: DELETE emails classified as Category A and Category C.
STEP 4: ("Anti step") - NEVER DELETE Category B emails. STEP 4: ("Anti step") - NEVER DELETE Category B emails.
STEP 5: SUMMARIZE emails classified as Category B and Category D. For each, include: STEP 5: Confirm via natrual lanugage output in the cat, the total number of emails fetched for review.
STEP 6: SUMMARIZE emails classified as Category B and Category D. For each, include:
1. Sender name and email address 1. Sender name and email address
2. Email subject line 2. Email subject line
3. Date and time received 3. Date and time received

View File

@@ -24,7 +24,7 @@ Immediately after logging to the spreadsheet, check whether ANY of these fields
- Company Second Interview - Company Second Interview
For EACH of the above fields that contains a date/time, create a Google Calendar event using the create_calendar_event tool: For EACH of the above fields that contains a date/time, create a Google Calendar event using the create_calendar_event tool:
- Title format: "[<Recruiter NaME> Call] Company - Role" or "[First Interview] Company - Role" or "[Second Interview] Company - Role" - Title format: "[<Recruiter Name> Call] Company - Role" or "[First Interview] Company - Role" or "[Second Interview] Company - Role"
- Start date/time: extracted from the email (convert to ISO 8601) - Start date/time: extracted from the email (convert to ISO 8601)
- Duration: 30 minutes unless otherwise specified in the email - Duration: 30 minutes unless otherwise specified in the email
- Location: the video meeting link, phone number, or platform name - Location: the video meeting link, phone number, or platform name
@@ -38,8 +38,8 @@ When an email indicates advancement beyond the recruiter screen to a company int
STRICT CONSTRAINTS: STRICT CONSTRAINTS:
- Do not make up phone numbers, links, dates, or anything at all. If it does not exist in text wihtin a verifiable email, IT DOES NOT EXIST. - Do not make up phone numbers, links, dates, or anything at all. If it does not exist in text wihtin a verifiable email, IT DOES NOT EXIST.
- PAST-DATE CHECK: Before creating a calendar event, compare the proposed meeting date/time against today's date. If the meeting date has already passed, do NOT create a calendar event for it. Still log it to the spreadsheet, but skip the calendar step. The tool will also enforce this server-side. - PAST-DATE CHECK: Before creating a calendar event. 1) verify today's date from an external, reliable source; 2) compare the proposed meeting date/time against today's date; 3) iIf the meeting date has already passed, do NOT create a calendar event for it. Still log it to the spreadsheet, but skip the calendar step. The tool will also enforce this server-side.
- As described in more detail below, each new date/time value in a scheduling column = one new calendar event. Never skip this. - As described in more detail below, each new date/time value in a scheduling column = one new calendar event. Never skip this.
- Always use the same account for calendar events as was used for email fetching. - Always use the same account for calendar events as was used for email fetching.
- Do not create duplicate calendar events for the same meeting. - Do not create duplicate calendar events for the same meeting (same company representative && same job description | same company && same date time)
- If information is not present in the email, leave that field empty do ot guess. - If information is not present in the email, leave that field empty (it's OK!) - do not guess or make up information.

View File

@@ -62,8 +62,8 @@ server.registerTool(
maxResults: z maxResults: z
.number() .number()
.min(1) .min(1)
.max(100) .max(200)
.describe("Maximum number of unread emails to fetch (1-100)"), .describe("Maximum number of unread emails to fetch (1-200)"),
}, },
}, },
async ({ account, maxResults }) => { async ({ account, maxResults }) => {