oh-my-harness 0.6.0 β 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -228
- package/dist/catalog/blocks/command-guard.js +2 -2
- package/dist/catalog/blocks/commit-test-gate.js +2 -2
- package/dist/catalog/blocks/commit-typecheck-gate.js +2 -2
- package/dist/catalog/blocks/format-on-save.js +3 -3
- package/dist/catalog/blocks/lint-on-save.js +3 -3
- package/dist/catalog/blocks/lockfile-guard.js +1 -1
- package/dist/catalog/blocks/path-guard.js +1 -1
- package/dist/catalog/blocks/secret-file-guard.js +1 -1
- package/dist/catalog/blocks/tdd-guard.js +11 -6
- package/dist/catalog/template-engine.js +5 -0
- package/dist/cli/command-checker.js +6 -2
- package/dist/cli/harness-tester.js +20 -11
- package/dist/cli/stats/components/Blocks.js +1 -1
- package/dist/cli/stats/components/Overview.js +1 -1
- package/dist/cli/stats/data.d.ts +1 -0
- package/dist/cli/stats/data.js +24 -3
- package/dist/cli/tool-checker.js +7 -1
- package/dist/cli/tui/init-flow.d.ts +1 -0
- package/dist/cli/tui/init-flow.js +134 -20
- package/dist/core/harness-converter-v2.js +2 -4
- package/dist/detector/detectors/node.js +38 -0
- package/dist/detector/detectors/python.js +56 -8
- package/dist/nl/parse-intent.js +38 -2
- package/dist/nl/prompt-templates.js +5 -1
- package/package.json +1 -1
- package/presets/actix/preset.yaml +55 -0
- package/presets/cargo/preset.yaml +11 -0
- package/presets/cpp/preset.yaml +50 -0
- package/presets/csharp/preset.yaml +42 -0
- package/presets/dart/preset.yaml +43 -0
- package/presets/django/preset.yaml +72 -0
- package/presets/elixir/preset.yaml +45 -0
- package/presets/express/preset.yaml +61 -0
- package/presets/fastapi/preset.yaml +1 -1
- package/presets/flask/preset.yaml +65 -0
- package/presets/flutter/preset.yaml +64 -0
- package/presets/gin/preset.yaml +57 -0
- package/presets/go/preset.yaml +43 -0
- package/presets/gradle/preset.yaml +13 -0
- package/presets/java/preset.yaml +48 -0
- package/presets/javascript/preset.yaml +44 -0
- package/presets/laravel/preset.yaml +70 -0
- package/presets/maven/preset.yaml +13 -0
- package/presets/nextjs/preset.yaml +1 -1
- package/presets/nextjs-fastapi/preset.yaml +1 -1
- package/presets/npm/preset.yaml +12 -0
- package/presets/phoenix/preset.yaml +64 -0
- package/presets/php/preset.yaml +46 -0
- package/presets/pip/preset.yaml +13 -0
- package/presets/pipenv/preset.yaml +12 -0
- package/presets/pnpm/preset.yaml +12 -0
- package/presets/python/preset.yaml +48 -0
- package/presets/rails/preset.yaml +65 -0
- package/presets/react/preset.yaml +61 -0
- package/presets/ruby/preset.yaml +45 -0
- package/presets/rust/preset.yaml +42 -0
- package/presets/scala/preset.yaml +44 -0
- package/presets/springboot/preset.yaml +61 -0
- package/presets/swift/preset.yaml +44 -0
- package/presets/typescript/preset.yaml +46 -0
- package/presets/uv/preset.yaml +12 -0
- package/presets/vue/preset.yaml +62 -0
- package/presets/yarn/preset.yaml +12 -0
- package/presets/zig/preset.yaml +41 -0
package/README.md
CHANGED
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
## The Problem
|
|
20
|
+
## π€ The Problem
|
|
21
21
|
|
|
22
22
|
Every AI code agent needs configuration files. Claude Code needs `CLAUDE.md` + hooks. Cursor needs `.cursorrules`. Codex needs `AGENTS.md`. You end up:
|
|
23
23
|
|
|
24
|
-
- Copy-pasting config files between projects
|
|
25
|
-
- Forgetting to set up TDD enforcement hooks
|
|
26
|
-
- Agents committing code without running tests
|
|
27
|
-
- Inconsistent behavior across projects
|
|
24
|
+
- π Copy-pasting config files between projects
|
|
25
|
+
- π Forgetting to set up TDD enforcement hooks
|
|
26
|
+
- π₯ Agents committing code without running tests
|
|
27
|
+
- π² Inconsistent behavior across projects
|
|
28
28
|
|
|
29
|
-
## The Solution
|
|
29
|
+
## β¨ The Solution
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
oh-my-harness init "React + FastAPI fullstack, TDD enforced, lint on save"
|
|
@@ -35,14 +35,17 @@ oh-my-harness init "React + FastAPI fullstack, TDD enforced, lint on save"
|
|
|
35
35
|
That's it. oh-my-harness generates **enforced guardrails** β not just instructions, but hooks that actually **block** bad behavior:
|
|
36
36
|
|
|
37
37
|
- β Commit without tests passing? **Blocked.**
|
|
38
|
+
- β Edit source without updating tests first? **Blocked.** _(TDD Guard)_
|
|
38
39
|
- β Write to `node_modules/` or `.next/`? **Blocked.**
|
|
39
40
|
- β Run `rm -rf /`? **Blocked.**
|
|
41
|
+
- β Commit on a merged branch? **Blocked.**
|
|
40
42
|
- β
Auto-lint on every file save? **Done.**
|
|
41
|
-
- β
|
|
43
|
+
- β
Auto-create PR after push? **Done.**
|
|
44
|
+
- π Track all hook events for analytics? **Done.**
|
|
42
45
|
|
|
43
46
|
---
|
|
44
47
|
|
|
45
|
-
## Quick Start
|
|
48
|
+
## π Quick Start
|
|
46
49
|
|
|
47
50
|
```bash
|
|
48
51
|
# Zero-install: run directly with npx
|
|
@@ -55,29 +58,37 @@ oh-my-harness init --preset nextjs fastapi
|
|
|
55
58
|
# Short alias works too
|
|
56
59
|
omh init "React app with TDD"
|
|
57
60
|
omh catalog list
|
|
61
|
+
omh test # Dry-run verify your harness
|
|
62
|
+
omh stats # TUI analytics dashboard
|
|
58
63
|
```
|
|
59
64
|
|
|
60
|
-
### What Gets Generated
|
|
65
|
+
### π What Gets Generated
|
|
61
66
|
|
|
62
|
-
```
|
|
67
|
+
```text
|
|
63
68
|
your-project/
|
|
64
|
-
βββ CLAUDE.md # TDD rules, coding standards
|
|
65
|
-
βββ harness.yaml # Your harness config (
|
|
69
|
+
βββ CLAUDE.md # TDD rules, coding standards
|
|
70
|
+
βββ harness.yaml # Your harness config (source of truth)
|
|
66
71
|
βββ .claude/
|
|
67
|
-
βββ settings.json # Hook configs, permissions
|
|
72
|
+
βββ settings.json # Hook configs, permissions
|
|
68
73
|
βββ hooks/
|
|
69
|
-
β βββ
|
|
70
|
-
β βββ
|
|
71
|
-
β βββ
|
|
72
|
-
β
|
|
74
|
+
β βββ catalog-branch-guard.sh # Blocks commits on merged branches
|
|
75
|
+
β βββ catalog-tdd-guard.sh # Enforces test-first workflow
|
|
76
|
+
β βββ catalog-commit-test-gate.sh # Tests must pass before commit
|
|
77
|
+
β βββ catalog-path-guard.sh # Protects build outputs
|
|
78
|
+
β βββ catalog-command-guard.sh # Blocks dangerous commands
|
|
79
|
+
β βββ catalog-lint-on-save.sh # Auto-lint on save
|
|
80
|
+
β βββ catalog-auto-pr.sh # Auto-create PR after push
|
|
81
|
+
β βββ .state/
|
|
82
|
+
β βββ events.jsonl # Hook event log (for analytics)
|
|
83
|
+
β βββ edit-history.json # TDD guard state
|
|
73
84
|
βββ oh-my-harness.json # Active preset tracking
|
|
74
85
|
```
|
|
75
86
|
|
|
76
87
|
---
|
|
77
88
|
|
|
78
|
-
## How It Works
|
|
89
|
+
## βοΈ How It Works
|
|
79
90
|
|
|
80
|
-
```
|
|
91
|
+
```text
|
|
81
92
|
βββββββββββββββββββββββ
|
|
82
93
|
"React + FastAPI β β
|
|
83
94
|
TDD enforced" β claude -p (NL) β
|
|
@@ -85,14 +96,17 @@ your-project/
|
|
|
85
96
|
β β
|
|
86
97
|
ββββββββββ¬βββββββββββββ
|
|
87
98
|
β
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
β
|
|
91
|
-
|
|
99
|
+
ββββββββββΌβββββββββββββ
|
|
100
|
+
β Project Detector β β Auto-detects language,
|
|
101
|
+
β (14 languages) β framework, package manager
|
|
102
|
+
ββββββββββ¬βββββββββββββ
|
|
103
|
+
β
|
|
104
|
+
ββββββββββΌβββββββββββββ
|
|
105
|
+
β harness.yaml β β Source of truth
|
|
106
|
+
β (editable, git β (hooks + rules)
|
|
92
107
|
β trackable) β
|
|
93
108
|
ββββββββββ¬βββββββββββββ
|
|
94
109
|
β
|
|
95
|
-
βΌ
|
|
96
110
|
ββββββββββββββββΌβββββββββββββββ
|
|
97
111
|
βΌ βΌ βΌ
|
|
98
112
|
ββββββββββββ ββββββββββββ ββββββββββββββββ
|
|
@@ -101,292 +115,271 @@ your-project/
|
|
|
101
115
|
ββββββββββββ ββββββββββββ ββββββββββββββββ
|
|
102
116
|
```
|
|
103
117
|
|
|
104
|
-
###
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
|
109
|
-
|
|
118
|
+
### π Project Detector
|
|
119
|
+
|
|
120
|
+
oh-my-harness automatically detects your project type and injects accurate facts into the LLM prompt:
|
|
121
|
+
|
|
122
|
+
| Language | Detection | Commands |
|
|
123
|
+
|----------|-----------|----------|
|
|
124
|
+
| π¦ TypeScript/JS | package.json, tsconfig | pnpm/npm/yarn test, eslint, tsc |
|
|
125
|
+
| π Python | pyproject.toml, requirements.txt, Pipfile, manage.py, .python-version | pytest, ruff, black, isort, mypy |
|
|
126
|
+
| π Swift | Package.swift, .xcodeproj | swift test, xcodebuild |
|
|
127
|
+
| π¦ Rust | Cargo.toml | cargo test, cargo clippy |
|
|
128
|
+
| πΉ Go | go.mod | go test, golangci-lint |
|
|
129
|
+
| β Java/Kotlin | pom.xml, build.gradle | mvn test, ./gradlew test |
|
|
130
|
+
| π Ruby | Gemfile | bundle exec rspec |
|
|
131
|
+
| π PHP | composer.json | phpunit |
|
|
132
|
+
| π― Dart/Flutter | pubspec.yaml | dart test, flutter test |
|
|
133
|
+
| β‘ C/C++ | CMakeLists.txt | cmake, make |
|
|
134
|
+
| π£ C#/.NET | *.csproj | dotnet test |
|
|
135
|
+
| π§ Elixir | mix.exs | mix test |
|
|
136
|
+
| π· Scala | build.sbt | sbt test |
|
|
137
|
+
| β‘ Zig | build.zig | zig build test |
|
|
110
138
|
|
|
111
139
|
---
|
|
112
140
|
|
|
113
|
-
##
|
|
114
|
-
|
|
115
|
-
### `_base` β Always Applied
|
|
116
|
-
- TDD enforcement (mandatory test-first workflow)
|
|
117
|
-
- Dangerous command blocking
|
|
118
|
-
- Pre-commit test gate
|
|
119
|
-
- Branch management rules
|
|
120
|
-
- File safety rules
|
|
121
|
-
|
|
122
|
-
### `nextjs` β Next.js + TypeScript
|
|
123
|
-
- App Router conventions (Server Components default)
|
|
124
|
-
- Component test enforcement
|
|
125
|
-
- ESLint auto-fix on save
|
|
126
|
-
- Build output protection (`.next/`, `out/`)
|
|
127
|
-
- pnpm permissions
|
|
128
|
-
|
|
129
|
-
### `fastapi` β FastAPI + Python
|
|
130
|
-
- Async-first, Pydantic v2 patterns
|
|
131
|
-
- pytest + real DB testing (no mocks)
|
|
132
|
-
- Ruff auto-format on save
|
|
133
|
-
- Virtual env protection
|
|
134
|
-
- uv/pytest permissions
|
|
135
|
-
|
|
136
|
-
### `nextjs-fastapi` β Full Stack
|
|
137
|
-
- Composes both presets
|
|
138
|
-
- Cross-cutting rules (CORS, API boundaries)
|
|
139
|
-
- Dual test suite enforcement (both must pass before commit)
|
|
141
|
+
## π§± Building Block Catalog
|
|
140
142
|
|
|
141
|
-
|
|
143
|
+
All enforcement is powered by **catalog blocks** β reusable, parameterized hook templates:
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
| Block | Category | Description |
|
|
146
|
+
|-------|----------|-------------|
|
|
147
|
+
| π‘οΈ `branch-guard` | git | Blocks commits on main/merged branches |
|
|
148
|
+
| π§ͺ `commit-test-gate` | quality | Runs tests before git commit |
|
|
149
|
+
| π `commit-typecheck-gate` | quality | Runs typecheck before git commit |
|
|
150
|
+
| π `command-guard` | security | Blocks dangerous shell commands |
|
|
151
|
+
| π `path-guard` | file-protection | Blocks writes to protected paths |
|
|
152
|
+
| π `lockfile-guard` | file-protection | Prevents manual lockfile edits |
|
|
153
|
+
| π€« `secret-file-guard` | security | Blocks edits to .env, credentials |
|
|
154
|
+
| βοΈ `lint-on-save` | auto-fix | Auto-lint on file save |
|
|
155
|
+
| π¨ `format-on-save` | auto-fix | Auto-format on file save |
|
|
156
|
+
| π `auto-pr` | automation | Auto-create PR after push |
|
|
157
|
+
| π§ͺ `tdd-guard` | quality | Blocks source edits unless test modified first (JS/TS/Python) |
|
|
144
158
|
|
|
145
|
-
|
|
146
|
-
# Initialize with natural language
|
|
147
|
-
oh-my-harness init "your project description"
|
|
159
|
+
### Usage in `harness.yaml`
|
|
148
160
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
```yaml
|
|
162
|
+
hooks:
|
|
163
|
+
- block: branch-guard
|
|
164
|
+
- block: tdd-guard
|
|
165
|
+
- block: commit-test-gate
|
|
166
|
+
params:
|
|
167
|
+
testCommand: "npx vitest run"
|
|
168
|
+
- block: path-guard
|
|
169
|
+
params:
|
|
170
|
+
blockedPaths:
|
|
171
|
+
- "node_modules/"
|
|
172
|
+
- "dist/"
|
|
173
|
+
- block: command-guard
|
|
174
|
+
params:
|
|
175
|
+
patterns:
|
|
176
|
+
- "rm -rf /"
|
|
177
|
+
- "sudo rm"
|
|
178
|
+
- block: lint-on-save
|
|
179
|
+
params:
|
|
180
|
+
filePattern: "*.ts"
|
|
181
|
+
command: "npx eslint --fix"
|
|
182
|
+
- block: auto-pr
|
|
183
|
+
params:
|
|
184
|
+
baseBranch: main
|
|
185
|
+
```
|
|
162
186
|
|
|
163
|
-
|
|
164
|
-
oh-my-harness add nextjs
|
|
187
|
+
---
|
|
165
188
|
|
|
166
|
-
|
|
167
|
-
oh-my-harness remove fastapi
|
|
189
|
+
## π₯οΈ Commands
|
|
168
190
|
|
|
169
|
-
|
|
170
|
-
|
|
191
|
+
```bash
|
|
192
|
+
# π Initialize
|
|
193
|
+
omh init "your project description" # NL-powered (requires Claude CLI)
|
|
194
|
+
omh init --preset nextjs fastapi # Preset-based (instant)
|
|
195
|
+
|
|
196
|
+
# π Catalog
|
|
197
|
+
omh catalog list # Browse all building blocks
|
|
198
|
+
omh catalog info branch-guard # Block details + params
|
|
199
|
+
|
|
200
|
+
# π§ Hook management
|
|
201
|
+
omh hook add branch-guard # Add a hook
|
|
202
|
+
omh hook remove auto-pr # Remove a hook
|
|
203
|
+
|
|
204
|
+
# π Sync & manage
|
|
205
|
+
omh sync # Regenerate from harness.yaml
|
|
206
|
+
omh add nextjs # Add a preset
|
|
207
|
+
omh remove fastapi # Remove a preset
|
|
208
|
+
|
|
209
|
+
# π©Ί Verify & monitor
|
|
210
|
+
omh doctor # Health check
|
|
211
|
+
omh test # Dry-run verify all hooks
|
|
212
|
+
omh stats # TUI analytics dashboard
|
|
171
213
|
```
|
|
172
214
|
|
|
173
|
-
### `doctor`
|
|
215
|
+
### π©Ί `omh doctor`
|
|
174
216
|
|
|
175
|
-
```
|
|
217
|
+
```text
|
|
176
218
|
oh-my-harness: running health checks...
|
|
177
|
-
β .claude/oh-my-harness.json found
|
|
219
|
+
β .claude/oh-my-harness.json found
|
|
178
220
|
β CLAUDE.md exists with intact markers
|
|
179
221
|
β .claude/settings.json is valid
|
|
180
222
|
β All hook scripts are executable
|
|
181
223
|
oh-my-harness: all checks passed
|
|
182
224
|
```
|
|
183
225
|
|
|
184
|
-
|
|
226
|
+
### π§ͺ `omh test` β Dry-Run Verification
|
|
227
|
+
|
|
228
|
+
Simulates hook inputs to verify block/allow behavior without entering Claude Code:
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
β omh test Harness dry-run verification
|
|
232
|
+
β
|
|
233
|
+
β Branch guard
|
|
234
|
+
β β git commit on feat/my-feature β ALLOWED
|
|
235
|
+
β
|
|
236
|
+
β TDD Guard
|
|
237
|
+
β β src/foo.ts without test β BLOCKED
|
|
238
|
+
β β tests/unit/foo.test.ts β ALLOWED
|
|
239
|
+
β β README.md β ALLOWED
|
|
240
|
+
β
|
|
241
|
+
β File guards
|
|
242
|
+
β β node_modules/test-file.js β BLOCKED
|
|
243
|
+
β β dist/test-file.js β BLOCKED
|
|
244
|
+
β β src/index.ts β ALLOWED
|
|
245
|
+
β
|
|
246
|
+
β Command guards
|
|
247
|
+
β β "rm -rf /" β BLOCKED
|
|
248
|
+
β β "npm test" β ALLOWED
|
|
249
|
+
β
|
|
250
|
+
β 14/14 checks passed β
|
|
251
|
+
```
|
|
185
252
|
|
|
186
|
-
|
|
253
|
+
### π `omh stats` β TUI Analytics Dashboard
|
|
187
254
|
|
|
188
|
-
|
|
255
|
+
Interactive dashboard powered by [ink](https://github.com/vadimdemedes/ink) with 3 views:
|
|
189
256
|
|
|
190
|
-
|
|
257
|
+
```text
|
|
258
|
+
[1] Overview [2] Timeline [3] Blocks d:filter r:reload q:quit
|
|
191
259
|
|
|
192
|
-
|
|
193
|
-
# Agent attempts: git commit -m "add login page"
|
|
194
|
-
oh-my-harness: Running tests before commit...
|
|
195
|
-
# pnpm test runs...
|
|
196
|
-
# If tests fail:
|
|
197
|
-
{"decision": "block", "reason": "oh-my-harness: tests failed, commit blocked"}
|
|
198
|
-
# Agent must fix tests before committing
|
|
199
|
-
```
|
|
260
|
+
Active: 8 Events: 1213 Block rate: 2%
|
|
200
261
|
|
|
201
|
-
|
|
262
|
+
branch-guard ββββββββββββββββββββ 0b/202a
|
|
263
|
+
tdd-guard βββββββββββββββββββββ 14b/68a
|
|
264
|
+
commit-test-gate ββββββββββββββββββββ 0b/199a
|
|
265
|
+
path-guard ββββββββββββββββββββ 4b/76a
|
|
266
|
+
command-guard ββββββββββββββββββββ 4b/202a
|
|
202
267
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
# Agent cannot write to build outputs
|
|
268
|
+
Dormant (0 hits):
|
|
269
|
+
β lockfile-guard
|
|
270
|
+
β secret-file-guard
|
|
207
271
|
```
|
|
208
272
|
|
|
209
|
-
|
|
273
|
+
- **Overview** β Active blocks with hit bar charts + dormant block detection
|
|
274
|
+
- **Timeline** β 24-hour heatmap + block rate + peak hour
|
|
275
|
+
- **Blocks** β Scrollable detail view with params, hits, last block reason
|
|
210
276
|
|
|
211
|
-
|
|
212
|
-
# Agent attempts: rm -rf /
|
|
213
|
-
{"decision": "block", "reason": "oh-my-harness: dangerous command blocked"}
|
|
214
|
-
```
|
|
277
|
+
Keyboard: `1/2/3` views, `β/β` scroll, `d` date filter, `r` reload, `q` quit
|
|
215
278
|
|
|
216
279
|
---
|
|
217
280
|
|
|
218
|
-
##
|
|
281
|
+
## π Stateful Hook Logging
|
|
219
282
|
|
|
220
|
-
|
|
283
|
+
Every hook invocation is recorded in `.claude/hooks/.state/events.jsonl`:
|
|
221
284
|
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
description: "E-commerce platform"
|
|
226
|
-
stacks:
|
|
227
|
-
- name: frontend
|
|
228
|
-
framework: nextjs
|
|
229
|
-
language: typescript
|
|
230
|
-
testRunner: vitest
|
|
231
|
-
linter: eslint
|
|
232
|
-
|
|
233
|
-
rules:
|
|
234
|
-
- id: tdd-rules
|
|
235
|
-
title: TDD Workflow
|
|
236
|
-
content: |
|
|
237
|
-
## TDD Workflow (MANDATORY)
|
|
238
|
-
1. Write failing test FIRST
|
|
239
|
-
2. Implement minimal code to pass
|
|
240
|
-
3. Refactor while green
|
|
241
|
-
priority: 10
|
|
242
|
-
|
|
243
|
-
enforcement:
|
|
244
|
-
preCommit: ["pnpm test", "npx eslint .", "npx tsc --noEmit"]
|
|
245
|
-
blockedPaths: [".next/", "node_modules/"]
|
|
246
|
-
blockedCommands: ["rm -rf", "sudo"]
|
|
247
|
-
postSave:
|
|
248
|
-
- pattern: "*.ts"
|
|
249
|
-
command: "eslint --fix"
|
|
250
|
-
|
|
251
|
-
# v2: Building block catalog (recommended)
|
|
252
|
-
hooks:
|
|
253
|
-
- block: branch-guard
|
|
254
|
-
params:
|
|
255
|
-
mainBranch: main
|
|
256
|
-
- block: commit-test-gate
|
|
257
|
-
params:
|
|
258
|
-
testCommand: "npx vitest run"
|
|
259
|
-
- block: auto-pr
|
|
260
|
-
params:
|
|
261
|
-
baseBranch: main
|
|
262
|
-
draft: false
|
|
285
|
+
```jsonl
|
|
286
|
+
{"ts":"2026-03-18T08:00:00Z","event":"PreToolUse","hook":"catalog-tdd-guard.sh","decision":"block","reason":"TDD β foo.test.* ν
μ€νΈ νμΌμ λ¨Όμ μμ νμΈμ"}
|
|
287
|
+
{"ts":"2026-03-18T08:00:05Z","event":"PreToolUse","hook":"catalog-command-guard.sh","decision":"allow","reason":""}
|
|
263
288
|
```
|
|
264
289
|
|
|
290
|
+
This powers `omh test` live verification and `omh stats` analytics.
|
|
291
|
+
|
|
265
292
|
---
|
|
266
293
|
|
|
267
|
-
## Architecture
|
|
294
|
+
## ποΈ Architecture
|
|
268
295
|
|
|
269
|
-
```
|
|
296
|
+
```text
|
|
270
297
|
oh-my-harness/
|
|
271
298
|
βββ bin/ # CLI entry point
|
|
272
299
|
βββ src/
|
|
273
300
|
β βββ catalog/
|
|
274
|
-
β β βββ blocks/
|
|
275
|
-
β β βββ types.ts
|
|
276
|
-
β β βββ registry.ts
|
|
277
|
-
β β βββ template-engine.ts # Handlebars
|
|
278
|
-
β β βββ converter.ts
|
|
279
|
-
β βββ cli/
|
|
301
|
+
β β βββ blocks/ # 11 building block definitions
|
|
302
|
+
β β βββ types.ts # BuildingBlock, HookEntry schemas
|
|
303
|
+
β β βββ registry.ts # Block discovery & search
|
|
304
|
+
β β βββ template-engine.ts # Handlebars rendering + applyDefaults
|
|
305
|
+
β β βββ converter.ts # HookEntry[] β rendered scripts
|
|
306
|
+
β βββ cli/
|
|
307
|
+
β β βββ commands/ # init, add, remove, doctor, catalog, hook, sync, test
|
|
308
|
+
β β βββ stats/ # TUI dashboard (ink/React)
|
|
309
|
+
β β β βββ App.tsx # App shell (tab bar, keyboard nav)
|
|
310
|
+
β β β βββ data.ts # Data aggregation layer
|
|
311
|
+
β β β βββ components/ # Overview, Timeline, Blocks views
|
|
312
|
+
β β βββ harness-tester.ts # Hook simulation engine
|
|
313
|
+
β β βββ event-logger.ts # events.jsonl read/write/stats
|
|
314
|
+
β β βββ event-verifier.ts # Event-based verification
|
|
315
|
+
β β βββ tool-checker.ts # Command executable checks
|
|
280
316
|
β βββ core/
|
|
281
|
-
β β βββ
|
|
282
|
-
β β βββ
|
|
283
|
-
β β βββ
|
|
284
|
-
β β
|
|
285
|
-
β β βββ harness-schema.ts # harness.yaml schema
|
|
286
|
-
β β βββ harness-converter.ts # harness.yaml β MergedConfig
|
|
287
|
-
β β βββ generator.ts # Orchestrates all generators
|
|
317
|
+
β β βββ harness-schema.ts # harness.yaml Zod schema
|
|
318
|
+
β β βββ harness-converter.ts # enforcementβhooks + MergedConfig
|
|
319
|
+
β β βββ generator.ts # Orchestrates all generators
|
|
320
|
+
β β βββ config-merger.ts # Multi-preset merge
|
|
288
321
|
β βββ generators/
|
|
289
322
|
β β βββ claude-md.ts # CLAUDE.md with idempotent markers
|
|
290
|
-
β β βββ hooks.ts #
|
|
323
|
+
β β βββ hooks.ts # Hook scripts + event logger injection
|
|
291
324
|
β β βββ settings.ts # .claude/settings.json
|
|
292
325
|
β β βββ gitignore.ts # .gitignore updater
|
|
293
326
|
β βββ detector/
|
|
294
327
|
β β βββ project-detector.ts # Deterministic project detection
|
|
295
328
|
β β βββ types.ts # ProjectFacts, Detector interface
|
|
296
329
|
β β βββ detectors/ # 14 language detectors
|
|
297
|
-
β
|
|
298
|
-
β
|
|
299
|
-
β
|
|
300
|
-
β βββ utils/
|
|
301
|
-
β βββ markdown.ts # Marker-based section management
|
|
302
|
-
β βββ yaml.ts # YAML helpers
|
|
330
|
+
β βββ nl/
|
|
331
|
+
β βββ parse-intent.ts # claude -p integration
|
|
332
|
+
β βββ prompt-templates.ts # LLM prompt construction
|
|
303
333
|
βββ presets/ # Built-in preset definitions
|
|
304
|
-
|
|
305
|
-
β βββ nextjs/
|
|
306
|
-
β βββ fastapi/
|
|
307
|
-
β βββ nextjs-fastapi/
|
|
308
|
-
βββ tests/ # 422 tests (unit + integration)
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## Adding Custom Presets
|
|
314
|
-
|
|
315
|
-
Create a directory in `presets/` with a `preset.yaml`:
|
|
316
|
-
|
|
317
|
-
```yaml
|
|
318
|
-
name: my-preset
|
|
319
|
-
displayName: "My Custom Preset"
|
|
320
|
-
description: "Custom rules for my team"
|
|
321
|
-
version: "1.0.0"
|
|
322
|
-
extends: ["_base"]
|
|
323
|
-
tags: ["custom"]
|
|
324
|
-
|
|
325
|
-
claudeMd:
|
|
326
|
-
sections:
|
|
327
|
-
- id: "my-rules"
|
|
328
|
-
title: "My Rules"
|
|
329
|
-
content: |
|
|
330
|
-
## My Team Rules
|
|
331
|
-
- Always write JSDoc comments
|
|
332
|
-
- Use barrel exports
|
|
333
|
-
priority: 30
|
|
334
|
-
|
|
335
|
-
hooks:
|
|
336
|
-
preToolUse:
|
|
337
|
-
- id: "my-guard"
|
|
338
|
-
matcher: "Bash"
|
|
339
|
-
inline: |
|
|
340
|
-
#!/bin/bash
|
|
341
|
-
# Your custom enforcement logic
|
|
342
|
-
exit 0
|
|
334
|
+
βββ tests/ # 745+ tests (unit + integration)
|
|
343
335
|
```
|
|
344
336
|
|
|
345
|
-
No code changes required. The registry auto-discovers it.
|
|
346
|
-
|
|
347
337
|
---
|
|
348
338
|
|
|
349
|
-
## Requirements
|
|
339
|
+
## π¦ Requirements
|
|
350
340
|
|
|
351
341
|
- **Node.js** >= 20
|
|
352
342
|
- **Claude CLI** (optional, for NL mode) β [Install guide](https://docs.anthropic.com/en/docs/claude-code)
|
|
353
343
|
|
|
354
344
|
---
|
|
355
345
|
|
|
356
|
-
## Roadmap
|
|
346
|
+
## πΊοΈ Roadmap
|
|
357
347
|
|
|
358
348
|
- [x] `npx oh-my-harness` β zero-install usage
|
|
359
|
-
- [x] `
|
|
360
|
-
- [x] Building block catalog β
|
|
361
|
-
- [x] Project detector β 14 language auto-detection
|
|
349
|
+
- [x] `omh sync` β regenerate from harness.yaml
|
|
350
|
+
- [x] Building block catalog β 11 verified hook templates
|
|
351
|
+
- [x] Project detector β 14 language auto-detection
|
|
352
|
+
- [x] `omh test` β dry-run hook verification
|
|
353
|
+
- [x] `omh stats` β TUI analytics dashboard (ink)
|
|
354
|
+
- [x] Stateful hook logging β events.jsonl
|
|
355
|
+
- [x] TDD Guard β enforce test-first workflow
|
|
362
356
|
- [ ] Cursor (`.cursor/rules/`) emitter
|
|
363
357
|
- [ ] Codex (`AGENTS.md`) emitter
|
|
364
358
|
- [ ] GitHub Copilot emitter
|
|
365
359
|
- [ ] Community preset registry
|
|
366
|
-
- [ ] `
|
|
367
|
-
- [ ] More building blocks (20+ Claude Code hook events)
|
|
360
|
+
- [ ] `omh modify "change X"` β NL config editing
|
|
368
361
|
|
|
369
362
|
---
|
|
370
363
|
|
|
371
|
-
## Contributing
|
|
364
|
+
## π€ Contributing
|
|
372
365
|
|
|
373
366
|
Contributions are welcome! Please read the [Contributing Guide](CONTRIBUTING.md) before submitting a PR.
|
|
374
367
|
|
|
375
368
|
---
|
|
376
369
|
|
|
377
|
-
## Support This Project
|
|
370
|
+
## πͺ Support This Project
|
|
378
371
|
|
|
379
372
|
oh-my-harness is free and open source. Here's how you can help:
|
|
380
373
|
|
|
381
|
-
- **Star** β [Give a star](https://github.com/kyu1204/oh-my-harness) to help others discover the project
|
|
382
|
-
- **Report Bugs** β [Open an issue](https://github.com/kyu1204/oh-my-harness/issues/new) when something doesn't work
|
|
383
|
-
- **Request Features** β [Suggest ideas](https://github.com/kyu1204/oh-my-harness/issues/new) for new
|
|
384
|
-
- **Contribute** β Fix a bug, add a
|
|
385
|
-
- **Spread the Word** β Share oh-my-harness with your team or community
|
|
374
|
+
- β **Star** β [Give a star](https://github.com/kyu1204/oh-my-harness) to help others discover the project
|
|
375
|
+
- π **Report Bugs** β [Open an issue](https://github.com/kyu1204/oh-my-harness/issues/new) when something doesn't work
|
|
376
|
+
- π‘ **Request Features** β [Suggest ideas](https://github.com/kyu1204/oh-my-harness/issues/new) for new blocks, emitters, or features
|
|
377
|
+
- π§ **Contribute** β Fix a bug, add a block, or improve docs β PRs are always welcome
|
|
378
|
+
- π’ **Spread the Word** β Share oh-my-harness with your team or community
|
|
386
379
|
|
|
387
380
|
---
|
|
388
381
|
|
|
389
|
-
## License
|
|
382
|
+
## π License
|
|
390
383
|
|
|
391
384
|
MIT
|
|
392
385
|
|
|
@@ -394,7 +387,7 @@ MIT
|
|
|
394
387
|
|
|
395
388
|
<div align="center">
|
|
396
389
|
|
|
397
|
-
**Your agents are only as good as their guardrails.**
|
|
390
|
+
**Your agents are only as good as their guardrails.** π΄
|
|
398
391
|
|
|
399
392
|
Built with frustration from hand-writing CLAUDE.md files.
|
|
400
393
|
|
|
@@ -21,9 +21,9 @@ set -euo pipefail
|
|
|
21
21
|
INPUT=$(cat)
|
|
22
22
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
23
23
|
[[ -z "$COMMAND" ]] && exit 0
|
|
24
|
-
PATTERNS=({{#each patterns}}"{{this}}" {{/each}})
|
|
24
|
+
PATTERNS=({{#each patterns}}"{{{this}}}" {{/each}})
|
|
25
25
|
for PATTERN in "\${PATTERNS[@]}"; do
|
|
26
|
-
if echo "$COMMAND" | grep -qF "$PATTERN"; then
|
|
26
|
+
if echo "$COMMAND" | grep -qF -- "$PATTERN"; then
|
|
27
27
|
_log_event "block" "oh-my-harness: command matches blocked pattern: $PATTERN"
|
|
28
28
|
echo "{\\"decision\\": \\"block\\", \\"reason\\": \\"oh-my-harness: command matches blocked pattern: $PATTERN\\"}"
|
|
29
29
|
exit 0
|
|
@@ -15,8 +15,8 @@ set -euo pipefail
|
|
|
15
15
|
INPUT=$(cat)
|
|
16
16
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
17
17
|
if echo "$COMMAND" | grep -qE "git commit"; then
|
|
18
|
-
echo "oh-my-harness: Running {{testCommand}} before commit..." >&2
|
|
19
|
-
if ! {{testCommand}} >&2 2>&1; then
|
|
18
|
+
echo "oh-my-harness: Running {{{testCommand}}} before commit..." >&2
|
|
19
|
+
if ! {{{testCommand}}} >&2 2>&1; then
|
|
20
20
|
_log_event "block" "oh-my-harness: pre-commit check failed"
|
|
21
21
|
echo "{\\"decision\\": \\"block\\", \\"reason\\": \\"oh-my-harness: pre-commit check failed\\"}"
|
|
22
22
|
exit 0
|
|
@@ -15,8 +15,8 @@ set -euo pipefail
|
|
|
15
15
|
INPUT=$(cat)
|
|
16
16
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
17
17
|
if echo "$COMMAND" | grep -qE "git commit"; then
|
|
18
|
-
echo "oh-my-harness: Running {{typecheckCommand}} before commit..." >&2
|
|
19
|
-
if ! {{typecheckCommand}} >&2 2>&1; then
|
|
18
|
+
echo "oh-my-harness: Running {{{typecheckCommand}}} before commit..." >&2
|
|
19
|
+
if ! {{{typecheckCommand}}} >&2 2>&1; then
|
|
20
20
|
_log_event "block" "oh-my-harness: pre-commit check failed"
|
|
21
21
|
echo "{\\"decision\\": \\"block\\", \\"reason\\": \\"oh-my-harness: pre-commit check failed\\"}"
|
|
22
22
|
exit 0
|
|
@@ -26,11 +26,11 @@ set -euo pipefail
|
|
|
26
26
|
INPUT=$(cat)
|
|
27
27
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
|
|
28
28
|
[[ -z "$FILE_PATH" ]] && exit 0
|
|
29
|
-
PATTERN='{{filePattern}}'
|
|
29
|
+
PATTERN='{{{filePattern}}}'
|
|
30
30
|
BASENAME=$(basename "$FILE_PATH")
|
|
31
31
|
if [[ "$BASENAME" == $PATTERN ]]; then
|
|
32
|
-
echo "oh-my-harness: Running {{command}} on $FILE_PATH..." >&2
|
|
33
|
-
{{command}} "$FILE_PATH" >&2 2>&1 || true
|
|
32
|
+
echo "oh-my-harness: Running {{{command}}} on $FILE_PATH..." >&2
|
|
33
|
+
{{{command}}} "$FILE_PATH" >&2 2>&1 || true
|
|
34
34
|
fi
|
|
35
35
|
exit 0`,
|
|
36
36
|
};
|
|
@@ -26,11 +26,11 @@ set -euo pipefail
|
|
|
26
26
|
INPUT=$(cat)
|
|
27
27
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
|
|
28
28
|
[[ -z "$FILE_PATH" ]] && exit 0
|
|
29
|
-
PATTERN='{{filePattern}}'
|
|
29
|
+
PATTERN='{{{filePattern}}}'
|
|
30
30
|
BASENAME=$(basename "$FILE_PATH")
|
|
31
31
|
if [[ "$BASENAME" == $PATTERN ]]; then
|
|
32
|
-
echo "oh-my-harness: Running {{command}} on $FILE_PATH..." >&2
|
|
33
|
-
{{command}} "$FILE_PATH" >&2 2>&1 || true
|
|
32
|
+
echo "oh-my-harness: Running {{{command}}} on $FILE_PATH..." >&2
|
|
33
|
+
{{{command}}} "$FILE_PATH" >&2 2>&1 || true
|
|
34
34
|
fi
|
|
35
35
|
exit 0`,
|
|
36
36
|
};
|