My AI Coding Setup Is Mostly Git and Markdown

My AI Coding Setup Is Mostly Git and Markdown

I keep swapping agents, dashboards, memory systems, and orchestration layers. What survives is unglamorous: terminals, worktrees, tests, and Markdown.

My AI coding setup sounds complicated when I describe it.

Two agents running side by side. Several tasks in parallel. Custom skills, plugins, MCP servers, a local Kanban board, Git worktrees, and an Obsidian vault that both agents can read and update.

None of it arrived as a plan. I am restless about tooling in a way that is probably a character flaw: I switch agents, adopt dashboards and abandon them, rebuild my memory layer, start orchestration frameworks and rip them out again. What is left is not a stack I chose. It is the residue of everything I tried and stopped using.

Which is why the actual interface is one terminal window.

Inside it, one tab might hold Claude working through an implementation, another Codex reviewing a diff, another a test process, another something unrelated in the same repository. I move between them with a keystroke. That is the whole thing.

I looked at my live session while writing this. It had outlasted every task that started inside it, and it had grown entirely by accretion. Several tabs still carried default names. One session held nothing but an idle shell and a session manager I had forgotten to close. Work-project panes sat next to personal ones with no separation except distance.

It was not a pristine productivity screenshot. It was a desk.

That is a more honest picture of how I work than any architecture diagram — and what has survived all the churn underneath it is embarrassingly old technology:

Terminals separate conversations. Git worktrees separate files. Markdown stores intent and memory. Tests decide whether the work is real. I decide what gets merged.

Every one of those predates the agents by a decade or more. Everything newer has to keep justifying its existence, and most of it eventually fails to.

I Do Not Have a Favorite Agent

People ask whether Codex or Claude Code is better. I use both, so I am apparently expected to maintain a league table.

I do not, because any ranking I published would rot before the release notes did.

I have flipped which agent I reach for first more times than I would like to admit, usually after one of them handled something the other had made a mess of. Model quality moves too fast for loyalty, and the answer changes with the task. A model that is excellent at tracing a backend bug can be strangely careless with a UI state transition. One will produce the cleaner first implementation; another will be much better at finding the assumption hidden inside it.

What matters to me is that they are independent.

If Claude writes a change, I can ask Codex to review it without inheriting the conversation that produced it. The reviewer sees the request, the repository, and the diff — not the long chain of small decisions that made the author emotionally attached to its own solution. The reverse works too.

Review is only one use of that independence. For a high-stakes design or a difficult diagnosis, I give the same problem to a reasoning-heavy Claude agent and to Codex in parallel. Neither sees the other’s answer. Then I compare assumptions, evidence, and proposed checks before choosing a direction. This blind pass is far more useful than asking one model whether it agrees with an answer already sitting in its own context.

For larger changes I make the separation explicit: one agent shapes the problem and writes a bounded specification, another implements it, and the first reviews the result against the spec it authored. I built a small codex-pair plugin around that pattern and published it in my agent-toolkit repository. In its strictest mode Codex is read-only — it can scope a slice, define allowed files and acceptance criteria, and later judge the diff, but Claude keeps the keyboard.

The point is not that Codex is the manager and Claude is the worker. Those roles swap depending on the day. The point is that authorship and judgment are different jobs.

A second model is useful for the same reason a second human reviewer is useful: not because it is guaranteed to be smarter, but because it is likely to be wrong in a different way.

One Terminal Is My Control Plane

I have installed most of the desktop dashboards for agents. Some are genuinely good, and I have used more than one of them happily for a week or two. Every time, I have drifted back to the terminal, because it is the only surface where the state of the work is impossible to hide from me.

Kitty is the outer shell — fast, scriptable, out of the way. Zellij turns that one window into a workspace of persistent sessions, tabs, and panes. My shell config auto-starts Zellij only when it detects it is running inside Kitty, which makes Kitty an entry point: I open a terminal and land back in the workspace, while SSH sessions and one-off shells stay plain.

I keep Zellij locked by default so its shortcuts do not collide with the agents. The bindings I use constantly are one modifier plus a digit or a direction; everything heavier — resizing, moving panes, session management — stays behind a mode I have to enter deliberately. Muscle memory for the cheap operations, friction for the expensive ones.

This sounds like a small ergonomic detail, but it changes how parallel work feels. I do not have floating terminal windows, IDE sidebars, and a browser dashboard competing for attention. I have one spatial map that outlives any individual agent conversation:

Kitty
└── one long-lived session
    ├── a tab per thing I am actually thinking about
    │   ├── an agent writing code
    │   ├── an agent reviewing it
    │   ├── whatever process proves it works
    │   └── a plain shell for me
    └── tabs from earlier work, still warm

Sometimes a tab is a project. Sometimes it is a theme. Sometimes it is just where a task happened to start. I do not force a taxonomy onto it, because the session is working memory, not a database.

The tabs are not the isolation boundary, though. They are the human interface. Git provides the real boundary when agents share a repository.

Parallel Agents Need Physical Separation

Running two agents in two panes against the same checkout is not orchestration. It is a race condition with a nice terminal UI.

If two tasks can write to the same repository at the same time, each gets its own Git worktree:

git worktree add -b agent/auth-cleanup ../project-auth
git worktree add -b agent/missing-tests ../project-tests

Both directories share the same Git history, but each has its own branch, working files, and HEAD. One agent can refactor authentication while another adds tests, and neither watches files change underneath it.

iOne writable owner per worktree

A read-only reviewer can inspect anything, anywhere. A second writer gets a second worktree — no exceptions, because the failure mode is silent. Two agents editing one checkout do not crash; they produce a diff that looks intentional and is not.

That rule is simple enough to follow by hand, but I also made worktrees a first-class workspace type in my local agent board. A task can run in the existing repository for a small supervised change, in a dedicated worktree for parallel implementation, or in an empty scratch directory when the repository should be out of scope entirely. The launcher enforces the distinction: if worktree creation fails, the task stops. It never falls back to editing my main checkout.

The habit is real, not aspirational — when I went looking, I found linked worktrees scattered across active repositories. Some under a hand-managed .worktrees/ directory, some temporary review checkouts, some created automatically by an agent and never reclaimed.

That also exposes the cost enthusiastic worktree tutorials skip: cleanup. Old review trees, stale branches, copied environment files, and duplicated dependency directories pile up unnoticed. Isolation reduces collisions, but it adds a lifecycle to maintain.

And worktrees only solve file interference. Parallel agents can still fight over a database, a development port, generated artifacts, a shared cache, or two migrations that are individually valid and mutually incompatible. Tasks have to be truly separable, not just separately checked out.

So I do not maximize the number of running agents. I maximize the number of independent feedback loops I can still understand.

Simon Willison hit the same wall when he started using parallel coding agents: generating more code is easy, reviewing it is the bottleneck. That matches my experience exactly. Three agents can save real time. Ten agents produce a queue of diffs nobody has understood.

The Uncomfortable Permissions Detail

In repositories I control and understand, I run both agents in permissive modes with most per-command confirmation removed. It is fast, and it is the part of my setup I would least recommend copying blindly.

!This is a convenience, not a security model

Removing approval prompts only works because the surrounding boundaries are explicit: a known working directory, a dedicated worktree when there are concurrent writers, read-only roles for research and review, visible Git diffs, verification before acceptance, and human control over anything external or destructive.

A worktree protects one checkout from another writer. It does not protect my credentials, my network, or the rest of my filesystem. Those are different boundaries and deserve to be treated as different boundaries — which is also why none of this is an argument for pointing an agent at a repository you do not know.

My Wiki Is Not a Second Brain

Every coding agent has a memory problem, and I have rebuilt my answer to it more than once.

The obvious fix is the tempting one: save everything. Keep the full transcript, index every command, embed every file, retrieve whatever looks similar next time.

That trades one problem for a worse one. Now the agent remembers too much.

My Obsidian vault is closer to a second memoization layer than a second brain. It stores the result of expensive thinking so the next session does not have to derive it again.

The shape comes from Andrej Karpathy’s LLM Wiki — keep raw sources separate, maintain a structured Markdown knowledge layer, cross-link it, lint it, let good answers file themselves back in. I also borrowed the spirit of autoresearch: the important program is usually a small Markdown document defining the loop, the constraints, and the success condition, not a giant agent framework.

My version is more conservative. Markdown stays the canonical, human-readable source. Raw imported material is immutable and never treated as instruction. Decisions, facts, tasks, and one-off episodes are different kinds of memory with different lifespans. A short hot cache loads at session start; deeper pages are retrieved only when the task actually needs them. Human edits survive. Secrets and raw transcripts never enter. And a lesson from one run does not become a permanent rule until it has survived being wrong at least once.

Both agents read the same vault through a small plugin, and the startup context stays bounded — recent project state and a few open tasks, not the entire history of my life. When a session produces something durable, the agent writes the smallest useful note and links it. When the work is routine, it writes nothing.

That distinction matters, because memory is not free. It consumes context. Stale facts look exactly as authoritative as fresh ones. Retrieved text can carry hostile or accidental instructions. Every stored detail becomes something a future agent may have to disambiguate.

I hit that failure mode while revising this article. A recent wiki note said the Hermes gateway was still running during my migration. A live status check showed the process had stopped and only a stale state file remained. The note was useful because it told me what to verify. It was not proof of current state.

The best memory system I have found is not the one that recalls the most. It is the one that makes forgetting intentional.

Context Is a Budget

I used to think a good agent setup needed a comprehensive instruction file. Every convention, command, exception, architecture rule, and preference in AGENTS.md or CLAUDE.md, ready before the model starts.

That fails surprisingly fast. A giant instruction file competes with the task and the code for context. Old rules outlive the repository. Hard constraints become indistinguishable from mild preferences. The document turns into a manual nobody — human or model — keeps current.

What I use now is progressive disclosure:

AGENTS.md / CLAUDE.md
    └── short map: boundaries, commands, verification, where to look
        ├── architecture docs
        ├── decisions
        ├── active plan
        ├── focused skills
        └── external tools only when needed

OpenAI’s harness engineering write-up lands on nearly the same lesson: the root instruction file is a map into structured sources of truth, not a thousand-page manual. Anthropic’s Claude Code guidance arrives from another direction — keep persistent guidance concise, give the agent executable verification, move optional workflows into skills.

The shared idea is bigger than either product: context is a budget, and instructions are only one line item on it.

The other line item is tool output. Claude has a pre-tool hook that routes noisy commands through a small proxy, which filters and summarizes them before anything reaches the model. Test runs come back as failures instead of pages of passing cases. Git commands lose their boilerplate. Directory listings, diffs, JSON, build logs — compacted down to the parts an agent can act on. Across thousands of commands, that has removed roughly a fifth of the tokens they would otherwise have cost me. The precise figure matters less than the direction: I do not want to pay a reasoning model to read four hundred passing tests so it can find the one failure at the bottom.

I keep the remaining budget visible too. My status line reports, at a glance, which model is running and how hard it is thinking, where it is and on what branch, how much is uncommitted, how full the context window is, and what the session has cost so far. It refreshes constantly. It is a tiny form of observability, and it exists so I notice a session going expensive, context-heavy, or detached from the branch I assumed it was on — before the diff surprises me.

The same principle governs subagents: the main agent should consume conclusions, not raw exploration. A test runner returns the failing signal. A research agent returns a bounded synthesis. The main context stays the place where requirements and decisions live.

Guidelines Are Brakes, Not Personality

I keep a small karpathy-guidelines skill installed, and it comes down to four rules I ask both agents to work by:

  1. Think before coding, and surface assumptions instead of guessing silently.
  2. Prefer the minimum solution over speculative flexibility.
  3. Make surgical changes and leave unrelated code alone.
  4. Turn the request into a goal with a check that can prove success.

This is not a role-play prompt telling a model to be a “10x senior architect.” It is a set of brakes for the four specific mistakes coding agents make when nobody constrains them: guessing quietly, inventing abstractions, tidying code that was not part of the task, and declaring victory without running anything.

Those rules are also why my tooling looks boring. Package managers are chosen explicitly rather than inferred. rg, jq, Git, and small scripts beat opaque automation, because I can read them. A non-trivial change starts with an issue that states the goal, context, approach, acceptance criteria, and verification plan — written before code, because that is the artifact a fresh session can resume from.

Which leaves three places where state lives, each with one job: the issue holds the team-visible plan and progress, the repository holds code and executable proof, and the wiki holds decisions and knowledge that should outlast the issue.

Sessions are disposable. Those three are not.

Skills, Plugins, and MCP — In That Order

It is very easy to turn an agent setup into a hobby of maintaining the agent setup. I like building tools, so I am especially vulnerable to this — and I have the uninstalled plugins to prove it.

What stopped the cycle was ordering the options by cost instead of by novelty. Reach for the smallest extension surface that solves the problem, and only move up when it visibly fails.

That does not mean my machine has a minimal plugin count — Claude carries noticeably more installed capability than Codex does, and the asymmetry is deliberate. I do not try to make both products look identical. A capability belongs wherever it removes a real loop.

More importantly, installed is not the same as active. Focused skills are discovered by description and loaded when a task matches. The alternative — copying every possible workflow into every startup prompt — would be minimal in file count and ruinous in context cost.

First, plain instructions. A workflow used once stays in the prompt or a Markdown plan. A repository with a stable build command or an unusual safety constraint puts that in its agent instructions. No framework required.

Then a skill, once I catch myself repeating the same workflow or the same corrections. A skill is mostly a focused SKILL.md: when this applies, what it expects, which steps to follow, where to stop, what evidence to return. References and deterministic scripts sit beside it and load only when needed. Mine save and retrieve durable memory, run a diff past an independent model, act as product manager for the local agent board. Each does one recognizable job. A skill named “do everything the way David likes” would be another monolithic instruction file with better branding.

Then a plugin, when a skill has to be installed consistently in both Codex and Claude Code, or when it needs hooks, scripts, and manifests around it. I keep one local marketplace with one hand-edited catalog; the product-specific manifests are generated and drift-checked, so the two agents cannot silently load different versions of the same workflow. That is infrastructure, and it is justified only because I genuinely use the same tools on both sides.

Finally MCP, when an agent needs live data or controlled actions outside the repository — a private service, a browser, an issue tracker, a database, an API that cannot be represented honestly as files and shell commands. Not merely because an integration exists.

Every external tool expands context, permissions, failure modes, and prompt-injection surface. The current Codex guidance recommends starting with one or two tools that remove a real manual loop instead of wiring in everything, which is also the shortest summary of my approach. When a CLI already exposes the operation clearly, I prefer the CLI — easy to inspect, script, sandbox, and reproduce without teaching the agent a new protocol.

What Hermes Taught Me

Hermes Agent is the best counterexample to my minimalism, and I mean that as praise and criticism at once.

Hermes arrives with almost everything:

  • persistent memory and external memory providers;
  • skills and MCP integration;
  • terminal, file, web, browser, and computer tools;
  • subagent delegation;
  • scheduled tasks;
  • voice, vision, image generation, and messaging;
  • provider routing;
  • hooks, code execution, batch processing, and a Kanban dispatcher.

At the time of writing, its tool reference lists roughly 81 built-in or dynamically registered tools. If you want one agent system with batteries, charger, solar panel, and emergency generator included, Hermes is impressive.

It was also too much system for the way I work. The Kanban feature I liked lived inside a much larger product with its own gateway, provider stack, profiles, memory layout, sessions, sandboxes, and lifecycle. The feature was powerful — but working out which layer owned a failure became a job of its own.

So I did not throw Hermes away and start from a blank page. I treated it as research, and kept the ideas that had proven themselves: roles with explicit capabilities, atomic claims and heartbeats, stale-worker recovery, failure breakers, task decomposition with dependency edges, resumable sessions, worktree isolation.

Then I rebuilt only that slice as a standalone local tool. My agent-board uses Markdown cards for visible state and SQLite for leases, run history, and budget admission. It has a CLI, a small local dashboard, and workers that shell out to the Codex or Claude CLI already installed on my machine. It does not own my model provider, my memory system, or my terminal.

This is new infrastructure, not a mature platform I have trusted for years. The live board is mostly implementation, review, and QA cards — a fair number of which were used to build and audit the board itself. Not every old Hermes board has moved across, though the Hermes gateway itself is no longer running.

That transitional state is the useful evidence. I am not swapping one complete system for another complete system in a weekend. I am moving proven responsibilities one at a time and leaving the old path available until the smaller one has earned trust.

Hermes gave me everything from the beginning. Its greatest strength and its greatest weakness were the same thing.

The Loop I Actually Use

Most days, the workflow is far less dramatic than the stack implies.

Put the outcome somewhere durable. For anything non-trivial I create or refine a tracker issue before code exists. It states what should be true when the work is done, plus context, intended approach, constraints, acceptance criteria, and how it will be verified. The test is whether a fresh session could pick up the work after this one disappears.

Decide whether it can run in parallel. Research, test coverage, a focused backend change, and a read-only review are usually separable. Two changes to the same subsystem usually are not. If two writers can run safely, each gets a worktree.

Give each agent one job and one kind of output. “Trace this bug and report the cause” or “implement this bounded slice and run these checks” — never “improve the project.” Reasoning-heavy analysis goes to a stronger reasoning model, mechanical edits to a faster one, large test output to a runner that returns only failures, and anything where disagreement is valuable to a different model family. Dependent tasks hand each other a short summary, not a full transcript.

Keep verification beside implementation. The implementing agent has to be able to see whether it succeeded: a test, a build, a lint rule, a browser interaction, a reproducible measurement, a concrete diff. An agent that cannot observe the result is guessing with confidence.

Review from a fresh context. I read the diff myself, and for meaningful changes I ask the other model for an adversarial pass with the original request and acceptance criteria in hand. For high-stakes calls the independent pass happens earlier, before implementation: both models analyze the problem blind to each other. Agreement raises my confidence; disagreement tells me exactly where judgment is still required.

Save only what should compound. The code and Git history already remember the implementation. The wiki gets the decision, the surprising fact, the unresolved task, the reusable lesson.

Then the terminal tab can disappear.

More Agents Are Not the Goal

The language around parallel agents gets ridiculous quickly. A handful of CLI processes becomes a “software organization.” A routing prompt becomes “management.” A Markdown role file becomes a “soul.”

Fun metaphors. They also hide the actual constraint, which is that agents do not remove coordination cost. They move it — onto the one resource that has not gotten cheaper: my attention. Writing tasks that can be judged. Choosing boundaries that prevent interference. Noticing when two correct changes conflict. Reviewing more output than one person could have produced. Deciding which lessons deserve to become permanent guidance.

My setup works because it tries to make those costs visible instead of comfortable. Worktrees show ownership. Cards show state. Tests show evidence. Git shows changes. The wiki shows what is meant to survive. If a new tool makes any of those less visible, it has to be extraordinary to stay.

Codex will change. Claude Code will change. The model I reach for when debugging six months from now probably is not the one I reach for today, because it has never stayed the same for long.

So I have stopped trying to pick winners at that layer and started building underneath it. If I lost every tool named in this article tomorrow, this is what I would rebuild first. One terminal workspace I can navigate without thinking. One coherent task per session. One writable owner per worktree. Short instructions pointing to deeper sources. Executable definitions of done. Independent review. A local, inspectable memory that saves conclusions rather than conversations.

The interesting part of using coding agents is not how many you can launch.

It is how little machinery you need before their work becomes understandable, verifiable, and worth keeping.