agents-md-compiler 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.
@@ -0,0 +1,127 @@
1
+ """Deterministic compiler for a single global ``AGENTS.md`` policy bundle.
2
+
3
+ The public surface is intentionally small. Pure computation (manifest parsing,
4
+ source validation, locking, rendering, state comparison) lives here; every
5
+ filesystem mutation lives in :mod:`agents_md_compiler.installation` so a library
6
+ consumer cannot confuse rendering with installing.
7
+
8
+ Typical read-only use::
9
+
10
+ from pathlib import Path
11
+ from agents_md_compiler import compile_bundle, load_manifest
12
+
13
+ manifest = load_manifest(Path("global-agents.toml").absolute())
14
+ compiled = compile_bundle(manifest)
15
+ rendered_bytes = compiled.rendered.data
16
+ """
17
+
18
+ from agents_md_compiler._version import distribution_version
19
+ from agents_md_compiler.codex import (
20
+ CodexCapability,
21
+ RuntimeVerification,
22
+ detect_capability,
23
+ verify_rendered_visibility,
24
+ )
25
+ from agents_md_compiler.errors import (
26
+ CodexVerificationError,
27
+ CompilerError,
28
+ ConcurrentChangeError,
29
+ LockError,
30
+ LockMissingError,
31
+ LockStaleError,
32
+ ManifestError,
33
+ MutationError,
34
+ OutputExistsError,
35
+ ReceiptError,
36
+ RenderError,
37
+ ShadowedError,
38
+ SourceError,
39
+ TargetError,
40
+ UnmanagedTargetError,
41
+ UsageError,
42
+ )
43
+ from agents_md_compiler.lockfile import build_lock, load_lock, serialize_lock
44
+ from agents_md_compiler.manifest import load_manifest
45
+ from agents_md_compiler.models import (
46
+ LOCK_FORMAT_VERSION,
47
+ MANIFEST_SCHEMA_VERSION,
48
+ RECEIPT_SCHEMA_VERSION,
49
+ RENDER_FORMAT_VERSION,
50
+ BundleLimits,
51
+ BundleLock,
52
+ BundleManifest,
53
+ BundleState,
54
+ BundleStatus,
55
+ CompiledBundle,
56
+ LockedModule,
57
+ ModuleSpec,
58
+ OverrideInspection,
59
+ RenderedBundle,
60
+ SourceSnapshot,
61
+ TargetInspection,
62
+ TargetKind,
63
+ )
64
+ from agents_md_compiler.rendering import render_bundle, validate_rendered
65
+ from agents_md_compiler.sources import read_source, read_sources
66
+ from agents_md_compiler.state import (
67
+ compile_bundle,
68
+ evaluate,
69
+ inspect_override,
70
+ inspect_target,
71
+ )
72
+
73
+ __all__ = [
74
+ "LOCK_FORMAT_VERSION",
75
+ "MANIFEST_SCHEMA_VERSION",
76
+ "RECEIPT_SCHEMA_VERSION",
77
+ "RENDER_FORMAT_VERSION",
78
+ "BundleLimits",
79
+ "BundleLock",
80
+ "BundleManifest",
81
+ "BundleState",
82
+ "BundleStatus",
83
+ "CodexCapability",
84
+ "CodexVerificationError",
85
+ "CompiledBundle",
86
+ "CompilerError",
87
+ "ConcurrentChangeError",
88
+ "LockError",
89
+ "LockMissingError",
90
+ "LockStaleError",
91
+ "LockedModule",
92
+ "ManifestError",
93
+ "ModuleSpec",
94
+ "MutationError",
95
+ "OutputExistsError",
96
+ "OverrideInspection",
97
+ "ReceiptError",
98
+ "RenderError",
99
+ "RenderedBundle",
100
+ "RuntimeVerification",
101
+ "ShadowedError",
102
+ "SourceError",
103
+ "SourceSnapshot",
104
+ "TargetError",
105
+ "TargetInspection",
106
+ "TargetKind",
107
+ "UnmanagedTargetError",
108
+ "UsageError",
109
+ "__version__",
110
+ "build_lock",
111
+ "compile_bundle",
112
+ "detect_capability",
113
+ "distribution_version",
114
+ "evaluate",
115
+ "inspect_override",
116
+ "inspect_target",
117
+ "load_lock",
118
+ "load_manifest",
119
+ "read_source",
120
+ "read_sources",
121
+ "render_bundle",
122
+ "serialize_lock",
123
+ "validate_rendered",
124
+ "verify_rendered_visibility",
125
+ ]
126
+
127
+ __version__ = distribution_version()
@@ -0,0 +1,6 @@
1
+ """Module entry point so ``python -m agents_md_compiler`` matches the console script."""
2
+
3
+ from agents_md_compiler.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -0,0 +1,28 @@
1
+ """Installed distribution version discovery.
2
+
3
+ The compiler reports its own version from installed package metadata rather than
4
+ from a hard-coded string, so a wheel and its metadata can never disagree. The
5
+ version deliberately never enters rendered output: a tool upgrade must not change
6
+ policy bytes when the format, manifest, lock, and modules are identical.
7
+ """
8
+
9
+ from importlib.metadata import PackageNotFoundError, version
10
+
11
+ DISTRIBUTION_NAME = "agents-md-compiler"
12
+ """Distribution name used for metadata lookup."""
13
+
14
+ UNKNOWN_VERSION = "0.0.0+unknown"
15
+ """Reported when the package is not installed, as in a bare source checkout."""
16
+
17
+
18
+ def distribution_version() -> str:
19
+ """Report the installed distribution version.
20
+
21
+ Returns:
22
+ The version from installed package metadata, or ``0.0.0+unknown`` when no
23
+ installed distribution provides it.
24
+ """
25
+ try:
26
+ return version(DISTRIBUTION_NAME)
27
+ except PackageNotFoundError:
28
+ return UNKNOWN_VERSION
@@ -0,0 +1,135 @@
1
+ """Atomic same-filesystem file replacement.
2
+
3
+ An interrupted write must leave either the complete old bytes or the complete new
4
+ bytes, never a partial file. The sequence is: create a temporary file in the
5
+ target's own directory, write it, flush it, ``fsync`` it, apply the intended mode,
6
+ atomically replace the target, then ``fsync`` the containing directory where the
7
+ platform supports it.
8
+
9
+ The temporary file must share the target's directory because ``os.replace`` is
10
+ atomic only within one filesystem. A temporary directory elsewhere would silently
11
+ degrade to a copy.
12
+ """
13
+
14
+ import os
15
+ import tempfile
16
+ from pathlib import Path
17
+
18
+ from agents_md_compiler.errors import MutationError, MutationProblem
19
+
20
+ TEMP_PREFIX = ".agents-md-compiler-"
21
+ """Prefix for the same-directory temporary file, so a leftover is identifiable."""
22
+
23
+ TEMP_SUFFIX = ".tmp"
24
+ """Suffix for the same-directory temporary file."""
25
+
26
+
27
+ def sync_directory(directory: Path) -> bool:
28
+ """Flush a directory entry to stable storage where supported.
29
+
30
+ Windows cannot open a directory as a file descriptor, so this is a documented
31
+ best effort rather than a guarantee. The replacement itself is still atomic;
32
+ only the durability of the rename across a crash is platform-dependent.
33
+
34
+ Args:
35
+ directory: Directory whose entries were changed.
36
+
37
+ Returns:
38
+ ``True`` when the directory was synced, ``False`` when the platform does not
39
+ support it.
40
+ """
41
+ try:
42
+ descriptor = os.open(directory, os.O_RDONLY)
43
+ except OSError:
44
+ return False
45
+ try:
46
+ os.fsync(descriptor)
47
+ except OSError:
48
+ return False
49
+ finally:
50
+ os.close(descriptor)
51
+ return True
52
+
53
+
54
+ def atomic_write(target: Path, data: bytes, *, mode: int) -> bool:
55
+ """Replace a file's contents atomically.
56
+
57
+ Args:
58
+ target: Destination path. Its parent directory must already exist.
59
+ data: Exact bytes to write.
60
+ mode: Permission bits to apply to the new file.
61
+
62
+ Returns:
63
+ ``True`` when the containing directory was also synced.
64
+
65
+ Raises:
66
+ MutationError: Any step failed. The temporary file is removed first, so a
67
+ failure never leaves a partial file behind and never damages the target.
68
+ """
69
+ directory = target.parent
70
+ try:
71
+ descriptor, temp_name = tempfile.mkstemp(
72
+ dir=directory, prefix=TEMP_PREFIX, suffix=TEMP_SUFFIX
73
+ )
74
+ except OSError as error:
75
+ raise MutationError(
76
+ MutationProblem.WRITE_FAILED,
77
+ path=target,
78
+ detail=error.strerror or type(error).__name__,
79
+ ) from error
80
+ temporary = Path(temp_name)
81
+ try:
82
+ try:
83
+ with os.fdopen(descriptor, "wb") as stream:
84
+ stream.write(data)
85
+ stream.flush()
86
+ os.fsync(stream.fileno())
87
+ except OSError as error:
88
+ raise MutationError(
89
+ MutationProblem.SYNC_FAILED,
90
+ path=target,
91
+ detail=error.strerror or type(error).__name__,
92
+ ) from error
93
+ try:
94
+ temporary.chmod(mode)
95
+ except OSError as error:
96
+ raise MutationError(
97
+ MutationProblem.PERMISSION_FAILED,
98
+ path=target,
99
+ detail=error.strerror or type(error).__name__,
100
+ ) from error
101
+ try:
102
+ temporary.replace(target)
103
+ except OSError as error:
104
+ raise MutationError(
105
+ MutationProblem.REPLACE_FAILED,
106
+ path=target,
107
+ detail=error.strerror or type(error).__name__,
108
+ ) from error
109
+ except MutationError:
110
+ temporary.unlink(missing_ok=True)
111
+ raise
112
+ return sync_directory(directory)
113
+
114
+
115
+ def create_state_directory(directory: Path, *, mode: int) -> None:
116
+ """Create a state directory tree with owner-only access.
117
+
118
+ An existing directory's mode is left alone, so a more restrictive operator
119
+ choice is never broadened.
120
+
121
+ Args:
122
+ directory: Directory to create, including parents.
123
+ mode: Mode applied to directories this call creates.
124
+
125
+ Raises:
126
+ MutationError: The directory could not be created.
127
+ """
128
+ try:
129
+ directory.mkdir(parents=True, exist_ok=True, mode=mode)
130
+ except OSError as error:
131
+ raise MutationError(
132
+ MutationProblem.STATE_ROOT_FAILED,
133
+ path=directory,
134
+ detail=error.strerror or type(error).__name__,
135
+ ) from error