hermes-ide-terminal
AI-native terminal emulator & IDE built with Tauri, React, and Rust
How do I install this agent skill?
npx skills add https://github.com/reason-machines/hermes-skills --skill hermes-ide-terminalIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The Hermes IDE Terminal skill provides documentation and setup instructions for an AI-native terminal emulator. It includes standard development procedures such as installing the Rust toolchain via the official rustup.rs script and configuring the Anthropic CLI for agent capabilities. The skill describes an automated project scanning feature that analyzes codebase structure to provide context for AI interactions. No malicious patterns were detected; all identified behaviors are consistent with the documented purpose of an IDE development environment.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Hermes IDE Terminal Skill
Skill by ara.so — Hermes Skills collection.
Overview
Hermes IDE is an AI-native terminal emulator built with Tauri 2, React 18, and Rust. It provides:
- Agent mode for Claude with rich chat interface, image support, and persistent conversations
- Multi-session terminal management with split panes and WebGL rendering
- Git integration with built-in panel for staging, commits, and inline diffs
- AI intelligence including ghost-text suggestions, prompt composer, and error pattern matching
- Project awareness that scans your codebase to build context for AI agents
- Cross-platform support for macOS, Windows, and Linux
Installation
Pre-built Binaries
Download from the official website:
# Visit https://www.hermes-ide.com/download
# Choose your platform: macOS (Intel/Apple Silicon), Windows, Linux (.deb/.AppImage/.rpm)
Build from Source
Prerequisites:
# Check versions
node --version # 18+
rustc --version # 1.70+
Platform-specific dependencies:
# Linux (Debian/Ubuntu)
sudo apt install libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
# macOS
xcode-select --install
# Windows
# Install Visual Studio Build Tools with "Desktop development with C++"
# Install WebView2 Runtime from Microsoft
Clone and build:
git clone https://github.com/hermes-hq/hermes-ide.git
cd hermes-ide
npm install
npm run tauri dev # Development mode with hot-reload
npm run tauri build # Production build
Architecture
Hermes IDE uses a layered architecture:
React Frontend (TypeScript) ← Tauri IPC Bridge → Rust Backend
UI & State PTY, DB, Scanner
Key directories:
src/- React/TypeScript frontend (components, hooks, state, terminal)src-tauri/- Rust backend (PTY management, SQLite, project scanning)src/api/- Tauri IPC command wrapperssrc/terminal/- Terminal pool & AI intelligence engine
Configuration
Claude Agent Setup
Hermes uses your existing claude CLI authentication:
# Install Claude CLI first
npm install -g @anthropic-ai/claude-cli
# Authenticate (choose one method)
claude auth login # Pro/Max account
claude auth api-key $ANTHROPIC_API_KEY # API key
In Hermes IDE:
- Open Command Palette (Cmd/Ctrl+K)
- Type "Claude"
- Select "Start Claude Agent Session"
- Conversations persist across restarts
Project Scanning
Hermes automatically scans projects to build AI context:
// Project scanning happens automatically when you:
// 1. Open a terminal in a directory
// 2. Attach a project via sidebar
// 3. Use Cmd/Ctrl+Shift+P → "Scan Project"
// Detected information includes:
// - Languages and frameworks
// - Project structure
// - Dependencies
// - Git repository info
Terminal Configuration
Configure shell and environment in settings:
{
"shell": {
"program": "/bin/zsh",
"args": ["-l"]
},
"environment": {
"TERM": "xterm-256color",
"COLORTERM": "truecolor"
},
"fontSize": 14,
"fontFamily": "JetBrains Mono, monospace"
}
Key Features & Usage
Terminal Sessions
// Create new session
// Cmd/Ctrl+T
// Switch sessions
// Cmd/Ctrl+1-9 (first 9 sessions)
// Or use sidebar
// Split panes
// Cmd/Ctrl+D (horizontal)
// Cmd/Ctrl+Shift+D (vertical)
// Close session
// Cmd/Ctrl+W
Git Integration
Access via sidebar panel:
# All git operations available via UI
# - View staged/unstaged/untracked files
# - Click files to see inline diffs
# - Stage/unstage with checkboxes
# - Commit with message input
# - Push/Pull buttons
# Or use git commands directly in terminal
git add .
git commit -m "feat: add new feature"
git push origin main
AI Ghost-Text Suggestions
Hermes provides real-time command suggestions:
# Start typing and ghost text appears
git com█
# Suggestion: git commit -m "message"
# Accept with Tab or Right Arrow
# Reject with Esc or continue typing
Prompt Composer
Use natural language for autonomous execution:
# Open Prompt Composer: Cmd/Ctrl+Shift+P
# Type natural language instructions:
"Create a new React component called UserProfile with TypeScript"
"Fix all eslint errors in src/"
"Run tests and commit if they pass"
# Hermes executes commands and tracks progress
Command Palette
Access all features quickly:
# Open: Cmd/Ctrl+K
# Fuzzy search for:
# - Commands
# - Settings
# - Projects
# - Sessions
# - Git operations
Development Workflow
Frontend Development
// src/components/Terminal.tsx
import React, { useEffect, useRef } from 'react';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
export const TerminalComponent: React.FC = () => {
const terminalRef = useRef<HTMLDivElement>(null);
const xtermRef = useRef<Terminal | null>(null);
useEffect(() => {
if (!terminalRef.current) return;
const term = new Terminal({
fontFamily: 'JetBrains Mono, monospace',
fontSize: 14,
theme: {
background: '#1e1e1e',
foreground: '#d4d4d4',
},
});
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
term.open(terminalRef.current);
fitAddon.fit();
xtermRef.current = term;
return () => {
term.dispose();
};
}, []);
return <div ref={terminalRef} className="terminal-container" />;
};
Backend Development
// src-tauri/src/pty/mod.rs
use portable_pty::{native_pty_system, CommandBuilder, PtySize};
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct PtySession {
pub id: String,
pty: Arc<Mutex<Box<dyn portable_pty::MasterPty + Send>>>,
writer: Arc<Mutex<Box<dyn std::io::Write + Send>>>,
}
impl PtySession {
pub fn new(shell: &str, cwd: &str) -> Result<Self, Box<dyn std::error::Error>> {
let pty_system = native_pty_system();
let pair = pty_system.openpty(PtySize {
rows: 24,
cols: 80,
pixel_width: 0,
pixel_height: 0,
})?;
let mut cmd = CommandBuilder::new(shell);
cmd.cwd(cwd);
let _child = pair.slave.spawn_command(cmd)?;
let writer = pair.master.take_writer()?;
Ok(Self {
id: uuid::Uuid::new_v4().to_string(),
pty: Arc::new(Mutex::new(pair.master)),
writer: Arc::new(Mutex::new(writer)),
})
}
pub async fn write(&self, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let mut writer = self.writer.lock().await;
writer.write_all(data)?;
writer.flush()?;
Ok(())
}
}
Tauri IPC Commands
// src-tauri/src/commands.rs
use tauri::command;
#[command]
pub async fn create_terminal_session(
shell: String,
cwd: String,
) -> Result<String, String> {
let session = PtySession::new(&shell, &cwd)
.map_err(|e| e.to_string())?;
let session_id = session.id.clone();
// Store session in global state
// ...
Ok(session_id)
}
#[command]
pub async fn write_to_terminal(
session_id: String,
data: String,
) -> Result<(), String> {
// Get session from global state
// ...
session.write(data.as_bytes())
.await
.map_err(|e| e.to_string())?;
Ok(())
}
Frontend API Wrappers
// src/api/terminal.ts
import { invoke } from '@tauri-apps/api/tauri';
export interface TerminalSession {
id: string;
cwd: string;
shell: string;
}
export async function createTerminalSession(
shell: string,
cwd: string
): Promise<string> {
return await invoke<string>('create_terminal_session', {
shell,
cwd,
});
}
export async function writeToTerminal(
sessionId: string,
data: string
): Promise<void> {
await invoke('write_to_terminal', {
sessionId,
data,
});
}
export async function resizeTerminal(
sessionId: string,
rows: number,
cols: number
): Promise<void> {
await invoke('resize_terminal', {
sessionId,
rows,
cols,
});
}
Common Patterns
State Management
Hermes uses React Context + useReducer:
// src/state/AppContext.tsx
import React, { createContext, useReducer } from 'react';
interface AppState {
sessions: TerminalSession[];
activeSessionId: string | null;
projects: Project[];
}
type AppAction =
| { type: 'ADD_SESSION'; session: TerminalSession }
| { type: 'REMOVE_SESSION'; sessionId: string }
| { type: 'SET_ACTIVE_SESSION'; sessionId: string };
const appReducer = (state: AppState, action: AppAction): AppState => {
switch (action.type) {
case 'ADD_SESSION':
return {
...state,
sessions: [...state.sessions, action.session],
};
case 'REMOVE_SESSION':
return {
...state,
sessions: state.sessions.filter(s => s.id !== action.sessionId),
};
case 'SET_ACTIVE_SESSION':
return {
...state,
activeSessionId: action.sessionId,
};
default:
return state;
}
};
export const AppContext = createContext<{
state: AppState;
dispatch: React.Dispatch<AppAction>;
} | null>(null);
export const AppProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(appReducer, {
sessions: [],
activeSessionId: null,
projects: [],
});
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
Custom Hooks
// src/hooks/useTerminal.ts
import { useContext, useCallback } from 'react';
import { AppContext } from '../state/AppContext';
import { createTerminalSession, writeToTerminal } from '../api/terminal';
export const useTerminal = () => {
const context = useContext(AppContext);
if (!context) throw new Error('useTerminal must be used within AppProvider');
const { state, dispatch } = context;
const createSession = useCallback(async (shell: string, cwd: string) => {
const sessionId = await createTerminalSession(shell, cwd);
dispatch({
type: 'ADD_SESSION',
session: { id: sessionId, shell, cwd },
});
dispatch({ type: 'SET_ACTIVE_SESSION', sessionId });
return sessionId;
}, [dispatch]);
const write = useCallback(async (data: string) => {
if (!state.activeSessionId) return;
await writeToTerminal(state.activeSessionId, data);
}, [state.activeSessionId]);
return {
sessions: state.sessions,
activeSessionId: state.activeSessionId,
createSession,
write,
};
};
Project Scanning
// src-tauri/src/project/scanner.rs
use std::path::Path;
use walkdir::WalkDir;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct ProjectContext {
pub languages: Vec<String>,
pub frameworks: Vec<String>,
pub dependencies: Vec<String>,
pub structure: String,
}
pub fn scan_project(path: &Path) -> Result<ProjectContext, Box<dyn std::error::Error>> {
let mut languages = Vec::new();
let mut frameworks = Vec::new();
let mut dependencies = Vec::new();
// Detect package.json (Node.js)
if path.join("package.json").exists() {
languages.push("JavaScript".to_string());
let package_json = std::fs::read_to_string(path.join("package.json"))?;
let package: serde_json::Value = serde_json::from_str(&package_json)?;
if let Some(deps) = package.get("dependencies") {
if deps.get("react").is_some() {
frameworks.push("React".to_string());
}
if deps.get("vue").is_some() {
frameworks.push("Vue".to_string());
}
}
}
// Detect Cargo.toml (Rust)
if path.join("Cargo.toml").exists() {
languages.push("Rust".to_string());
}
// Count files by type for structure summary
let mut file_counts = std::collections::HashMap::new();
for entry in WalkDir::new(path).max_depth(3) {
if let Ok(entry) = entry {
if let Some(ext) = entry.path().extension() {
*file_counts.entry(ext.to_string_lossy().to_string()).or_insert(0) += 1;
}
}
}
Ok(ProjectContext {
languages,
frameworks,
dependencies,
structure: format!("{:?}", file_counts),
})
}
Testing
Frontend Tests
# Run all tests
npm run test
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watch
// src/components/__tests__/Terminal.test.tsx
import { render, screen } from '@testing-library/react';
import { TerminalComponent } from '../Terminal';
describe('TerminalComponent', () => {
it('renders terminal container', () => {
render(<TerminalComponent />);
const container = screen.getByClassName('terminal-container');
expect(container).toBeInTheDocument();
});
});
Backend Tests
cd src-tauri
cargo test
cargo test -- --nocapture # Show output
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pty_session_creation() {
let session = PtySession::new("/bin/bash", "/tmp");
assert!(session.is_ok());
let session = session.unwrap();
assert!(!session.id.is_empty());
}
#[tokio::test]
async fn test_write_to_pty() {
let session = PtySession::new("/bin/bash", "/tmp").unwrap();
let result = session.write(b"echo test\n").await;
assert!(result.is_ok());
}
}
Troubleshooting
Build Issues
Error: webkit2gtk not found
# Linux
sudo apt install libwebkit2gtk-4.1-dev
# If still failing, check pkg-config
pkg-config --modversion webkit2gtk-4.1
Error: Rust toolchain not found
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
Error: node-gyp failures on Windows
npm install --global windows-build-tools
npm config set msvs_version 2019
Runtime Issues
Terminal not rendering
// Check terminal element is mounted
useEffect(() => {
console.log('Terminal ref:', terminalRef.current);
if (!terminalRef.current) {
console.error('Terminal container not found');
return;
}
// ...
}, []);
PTY session hangs
// Add timeout to PTY operations
use tokio::time::{timeout, Duration};
let result = timeout(
Duration::from_secs(5),
session.write(data)
).await;
if result.is_err() {
eprintln!("PTY write timed out");
}
Git authentication fails
# Check SSH agent
ssh-add -l
# Or use Git Credential Manager
git config --global credential.helper manager
# Set environment variable in Hermes settings
# GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa"
Performance Issues
High CPU usage
# Check terminal rendering settings
# Reduce font size or disable ligatures
# Limit scrollback buffer size in settings
Memory leaks
// Ensure proper cleanup in useEffect
useEffect(() => {
const term = new Terminal();
// ...
return () => {
term.dispose(); // Critical!
};
}, []);
Environment Variables
# Claude API configuration
export ANTHROPIC_API_KEY=sk-ant-...
# Custom shell
export HERMES_SHELL=/bin/zsh
# Debug logging
export RUST_LOG=debug
export HERMES_DEBUG=1
# Custom config directory
export HERMES_CONFIG_DIR=$HOME/.config/hermes
Contributing
# Fork and clone
git clone https://github.com/YOUR_USERNAME/hermes-ide.git
cd hermes-ide
# Create feature branch
git checkout -b feature/my-feature
# Make changes and test
npm run tauri dev
npm run test
cd src-tauri && cargo test
# Type check
npx tsc --noEmit
# Commit (follows conventional commits)
git commit -m "feat: add new terminal feature"
# Push and open PR
git push origin feature/my-feature
# Sign CLA when prompted
Read CONTRIBUTING.md and DESIGN_PRINCIPLES.md before contributing.
Resources
- Documentation: https://github.com/hermes-hq/hermes-ide#readme
- Architecture: https://github.com/hermes-hq/hermes-ide/blob/main/ARCHITECTURE.md
- Discord: https://discord.gg/vMQXSTY6BM
- Discussions: https://github.com/hermes-hq/hermes-ide/discussions
- Changelog: https://hermes-ide.com/changelog
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/reason-machines/hermes-skills/hermes-ide-terminal">View hermes-ide-terminal on skillZs</a>