AgentConnect: cover all AI providers + external agents; Settings: Help section

AgentConnect.jsx rewritten:
- Option 1: Built-in AI Coach — lists all 8 providers with free tier
  badges and 'Get key' links, step-by-step setup instructions
- Option 2: External AI Agent (MCP) — Claude Desktop (with Windows
  path), Claude Code CLI, Cursor, Windsurf/other
- Page renamed from 'Connect Your AI Agent' to 'Connect AI'

Settings.jsx:
- Added Help & Support section: Report a Bug (GitHub Issues),
  Ask a Question (GitHub Discussions), Star on GitHub
This commit is contained in:
Claude 2026-07-13 23:27:36 +00:00
parent e1b213a54e
commit 8728776f21
2 changed files with 159 additions and 18 deletions

View file

@ -39,6 +39,17 @@ function CodeBlock({ children, copyText }) {
)
}
const AI_PROVIDERS = [
{ name: 'OpenRouter', desc: '300+ models, one key. 26 free models.', url: 'https://openrouter.ai/keys', free: true },
{ name: 'Groq', desc: 'Ultra-fast Llama inference.', url: 'https://console.groq.com/keys', free: true },
{ name: 'Hugging Face', desc: 'Thousands of open-source models.', url: 'https://huggingface.co/settings/tokens', free: true },
{ name: 'Ollama', desc: 'Run LLMs locally. No key needed.', url: 'https://ollama.com/', free: true },
{ name: 'Google Gemini', desc: 'Gemini 2.0 Flash, 2.5 Pro.', url: 'https://aistudio.google.com/apikey', free: true },
{ name: 'OpenAI', desc: 'GPT-4o, GPT-4o-mini.', url: 'https://platform.openai.com/api-keys', free: false },
{ name: 'Anthropic Claude', desc: 'Claude Sonnet 4.6, Opus 4.8.', url: 'https://console.anthropic.com/', free: false },
{ name: 'Custom', desc: 'Any OpenAI-compatible endpoint.', url: '', free: false },
]
export default function AgentConnect() {
const [config, setConfig] = useState(null)
const [loading, setLoading] = useState(true)
@ -67,30 +78,89 @@ export default function AgentConnect() {
}
}, null, 2)
const cursorConfig = JSON.stringify({
"mcpServers": {
"diligence": {
"url": config.mcp_url,
...(config.api_token ? { "headers": { "Authorization": `Bearer ${config.api_token}` } } : {})
}
}
}, null, 2)
return (
<div className="page">
<a href="/settings/integrations" style={{
fontSize: '0.82rem', color: 'var(--accent)', textDecoration: 'none',
display: 'inline-block', marginBottom: '12px',
}}>
Back to Integrations
&larr; Back to Integrations
</a>
<div style={{ marginBottom: '20px' }}>
<h1 style={{
fontFamily: 'var(--font-display)', fontWeight: 800,
fontSize: '1.3rem', marginBottom: '4px',
}}>
Connect Your AI Agent
Connect AI
</h1>
<p style={{ color: 'var(--text-3)', fontSize: '0.85rem' }}>
Use any MCP-compatible AI to log workouts, track food, and manage your fitness.
Two ways to use AI with Diligence: the built-in chat, or an external agent.
</p>
</div>
{/* Connection Details */}
{/* === OPTION 1: Built-in AI Coach === */}
<div className="card">
<div className="section-label">Connection</div>
<div className="section-label">Option 1 &mdash; Built-in AI Coach</div>
<p style={{ fontSize: '0.85rem', color: 'var(--text-2)', marginBottom: '14px' }}>
Get an API key from any provider below, paste it into{' '}
<a href="/settings/integrations#ai-coaching" style={{ color: 'var(--accent)', fontWeight: 600 }}>
Settings &rarr; Integrations
</a>, then open the <strong>Coach</strong> tab. One key is all you need.
</p>
<div style={{ display: 'grid', gap: '8px' }}>
{AI_PROVIDERS.map(p => (
<div key={p.name} style={{
display: 'flex', alignItems: 'center', gap: '10px',
padding: '10px 0', borderBottom: '1px solid var(--divider)',
}}>
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<span style={{ fontWeight: 700, fontSize: '0.88rem' }}>{p.name}</span>
{p.free && (
<span style={{
fontSize: '0.6rem', padding: '1px 6px', borderRadius: 10,
background: 'var(--green-bg)', color: 'var(--green)',
fontWeight: 700, fontFamily: 'var(--font-mono)',
}}>FREE</span>
)}
</div>
<div style={{ fontSize: '0.78rem', color: 'var(--text-3)' }}>{p.desc}</div>
</div>
{p.url && (
<a href={p.url} target="_blank" rel="noopener noreferrer"
className="btn-outline btn-sm"
style={{ fontSize: '0.72rem', textDecoration: 'none', whiteSpace: 'nowrap' }}>
Get key &rarr;
</a>
)}
</div>
))}
</div>
<p style={{ fontSize: '0.75rem', color: 'var(--text-3)', marginTop: '14px' }}>
After getting a key: <strong>Settings &rarr; Integrations &rarr; AI Coaching</strong> &rarr; paste your key &rarr; open the <strong>Coach</strong> tab.
</p>
</div>
{/* === OPTION 2: External AI Agents (MCP) === */}
<div className="card">
<div className="section-label">Option 2 &mdash; External AI Agent (MCP)</div>
<p style={{ fontSize: '0.85rem', color: 'var(--text-2)', marginBottom: '14px' }}>
Connect an MCP-compatible AI tool to control Diligence from outside the app.
The agent can log workouts, track food, check progress, and manage rewards.
</p>
{/* Connection Details */}
<div style={{ marginBottom: '14px' }}>
<div style={{ fontSize: '0.75rem', color: 'var(--text-3)', fontWeight: 600, marginBottom: '4px' }}>
MCP Endpoint
@ -145,18 +215,9 @@ export default function AgentConnect() {
API token is set but only visible to admin users.
</div>
)}
<div style={{
display: 'flex', gap: '12px', flexWrap: 'wrap',
fontSize: '0.78rem', color: 'var(--text-3)',
}}>
<span>{config.tools_count} tools available</span>
<span style={{ color: 'var(--divider)' }}>|</span>
<span>{config.deployment === 'local' ? 'Local (SQLite)' : 'Docker (PostgreSQL)'}</span>
</div>
</div>
{/* Claude Desktop Setup */}
{/* Claude Desktop */}
<div className="card">
<div className="section-label">Claude Desktop</div>
<p style={{ fontSize: '0.82rem', color: 'var(--text-2)', marginBottom: '12px' }}>
@ -166,15 +227,18 @@ export default function AgentConnect() {
{claudeDesktopConfig}
</CodeBlock>
<p style={{ fontSize: '0.75rem', color: 'var(--text-3)', marginTop: '10px' }}>
On macOS: <code style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem' }}>
macOS: <code style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem' }}>
~/Library/Application Support/Claude/claude_desktop_config.json
</code><br />
Windows: <code style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem' }}>
%APPDATA%\Claude\claude_desktop_config.json
</code>
</p>
</div>
{/* Claude Code Setup */}
{/* Claude Code */}
<div className="card">
<div className="section-label">Claude Code</div>
<div className="section-label">Claude Code (CLI)</div>
<p style={{ fontSize: '0.82rem', color: 'var(--text-2)', marginBottom: '12px' }}>
Add the MCP server from your terminal:
</p>
@ -183,6 +247,27 @@ export default function AgentConnect() {
</CodeBlock>
</div>
{/* Cursor */}
<div className="card">
<div className="section-label">Cursor</div>
<p style={{ fontSize: '0.82rem', color: 'var(--text-2)', marginBottom: '12px' }}>
Add to <code style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem' }}>.cursor/mcp.json</code> in your project:
</p>
<CodeBlock copyText={cursorConfig}>
{cursorConfig}
</CodeBlock>
</div>
{/* Windsurf / Other */}
<div className="card">
<div className="section-label">Windsurf / Other MCP Tools</div>
<p style={{ fontSize: '0.82rem', color: 'var(--text-2)', marginBottom: '12px' }}>
Any MCP-compatible tool can connect using the endpoint and token above.
Use the same JSON format as Cursor &mdash; add a server named "diligence"
pointing to the MCP endpoint URL.
</p>
</div>
{/* What your agent can do */}
<div className="card">
<div className="section-label">What Your Agent Can Do</div>
@ -208,6 +293,9 @@ export default function AgentConnect() {
</div>
))}
</div>
<div style={{ fontSize: '0.72rem', color: 'var(--text-3)', marginTop: '12px' }}>
{config.tools_count} tools available &middot; {config.deployment === 'local' ? 'Local (SQLite)' : 'Docker (PostgreSQL)'}
</div>
</div>
</div>
)

View file

@ -212,6 +212,59 @@ export default function Settings() {
</div>
</div>
{/* Help & Support */}
<div className="card">
<div className="section-label">Help & Support</div>
<div
onClick={() => window.open('https://github.com/DiligenceWorks/Diligence/issues', '_blank')}
style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '14px 0', borderBottom: '1px solid var(--divider)', cursor: 'pointer',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ fontSize: '1.2rem' }}>🐛</span>
<div>
<div style={{ fontWeight: 700, fontSize: '0.95rem' }}>Report a Bug</div>
<div style={{ fontSize: '0.78rem', color: 'var(--text-3)', fontWeight: 500 }}>Open an issue on GitHub</div>
</div>
</div>
<span style={{ color: 'var(--text-3)', fontSize: '1.1rem' }}></span>
</div>
<div
onClick={() => window.open('https://github.com/DiligenceWorks/Diligence/discussions', '_blank')}
style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '14px 0', borderBottom: '1px solid var(--divider)', cursor: 'pointer',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ fontSize: '1.2rem' }}>💬</span>
<div>
<div style={{ fontWeight: 700, fontSize: '0.95rem' }}>Ask a Question</div>
<div style={{ fontSize: '0.78rem', color: 'var(--text-3)', fontWeight: 500 }}>GitHub Discussions</div>
</div>
</div>
<span style={{ color: 'var(--text-3)', fontSize: '1.1rem' }}></span>
</div>
<div
onClick={() => window.open('https://github.com/DiligenceWorks/Diligence', '_blank')}
style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '14px 0', cursor: 'pointer',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ fontSize: '1.2rem' }}></span>
<div>
<div style={{ fontWeight: 700, fontSize: '0.95rem' }}>Star on GitHub</div>
<div style={{ fontSize: '0.78rem', color: 'var(--text-3)', fontWeight: 500 }}>DiligenceWorks/Diligence</div>
</div>
</div>
<span style={{ color: 'var(--text-3)', fontSize: '1.1rem' }}></span>
</div>
</div>
<button className="btn-danger btn-full" style={{ marginTop: '10px' }} onClick={() => { clearToken(); navigate('/login') }}>
Sign Out
</button>