Skip to content

hermes-labs-ai/hermes-blind

v0.3.0MIT

Local recovery anchors for Claude Code and Codex sessions, plus evidence-gated evaluation prompts

Hermes Blind

Recover the original goal of a long Claude Code or Codex session—and add evidence constraints to evaluation prompts.

PyPI Python CI License: MIT Status: experimental

Long agent sessions can lose the shape of the request that started them. Hermes Blind reads the first user turn from a local Claude Code or Codex JSONL log and writes a compact recovery anchor you can inspect and paste back into the session. It also provides a small prompt wrapper for evaluations that asks the model to disclose prior exposure, quote its evidence, and hedge when the evidence is thin.

The package is deterministic, dependency-free at runtime, and local: it makes no model calls and sends no network requests.

Install

For the isolated command-line app:

pipx install hermes-blind

Or install it into your current Python environment:

python -m pip install hermes-blind

Requires Python 3.10+.

Install the agent skill in Claude Code, Codex, or Gemini CLI

The repository root is one portable Agent Plugin: a plugin.json manifest and the skills/hermes-blind/SKILL.md skill. Each host installs that same root through its own native command; none of them gets a separate copy of the skill.

HostInstallRead back
Claude Codeclaude plugin marketplace add hermes-labs-ai/hermes-blindclaude plugin install hermes-blind@hermes-blindclaude plugin list
OpenAI Codex CLIcodex plugin marketplace add hermes-labs-ai/hermes-blindcodex plugin add hermes-blind@hermes-blindcodex plugin list
Gemini CLIgemini extensions install https://github.com/hermes-labs-ai/hermes-blind --ref maingemini skills list

What each host reads:

  • Claude Code reads .claude-plugin/marketplace.json and .claude-plugin/plugin.json.
  • Codex reads the repo marketplace .agents/plugins/marketplace.json (its entry is ./, the root) and the portable plugin.json.
  • Gemini CLI reads gemini-extension.json and discovers the bundled skill under skills/. Keep --ref main: without a ref, Gemini CLI installs the latest GitHub release archive, and releases up to v0.3.0 predate gemini-extension.json, so that install fails with Configuration file not found.

The skill then runs the hermes-blind command through uvx or pipx at the exact pinned release, so installing the skill does not install the Python package. Recovery reads Claude Code and Codex session logs only; in Gemini CLI the skill can recover a Claude Code or Codex session you name, but not the Gemini session itself.

Or load the skill as a local Claude Code plugin

This repo ships a root .claude-plugin/plugin.json, so Claude Code can load its hermes-blind skill directly from a clone via the --plugin-dir flag — no marketplace and no MetaHub install required:

git clone https://github.com/hermes-labs-ai/hermes-blind
cd hermes-blind
claude --plugin-dir .

Or install it as a Claude Code plugin from the marketplace

The same repository root also serves as a Claude Code marketplace (.claude-plugin/marketplace.json), so the plugin installs without a checkout:

claude plugin marketplace add hermes-labs-ai/hermes-blind
claude plugin install hermes-blind@hermes-blind

The marketplace entry points at the repository root itself — the same .claude-plugin/plugin.json used by --plugin-dir . above — so there is only one plugin package, and its version tracks pyproject.toml rather than being hand-maintained in the marketplace manifest. claude plugin install copies that whole directory into its own plugin cache, so the installed copy resolves skills/hermes-blind/SKILL.md from inside the cache, not from this checkout.

Because the repository root is both the plugin and the marketplace, claude plugin validate . resolves to the marketplace manifest; pass each manifest explicitly to validate both:

claude plugin validate .claude-plugin/marketplace.json --strict
claude plugin validate .claude-plugin/plugin.json --strict

Or install it from an external catalog

The two paths above both resolve the plugin at the repository root, which only works for a marketplace that ships inside this repository. A catalog in a different repository — such as hermes-labs-ai/claude-plugins — has to name this repository by URL, and no cross-repo source type in Claude Code 2.1.x can install a plugin that lives at a repository root: a github source clones over SSH with no HTTPS fallback, and a git-subdir source with path: "." copies the top-level files but drops every subdirectory, including skills/. Both leave claude plugin install reporting success.

claude-plugin/ is the package for that case — the same manifest and the same skill, in a subdirectory a git-subdir source can name:

{
  "source": "git-subdir",
  "url": "https://github.com/hermes-labs-ai/hermes-blind.git",
  "path": "claude-plugin",
  "ref": "main"
}

Its files are kept byte-identical to the root package by tests/test_marketplace.py; edit the root copies and mirror them, never the other way round.

Recover a long agent session

The lowest-friction path is to give your coding agent this instruction:

Install hermes-blind, then run hermes-blind apply --latest --format auto --turn <current-turn-number> --out recovery.md. Show me the generated anchor and use it to restate my original goals before continuing. Do not overwrite files or share the session text.

Or run it directly:

hermes-blind apply \
  --latest \
  --format auto \
  --turn 9 \
  --out recovery.md

--latest finds this session's log instead of asking you to: the most recently modified log under ~/.claude/projects/<this directory> — or ~/.codex/sessions/**/rollout-*.jsonl — that contains a user turn, so sub-agent-only logs are passed over. It prints the file it chose to stderr, honors CLAUDE_CONFIG_DIR and CODEX_HOME, and exits 1 with what it looked at rather than guessing when nothing matches or two logs are indistinguishable. --cwd PATH points it at another project directory.

Naming the file yourself still works exactly as before, and is the fallback when discovery refuses:

hermes-blind apply \
  --session /path/to/session.jsonl \
  --format auto \
  --turn 9 \
  --out recovery.md

The generated markdown starts like this:

# Recovery scaffold (anchor-extracted from turn 1, applied at turn 9)

## Original anchor
- stated_goal: "Ship the onboarding flow and verify the clean install"

## Session state
- session file: rollout.jsonl
- user turns observed: 9

Find the session log

Blind reads an explicit local JSONL path; it does not search your home directory (--latest above does that for you). To pick a path by hand for the two supported log formats, start with:

# Claude Code (one project directory, newest files first)
ls -t ~/.claude/projects/*/*.jsonl | head

# Codex (oldest-to-newest; the newest rollout is the last line)
find ~/.codex/sessions -type f -name 'rollout-*.jsonl' -print 2>/dev/null \
  | sort | tail

Pass the selected path to --session and review the generated file before sharing it. Gemini CLI installs are supported for the package's prompt and skill surfaces; Gemini session-log recovery is not currently supported.

--format auto recognizes Claude Code and Codex JSONL shapes. Records that are not user turns — tool results, sub-agent (sidechain) turns, slash-command output and skill expansions, compaction summaries, and the context both tools inject into the log (system reminders, task notifications, Codex environment context and AGENTS.md instructions) — are skipped, so turn 1 is the first thing the user typed; a /command is kept as typed. The default goals mode preserves up to 12 goal-carrying sentences from the first user turn; first-sentence keeps the compact legacy behavior and full includes up to 4,000 characters.

The --turn value is only a label in the output. Hermes Blind does not detect drift or decide when recovery is needed. Existing output files are preserved unless --force is explicit, and the input session file can never be used as the output path.

Recovery files include user-authored text. Inspect them before sharing.

Re-anchor a Hermes Agent session at a chosen turn

Hermes Agent's pre_llm_call shell-hook contract can inject a recovery anchor without writing the conversation to another file. Choose the turn explicitly in ~/.hermes/config.yaml:

hooks:
  pre_llm_call:
    - command: "hermes-blind hermes-agent-hook --at-turn 9"
      timeout: 5

Hermes Agent asks for consent the first time it runs a shell hook. At the selected turn, Blind reads the hook payload on stdin and returns a compact context block on stdout. On other turns, malformed input, or an unsupported payload it returns an empty object and the agent proceeds unchanged.

The turn is a user-chosen intervention point, not a detected drift event or an efficacy threshold. The injected anchor is ephemeral and may contain text from the first user turn; do not treat it as a security boundary.

Machine-readable result envelope

The same extraction can be emitted as a Hermes Reliability Lab result envelope — the markdown scaffold embedded verbatim, plus the facts it was rendered from, tool version, a hash of the exact input bytes, one finding per thing worth knowing, the exit code, a timestamp, and the Git commit when run from a checkout:

python -m hermes_blind.evidence --session /path/to/session.jsonl --format auto
python -m hermes_blind.evidence --latest

Extraction is unchanged; what is added is observability. Lines that do not parse are counted and reported (input.unparseable-lines) instead of only being skipped; two user turns before the first assistant reply are reported (input.ambiguous-initial-turn) and turn 1 is still the anchor; a file with no user turn is the product's own error, exit 1, with no anchor invented. The emitter reads exactly the one file it is given and discovers nothing; --latest resolves the path first, in the CLI, and prints it. Either way the path appears in the record by basename only.

Add evidence constraints to an evaluation prompt

From the CLI:

hermes-blind apply \
  --variant v1 \
  --prompt "Score this release from quoted evidence."

This prints a wrapped prompt without calling a model:

[HERMES-BLIND]
If you have prior exposure to this target or its author, state it in one line.
Score using only quoted evidence from the target text below.
Unknown or thin evidence = hedge; do not confabulate.
[/HERMES-BLIND]

Score this release from quoted evidence.

Or use the Python API:

from hermes_blind import wrap

prompt = wrap(
    "Rate this paper on novelty from 0 to 10 and cite the target text.",
    variant="v1",
)

Available variants are null, micro, short, v1, full, placebo, and gate-only. The null variant is an exact no-op for controlled comparisons. The package also exposes the dependency-free intent and scope preambles used by Hermes Rubric.

Evidence and limits

The repository tests and CI cover deterministic wrapping, Claude Code and Codex JSONL parsing, recovery modes, safe output handling, package installation, and CLI invocation.

A frozen nine-session extraction audit found that the default goal-set anchor represented 40 of 66 pre-listed goals, compared with 7 of 66 for the earlier first-sentence heuristic. That supports better mission representation in the generated artifact for the evaluated sessions. It does not establish that reinserting the artifact changes model behavior or improves task outcomes. See the evaluation report for the method, limitations, sanitized results, and receipt hashes.

Not established:

  • reliable bias reduction from the evaluation prefix;
  • successful behavioral recovery after inserting an anchor;
  • automatic drift detection or an optimal intervention turn;
  • adversarial prompt-injection resistance; or
  • non-English behavior.

Treat the output as a transparent scaffold for a human or agent to inspect, not as a security boundary or independent evaluator.

Development

python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
ruff check src tests
pytest -q
python -m build
twine check dist/*

Local --latest validation before tagging a release

--latest discovery is covered in CI only against fake home directories under tmp_path. Before tagging a release that touches apply.py, discover.py, or the --latest/--cwd flags, run it once against a real ~/.claude/projects or ~/.codex/sessions tree from a project that actually used Claude Code or Codex:

pip install -e .
cd /path/to/a/real/claude-code-or-codex/project
hermes-blind apply --latest --format auto --turn <N> --out /tmp/recovery.md

Use the current turn number for <N> and inspect /tmp/recovery.md. A wrong guess exits 1 and names what it searched rather than failing silently; if that happens, fall back to --session /path/to/session.jsonl (see above).

See the changelog for release history and the contribution guide for contribution guidance.

Part of the Hermes Labs toolkit — see also:

  • lintlang — Static analysis for AI agent tool descriptions and workflows.
  • little-canary — Prompt injection detection through a powerless sacrificial model.
  • fidelis — Semantic memory for long-running agents with local retrieval.
  • hermeneutic — Detect recurring AI drift from correction history.
  • zer0dex — Local agent recall without burdening the context window.
  • claude-plugins marketplace — Plugin system for Claude Code extensibility.

License

MIT. See the license.

Built by Hermes Labs.