behavioral constraints, and personality baseline. This content is injected as the first slot in the system prompt, ensuring consistent persona adherence across all interactions.
2. Knowledge Domain
Persistent memory is split into two layers to optimize context usage and personalization.
memories/MEMORY.md: Acts as a fact base for long-term world knowledge and agent-learned insights. This file grows over time and should be periodically summarized to prevent token bloat.
memories/USER.md: Maintains a dynamic profile of the user, including preferences, communication style, and historical interactions. This enables personalized responses without polluting the global knowledge base.
3. Capabilities (Skills) Domain
Skills are modular packs that grant the agent specific executable abilities. Each skill follows a strict triad structure to ensure consistency and isolation.
SKILL.md: Contains the system prompt instructions that define when and how the agent should invoke the skill. This acts as the skill's contract.
references/: Holds contextual documentation, API specifications, or reference guides required by the skill. The agent reads these files to ground its responses in accurate technical details.
scripts/: Stores executable automation code, binaries, or helper tools. Skills trigger these scripts to perform actions, keeping execution logic separate from prompt logic.
Skills can be bundled, downloaded via a hub, or dynamically generated. The skills/.hub/ directory tracks the state of downloaded skills, including versioning and update metadata.
4. Runtime State Domain
Runtime data is ephemeral but requires structured storage for session continuity and searchability.
sessions/: Stores metadata partitioned by platform (e.g., Slack, Discord, Terminal). This allows the agent to maintain distinct contexts for different communication channels.
state.db: An embedded SQLite database utilizing the FTS5 extension. This provides ultra-fast, full-text search over historical interactions and state changes, enabling the agent to query past events efficiently without loading entire logs into memory.
5. Automation Domain
Autonomous operations are managed through a dedicated automation layer.
cron/jobs.json: Defines scheduled tasks, including intervals, target functions, and retry policies. This allows the agent to perform periodic checks, data syncs, or maintenance tasks without user invocation.
cron/output/: Captures execution logs and standard output from cron jobs. The agent can audit these outputs to verify task success or trigger follow-up actions.
6. Extension Domain
Advanced customization is handled through plugins, hooks, and skins.
plugins/: Contains standalone logic blocks that extend core architectural capabilities. Plugins can modify routing, add new commands, or integrate external APIs.
hooks/: Intercepts system lifecycle events such as pre-boot, post-response, or error handling. Hooks enable developers to inject custom logic at critical points in the agent's execution flow.
skins/: Defines custom visual themes and styling sheets for the command-line interface, allowing for personalized user experiences.
7. Observability Domain
Diagnostics are segregated by subsystem to streamline troubleshooting.
logs/agent.log: Core agent diagnostics, including decision traces and model interactions.
logs/gateway.log: Traffic logs for API requests and responses, useful for monitoring rate limits and latency.
logs/errors.log: Dedicated error tracking for stack traces and exception handling.
Implementation Example
The following TypeScript utility demonstrates how to interact with the skill structure programmatically. This example validates skill manifests and loads references on demand.
import { readFileSync, readdirSync, existsSync, statSync } from 'fs';
import { join, resolve } from 'path';
interface SkillDefinition {
name: string;
prompt: string;
references: string[];
executablePaths: string[];
}
class SkillRegistry {
private workspaceRoot: string;
constructor(baseDir: string) {
this.workspaceRoot = resolve(baseDir);
}
async discoverSkills(): Promise<SkillDefinition[]> {
const skillsDir = join(this.workspaceRoot, 'skills');
if (!existsSync(skillsDir)) return [];
const entries = readdirSync(skillsDir, { withFileTypes: true });
const skills: SkillDefinition[] = [];
for (const entry of entries) {
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
const skillPath = join(skillsDir, entry.name);
const skillMdPath = join(skillPath, 'SKILL.md');
if (!existsSync(skillMdPath)) {
console.warn(`Skill ${entry.name} missing SKILL.md; skipping.`);
continue;
}
const prompt = readFileSync(skillMdPath, 'utf-8');
const references = this.loadReferences(skillPath);
const executables = this.listExecutables(skillPath);
skills.push({
name: entry.name,
prompt,
references,
executablePaths: executables,
});
}
return skills;
}
private loadReferences(skillPath: string): string[] {
const refDir = join(skillPath, 'references');
if (!existsSync(refDir)) return [];
return readdirSync(refDir)
.filter((file) => file.endsWith('.md') || file.endsWith('.txt'))
.map((file) => readFileSync(join(refDir, file), 'utf-8'));
}
private listExecutables(skillPath: string): string[] {
const scriptDir = join(skillPath, 'scripts');
if (!existsSync(scriptDir)) return [];
return readdirSync(scriptDir)
.filter((file) => {
const fullPath = join(scriptDir, file);
return statSync(fullPath).isFile();
})
.map((file) => join(scriptDir, file));
}
}
export { SkillRegistry, SkillDefinition };
Architecture Rationale:
- Markdown-First Knowledge: Using
.md files for SOUL.md, MEMORY.md, and SKILL.md ensures that agent behavior is version-controllable, diffable, and editable by humans without specialized tools.
- SQLite FTS5 for State: While Markdown handles human-readable data,
state.db leverages FTS5 for efficient querying. This hybrid approach avoids the overhead of full-text search on large text files while maintaining the simplicity of a file-based workspace.
- Skill Triad: Separating prompt, references, and scripts enforces modularity. Skills can be shared, updated, or removed without affecting the core agent logic.
Pitfall Guide
| Pitfall | Explanation | Fix |
|---|
| Secret Leakage | Committing .env or auth.json to version control exposes credentials. | Enforce .gitignore rules; use secret managers or CI/CD injection for sensitive data. |
Token Bloat in SOUL.md | Overloading SOUL.md with excessive personality text consumes context window. | Keep constraints concise; use dynamic loading for detailed behavioral examples. |
| Skill Namespace Collisions | Multiple skills with identical names or overlapping triggers cause invocation errors. | Use domain-prefixed names (e.g., devops/deploy); implement trigger validation in the skill loader. |
Unbounded state.db Growth | SQLite database grows indefinitely, degrading query performance and consuming disk space. | Implement retention policies; schedule periodic VACUUM operations; archive old sessions. |
| Hook Recursion | Hooks triggering agent actions that re-trigger the same hook, causing infinite loops. | Add guard flags or depth limits in hook execution; avoid calling agent methods within pre-response hooks. |
| Log Disk Saturation | logs/ directory fills disk space due to lack of rotation. | Configure logrotate or implement application-level log rotation with size/time limits. |
| Stale Skill References | Skills using outdated documentation in references/ lead to hallucinations. | Version skill references; implement update hooks to refresh docs when APIs change. |
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Local Development | File-based memories + SQLite | Fast iteration; low overhead | Low |
| Multi-User Deployment | SQLite + User profiles | Isolation; scalable state | Medium |
| High-Scale Production | External DB + FS Cache | Performance; reliability | High |
| Skill-Heavy Workloads | Modular skills + Hub | Reusability; maintainability | Low |
| Compliance Requirements | Encrypted .env + Audit logs | Security; traceability | Medium |
Configuration Template
config.yaml
workspace:
root: ~/.hermes
version: "2.0"
model:
provider: "openai"
endpoint: "https://api.openai.com/v1"
default_model: "gpt-4o"
temperature: 0.7
memory:
strategy: "hybrid"
retention_days: 90
fts5_enabled: true
logging:
level: "info"
rotation:
max_size: "50MB"
keep_files: 5
setup.sh
#!/usr/bin/env bash
set -euo pipefail
WORKSPACE="${HOME}/.hermes"
echo "Initializing Hermes workspace at ${WORKSPACE}..."
mkdir -p "${WORKSPACE}/memories"
mkdir -p "${WORKSPACE}/skills/{mlops/axolotl/{references,scripts},mlops/vllm,devops,.hub}"
mkdir -p "${WORKSPACE}/sessions"
mkdir -p "${WORKSPACE}/cron/output"
mkdir -p "${WORKSPACE}/plugins"
mkdir -p "${WORKSPACE}/hooks"
mkdir -p "${WORKSPACE}/skins"
mkdir -p "${WORKSPACE}/logs"
touch "${WORKSPACE}/config.yaml"
touch "${WORKSPACE}/.env"
touch "${WORKSPACE}/auth.json"
touch "${WORKSPACE}/SOUL.md"
touch "${WORKSPACE}/memories/MEMORY.md"
touch "${WORKSPACE}/memories/USER.md"
touch "${WORKSPACE}/cron/jobs.json"
touch "${WORKSPACE}/logs/agent.log"
touch "${WORKSPACE}/logs/gateway.log"
touch "${WORKSPACE}/logs/errors.log"
chmod 600 "${WORKSPACE}/.env" "${WORKSPACE}/auth.json"
echo "Workspace initialized successfully."
Quick Start Guide
- Run Setup: Execute
setup.sh to create the directory structure and initialize files.
- Configure Secrets: Edit
.env to add API keys and tokens. Ensure file permissions are restricted.
- Define Identity: Populate
SOUL.md with the agent's core constraints and personality baseline.
- Add a Skill: Create a new directory under
skills/ with SKILL.md, references/, and scripts/ subdirectories.
- Launch Agent: Start the agent process; it will automatically load configuration, skills, and state from the workspace.