AgentsOpen SourceFreeActiveMachine-verified· beginner · ~10 min setup

Hermes + OKF: a knowledge folder your agent reads before it answers

Wire an OKF knowledge bundle into Hermes so the agent reads knowledge/index.md first, validated bundle conformance + a SOUL.md house rule that points at it.

by Shilpa Mitra· verified today· v1.0.0

Run this workflow

CI-verified, 3/3 fixtures passing.

Build this with your agent

One copy-paste hands Claude Code, Codex, or Cursor the full recipe, steps included, nothing to fetch.

Intended Use

Anyone who wants Hermes to consult a portable knowledge folder instead of re-typing context. CI verifies two things: the knowledge/ bundle passes the OKF v0.1 conformance rule (every non-reserved note parses with a non-empty type), and SOUL.md (Hermes' top-slot standing-instructions file) actually points the agent at knowledge/index.md. No key, no model call. Whether the agent's answer improves is the fenced model step.

Not for

  • Secrets, OKF bundles are plain text for reusable context (projects, preferences, how-tos), not passwords or private numbers
  • Expecting automatic memory, the agent only benefits if SOUL.md tells it to read the folder, and only as far as the notes are correct

The Stack

Tested Against

hermes-agent docs (2026-06)okf@0.1ruby@3.x (YAML stdlib)

Side effects & data flow

Network
none, local only
Writes
./knowledge/ (markdown bundle), ./SOUL.md
Credentials
none required

Data privacy

  • nobody (the bundle is local files) your reusable project/preference context (retention: stays in your repo or ~/.hermes; OKF is plain text you own, keep secrets out of it)

Prerequisites

  • Hermes Agent installed
  • A provider/API key to actually run the agent

Steps

  1. 1

    Write the bundle, point Hermes at it in SOUL.md, and validate

    Make a knowledge/ folder with one OKF note per thing you keep re-explaining (each with a non-empty type) and an index.md listing them. Then add a one-line house rule to SOUL.md (Hermes' slot-1 system-prompt file in ~/.hermes/) telling it to read knowledge/index.md first and follow the links. CI checks the bundle conforms and that SOUL.md references the index.

    mkdir -p knowledge/notes
    cat > knowledge/index.md <<'MD'
    # Projects
    * [Recipe app](notes/recipe-app.md) - Solo weekend project, no accounts.
    
    # Preferences
    * [Writing voice](notes/writing-voice.md) - How I like copy written.
    MD
    cat > knowledge/notes/recipe-app.md <<'MD'
    ---
    type: Project
    title: Side project, the recipe app
    description: What it is and the decisions I keep forgetting.
    tags: [personal, project]
    timestamp: 2026-06-01T00:00:00Z
    ---
    
    # What it is
    A simple app for saving and scaling recipes. Use the [writing voice](/notes/writing-voice.md) for any copy.
    MD
    cat > knowledge/notes/writing-voice.md <<'MD'
    ---
    type: Preference
    title: Writing voice
    description: Plain, warm, no jargon.
    ---
    
    # Voice
    Short sentences. Friendly. No hype.
    MD
    cat > SOUL.md <<'MD'
    # House rules
    Before answering anything about my projects or preferences, read
    knowledge/index.md first and follow the links to the relevant notes.
    Treat those notes as the source of truth for my context.
    MD
    ruby -ryaml -e '
    require "date"
    RESERVED = ["index.md", "log.md"]
    concepts = 0; errors = []
    Dir.glob("knowledge/**/*.md").sort.each do |path|
      next if RESERVED.include?(File.basename(path))
      lines = File.read(path).lines
      unless lines.first && lines.first.strip == "---"
        errors << (path + ": no frontmatter"); next
      end
      rest = lines[1..]
      idx = rest.index { |l| l.strip == "---" }
      unless idx; errors << (path + ": unterminated frontmatter"); next; end
      fm = (begin; YAML.safe_load(rest[0...idx].join, permitted_classes: [Date, Time]); rescue; nil; end)
      unless fm.is_a?(Hash) && !fm["type"].to_s.empty?
        errors << (path + ": missing or empty type"); next
      end
      concepts += 1
    end
    abort "NONCONFORMANT bundle: " + errors.join("; ") unless errors.empty?
    soul = File.exist?("SOUL.md") ? File.read("SOUL.md") : ""
    abort "BAD: SOUL.md is missing" if soul.empty?
    abort "BAD: SOUL.md does not point Hermes at knowledge/index.md" unless soul.include?("knowledge/index.md")
    puts "Hermes+OKF wired OK: knowledge bundle conforms (" + concepts.to_s + " concepts) and SOUL.md reads knowledge/index.md first"
    '
  2. 2

    Let Hermes use it (the model step, not checked by CI)

    Start Hermes with a provider key and ask it something about your project. With the SOUL.md rule in place it reads the bundle first. The same one-line-house-rule trick works in coding assistants: drop the folder in your project and tell the tool to consult it. Whether the answer is right depends on the notes, so CI never claims it.

Eval, 3 fixtures

Last passed: verified today
  • wiredcontainstimeout 30s · max $0

    Expected: Hermes+OKF wired OK: knowledge bundle conforms

  • reads-indexcontainstimeout 30s · max $0

    Expected: SOUL.md reads knowledge/index.md first

  • clean-exitexit_codetimeout 30s · max $0

    Expected: 0

Results

Stop re-explaining your context every chat. Keep an OKF folder where Hermes can see it and tell it once, in SOUL.md, to read the index first. No plugin, because OKF is just files any agent can read.

Did this work for you?

Our CI checks the setup runs. You tell us if the whole thing worked. Tell us straight.

Liked this workflow?

Get new verified workflows in WebAfterAI, three issues a week (Tue, Thu, Sat).