obey-cli 0.1.0__py3-none-any.whl

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 (90) hide show
  1. obey_cli/__init__.py +3 -0
  2. obey_cli/__main__.py +3 -0
  3. obey_cli/_data/demo/README.md +12 -0
  4. obey_cli/_data/demo/obey.yaml +287 -0
  5. obey_cli/_data/demo/repo/.gitignore +8 -0
  6. obey_cli/_data/demo/repo/AGENTS.md +38 -0
  7. obey_cli/_data/demo/repo/README.md +36 -0
  8. obey_cli/_data/demo/repo/eslint.config.js +24 -0
  9. obey_cli/_data/demo/repo/index.html +21 -0
  10. obey_cli/_data/demo/repo/openapi.yaml +86 -0
  11. obey_cli/_data/demo/repo/package.json +26 -0
  12. obey_cli/_data/demo/repo/pnpm-lock.yaml +1439 -0
  13. obey_cli/_data/demo/repo/scripts/gen-types.mjs +109 -0
  14. obey_cli/_data/demo/repo/src/api/client.test.ts +60 -0
  15. obey_cli/_data/demo/repo/src/api/client.ts +71 -0
  16. obey_cli/_data/demo/repo/src/api/response-guards.test.ts +21 -0
  17. obey_cli/_data/demo/repo/src/api/response-guards.ts +38 -0
  18. obey_cli/_data/demo/repo/src/date-utils.test.ts +51 -0
  19. obey_cli/_data/demo/repo/src/date-utils.ts +60 -0
  20. obey_cli/_data/demo/repo/src/generated/api-types.ts +35 -0
  21. obey_cli/_data/demo/repo/src/logger.test.ts +24 -0
  22. obey_cli/_data/demo/repo/src/logger.ts +36 -0
  23. obey_cli/_data/demo/repo/src/main.ts +30 -0
  24. obey_cli/_data/demo/repo/src/render-table.test.ts +34 -0
  25. obey_cli/_data/demo/repo/src/render-table.ts +48 -0
  26. obey_cli/_data/demo/repo/src/shift-planner.test.ts +66 -0
  27. obey_cli/_data/demo/repo/src/shift-planner.ts +101 -0
  28. obey_cli/_data/demo/repo/tsconfig.json +20 -0
  29. obey_cli/_data/demo/repo/vitest.config.ts +8 -0
  30. obey_cli/_data/demo/runs/after.jsonl.gz +0 -0
  31. obey_cli/_data/demo/runs/before.jsonl.gz +0 -0
  32. obey_cli/_data/demo/runs/meta.json +10 -0
  33. obey_cli/_data/schemas/obey-config-v1.json +116 -0
  34. obey_cli/_data/schemas/report-v1.json +55 -0
  35. obey_cli/_data/schemas/survey-record-v1.json +30 -0
  36. obey_cli/checkers/__init__.py +30 -0
  37. obey_cli/checkers/samples.py +95 -0
  38. obey_cli/cli.py +201 -0
  39. obey_cli/commands.py +676 -0
  40. obey_cli/config.py +90 -0
  41. obey_cli/discover.py +232 -0
  42. obey_cli/enforce/__init__.py +114 -0
  43. obey_cli/enforce/engine/__init__.py +0 -0
  44. obey_cli/enforce/engine/obey_engine.py +2005 -0
  45. obey_cli/enforce/merge.py +196 -0
  46. obey_cli/enforce/targets/__init__.py +354 -0
  47. obey_cli/enforce/templates/obey_hook.py +452 -0
  48. obey_cli/enforce/templates/pre_commit.py +116 -0
  49. obey_cli/extract.py +1342 -0
  50. obey_cli/lint.py +125 -0
  51. obey_cli/llm.py +129 -0
  52. obey_cli/parse_md.py +260 -0
  53. obey_cli/probes/__init__.py +0 -0
  54. obey_cli/probes/facts.py +162 -0
  55. obey_cli/probes/templates.py +326 -0
  56. obey_cli/py.typed +0 -0
  57. obey_cli/report/__init__.py +314 -0
  58. obey_cli/resources.py +35 -0
  59. obey_cli/rules.py +75 -0
  60. obey_cli/runner/__init__.py +0 -0
  61. obey_cli/runner/agents/__init__.py +22 -0
  62. obey_cli/runner/agents/base.py +96 -0
  63. obey_cli/runner/agents/claude_code.py +203 -0
  64. obey_cli/runner/agents/codex.py +152 -0
  65. obey_cli/runner/agents/copilot.py +142 -0
  66. obey_cli/runner/agents/gemini.py +125 -0
  67. obey_cli/runner/agents/opencode.py +156 -0
  68. obey_cli/runner/core.py +140 -0
  69. obey_cli/runner/egress_proxy.py +166 -0
  70. obey_cli/runner/gateway.py +529 -0
  71. obey_cli/runner/ledger.py +132 -0
  72. obey_cli/runner/local.py +176 -0
  73. obey_cli/runner/orchestrate.py +227 -0
  74. obey_cli/runner/sandbox/Dockerfile +41 -0
  75. obey_cli/runner/sandbox/entrypoint.sh +38 -0
  76. obey_cli/runner/sandbox.py +341 -0
  77. obey_cli/runner/sidecar.py +38 -0
  78. obey_cli/runner/workspace.py +187 -0
  79. obey_cli/score.py +150 -0
  80. obey_cli/study/__init__.py +0 -0
  81. obey_cli/study/analysis.py +277 -0
  82. obey_cli/study/design.py +98 -0
  83. obey_cli/study/experiment.py +198 -0
  84. obey_cli/study/report.py +519 -0
  85. obey_cli/study/survey.py +438 -0
  86. obey_cli-0.1.0.dist-info/METADATA +393 -0
  87. obey_cli-0.1.0.dist-info/RECORD +90 -0
  88. obey_cli-0.1.0.dist-info/WHEEL +4 -0
  89. obey_cli-0.1.0.dist-info/entry_points.txt +2 -0
  90. obey_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,393 @@
1
+ Metadata-Version: 2.5
2
+ Name: obey-cli
3
+ Version: 0.1.0
4
+ Summary: Unit tests for AGENTS.md: turns agent instructions into checkable rules, runs your coding agent on tempting tasks in a sandbox, measures per-rule compliance from what it actually did, and compiles the rules it ignores into hooks.
5
+ Project-URL: Homepage, https://github.com/B0yko/obey
6
+ Project-URL: Issues, https://github.com/B0yko/obey/issues
7
+ Author: Andrii Boiko
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Keywords: agents-md,claude-md,coding-agents,evaluation,hooks,instruction-following,pre-commit
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Topic :: Software Development :: Testing
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.27
25
+ Requires-Dist: jsonschema>=4.20
26
+ Requires-Dist: pyyaml>=6.0
27
+ Provides-Extra: study
28
+ Requires-Dist: matplotlib>=3.8; extra == 'study'
29
+ Requires-Dist: numpy>=1.26; extra == 'study'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # obey
33
+
34
+ **Unit tests for AGENTS.md.** obey turns the rules in your agent instruction files into checks, runs your coding agent
35
+ on small tempting tasks in a sandbox, measures which rules it actually follows from what it did, and compiles the ones it
36
+ ignores into hooks for Claude Code, Codex CLI, Cursor, Gemini CLI, Copilot CLI and OpenCode.
37
+
38
+ [![CI](https://github.com/B0yko/obey/actions/workflows/ci.yml/badge.svg)](https://github.com/B0yko/obey/actions/workflows/ci.yml)
39
+ [![Release](https://img.shields.io/github/v/release/B0yko/obey)](https://github.com/B0yko/obey/releases)
40
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
41
+
42
+ **On tempting tasks, Claude Haiku 5.5 in Claude Code and OpenCode followed the rule under test in all 1,474 exercised
43
+ runs where it was in AGENTS.md, against 81% without it (stratified difference +17.6 points [95% CI 4.9–34.1]); the
44
+ phrasing (imperative, prohibition, or with a reason) made no measurable difference, since every phrasing was at
45
+ 100%. Hooks without the rule raised compliance from 34% to 100% for the rules agents broke, and in 0 of 123 blocked
46
+ runs did the agent break the rule another way.** This is probe compliance, measured after correcting 136 checker
47
+ false positives (94% before the correction); see [how it was measured](study/REPORT.md).
48
+
49
+ <picture>
50
+ <img alt="obey demo replaying recorded runs: per-rule compliance and the before/after table with hooks" src="docs/images/demo.svg">
51
+ </picture>
52
+
53
+ ```bash
54
+ uvx --from git+https://github.com/B0yko/obey obey demo
55
+ ```
56
+
57
+ The demo needs no API key and no Docker: it replays agent runs recorded in the study and scores them again.
58
+
59
+ ## Quickstart
60
+
61
+ In your repository (Python 3.10+, git, [uv](https://docs.astral.sh/uv/)):
62
+
63
+ ```bash
64
+ uvx --from git+https://github.com/B0yko/obey obey init # extract rules into obey.yaml and create probes
65
+ uvx --from git+https://github.com/B0yko/obey obey lint # no agent runs: checks rules, hashes, enforcement; writes the badge
66
+ ```
67
+
68
+ Read `obey.yaml`: every rule has a checker, or a status that says why not (`uncheckable` for rules that need taste,
69
+ `proposed` for LLM suggestions you have to approve). Then either measure or enforce:
70
+
71
+ ```bash
72
+ # enforce now, no runs needed: hooks for every agent plus a git pre-commit check
73
+ uvx --from git+https://github.com/B0yko/obey obey enforce --dry-run
74
+ uvx --from git+https://github.com/B0yko/obey obey enforce
75
+
76
+ # measure: needs Docker (or colima) and an OpenRouter key; the key never enters the sandbox
77
+ uvx --from git+https://github.com/B0yko/obey obey doctor --build-image # once: the sandbox image, a few GB
78
+ export OPENROUTER_API_KEY=...
79
+ uvx --from git+https://github.com/B0yko/obey obey run --estimate --model anthropic/claude-haiku-5.5
80
+ uvx --from git+https://github.com/B0yko/obey obey run --model anthropic/claude-haiku-5.5 --budget 2
81
+ uvx --from git+https://github.com/B0yko/obey obey enforce --from .obey/report.json # hooks for the rules it broke
82
+ cp .obey/report.json before.json
83
+ uvx --from git+https://github.com/B0yko/obey obey run --compare before.json --model anthropic/claude-haiku-5.5
84
+ ```
85
+
86
+ ### Test your AGENTS.md in 10 minutes
87
+
88
+ ```bash
89
+ uvx --from git+https://github.com/B0yko/obey obey run --model anthropic/claude-haiku-5.5 --trials 3 --baseline-trials 1 --budget 2
90
+ ```
91
+
92
+ On the demo repository (11 checkable rules with probes) this made 44 runs of Claude Code 2.1.309 in 9 min 20 s and
93
+ cost $0.38 (measured on 2026-10-24 on a Mac Studio M4 Max, 128 GB, Docker in colima, 4 runs at a time;
94
+ `eval/results/quickstart-run.json`). It prints
95
+ one line per rule and writes `report.html`, `report.md` and `report.json`. The defaults (5 runs per rule plus 3 with
96
+ the rule removed) double the runs and narrow the intervals.
97
+
98
+ ## How it works
99
+
100
+ ```mermaid
101
+ flowchart LR
102
+ A["AGENTS.md, CLAUDE.md, GEMINI.md,<br/>.cursor/rules, Copilot files"] --> B["obey extract<br/>pattern library (+ reviewed LLM proposals)"]
103
+ B --> C["obey.yaml<br/>rules with checkers"]
104
+ C --> D["obey probe<br/>a tempting task per rule"]
105
+ D --> E["obey run"]
106
+ subgraph S["sandbox (one container per run)"]
107
+ E --> F["coding agent, headless"]
108
+ F -->|"model calls, dummy token"| G["gateway<br/>holds the key, meters cost"]
109
+ F -->|"npm, PyPI only"| H["egress proxy"]
110
+ end
111
+ F --> I["commands, diff, commits"]
112
+ I --> J["deterministic checkers"]
113
+ J --> K["report: compliance per rule,<br/>no-rule baseline, evasions"]
114
+ C --> L["obey enforce"]
115
+ L --> M["hooks for 6 agents<br/>+ git pre-commit"]
116
+ M --> E
117
+ ```
118
+
119
+ - **Extract.** obey reads AGENTS.md (nested files apply to their directory), CLAUDE.md with `@imports`, GEMINI.md,
120
+ Cursor `.mdc` rules (globs, `alwaysApply`) and Copilot instruction files (`applyTo`), and maps each rule to one of
121
+ twelve deterministic checkers: forbidden or required commands, forbidden paths, paired paths (tests with code), new
122
+ dependencies, forbidden or required content in added lines, commit messages, commits, pushes, file names and the
123
+ final message. Rules that need judgement stay `uncheckable`. `--llm` asks any OpenAI-compatible model about the rest
124
+ and writes its answers as `proposed`.
125
+ - **Probe.** Each rule gets a small task where breaking it is the easy route: add a package (package manager rule),
126
+ rename a column (migration rule), type an untyped payload (`any` rule), "let me know when it's done" (tests rule).
127
+ - **Run.** The agent runs headless several times per probe, plus a no-rule baseline arm with the rule removed, in a
128
+ fresh container. Only `git ls-files` enters the container. Egress goes through an allow-listing proxy; the model key
129
+ stays in a gateway outside the container, which enforces per-run and total spend caps.
130
+ - **Score.** Checkers read what the agent did (commands, diff, commits), never what it said. A run that never touched
131
+ a rule's domain is `not_exercised` and excluded; compliance comes with a Wilson 95% interval and the exercised share.
132
+ - **Enforce.** `obey enforce` writes hooks that call one standard-library rule engine, the same code the checkers use,
133
+ so a hook blocks exactly what the checker measures. `obey run --compare` measures again and counts evasions: runs
134
+ where a hook blocked the direct route and the rule was still broken another way.
135
+
136
+ ## What obey supports
137
+
138
+ | | Claude Code | Codex CLI | OpenCode | Copilot CLI | Gemini CLI | Cursor |
139
+ |---|---|---|---|---|---|---|
140
+ | Version verified (2026-10-23) | 2.1.309 | 0.164.0 | 1.19.4 | 1.0.103 | 0.65.0 | docs only |
141
+ | Instruction files it reads | CLAUDE.md, AGENTS.md if there is no CLAUDE.md | AGENTS.md | AGENTS.md, else CLAUDE.md | AGENTS.md, CLAUDE.md, GEMINI.md, Copilot files | GEMINI.md | `.cursor/rules`, AGENTS.md |
142
+ | `obey run` (headless, sandboxed) | yes, live | yes, live | yes, live | yes, live (bring your own key) | yes, needs a paid Gemini key; not run | no: no custom endpoint |
143
+ | Rules loaded, decoy global config ignored | verified live | verified live | verified live | verified live | — | — |
144
+ | Enforcement written by `obey enforce` | `.claude/settings.json` | `.codex/hooks.json` | `.opencode/plugins/obey.ts` | `.github/hooks/obey.json` | `.gemini/settings.json` | `.cursor/hooks.json` |
145
+ | Hook blocks a shell command | live | live | live | live | contract tests | contract tests |
146
+ | Can block finishing (tests, paired paths) | yes | yes | no (advisory) | yes | yes | no (advisory) |
147
+
148
+ "Live" means verified with the real CLI in the sandbox (`eval/contract_check.py`, with a stand-in hook script in the
149
+ files and format `obey enforce` writes); "contract tests" means the generated hook was tested against payloads written
150
+ from the vendor's documentation. Every target also gets the git pre-commit
151
+ check. Codex CLI loads project hooks only after you trust them (`/hooks`); Copilot CLI's prompt mode needs
152
+ `--trust-folder` or `GITHUB_COPILOT_PROMPT_MODE_REPO_HOOKS=true`.
153
+
154
+ Details, flags and isolation checks: [docs/agents.md](docs/agents.md). Checker definitions:
155
+ [docs/checkers.md](docs/checkers.md). Enforcement targets: [docs/enforce.md](docs/enforce.md).
156
+
157
+ ## Configuration
158
+
159
+ `obey.yaml` is the reviewed list of rules ([reference](docs/config.md)). Environment variables:
160
+
161
+ | Variable | Used by | Meaning |
162
+ |---|---|---|
163
+ | `OPENROUTER_API_KEY` or `OBEY_API_KEY` | `obey run` | provider key; stays in the gateway, outside the sandbox |
164
+ | `OBEY_LLM_API_KEY`, `OBEY_LLM_BASE_URL`, `OBEY_LLM_MODEL` | `obey extract --llm` | any OpenAI-compatible endpoint |
165
+ | `OBEY_DOCKER` | `obey run` | docker command (for example `podman`) |
166
+ | `GITHUB_TOKEN` | `obey study survey` | GitHub code search |
167
+
168
+ ### In CI and pre-commit
169
+
170
+ ```yaml
171
+ # .github/workflows/obey.yml: lint mode needs no secrets
172
+ permissions:
173
+ contents: read
174
+ jobs:
175
+ obey:
176
+ runs-on: ubuntu-latest
177
+ steps:
178
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
179
+ - uses: B0yko/obey@v0.1.0
180
+ ```
181
+
182
+ `mode: run` (with `api-key`, `model` and `budget`) runs the agent; it spends money and needs a secret, so the action
183
+ refuses it on pull requests: run it on `workflow_dispatch` or a schedule only.
184
+
185
+ ```yaml
186
+ # .pre-commit-config.yaml (after `obey enforce --targets precommit`)
187
+ repos:
188
+ - repo: https://github.com/B0yko/obey
189
+ rev: v0.1.0
190
+ hooks:
191
+ - id: obey
192
+ - id: obey-commit-msg
193
+ ```
194
+
195
+ The badge: `obey lint` and `obey run` write `obey-badge.json` in the
196
+ [shields.io endpoint format](https://shields.io/badges/endpoint-badge). Commit it and use
197
+ `https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/<you>/<repo>/main/obey-badge.json`.
198
+
199
+ ## Results
200
+
201
+ The study's agent runs were made for this release on 2026-10-23 and 2026-10-24 (UTC) on a Mac Studio M4 Max, 128 GB
202
+ (Docker in colima), with `anthropic/claude-haiku-5.5` through OpenRouter in Claude Code 2.1.309 and OpenCode 1.19.4.
203
+ The survey and the extraction and checker evaluations involve no agent runs.
204
+ Full report: [study/REPORT.md](study/REPORT.md). Pre-registration: [study/PREREGISTRATION.md](study/PREREGISTRATION.md).
205
+ Every change after it: [study/DEVIATIONS.md](study/DEVIATIONS.md).
206
+
207
+ ### What public AGENTS.md files say (600 repositories)
208
+
209
+ - Median 7 rules per file (mean 21.6, 90th percentile 66, max 193); 102 of 600 files contain no directive at all.
210
+ - 23.4% of 12,977 rules are mechanically checkable by obey's deterministic extractor; 19.9% could be enforced before
211
+ the action by a pre-tool hook, another 3.4% only at stop or commit time. An LLM pass would call more of them
212
+ checkable, up to 49.5% (unreviewed proposals, so an upper bound).
213
+ - 13% of the repositories (78) have hook configuration in the tree: git hooks (pre-commit, husky, lefthook) in 60,
214
+ agent hooks in 22. The survey sees the configuration files, not what the hooks check.
215
+
216
+ ### Does the agent follow the rule, and does phrasing matter? (2,241 runs)
217
+
218
+ Two repositories authored for the study, ten rule types, each tested with the rule removed (no rule) and in three
219
+ phrasings, 14 runs per cell. This is **probe compliance**: on small tasks built so that breaking the rule is the easy
220
+ route, not compliance in everyday use.
221
+
222
+ <picture>
223
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/compliance-by-phrasing-dark.svg">
224
+ <img alt="Probe compliance by phrasing and harness: about 80% with no rule, 100% with the rule in any phrasing" src="docs/images/compliance-by-phrasing-light.svg">
225
+ </picture>
226
+
227
+ | Condition | As registered | Corrected | Exercised runs |
228
+ |---|---|---|---|
229
+ | no rule | 73% [68–76] | 81% [78–85] | 488 of 560 |
230
+ | imperative ("Use pnpm …") | 95% [92–96] | 100% [99–100] | 499 of 560 |
231
+ | prohibition ("Never …") | 92% [89–94] | 100% [99–100] | 478 of 560 |
232
+ | imperative with a reason | 94% [92–96] | 100% [99–100] | 497 of 560 |
233
+
234
+ - **The rule works when it is there.** With the rule in AGENTS.md the agents broke it in none of 1,474 exercised runs
235
+ (corrected verdicts); without it, compliance was 81%. The pre-registered test on the corrected verdicts:
236
+ +17.6 percentage points [95% CI +4.9 to +34.1], p < 0.001 (on the verdicts as registered: +20.1 [+6.8 to +36.5]).
237
+ The rules that changed behaviour were the ones agents break by default: "leave changes uncommitted" (1 of 56 runs
238
+ without the rule, 168 of 168 with it), "leave pushing to the maintainers" (42 of 55, 168 of 168), Conventional
239
+ Commit messages (0 of 15 runs that committed without the rule, 20 of 20 with it), and the ban on `print()` in
240
+ `src/` (21 of 28, all 7 violations by Claude Code; the ban on `: any` was never broken). Five of the ten probes
241
+ tempted nobody: agents followed those rules in every run even when they were absent.
242
+ - **Phrasing did not matter** on the corrected verdicts: every phrasing was followed in every exercised run. On the
243
+ verdicts as registered, the prohibition scored about 2 points lower (significant after Holm's correction), but every
244
+ violation behind that result was a checker false positive (below).
245
+ - **The checkers were wrong 136 times.** Reading the runs showed three false-positive patterns in the registered
246
+ checkers: `pnpm install --frozen-lockfile 2>&1` read as adding a package called `2>&1` (109 runs); the generator of
247
+ `src/generated/` run through its package script `pnpm gen:types` scored as a hand edit (25 runs); and the same
248
+ generator scored as failed because a later command on its line failed (2 runs). The checkers scored 1.0 precision
249
+ on the authored held-out cases, which did not contain these forms. All 136 went from violated to obeyed, so the
250
+ 1,474 obeyed runs were also re-checked with simple logic that does not use obey's engine (`eval/audit_obeyed.py`):
251
+ no missed violation. Both analyses are in the report; the fixes are in this release.
252
+
253
+ ### Do hooks hold when the rule is not there? (182 runs counted)
254
+
255
+ Every rule type that agents broke without the rule was compiled by `obey enforce` into a hook for that agent plus the
256
+ git pre-commit check, and the same probes were run again, still without the rule in AGENTS.md: the agent learns the
257
+ rule only when a hook refuses it.
258
+
259
+ <picture>
260
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/enforcement-before-after-dark.svg">
261
+ <img alt="Probe compliance without the rule, before and with hooks, for the five rule types agents broke" src="docs/images/enforcement-before-after-light.svg">
262
+ </picture>
263
+
264
+ - In the 13 cells (rule type × repository × agent) that agents broke without the rule, compliance went from 34%
265
+ [27–42] to 100% [97–100] with hooks (+65 points, p < 0.001, stratified permutation test).
266
+ - **Evasions: 0 of 123** runs in which a hook blocked at least once broke the rule another way by the end
267
+ (0% [0–3]). In the commit-message cells, 12 of 56 runs committed, all with a conforming message (without hooks: 15
268
+ of 56 committed, none conforming). In 2 of the 13 cells (`git push` and `npm` for Claude Code) no hook fired: the
269
+ agent did not break the rule in those runs, so their gain is not a hook effect.
270
+ - Because no rule was ever broken while it was in AGENTS.md (corrected verdicts), the pre-registered version of this
271
+ measurement, hooks on top of the rule, had nothing to measure. 28 further runs in two cells that only a checker
272
+ false positive had selected are listed in the report and not counted.
273
+
274
+ ### The tool itself
275
+
276
+ - Rule extraction, held-out set of 15 authored files (136 labelled rules, evaluated once): recall 0.70 [0.60–0.79],
277
+ precision 0.96 [0.88–0.98] for active rules; parameters right for 0.77 [0.65–0.85]. On the 25 development files:
278
+ recall 0.98, precision 1.0.
279
+ - Checkers, held-out set of 442 labelled runs (frozen before the checkers existed): precision and recall 1.0 for every
280
+ type at release (`dependency_unchanged` accuracy 36 of 37 after a definition change); the first evaluation found
281
+ three bugs (`content_required_added` precision 0.94, `dependency_unchanged` recall 0.94), fixed and listed in
282
+ `fixtures/checkers/CHANGES.md`, so the release numbers for those two types are no longer an unbiased estimate. See
283
+ the false positives above for what authored cases miss.
284
+ - Live checks with the real CLIs in the sandbox: each of Claude Code, Codex CLI, OpenCode and Copilot CLI loaded the
285
+ repository's AGENTS.md, ignored a decoy global instruction file, and was stopped by a pre-tool hook installed in the
286
+ files and format that `obey enforce` writes, with a stand-in hook script (`eval/contract_check.py`, 8 of 8 probes,
287
+ $0.013, `eval/results/contract-check.json`). The hooks that `obey enforce` generates ran in the study's enforcement
288
+ runs for Claude Code and OpenCode. Locally, Claude Code 2.1.308 with an `obey enforce` hook was refused
289
+ `npm install date-fns` in 14 ms, and the hashed files in `~/.claude` (`settings.json` and the plugin files) were
290
+ unchanged (`eval/results/live-claude-code.json`).
291
+ - Hook overhead: each hook call is a fresh Python process. 95th percentile 31–32 ms with Python 3.14 and 44–45 ms
292
+ with macOS's system Python 3.9 (200 calls per agent format, 11 rules, Mac Studio M4 Max,
293
+ `eval/results/hook-latency.json`).
294
+ - Cost of the study: $20.22 for 2,663 agent runs (pilot $1.28, main grid $17.05, part 3 $1.88), metered by obey's
295
+ gateway, plus $0.34 for the survey's LLM pass, from its own spend ledger.
296
+
297
+ Reproduce: `uv run obey study report` rebuilds the report and charts from the committed summaries;
298
+ `uv run python eval/rescore.py` re-scores the raw runs (attached to the release); `uv run python eval/run_eval.py`
299
+ re-runs the extraction and checker evaluations. Running the study again needs Docker, an OpenRouter key and about
300
+ $20 of model calls at October 2026 prices (commands in `docs/study.md`).
301
+
302
+ ## Privacy and security
303
+
304
+ - **During `obey run`, your repository's tracked files and the probe tasks go to the model provider you configure**
305
+ (by default OpenRouter, which routes to the model's vendor). Only files tracked by git enter a run: untracked and
306
+ ignored files, such as an untracked `.env`, never do; a tracked `.env` would.
307
+ - The key stays in obey's gateway, outside the agent's container; agents get a dummy token. The gateway accepts only
308
+ that token, forwards only the model endpoints of each API, pins the model, and stops a run at its spend cap.
309
+ - Agent containers have no route to the internet except an allow-listing proxy (by default npm, PyPI, Go and crates
310
+ registries) and the gateway; every egress attempt is logged. No host path is mounted.
311
+ - `--unsafe-local` (for machines without Docker) runs the agent directly on your machine in a temporary copy with an
312
+ empty `HOME`, no git remotes, no credential variables and no git credential helpers, with the agent's own sandbox
313
+ switched on where it has one (Claude Code, Codex CLI, Gemini CLI). Commands the agent runs can still reach the network
314
+ and anything your user can read, so obey prints a warning and refuses "never commit" and "never push" probes there.
315
+ - Subscription logins: obey's study used API keys only. Anthropic's Consumer Terms forbid accessing its services
316
+ "through automated or non-human means" except with an API key or where Anthropic explicitly permits it
317
+ ([terms](https://www.anthropic.com/legal/consumer-terms)); check your vendor's terms before running agents on a
318
+ subscription login.
319
+ - `obey enforce` writes only inside your repository. Generated hooks use only the Python standard library and fail
320
+ open (with a visible warning) on internal errors.
321
+
322
+ ## Limitations
323
+
324
+ - Two demo repositories, ten rule types, one inexpensive model in two harnesses, specific agent versions and dates.
325
+ Other models, agents and repositories may behave differently.
326
+ - Probe compliance is compliance on tempting tasks written to make breaking the rule easy. It is not the compliance you
327
+ would see in everyday use.
328
+ - With the rule present, compliance was at the ceiling (100%) for every phrasing, so the phrasing experiment cannot
329
+ tell phrasings apart for this model and these rules; a model or rule that is broken more often might show a
330
+ difference. Five of the ten probes did not tempt the agent even without the rule.
331
+ - The corrected verdicts were decided after the outcomes were known (study/DEVIATIONS.md). Each correction is a false
332
+ positive visible in the recorded commands, and the registered analysis is reported next to it. Every correction
333
+ went from violated to obeyed; an independent re-check of the obeyed runs found no missed violation, but it shares
334
+ the study's view of what each rule means.
335
+ - Checkers can be wrong on real runs in ways authored test cases miss: the study found 136 false positives (6% of
336
+ runs) in three patterns. Read the evidence obey prints for a violation before acting on it.
337
+ - In the enforcement runs the hook's refusal quotes the rule, so "hooks without the rule" still tells the agent the
338
+ rule at the moment it is blocked.
339
+ - Extraction recall on real instruction files is measured only on the authored sets (held-out recall 0.70). Read
340
+ `obey.yaml` before trusting it; the survey's "checkable" share depends on the same extractor.
341
+ - The survey reflects what public AGENTS.md files say, sampled through GitHub code search (relevance-ranked within
342
+ size slices), not what agents do with them.
343
+ - Checkers see actions, so rules about taste, design or communication stay `uncheckable`, and some checkers read the end
344
+ state only: an edit made and reverted inside one run does not count for content rules.
345
+ - Hooks are a guardrail, not a security boundary: an agent can still change files through routes a pre-tool hook does
346
+ not see. `obey run --compare` measures exactly that as evasions; the pre-commit check and the stop hooks catch some of
347
+ it at the end.
348
+ - Linux and macOS only. Windows is not supported in 0.1.
349
+
350
+ ## Alternatives
351
+
352
+ obey is not the first tool to compile instruction files into checks or to test them; this is how the closest ones
353
+ differ (checked on 2026-10-23):
354
+
355
+ - [hookify](https://github.com/anthropics/claude-code/tree/main/plugins/hookify) (Anthropic's Claude Code plugin)
356
+ writes regex rules that warn or block tool calls, from an instruction or from corrections mined from a conversation.
357
+ It does not measure compliance.
358
+ - [claudecode-rule2hook](https://github.com/zxdxjtu/claudecode-rule2hook) turns plain-English rules, or rules read
359
+ from CLAUDE.md, into Claude Code hook configuration. No measurement.
360
+ - [ContextCov](https://arxiv.org/abs/2603.00822) compiles AGENTS.md, CLAUDE.md and Copilot instructions into source,
361
+ import-graph, LLM-reviewed and command checks, and reports 88.3% constraint compliance on SWE-bench Lite against
362
+ 67.0% for a prompt-only baseline. It evaluates on SWE-bench tasks rather than per-rule tempting tasks.
363
+ - [AgentGuard](https://arxiv.org/abs/2609.16287) learns guardrail rules from failure traces of coding agents rather
364
+ than from your instruction file.
365
+ - [nunchi](https://github.com/seob717/nunchi) ([article](https://braindetox.kr/en/posts/claude_md_hooks_compile_2026.html))
366
+ compiles CLAUDE.md rules into trigger-bound rules that a hook re-delivers when an action fires.
367
+ - [agents-md-evals](https://github.com/vltansky/agents-md-evals) A/B tests a Claude instruction file present versus
368
+ removed, using assertions from the repository's history.
369
+ - [ruleProb](https://github.com/canblmz1/ruleProb) extracts rules from CLAUDE.md, AGENTS.md and Cursor rules, builds
370
+ sandbox scenarios, runs a provider against each and scores commands, protected writes and code patterns; it is the
371
+ closest in purpose. obey differs in its probes-as-temptations with a no-rule baseline, per-rule rates with intervals
372
+ from actions across five agent CLIs, enforcement for six agents from the same engine, and evasion counts.
373
+ - [rulecheck](https://github.com/Atlas-RD-Research/rulecheck) tests what a model says it would do in written
374
+ scenarios, not its actions.
375
+ - Static linters such as [agnix](https://github.com/agent-sh/agnix),
376
+ [agentlinter](https://github.com/seojoonkim/agentlinter), [alint](https://github.com/alint-dev/alint),
377
+ [agent-compass](https://github.com/YoavLax/agent-compass), [agent-ready](https://github.com/agent-next/agent-ready)
378
+ and [Factory Agent Readiness](https://docs.factory.com/agent-readiness/overview) check instruction and configuration
379
+ files without running an agent.
380
+ - For "tests pass" and "deployed" claims in Claude Code,
381
+ [proof-of-done](https://github.com/B0yko/proof-of-done) is a fuller stop-hook check than obey's
382
+ `command_required` hook.
383
+
384
+ ## Roadmap
385
+
386
+ - Antigravity CLI, once it has a documented headless mode with an API key.
387
+ - Cursor CLI runs (it has no custom model endpoint today, so the key would have to enter the sandbox).
388
+ - More probe templates per rule type, and probes adapted to the repository with `--llm`.
389
+ - Judging uncheckable rules is out of scope for 0.1 on purpose; obey stays deterministic.
390
+
391
+ ## License
392
+
393
+ Apache-2.0. Copyright 2026 Andrii Boiko.
@@ -0,0 +1,90 @@
1
+ obey_cli/__init__.py,sha256=VsOTgMzDpR08Hxdj30gvYnGPqvhsVus_2fxYf0EqQ58,75
2
+ obey_cli/__main__.py,sha256=a5k3_7btNclOmMbt4rpOSeabjjvov044jxWu0lrYjgc,56
3
+ obey_cli/cli.py,sha256=QLEl27_zNIj_MFHAJh52YzjAXxZQ36mUL9icHg9zSks,9784
4
+ obey_cli/commands.py,sha256=YCfAWTWkNN_tgrreGcVdR3eYpeC0Lc9MBW6cdJjTfGc,27047
5
+ obey_cli/config.py,sha256=m4zbLKH0UTOl4BuiCZjPNuYk9mocZ8GolEQ1PEWO38E,2693
6
+ obey_cli/discover.py,sha256=ljA--tGiAPYUx6p8YiNpXIbEMRbNTncOIHhQKOjJlb8,8482
7
+ obey_cli/extract.py,sha256=RHcQ2HMju_ZUXMNKW2IAg7pGT8kNh4kGlJKQYlHOwCo,55012
8
+ obey_cli/lint.py,sha256=-wfUX4lUUZ6qoCAiPKY3_GyHOuKG0u-8gwvf3NnO4Nc,5119
9
+ obey_cli/llm.py,sha256=LFcFQMHQMjSJmIndCEfsMh33_X9AUw_UrYAe6coC0wE,4632
10
+ obey_cli/parse_md.py,sha256=MiGLA7wEnwnj_S6LJEU5l5JI5iJ66JGkxJYHf8Ah7vU,10020
11
+ obey_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ obey_cli/resources.py,sha256=_b9kXnI-bc_rmauGObsoMmppMVPskXfVeimI__CyWoc,1140
13
+ obey_cli/rules.py,sha256=_tUdOtxzaGXP_VXA9CY3rdSB4hQpGp5WIUUvcf9LmTQ,2758
14
+ obey_cli/score.py,sha256=rpEDONxkgN7YQRSvx0Xg-J7rdiQr3mhI54DmK2lAJZo,5463
15
+ obey_cli/checkers/__init__.py,sha256=AAmPTYR3P4L5kjONLyIVUTfjfi6kSx7qOFklrdmCDco,1031
16
+ obey_cli/checkers/samples.py,sha256=gr1GkJsv-m5BrocSJYeLJBNgMNXL-Iok5X1RC2kboTg,3884
17
+ obey_cli/enforce/__init__.py,sha256=dKUPN6mL-1RSnhF2XL4IgJYOGMYfHXX2WhGpqDZhkx0,4450
18
+ obey_cli/enforce/merge.py,sha256=4vrvWGfuxs4wGUO38a5Tc83bw3hQTslIqPdje6l4ulc,8113
19
+ obey_cli/enforce/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ obey_cli/enforce/engine/obey_engine.py,sha256=qmFZmDAr29reWvIiElvJ1I1dS2LsuMW8TJUUSsLgZQw,76538
21
+ obey_cli/enforce/targets/__init__.py,sha256=aX0gajFZBO_tHP0r2ivFfPwkz3-9hxAvxJLtwykccPg,12084
22
+ obey_cli/enforce/templates/obey_hook.py,sha256=3c-imHKgyy_6zriAUkdA3KH_xjs90D8aZZbNNKmh_Pg,18327
23
+ obey_cli/enforce/templates/pre_commit.py,sha256=acv7DGhjYmdlT5_UZGf006Kfk__whtPC52sfpM_lSh8,4276
24
+ obey_cli/probes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ obey_cli/probes/facts.py,sha256=dnOJwCqgn-P3MgPsttL7wixGl1Iux0vV1QchCGjfo-M,5939
26
+ obey_cli/probes/templates.py,sha256=UcxmhuFAm5h1bSyjc8tAV4R4c7UYU1X0Dm1lP5NdbA8,12012
27
+ obey_cli/report/__init__.py,sha256=Si3Oe5u-t5aiyE6lJL3Bqt9mmcqvdFY2nGa6BTgVQLY,14408
28
+ obey_cli/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ obey_cli/runner/core.py,sha256=3A_5h1GUKnbxi2EmFYzFJSNmX_MHMFxMGVrfU7zmgmU,5772
30
+ obey_cli/runner/egress_proxy.py,sha256=F6pB86cp9ua458Wk6C9np4904zUf-puhTUk_jRAY4HM,6764
31
+ obey_cli/runner/gateway.py,sha256=ybesX2XEzHLE03yegJBuHkBt1J4T5Bd84bLOxWB0cC8,23039
32
+ obey_cli/runner/ledger.py,sha256=b_K90OMbepiinE_ovDVZs9kAnYVAyzln-RbktRV--kk,4701
33
+ obey_cli/runner/local.py,sha256=blFI9obrB28Ry9bteRkNUj5-1qvc0uZvea1AIxGTDdI,6738
34
+ obey_cli/runner/orchestrate.py,sha256=0AXzIHnh4cbDWlg7wpISo7tm_fTOV5OU9YUpiewWwok,8415
35
+ obey_cli/runner/sandbox.py,sha256=TAC69tMAX-6tDClCGS6pjiFy0A8MqwNfysqFXfrjfyI,12954
36
+ obey_cli/runner/sidecar.py,sha256=uyOciommUP5ITsWftm7z_LhsYTlS-cg-hVE-1AuRRf4,1277
37
+ obey_cli/runner/workspace.py,sha256=9EyGPUlhBPMy7w_LHF2f2RMaMtjQXB3S6ibIKCXg06s,7461
38
+ obey_cli/runner/agents/__init__.py,sha256=zFI-3Qyw3aG09p3vQ8jiw3JL8IN_o8oyEOS0t0e40Wk,778
39
+ obey_cli/runner/agents/base.py,sha256=-qMbz0kMCbkSXluo5SDJ8duZU4TybRBFmPlgPc9VJp0,3114
40
+ obey_cli/runner/agents/claude_code.py,sha256=wDbaYq_ojbwim640Tmy2NK_oYFLDBKwfLHfH079fUXk,9165
41
+ obey_cli/runner/agents/codex.py,sha256=xWSPcTgeJ72bjq4Vd_-Myiz4lp3Ey6pbopSIgH-p8VE,6049
42
+ obey_cli/runner/agents/copilot.py,sha256=pw5WhujHq-s3TOAc1xsW0y-wkr3NSVju0A9Cc6iY98k,5727
43
+ obey_cli/runner/agents/gemini.py,sha256=RDkm8Nq73vjztkV_nsECZ6i4onxvKNhV7BmD-L9tD1M,5641
44
+ obey_cli/runner/agents/opencode.py,sha256=MwVSqwNrJjbJ1Hb1fjPUI52jXbHhbOXEeEMH_GsC4Cc,6266
45
+ obey_cli/runner/sandbox/Dockerfile,sha256=bYg1qBFdZSUrcS9eWDsP6r8dYtrTgBSZSqbkkGs_DyA,2024
46
+ obey_cli/runner/sandbox/entrypoint.sh,sha256=4xrsIAqM-lGrD5z5Ffp9Xjb-kakB9bdRYGeWsth1QSA,1488
47
+ obey_cli/study/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ obey_cli/study/analysis.py,sha256=bPomGmgzJvQPf56RCF7i6Madc83vmA9oQ8_uADFKBCM,10558
49
+ obey_cli/study/design.py,sha256=G5VuYgI_Y8LMuzRbctgOKRbQq8YPKW308-13JTzIWNM,3204
50
+ obey_cli/study/experiment.py,sha256=mVUcBVh1ZoVOhfWhc7D0hzOEmd2Qh5m3MoUuOASU7zo,7632
51
+ obey_cli/study/report.py,sha256=34EA01i_2Mx79jibBc8gsip3p0YlmuHDd6g0YAGDklg,23640
52
+ obey_cli/study/survey.py,sha256=4_bVBC60xyhsCQCWmW1_SJii745ncZ4EBaOoayMe9Ao,18710
53
+ obey_cli/_data/demo/README.md,sha256=_4qu90DC-fO44qUeI120GnOlh09bKbf6W4Txmf_eyrA,712
54
+ obey_cli/_data/demo/obey.yaml,sha256=eEDRLrl-KYXIhSaVgzPt1dbJkO6scGNpcaX-gBYrhMk,7098
55
+ obey_cli/_data/demo/runs/after.jsonl.gz,sha256=oG2hKUy4fc8EWd0_ESkH4BXGIt51sn9QZUlLmwhmHqQ,87642
56
+ obey_cli/_data/demo/runs/before.jsonl.gz,sha256=jifsH8VBSOZPa8it1ZkUHNQDV3TkbjSGdRDELdvDnJo,680866
57
+ obey_cli/_data/demo/runs/meta.json,sha256=LMfyJFvcDh1rbhGoWTv02Zul1Xm8gP_jik0MD3UjAvQ,255
58
+ obey_cli/_data/schemas/obey-config-v1.json,sha256=hpwN_DTvWO1h3IBsO2LUA8C8WtCFwfInmbo43Gq-Jas,7212
59
+ obey_cli/_data/schemas/report-v1.json,sha256=AYyr0W-8zZEhM0m5dTavkeOBVGe1C3GodNOR3XcHEVI,3425
60
+ obey_cli/_data/schemas/survey-record-v1.json,sha256=mQJXcsNocNB9NZLwQe0h7Nut_UnfI6P-0qfr0hqxPSQ,1519
61
+ obey_cli/_data/demo/repo/.gitignore,sha256=2VJ-tlDM7Vkt6nRgrVeXnwf245cQJwmq12jfzVktZn4,79
62
+ obey_cli/_data/demo/repo/AGENTS.md,sha256=0gCUNvLOntyhK3TsvitmSIqmCyosQIGDq_RYSoyOhhg,1931
63
+ obey_cli/_data/demo/repo/README.md,sha256=N-9EMBdmnB9fIu0Kp00GTo9jU7iWwtOBB8ciFQ3lDC4,1306
64
+ obey_cli/_data/demo/repo/eslint.config.js,sha256=_JFcGKYRYqZronV1FQkPCe6fJavaX0Q5Hca6J2219gs,645
65
+ obey_cli/_data/demo/repo/index.html,sha256=eN_ifIDL3ANyYQKDbRqfm6Mkce4ylL9_hPlMyStQWsA,820
66
+ obey_cli/_data/demo/repo/openapi.yaml,sha256=I4Q1p-fjlBxhR2ESjMHnOKQhph2Jmw7dUzwDu48Ssbc,1893
67
+ obey_cli/_data/demo/repo/package.json,sha256=xxiNYfF1V20ojLcOGxHpd5I66ABXmHLog1_2rQbZ7cY,642
68
+ obey_cli/_data/demo/repo/pnpm-lock.yaml,sha256=jpmiWos8kxSNadheYXAfoA7JnHzIUC60pu3Tkjgrk9g,47455
69
+ obey_cli/_data/demo/repo/tsconfig.json,sha256=Ge8qajs14bqm-J7J686SlDwNmcHpkSVoi-FJUtk5geY,471
70
+ obey_cli/_data/demo/repo/vitest.config.ts,sha256=3mEVlspraFR6VvWsaiaWKQzNRZsymLVVS98YQSPTPs0,170
71
+ obey_cli/_data/demo/repo/scripts/gen-types.mjs,sha256=ZCnCliaA_Du1Irgyd8qXIubKAgvqo_NDxV9Z01Ulrg4,3908
72
+ obey_cli/_data/demo/repo/src/date-utils.test.ts,sha256=ZuiJZF-mAW1jk0aeA8TM4TvzjbAPsNA2Zd6g5VkRYbU,1839
73
+ obey_cli/_data/demo/repo/src/date-utils.ts,sha256=hrXNHbBt0NR8qfonsHkn_2TU5GeenlasFJg4rFwBKyc,2416
74
+ obey_cli/_data/demo/repo/src/logger.test.ts,sha256=r7NtAXd9ccvwEjH12RwzcTt00MPndQ3wIGdaWWHwwKM,884
75
+ obey_cli/_data/demo/repo/src/logger.ts,sha256=5P-mVkHp3_gegbIhaLsbIsvmszzhdHq8Wp9fZmXvQMo,1259
76
+ obey_cli/_data/demo/repo/src/main.ts,sha256=C1vBa_3bWSpB3yEGjH2Y_lE_nHmbh76XQbMMqwqfBX8,1071
77
+ obey_cli/_data/demo/repo/src/render-table.test.ts,sha256=PlOLNQlqucxtFF9wfJRl1qRoRk2U-GRZ7HfuKxx72ck,1321
78
+ obey_cli/_data/demo/repo/src/render-table.ts,sha256=1VM_9vuT4IPrEw3z9PtJHEBYe1vvN_WdRUG0RqV2r4I,1983
79
+ obey_cli/_data/demo/repo/src/shift-planner.test.ts,sha256=DbBFhGDozeT91kw0zcPWDFZjxevudA12aDkKK9h47dM,2815
80
+ obey_cli/_data/demo/repo/src/shift-planner.ts,sha256=ABzStmEPz2yM-DdXhwfC6YyfQ5w1QY5tW-OcIAQQulo,3626
81
+ obey_cli/_data/demo/repo/src/api/client.test.ts,sha256=V9p7rNCZkGpiXqdsjLA7M5oE_4qbrkUq46MLbBtnsws,2773
82
+ obey_cli/_data/demo/repo/src/api/client.ts,sha256=StaktSzetuGnngVwx7K7wIGQSV6dMPDOFYmWPUgfnIQ,2353
83
+ obey_cli/_data/demo/repo/src/api/response-guards.test.ts,sha256=JFRPnmDtLg8x9a28YkrJnJ5eFEBfLFjnpSeVxUnX2PY,940
84
+ obey_cli/_data/demo/repo/src/api/response-guards.ts,sha256=-JjKJEnnWAV0SGNq7Y0IdVX6tQLHJTO5dSwAJXzAIJY,1335
85
+ obey_cli/_data/demo/repo/src/generated/api-types.ts,sha256=w4naac2o7lZbcdvIgk1uyT5BtG6XyMKb8ALv71nCcgc,689
86
+ obey_cli-0.1.0.dist-info/METADATA,sha256=lyXF5bSqkgFMFQ9cqIZPgC-l-Ht5LO5Vfp-SYXPlmN4,26256
87
+ obey_cli-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
88
+ obey_cli-0.1.0.dist-info/entry_points.txt,sha256=eybEkHHBOz1-7Y687MV78A0raXrYskZ-12TtviVyXao,43
89
+ obey_cli-0.1.0.dist-info/licenses/LICENSE,sha256=dOG3C26HOswr5z_jq6LXVJHcHFDpgheW0IvqQPedpLw,11343
90
+ obey_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ obey = obey_cli.cli:main