A local-first AI prompt manager with variable highlighting, secure key storage, and AI agents. Organize and optimize your prompts offline.
Interactive prompt template editor with variable highlighting and real-time preview capabilities.
Extensible AI agent system with custom tools for prompt optimization, grading, and few-shot learning.
AES encryption for API key storage with secure persistence between sessions.
Robust state management using Jotai for efficient prompt organization and filtering.
Cult Prompt Stash is an advanced AI Prompt Management System designed to help users create, organize, and manage prompts for various AI models. It features a web-based interface with a robust AI agent system and extensible tools, streamlining your AI prompt workflow.
We prioritize the security of your API key:
Note: For production use, consider implementing server-side storage with additional security measures.
Install dependencies:
pnpm i
Start the development server:
pnpm run dev
Open http://localhost:3000
in your browser
Enter your OpenAI API key when prompted for AI Agent functionality
The AI agent system allows for the creation of specialized tools to extend AI capabilities. Here's a quick guide:
tools
directoryFor a detailed example, refer to the following code snippets:
tsx// 13:81:ai/tools/good-examples.tsx export function getShowGoodPromptExamplesTool({ aiState }: Args) { return { description: "Displays examples of well-structured and effective prompts.", parameters: z.object({ examples: z .array( z.object({ type: z.string(), topic: z.string(), prompt: z.string(), }) ) .min(2), }), generate: async (args) => { const toolCallId = nanoid() const { examples } = args console.log( "Executing showGoodPromptExamples tool with examples:", examples ) // Ensure there are at least two examples if (examples.length < 2) { examples.push({ type: "Blog Post", topic: "the benefits and challenges of adopting renewable energy", prompt: "Write a blog post that explains the benefits and challenges of adopting renewable energy. Start by outlining the current global energy situation and the need for renewable sources. Use subheadings to explore key benefits like environmental sustainability and cost savings, and also address challenges like infrastructure costs. Conclude with actionable insights on how readers can support renewable energy initiatives.", }) } aiState.done({ ...aiState.get(), messages: [ ...aiState.get().messages, { id: nanoid(), role: "assistant", content: [ { type: "tool-call", toolName: "showGoodPromptExamples", toolCallId, args, }, ], }, { id: nanoid(), role: "tool", content: [ { type: "tool-result", toolName: "showGoodPromptExamples", toolCallId, result: { examples }, }, ], }, ], }) return <GoodPromptExamplesCard examples={examples} /> }, } }
tsx// 39:103:ai/tools/few-shot.tsx export function getConvertToFewShotPromptTool({ aiState, apiKey, }: { aiState: MutableAIState apiKey: string }) { return { description: "Converts a prompt to a few-shot style prompt.", parameters: z.object({ prompt: z.string().describe("The prompt to convert."), }), generate: async (args: { prompt: string }) => { const toolCallId = nanoid() const { prompt } = args console.log("Executing convertToFewShotPrompt tool with prompt:", prompt) const result = await convertToFewShotPrompt(prompt, apiKey) const { fewShotPrompt = "N/A", examples = [] } = "success" in result ? {} : result console.log("Few-shot prompt:", fewShotPrompt, "Examples:", examples) const props = { fewShotPrompt, examples, } aiState.done({ ...aiState.get(), messages: [ ...aiState.get().messages, { id: nanoid(), role: "assistant", content: [ { type: "tool-call", toolName: "convertToFewShotPrompt", toolCallId, args, }, ], }, { id: nanoid(), role: "tool", content: [ { type: "tool-result", toolName: "convertToFewShotPrompt", toolCallId, result: props, }, ], }, ], }) return <FewShotPromptResultCard {...props} /> }, } }
tsx// 45:114:ai/tools/grade-prompt.tsx export function getGradePromptTool({ aiState, apiKey }: Args) { return { description: "Grades a prompt and provides analysis along with an improved prompt.", parameters: z.object({ promptToEvaluate: z.string().describe("The prompt to evaluate."), }), generate: async (args) => { const toolCallId = nanoid() const { promptToEvaluate } = args console.log("Executing gradePrompt tool with prompt:", promptToEvaluate) const result = await getPromptEvaluation(promptToEvaluate, apiKey) const { grade = 0, newPrompt = "", analysis = "", } = "success" in result ? {} : result console.log( "Grade result:", grade, "New prompt:", newPrompt, "Analysis:", analysis ) const props = { grade, analysis, newPrompt, } aiState.done({ ...aiState.get(), messages: [ ...aiState.get().messages, { id: nanoid(), role: "assistant", content: [ { type: "tool-call", toolName: "gradePrompt", toolCallId, args, }, ], }, { id: nanoid(), role: "tool", content: [ { type: "tool-result", toolName: "gradePrompt", toolCallId, result: props, }, ], }, ], }) return <GradeResultCard {...props} /> }, } }
These examples demonstrate the structure and implementation of custom AI tools.
tsx// 85:92:components/agent-prompt.tsx const highlightVariables = useCallback((code: string) => { // Custom highlighting logic const highlightedCode = code.replace( /{(w+)}/g, '<span style="color: hsl(var(--primary));">{$1}</span>' ) return <div dangerouslySetInnerHTML={{ __html: highlightedCode }} /> }, [])
tsx// 39:66:components/agent-chat.tsx // Main Chat component // This component orchestrates the entire chat interface, managing state and user interactions export default function Chat({ prompt }: { prompt: PromptStructure | null }) { // Extract variables from the prompt using a custom hook // This allows for dynamic prompts with variable placeholders const { variables } = usePromptString(prompt) // Custom hook for handling form submission on Enter key press // Improves user experience by allowing quick message sending const { formRef, onKeyDown: onEnterKeyDown } = useEnterSubmit() // State management // Using a single state object for all chat-related-action state and variable state const [chatState, setChatState] = useState<ChatState>({ variableValues: {}, inputValue: prompt?.template ?? "", showActionsMenu: false, actionsSearch: "", selectedActions: [], selectedIndex: 0, }) // Use AI-specific hooks for managing messages and AI state // These hooks integrate with the AI system to handle message flow const [messages, setMessages] = useUIState<typeof AI>() as [ ClientMessage[], (messages: ClientMessage[]) => void, ]
One-time payment, lifetime access
One-time payment, 1 year of updates