10 Commits

Author SHA1 Message Date
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
7 changed files with 101 additions and 71 deletions

10
.gitignore vendored
View File

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

View File

@@ -4,37 +4,47 @@
# Declawed: your kuddly assisty-kitty
# Inbox brimming with stank-ass algorithmically-generated turds?
Inbox brimming with stank, algorithmically-generated logs? Wanna cut the crap and get the stuff that matters into your calendars, Atlassian, Jira, Asana, G sheets (etc., etc.) ... in two clicks?
# (Cat) nip that sh*t in the bud!!!
### (Cat) nip it in the bud!!
### 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.
# The "why":
# Privacy
### Declawed is a configurable, prompt purrr-fectable LLM mail and task automation assisty-kitty.
### More private than other options (not purrfect *yet* - but privacy is our number one goal with every release).
### More accurate actions/results (read on).
### Eliminates the frustrating feeling you'll never get the dangling, catnip-stuffed mouse you've been chasing all week
# More than the blue-plate crustacean
# Privacy: 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.
Your own local Model Context Protocol (MCP) server integrates with your preferred LLM API.
Each step offers greater control, configurability and transparency over permissions.
# Avoid flakey (molty?) black box installs.
Avoid the flakey (molty?) black-box installs that are loaded with more Trojans than an Olympic Village. Silent/dark processes, data harvesting phone-homes, behavioral analytics, telemetry backdoors - the nasties lurking in the dark detritus.
Billed as QUICK AND EASY! ...loaded with more Trojans than an Olympic Village.
... Silent processes: data harvest phone-homes, behavioral analytic reporting, telemetry backdoors - all the nasties crawling the dark detritus. You know lobsters eat poop, right?
💩 You know lobsters eat poop, right? 💩
# Declawed: **actually** easy
# Simplicity
### NLP prompts are **actually** easy and **demonstrably** more effective
## Stop drilling byzantine menus in mail, scehduling and sheets pltforms to 1. configure filters that don't even work 2. change every three months
Stop drilling down byzantine menus in mail, scheduling and workflow platforms to configure filters and actions that 1. only ever work 20% of the time 2. change in functionality and scope every two months.
## Short, declarative prompts are teh equivelent of wrapping slop, bulk-mail's feet in tin foil and tossing it in the bathtub.
Write short, declarative prompts instead. It's like wrapping busywork's paws in tinfoil and tossing it in a 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.
### 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.
Prompts
# 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.
1. Use semantic grouping, organizing by message intent, purpose, and context.
2. The prompt templates in our "built in" library are evaluated using, for example, cosine similarity, ROUGE scoring and real human feedback.
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 into the whole house.
### Imagine your morning inbox or summaries brimming with a fresh, spring-meadow aroma of opportunity and insight -- not last night's litter.
# -- 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.
---
@@ -44,11 +54,12 @@ Assisty-kitty easily interfaces with mail and calendar and other app APIs (about
-- Claude Desktop (as UI - proprietary UI coming soonish)
-- Building/Connecting a Local Model Context Protocol Server
-- A DNS-configd domain, with MX records pointing to:
-- A commercial or self-hosted SMTP Serve
-- A commercial or self-hosted SMTP Server
## Coming soonish:
-- Our own custom UI
-- Suppost for wiring up local LLMs.
-- Proprietary custom UI.
-- Support for Hugging Face models galore.
- Support for wiring to local LLMs.
# Setup
@@ -118,14 +129,14 @@ Create file `accounts.json` in the project root. Each key is an account alias wi
```json
{
"work": {
"label": "you@yourdomain.com",
"tokenFile": "token.json",
"spreadsheetId": "YOUR_GOOGLE_SHEET_ID",
"calendarId": "primary"
"label": "you@yourdomain.com",
"tokenFile": "token.json",
"spreadsheetId": "YOUR_GOOGLE_SHEET_ID",
"calendarId": "primary"
},
"secondary": {
"label": "you@gmail.com",
"tokenFile": "token-secondary.json"
"label": "you@gmail.com",
"tokenFile": "token-secondary.json"
}
}
```
@@ -188,12 +199,12 @@ Add the server:
```json
{
"mcpServers": {
"assistant": {
"assistant": {
"command": "/ABSOLUTE/PATH/TO/node",
"args": [
"/ABSOLUTE/PATH/TO/deClawed-Assity-Kitty/build/index.js"
]
}
}
}
}
```
@@ -217,7 +228,7 @@ The structure looks like this (the example file ships with empty values):
```json
{
"installed": {
"installed": {
"client_id": "",
"project_id": "",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
@@ -225,7 +236,7 @@ The structure looks like this (the example file ships with empty values):
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "",
"redirect_uris": ["http://localhost"]
}
}
}
```
@@ -340,6 +351,14 @@ deClawed-Assity-Kitty/
│ ├── tools-email.ts # fetch, delete, append_to_summary
│ ├── tools-calendar.ts # create_calendar_event
│ └── 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/
│ ├── fixtures/
│ │ └── mock-emails.ts # Mock Gmail API responses
@@ -347,21 +366,13 @@ deClawed-Assity-Kitty/
│ │ └── email-workflow.test.ts # Integration tests
│ └── unit/
│ └── 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)
├── accounts.json # Multi-account configuration
├── credentials.json # Google OAuth client credentials
├── token*.json # Per-account OAuth tokens (auto-generated)
├── assets/
│ └── logo.png # Project logo
├── package.json
├── tsconfig.json
├── vitest.config.ts
├── .gitignore
└── 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

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

View File

@@ -9,12 +9,12 @@ import path from "path";
// ---------------------------------------------------------------------------
const __dirname = path.dirname(new URL(import.meta.url).pathname);
export const PROJECT_ROOT = path.resolve(__dirname, "..", "..");
const CREDENTIALS_PATH = path.join(PROJECT_ROOT, "credentials.json");
const ACCOUNTS_PATH = path.join(PROJECT_ROOT, "accounts.json");
const CLASSIFY_PROMPT_PATH = path.join(PROJECT_ROOT, "classify-emails.txt");
const ACTION_PROMPT_PATH = path.join(
PROJECT_ROOT, "src", "prompts", "take-action-on-emails.txt"
);
const SECRETS_DIR = path.join(PROJECT_ROOT, "accountsAndCredentials");
const CREDENTIALS_PATH = path.join(SECRETS_DIR, "credentials.json");
const ACCOUNTS_PATH = path.join(SECRETS_DIR, "accounts.json");
const PROMPTS_DIR = path.join(PROJECT_ROOT, "src", "prompts");
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
@@ -46,7 +46,7 @@ export const getTokenPath = (account: string): string => {
`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 => {