omh 0.6.0

Launch any coding harness, in a sandbox, with your setup already there.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# omh — oh-my-zsh for agentic coding

> Launch any coding harness, in a sandbox, with your setup already there.

[![verify](https://github.com/mindsers/ohmyharness/actions/workflows/ci.yml/badge.svg)](https://github.com/mindsers/ohmyharness/actions/workflows/ci.yml)
[![release](https://img.shields.io/github/v/release/mindsers/ohmyharness)](https://github.com/mindsers/ohmyharness/releases)
[![licence: MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](LICENSE)

```console
$ omh init         # detects your stack, decides, reports. no questions.
$ omh claude       # sandboxed, curated, your setup already inside
$ omh attach       # open that same session in your editor
$ omh graph        # browse your codebase as a graph
```

**Status: early.** `0.6.0`, one harness verified end to end. This release is the
work loop: reading a session's work, landing it in stages and staying current
with trunk, without typing `git`. Useful today if you want a sandboxed agent with
your config in it; not yet the finished distribution [the docs](docs/) describe —
[What isn't done](#what-isnt-done) is a real list, not a modesty ritual.

---

## The problem

A good agentic setup in 2026 is a pile of parts: a harness, rules, skills, MCP
servers, a sandbox, a code index, hooks, credentials. Each is a rabbit hole, so
most people stop at *"installed Claude Code, wrote a CLAUDE.md"* — not from
inability, but because assembling the rest is a research project nobody has
budget for.

The ecosystem's answer has been catalogues: [23,600+ skills and 12,700+ MCP
servers](https://claudemarketplaces.com/). That is a **problem statement**, not
an opinion. Nobody can evaluate 23,600 of anything.

omh is a **distribution**. Debian didn't write the kernel; oh-my-zsh didn't write
zsh — their genius was that installing them gave you a good system *immediately*.
The value is curation, integration and defaults, and the metric is **decisions
removed**, targeting zero.

## What you actually get

Running `omh claude` instead of `claude` buys five things:

**A sandbox that protects your repo, not just your host.** The agent works in a
git worktree on its own branch. Your checkout is never mounted. Review with
`omh s diff`, ship with `omh s commit` and `omh s push`, discard by deleting a
branch. You never go near the worktree directory itself.

The agent gets git too — its own repository, holding one commit and none of your
history, so `stash` and `reset --hard` are its to use. `omh s commit --keep`
brings its commits onto your branch with the messages it wrote — all of them in
order, or the ones you name: `omh s01 commit --keep 1,3-4`.

**Several sessions, from one place.** `omh s` is every session with its state,
how far behind trunk it has fallen, and the files two of them are both about to
change — the collision you would otherwise meet at merge time. `omh s01` is that
same row, scoped to one.

```console
$ omh s
  s01  omh/s01  stopped  2 uncommitted
  s02  omh/s02  stopped  2 uncommitted

  s01 and s02 both change shared.rs
```

`omh s01 sync` brings trunk in, merged on the host rather than inside the
sandbox. A conflict still lands in the worktree with its markers — labelled
`main` and `s01`, so which side is yours is obvious — and `omh s commit` refuses
to land a file that still holds them. An agent that commits nothing leaves a
timeline anyway: omh photographs the worktree at the end of every turn, and
`omh s01 log --turns` reads them back.

**Your setup, in any harness.** Rules, skills, MCP servers, commands, subagents
and hooks are declared once and rendered into whatever shape each harness reads.
Switch from Claude Code to opencode and everything follows.

**A code graph that is current and actually used.** Indexed per session,
refreshed after every turn (0.14s), with hooks that point the agent at it when
it is about to grep or read a whole file.

**Your editor attached to the same place.** `omh attach` opens VS Code, Zed,
Cursor or Neovim over SSH *into the sandbox* — one dependency tree, shared with
the agent, instead of a second one on your host that silently diverges.

## Install

Requires **Docker** and **git**.

```console
$ brew install mindsers/tap/omh
```

macOS and Linux, arm64 and x86_64. `brew upgrade` keeps it current afterwards,
which is the part the script below cannot do.

Without Homebrew:

```console
$ curl -fsSL https://raw.githubusercontent.com/mindsers/ohmyharness/main/install.sh | sh
```

Picks the build for your machine, checks it against the published
`SHA256SUMS`, runs it once to confirm it works here, and moves it into
`~/.local/bin`. A failed install never replaces a working `omh`. Read it first
if you would rather — it is [one file](install.sh). Re-run it to update.

From source, which needs Rust 1.85+:

```console
$ git clone https://github.com/mindsers/ohmyharness && cd ohmyharness
$ cargo build --release
$ cp target/release/omh ~/.local/bin/      # or add target/release to PATH
```

## Quick start

```console
$ cd ~/code/your-project
$ omh init
```

```
omh init — decided, asked nothing

  harnesses  3 (claude, omp, opencode)
  editors    4 (code, cursor, nvim, zed)
  harness    claude  (found on your host)
  stack      rust (from Cargo.toml) → test `cargo test`, format `cargo fmt`
  memory     seeded from 2 sources:
               README.md    Launch any coding harness, in a sandbox…
               Cargo.toml   stack: rust (test `cargo test`, format `cargo fmt`)
  image      omh/claude:a1240cb9 (built)
  graph      indexing in background → omh-cache-your-project

  base set  (2026.08)
    codegraph  structural queries instead of re-grepping the repo every task

  omh why <name>  what it costs, what was considered instead, how to remove it

not yet done: cost accounting.
next: omh claude
```

`init` **decides and reports** — it never asks. Every question is hassle the tool
promised to remove, and most answers are already lying around: manifests name the
stack, git log names what you work on, the README names the project.

Then log in once and go:

```console
$ omh auth claude personal    # runs the harness's own login, captures it
$ omh claude                  # sandboxed, logged in, configured
```

## Commands

```
omh init                          set this repo up
omh <harness> [args…]      claude · omp · opencode   ← bare name = run an agent
omh attach [editor]           a   open the session in your editor, over SSH
omh graph [--stop]                browse the code graph in a browser
omh auth <harness> [account]      log in once; repeat for several accounts
omh doctor [harness]          d   verify a harness really sees your profile
omh why <thing>                   who put this here, and on what grounds
omh ls                            harnesses, editors, sessions
omh sessions [log|diff|commit|push|sync|down|rm]  s   omh s, omh s01 diff
omh config [set|unset|edit|mcp] c you: your defaults and your catalogue
omh repo [enable|disable|set|unset] this checkout: what it uses and why
omh use|unuse <capability> <name> omh use skills tdd, omh use --all
```

Noun-verb groups with single-letter aliases. A bare name is always a **harness**;
editors live under `attach`, so `omh claude` and `omh attach zed` can't be
confused for each other.

## How it works

### Sessions

A session is a running container, a git worktree, and a branch — which many
harnesses take turns inhabiting.

```
       omh claude ──┐
       omh opencode ┼── exec ──┐
       omh attach ──┘  (ssh)   │
 ┌──────────────────────────────────────────────────────┐
 │ SESSION  omh-<repo>-s01          detached, long-lived │
 │  sshd 127.0.0.1 ──── your editor attaches here        │
 │  /work  ← worktree, the code you get back             │
 │  staged profile, read-only                            │
 │  graph cache ← volume keyed by REPO, not harness       │
 └──────────────────────────────────────────────────────┘
```

Harnesses run under `dtach`, so closing your terminal doesn't kill the agent —
`omh claude` again reattaches to the one you left running.

### One catalogue, and it is personal

```
~/.omh/
  rules/  skills/  commands/  subagents/  hooks/   the only place these live
  mcp.json
  settings.toml                                    your defaults
```

A repo holds configuration, and one kind of content:

```
<repo>/.omh/
  settings.toml        committed: settings, and which of omh's features are on
  settings.local.toml  gitignored: your overrides, and the secrets the other must not hold
  memory.toml          committed: how the note store keys and expires
  hooks/               committed: hooks that only make sense in this repo
<repo>/AGENTS.md       the project's own rules — tracked, and actually read
```

A project cannot declare a skill, an MCP server, a command or a subagent; it
**names** ones from your catalogue. Hooks are the exception, being the one
capability whose scope is genuinely the repo — `cargo test` here, `pnpm test`
next door, one name and two bodies.

Naming them is one table, and one mechanism — an allowlist, so removing
something is deleting its name:

```toml
# <repo>/.omh/settings.toml
[use]
rules  = ["tdd", "commit-style"]   # for rules, the list is the order
skills = ["review-diff"]
mcp    = ["*"]                     # keep following the catalogue as it grows
```

Absent means everything, so upgrading changes nothing and a new checkout is
useful before it's configured. Two scopes, so two commands: `omh config` means
you, `omh repo` means this checkout, and they want opposite defaults —
[Configuration](docs/configuration.md#two-scopes-two-commands) has the rest.

### The base set is data too, and it has to justify itself

omh's opinion lives in a versioned file, not in the binary — `init` seeds from
it and `omh why` explains from it, so the two can't disagree about what is
installed or why:

```console
$ omh why codegraph
codegraph — omh's choice, in the base set since 2026.06

  because     structural queries instead of re-grepping the repo every task
  costs       0.46s to index this repo, cold   measured 2026-08-06
              index_repository --mode fast, 821 nodes / 3813 edges, in the sandbox
  instead of  gitnexus            PolyForm-Noncommercial licence
  remove      omh config mcp rm codegraph

  answered from ~/.omh/base/2026.08.toml · 2026.08
```

**Cost is measured; benefit is argued.** Those are different kinds of claim and
the output never blurs them — every number carries the date it was taken and the
method, while `because` is a judgement you're free to reject.

The four fields aren't a convention, they're a **test**: an entry that can't say
what it costs, what it buys, what was considered instead and how to remove it
fails the build. `omh why` answers for things omh *rejected* too, so a candidate
turned down over its licence isn't re-litigated every time someone rediscovers
it — and it offers no rationale at all for something *you* added, because it has
none. Telling those apart is the entire point.

### Adapters are data

Adding a harness is a TOML file, not a code change:

```toml
name    = "claude"
bin     = "claude"
install = "npm install -g @anthropic-ai/claude-code"

[capabilities.rules]
path   = "/work/CLAUDE.md"
also   = ["/work/AGENTS.md"]
render = "concat"

[capabilities.mcp]
path   = "/work/.mcp.json"
render = "mcp-json"
verify = "claude mcp list"   # and how omh knows it worked
ready  = "Connected"
```

**An absent key means the harness cannot do that thing.** Degradation is a
missing map entry rather than special-case logic, and it is announced once:

```console
$ omh opencode
omh: opencode on omh/s01 — dropped hooks: graph-first (no `search` tool),
     graph-orient (no `session-start` moment),
     graph-read (no way to inject text before a tool runs)
```

Editors work the same way — `~/.omh/editors/zed.toml` is four lines.

### The code graph

Every session is indexed into a graph ([codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp),
MIT, a static binary with no runtime or database). Four hooks make it something
the agent uses rather than something merely installed:

| Hook | When | Cost | Buys |
|---|---|---|---|
| `graph-orient` | session start | 2.3 KB | modules, layers, boundaries, entry points |
| `graph-first` | before Grep/Glob | 243 B | structural questions in one call |
| `graph-read` | before Read | 0 unless it speaks | **1,511 bytes** for one symbol, not the whole module |
| `graph-refresh` | end of turn | 0.14s | a graph describing the code as it is *now* |

They are nudges, never walls: grep is right for a literal string, and a hook that
blocks correct work gets disabled. `graph-read` stays silent unless a symbol
lookup would genuinely be cheaper.

```console
$ omh graph
omh: graph at http://127.0.0.1:56286
  every session's graph for this repo, in one place
```

### Credentials

```console
$ omh auth claude personal
$ omh auth claude work
$ omh -a work claude          # or, per project: omh repo set account work
```

Accounts are per harness, and which one a session uses is a project-level
setting — because that is how it actually varies: this repo is work, that one is
personal. Ambiguity is an error, never a guess: two identities and no stated
preference stops the launch rather than sending work traffic through a personal
account.

### Files your worktree needs

A worktree holds only tracked files — no `.env`, no certs, so the agent lands
somewhere that cannot run your app.

```toml
carry_in = [".env.local", "certs/"]
```

An explicit allowlist, because **this is the only path by which a secret reaches
the agent**. A listed path that doesn't exist is reported, not skipped.

It is for files git does **not** track. A tracked path is already in the
worktree, so listing one would replace the branch's copy with whatever your
checkout holds right now — usually an uncommitted edit, on the one path a secret
travels. omh says so at launch and does not copy it.

## Verify it yourself

`omh doctor` is the only thing that can prove an adapter is right. It launches
the real image with the real mounts and inspects the paths the harness actually
reads:

```console
$ omh doctor
omh doctor: claude (in omh/claude:2133265d, account personal)

  ✓ rules      /work/CLAUDE.md
  ✓ skills     /home/agent/.claude/skills
  ✓ mcp        /home/agent/.mcp.json
  ✓ commands   /home/agent/.claude/commands
  ✓ hooks      /home/agent/.claude/settings.json
  ✓ token      /home/agent/.claude/.credentials.json (atomic write)

  all 6 checks passed — claude's adapter paths are verified
```

A green unit suite proves omh mounts a path faithfully; it proves nothing about
whether anything reads it. That gap is what `doctor` closes.

## What isn't done

| | |
|---|---|
| **Memory** | mostly [built]docs/commands.md#omh-memory- — the store, its schemas, retrieval, the team layer and `remember` / `recall` as MCP tools all ship. What remains is hub pages, whose lint needs a threshold the design refuses to let anyone guess. |
| **Cost accounting** | each base-set entry should report what it injects, in bytes, so the set has a reason to shrink. Not a benchmark — [here's why]docs/design/trust.md#measure-the-cost-argue-the-benefit. |
| **`omh eject`** | a credible exit: write out the raw per-harness config and step aside. |
| **`sbx` backend** | the trait exists and declares capabilities; the spike that resolves file-mounts, guest paths and IDE attach has not run. Docker is the only verified runtime. |
| **Egress allowlist** | designed, not wired. |
| **Other harnesses** | `opencode` and `omp` pass `doctor`, but only `claude` has been driven for real work. |

Known rough edges: the graph store is shared across sessions of one repo, so an
agent can query another session's graph (mitigated, not prevented);
`.claude.json` is a file mount that cannot be atomically replaced; `omh s rm`
drops a session branch only when it has no commits.

## Contributing

```console
$ cargo test
$ ./scripts/smoke.sh    # end-to-end walkthrough in a throwaway repo
$ omh doctor            # the only thing that verifies an adapter
```

Two rules do most of the work, and [`CONTRIBUTING.md`](.github/CONTRIBUTING.md)
has the rest with the invariant list. **[TDD,
always](.github/CONTRIBUTING.md#tdd-always)** — a green suite is not evidence on
its own, so a bug fix's test goes red before the fix lands. And **adapters
assert facts about external software**: almost every bug this project has
shipped lived at that boundary and none was catchable in-process, so if you
change an adapter, run `omh doctor`.

## Documentation

Full docs live in [`docs/`](docs/README.md), which indexes them.

Start with [Getting started](docs/getting-started.md) — install to first session.
[Commands](docs/commands.md) is the whole surface and what each one prints;
[Configuration](docs/configuration.md), [Sessions](docs/sessions.md),
[Accounts](docs/accounts.md) and [Editors](docs/editors.md) cover the parts in
depth.

Read the [design pages](docs/README.md#understanding-omh) before changing
architecture — most record something that was tried and cost something, and
[Risks](docs/design/risks.md) states plainly what is still weak.

## Licence

MIT — see [`LICENSE`](LICENSE).

One dependency, `option-ext` (transitive via `dirs`), is MPL-2.0; everything else
in the tree is Apache-2.0, MIT, BSD, ISC or Unicode-3.0.