From bb64af9e7a01f5d3f6225c8e51454f6ee28d608e Mon Sep 17 00:00:00 2001 From: Cole Medin Date: Wed, 13 Aug 2025 18:36:36 -0500 Subject: [PATCH] Archon onboarding, README updates, and MCP/global rule expansion for more coding assistants --- README.md | 17 +- archon-ui-main/src/App.tsx | 2 + .../src/components/layouts/MainLayout.tsx | 91 ++++++++- .../components/onboarding/ProviderStep.tsx | 193 ++++++++++++++++++ .../prp/components/SimpleMarkdown.tsx | 4 +- .../components/settings/IDEGlobalRules.tsx | 138 +++++-------- .../src/components/settings/TestStatus.tsx | 2 +- .../src/pages/KnowledgeBasePage.tsx | 4 +- archon-ui-main/src/pages/MCPPage.tsx | 180 ++++++++++++---- archon-ui-main/src/pages/OnboardingPage.tsx | 163 +++++++++++++++ .../src/services/credentialsService.ts | 6 +- archon-ui-main/src/utils/onboarding.ts | 79 +++++++ archon-ui-main/test/pages.test.tsx | 73 ++++++- archon-ui-main/vite.config.ts | 10 +- migration/complete_setup.sql | 8 +- python/src/server/api_routes/projects_api.py | 4 +- python/src/server/config/config.py | 6 +- .../services/storage/code_storage_service.py | 2 +- .../storage/document_storage_service.py | 6 +- 19 files changed, 808 insertions(+), 180 deletions(-) create mode 100644 archon-ui-main/src/components/onboarding/ProviderStep.tsx create mode 100644 archon-ui-main/src/pages/OnboardingPage.tsx create mode 100644 archon-ui-main/src/utils/onboarding.ts diff --git a/README.md b/README.md index a4b568cc..43e83bdc 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ ## 🎯 What is Archon? +> Archon is currently in beta! Expect things to not work 100%, and please feel free to share any feedback and contribute with fixes/new features! + Archon is the **command center** for AI coding assistants. For you, it's a sleek interface to manage knowledge, context, and tasks for your projects. For the AI coding assistant(s), it's a **Model Context Protocol (MCP) server** to collaborate on and leverage the same knowledge, context, and tasks. Connect Claude Code, Kiro, Cursor, Windsurf, etc. to give your AI agents access to: - **Your documentation** (crawled websites, uploaded PDFs/docs) @@ -32,7 +34,7 @@ This new vision for Archon replaces the old one (the agenteer). Archon used to b - **[GitHub Discussions](https://github.com/coleam00/Archon/discussions)** - Join the conversation and share ideas about Archon - **[Contributing Guide](CONTRIBUTING.md)** - How to get involved and contribute to Archon -- **[Introduction Video](#)** - Coming Soon +- **[Introduction Video](https://youtu.be/8pRc_s2VQIo)** - Getting Started Guide and Vision for Archon - **[Dynamous AI Mastery](https://dynamous.ai)** - The birthplace of Archon - come join a vibrant community of other early AI adopters all helping each other transform their careers and businesses! ## Quick Start @@ -70,7 +72,7 @@ This new vision for Archon replaces the old one (the agenteer). Archon used to b This starts the core microservices: - **Server**: Core API and business logic (Port: 8181) - **MCP Server**: Protocol interface for AI clients (Port: 8051) - - **Agents**: AI operations and streaming (Port: 8052) + - **Agents (coming soon!)**: AI operations and streaming (Port: 8052) - **UI**: Web interface (Port: 3737) Ports are configurable in your .env as well! @@ -126,17 +128,6 @@ Once everything is running: | **MCP Server** | archon-mcp | http://localhost:8051 | Model Context Protocol interface | | **Agents Service** | archon-agents | http://localhost:8052 | AI/ML operations, reranking | -### Optional Documentation Service - -The documentation service is optional. To run it: - -```bash -# Start core services + documentation -docker-compose -f docker-compose.yml -f docker-compose.docs.yml up --build -d -``` - -Then access documentation at: **http://localhost:3838** - ## What's Included ### 🧠 Knowledge Management diff --git a/archon-ui-main/src/App.tsx b/archon-ui-main/src/App.tsx index 5ed482e4..c09fc539 100644 --- a/archon-ui-main/src/App.tsx +++ b/archon-ui-main/src/App.tsx @@ -3,6 +3,7 @@ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-d import { KnowledgeBasePage } from './pages/KnowledgeBasePage'; import { SettingsPage } from './pages/SettingsPage'; import { MCPPage } from './pages/MCPPage'; +import { OnboardingPage } from './pages/OnboardingPage'; import { MainLayout } from './components/layouts/MainLayout'; import { ThemeProvider } from './contexts/ThemeContext'; import { ToastProvider } from './contexts/ToastContext'; @@ -18,6 +19,7 @@ const AppRoutes = () => { return ( } /> + } /> } /> } /> {projectsEnabled ? ( diff --git a/archon-ui-main/src/components/layouts/MainLayout.tsx b/archon-ui-main/src/components/layouts/MainLayout.tsx index 8bdbf22b..fca31387 100644 --- a/archon-ui-main/src/components/layouts/MainLayout.tsx +++ b/archon-ui-main/src/components/layouts/MainLayout.tsx @@ -1,10 +1,11 @@ import React, { useState, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import { SideNavigation } from './SideNavigation'; import { ArchonChatPanel } from './ArchonChatPanel'; import { X } from 'lucide-react'; import { useToast } from '../../contexts/ToastContext'; import { credentialsService } from '../../services/credentialsService'; +import { isLmConfigured } from '../../utils/onboarding'; /** * Props for the MainLayout component */ @@ -26,6 +27,7 @@ export const MainLayout: React.FC = ({ const [isChatOpen, setIsChatOpen] = useState(false); const { showToast } = useToast(); const navigate = useNavigate(); + const location = useLocation(); const [backendReady, setBackendReady] = useState(false); // Check backend readiness @@ -36,11 +38,17 @@ export const MainLayout: React.FC = ({ const retryDelay = 1000; try { + // Create AbortController for proper timeout handling + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + // Check if backend is responding with a simple health check const response = await fetch(`${credentialsService['baseUrl']}/health`, { method: 'GET', - timeout: 5000 - } as any); + signal: controller.signal + }); + + clearTimeout(timeoutId); if (response.ok) { const healthData = await response.json(); @@ -68,7 +76,10 @@ export const MainLayout: React.FC = ({ throw new Error(`Backend health check failed: ${response.status}`); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + // Handle AbortError separately for timeout + const errorMessage = error instanceof Error + ? (error.name === 'AbortError' ? 'Request timeout (5s)' : error.message) + : 'Unknown error'; console.log(`Backend not ready yet (attempt ${retryCount + 1}/${maxRetries}):`, errorMessage); // Retry if we haven't exceeded max retries @@ -90,6 +101,59 @@ export const MainLayout: React.FC = ({ }, 1000); // Wait 1 second for initial app startup }, [showToast, navigate]); // Removed backendReady from dependencies to prevent double execution + // Check for onboarding redirect after backend is ready + useEffect(() => { + const checkOnboarding = async () => { + // Skip if not ready, already on onboarding, or already dismissed + if (!backendReady || location.pathname === '/onboarding') { + return; + } + + // Check if onboarding was already dismissed + if (localStorage.getItem('onboardingDismissed') === 'true') { + return; + } + + try { + // Fetch credentials in parallel + const [ragCreds, apiKeyCreds] = await Promise.all([ + credentialsService.getCredentialsByCategory('rag_strategy'), + credentialsService.getCredentialsByCategory('api_keys') + ]); + + // Check if LM is configured + const configured = isLmConfigured(ragCreds, apiKeyCreds); + + if (!configured) { + // Redirect to onboarding + navigate('/onboarding', { replace: true }); + } + } catch (error) { + // Detailed error handling per alpha principles - fail loud but don't block + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + const errorDetails = { + context: 'Onboarding configuration check', + pathname: location.pathname, + error: errorMessage, + timestamp: new Date().toISOString() + }; + + // Log with full context and stack trace + console.error('ONBOARDING_CHECK_FAILED:', errorDetails, error); + + // Make error visible to user but don't block app functionality + showToast( + `Configuration check failed: ${errorMessage}. You can manually configure in Settings.`, + 'warning' + ); + + // Let user continue - onboarding is optional, they can configure manually + } + }; + + checkOnboarding(); + }, [backendReady, location.pathname, navigate, showToast]); + return
{/* Fixed full-page background grid that doesn't scroll */}
@@ -104,9 +168,22 @@ export const MainLayout: React.FC = ({
{/* Floating Chat Button - Only visible when chat is closed */} - {!isChatOpen && } + {!isChatOpen && ( +
+ + {/* Tooltip */} +
+
Coming Soon
+
Knowledge Assistant is under development
+
+
+
+ )} {/* Chat Sidebar - Slides in/out from right */}
void; + onSkip: () => void; +} + +export const ProviderStep = ({ onSaved, onSkip }: ProviderStepProps) => { + const [provider, setProvider] = useState('openai'); + const [apiKey, setApiKey] = useState(''); + const [saving, setSaving] = useState(false); + const { showToast } = useToast(); + + const handleSave = async () => { + if (!apiKey.trim()) { + showToast('Please enter an API key', 'error'); + return; + } + + setSaving(true); + try { + // Save the API key + await credentialsService.createCredential({ + key: 'OPENAI_API_KEY', + value: apiKey, + is_encrypted: true, + category: 'api_keys' + }); + + // Update the provider setting if needed + await credentialsService.updateCredential({ + key: 'LLM_PROVIDER', + value: 'openai', + is_encrypted: false, + category: 'rag_strategy' + }); + + showToast('API key saved successfully!', 'success'); + onSaved(); + } catch (error) { + // Detailed error handling for critical configuration per alpha principles + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + const errorDetails = { + context: 'API key configuration', + operation: 'save_openai_key', + provider: 'openai', + error: errorMessage, + timestamp: new Date().toISOString() + }; + + // Log with full context and stack trace + console.error('API_KEY_SAVE_FAILED:', errorDetails, error); + + // Show specific error details to help user resolve the issue + if (errorMessage.includes('duplicate') || errorMessage.includes('already exists')) { + showToast( + 'API key already exists. Please update it in Settings if you want to change it.', + 'warning' + ); + } else if (errorMessage.includes('network') || errorMessage.includes('fetch')) { + showToast( + `Network error while saving API key: ${errorMessage}. Please check your connection.`, + 'error' + ); + } else if (errorMessage.includes('unauthorized') || errorMessage.includes('forbidden')) { + showToast( + `Permission error: ${errorMessage}. Please check backend configuration.`, + 'error' + ); + } else { + // Show the actual error for unknown issues + showToast( + `Failed to save API key: ${errorMessage}`, + 'error' + ); + } + } finally { + setSaving(false); + } + }; + + const handleSkip = () => { + showToast('You can configure your provider in Settings', 'info'); + onSkip(); + }; + + return ( +
+ {/* Provider Selection */} +
+ setApiKey(e.target.value)} + placeholder="sk-..." + accentColor="green" + icon={} + /> +

+ Your API key will be encrypted and stored securely. +

+
+ + + +
+ + +
+ + )} + + {/* Non-OpenAI Provider Message */} + {provider !== 'openai' && ( +
+
+

+ {provider === 'google' && 'Google Gemini configuration will be available in Settings after setup.'} + {provider === 'ollama' && 'Ollama configuration will be available in Settings after setup. Make sure Ollama is running locally.'} +

+
+ +
+ +
+
+ )} +
+ ); +}; \ No newline at end of file diff --git a/archon-ui-main/src/components/prp/components/SimpleMarkdown.tsx b/archon-ui-main/src/components/prp/components/SimpleMarkdown.tsx index c4b9104b..bf59aabc 100644 --- a/archon-ui-main/src/components/prp/components/SimpleMarkdown.tsx +++ b/archon-ui-main/src/components/prp/components/SimpleMarkdown.tsx @@ -38,7 +38,7 @@ export const SimpleMarkdown: React.FC = ({ content, classNa }; const processInlineMarkdown = (text: string): React.ReactNode => { - let processed = text; + const processed = text; const elements: React.ReactNode[] = []; let lastIndex = 0; @@ -55,7 +55,7 @@ export const SimpleMarkdown: React.FC = ({ content, classNa // Process *italic* text const italicRegex = /\*(.*?)\*/g; - let remainingText = processed.slice(lastIndex); + const remainingText = processed.slice(lastIndex); lastIndex = 0; const italicElements: React.ReactNode[] = []; diff --git a/archon-ui-main/src/components/settings/IDEGlobalRules.tsx b/archon-ui-main/src/components/settings/IDEGlobalRules.tsx index 014f1036..0221e78c 100644 --- a/archon-ui-main/src/components/settings/IDEGlobalRules.tsx +++ b/archon-ui-main/src/components/settings/IDEGlobalRules.tsx @@ -4,11 +4,11 @@ import { Card } from '../ui/Card'; import { Button } from '../ui/Button'; import { useToast } from '../../contexts/ToastContext'; -type IDE = 'claude' | 'windsurf' | 'cursor'; +type RuleType = 'claude' | 'universal'; export const IDEGlobalRules = () => { const [copied, setCopied] = useState(false); - const [selectedIDE, setSelectedIDE] = useState('claude'); + const [selectedRuleType, setSelectedRuleType] = useState('claude'); const { showToast } = useToast(); const claudeRules = `# CRITICAL: ARCHON-FIRST RULE - READ THIS FIRST @@ -355,7 +355,7 @@ archon:manage_task( - [ ] Basic functionality tested - [ ] Documentation updated if needed`; - const genericRules = `# Archon Integration & Workflow + const universalRules = `# Archon Integration & Workflow **CRITICAL: This project uses Archon for knowledge management, task tracking, and project organization.** @@ -378,13 +378,7 @@ archon:manage_task( - Maintain task descriptions and add implementation notes - DO NOT MAKE ASSUMPTIONS - check project documentation for questions`; - const ideRules = { - claude: claudeRules, - windsurf: genericRules, - cursor: genericRules - }; - - const currentRules = ideRules[selectedIDE]; + const currentRules = selectedRuleType === 'claude' ? claudeRules : universalRules; // Simple markdown parser for display const renderMarkdown = (text: string) => { @@ -481,7 +475,7 @@ archon:manage_task( try { await navigator.clipboard.writeText(currentRules); setCopied(true); - showToast(`${selectedIDE.charAt(0).toUpperCase() + selectedIDE.slice(1)} rules copied to clipboard!`, 'success'); + showToast(`${selectedRuleType === 'claude' ? 'Claude Code' : 'Universal'} rules copied to clipboard!`, 'success'); // Reset copy icon after 2 seconds setTimeout(() => { @@ -494,94 +488,57 @@ archon:manage_task( }; return ( - +

- Add Global rules to your IDE to increase the consistency of the workflow. + Add global rules to your AI assistant to ensure consistent Archon workflow integration.

- {/* IDE Cards Section */} -
- {/* Cursor Card */} -
setSelectedIDE('cursor')} - className={`relative p-4 rounded-xl cursor-pointer transition-all duration-200 ${ - selectedIDE === 'cursor' - ? 'bg-gradient-to-br from-gray-800/50 to-gray-900/40 dark:from-white/50 dark:to-gray-200/40 border-2 border-gray-500/50 shadow-[0_0_20px_rgba(107,114,128,0.3)]' - : 'bg-gradient-to-br from-gray-800/30 to-gray-900/20 dark:from-white/30 dark:to-gray-200/20 border border-gray-500/30 shadow-lg hover:shadow-[0_0_15px_rgba(107,114,128,0.2)]' - }`} - > -
- Cursor -

Cursor

- {selectedIDE === 'cursor' && ( - - )} -
-

- Create .cursorrules file in project root or use Settings → Rules -

-
+ {/* Rule Type Selector */} +
+ Select rule type + + +
- {/* Windsurf Card */} -
setSelectedIDE('windsurf')} - className={`relative p-4 rounded-xl cursor-pointer transition-all duration-200 ${ - selectedIDE === 'windsurf' - ? 'bg-gradient-to-br from-emerald-500/50 to-green-600/40 border-2 border-emerald-500/50 shadow-[0_0_20px_rgba(16,185,129,0.3)]' - : 'bg-gradient-to-br from-emerald-500/30 to-green-600/20 border border-emerald-500/30 shadow-lg hover:shadow-[0_0_15px_rgba(16,185,129,0.2)]' - }`} - > -
- Windsurf -

Windsurf

- {selectedIDE === 'windsurf' && ( - - )} -
-

- Create .windsurfrules file in project root or use IDE settings -

-
- - {/* Claude Card */} -
setSelectedIDE('claude')} - className={`relative p-4 rounded-xl cursor-pointer transition-all duration-200 ${ - selectedIDE === 'claude' - ? 'bg-gradient-to-br from-orange-500/50 to-orange-600/40 border-2 border-orange-500/50 shadow-[0_0_20px_rgba(251,146,60,0.3)]' - : 'bg-gradient-to-br from-orange-500/30 to-orange-600/20 border border-orange-500/30 shadow-lg hover:shadow-[0_0_15px_rgba(251,146,60,0.2)]' - }`} - > -
- Claude -

Claude Code

- {selectedIDE === 'claude' && ( - - )} -
-

- Create CLAUDE.md file in project root for Claude Code integration -

-
-
- -
+

- {selectedIDE.charAt(0).toUpperCase() + selectedIDE.slice(1)} Rules + {selectedRuleType === 'claude' ? 'Claude Code' : 'Universal Agent'} Rules

@@ -591,16 +548,17 @@ archon:manage_task(
- {/* Security Note */} -
-
- - - -
+ {/* Info Note */} +

- Adding global rules to your IDE helps maintain consistency across your project and ensures all team members follow the same workflow. + Where to place these rules:

+
    +
  • Claude Code: Create a CLAUDE.md file in your project root
  • +
  • Cursor: Create .cursorrules file or add to Settings → Rules
  • +
  • Windsurf: Create .windsurfrules file in project root
  • +
  • Other IDEs: Add to your IDE's AI assistant configuration
  • +
diff --git a/archon-ui-main/src/components/settings/TestStatus.tsx b/archon-ui-main/src/components/settings/TestStatus.tsx index 0f82505c..7484bdf9 100644 --- a/archon-ui-main/src/components/settings/TestStatus.tsx +++ b/archon-ui-main/src/components/settings/TestStatus.tsx @@ -177,7 +177,7 @@ export const TestStatus = () => { const handleStreamMessage = (testType: TestType, message: TestStreamMessage) => { updateTestState(testType, (prev) => { const newLogs = [...prev.logs]; - let newResults = [...prev.results]; + const newResults = [...prev.results]; switch (message.type) { case 'status': diff --git a/archon-ui-main/src/pages/KnowledgeBasePage.tsx b/archon-ui-main/src/pages/KnowledgeBasePage.tsx index 8398f954..dccc5522 100644 --- a/archon-ui-main/src/pages/KnowledgeBasePage.tsx +++ b/archon-ui-main/src/pages/KnowledgeBasePage.tsx @@ -567,8 +567,8 @@ export const KnowledgeBasePage = () => { // Show success toast const message = data.uploadType === 'document' - ? `Document "${data.fileName}" uploaded successfully! ${data.chunksStored || 0} chunks stored.` - : `Crawling completed for ${data.currentUrl}! ${data.chunksStored || 0} chunks stored.`; + ? `Document "${data.fileName}" uploaded successfully!` + : `Crawling completed for ${data.currentUrl}!`; showToast(message, 'success'); // Remove from progress items after a brief delay to show completion diff --git a/archon-ui-main/src/pages/MCPPage.tsx b/archon-ui-main/src/pages/MCPPage.tsx index fdff59f0..b25cf976 100644 --- a/archon-ui-main/src/pages/MCPPage.tsx +++ b/archon-ui-main/src/pages/MCPPage.tsx @@ -6,8 +6,12 @@ import { Button } from '../components/ui/Button'; import { useStaggeredEntrance } from '../hooks/useStaggeredEntrance'; import { useToast } from '../contexts/ToastContext'; import { mcpServerService, ServerStatus, LogEntry, ServerConfig } from '../services/mcpServerService'; +import { IDEGlobalRules } from '../components/settings/IDEGlobalRules'; // import { MCPClients } from '../components/mcp/MCPClients'; // Commented out - feature not implemented +// Supported IDE/Agent types +type SupportedIDE = 'windsurf' | 'cursor' | 'claudecode' | 'cline' | 'kiro' | 'augment'; + /** * MCP Dashboard Page Component * @@ -43,7 +47,7 @@ export const MCPPage = () => { const [isLoading, setIsLoading] = useState(true); const [isStarting, setIsStarting] = useState(false); const [isStopping, setIsStopping] = useState(false); - const [selectedIDE, setSelectedIDE] = useState<'windsurf' | 'cursor' | 'claudecode'>('windsurf'); + const [selectedIDE, setSelectedIDE] = useState('windsurf'); const logsEndRef = useRef(null); const logsContainerRef = useRef(null); const statusPollInterval = useRef(null); @@ -213,41 +217,58 @@ export const MCPPage = () => { - const getConfigForIDE = (ide: 'windsurf' | 'cursor' | 'claudecode') => { - if (!config) return ''; + const getConfigForIDE = (ide: SupportedIDE) => { + if (!config || !config.host || !config.port) { + return '// Configuration not available. Please ensure the server is running.'; + } - if (ide === 'cursor') { - // Cursor connecting to Streamable HTTP server - const cursorConfig = { - mcpServers: { - archon: { - url: `http://${config.host}:${config.port}/mcp` + const mcpUrl = `http://${config.host}:${config.port}/mcp`; + + switch(ide) { + case 'claudecode': + return JSON.stringify({ + name: "archon", + transport: "http", + url: mcpUrl + }, null, 2); + + case 'cline': + case 'kiro': + // Cline and Kiro use stdio transport with mcp-remote + return JSON.stringify({ + mcpServers: { + archon: { + command: "npx", + args: ["mcp-remote", mcpUrl] + } } - } - }; - return JSON.stringify(cursorConfig, null, 2); - } else if (ide === 'windsurf') { - // Windsurf can use Streamable HTTP transport - const windsurfConfig = { - mcpServers: { - archon: { - "serverUrl": `http://${config.host}:${config.port}/mcp` + }, null, 2); + + case 'windsurf': + return JSON.stringify({ + mcpServers: { + archon: { + serverUrl: mcpUrl + } } - } - }; - return JSON.stringify(windsurfConfig, null, 2); - } else { - // Claude Code uses CLI commands, show HTTP config as example - const claudeConfig = { - name: "archon", - transport: "http", - url: `http://${config.host}:${config.port}/mcp` - }; - return JSON.stringify(claudeConfig, null, 2); + }, null, 2); + + case 'cursor': + case 'augment': + return JSON.stringify({ + mcpServers: { + archon: { + url: mcpUrl + } + } + }, null, 2); + + default: + return ''; } }; - const getIDEInstructions = (ide: 'windsurf' | 'cursor' | 'claudecode') => { + const getIDEInstructions = (ide: SupportedIDE) => { switch (ide) { case 'windsurf': return { @@ -278,6 +299,42 @@ export const MCPPage = () => { '3. The connection will be established automatically' ] }; + case 'cline': + return { + title: 'Cline Configuration', + steps: [ + '1. Open VS Code settings (Cmd/Ctrl + ,)', + '2. Search for "cline.mcpServers"', + '3. Click "Edit in settings.json"', + '4. Add the configuration shown below', + '5. Restart VS Code for changes to take effect' + ] + }; + case 'kiro': + return { + title: 'Kiro Configuration', + steps: [ + '1. Open Kiro settings', + '2. Navigate to MCP Servers section', + '3. Add the configuration shown below', + '4. Save and restart Kiro' + ] + }; + case 'augment': + return { + title: 'Augment Configuration', + steps: [ + '1. Open Augment settings', + '2. Navigate to Extensions > MCP', + '3. Add the configuration shown below', + '4. Reload configuration' + ] + }; + default: + return { + title: 'Configuration', + steps: ['Add the configuration to your IDE settings'] + }; } }; @@ -483,16 +540,16 @@ export const MCPPage = () => { {/* IDE Selection Tabs */}
-
+
+ + +
@@ -538,7 +625,15 @@ export const MCPPage = () => { ? 'Copy this configuration and add it to ~/.cursor/mcp.json' : selectedIDE === 'windsurf' ? 'Copy this configuration and add it to your Windsurf MCP settings' - : 'This shows the configuration format for Claude Code' + : selectedIDE === 'claudecode' + ? 'This shows the configuration format for Claude Code' + : selectedIDE === 'cline' + ? 'Copy this configuration and add it to VS Code settings.json under "cline.mcpServers"' + : selectedIDE === 'kiro' + ? 'Copy this configuration and add it to your Kiro MCP settings' + : selectedIDE === 'augment' + ? 'Copy this configuration and add it to your Augment MCP settings' + : 'Copy this configuration and add it to your IDE settings' }

@@ -623,6 +718,15 @@ export const MCPPage = () => {
+ + {/* Global Rules Section */} + +

+ + Global IDE Rules +

+ +
)} diff --git a/archon-ui-main/src/pages/OnboardingPage.tsx b/archon-ui-main/src/pages/OnboardingPage.tsx new file mode 100644 index 00000000..3d92b9eb --- /dev/null +++ b/archon-ui-main/src/pages/OnboardingPage.tsx @@ -0,0 +1,163 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { motion } from 'framer-motion'; +import { Sparkles, Key, Check, ArrowRight } from 'lucide-react'; +import { Button } from '../components/ui/Button'; +import { Card } from '../components/ui/Card'; +import { ProviderStep } from '../components/onboarding/ProviderStep'; + +export const OnboardingPage = () => { + const [currentStep, setCurrentStep] = useState(1); + const navigate = useNavigate(); + + const handleProviderSaved = () => { + setCurrentStep(3); + }; + + const handleProviderSkip = () => { + // Navigate to settings with guidance + navigate('/settings'); + }; + + const handleComplete = () => { + // Mark onboarding as dismissed and navigate to home + localStorage.setItem('onboardingDismissed', 'true'); + navigate('/'); + }; + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1 + } + } + }; + + const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5 } + } + }; + + return ( +
+ + {/* Progress Indicators */} + + {[1, 2, 3].map((step) => ( +
+ ))} + + + {/* Step 1: Welcome */} + {currentStep === 1 && ( + + +
+
+ +
+
+ +

+ Welcome to Archon +

+ +

+ Let's get you set up with your AI provider in just a few steps. This will enable intelligent knowledge retrieval and code assistance. +

+ + +
+
+ )} + + {/* Step 2: Provider Setup */} + {currentStep === 2 && ( + + +
+
+ +
+

+ Configure AI Provider +

+
+ + +
+
+ )} + + {/* Step 3: All Set */} + {currentStep === 3 && ( + + +
+ + + +
+ +

+ All Set! +

+ +

+ You're ready to start using Archon. Begin by adding knowledge sources through website crawling or document uploads. +

+ + +
+
+ )} + +
+ ); +}; \ No newline at end of file diff --git a/archon-ui-main/src/services/credentialsService.ts b/archon-ui-main/src/services/credentialsService.ts index c8b09684..d3e10962 100644 --- a/archon-ui-main/src/services/credentialsService.ts +++ b/archon-ui-main/src/services/credentialsService.ts @@ -121,9 +121,9 @@ class CredentialsService { const settings: RagSettings = { USE_CONTEXTUAL_EMBEDDINGS: false, CONTEXTUAL_EMBEDDINGS_MAX_WORKERS: 3, - USE_HYBRID_SEARCH: false, - USE_AGENTIC_RAG: false, - USE_RERANKING: false, + USE_HYBRID_SEARCH: true, + USE_AGENTIC_RAG: true, + USE_RERANKING: true, MODEL_CHOICE: 'gpt-4.1-nano', LLM_PROVIDER: 'openai', LLM_BASE_URL: '', diff --git a/archon-ui-main/src/utils/onboarding.ts b/archon-ui-main/src/utils/onboarding.ts new file mode 100644 index 00000000..743566f2 --- /dev/null +++ b/archon-ui-main/src/utils/onboarding.ts @@ -0,0 +1,79 @@ +export interface NormalizedCredential { + key: string; + value?: string; + encrypted_value?: string | null; + is_encrypted?: boolean; + category: string; +} + +export interface ProviderInfo { + provider?: string; +} + +/** + * Determines if LM (Language Model) is configured based on credentials + * + * Logic: + * - provider := value of 'LLM_PROVIDER' from ragCreds (if present) + * - if provider === 'openai': check for valid OPENAI_API_KEY + * - if provider === 'google' or 'gemini': check for valid GOOGLE_API_KEY + * - if provider === 'ollama': return true (local, no API key needed) + * - if no provider: check for any valid API key (OpenAI or Google) + */ +export function isLmConfigured( + ragCreds: NormalizedCredential[], + apiKeyCreds: NormalizedCredential[] +): boolean { + // Find the LLM_PROVIDER setting from RAG credentials + const providerCred = ragCreds.find(c => c.key === 'LLM_PROVIDER'); + const provider = providerCred?.value?.toLowerCase(); + + // Debug logging + console.log('🔎 isLmConfigured - Provider:', provider); + console.log('🔎 isLmConfigured - API Keys:', apiKeyCreds.map(c => ({ + key: c.key, + value: c.value, + encrypted_value: c.encrypted_value, + is_encrypted: c.is_encrypted, + hasValidValue: !!(c.value && c.value !== 'null' && c.value !== null) + }))); + + // Helper function to check if a credential has a valid value + const hasValidCredential = (cred: NormalizedCredential | undefined): boolean => { + if (!cred) return false; + return !!( + (cred.value && cred.value !== 'null' && cred.value !== null && cred.value.trim() !== '') || + (cred.is_encrypted && cred.encrypted_value && cred.encrypted_value !== 'null' && cred.encrypted_value !== null) + ); + }; + + // Find API keys + const openAIKeyCred = apiKeyCreds.find(c => c.key.toUpperCase() === 'OPENAI_API_KEY'); + const googleKeyCred = apiKeyCreds.find(c => c.key.toUpperCase() === 'GOOGLE_API_KEY'); + + const hasOpenAIKey = hasValidCredential(openAIKeyCred); + const hasGoogleKey = hasValidCredential(googleKeyCred); + + console.log('🔎 isLmConfigured - OpenAI key valid:', hasOpenAIKey); + console.log('🔎 isLmConfigured - Google key valid:', hasGoogleKey); + + // Check based on provider + if (provider === 'openai') { + // OpenAI provider requires OpenAI API key + return hasOpenAIKey; + } else if (provider === 'google' || provider === 'gemini') { + // Google/Gemini provider requires Google API key + return hasGoogleKey; + } else if (provider === 'ollama') { + // Ollama is local, doesn't need API key + return true; + } else if (provider) { + // Unknown provider, assume it doesn't need an API key + console.log('🔎 isLmConfigured - Unknown provider, assuming configured:', provider); + return true; + } else { + // No provider specified, check if ANY API key is configured + // This allows users to configure either OpenAI or Google without specifying provider + return hasOpenAIKey || hasGoogleKey; + } +} \ No newline at end of file diff --git a/archon-ui-main/test/pages.test.tsx b/archon-ui-main/test/pages.test.tsx index 0cb31880..5666ff9d 100644 --- a/archon-ui-main/test/pages.test.tsx +++ b/archon-ui-main/test/pages.test.tsx @@ -1,6 +1,13 @@ import { render, screen } from '@testing-library/react' -import { describe, test, expect } from 'vitest' +import { describe, test, expect, vi } from 'vitest' import React from 'react' +import { isLmConfigured } from '../src/utils/onboarding' +import type { NormalizedCredential } from '../src/utils/onboarding' + +// Mock useNavigate for onboarding page test +vi.mock('react-router-dom', () => ({ + useNavigate: () => vi.fn() +})) describe('Page Load Tests', () => { test('simple page component renders', () => { @@ -42,4 +49,68 @@ describe('Page Load Tests', () => { expect(screen.getByText('In Progress')).toBeInTheDocument() expect(screen.getByText('Done')).toBeInTheDocument() }) + + test('onboarding page renders', () => { + const MockOnboardingPage = () =>

Welcome to Archon

+ render() + expect(screen.getByText('Welcome to Archon')).toBeInTheDocument() + }) +}) + +describe('Onboarding Detection Tests', () => { + test('isLmConfigured returns true when provider is openai and OPENAI_API_KEY exists', () => { + const ragCreds: NormalizedCredential[] = [ + { key: 'LLM_PROVIDER', value: 'openai', category: 'rag_strategy' } + ] + const apiKeyCreds: NormalizedCredential[] = [ + { key: 'OPENAI_API_KEY', value: 'sk-test123', category: 'api_keys' } + ] + + expect(isLmConfigured(ragCreds, apiKeyCreds)).toBe(true) + }) + + test('isLmConfigured returns true when provider is openai and OPENAI_API_KEY is encrypted', () => { + const ragCreds: NormalizedCredential[] = [ + { key: 'LLM_PROVIDER', value: 'openai', category: 'rag_strategy' } + ] + const apiKeyCreds: NormalizedCredential[] = [ + { key: 'OPENAI_API_KEY', is_encrypted: true, category: 'api_keys' } + ] + + expect(isLmConfigured(ragCreds, apiKeyCreds)).toBe(true) + }) + + test('isLmConfigured returns false when provider is openai and no OPENAI_API_KEY', () => { + const ragCreds: NormalizedCredential[] = [ + { key: 'LLM_PROVIDER', value: 'openai', category: 'rag_strategy' } + ] + const apiKeyCreds: NormalizedCredential[] = [] + + expect(isLmConfigured(ragCreds, apiKeyCreds)).toBe(false) + }) + + test('isLmConfigured returns true when provider is ollama regardless of API keys', () => { + const ragCreds: NormalizedCredential[] = [ + { key: 'LLM_PROVIDER', value: 'ollama', category: 'rag_strategy' } + ] + const apiKeyCreds: NormalizedCredential[] = [] + + expect(isLmConfigured(ragCreds, apiKeyCreds)).toBe(true) + }) + + test('isLmConfigured returns true when no provider but OPENAI_API_KEY exists', () => { + const ragCreds: NormalizedCredential[] = [] + const apiKeyCreds: NormalizedCredential[] = [ + { key: 'OPENAI_API_KEY', value: 'sk-test123', category: 'api_keys' } + ] + + expect(isLmConfigured(ragCreds, apiKeyCreds)).toBe(true) + }) + + test('isLmConfigured returns false when no provider and no OPENAI_API_KEY', () => { + const ragCreds: NormalizedCredential[] = [] + const apiKeyCreds: NormalizedCredential[] = [] + + expect(isLmConfigured(ragCreds, apiKeyCreds)).toBe(false) + }) }) \ No newline at end of file diff --git a/archon-ui-main/vite.config.ts b/archon-ui-main/vite.config.ts index 6436437d..0c812614 100644 --- a/archon-ui-main/vite.config.ts +++ b/archon-ui-main/vite.config.ts @@ -19,15 +19,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { const internalHost = 'archon-server'; // Docker service name for internal communication const externalHost = process.env.HOST || 'localhost'; // Host for external access const host = isDocker ? internalHost : externalHost; - const port = process.env.ARCHON_SERVER_PORT; - - if (!port) { - throw new Error( - 'ARCHON_SERVER_PORT environment variable is required. ' + - 'Please set it in your .env file or environment. ' + - 'Default value: 8181' - ); - } + const port = process.env.ARCHON_SERVER_PORT || env.ARCHON_SERVER_PORT || '8181'; return { plugins: [ diff --git a/migration/complete_setup.sql b/migration/complete_setup.sql index d53fb7cf..58144eb8 100644 --- a/migration/complete_setup.sql +++ b/migration/complete_setup.sql @@ -75,11 +75,11 @@ INSERT INTO archon_settings (key, value, is_encrypted, category, description) VA -- RAG Strategy Configuration (all default to true) INSERT INTO archon_settings (key, value, is_encrypted, category, description) VALUES -('USE_CONTEXTUAL_EMBEDDINGS', 'true', true, 'rag_strategy', 'Enhances embeddings with contextual information for better retrieval'), +('USE_CONTEXTUAL_EMBEDDINGS', 'false', false, 'rag_strategy', 'Enhances embeddings with contextual information for better retrieval'), ('CONTEXTUAL_EMBEDDINGS_MAX_WORKERS', '3', false, 'rag_strategy', 'Maximum parallel workers for contextual embedding generation (1-10)'), -('USE_HYBRID_SEARCH', 'true', true, 'rag_strategy', 'Combines vector similarity search with keyword search for better results'), -('USE_AGENTIC_RAG', 'true', true, 'rag_strategy', 'Enables code example extraction, storage, and specialized code search functionality'), -('USE_RERANKING', 'true', true, 'rag_strategy', 'Applies cross-encoder reranking to improve search result relevance'); +('USE_HYBRID_SEARCH', 'true', false, 'rag_strategy', 'Combines vector similarity search with keyword search for better results'), +('USE_AGENTIC_RAG', 'true', false, 'rag_strategy', 'Enables code example extraction, storage, and specialized code search functionality'), +('USE_RERANKING', 'true', false, 'rag_strategy', 'Applies cross-encoder reranking to improve search result relevance'); -- Monitoring Configuration INSERT INTO archon_settings (key, value, is_encrypted, category, description) VALUES diff --git a/python/src/server/api_routes/projects_api.py b/python/src/server/api_routes/projects_api.py index 64160071..4d97ba34 100644 --- a/python/src/server/api_routes/projects_api.py +++ b/python/src/server/api_routes/projects_api.py @@ -554,13 +554,12 @@ async def create_task(request: CreateTaskRequest): async def list_tasks( status: str | None = None, project_id: str | None = None, - parent_task_id: str | None = None, include_closed: bool = False, page: int = 1, per_page: int = 50, exclude_large_fields: bool = False, ): - """List tasks with optional filters including status, project, and parent task.""" + """List tasks with optional filters including status and project.""" try: logfire.info( f"Listing tasks | status={status} | project_id={project_id} | include_closed={include_closed} | page={page} | per_page={per_page}" @@ -570,7 +569,6 @@ async def list_tasks( task_service = TaskService() success, result = task_service.list_tasks( project_id=project_id, - parent_task_id=parent_task_id, status=status, include_closed=include_closed, ) diff --git a/python/src/server/config/config.py b/python/src/server/config/config.py index d3871792..a86a13e0 100644 --- a/python/src/server/config/config.py +++ b/python/src/server/config/config.py @@ -30,9 +30,9 @@ class RAGStrategyConfig: """Configuration for RAG strategies.""" use_contextual_embeddings: bool = False - use_hybrid_search: bool = False - use_agentic_rag: bool = False - use_reranking: bool = False + use_hybrid_search: bool = True + use_agentic_rag: bool = True + use_reranking: bool = True def validate_openai_api_key(api_key: str) -> bool: diff --git a/python/src/server/services/storage/code_storage_service.py b/python/src/server/services/storage/code_storage_service.py index aa7a3d6f..cacc7d7d 100644 --- a/python/src/server/services/storage/code_storage_service.py +++ b/python/src/server/services/storage/code_storage_service.py @@ -837,7 +837,7 @@ async def add_code_examples_to_supabase( full_documents.append(full_doc) # Generate contextual embeddings - contextual_results = generate_contextual_embeddings_batch( + contextual_results = await generate_contextual_embeddings_batch( full_documents, combined_texts ) diff --git a/python/src/server/services/storage/document_storage_service.py b/python/src/server/services/storage/document_storage_service.py index 17f4676d..340870ee 100644 --- a/python/src/server/services/storage/document_storage_service.py +++ b/python/src/server/services/storage/document_storage_service.py @@ -205,9 +205,9 @@ async def add_documents_to_supabase( sub_batch_contents = batch_contents[ctx_i:ctx_end] sub_batch_docs = full_documents[ctx_i:ctx_end] - # Process sub-batch with a single API call using asyncio.to_thread - sub_results = await asyncio.to_thread( - generate_contextual_embeddings_batch, sub_batch_docs, sub_batch_contents + # Process sub-batch with a single API call + sub_results = await generate_contextual_embeddings_batch( + sub_batch_docs, sub_batch_contents ) # Extract results from this sub-batch