Loading...
Loading...
Desktop GUI companion for Hermes Agent - install, configure, chat with AI assistant featuring tool use, memory, skills, and multi-platform messaging
npx skill4agent add aradotso/hermes-skills hermes-desktop-companionSkill by ara.so — Hermes Skills collection.
~/.hermes127.0.0.1:8642/new/clear/fast/web/image/browse/code/shell/usage/help/tools/skills/model/memory/persona/version/compact/compress/undo/retry/debug/status| Platform | File |
|---|---|
| macOS | |
| Linux (any) | |
| Debian/Ubuntu | |
| Fedora/RHEL | |
| Windows | |
# After installing the .dmg, remove quarantine attribute
xattr -cr "/Applications/Hermes Agent.app".exewinget install NousResearch.HermesDesktopchmod +x hermes-desktop-*.AppImage
./hermes-desktop-*.AppImagesudo dpkg -i hermes-desktop-*.deb
sudo apt-get install -f # Fix dependencies if neededsudo dnf install ./hermes-desktop-*.rpm --nogpgcheckecho "$USER ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/hermes-install
# Re-run installer, then:
sudo rm /etc/sudoers.d/hermes-install~/.hermes127.0.0.1:8642// Example: OpenRouter setup
{
provider: "openrouter",
apiKey: process.env.OPENROUTER_API_KEY,
model: "anthropic/claude-3.5-sonnet",
baseUrl: "https://openrouter.ai/api/v1"
}
// Example: Local Ollama
{
provider: "openai",
apiKey: "not-needed",
model: "llama3.1:8b",
baseUrl: "http://localhost:11434/v1"
}https://your-hermes-server.com/api/keys~/.hermes/profiles/<name># Profiles stored at:
~/.hermes/profiles/
├── default/
│ ├── config.yaml
│ ├── SOUL.md
│ ├── memory.db
│ └── sessions.db
└── work/
└── ...// Example: Honcho
{
provider: "honcho",
apiKey: process.env.HONCHO_API_KEY,
appId: "hermes-desktop",
userId: "user-123"
}
// Example: Mem0
{
provider: "mem0",
apiKey: process.env.MEM0_API_KEY,
userId: "user-123"
}enabled: true
bot_token: ${TELEGRAM_BOT_TOKEN}
allowed_users: [123456789]enabled: true
token: ${DISCORD_BOT_TOKEN}
channel_ids: [1234567890123456789]enabled: true
imap_server: imap.gmail.com
imap_port: 993
smtp_server: smtp.gmail.com
smtp_port: 587
username: ${EMAIL_USERNAME}
password: ${EMAIL_APP_PASSWORD}enabled: true
session_path: ~/.hermes/whatsapp-session
qr_code_callback: true> What's the weather in San Francisco?
[Hermes uses web search tool, returns answer]
> /web latest news on AI regulation
[Forces web search, streams results]
> /image a cyberpunk city at sunset
[Generates image via FAL.ai or configured provider]| Command | Description |
|---|---|
| Start new conversation |
| Clear current chat |
| Switch to faster model |
| Force web search |
| Generate image |
| Browse and extract from URL |
| Execute code task |
| Run shell command |
| Show token usage stats |
| List all commands |
| Show enabled tools |
| List installed skills |
| Get/set current model |
| Search memory |
| Show current persona |
| Show Hermes version |
| Compress chat history |
| Deep compress with MoA |
| Remove last message |
| Retry last message |
| Toggle debug mode |
| Show system status |
📊 Prompt: 1,234 tokens • Completion: 567 tokens • Cost: $0.0123/usage> /usage
Session Usage:
Total Prompt Tokens: 12,345
Total Completion Tokens: 5,678
Total Cost: $0.123
Messages: 15Repository: username/repo-name
Branch: main (optional)---
name: web-search-expert
description: Expert at web searching and information retrieval
triggers:
- "search the web for"
- "find information about"
- "look up"
---
# Web Search Expert
Use the web_search tool to find current information...
## Examples
When user asks: "What's the latest on GPT-5?"
1. Use web_search with query "GPT-5 latest news"
2. Summarize findings
3. Cite sources~/.hermes/skills/<name>/SKILL.md> My name is Alice and I prefer Python over JavaScript
[Hermes stores to memory automatically]
> What's my name?
[Hermes retrieves: "Alice"]> Remember I'm working on a TypeScript project called Hermes Desktop
[Hermes: ✓ Stored to memory]// Example: Search sessions programmatically (if extending app)
import { searchSessions } from './main/database';
const results = await searchSessions('typescript error handling');
// Returns: [{ id, title, timestamp, snippet, profileId }, ...]| Toolset | Capabilities |
|---|---|
| web | Exa/Tavily search, Firecrawl scraping |
| browser | Playwright automation, screenshot, PDF |
| terminal | Shell command execution |
| file | Read, write, list, move files |
| code | Python execution in sandbox |
| vision | Image analysis (GPT-4V, Claude Vision) |
| image | Generation via FAL.ai/DALL-E |
| tts | Text-to-speech synthesis |
| skills | Install/manage skills |
| memory | Store/retrieve context |
| session | Search past conversations |
| clarify | Ask clarifying questions |
| delegation | Multi-agent task delegation |
| moa | Mixture-of-Agents synthesis |
| planning | Break down complex tasks |
# ~/.hermes/profiles/default/config.yaml
tools:
web:
enabled: true
exa_api_key: ${EXA_API_KEY}
tavily_api_key: ${TAVILY_API_KEY}name: "Daily standup summary"
schedule: "0 9 * * 1-5" # 9 AM weekdays
task: "Summarize yesterday's GitHub activity and send to Slack"
delivery:
type: slack
channel: "#standup"node --version # v18+ recommended
npm --version # v9+git clone https://github.com/fathah/hermes-desktop.git
cd hermes-desktop
npm installnpm run devhermes-desktop/
├── src/
│ ├── main/ # Electron main process
│ │ ├── index.ts # Entry point, IPC handlers
│ │ ├── database.ts # SQLite sessions/memory
│ │ ├── installer.ts # Hermes install logic
│ │ └── updater.ts # Auto-update
│ ├── preload/ # Electron preload script
│ │ └── index.ts # IPC bridge to renderer
│ └── renderer/ # React UI
│ ├── App.tsx
│ ├── screens/ # Chat, Sessions, Tools, etc.
│ ├── components/
│ └── lib/ # SSE parser, utils
├── electron.vite.config.ts
├── package.json
└── resources/ # Icons, installers// src/renderer/lib/api.ts
export async function sendChatMessage(message: string, profileId: string) {
return window.electron.ipcRenderer.invoke('chat:send', { message, profileId });
}// src/main/index.ts
ipcMain.handle('chat:send', async (event, { message, profileId }) => {
const response = await fetch('http://127.0.0.1:8642/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, profile: profileId })
});
// Stream SSE events back to renderer
});// src/renderer/lib/sse-parser.ts
export class SSEParser {
private buffer = '';
parse(chunk: string): SSEEvent[] {
this.buffer += chunk;
const lines = this.buffer.split('\n');
this.buffer = lines.pop() || '';
const events: SSEEvent[] = [];
let currentEvent: Partial<SSEEvent> = {};
for (const line of lines) {
if (line.startsWith('event:')) {
currentEvent.event = line.slice(7).trim();
} else if (line.startsWith('data:')) {
currentEvent.data = line.slice(6).trim();
} else if (line === '') {
if (currentEvent.event) {
events.push(currentEvent as SSEEvent);
}
currentEvent = {};
}
}
return events;
}
}// src/renderer/screens/Chat.tsx
const parser = new SSEParser();
fetch('http://127.0.0.1:8642/chat', {
method: 'POST',
body: JSON.stringify({ message })
}).then(async (response) => {
const reader = response.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const events = parser.parse(chunk);
for (const event of events) {
if (event.event === 'content') {
appendContent(JSON.parse(event.data).text);
} else if (event.event === 'tool_start') {
showToolProgress(JSON.parse(event.data));
} else if (event.event === 'usage') {
updateTokenCount(JSON.parse(event.data));
}
}
}
});npm run builddist/npm run build:win # Windows .exe
npm run build:mac # macOS .dmg
npm run build:linux # .AppImage, .deb, .rpmelectron-builder.ymlappId: com.nousresearch.hermesdesktop
productName: Hermes Agent
directories:
output: dist
buildResources: resources
mac:
target:
- dmg
- zip
category: public.app-category.productivity
icon: resources/icon.icns
win:
target:
- nsis
icon: resources/icon.ico
linux:
target:
- AppImage
- deb
- rpm
category: Utility
icon: resources/icon.png~/.hermes/logs/install.logtail -f ~/.hermes/logs/install.logcd ~/.hermes
source venv/bin/activate
python -m hermes.servercurl https://your-hermes-server.com/healthhermes keys list// Test Honcho connection
const response = await fetch('https://api.honcho.dev/apps', {
headers: { 'Authorization': `Bearer ${process.env.HONCHO_API_KEY}` }
});@BotFatherSEND_MESSAGEStelnet smtp.server.com 587.rpmsudo dnf install ./hermes-desktop-<new-version>.rpm --nogpgcheckcurl -I http://127.0.0.1:8642/health/debugcd ~/.hermes/profiles/default
sqlite3 sessions.db "DELETE FROM sessions_fts; INSERT INTO sessions_fts SELECT * FROM sessions;"xattr -cr "/Applications/Hermes Agent.app"# LLM Providers
OPENROUTER_API_KEY=sk-or-...
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GOOGLE_API_KEY=...
XAI_API_KEY=...
# Search/Web Tools
EXA_API_KEY=...
TAVILY_API_KEY=...
FIRECRAWL_API_KEY=...
# Image Generation
FAL_API_KEY=...
REPLICATE_API_TOKEN=...
# Memory Providers
HONCHO_API_KEY=...
MEM0_API_KEY=...
# Messaging Gateways
TELEGRAM_BOT_TOKEN=...
DISCORD_BOT_TOKEN=...
SLACK_BOT_TOKEN=...
TWILIO_ACCOUNT_SID=...
TWILIO_AUTH_TOKEN=...
# Email
EMAIL_USERNAME=...
EMAIL_APP_PASSWORD=...
# Analytics
WANDB_API_KEY=...~/.hermes/.envnpm test// src/__tests__/sse-parser.test.ts
import { describe, it, expect } from 'vitest';
import { SSEParser } from '../renderer/lib/sse-parser';
describe('SSEParser', () => {
it('parses complete events', () => {
const parser = new SSEParser();
const events = parser.parse('event: content\ndata: {"text":"Hello"}\n\n');
expect(events).toHaveLength(1);
expect(events[0].event).toBe('content');
expect(JSON.parse(events[0].data).text).toBe('Hello');
});
it('buffers incomplete events', () => {
const parser = new SSEParser();
const events1 = parser.parse('event: content\n');
const events2 = parser.parse('data: {"text":"Hi"}\n\n');
expect(events1).toHaveLength(0);
expect(events2).toHaveLength(1);
});
});