agents-md-compiler 0.1.0__tar.gz
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.
- agents_md_compiler-0.1.0/LICENSE +21 -0
- agents_md_compiler-0.1.0/PKG-INFO +312 -0
- agents_md_compiler-0.1.0/README.md +285 -0
- agents_md_compiler-0.1.0/pyproject.toml +177 -0
- agents_md_compiler-0.1.0/pyproject.toml.orig +244 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/__init__.py +127 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/__main__.py +6 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/_version.py +28 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/atomic.py +135 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/cli.py +1220 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/codex.py +505 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/errors.py +626 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/hashing.py +45 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/installation.py +942 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/lockfile.py +428 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/locking.py +148 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/manifest.py +379 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/models.py +529 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/paths.py +158 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/py.typed +0 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/receipts.py +825 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/rendering.py +276 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/schemas/lock-v1.schema.json +77 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/schemas/manifest-v1.schema.json +66 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/schemas/receipt-v1.schema.json +382 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/sources.py +249 -0
- agents_md_compiler-0.1.0/src/agents_md_compiler/state.py +319 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 netopsengineer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agents-md-compiler
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Compile ordered Markdown policy modules into one deterministic, verifiable global AGENTS.md.
|
|
5
|
+
Keywords: agents-md,codex,agent,policy,compiler,deterministic,provenance,cli
|
|
6
|
+
Author: netopsengineer
|
|
7
|
+
Author-email: netopsengineer <enterprise.code.developer@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.14
|
|
22
|
+
Project-URL: Homepage, https://github.com/netopsengineer/agents-md-compiler
|
|
23
|
+
Project-URL: Repository, https://github.com/netopsengineer/agents-md-compiler
|
|
24
|
+
Project-URL: Issues, https://github.com/netopsengineer/agents-md-compiler/issues
|
|
25
|
+
Project-URL: Changelog, https://github.com/netopsengineer/agents-md-compiler/blob/main/CHANGELOG.md
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# agents-md-compiler
|
|
29
|
+
|
|
30
|
+
Compile an ordered set of Markdown policy modules into one deterministic,
|
|
31
|
+
verifiable global `AGENTS.md`. No runtime dependencies, no network access, no
|
|
32
|
+
shell invocation, and no LLM anywhere in the pipeline.
|
|
33
|
+
|
|
34
|
+
## Why this exists
|
|
35
|
+
|
|
36
|
+
Coding agents read their instruction files once, at startup. Codex selects a
|
|
37
|
+
single non-empty global file, builds its project instruction chain from the
|
|
38
|
+
project root through the startup working directory, and then stops looking.
|
|
39
|
+
Changing directories later does not add instructions to a running session, and
|
|
40
|
+
Codex treats a Claude-style `@path` import as ordinary Markdown rather than as an
|
|
41
|
+
include directive.
|
|
42
|
+
|
|
43
|
+
That leaves two options for a rule that must be available before task scope is
|
|
44
|
+
known: paste everything into one enormous hand-maintained file, or generate that
|
|
45
|
+
file from modular sources. Hand-maintaining it means the file drifts from the
|
|
46
|
+
sources, nobody can tell which version is live, and a careless edit silently
|
|
47
|
+
deletes a rule.
|
|
48
|
+
|
|
49
|
+
This tool takes the second option and makes it auditable:
|
|
50
|
+
|
|
51
|
+
- canonical Markdown modules stay the only editable policy sources;
|
|
52
|
+
- one reviewed manifest fixes the module set and its order;
|
|
53
|
+
- a lock records each source's resolved path, byte size, and SHA-256;
|
|
54
|
+
- rendering emits one self-contained file with stable provenance headers and the
|
|
55
|
+
exact source bytes between generated markers;
|
|
56
|
+
- checking detects source drift, output drift, a missing target, shadowing, and
|
|
57
|
+
an unmanaged target;
|
|
58
|
+
- installation is explicit, backed up, concurrency-safe, and atomic;
|
|
59
|
+
- rollback restores the exact prior bytes, and refuses if the target changed;
|
|
60
|
+
- verification inspects the model-visible startup input instead of trusting a
|
|
61
|
+
model to say it read the rules.
|
|
62
|
+
|
|
63
|
+
It deliberately does not generate nested or project-level `AGENTS.md` files, and
|
|
64
|
+
it never detects your stack and writes rules for it. A repository may keep its own
|
|
65
|
+
root `AGENTS.md` for concrete project facts; this compiler leaves that file alone.
|
|
66
|
+
|
|
67
|
+
## Requirements
|
|
68
|
+
|
|
69
|
+
- Python 3.14 or newer.
|
|
70
|
+
- Linux, macOS, or Windows.
|
|
71
|
+
- No runtime dependencies. The package uses the standard library only.
|
|
72
|
+
- `codex` on `PATH` only for the optional `verify-codex` command.
|
|
73
|
+
|
|
74
|
+
## Install
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Recommended: install as an isolated tool.
|
|
78
|
+
uv tool install agents-md-compiler
|
|
79
|
+
|
|
80
|
+
# Or run without installing.
|
|
81
|
+
uvx agents-md-compiler --help
|
|
82
|
+
|
|
83
|
+
# Or into an existing environment.
|
|
84
|
+
pip install agents-md-compiler
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Both invocation forms are supported and equivalent:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
agents-md-compiler --help
|
|
91
|
+
python -m agents_md_compiler --help
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Five minutes end to end
|
|
95
|
+
|
|
96
|
+
Scaffold an example manifest and modules, then lock, render, check, and install.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# 1. Scaffold. The manifest targets ~/.codex/AGENTS.md by default.
|
|
100
|
+
agents-md-compiler init --directory ./policy
|
|
101
|
+
|
|
102
|
+
# 2. Create the selected target's parent if this is a new Codex home.
|
|
103
|
+
mkdir -p ~/.codex
|
|
104
|
+
|
|
105
|
+
# 3. Record exact source paths, sizes, and digests.
|
|
106
|
+
agents-md-compiler lock --manifest ./policy/global-agents.toml
|
|
107
|
+
|
|
108
|
+
# 4. Validate the manifest, lock, sources, and rendered structure.
|
|
109
|
+
agents-md-compiler validate --manifest ./policy/global-agents.toml
|
|
110
|
+
|
|
111
|
+
# 5. Look at the bytes before writing anything anywhere.
|
|
112
|
+
agents-md-compiler render --manifest ./policy/global-agents.toml --locked | head -20
|
|
113
|
+
|
|
114
|
+
# 6. Compare a fresh locked render against the target.
|
|
115
|
+
agents-md-compiler check --manifest ./policy/global-agents.toml
|
|
116
|
+
|
|
117
|
+
# 7. Preview the install. This writes nothing at all.
|
|
118
|
+
agents-md-compiler install --manifest ./policy/global-agents.toml
|
|
119
|
+
|
|
120
|
+
# 8. Install for real.
|
|
121
|
+
agents-md-compiler install --manifest ./policy/global-agents.toml --apply
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Editing a canonical module after step 3 makes `check` exit 2 with `LOCK_STALE`
|
|
125
|
+
until you rerun `lock`. That coupling is the point: the reviewed manifest, the
|
|
126
|
+
lock, and the live file always agree or the tool tells you they do not.
|
|
127
|
+
|
|
128
|
+
## Choose where output goes
|
|
129
|
+
|
|
130
|
+
Use one of three destination controls according to the operation:
|
|
131
|
+
|
|
132
|
+
- Set manifest `default_target` for the persistent managed destination. A relative
|
|
133
|
+
value resolves from the manifest directory. `~` expands to the user's home.
|
|
134
|
+
- Pass `--target PATH` to `validate`, `check`, `status`, `install`, `rollback`, or
|
|
135
|
+
`verify-codex` to override the managed destination for one invocation. A relative
|
|
136
|
+
command-line path resolves from the current working directory.
|
|
137
|
+
- Pass `render --locked --output PATH` to export one new standalone file. This
|
|
138
|
+
never replaces an existing path and does not create its parent directory.
|
|
139
|
+
|
|
140
|
+
For example, preview and then install to an explicit managed target:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
agents-md-compiler install \
|
|
144
|
+
--manifest ./policy/global-agents.toml \
|
|
145
|
+
--target ~/.codex/AGENTS.md
|
|
146
|
+
|
|
147
|
+
agents-md-compiler install \
|
|
148
|
+
--manifest ./policy/global-agents.toml \
|
|
149
|
+
--target ~/.codex/AGENTS.md \
|
|
150
|
+
--apply
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The selected target may have another file name, but only a real global
|
|
154
|
+
`AGENTS.md` can be verified through Codex startup input. `verify-codex` therefore
|
|
155
|
+
fails for an otherwise current custom target that Codex does not load globally.
|
|
156
|
+
|
|
157
|
+
## Safety behavior you should know before installing
|
|
158
|
+
|
|
159
|
+
**An existing target that this tool did not generate is never replaced
|
|
160
|
+
silently.** If the target exists but carries no recognized generated header,
|
|
161
|
+
`install --apply` refuses with `UNMANAGED_TARGET` and exit code 3. Adopting that
|
|
162
|
+
file requires both flags, and the digest must match what you captured from the
|
|
163
|
+
dry run:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Capture the exact current bytes first.
|
|
167
|
+
shasum -a 256 ~/.codex/AGENTS.md
|
|
168
|
+
|
|
169
|
+
EXPECTED_TARGET_SHA256=<the 64-character digest you just captured>
|
|
170
|
+
agents-md-compiler install \
|
|
171
|
+
--manifest ./policy/global-agents.toml \
|
|
172
|
+
--apply --replace-unmanaged \
|
|
173
|
+
--expect-target-sha256 "${EXPECTED_TARGET_SHA256}"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
If the digest does not match, the install refuses rather than overwriting a file
|
|
177
|
+
that changed under you.
|
|
178
|
+
|
|
179
|
+
Every successful install writes an immutable backup and a receipt under the user
|
|
180
|
+
state root:
|
|
181
|
+
|
|
182
|
+
- Linux and macOS: `${XDG_STATE_HOME:-~/.local/state}/agents-md-compiler/`
|
|
183
|
+
- Windows: `%LOCALAPPDATA%/agents-md-compiler/`
|
|
184
|
+
|
|
185
|
+
Rollback takes a specific receipt and refuses if the target changed after
|
|
186
|
+
installation:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
agents-md-compiler rollback --receipt <path/to/receipt.json> --apply
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Backups are never deleted or rotated automatically. If installation created a
|
|
193
|
+
previously missing target, rollback moves the generated file into the state
|
|
194
|
+
directory instead of deleting it irrecoverably.
|
|
195
|
+
|
|
196
|
+
Other refusals worth knowing:
|
|
197
|
+
|
|
198
|
+
- `render --output PATH` writes only to a path that does not exist. Use `install`
|
|
199
|
+
when you want replacement, backup, and rollback semantics.
|
|
200
|
+
- The selected output or target parent must already exist and must be a directory.
|
|
201
|
+
Dry runs reject an unusable parent before describing the target as missing.
|
|
202
|
+
- A symlinked target is refused, never followed.
|
|
203
|
+
- A symlinked source is refused, with both the lexical and resolved paths
|
|
204
|
+
reported.
|
|
205
|
+
- A non-empty global `AGENTS.override.md` beside the target is a `SHADOWED`
|
|
206
|
+
failure, because Codex would load the override instead of the file you just
|
|
207
|
+
installed.
|
|
208
|
+
|
|
209
|
+
If an install fails after atomically replacing the target but before all receipt
|
|
210
|
+
state is committed, recovery runs while the target lock is still held. It first
|
|
211
|
+
proves the target still contains the bytes written by that operation. It then
|
|
212
|
+
restores an existing predecessor from its verified backup, or preserves a newly
|
|
213
|
+
created generated file under the private state root and restores the target to
|
|
214
|
+
its prior missing state. If either proof fails, the command reports recovery as
|
|
215
|
+
failed and does not overwrite unknown bytes.
|
|
216
|
+
|
|
217
|
+
## Guarantees
|
|
218
|
+
|
|
219
|
+
Deterministic output:
|
|
220
|
+
|
|
221
|
+
- identical manifest bytes, lock bytes, format version, and source bytes produce
|
|
222
|
+
identical output bytes;
|
|
223
|
+
- generated output contains no timestamp, host name, process ID, random value,
|
|
224
|
+
temporary path, tool version, or working directory, so upgrading this tool
|
|
225
|
+
cannot change your policy bytes;
|
|
226
|
+
- manifest order is output order, and rendering never depends on directory
|
|
227
|
+
enumeration order.
|
|
228
|
+
|
|
229
|
+
Source preservation:
|
|
230
|
+
|
|
231
|
+
- every source is read as strict UTF-8 bytes and must use LF line endings with
|
|
232
|
+
exactly one final LF;
|
|
233
|
+
- a UTF-8 BOM, a NUL byte, a CR byte, or invalid UTF-8 is rejected;
|
|
234
|
+
- accepted bytes are copied verbatim between generated markers, never trimmed,
|
|
235
|
+
reflowed, lint-fixed, normalized, or summarized;
|
|
236
|
+
- a source containing a compiler marker line is rejected rather than escaped.
|
|
237
|
+
|
|
238
|
+
Provenance, not trust: digests prove that bytes did not change between locking
|
|
239
|
+
and rendering. They establish neither authorship nor safety. See
|
|
240
|
+
[`docs/security-model.md`](docs/security-model.md).
|
|
241
|
+
|
|
242
|
+
## JSON automation
|
|
243
|
+
|
|
244
|
+
Every command accepts `--format json` and prints exactly one JSON object to
|
|
245
|
+
stdout. Diagnostics stay on stderr, so a pipeline can parse stdout unconditionally.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
agents-md-compiler status --manifest ./policy/global-agents.toml --format json
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"command": "status",
|
|
254
|
+
"ok": true,
|
|
255
|
+
"schema_version": 1,
|
|
256
|
+
"state": "CURRENT"
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Drive automation from the exit code, and read `state` for the reason:
|
|
261
|
+
|
|
262
|
+
| Code | Meaning |
|
|
263
|
+
|------|--------------------------------------------------------------------------|
|
|
264
|
+
| 0 | Succeeded; `CURRENT` when a target state applies |
|
|
265
|
+
| 1 | Invalid invocation, invalid manifest, lock, or source, or an I/O error |
|
|
266
|
+
| 2 | Read-only difference: `LOCK_MISSING`, `LOCK_STALE`, `DRIFTED`, `MISSING` |
|
|
267
|
+
| 3 | Safety refusal: `SHADOWED`, `UNMANAGED_TARGET`, `CONCURRENT_CHANGE` |
|
|
268
|
+
|
|
269
|
+
`--quiet` suppresses non-error stderr. It never suppresses JSON output or
|
|
270
|
+
requested render output.
|
|
271
|
+
|
|
272
|
+
## Codex verification, and its limits
|
|
273
|
+
|
|
274
|
+
`verify-codex` confirms that the installed bundle is actually visible to the
|
|
275
|
+
model at startup:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
agents-md-compiler verify-codex --manifest ./policy/global-agents.toml
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
It resolves `codex` from `PATH` without a shell, records `codex --version`,
|
|
282
|
+
confirms the CLI exposes `debug prompt-input`, runs a non-interactive probe from a
|
|
283
|
+
disposable directory containing no project instruction file, parses the returned
|
|
284
|
+
JSON, and confirms that the generated header, every begin and end module marker,
|
|
285
|
+
and unique content sentinels from the first and last modules are all present.
|
|
286
|
+
|
|
287
|
+
What it does not do:
|
|
288
|
+
|
|
289
|
+
- send a model request or require API authentication;
|
|
290
|
+
- modify Codex configuration or copy policy files into the probe directory;
|
|
291
|
+
- claim semantic compliance. Marker presence proves the bytes reached the model's
|
|
292
|
+
input. It does not prove the model obeyed them.
|
|
293
|
+
|
|
294
|
+
`codex debug prompt-input` is a debug interface, not a promised stable API. If the
|
|
295
|
+
installed Codex removes or changes it, this command reports `RUNTIME_UNVERIFIED`
|
|
296
|
+
with the exact command and the observed failure, and exits 1. It never falls back
|
|
297
|
+
to asking a model to summarize its own instructions.
|
|
298
|
+
|
|
299
|
+
## Documentation
|
|
300
|
+
|
|
301
|
+
| Document | Contents |
|
|
302
|
+
|------------------------------------------------------------|-----------------------------------------------------------------|
|
|
303
|
+
| [`docs/manifest-v1.md`](docs/manifest-v1.md) | Manifest schema version 1, path bases, and every rejection rule |
|
|
304
|
+
| [`docs/rendered-format-v1.md`](docs/rendered-format-v1.md) | Exact output bytes, markers, and hashing boundaries |
|
|
305
|
+
| [`docs/cli-contract.md`](docs/cli-contract.md) | Commands, options, JSON envelopes, states, and exit codes |
|
|
306
|
+
| [`docs/security-model.md`](docs/security-model.md) | Trust boundaries, path and TOCTOU handling, and recovery |
|
|
307
|
+
| [`AGENTS.md`](AGENTS.md) | Project contract for agents working on this repository |
|
|
308
|
+
| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Setup, commit conventions, and release boundaries |
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# agents-md-compiler
|
|
2
|
+
|
|
3
|
+
Compile an ordered set of Markdown policy modules into one deterministic,
|
|
4
|
+
verifiable global `AGENTS.md`. No runtime dependencies, no network access, no
|
|
5
|
+
shell invocation, and no LLM anywhere in the pipeline.
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
Coding agents read their instruction files once, at startup. Codex selects a
|
|
10
|
+
single non-empty global file, builds its project instruction chain from the
|
|
11
|
+
project root through the startup working directory, and then stops looking.
|
|
12
|
+
Changing directories later does not add instructions to a running session, and
|
|
13
|
+
Codex treats a Claude-style `@path` import as ordinary Markdown rather than as an
|
|
14
|
+
include directive.
|
|
15
|
+
|
|
16
|
+
That leaves two options for a rule that must be available before task scope is
|
|
17
|
+
known: paste everything into one enormous hand-maintained file, or generate that
|
|
18
|
+
file from modular sources. Hand-maintaining it means the file drifts from the
|
|
19
|
+
sources, nobody can tell which version is live, and a careless edit silently
|
|
20
|
+
deletes a rule.
|
|
21
|
+
|
|
22
|
+
This tool takes the second option and makes it auditable:
|
|
23
|
+
|
|
24
|
+
- canonical Markdown modules stay the only editable policy sources;
|
|
25
|
+
- one reviewed manifest fixes the module set and its order;
|
|
26
|
+
- a lock records each source's resolved path, byte size, and SHA-256;
|
|
27
|
+
- rendering emits one self-contained file with stable provenance headers and the
|
|
28
|
+
exact source bytes between generated markers;
|
|
29
|
+
- checking detects source drift, output drift, a missing target, shadowing, and
|
|
30
|
+
an unmanaged target;
|
|
31
|
+
- installation is explicit, backed up, concurrency-safe, and atomic;
|
|
32
|
+
- rollback restores the exact prior bytes, and refuses if the target changed;
|
|
33
|
+
- verification inspects the model-visible startup input instead of trusting a
|
|
34
|
+
model to say it read the rules.
|
|
35
|
+
|
|
36
|
+
It deliberately does not generate nested or project-level `AGENTS.md` files, and
|
|
37
|
+
it never detects your stack and writes rules for it. A repository may keep its own
|
|
38
|
+
root `AGENTS.md` for concrete project facts; this compiler leaves that file alone.
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Python 3.14 or newer.
|
|
43
|
+
- Linux, macOS, or Windows.
|
|
44
|
+
- No runtime dependencies. The package uses the standard library only.
|
|
45
|
+
- `codex` on `PATH` only for the optional `verify-codex` command.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Recommended: install as an isolated tool.
|
|
51
|
+
uv tool install agents-md-compiler
|
|
52
|
+
|
|
53
|
+
# Or run without installing.
|
|
54
|
+
uvx agents-md-compiler --help
|
|
55
|
+
|
|
56
|
+
# Or into an existing environment.
|
|
57
|
+
pip install agents-md-compiler
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Both invocation forms are supported and equivalent:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
agents-md-compiler --help
|
|
64
|
+
python -m agents_md_compiler --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Five minutes end to end
|
|
68
|
+
|
|
69
|
+
Scaffold an example manifest and modules, then lock, render, check, and install.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# 1. Scaffold. The manifest targets ~/.codex/AGENTS.md by default.
|
|
73
|
+
agents-md-compiler init --directory ./policy
|
|
74
|
+
|
|
75
|
+
# 2. Create the selected target's parent if this is a new Codex home.
|
|
76
|
+
mkdir -p ~/.codex
|
|
77
|
+
|
|
78
|
+
# 3. Record exact source paths, sizes, and digests.
|
|
79
|
+
agents-md-compiler lock --manifest ./policy/global-agents.toml
|
|
80
|
+
|
|
81
|
+
# 4. Validate the manifest, lock, sources, and rendered structure.
|
|
82
|
+
agents-md-compiler validate --manifest ./policy/global-agents.toml
|
|
83
|
+
|
|
84
|
+
# 5. Look at the bytes before writing anything anywhere.
|
|
85
|
+
agents-md-compiler render --manifest ./policy/global-agents.toml --locked | head -20
|
|
86
|
+
|
|
87
|
+
# 6. Compare a fresh locked render against the target.
|
|
88
|
+
agents-md-compiler check --manifest ./policy/global-agents.toml
|
|
89
|
+
|
|
90
|
+
# 7. Preview the install. This writes nothing at all.
|
|
91
|
+
agents-md-compiler install --manifest ./policy/global-agents.toml
|
|
92
|
+
|
|
93
|
+
# 8. Install for real.
|
|
94
|
+
agents-md-compiler install --manifest ./policy/global-agents.toml --apply
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Editing a canonical module after step 3 makes `check` exit 2 with `LOCK_STALE`
|
|
98
|
+
until you rerun `lock`. That coupling is the point: the reviewed manifest, the
|
|
99
|
+
lock, and the live file always agree or the tool tells you they do not.
|
|
100
|
+
|
|
101
|
+
## Choose where output goes
|
|
102
|
+
|
|
103
|
+
Use one of three destination controls according to the operation:
|
|
104
|
+
|
|
105
|
+
- Set manifest `default_target` for the persistent managed destination. A relative
|
|
106
|
+
value resolves from the manifest directory. `~` expands to the user's home.
|
|
107
|
+
- Pass `--target PATH` to `validate`, `check`, `status`, `install`, `rollback`, or
|
|
108
|
+
`verify-codex` to override the managed destination for one invocation. A relative
|
|
109
|
+
command-line path resolves from the current working directory.
|
|
110
|
+
- Pass `render --locked --output PATH` to export one new standalone file. This
|
|
111
|
+
never replaces an existing path and does not create its parent directory.
|
|
112
|
+
|
|
113
|
+
For example, preview and then install to an explicit managed target:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
agents-md-compiler install \
|
|
117
|
+
--manifest ./policy/global-agents.toml \
|
|
118
|
+
--target ~/.codex/AGENTS.md
|
|
119
|
+
|
|
120
|
+
agents-md-compiler install \
|
|
121
|
+
--manifest ./policy/global-agents.toml \
|
|
122
|
+
--target ~/.codex/AGENTS.md \
|
|
123
|
+
--apply
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The selected target may have another file name, but only a real global
|
|
127
|
+
`AGENTS.md` can be verified through Codex startup input. `verify-codex` therefore
|
|
128
|
+
fails for an otherwise current custom target that Codex does not load globally.
|
|
129
|
+
|
|
130
|
+
## Safety behavior you should know before installing
|
|
131
|
+
|
|
132
|
+
**An existing target that this tool did not generate is never replaced
|
|
133
|
+
silently.** If the target exists but carries no recognized generated header,
|
|
134
|
+
`install --apply` refuses with `UNMANAGED_TARGET` and exit code 3. Adopting that
|
|
135
|
+
file requires both flags, and the digest must match what you captured from the
|
|
136
|
+
dry run:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Capture the exact current bytes first.
|
|
140
|
+
shasum -a 256 ~/.codex/AGENTS.md
|
|
141
|
+
|
|
142
|
+
EXPECTED_TARGET_SHA256=<the 64-character digest you just captured>
|
|
143
|
+
agents-md-compiler install \
|
|
144
|
+
--manifest ./policy/global-agents.toml \
|
|
145
|
+
--apply --replace-unmanaged \
|
|
146
|
+
--expect-target-sha256 "${EXPECTED_TARGET_SHA256}"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If the digest does not match, the install refuses rather than overwriting a file
|
|
150
|
+
that changed under you.
|
|
151
|
+
|
|
152
|
+
Every successful install writes an immutable backup and a receipt under the user
|
|
153
|
+
state root:
|
|
154
|
+
|
|
155
|
+
- Linux and macOS: `${XDG_STATE_HOME:-~/.local/state}/agents-md-compiler/`
|
|
156
|
+
- Windows: `%LOCALAPPDATA%/agents-md-compiler/`
|
|
157
|
+
|
|
158
|
+
Rollback takes a specific receipt and refuses if the target changed after
|
|
159
|
+
installation:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
agents-md-compiler rollback --receipt <path/to/receipt.json> --apply
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Backups are never deleted or rotated automatically. If installation created a
|
|
166
|
+
previously missing target, rollback moves the generated file into the state
|
|
167
|
+
directory instead of deleting it irrecoverably.
|
|
168
|
+
|
|
169
|
+
Other refusals worth knowing:
|
|
170
|
+
|
|
171
|
+
- `render --output PATH` writes only to a path that does not exist. Use `install`
|
|
172
|
+
when you want replacement, backup, and rollback semantics.
|
|
173
|
+
- The selected output or target parent must already exist and must be a directory.
|
|
174
|
+
Dry runs reject an unusable parent before describing the target as missing.
|
|
175
|
+
- A symlinked target is refused, never followed.
|
|
176
|
+
- A symlinked source is refused, with both the lexical and resolved paths
|
|
177
|
+
reported.
|
|
178
|
+
- A non-empty global `AGENTS.override.md` beside the target is a `SHADOWED`
|
|
179
|
+
failure, because Codex would load the override instead of the file you just
|
|
180
|
+
installed.
|
|
181
|
+
|
|
182
|
+
If an install fails after atomically replacing the target but before all receipt
|
|
183
|
+
state is committed, recovery runs while the target lock is still held. It first
|
|
184
|
+
proves the target still contains the bytes written by that operation. It then
|
|
185
|
+
restores an existing predecessor from its verified backup, or preserves a newly
|
|
186
|
+
created generated file under the private state root and restores the target to
|
|
187
|
+
its prior missing state. If either proof fails, the command reports recovery as
|
|
188
|
+
failed and does not overwrite unknown bytes.
|
|
189
|
+
|
|
190
|
+
## Guarantees
|
|
191
|
+
|
|
192
|
+
Deterministic output:
|
|
193
|
+
|
|
194
|
+
- identical manifest bytes, lock bytes, format version, and source bytes produce
|
|
195
|
+
identical output bytes;
|
|
196
|
+
- generated output contains no timestamp, host name, process ID, random value,
|
|
197
|
+
temporary path, tool version, or working directory, so upgrading this tool
|
|
198
|
+
cannot change your policy bytes;
|
|
199
|
+
- manifest order is output order, and rendering never depends on directory
|
|
200
|
+
enumeration order.
|
|
201
|
+
|
|
202
|
+
Source preservation:
|
|
203
|
+
|
|
204
|
+
- every source is read as strict UTF-8 bytes and must use LF line endings with
|
|
205
|
+
exactly one final LF;
|
|
206
|
+
- a UTF-8 BOM, a NUL byte, a CR byte, or invalid UTF-8 is rejected;
|
|
207
|
+
- accepted bytes are copied verbatim between generated markers, never trimmed,
|
|
208
|
+
reflowed, lint-fixed, normalized, or summarized;
|
|
209
|
+
- a source containing a compiler marker line is rejected rather than escaped.
|
|
210
|
+
|
|
211
|
+
Provenance, not trust: digests prove that bytes did not change between locking
|
|
212
|
+
and rendering. They establish neither authorship nor safety. See
|
|
213
|
+
[`docs/security-model.md`](docs/security-model.md).
|
|
214
|
+
|
|
215
|
+
## JSON automation
|
|
216
|
+
|
|
217
|
+
Every command accepts `--format json` and prints exactly one JSON object to
|
|
218
|
+
stdout. Diagnostics stay on stderr, so a pipeline can parse stdout unconditionally.
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
agents-md-compiler status --manifest ./policy/global-agents.toml --format json
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"command": "status",
|
|
227
|
+
"ok": true,
|
|
228
|
+
"schema_version": 1,
|
|
229
|
+
"state": "CURRENT"
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Drive automation from the exit code, and read `state` for the reason:
|
|
234
|
+
|
|
235
|
+
| Code | Meaning |
|
|
236
|
+
|------|--------------------------------------------------------------------------|
|
|
237
|
+
| 0 | Succeeded; `CURRENT` when a target state applies |
|
|
238
|
+
| 1 | Invalid invocation, invalid manifest, lock, or source, or an I/O error |
|
|
239
|
+
| 2 | Read-only difference: `LOCK_MISSING`, `LOCK_STALE`, `DRIFTED`, `MISSING` |
|
|
240
|
+
| 3 | Safety refusal: `SHADOWED`, `UNMANAGED_TARGET`, `CONCURRENT_CHANGE` |
|
|
241
|
+
|
|
242
|
+
`--quiet` suppresses non-error stderr. It never suppresses JSON output or
|
|
243
|
+
requested render output.
|
|
244
|
+
|
|
245
|
+
## Codex verification, and its limits
|
|
246
|
+
|
|
247
|
+
`verify-codex` confirms that the installed bundle is actually visible to the
|
|
248
|
+
model at startup:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
agents-md-compiler verify-codex --manifest ./policy/global-agents.toml
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
It resolves `codex` from `PATH` without a shell, records `codex --version`,
|
|
255
|
+
confirms the CLI exposes `debug prompt-input`, runs a non-interactive probe from a
|
|
256
|
+
disposable directory containing no project instruction file, parses the returned
|
|
257
|
+
JSON, and confirms that the generated header, every begin and end module marker,
|
|
258
|
+
and unique content sentinels from the first and last modules are all present.
|
|
259
|
+
|
|
260
|
+
What it does not do:
|
|
261
|
+
|
|
262
|
+
- send a model request or require API authentication;
|
|
263
|
+
- modify Codex configuration or copy policy files into the probe directory;
|
|
264
|
+
- claim semantic compliance. Marker presence proves the bytes reached the model's
|
|
265
|
+
input. It does not prove the model obeyed them.
|
|
266
|
+
|
|
267
|
+
`codex debug prompt-input` is a debug interface, not a promised stable API. If the
|
|
268
|
+
installed Codex removes or changes it, this command reports `RUNTIME_UNVERIFIED`
|
|
269
|
+
with the exact command and the observed failure, and exits 1. It never falls back
|
|
270
|
+
to asking a model to summarize its own instructions.
|
|
271
|
+
|
|
272
|
+
## Documentation
|
|
273
|
+
|
|
274
|
+
| Document | Contents |
|
|
275
|
+
|------------------------------------------------------------|-----------------------------------------------------------------|
|
|
276
|
+
| [`docs/manifest-v1.md`](docs/manifest-v1.md) | Manifest schema version 1, path bases, and every rejection rule |
|
|
277
|
+
| [`docs/rendered-format-v1.md`](docs/rendered-format-v1.md) | Exact output bytes, markers, and hashing boundaries |
|
|
278
|
+
| [`docs/cli-contract.md`](docs/cli-contract.md) | Commands, options, JSON envelopes, states, and exit codes |
|
|
279
|
+
| [`docs/security-model.md`](docs/security-model.md) | Trust boundaries, path and TOCTOU handling, and recovery |
|
|
280
|
+
| [`AGENTS.md`](AGENTS.md) | Project contract for agents working on this repository |
|
|
281
|
+
| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Setup, commit conventions, and release boundaries |
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
MIT. See [`LICENSE`](LICENSE).
|