Compare commits
10 Commits
refine-mai
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a91c8c0009 | ||
|
|
8874b32334 | ||
|
|
f3cf4a51bd | ||
|
|
065645983a | ||
|
|
98585b52ad | ||
|
|
ea62edbc21 | ||
|
|
21b4e656f6 | ||
|
|
fc3223a119 | ||
|
|
9adc9cc3e0 | ||
|
|
7a02c49bd2 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -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/
|
||||||
|
|||||||
121
README.md
121
README.md
@@ -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
|
||||||
|
|
||||||
@@ -118,14 +129,14 @@ Create file `accounts.json` in the project root. Each key is an account alias wi
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"work": {
|
"work": {
|
||||||
"label": "you@yourdomain.com",
|
"label": "you@yourdomain.com",
|
||||||
"tokenFile": "token.json",
|
"tokenFile": "token.json",
|
||||||
"spreadsheetId": "YOUR_GOOGLE_SHEET_ID",
|
"spreadsheetId": "YOUR_GOOGLE_SHEET_ID",
|
||||||
"calendarId": "primary"
|
"calendarId": "primary"
|
||||||
},
|
},
|
||||||
"secondary": {
|
"secondary": {
|
||||||
"label": "you@gmail.com",
|
"label": "you@gmail.com",
|
||||||
"tokenFile": "token-secondary.json"
|
"tokenFile": "token-secondary.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -188,12 +199,12 @@ Add the server:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"assistant": {
|
"assistant": {
|
||||||
"command": "/ABSOLUTE/PATH/TO/node",
|
"command": "/ABSOLUTE/PATH/TO/node",
|
||||||
"args": [
|
"args": [
|
||||||
"/ABSOLUTE/PATH/TO/deClawed-Assity-Kitty/build/index.js"
|
"/ABSOLUTE/PATH/TO/deClawed-Assity-Kitty/build/index.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -217,15 +228,15 @@ The structure looks like this (the example file ships with empty values):
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"installed": {
|
"installed": {
|
||||||
"client_id": "",
|
"client_id": "",
|
||||||
"project_id": "",
|
"project_id": "",
|
||||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||||
"token_uri": "https://oauth2.googleapis.com/token",
|
"token_uri": "https://oauth2.googleapis.com/token",
|
||||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||||
"client_secret": "",
|
"client_secret": "",
|
||||||
"redirect_uris": ["http://localhost"]
|
"redirect_uris": ["http://localhost"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
11
accountsAndCredentials/credentials.example.json
Normal file
11
accountsAndCredentials/credentials.example.json
Normal 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"]
|
||||||
|
}
|
||||||
|
}
|
||||||
7
accountsAndCredentials/token.example.json
Normal file
7
accountsAndCredentials/token.example.json
Normal 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
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
Reference in New Issue
Block a user