achmadya.dev
Available
COMMAND PALETTE

Find something

10 resultsUse the links below to open a page
Projects
projectMandor PlateA reusable SaaS boilerplate with an API, dashboard, database, and tests in one monorepo.projectMCP QueryA suite of MCP servers for querying Excel and four databases over npx and stdio, with a small runtime and explicit error handling.
Writing
WritingRecording Personal Finances in a Spreadsheet with HermesHow I turn a transaction message into a structured Financial Planner entry with metadata, approval, and verification.WritingBuilding a Dedicated Workspace for Hermes to Work ReliablyHow I separated AI conversation from deterministic execution with a workspace, the kw CLI, skills, jobs, and an approval lifecycle.WritingHow I render Markdown and Mermaid in ReactThe rendering pipeline I use for safe Markdown, highlighted code, and responsive Mermaid diagrams.WritingDesigning an MCP tool call I can traceHow I separate protocol handling, database adapters, and public errors in a small MCP query server.WritingInstalling Hermes Agent and Understanding Its ArchitectureA complete guide to installing Hermes Agent and understanding profiles, skills, tools, gateway, schedules, Kanban, memory, and agent architecture.WritingA monorepo as a context boundary for AIWhat changed when I put contracts, backend, frontend, and tests in one workspace for AI-assisted development.WritingLearning Microsoft SQL Server and its backup mechanismNotes on learning Microsoft SQL Server through an online-store case: from containers and queries to recovery models, backup chains, and restore operations.WritingBuilding CCTV Live Streaming and Playback on the Web with FFmpegR&D notes on taking Hikvision video from RTSP to the browser, including H.265 transcoding, MPEG-TS, WebSocket delivery, and time-based playback.
~/writing / hermes-workspace

Building a Dedicated Workspace for Hermes to Work Reliably

HermesAI agentsCLIautomationarchitecture

The problem is not only choosing a model

When I started using AI agents, the tempting approach was to let the agent work anywhere: run commands, edit files, send notifications, or call external APIs directly.

That is fine for experiments. As the workflow grew, the same problems appeared repeatedly:

  • conversational context became mixed with business logic;
  • important commands lived only in prompts;
  • results were hard to audit;
  • schedulers accumulated implementation details;
  • external mutations had no consistent approval and verification path.

So I created karina-workspace: a dedicated repository that acts as Hermes' execution plane.

The principle is simple: Hermes decides and communicates; the workspace executes operations through inspectable contracts.

Separating the conversational brain from execution

Hermes remains the main interface. I use it through Telegram and the CLI to understand requests, retain context, select skills, and route work.

The workspace does not replace Hermes. It exposes deterministic capabilities through a single CLI named kw.

Merender diagram...

With this boundary, Hermes does not need to know how to write a transaction, run a watcher, or deliver a notification. It calls a capability with a defined input, output, and lifecycle.

The repository as a work contract

The important parts of the workspace look like this:

src/kw/kernel/                    # shared lifecycle primitives
src/kw/capabilities/<name>/       # vertical domain slices
src/kw/adapters/                  # concrete adapters
src/kw/composition.py             # built-in handler wiring
config/jobs.toml                  # canonical schedules
config/projects.toml              # project registry
agent/skills/<name>/SKILL.md      # thin conversational routers
docs/                             # architecture and runbook

Several rules are intentionally strict:

  • kernel/ cannot import concrete capabilities or adapters.
  • Domain logic lives in vertical slices, not in a giant CLI module.
  • composition.py is the single place where concrete implementations are wired.
  • Credentials, tokens, chat IDs, and runtime data never enter the repository.
  • The Hermes profile contains identity, environment, scheduler launchers, and thin skills only.

These rules make the repository easier for both humans and agents to understand. When I add a domain, I know where to look for its capability, adapter, configuration, and tests.

Why route everything through a CLI?

I chose a CLI because it provides a simple contract that humans, Hermes, and schedulers can all use. Every command can be called programmatically, and results are available as JSON.

For example:

.venv/bin/kw doctor --json
.venv/bin/kw capability list --json
.venv/bin/kw job list --json

The output does not need to be prose. Another program can inspect ok, consume data, and react to error consistently.

The project registry also prevents Hermes from guessing repository locations:

.venv/bin/kw project list --json
.venv/bin/kw project show achmadya-dev/achmadya.dev --json
.venv/bin/kw project preflight achmadya-dev/achmadya.dev --json
.venv/bin/kw project sync achmadya-dev/achmadya.dev --json

preflight checks the path, remote, branch, dirty state, and divergence before coding begins. sync only fetches and fast-forward pulls when the state is safe. If the repository is dirty, detached, conflicted, or diverged, the command stops instead of forcing git reset --hard.

External writes need a lifecycle

Read-only operations are relatively straightforward. The dangerous part is mutation: creating an event, writing a transaction, opening an issue, committing, or pushing to a remote.

I use this lifecycle:

plan payload
  → persist frozen payload + hash
  → explicit approval
  → execute the same payload
  → read-back verification
  → persist result and status

The important property is that the payload is not rebuilt from conversational context after approval. The exact approved payload is what gets executed.

An API acknowledgement is not always enough evidence. When a resource can be read back, the adapter verifies it through a read operation. A success status therefore means more than “the request was sent”.

The scheduler only triggers jobs

It is easy to create many cron entries, each carrying its own business logic. I replaced that pattern with one generic Hermes scheduler that reads the workspace job manifest.

Hermes scheduler
      → kw job run <job-id> --json
      → JobRuntime
      → capability handler
      → persisted run result

Canonical schedules live in config/jobs.toml, while concrete handlers are registered in the composition root. The scheduler does not need to know how a watcher fetches data or evaluates results.

Several jobs use this path, including employment, freelance, investing, finance import, and server health watchers. The manual command is the same path used by the scheduler:

.venv/bin/kw job run employment.watch --json
.venv/bin/kw job latest employment.watch --json

This lets me test jobs outside Hermes and inspect the latest result without opening a conversation transcript.

Producers do not send Telegram directly

Notifications are split into two phases:

watcher
  → evaluate
  → enqueue_once into a durable outbox
  → delivery drain
  → Telegram

A producer only enqueues a delivery with an idempotency key. The delivery job handles formatting, retries, and dead-letter state.

This solves several practical problems:

  • a watcher does not fail completely because Telegram is unavailable;
  • the same message is not accidentally sent multiple times;
  • failed deliveries can be retried explicitly;
  • watcher results and delivery results can be audited separately.

Telegram routes and credentials live in a private, Git-ignored overlay. The repository stores the schema and route names, not secret values.

Skills stay thin and repository-owned

I do not put all logic into Hermes skills. A skill acts as a conversational router: it explains when to use a capability, which command is relevant, and which constraints apply.

The implementation remains in src/kw/. This matters because a skill is not a security boundary and is not a substitute for tests. It can help Hermes select a command, but the workspace still validates and enforces behavior.

The Hermes profile loads agent/skills/ as an external directory. Skills can therefore be reviewed, tested, and versioned with the workspace code. I avoid profile-local duplicates because a duplicate can take precedence and make behavior difficult to trace.

What this gives me

A dedicated workspace does not make an agent automatically intelligent. It makes the problem manageable:

  • conversational decisions are separated from execution;
  • commands have testable JSON contracts;
  • risky operations have approval and verification;
  • the scheduler triggers canonical jobs only;
  • notifications have an outbox and retry path;
  • project coding has a registry and preflight;
  • skills remain thin operational documentation instead of hidden business logic.

For me, this is a practical way to turn Hermes from a chatbot that can call tools into a work system with boundaries, state, and evidence.

The workspace is still evolving, but the central principle remains: the agent can be flexible when understanding the goal, while execution must have a deterministic and verifiable contract.

metadata
published
2026-08-04
topic
HermesAI agentsCLIautomationarchitecture
read time
5 min
Related