Summary by the numbers
Hermes core code loads a small, well-defined set of context files at every prompt build. The big surprise: most workspace files that Morsy's AGENTS.md tells the agent to read are not loaded by Hermes — they're a self-prompting convention.
build_system_prompt() at agent/system_prompt.py:1008 calls build_system_prompt_parts() which assembles three tiers: stable (identity + communication style + tool guidance, byte-stable for the session), context (project-context files + SOUL.md), volatile (skills index, memory, USER profile, plugins, timestamp). Each tier is injected at a distinct cache tier to maximize prefix-cache reuse.
Files auto-loaded by Hermes core
Every row below is anchored to file:line in the source. HERMES_HOME resolves per-profile (default → /Users/morsy/.hermes/). <cwd> / <git-root> are computed at session start from the active working directory.
| # | Full path | Loaded by | When loaded |
|---|---|---|---|
| A1 | /Users/morsy/.hermes/SOUL.md |
agent/prompt_builder.py:2196
load_soul_md() → called from system_prompt.py:451
|
Every sessionWhen load_soul_identity=True OR skip_context_files=False. Confirmed exists (3,584 bytes). |
| A2 | /Users/morsy/.hermes/agent-communication-style.md |
agent/prompt_builder.py:2229
load_communication_style_md() → called from system_prompt.py:465
|
Every sessionSingle source of truth for response format (One-liner / Bullets / Cards). Confirmed exists (3,333 bytes). |
build_context_files_prompt() tries .hermes.md → AGENTS.md chain → CLAUDE.md → .cursorrules. Only one project-context type is loaded per session (prompt_builder.py:2465–2470).
| # | Full path pattern | Loaded by | When loaded |
|---|---|---|---|
| B1 | <cwd>/.hermes.md |
agent/prompt_builder.py:104
_find_hermes_md() → _load_hermes_md() at line 2244
|
If cwd or any parent up to git root contains this fileWalks cwd → git root, returns first match. |
| B2 | <cwd>/HERMES.md |
Same loader as B1
_HERMES_MD_NAMES = (".hermes.md", "HERMES.md")
|
Same as B1; uppercase fallback |
| B3 | <git-root>/AGENTS.override.md |
agent/prompt_builder.py:2297
_load_agents_md(); chain from line 2270
|
If no .hermes.md/HERMES.md found AND chain dir contains this fileFirst-name-wins per directory. Personal, typically gitignored. |
| B4 | <git-root>/AGENTS.md | Same loader, line 2315 | Same as B3; lower-priority name |
| B5 | <git-root>/agents.md | Same loader, line 2315 | Same as B3; lowercase fallback |
| B6 | <intermediate-dir>/AGENTS.override.md <intermediate-dir>/AGENTS.md <intermediate-dir>/agents.md |
Same loader, line 2314 Loop walks chain dirs root → cwd | One row per dir between git-root and cwdMerged into one section. Identical content across chain dirs is deduped (prompt_builder.py:2326). |
| B7 | <cwd>/CLAUDE.md |
agent/prompt_builder.py:2355
_load_claude_md()
|
Only if no .hermes.md AND no AGENTS.md chain AND cwd contains thisCwd only, no parent walk. |
| B8 | <cwd>/claude.md | Same loader, line 2357 | Same as B7; lowercase fallback |
| B9 | <cwd>/.cursorrules |
agent/prompt_builder.py:2374
_load_cursorrules()
|
Only if none of B1–B8 matchedCwd only. |
| B10 | <cwd>/.cursor/rules/*.mdc (one per .mdc file; sorted) |
Same loader, line 2389
cursor_rules_dir.glob("*.mdc")
|
Same as B9; sorted alphabeticallyAll matched files included. |
| # | Full path | Loaded by | When loaded |
|---|---|---|---|
| C1 | /Users/morsy/.hermes/memories/MEMORY.md |
tools/memory_tool.py:247
load_from_disk() at line 227
|
Every sessionInjected as volatile-tier MEMORY block at system_prompt.py:902. Only when _memory_enabled=True. Confirmed exists (2,034 bytes). |
| C2 | /Users/morsy/.hermes/memories/USER.md | tools/memory_tool.py:248 | Every sessionInjected at system_prompt.py:907. Only when _user_profile_enabled=True. Confirmed exists (1,367 bytes). |
skill_view(name) tool — never auto-loaded.
| # | Full path pattern | Loaded by | When loaded |
|---|---|---|---|
| D1 | /Users/morsy/.hermes/skills/**/SKILL.md (one per skill) |
agent/prompt_builder.py:1876
iter_skill_index_files() at skill_utils.py:1203
|
Every sessionCold path (line 1876) or snapshot path (line 1853). |
| D2 | /Users/morsy/.hermes/skills/**/DESCRIPTION.md (one per category subdir) |
agent/prompt_builder.py:1971 | Every sessionCategory-level description lines. |
| D3 | /Users/morsy/.hermes/skills/_snapshot.json (pre-parsed cache) |
agent/prompt_builder.py:1579
_load_skills_snapshot()
|
Every sessionWhen present and valid, metadata is read from this file instead of re-parsing disk. Skipped on miss → cold path scans D1 + D2. |
| D4 | /Users/morsy/.hermes/skills/_org/<active_org>/**/SKILL.md (active org mirror only) |
agent/skill_utils.py:1220–1228
Token-gated by .active_org marker
|
Every sessionSame description-only injection as D1. Org mirror subset only. |
| D5 | /Users/morsy/.hermes/skills/_org/<active_org>/**/DESCRIPTION.md | Same loader as D4 | Same as D4 |
| D6 | <skills.external_dirs[i]>/**/SKILL.md (per external dir, expanded ~/${VAR}) |
agent/prompt_builder.py:2003
Dirs from get_external_skills_dirs() at skill_utils.py:533
|
Every sessionCold-path scan, no snapshot cache. Deduped against local. |
| D7 | <skills.external_dirs[i]>/**/DESCRIPTION.md | agent/prompt_builder.py:2029 | Same as D6 |
| D8 | <project-root>/.hermes/skills/**/SKILL.md (trusted only) |
agent/skill_utils.py:895
iter_project_skill_files()
|
When cwd is in a git repo whose root is in skills.trusted_project_dirsTrust gate at skill_utils.py:776. Highest precedence — shadows same-name local/external skills. |
| D9 | <project-root>/.agents/skills/**/SKILL.md (cross-tool convention; trusted only) |
Same loader as D8 | Same trigger as D8 |
| # | Full path pattern | Loaded by | When loaded |
|---|---|---|---|
| F1 | /Users/morsy/.hermes/cron/output/<source_job_id>/*.md (latest by mtime; one per context_from job) |
cron/scheduler.py:4662
job_output_dir.glob("*.md"), sorted mtime desc
|
Each cron tickWhen the job's context_from references an upstream job whose output dir contains *.md. Truncated to 8,000 chars. |
| F2 | <stored job["prompt"]> (user-supplied cron prompt string, not a file) |
cron/scheduler.py:4588 | Every cron tick |
Files loaded on demand
These files are NOT injected at session start. They're fetched and injected into the user message of the specific turn that triggers them.
| # | Full path pattern | Loaded by | When loaded |
|---|---|---|---|
| E1 | /Users/morsy/.hermes/skills/<category>/<name>/SKILL.md (full body) |
tools/skills_tool.py:1086
skill_view(name)
|
When the model calls skill_view, OR a cron job has skills: [name] (cron/scheduler.py:4773)Injects the full body into the user message of that turn only. |
| E2 | …/skills/<cat>/<name>/references/* |
Same loader
skill_view(name, file_path="…")
|
When the model explicitly opens a referenced file |
| E3 | …/skills/<cat>/<name>/templates/* | Same as E2 | Same trigger as E2 |
| E4 | …/skills/<cat>/<name>/scripts/* | Same as E2 | Same trigger as E2 |
| E5 | …/skills/<cat>/<name>/assets/* | Same as E2 | Same trigger as E2 |
Plugin-driven / external-memory blocks
agent/system_prompt.py:915–926callsagent._memory_manager.build_system_prompt()agent/system_prompt.py:931calls_frozen_plugin_prompt_sections(agent)
Files NOT loaded by Hermes core
These files exist in ~/.hermes/workspaces/<agent>/ and the workspace AGENTS.md tells the agent to read them manually. Hermes has no loader for them — confirmed by AST scan of every load_*, read_text, glob, iterdir, os.walk call in the prompt-assembly path.
read_file to fetch them after its own AGENTS.md instructs it to.
| # | Full path pattern | What it is | How the agent reads it |
|---|---|---|---|
| N1 | /Users/morsy/.hermes/workspaces/<agent>/AGENTS.md | Instructions to the agent itself | Agent reads it manually via read_fileHermes loads the cwd-rooted AGENTS.md chain only, not the workspace one. |
| N2 | /Users/morsy/.hermes/workspaces/<agent>/SOUL.md | Same file as HERMES_HOME/SOUL.md for default profile |
Agent reads it manuallyHermes auto-loads HERMES_HOME/SOUL.md, which is the same physical file for the default profile. |
| N3 | /Users/morsy/.hermes/workspaces/<agent>/IDENTITY.md | No core loader exists | Agent reads it manuallyOnly referenced in plugins/memory/honcho/cli.py:1615 as a workspace convention listing. |
| N4 | /Users/morsy/.hermes/workspaces/<agent>/MEMORY.md | No core loader exists | Agent reads it manuallyOnly HERMES_HOME/memories/MEMORY.md is auto-injected. |
| N5 | /Users/morsy/.hermes/workspaces/<agent>/USER.md | No core loader exists | Agent reads it manuallyOnly HERMES_HOME/memories/USER.md is auto-injected. |
| N6 | /Users/morsy/.hermes/workspaces/<agent>/TOOLS.md | No core loader exists | Agent reads it manually |
| N7 | /Users/morsy/.hermes/workspaces/<agent>/HEARTBEAT.md | Read on heartbeat poll | Agent reads it manually when it receives a heartbeat pollWorkspace AGENTS.md instructs this behavior, not Hermes core. |
| N8 | /Users/morsy/.hermes/workspaces/<agent>/memory/YYYY-MM-DD.md (today + yesterday) |
No core loader exists | Agent reads them manually |
| N9 | /Users/morsy/.hermes/workspaces/<agent>/BOOTSTRAP.md | First-run convention | Agent reads it once on first run, then deletes it |
Key findings
What this audit means in practice for clawy (and any agent running under the default profile).
~/.hermes/workspaces/clawy/AGENTS.md is NOT auto-loaded by Hermes. Only the cwd-rooted chain (_agents_md_directory_chain()) is. The workspace AGENTS.md is a self-prompting convention.MEMORY.md / USER.md come from ~/.hermes/memories/, NOT from ~/.hermes/workspaces/<agent>/. Both files exist for clawy but only the memories/ copy is auto-injected.plugins/memory/honcho/cli.py:1615 references those filenames — and that's a CLI helper listing workspace conventions, not a load path..hermes.md > AGENTS.md chain > CLAUDE.md > .cursorrules. Only one is loaded per session. If a git root has .hermes.md and cwd has .cursorrules, only the .hermes.md lands.SKILL.md body fetched via skill_view(name) or cron skills: [name]. Keeps the stable prefix cacheable.~/.hermes/skills/_snapshot.json — on warm cache, SKILL.md / DESCRIPTION.md files are NOT re-read at session start. Manifest mtime/size invalidates the snapshot._is_install_tree()) skips all four context loaders — prevents dev/hermes/AGENTS.md from auto-loading as authoritative project context (#64590).gateway/platforms/base.py:2914. The value is a literal string in ~/.hermes/config.yaml, ephemeral, never persisted to transcripts.Citations index
All paths above are anchored in the following source files. Each row is (file:line → loader function → what it loads).
agent/prompt_builder.py:104 _find_hermes_md .hermes.md / HERMES.md walk
agent/prompt_builder.py:150 DEFAULT_AGENT_IDENTITY fallback identity string
agent/prompt_builder.py:1579 _load_skills_snapshot _snapshot.json
agent/prompt_builder.py:1676 _parse_skill_file individual SKILL.md
agent/prompt_builder.py:1820+ build_skills_system_prompt orchestration
agent/prompt_builder.py:1876 cold-path SKILL.md scan
agent/prompt_builder.py:1971 category DESCRIPTION.md
agent/prompt_builder.py:2003 external SKILL.md scan
agent/prompt_builder.py:2029 external DESCRIPTION.md
agent/prompt_builder.py:2173 load_soul_md SOUL.md
agent/prompt_builder.py:2214 load_communication_style_md agent-communication-style.md
agent/prompt_builder.py:2244 _load_hermes_md .hermes.md / HERMES.md body
agent/prompt_builder.py:2270 _agents_md_directory_chain AGENTS.md chain dirs
agent/prompt_builder.py:2297 _load_agents_md AGENTS.md / agents.md / AGENTS.override.md
agent/prompt_builder.py:2355 _load_claude_md CLAUDE.md / claude.md
agent/prompt_builder.py:2374 _load_cursorrules .cursorrules + .cursor/rules/*.mdc
agent/prompt_builder.py:2407 build_context_files_prompt full project-context orchestration
agent/skill_utils.py:533 get_external_skills_dirs skills.external_dirs
agent/skill_utils.py:616 get_all_skills_dirs local + external
agent/skill_utils.py:762 get_project_skills_dirs trusted project roots
agent/skill_utils.py:895 iter_project_skill_files /.hermes/skills + .agents/skills
agent/skill_utils.py:1203 iter_skill_index_files generic walker
agent/system_prompt.py:406 build_system_prompt_parts 3-tier assembly
agent/system_prompt.py:447–454 SOUL.md loader hook
agent/system_prompt.py:458 DEFAULT_AGENT_IDENTITY fallback
agent/system_prompt.py:465–467 communication-style loader hook
agent/system_prompt.py:900–909 MEMORY/USER block loader hook
agent/system_prompt.py:915–926 external memory-provider block
agent/system_prompt.py:1008 build_system_prompt full assembly
tools/memory_tool.py:64 get_memory_dir memories dir
tools/memory_tool.py:227 load_from_disk MEMORY.md + USER.md
tools/memory_tool.py:247 MEMORY.md read
tools/memory_tool.py:248 USER.md read
tools/skills_tool.py:1086 skill_view full SKILL.md body on demand
cron/scheduler.py:4570 _assemble_cron_prompt cron-specific user msg
cron/scheduler.py:4588 job["prompt"]
cron/scheduler.py:4602–4627 pre-run script output
cron/scheduler.py:4662 upstream-job *.md glob
cron/scheduler.py:4773 skill_view(name) for cron skills
gateway/platforms/base.py:2914 resolve_channel_prompt channel_prompts config lookup
hermes_constants.py:114 get_hermes_home HERMES_HOME root
hermes_constants.py:1658 get_skills_dir HERMES_HOME/skills
Methodology
Reproducible steps. ~5 minutes on /Users/morsy/dev/hermes.
- Resolved the active source tree:
/Users/morsy/dev/hermes@2f7a3274c4onmain(with reapply v0.20.6 local changes) - Located the central loader:
agent/prompt_builder.py+agent/system_prompt.py - Read every loader function:
load_soul_md,load_communication_style_md,_load_hermes_md,_load_agents_md,_load_claude_md,_load_cursorrules,build_context_files_prompt,build_skills_system_prompt - Traced call sites:
build_system_prompt_parts()atagent/system_prompt.py:406→ 3-tier assembly - Memory path:
tools/memory_tool.py:load_from_disk()at line 227 - Skills index path:
agent/skill_utils.py:iter_skill_index_files()at line 1203 +prompt_builder.pyscanners - Cron-specific:
cron/scheduler.py:_assemble_cron_prompt()at line 4570 - Channel prompts:
gateway/platforms/base.py:resolve_channel_prompt()at line 2914 - Negative-result verification: AST scan for
MEMORY.md/USER.md/IDENTITY.md/TOOLS.md/HEARTBEAT.md/BOOTSTRAP.mdacross all*.py(excludingtests/) → onlyplugins/memory/honcho/cli.py:1615and migration scripts reference the no-loader filenames, confirming they are NOT loaded at runtime.