From 2f99c6fb0bd6a274f26ec064a7760f5fe6fbc1f6 Mon Sep 17 00:00:00 2001 From: logikonline Date: Sat, 24 Jan 2026 17:51:55 -0500 Subject: [PATCH] feat: add AI conversation capabilities to demo addon Implements two AI integration methods: - aiChat: simple text-based AI conversation - aiStructuredOutput: schema-based AI responses using tools Adds ai:conversation permission and AnalyzeCode tool definition to addon.json with rocket icon. Both methods communicate with AIHost service on localhost:5678. --- addon.json | 43 ++++++++++++- main/index.js | 105 ++++++++++++++++++++++++++++++ views/demo.html | 166 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 310 insertions(+), 4 deletions(-) diff --git a/addon.json b/addon.json index 01ce9cc..3f74426 100644 --- a/addon.json +++ b/addon.json @@ -16,6 +16,12 @@ "_comment_description": "Short description for the addon list", "description": "A demo addon showcasing all GitCaddy addon features with detailed examples.", + "_comment_icon": "Icon displayed in the Addon Manager (octicon, emoji, or url)", + "icon": { + "type": "octicon", + "value": "rocket" + }, + "_comment_author": "Author information (shown in addon details)", "author": { "name": "Your Name", @@ -54,7 +60,8 @@ "network:external", "settings:store", "notifications:show", - "ui:dialogs" + "ui:dialogs", + "ai:conversation" ], "_comment_capabilities": "What your addon can do - GitCaddy uses these to find addons", @@ -189,6 +196,40 @@ } ], + "_comment_aiTools": "AI tools for structured output (requires ai:conversation permission)", + "aiTools": [ + { + "name": "AnalyzeCode", + "description": "Analyzes code and provides a structured evaluation with sentiment, complexity, and suggestions.", + "parameters": [ + { + "name": "sentiment", + "type": "string", + "description": "Overall sentiment: positive, neutral, or negative", + "required": true + }, + { + "name": "complexity", + "type": "number", + "description": "Complexity score from 1-10", + "required": true + }, + { + "name": "summary", + "type": "string", + "description": "Brief summary of the code", + "required": true + }, + { + "name": "suggestions", + "type": "array", + "description": "Array of improvement suggestions", + "required": false + } + ] + } + ], + "_comment_contextMenuItems": "Items added to right-click context menus", "contextMenuItems": [ { diff --git a/main/index.js b/main/index.js index 251153f..d8077f1 100644 --- a/main/index.js +++ b/main/index.js @@ -218,6 +218,14 @@ class MyFirstAddon { case 'generateCommitMessage': return this.generateCommitMessage(args[0]); + // ---- AI Methods ---- + // These demonstrate the AI conversation API + case 'aiChat': + return this.aiChat(args[0]); + + case 'aiStructuredOutput': + return this.aiStructuredOutput(args[0], args[1]); + default: throw new Error(`Unknown method: ${method}`); } @@ -437,6 +445,8 @@ class MyFirstAddon { 'IPC communication', 'Event handling', 'Native file dialogs', + 'AI chat (simple)', + 'AI structured output (tools)', ], hasDialogs: !!this.context?.dialogs, }; @@ -626,6 +636,101 @@ class MyFirstAddon { } } + // ============================================================ + // AI METHODS + // These demonstrate how to use GitCaddy's AI conversation API + // Requires "ai:conversation" permission in addon.json + // ============================================================ + + /** + * Simple AI chat - send a message and get a text response. + * + * This calls the AIHost service running on localhost:5678. + * The AIHost uses the user's configured AI provider (Claude, OpenAI, etc.) + * + * @param {string} prompt - The user's message + * @returns {Promise<{success: boolean, content?: string, error?: string}>} + */ + async aiChat(prompt) { + const AI_HOST_PORT = 5678; + + try { + this.context?.log.info('Sending AI chat request...'); + + const response = await fetch(`http://127.0.0.1:${AI_HOST_PORT}/ai/chat`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + messages: [ + { role: 'user', content: prompt } + ] + }), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const result = await response.json(); + this.context?.log.info('AI chat response received'); + return result; + } catch (error) { + this.context?.log.error('AI chat failed:', error); + return { success: false, error: error.message }; + } + } + + /** + * AI structured output - get a response matching a defined schema. + * + * This uses AI tools/functions to get structured JSON output. + * The tool must be defined in addon.json under contributes.aiTools. + * + * @param {string} prompt - The prompt describing what to analyze + * @param {string} toolName - Name of the tool defined in addon.json (e.g., "AnalyzeCode") + * @returns {Promise<{success: boolean, data?: object, error?: string, fallbackText?: string}>} + */ + async aiStructuredOutput(prompt, toolName) { + const AI_HOST_PORT = 5678; + + try { + this.context?.log.info(`Sending AI structured output request using tool: ${toolName}`); + + // Find the tool definition from our manifest + const tool = this.context?.manifest?.contributes?.aiTools?.find(t => t.name === toolName); + if (!tool) { + return { success: false, error: `Tool '${toolName}' not found in addon manifest` }; + } + + const response = await fetch(`http://127.0.0.1:${AI_HOST_PORT}/ai/addon/structured-output`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + addonId: this.context?.addonId, + prompt: prompt, + tool: tool, + systemPrompt: 'You are a helpful code analysis assistant. Analyze the provided code and respond using the tool.', + temperature: 0.3, + }), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const result = await response.json(); + this.context?.log.info('AI structured output received:', result.success ? 'success' : 'failed'); + return result; + } catch (error) { + this.context?.log.error('AI structured output failed:', error); + return { success: false, error: error.message }; + } + } + // ============================================================ // EVENT HANDLERS // ============================================================ diff --git a/views/demo.html b/views/demo.html index 15dc36f..f3310a2 100644 --- a/views/demo.html +++ b/views/demo.html @@ -32,6 +32,18 @@ flex-direction: column; } + /* Light theme overrides */ + body.light-theme { + --foreground: #24292f; + --background: #ffffff; + --background-secondary: #f6f8fa; + --scrollbar-thumb: #afb8c1; + --scrollbar-thumb-hover: #8c959f; + --border-color: #d0d7de; + --accent-color: #0969da; + color-scheme: light; + } + /* ============================================================ DARK MODE SCROLLBAR STYLING Use theme colors for scrollbars to match dark mode @@ -264,12 +276,11 @@ display: none; flex: 1; min-height: 0; - overflow: hidden; + overflow-y: auto; } .tab-content.active { - display: flex; - flex-direction: column; + display: block; } /* Make the logs section fill available space */ @@ -311,6 +322,7 @@ + @@ -467,6 +479,62 @@ + +
+
+

AI Chat

+

+ Send a message to the AI and get a text response. + This uses GitCaddy's built-in AI service (configured in Settings > AI). +

+
+ +
+
+ +
+

AI Response

+
+ Enter a message above and click "Send to AI" to get a response. +
+
+ +
+

AI Structured Output

+

+ Get structured JSON responses using AI tools. + This example uses the "AnalyzeCode" tool defined in addon.json. +

+
+ +
+
+ +
+

Structured Analysis Result

+
+ Paste code above and click "Analyze Code" to get a structured analysis. +
+
+
+
@@ -540,6 +608,15 @@ // Handle messages from GitCaddy // ============================================================ + // Apply theme based on isDarkTheme flag + function applyTheme(isDarkTheme) { + if (isDarkTheme === false) { + document.body.classList.add('light-theme'); + } else { + document.body.classList.remove('light-theme'); + } + } + window.addEventListener('message', (event) => { const { type, data, actionId, requestId, result, error } = event.data || {}; @@ -548,10 +625,16 @@ // Received initial context from GitCaddy repositoryPath = data?.repositoryPath; hostPort = data?.hostPort; + applyTheme(data?.isDarkTheme); log('info', 'Received context from GitCaddy'); initializeView(); break; + case 'addon:context-update': + // Context updated (e.g., theme changed) + applyTheme(data?.isDarkTheme); + break; + case 'addon:header-action': // Header button was clicked log('info', `Header action clicked: ${actionId}`); @@ -816,6 +899,83 @@ } } + // ============================================================ + // AI DEMO FUNCTIONS + // ============================================================ + + /** + * Send a chat message to the AI. + * Uses the aiChat method in main/index.js which calls the AIHost service. + */ + async function sendAIChat() { + const inputEl = document.getElementById('chat-input'); + const resultEl = document.getElementById('chat-result'); + const prompt = inputEl.value.trim(); + + if (!prompt) { + log('warn', 'Please enter a message to send to the AI'); + inputEl.focus(); + return; + } + + resultEl.innerHTML = ' Sending to AI...'; + log('info', 'Sending AI chat request...'); + + try { + const result = await invokeAddon('aiChat', prompt); + + if (result.success) { + resultEl.textContent = result.content || '(No response)'; + log('info', 'AI chat response received'); + } else { + resultEl.textContent = 'Error: ' + (result.error || 'Unknown error'); + log('error', 'AI chat failed: ' + result.error); + } + } catch (err) { + resultEl.textContent = 'Error: ' + err.message; + log('error', 'AI chat exception: ' + err.message); + } + } + + /** + * Analyze code using AI structured output. + * Uses the aiStructuredOutput method which calls the AnalyzeCode tool. + */ + async function analyzeCode() { + const inputEl = document.getElementById('code-input'); + const resultEl = document.getElementById('structured-result'); + const code = inputEl.value.trim(); + + if (!code) { + log('warn', 'Please enter some code to analyze'); + inputEl.focus(); + return; + } + + resultEl.innerHTML = ' Analyzing code...'; + log('info', 'Sending AI structured output request...'); + + try { + const prompt = `Analyze the following code and provide a structured evaluation:\n\n\`\`\`\n${code}\n\`\`\``; + const result = await invokeAddon('aiStructuredOutput', prompt, 'AnalyzeCode'); + + if (result.success && result.data) { + // Format the structured data nicely + resultEl.textContent = JSON.stringify(result.data, null, 2); + log('info', 'AI analysis complete: ' + result.data.sentiment); + } else if (result.success && result.fallbackText) { + resultEl.textContent = 'Fallback response:\n' + result.fallbackText; + log('warn', 'AI returned fallback text instead of structured output'); + } else { + resultEl.textContent = 'Error: ' + (result.error || 'Unknown error'); + log('error', 'AI analysis failed: ' + result.error); + } + } catch (err) { + resultEl.textContent = 'Error: ' + err.message; + log('error', 'AI analysis exception: ' + err.message); + } + } + // ============================================================ // LOGGING // ============================================================