12 Commits

Author SHA1 Message Date
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
9 changed files with 108 additions and 76 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/

View File

@@ -4,37 +4,47 @@
# Declawed: your kuddly assisty-kitty # 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. # The "why":
### 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 ### 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. 💩 You know lobsters eat poop, right? 💩
... 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 # 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. Prompts
### 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.
# Kitty connex are simple 1. Use semantic grouping, organizing by message intent, purpose, and context.
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. 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) -- Claude Desktop (as UI - proprietary UI coming soonish)
-- Building/Connecting a Local Model Context Protocol Server -- Building/Connecting a 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 Hugging Face models galore.
- Support for wiring to local LLMs.
# Setup # Setup
@@ -340,6 +351,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 +366,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

@@ -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, in reverse chronoloical order, the first 150 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.

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.