install
attest install: idempotent setup + --check doctor mode.
Step functions each return a StepResult; run_install() executes them in spec order and prints one aligned line per step. All subprocess calls go through the single _run() seam so tests can monkeypatch it.
This module implements the detection steps + pure-local fixes (uv, Ollama
reachability, models, .env, first data, warmup) and the agent-wiring steps
(mcp/skill/reasoning/cron), all gated on _find_agent_binary() finding
hermes-agent's own CLI (never our venv's console script).
Status
Bases: StrEnum
The closed set of step outcomes.
A StrEnum member IS a str -- every existing result.status == "OK"
comparison, f-string, and json.dumps call keeps working untouched -- but
ty now rejects a typo'd literal at check time instead of
_STATUS_LABEL[result.status] raising KeyError after all ten install
steps have already run.
StepResult
dataclass
One step's outcome: its Status plus a one-line human detail,
printed verbatim as run_install's aligned report line.
run_install(check=False, yes=False, now=False)
Run every step in spec order, print one aligned line each, and exit
nonzero iff any step is BROKEN -- the CLI entry point for attest install
and its --check doctor mode.
step_env_file(check=False)
Does .env exist; create it from .env.sample if not (skipped
entirely outside a checkout, where there is no sample to copy).
step_first_data(check=False, yes=False, now=False)
Does the database hold any items yet; ingest (and with now, tag)
with consent if not.
An absent database is reported rather than opened: get_db runs
CREATE TABLE as a side effect, so --check opening one would write a
~90KB file for a step whose whole point is to change nothing.
step_mcp_wiring(agent, check=False)
Register the attestation MCP server with hermes-agent (and its per-surface tool restrictions) if not already wired, and verify the registration rather than trust it.
Confirms mcp add actually took (see the comment above the returncode
check) because this is the one step every other tool depends on: a
silent failure here means the agent has no attestation tools while the
installer reports success.
step_models(check=False, yes=False)
Are the configured chat + embed models installed; pull them with consent if not.
Every failure mode here is reported rather than left to abort the whole
run (see the comment above the shutil.which guard): a missing ollama
binary, an unreachable daemon, or a pull that fails partway all become a
StepResult instead of an uncaught exception that would blank the report
for every step after this one.
step_ollama_reachable()
Can the configured Ollama root be reached at all -- skipped, not broken, for a non-Ollama LLM_BASE_URL, which has nothing for this to check.
step_reasoning_override(agent, check=False)
Set agent.reasoning_overrides.<model>: none for a hermes3 chat model.
Scoped to hermes3 by fnmatch (see CLAUDE.md's llm.py note): the override exists for a model-specific quirk and every other chat model is skipped as not needing it, rather than the override being applied broadly on the theory that it is harmless elsewhere.
step_schedule(agent, check=False)
Write the refresh script and register hermes-agent's own cron job for it, detecting a hand-added crontab duplicate rather than reporting OK for a schedule that is silently racing itself.
Verifies agent cron create actually registered the job (see the
comment above the returncode check): one build parsed "cron" as a chat
prompt and exited 0 having registered nothing, so this checks
cron list afterwards rather than trusting the exit code.
step_skill_copy(check=False)
Sync every bundled skill into every skills tree Hermes reads,
byte-for-byte, skipping unchanged files -- and disable the superseded
research-provenance monolith wherever it is still enabled.
Skipped -- not broken -- when the skills are not bundled with this install (see the comment below): this is a fallback lane, and an odd packaging mode losing it should not fail the rest of the run.
step_uv()
Is uv on PATH -- the one dependency this installer cannot fix itself.
step_warmup(check=False)
Pin chat + embed models in VRAM, skipped under --check (see below) and for a non-Ollama backend, which has nothing to pin.