A Hermes skills trust counts a folder the session never opens

John Burns

The trust entry was <subdir>. The session opened <repo>. <repo> was not on the list, so delta never loaded. hermes skills trust <subdir> had already exited 0 and printed that one project skill would load in sessions started inside this repo.

That sentence is the wrong object. On this binary the path you pass is the path that is stored. The path a later session opens is the nearest ancestor that contains .git, and only when that ancestor is an exact entry in skills.trusted_project_dirs. A folder inside a checkout can be trusted, counted, and still be invisible to every session that starts in it.

The install and the two checkouts

The command was Hermes Agent v0.22.5+471.g5894e75, dated 2026.9.24, commit 5894e750c3db, running on Python 3.14.9, Linux x86_64. HERMES_HOME pointed at a new directory created for the run. The live profile was not read and was not written. No model turn was started. No skill was installed from a registry.

<repo> was a disposable git checkout with four skill files:

<repo>/.hermes/skills/alpha/SKILL.md
<repo>/.agents/skills/beta/SKILL.md
<repo>/.claude/skills/gamma/SKILL.md
<repo>/<subdir>/.hermes/skills/delta/SKILL.md

<subdir> had no .git of its own. <other-repo> was a second checkout, and its only skill was .hermes/skills/delta/SKILL.md. Each file had a name, a one-line description, and a one-sentence body. The skills page, read on 1 November 2026, says a project root is the nearest ancestor containing .git, and that project skills are read from <project-root>/.hermes/skills/ and <project-root>/.agents/skills/.

The shell already had TERMINAL_CWD set to the account home directory. That inherited value is one of the results below. The same list command was repeated with the variable unset, and again with it set to a checkout.

The stored path is not the opened path

hermes skills trust <subdir> printed:

Trusted: <subdir>
1 project skill(s) will load in sessions started inside this repo (they take precedence over same-named profile skills).

The config then contained the absolute path of <subdir> under skills.trusted_project_dirs. The command checks that the argument is a directory, resolves it, and appends it when that exact path is absent. It does not require .git. It does not rewrite the path to the enclosing checkout. The success line still says “this repo”.

A session does not open the stored path. It walks from the working directory to the nearest .git and tests that directory against the trust list. From <subdir>, the walk stops at <repo>. <repo> was not trusted at this step. hermes skills list with TERMINAL_CWD=<subdir> printed an empty table and this footer:

0 hub-installed, 0 builtin, 0 local — 0 enabled, 0 disabled

Delta was the skill the trust command had counted. It was not in the table. The <subdir> entry was still in the config. A membership test on that exact path would succeed. The loader never asks that question, because <subdir> is not what the walk returns.

Trusting <repo> printed a count of 2. Listing from <repo> with TERMINAL_CWD unset printed alpha and beta. Both rows had Source project. Delta was absent. Gamma was absent. Untrusting <repo> and leaving the <subdir> entry in place returned the empty table for a list whose working directory resolved to <subdir>. The untrust line said project skills from <repo> would no longer load. It did not say that the remaining entry could no longer be selected.

Two directories are counted

The count of 2 is alpha plus beta. Gamma lives under .claude/skills/. Other coding agents read that layout. This Hermes build does not scan it when it counts or loads project skills. The trust command and the session use the same two relative directories, .hermes/skills and .agents/skills, but they attach those directories to different roots. Trust attaches them to the path you passed. The session attaches them to the git root it resolved.

Those trees are the same only when the path you passed is the git root. Delta was visible to hermes skills trust <subdir> and invisible to a session started in <subdir>. Alpha and beta were invisible to that subdirectory count and visible once <repo> itself was on the list.

A checkout that is its own git root matches the sentence. <other-repo> contained .git and one skill. Trusting it printed a count of 1. hermes skills list with TERMINAL_CWD=<other-repo>, started from a different working directory, printed delta. Source was project. Status was enabled. The footer said 1 local, not 1 project.

The Trust column is not the trust list

The table has a Trust column. For alpha, beta, and delta it printed local. That word is not a reading of skills.trusted_project_dirs. A skill recorded in the hub lock gets the trust level stored with that install. A skill that is not in the lock is labeled builtin or local first. Source is then rewritten to project when the file was found under a trusted project directory. Trust stays local. The footer counts the first label, so a project skill increments local.

hermes skills list --source local, run in <repo> with TERMINAL_CWD unset, included alpha and beta. The help text on this binary has no --source project. A filter on the Trust column, or a reading of the footer as a project-skill count, does not tell you whether the trust list names a git root. The Source column does. On this run it was the only field that said project.

An inherited directory hides the checkout

The first list after <repo> was trusted was empty, and the shell was inside <repo>. The process had inherited TERMINAL_CWD=<home>. The skills page says a non-interactive surface resolves the project root from that surface’s working directory, and that a cron job uses its workdir through the same mechanism as the terminal tool. On this binary the carrier is TERMINAL_CWD, and it wins over the process working directory.

With the variable unset, the same binary in the same <repo> printed alpha and beta. With the variable set to <other-repo> while the shell was elsewhere, it printed delta and did not print alpha or beta. The directory in the variable is the directory hermes skills list treats as the session. The directory you cd into is not, for as long as the variable is set.

The install tree also refuses to treat the home directory itself as a project, even if a .git entry is present there. This run did not create a checkout at <home>. It only observed that an inherited home value made skills list ignore the trusted checkout the shell was standing in. An empty table under that inherited value is not evidence that the trust entry is missing.

What to run before you keep the sentence

Trust the directory that contains .git, not a folder inside it. From that checkout:

unset TERMINAL_CWD
hermes skills trust
hermes skills list

If you pass a path, pass that same directory, then list with the variable set to it:

hermes skills trust <repo>
TERMINAL_CWD=<repo> hermes skills list

The count in the success line should match the rows whose Source is project. It should not be checked against the local number in the footer. A count of 2 matched alpha and beta. A count of 1 matched delta only when the trusted path was <other-repo>, the checkout that contained delta’s .git. A count printed for a path with no .git is a count of SKILL.md files under the path you named. It is not a prediction of the next session.

If the table is empty and echo "$TERMINAL_CWD" prints a directory, run the list again with the variable unset or set to the checkout. Then read skills.trusted_project_dirs in the profile config. Each entry has to be equal to the nearest .git ancestor of the directory the session will use. An entry that is a child of that ancestor does not load, and it does not cause the ancestor to load.

hermes skills untrust removes one exact path. It does not remove a nested entry added earlier, and it does not warn that a remaining entry can no longer be selected. After untrust, list from the git root and from any subdirectory you previously passed. Both tables should be empty before you treat the checkout as untrusted.

The skills page already says the project root is the nearest .git ancestor, and that a job loads project skills only when its working directory falls inside a repo you trusted. It does not say that hermes skills trust will accept a non-git directory and describe that directory as this repo. It does not say that an inherited TERMINAL_CWD makes hermes skills list ignore the directory you are standing in. It does not say that the Trust column and the footer will call a project skill local. Those three prints are what this binary produced on 1 November 2026.

Sources

  • Hermes Agent v0.22.5+471.g5894e75, commit 5894e750c3db, Python 3.14.9, Linux x86_64, 1 November 2026. Commands were hermes skills trust, hermes skills untrust, and hermes skills list against a disposable HERMES_HOME. No model turn.
  • Skills system, project-local skills, read 1 November 2026 from the install tree and from https://hermes-agent.nousresearch.com/docs/user-guide/features/skills
  • find_project_root and is_project_root_trusted in the same install tree: the session root is the nearest .git ancestor, compared by exact resolved path, and the home directory is not used as a project root.