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.
Files changed (65) hide show
  1. package/README.md +221 -228
  2. package/dist/catalog/blocks/command-guard.js +2 -2
  3. package/dist/catalog/blocks/commit-test-gate.js +2 -2
  4. package/dist/catalog/blocks/commit-typecheck-gate.js +2 -2
  5. package/dist/catalog/blocks/format-on-save.js +3 -3
  6. package/dist/catalog/blocks/lint-on-save.js +3 -3
  7. package/dist/catalog/blocks/lockfile-guard.js +1 -1
  8. package/dist/catalog/blocks/path-guard.js +1 -1
  9. package/dist/catalog/blocks/secret-file-guard.js +1 -1
  10. package/dist/catalog/blocks/tdd-guard.js +11 -6
  11. package/dist/catalog/template-engine.js +5 -0
  12. package/dist/cli/command-checker.js +6 -2
  13. package/dist/cli/harness-tester.js +20 -11
  14. package/dist/cli/stats/components/Blocks.js +1 -1
  15. package/dist/cli/stats/components/Overview.js +1 -1
  16. package/dist/cli/stats/data.d.ts +1 -0
  17. package/dist/cli/stats/data.js +24 -3
  18. package/dist/cli/tool-checker.js +7 -1
  19. package/dist/cli/tui/init-flow.d.ts +1 -0
  20. package/dist/cli/tui/init-flow.js +134 -20
  21. package/dist/core/harness-converter-v2.js +2 -4
  22. package/dist/detector/detectors/node.js +38 -0
  23. package/dist/detector/detectors/python.js +56 -8
  24. package/dist/nl/parse-intent.js +38 -2
  25. package/dist/nl/prompt-templates.js +5 -1
  26. package/package.json +1 -1
  27. package/presets/actix/preset.yaml +55 -0
  28. package/presets/cargo/preset.yaml +11 -0
  29. package/presets/cpp/preset.yaml +50 -0
  30. package/presets/csharp/preset.yaml +42 -0
  31. package/presets/dart/preset.yaml +43 -0
  32. package/presets/django/preset.yaml +72 -0
  33. package/presets/elixir/preset.yaml +45 -0
  34. package/presets/express/preset.yaml +61 -0
  35. package/presets/fastapi/preset.yaml +1 -1
  36. package/presets/flask/preset.yaml +65 -0
  37. package/presets/flutter/preset.yaml +64 -0
  38. package/presets/gin/preset.yaml +57 -0
  39. package/presets/go/preset.yaml +43 -0
  40. package/presets/gradle/preset.yaml +13 -0
  41. package/presets/java/preset.yaml +48 -0
  42. package/presets/javascript/preset.yaml +44 -0
  43. package/presets/laravel/preset.yaml +70 -0
  44. package/presets/maven/preset.yaml +13 -0
  45. package/presets/nextjs/preset.yaml +1 -1
  46. package/presets/nextjs-fastapi/preset.yaml +1 -1
  47. package/presets/npm/preset.yaml +12 -0
  48. package/presets/phoenix/preset.yaml +64 -0
  49. package/presets/php/preset.yaml +46 -0
  50. package/presets/pip/preset.yaml +13 -0
  51. package/presets/pipenv/preset.yaml +12 -0
  52. package/presets/pnpm/preset.yaml +12 -0
  53. package/presets/python/preset.yaml +48 -0
  54. package/presets/rails/preset.yaml +65 -0
  55. package/presets/react/preset.yaml +61 -0
  56. package/presets/ruby/preset.yaml +45 -0
  57. package/presets/rust/preset.yaml +42 -0
  58. package/presets/scala/preset.yaml +44 -0
  59. package/presets/springboot/preset.yaml +61 -0
  60. package/presets/swift/preset.yaml +44 -0
  61. package/presets/typescript/preset.yaml +46 -0
  62. package/presets/uv/preset.yaml +12 -0
  63. package/presets/vue/preset.yaml +62 -0
  64. package/presets/yarn/preset.yaml +12 -0
  65. 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
- - βœ… TDD workflow enforced in every plan? **Done.**
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, architecture guide
65
- β”œβ”€β”€ harness.yaml # Your harness config (editable, git-trackable)
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 (allow/deny)
72
+ β”œβ”€β”€ settings.json # Hook configs, permissions
68
73
  β”œβ”€β”€ hooks/
69
- β”‚ β”œβ”€β”€ base-command-guard.sh # Blocks dangerous commands
70
- β”‚ β”œβ”€β”€ base-test-before-commit.sh # Tests must pass before commit
71
- β”‚ β”œβ”€β”€ nextjs-file-guard.sh # Protects build outputs
72
- β”‚ └── fastapi-lint-on-save.sh # Auto-formats Python on save
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
- β”‚ harness.yaml β”‚ ← Intermediate representation
91
- β”‚ (editable, git β”‚ (source of truth)
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
- ### Two Modes
105
-
106
- | Mode | Command | Speed | Requires |
107
- |------|---------|-------|----------|
108
- | **NL-first** | `init "description"` | ~5s | Claude CLI installed |
109
- | **Preset** | `init --preset nextjs` | Instant | Nothing |
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
- ## Built-in Presets
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
- ## Commands
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
- ```bash
146
- # Initialize with natural language
147
- oh-my-harness init "your project description"
159
+ ### Usage in `harness.yaml`
148
160
 
149
- # Initialize with presets
150
- oh-my-harness init --preset nextjs fastapi
151
-
152
- # Browse building block catalog
153
- oh-my-harness catalog list
154
- oh-my-harness catalog info branch-guard
155
-
156
- # Add/remove building blocks
157
- oh-my-harness hook add branch-guard
158
- oh-my-harness hook remove auto-pr
159
-
160
- # Regenerate from harness.yaml
161
- oh-my-harness sync
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
- # Add a preset to existing config
164
- oh-my-harness add nextjs
187
+ ---
165
188
 
166
- # Remove a preset
167
- oh-my-harness remove fastapi
189
+ ## πŸ–₯️ Commands
168
190
 
169
- # Health check
170
- oh-my-harness doctor
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` Output
215
+ ### 🩺 `omh doctor`
174
216
 
175
- ```
217
+ ```text
176
218
  oh-my-harness: running health checks...
177
- βœ“ .claude/oh-my-harness.json found (presets: _base, nextjs)
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
- ## Enforcement in Action
253
+ ### πŸ“Š `omh stats` β€” TUI Analytics Dashboard
187
254
 
188
- ### TDD Gate (Pre-commit Hook)
255
+ Interactive dashboard powered by [ink](https://github.com/vadimdemedes/ink) with 3 views:
189
256
 
190
- When Claude Code tries to `git commit`, oh-my-harness intercepts:
257
+ ```text
258
+ [1] Overview [2] Timeline [3] Blocks d:filter r:reload q:quit
191
259
 
192
- ```bash
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
- ### File Guard (Pre-write Hook)
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
- ```bash
204
- # Agent attempts: Write to .next/cache/something.js
205
- {"decision": "block", "reason": "oh-my-harness: protected path .next/"}
206
- # Agent cannot write to build outputs
268
+ Dormant (0 hits):
269
+ β–‘ lockfile-guard
270
+ β–‘ secret-file-guard
207
271
  ```
208
272
 
209
- ### Command Guard
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
- ```bash
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
- ## The `harness.yaml` File
281
+ ## πŸ“Š Stateful Hook Logging
219
282
 
220
- After init, a `harness.yaml` is saved to your project root. This is the **source of truth** β€” edit it directly, then re-run init to regenerate:
283
+ Every hook invocation is recorded in `.claude/hooks/.state/events.jsonl`:
221
284
 
222
- ```yaml
223
- version: "1.0"
224
- project:
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/ # 10 building block definitions
275
- β”‚ β”‚ β”œβ”€β”€ types.ts # BuildingBlock, HookEntry schemas
276
- β”‚ β”‚ β”œβ”€β”€ registry.ts # Block discovery & search
277
- β”‚ β”‚ β”œβ”€β”€ template-engine.ts # Handlebars-based rendering
278
- β”‚ β”‚ └── converter.ts # HookEntry[] β†’ settings.json
279
- β”‚ β”œβ”€β”€ cli/commands/ # init, add, remove, doctor, catalog, hook, sync
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
- β”‚ β”‚ β”œβ”€β”€ preset-types.ts # Zod schemas
282
- β”‚ β”‚ β”œβ”€β”€ preset-loader.ts # YAML β†’ typed config
283
- β”‚ β”‚ β”œβ”€β”€ preset-registry.ts # Discover & search presets
284
- β”‚ β”‚ β”œβ”€β”€ config-merger.ts # Merge multiple presets
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 # Executable hook scripts
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
- β”‚ β”œβ”€β”€ nl/
298
- β”‚ β”‚ β”œβ”€β”€ parse-intent.ts # claude -p integration
299
- β”‚ β”‚ └── prompt-templates.ts # LLM prompt construction
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
- β”‚ β”œβ”€β”€ _base/
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] `oh-my-harness sync` β€” regenerate from harness.yaml
360
- - [x] Building block catalog β€” 10 verified hook templates
361
- - [x] Project detector β€” 14 language auto-detection for accurate NL generation
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
- - [ ] `oh-my-harness modify "change X"` β€” NL config editing
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 presets, hooks, or emitters
384
- - **Contribute** β€” Fix a bug, add a preset, or improve docs β€” PRs are always welcome
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
  };