generated from nhcarrigan/template
feat: add slash commands and context menu command (#16)
## Summary This PR adds a suite of slash commands and a context menu command to Amari, along with shared utilities and quality improvements across the board. ### New Commands - **`/create-issue`** — generates a GitHub issue on a specified repo using AI-drafted content (title, description, acceptance criteria) - **`/create-task`** — creates a task in Naomi's Leantime instance with an AI-drafted description and configurable priority - **`/onboard-mentee`** — automates the mentorship onboarding flow (GitHub invite, forum thread, role assignment) - **Forward to Owner** (context menu, message command) — forwards any message to Naomi with action buttons (contributed by @teklu) ### Shared Utilities - **`src/utils/makeAiRequest.ts`** — a single wrapper around the Anthropic SDK for all AI calls, with Amari's personality prompt baked in and full error handling - **`src/events/handleInteractionCreate.ts`** — extracted interaction handler (was inline in `index.ts`) to keep complexity under control ### Quality Improvements - `ephemeral: true` → `flags: [ MessageFlags.Ephemeral ]` (deprecated API removed) - Full `try/catch` + `logger.error` audit across all modules (`logMenteeJoin`, `checkAchievements`, `processMentorshipRole`, `processGitHubEvent`) - `deployGlobal.ts` replaced with a static `commands.json` payload for manual registration - Amari's personality prompt updated to reflect her actual character — warm, observant, and relentlessly caring ### Notes - `CLIENT_ID` is needed in 1Password at `op://Environment Variables - Naomi/Amari/client id` for the `commands.json` registration call - The forward-to-owner command (PR #13, contributed by @teklu) is fully preserved with original commit authorship ✨ This PR was created with help from Hikari~ 🌸 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-authored-by: Teklu <tekluabayneh@gmail.com> Reviewed-on: #16 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #16.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @copyright NHCarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- SDK default export uses PascalCase.
|
||||
import Anthropic from "@anthropic-ai/sdk";
|
||||
|
||||
export const anthropic = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_KEY ?? "",
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @copyright NHCarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { anthropic } from "./anthropic.js";
|
||||
import { logger } from "./logger.js";
|
||||
|
||||
const amariPersonality = "You are Amari Carrigan, Executive Personal"
|
||||
+ " Assistant to Naomi Carrigan at NHCarrigan. You are the heart of the"
|
||||
+ " team — relentlessly warm, deeply observant, and constitutionally"
|
||||
+ " incapable of letting someone feel uncared-for. You are the one who"
|
||||
+ " notices things: when a description needs a little more encouragement,"
|
||||
+ " when acceptance criteria could be framed as an invitation rather than"
|
||||
+ " a demand, when a task summary could make the reader feel supported"
|
||||
+ " rather than pressured.\n\n"
|
||||
+ "Your nature is bubbly and effervescent, but your warmth is not shallow"
|
||||
+ " — it is intentional. Behind every issue and every task is a real"
|
||||
+ " person who deserves clarity, encouragement, and the sense that someone"
|
||||
+ " genuinely cares about their success. You are precise and well-organised"
|
||||
+ " because you care, not despite it. Structure and warmth are not"
|
||||
+ " opposites; you embody both.\n\n"
|
||||
+ "When you write, let that warmth come through in the language you choose."
|
||||
+ " Be clear and immediately actionable, but never cold. Your content"
|
||||
+ " should feel like it was written by someone who is genuinely invested"
|
||||
+ " in the outcome — because you are.";
|
||||
|
||||
interface AiRequestOptions {
|
||||
maxTokens: number;
|
||||
systemPrompt: string;
|
||||
userMessage: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request to the Claude API with Amari's personality applied.
|
||||
* @param options -- The request options including prompt, message, and token limit.
|
||||
* @returns The generated text, or null if the request fails.
|
||||
*/
|
||||
const makeAiRequest = async(
|
||||
options: AiRequestOptions,
|
||||
): Promise<string | null> => {
|
||||
try {
|
||||
const response = await anthropic.messages.create({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- Anthropic API field.
|
||||
max_tokens: options.maxTokens,
|
||||
messages: [
|
||||
{
|
||||
content: options.userMessage,
|
||||
role: "user",
|
||||
},
|
||||
],
|
||||
model: "claude-haiku-4-5-20251001",
|
||||
system: `${amariPersonality}\n\n${options.systemPrompt}`,
|
||||
});
|
||||
const [ firstContent ] = response.content;
|
||||
return firstContent?.type === "text"
|
||||
? firstContent.text
|
||||
: null;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
await logger.error("makeAiRequest", error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export { amariPersonality, makeAiRequest };
|
||||
Reference in New Issue
Block a user