From 6817014b7f8ffd13b418b919fe1d7cf1f23ad76d Mon Sep 17 00:00:00 2001 From: KS Jannette Date: Tue, 11 Aug 2026 20:52:43 -0400 Subject: [PATCH] update prompt controller and loader to use new prompt files --- src/loaders/prompt-config-loaders.ts | 156 +++++++++--------- .../prompt-controller-service.ts | 94 +++++------ 2 files changed, 122 insertions(+), 128 deletions(-) diff --git a/src/loaders/prompt-config-loaders.ts b/src/loaders/prompt-config-loaders.ts index 4f6533c..a74d1c9 100644 --- a/src/loaders/prompt-config-loaders.ts +++ b/src/loaders/prompt-config-loaders.ts @@ -3,11 +3,13 @@ import { google, gmail_v1, sheets_v4, calendar_v3 } from "googleapis"; import { OAuth2Client } from "google-auth-library"; import fs from "fs"; import path from "path"; - +import { fileURLToPath } from "url"; // --------------------------------------------------------------------------- // Paths // --------------------------------------------------------------------------- -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + export const PROJECT_ROOT = path.resolve(__dirname, "..", ".."); const SECRETS_DIR = path.join(PROJECT_ROOT, "accountsAndCredentials"); const CREDENTIALS_PATH = path.join(SECRETS_DIR, "credentials.json"); @@ -20,130 +22,130 @@ const ACTION_PROMPT_PATH = path.join(PROMPTS_DIR, "take-action-on-emails.txt"); // Account configuration // --------------------------------------------------------------------------- export interface AccountConfig { - label: string; - tokenFile: string; - spreadsheetId?: string; - calendarId?: string; + label: string; + tokenFile: string; + spreadsheetId?: string; + calendarId?: string; } export interface AccountsMap { - [key: string]: AccountConfig; + [key: string]: AccountConfig; } export const loadAccounts = (): AccountsMap => { - if (!fs.existsSync(ACCOUNTS_PATH)) { - throw new Error(`Missing accounts.json at ${ACCOUNTS_PATH}.`); - } - return JSON.parse(fs.readFileSync(ACCOUNTS_PATH, "utf-8")); + if (!fs.existsSync(ACCOUNTS_PATH)) { + throw new Error(`Missing accounts.json at ${ACCOUNTS_PATH}.`); + } + return JSON.parse(fs.readFileSync(ACCOUNTS_PATH, "utf-8")); }; export const getTokenPath = (account: string): string => { - const accts = loadAccounts(); - const acct = accts[account]; - if (!acct) { - const available = Object.keys(accts).join(", "); - throw new Error( - `Unknown account "${account}". Available accounts: ${available}` - ); - } - return path.join(SECRETS_DIR, acct.tokenFile); + const accts = loadAccounts(); + const acct = accts[account]; + if (!acct) { + const available = Object.keys(accts).join(", "); + throw new Error( + `Unknown account "${account}". Available accounts: ${available}` + ); + } + return path.join(SECRETS_DIR, acct.tokenFile); }; export const getSummaryPath = (account: string): string => { - if (account === "work") { - return path.join(PROJECT_ROOT, "mailSummaries", "summary.json"); - } - return path.join(PROJECT_ROOT, "mailSummaries", `summary-${account}.json`); + if (account === "work") { + return path.join(PROJECT_ROOT, "mailSummaries", "summary.json"); + } + return path.join(PROJECT_ROOT, "mailSummaries", `summary-${account}.json`); }; export const VALID_ACCOUNTS = ["work", "secondary"] as const; export const accounts = loadAccounts(); const accountDescription = VALID_ACCOUNTS - .map((key) => `"${key}" (${accounts[key]?.label ?? key})`) - .join(" or "); + .map((key) => `"${key}" (${accounts[key]?.label ?? key})`) + .join(" or "); export const accountSchema = z - .enum(VALID_ACCOUNTS) - .describe(`Which email account to use: ${accountDescription}`); + .enum(VALID_ACCOUNTS) + .describe(`Which email account to use: ${accountDescription}`); // --------------------------------------------------------------------------- // Prompt loaders // --------------------------------------------------------------------------- const loadPromptFile = (filePath: string, label: string): string => { - if (!fs.existsSync(filePath)) { - console.error(`Warning: ${filePath} not found. ${label} will be missing.`); - return ""; - } - return fs.readFileSync(filePath, "utf-8"); + if (!fs.existsSync(filePath)) { + console.error(`Warning: ${filePath} not found. ${label} will be missing.`); + return ""; + } + return fs.readFileSync(filePath, "utf-8"); }; export const loadClassificationPrompt = (): string => - loadPromptFile(CLASSIFY_PROMPT_PATH, "Classification instructions"); + loadPromptFile(CLASSIFY_PROMPT_PATH, "Classification instructions"); export const loadActionPrompt = (): string => - loadPromptFile(ACTION_PROMPT_PATH, "Action instructions"); + loadPromptFile(ACTION_PROMPT_PATH, "Action instructions"); // --------------------------------------------------------------------------- // Auth helpers — generic OAuth2 client, then service-specific factories // --------------------------------------------------------------------------- export const getOAuth2Client = (account: string): OAuth2Client => { - const tokenPath = getTokenPath(account); + const tokenPath = getTokenPath(account); - if (!fs.existsSync(CREDENTIALS_PATH)) { - throw new Error( - `Missing credentials.json at ${CREDENTIALS_PATH}. Run "npm run auth" first.` + if (!fs.existsSync(CREDENTIALS_PATH)) { + throw new Error( + `Missing credentials.json at ${CREDENTIALS_PATH}. Run "npm run auth" first.` + ); + } + if (!fs.existsSync(tokenPath)) { + throw new Error( + `Missing token file at ${tokenPath} for account "${account}". Run "npm run auth" first.` + ); + } + + const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8")); + const { client_id, client_secret, redirect_uris } = + credentials.installed || credentials.web; + + const oAuth2Client = new google.auth.OAuth2( + client_id, + client_secret, + redirect_uris[0] ); - } - if (!fs.existsSync(tokenPath)) { - throw new Error( - `Missing token file at ${tokenPath} for account "${account}". Run "npm run auth" first.` - ); - } - const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8")); - const { client_id, client_secret, redirect_uris } = - credentials.installed || credentials.web; + const token = JSON.parse(fs.readFileSync(tokenPath, "utf-8")); + oAuth2Client.setCredentials(token); - const oAuth2Client = new google.auth.OAuth2( - client_id, - client_secret, - redirect_uris[0] - ); + oAuth2Client.on("tokens", (newTokens) => { + const current = JSON.parse(fs.readFileSync(tokenPath, "utf-8")); + fs.writeFileSync( + tokenPath, + JSON.stringify({ ...current, ...newTokens }, null, 2) + ); + console.error(`Token refreshed and saved for account "${account}".`); + }); - const token = JSON.parse(fs.readFileSync(tokenPath, "utf-8")); - oAuth2Client.setCredentials(token); - - oAuth2Client.on("tokens", (newTokens) => { - const current = JSON.parse(fs.readFileSync(tokenPath, "utf-8")); - fs.writeFileSync( - tokenPath, - JSON.stringify({ ...current, ...newTokens }, null, 2) - ); - console.error(`Token refreshed and saved for account "${account}".`); - }); - - return oAuth2Client; + return oAuth2Client; }; export const getGmailClient = (account: string): gmail_v1.Gmail => - google.gmail({ version: "v1", auth: getOAuth2Client(account) }); + google.gmail({ version: "v1", auth: getOAuth2Client(account) }); export const getSheetsClient = (account: string): sheets_v4.Sheets => - google.sheets({ version: "v4", auth: getOAuth2Client(account) }); + google.sheets({ version: "v4", auth: getOAuth2Client(account) }); export const getCalendarClient = (account: string): calendar_v3.Calendar => - google.calendar({ version: "v3", auth: getOAuth2Client(account) }); + google.calendar({ version: "v3", auth: getOAuth2Client(account) }); export const getSpreadsheetId = (account: string): string => { - const acct = accounts[account]; - if (!acct?.spreadsheetId || acct.spreadsheetId === "PASTE_YOUR_SHEET_ID_HERE") { - throw new Error( - `No spreadsheetId configured for account "${account}" in accounts.json.` - ); - } - return acct.spreadsheetId; + const acct = accounts[account]; + if (!acct?.spreadsheetId || acct.spreadsheetId === "PASTE_YOUR_SHEET_ID_HERE") { + throw new Error( + `No spreadsheetId configured for account "${account}" in accounts.json.` + ); + } + return acct.spreadsheetId; }; export const getCalendarId = (account: string): string => - accounts[account]?.calendarId ?? "primary"; + accounts[account]?.calendarId ?? "primary"; diff --git a/src/prompt-controller-service/prompt-controller-service.ts b/src/prompt-controller-service/prompt-controller-service.ts index fb8a202..d5430cd 100644 --- a/src/prompt-controller-service/prompt-controller-service.ts +++ b/src/prompt-controller-service/prompt-controller-service.ts @@ -1,64 +1,56 @@ import { server } from "../McpServer.js"; import { - accounts, - loadClassificationPrompt, - loadActionPrompt, + accounts, + loadClassificationPrompt, + loadActionPrompt, } from "../loaders/prompt-config-loaders.js"; // --------------------------------------------------------------------------- -// Prompt: review_emails (work account) +// Prompt: Classify Emails (Phase 1) // --------------------------------------------------------------------------- server.registerPrompt( - "review_emails", - { - description: - `Review WORK inbox (${accounts.work?.label ?? "work"}): classify emails, delete A+C, summarize B+D, log B to spreadsheet, create calendar events.`, - }, - () => { - const phase1 = loadClassificationPrompt(); - const phase2 = loadActionPrompt(); - return { - messages: [ - { - role: "user" as const, - content: { - type: "text" as const, - text: - `ACCOUNT: Use account = "work" for ALL tool calls in this session.\n\n` + - (phase1 || "Review my new emails and classify them by job application category.") + - (phase2 ? `\n\n${phase2}` : ""), - }, - }, - ], - }; - } + "classify-emails", + { + description: "Phase 1: Organize, prioritize, and summarize incoming recruiter emails.", + }, + () => { + const phase1 = loadClassificationPrompt(); + return { + messages: [ + { + role: "user" as const, + content: { + type: "text" as const, + text: `ACCOUNT: Use account = "work" for ALL tool calls in this session.\n\n` + + (phase1 || "Review my new emails and classify them by job application category.") + }, + }, + ], + }; + } ); // --------------------------------------------------------------------------- -// Prompt: review_secondary_emails (secondary account) +// Prompt: Take Action on Emails (Phase 2) // --------------------------------------------------------------------------- server.registerPrompt( - "review_secondary_emails", - { - description: - `Review SECONDARY inbox (${accounts.secondary?.label ?? "secondary"}): classify emails, delete A+C, summarize B+D, log B to spreadsheet, create calendar events.`, - }, - () => { - const phase1 = loadClassificationPrompt(); - const phase2 = loadActionPrompt(); - return { - messages: [ - { - role: "user" as const, - content: { - type: "text" as const, - text: - `ACCOUNT: Use account = "secondary" for ALL tool calls in this session.\n\n` + - (phase1 || "Review my new emails and classify them by job application category.") + - (phase2 ? `\n\n${phase2}` : ""), - }, - }, - ], - }; - } + "take-action-on-emails", + { + description: "Phase 2: Log Category B recruiters to spreadsheet and create calendar events.", + }, + () => { + const phase2 = loadActionPrompt(); + return { + messages: [ + { + role: "user" as const, + content: { + type: "text" as const, + text: `ACCOUNT: Use account = "work" for ALL tool calls in this session.\n\n` + + (phase2 || "Process remaining action items for flagged recruiters.") + }, + }, + ], + }; + } ); -- 2.43.0