@trac3er/oh-my-god 2.0.2 → 2.0.4

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 (97) hide show
  1. package/.agents/skills/omg/AGENTS.fragment.md +5 -0
  2. package/.agents/skills/omg/codex-mcp.toml +4 -0
  3. package/.agents/skills/omg/control-plane/SKILL.md +11 -0
  4. package/.agents/skills/omg/control-plane/openai.yaml +14 -0
  5. package/.agents/skills/omg/hook-governor/SKILL.md +11 -0
  6. package/.agents/skills/omg/hook-governor/openai.yaml +11 -0
  7. package/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
  8. package/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
  9. package/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
  10. package/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
  11. package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
  12. package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
  13. package/.claude-plugin/marketplace.json +3 -3
  14. package/.claude-plugin/plugin.json +1 -1
  15. package/.mcp.json +20 -4
  16. package/CHANGELOG.md +16 -0
  17. package/OMG-setup.sh +9 -3
  18. package/OMG_COMPAT_CONTRACT.md +92 -0
  19. package/README.md +26 -8
  20. package/SECURITY.md +6 -0
  21. package/commands/OMG:api-twin.md +22 -0
  22. package/commands/OMG:preflight.md +26 -0
  23. package/commands/OMG:security-check.md +28 -0
  24. package/commands/OMG:setup.md +1 -2
  25. package/dist/enterprise/bundle/.agents/skills/omg/AGENTS.fragment.md +5 -0
  26. package/dist/enterprise/bundle/.agents/skills/omg/codex-mcp.toml +4 -0
  27. package/dist/enterprise/bundle/.agents/skills/omg/control-plane/SKILL.md +11 -0
  28. package/dist/enterprise/bundle/.agents/skills/omg/control-plane/openai.yaml +14 -0
  29. package/dist/enterprise/bundle/.agents/skills/omg/hook-governor/SKILL.md +11 -0
  30. package/dist/enterprise/bundle/.agents/skills/omg/hook-governor/openai.yaml +11 -0
  31. package/dist/enterprise/bundle/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
  32. package/dist/enterprise/bundle/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
  33. package/dist/enterprise/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
  34. package/dist/enterprise/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
  35. package/dist/enterprise/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
  36. package/dist/enterprise/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
  37. package/dist/enterprise/bundle/.claude-plugin/marketplace.json +36 -0
  38. package/dist/enterprise/bundle/.claude-plugin/plugin.json +23 -0
  39. package/dist/enterprise/bundle/.mcp.json +40 -0
  40. package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +92 -0
  41. package/dist/enterprise/bundle/settings.json +366 -0
  42. package/dist/enterprise/manifest.json +99 -0
  43. package/dist/public/bundle/.agents/skills/omg/AGENTS.fragment.md +5 -0
  44. package/dist/public/bundle/.agents/skills/omg/codex-mcp.toml +4 -0
  45. package/dist/public/bundle/.agents/skills/omg/control-plane/SKILL.md +11 -0
  46. package/dist/public/bundle/.agents/skills/omg/control-plane/openai.yaml +14 -0
  47. package/dist/public/bundle/.agents/skills/omg/hook-governor/SKILL.md +11 -0
  48. package/dist/public/bundle/.agents/skills/omg/hook-governor/openai.yaml +11 -0
  49. package/dist/public/bundle/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
  50. package/dist/public/bundle/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
  51. package/dist/public/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
  52. package/dist/public/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
  53. package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
  54. package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
  55. package/dist/public/bundle/.claude-plugin/marketplace.json +36 -0
  56. package/dist/public/bundle/.claude-plugin/plugin.json +23 -0
  57. package/dist/public/bundle/.mcp.json +40 -0
  58. package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +92 -0
  59. package/dist/public/bundle/settings.json +366 -0
  60. package/dist/public/manifest.json +99 -0
  61. package/hooks/policy_engine.py +38 -7
  62. package/hooks/post-write.py +1 -1
  63. package/hooks/prompt-enhancer.py +2 -2
  64. package/hooks/security_validators.py +75 -0
  65. package/hooks/setup_wizard.py +44 -20
  66. package/hooks/shadow_manager.py +22 -2
  67. package/package.json +1 -1
  68. package/plugins/README.md +4 -2
  69. package/plugins/advanced/commands/OMG:deep-plan.md +1 -1
  70. package/plugins/advanced/commands/OMG:security-review.md +10 -113
  71. package/plugins/advanced/commands/OMG:ship.md +1 -1
  72. package/plugins/advanced/plugin.json +1 -10
  73. package/plugins/core/plugin.json +25 -2
  74. package/pyproject.toml +1 -1
  75. package/runtime/adoption.py +1 -1
  76. package/runtime/api_twin.py +130 -0
  77. package/runtime/compat.py +21 -1
  78. package/runtime/contract_compiler.py +698 -0
  79. package/runtime/domain_packs.py +34 -0
  80. package/runtime/guide_assert.py +45 -0
  81. package/runtime/mcp_config_writers.py +145 -39
  82. package/runtime/omg_compat_contract_snapshot.json +8 -7
  83. package/runtime/omg_contract_snapshot.json +8 -7
  84. package/runtime/omg_mcp_server.py +205 -0
  85. package/runtime/preflight.py +52 -0
  86. package/runtime/providers/codex_provider.py +2 -12
  87. package/runtime/providers/gemini_provider.py +2 -21
  88. package/runtime/providers/kimi_provider.py +2 -21
  89. package/runtime/runtime_profile.py +61 -0
  90. package/runtime/security_check.py +347 -0
  91. package/runtime/subagent_dispatcher.py +117 -10
  92. package/runtime/team_router.py +3 -3
  93. package/runtime/untrusted_content.py +102 -0
  94. package/scripts/omg.py +174 -1
  95. package/settings.json +66 -18
  96. package/tools/python_repl.py +33 -3
  97. package/runtime/providers/opencode_provider.py +0 -144
@@ -0,0 +1,698 @@
1
+ """Canonical OMG contract registry, compiler, and release-readiness checks."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import asyncio
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ import shutil
10
+ from typing import Any, Iterable
11
+
12
+ import yaml
13
+
14
+ from runtime.adoption import (
15
+ CANONICAL_MARKETPLACE_ID,
16
+ CANONICAL_PACKAGE_NAME,
17
+ CANONICAL_PLUGIN_ID,
18
+ CANONICAL_REPO_URL,
19
+ CANONICAL_VERSION,
20
+ )
21
+
22
+
23
+ CONTRACT_DOC_PATH = Path("OMG_COMPAT_CONTRACT.md")
24
+ SCHEMA_PATH = Path("registry") / "omg-capability.schema.json"
25
+ BUNDLES_DIR = Path("registry") / "bundles"
26
+ SUPPORTED_HOSTS = ("claude", "codex")
27
+ SUPPORTED_CHANNELS = ("public", "enterprise")
28
+ DEFAULT_REQUIRED_BUNDLES = (
29
+ "control-plane",
30
+ "hook-governor",
31
+ "mcp-fabric",
32
+ "lsp-pack",
33
+ "secure-worktree-pipeline",
34
+ )
35
+ REQUIRED_DOC_TOKENS = (
36
+ "execution_contract",
37
+ "tool_policy",
38
+ "invocation_policy",
39
+ "host_compilation_rules",
40
+ "local_supervisor",
41
+ )
42
+ REQUIRED_BUNDLE_FIELDS = (
43
+ "id",
44
+ "kind",
45
+ "version",
46
+ "title",
47
+ "description",
48
+ "hosts",
49
+ "assets",
50
+ "invocation_policy",
51
+ "tool_policy",
52
+ "lifecycle_hooks",
53
+ "mcp_contract",
54
+ "lsp_contract",
55
+ "evidence_outputs",
56
+ "execution_contract",
57
+ "channel_overrides",
58
+ )
59
+
60
+
61
+ def _resolve_root(root_dir: str | Path | None) -> Path:
62
+ if root_dir is None:
63
+ return Path(__file__).resolve().parents[1]
64
+ return Path(root_dir).resolve()
65
+
66
+
67
+ def _resolve_output_root(root_dir: Path, output_root: str | Path | None) -> Path:
68
+ if output_root is None or str(output_root).strip() == "":
69
+ return root_dir
70
+ return Path(output_root).resolve()
71
+
72
+
73
+ def _load_json(path: Path) -> dict[str, Any]:
74
+ parsed = json.loads(path.read_text(encoding="utf-8"))
75
+ if not isinstance(parsed, dict):
76
+ raise ValueError(f"Expected JSON object in {path}")
77
+ return parsed
78
+
79
+
80
+ def _write_json(path: Path, payload: dict[str, Any]) -> None:
81
+ path.parent.mkdir(parents=True, exist_ok=True)
82
+ path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
83
+
84
+
85
+ def _write_text(path: Path, content: str) -> None:
86
+ path.parent.mkdir(parents=True, exist_ok=True)
87
+ path.write_text(content, encoding="utf-8")
88
+
89
+
90
+ def _sha256_file(path: Path) -> str:
91
+ digest = hashlib.sha256()
92
+ with path.open("rb") as handle:
93
+ for chunk in iter(lambda: handle.read(65536), b""):
94
+ digest.update(chunk)
95
+ return digest.hexdigest()
96
+
97
+
98
+ def load_contract_doc(root_dir: str | Path | None = None) -> str:
99
+ root = _resolve_root(root_dir)
100
+ return (root / CONTRACT_DOC_PATH).read_text(encoding="utf-8")
101
+
102
+
103
+ def load_contract_schema(root_dir: str | Path | None = None) -> dict[str, Any]:
104
+ root = _resolve_root(root_dir)
105
+ return _load_json(root / SCHEMA_PATH)
106
+
107
+
108
+ def load_contract_bundles(root_dir: str | Path | None = None) -> list[dict[str, Any]]:
109
+ root = _resolve_root(root_dir)
110
+ bundles: list[dict[str, Any]] = []
111
+ for path in sorted((root / BUNDLES_DIR).glob("*.yaml")):
112
+ parsed = yaml.safe_load(path.read_text(encoding="utf-8"))
113
+ if not isinstance(parsed, dict):
114
+ raise ValueError(f"Expected mapping bundle manifest in {path}")
115
+ bundle = dict(parsed)
116
+ bundle["_path"] = str(path.relative_to(root))
117
+ bundles.append(bundle)
118
+ return bundles
119
+
120
+
121
+ def _bundle_summary(bundle: dict[str, Any]) -> dict[str, Any]:
122
+ return {
123
+ "id": bundle.get("id", ""),
124
+ "kind": bundle.get("kind", ""),
125
+ "version": bundle.get("version", ""),
126
+ "title": bundle.get("title", ""),
127
+ "hosts": list(bundle.get("hosts", [])),
128
+ "path": bundle.get("_path", ""),
129
+ }
130
+
131
+
132
+ def validate_contract_registry(root_dir: str | Path | None = None) -> dict[str, Any]:
133
+ root = _resolve_root(root_dir)
134
+ errors: list[str] = []
135
+
136
+ doc_path = root / CONTRACT_DOC_PATH
137
+ schema_path = root / SCHEMA_PATH
138
+ bundles_path = root / BUNDLES_DIR
139
+
140
+ if not doc_path.exists():
141
+ errors.append(f"missing contract doc: {CONTRACT_DOC_PATH}")
142
+ doc_text = ""
143
+ else:
144
+ doc_text = doc_path.read_text(encoding="utf-8")
145
+ for token in REQUIRED_DOC_TOKENS:
146
+ if token not in doc_text:
147
+ errors.append(f"contract doc missing token: {token}")
148
+ if CANONICAL_VERSION not in doc_text:
149
+ errors.append(f"contract doc missing version: {CANONICAL_VERSION}")
150
+
151
+ if not schema_path.exists():
152
+ errors.append(f"missing contract schema: {SCHEMA_PATH}")
153
+ schema_payload: dict[str, Any] = {}
154
+ else:
155
+ schema_payload = _load_json(schema_path)
156
+ if str(schema_payload.get("version", "")) != CANONICAL_VERSION:
157
+ errors.append(f"contract schema version drift: {schema_payload.get('version')!r}")
158
+
159
+ if not bundles_path.exists():
160
+ errors.append(f"missing bundles directory: {BUNDLES_DIR}")
161
+ bundles: list[dict[str, Any]] = []
162
+ else:
163
+ bundles = load_contract_bundles(root)
164
+
165
+ bundle_ids = set()
166
+ bundle_summaries: list[dict[str, Any]] = []
167
+ for bundle in bundles:
168
+ bundle_summaries.append(_bundle_summary(bundle))
169
+ bundle_id = str(bundle.get("id", "")).strip()
170
+ if not bundle_id:
171
+ errors.append(f"bundle missing id: {bundle.get('_path', '<unknown>')}")
172
+ continue
173
+ if bundle_id in bundle_ids:
174
+ errors.append(f"duplicate bundle id: {bundle_id}")
175
+ bundle_ids.add(bundle_id)
176
+ for field in REQUIRED_BUNDLE_FIELDS:
177
+ if field not in bundle:
178
+ errors.append(f"{bundle_id}: missing field {field}")
179
+ if bundle.get("version") != CANONICAL_VERSION:
180
+ errors.append(f"{bundle_id}: version drift {bundle.get('version')!r}")
181
+ hosts = bundle.get("hosts", [])
182
+ if not isinstance(hosts, list) or not hosts:
183
+ errors.append(f"{bundle_id}: hosts must be a non-empty list")
184
+ else:
185
+ bad_hosts = [host for host in hosts if host not in SUPPORTED_HOSTS]
186
+ if bad_hosts:
187
+ errors.append(f"{bundle_id}: unsupported hosts {bad_hosts}")
188
+
189
+ missing_bundles = [bundle_id for bundle_id in DEFAULT_REQUIRED_BUNDLES if bundle_id not in bundle_ids]
190
+ for bundle_id in missing_bundles:
191
+ errors.append(f"missing required bundle: {bundle_id}")
192
+
193
+ contract = {
194
+ "path": str(CONTRACT_DOC_PATH),
195
+ "schema_path": str(SCHEMA_PATH),
196
+ "version": CANONICAL_VERSION,
197
+ "bundle_count": len(bundle_summaries),
198
+ }
199
+ return {
200
+ "schema": "OmgContractValidationResult",
201
+ "status": "ok" if not errors else "error",
202
+ "contract": contract,
203
+ "bundles": bundle_summaries,
204
+ "errors": errors,
205
+ }
206
+
207
+
208
+ def _copy_contract_inputs(root: Path, output_root: Path) -> list[Path]:
209
+ copied: list[Path] = []
210
+ for rel_path in [CONTRACT_DOC_PATH, SCHEMA_PATH]:
211
+ src = root / rel_path
212
+ dst = output_root / rel_path
213
+ _write_text(dst, src.read_text(encoding="utf-8"))
214
+ copied.append(dst)
215
+ for path in sorted((root / BUNDLES_DIR).glob("*.yaml")):
216
+ rel_path = path.relative_to(root)
217
+ dst = output_root / rel_path
218
+ _write_text(dst, path.read_text(encoding="utf-8"))
219
+ copied.append(dst)
220
+ return copied
221
+
222
+
223
+ def _base_mcp_servers() -> dict[str, Any]:
224
+ return {
225
+ "context7": {
226
+ "command": "npx",
227
+ "args": ["@upstash/context7-mcp@2.1.3"],
228
+ },
229
+ "filesystem": {
230
+ "command": "npx",
231
+ "args": ["@modelcontextprotocol/server-filesystem@2026.1.14", "."],
232
+ },
233
+ "websearch": {
234
+ "command": "npx",
235
+ "args": ["@zhafron/mcp-web-search@1.2.2"],
236
+ },
237
+ "chrome-devtools": {
238
+ "command": "npx",
239
+ "args": ["chrome-devtools-mcp@0.19.0"],
240
+ },
241
+ "omg-memory": {
242
+ "type": "http",
243
+ "url": "http://127.0.0.1:8765/mcp",
244
+ },
245
+ "omg-control": {
246
+ "command": "python3",
247
+ "args": ["-m", "runtime.omg_mcp_server"],
248
+ },
249
+ }
250
+
251
+
252
+ def _build_claude_plugin() -> dict[str, Any]:
253
+ return {
254
+ "name": CANONICAL_PLUGIN_ID,
255
+ "version": CANONICAL_VERSION,
256
+ "description": "OMG plugin layer for Claude Code with native setup, orchestration, and interop.",
257
+ "author": {"name": "trac3er00"},
258
+ "repository": CANONICAL_REPO_URL,
259
+ "homepage": CANONICAL_REPO_URL,
260
+ "license": "MIT",
261
+ "keywords": [
262
+ "claude-code",
263
+ "plugin",
264
+ "orchestration",
265
+ "multi-agent",
266
+ "omg",
267
+ "codex",
268
+ "gemini",
269
+ "crazy-mode",
270
+ "escalation",
271
+ ],
272
+ "mcpServers": "./.mcp.json",
273
+ }
274
+
275
+
276
+ def _build_claude_marketplace() -> dict[str, Any]:
277
+ return {
278
+ "name": CANONICAL_MARKETPLACE_ID,
279
+ "description": "Marketplace metadata for the OMG Claude plugin",
280
+ "owner": {"name": "trac3er00"},
281
+ "metadata": {
282
+ "description": "OMG - Oh-My-God for Claude Code",
283
+ "version": CANONICAL_VERSION,
284
+ "homepage": CANONICAL_REPO_URL,
285
+ "repository": CANONICAL_REPO_URL,
286
+ },
287
+ "plugins": [
288
+ {
289
+ "name": CANONICAL_PLUGIN_ID,
290
+ "description": "OMG plugin layer for Claude Code with native setup, orchestration, and interop.",
291
+ "version": CANONICAL_VERSION,
292
+ "source": "./",
293
+ "author": {"name": "trac3er00"},
294
+ "license": "MIT",
295
+ "category": "productivity",
296
+ "tags": [
297
+ "orchestration",
298
+ "automation",
299
+ "multi-agent",
300
+ "omg",
301
+ "codex",
302
+ "gemini",
303
+ "crazy-mode",
304
+ ],
305
+ }
306
+ ],
307
+ "version": CANONICAL_VERSION,
308
+ }
309
+
310
+
311
+ def _bundle_map(bundles: Iterable[dict[str, Any]]) -> dict[str, dict[str, Any]]:
312
+ return {str(bundle["id"]): bundle for bundle in bundles}
313
+
314
+
315
+ def _compile_hook_settings(bundle: dict[str, Any]) -> dict[str, Any]:
316
+ events = bundle.get("compiled_hooks", {})
317
+ if not isinstance(events, dict):
318
+ return {}
319
+
320
+ compiled: dict[str, Any] = {}
321
+ for event_name, items in events.items():
322
+ if not isinstance(items, list):
323
+ continue
324
+ compiled_entries: list[dict[str, Any]] = []
325
+ for item in items:
326
+ if not isinstance(item, dict):
327
+ continue
328
+ command = str(item.get("command", "")).strip()
329
+ if not command:
330
+ continue
331
+ hook_payload: dict[str, Any] = {"type": "command", "command": command}
332
+ timeout = item.get("timeout")
333
+ if isinstance(timeout, int):
334
+ hook_payload["timeout"] = timeout
335
+ entry: dict[str, Any] = {"hooks": [hook_payload]}
336
+ if "matcher" in item:
337
+ entry["matcher"] = str(item.get("matcher", ""))
338
+ compiled_entries.append(entry)
339
+ if compiled_entries:
340
+ compiled[str(event_name)] = compiled_entries
341
+ return compiled
342
+
343
+
344
+ def _protected_paths_for_channel(channel: str) -> list[str]:
345
+ paths = [".omg/**", ".agents/**", ".codex/**", ".claude/**"]
346
+ if channel == "enterprise":
347
+ paths.extend(["registry/**", "dist/**"])
348
+ return paths
349
+
350
+
351
+ def _compile_claude_outputs(
352
+ *,
353
+ root: Path,
354
+ output_root: Path,
355
+ bundles: list[dict[str, Any]],
356
+ channel: str,
357
+ ) -> list[Path]:
358
+ artifacts: list[Path] = []
359
+
360
+ _write_json(output_root / ".claude-plugin" / "plugin.json", _build_claude_plugin())
361
+ artifacts.append(output_root / ".claude-plugin" / "plugin.json")
362
+
363
+ _write_json(output_root / ".claude-plugin" / "marketplace.json", _build_claude_marketplace())
364
+ artifacts.append(output_root / ".claude-plugin" / "marketplace.json")
365
+
366
+ mcp_payload = {"mcpServers": _base_mcp_servers()}
367
+ _write_json(output_root / ".mcp.json", mcp_payload)
368
+ artifacts.append(output_root / ".mcp.json")
369
+
370
+ settings_path = root / "settings.json"
371
+ settings = _load_json(settings_path)
372
+ hook_bundle = _bundle_map(bundles)["hook-governor"]
373
+ settings["hooks"] = _compile_hook_settings(hook_bundle)
374
+ omg_settings = dict(settings.get("_omg", {}))
375
+ omg_settings["_version"] = CANONICAL_VERSION
376
+ omg_settings["generated"] = {
377
+ "contract_version": CANONICAL_VERSION,
378
+ "channel": channel,
379
+ "required_bundles": list(DEFAULT_REQUIRED_BUNDLES),
380
+ "protected_paths": _protected_paths_for_channel(channel),
381
+ "emulated_events": list(hook_bundle.get("lifecycle_hooks", {}).get("emulated", [])),
382
+ }
383
+ settings["_omg"] = omg_settings
384
+ _write_json(output_root / "settings.json", settings)
385
+ artifacts.append(output_root / "settings.json")
386
+
387
+ return artifacts
388
+
389
+
390
+ def _yaml_string(value: str) -> str:
391
+ return json.dumps(value, ensure_ascii=True)
392
+
393
+
394
+ def _render_codex_skill(bundle: dict[str, Any], channel: str) -> str:
395
+ execution_modes = ", ".join(str(mode) for mode in bundle.get("execution_contract", {}).get("modes", []))
396
+ mcp_servers = ", ".join(str(name) for name in bundle.get("mcp_contract", {}).get("servers", []))
397
+ return (
398
+ f"---\n"
399
+ f"name: omg-{bundle['id']}\n"
400
+ f"description: {_yaml_string(str(bundle['description']))}\n"
401
+ f"---\n\n"
402
+ f"# {bundle['title']}\n\n"
403
+ f"- Channel: `{channel}`\n"
404
+ f"- Execution modes: `{execution_modes}`\n"
405
+ f"- MCP servers: `{mcp_servers}`\n"
406
+ f"- Evidence outputs: `{', '.join(bundle.get('evidence_outputs', {}).get('artifacts', []))}`\n"
407
+ )
408
+
409
+
410
+ def _render_openai_yaml(bundle: dict[str, Any], channel: str) -> str:
411
+ invocation = bundle.get("invocation_policy", {})
412
+ servers = bundle.get("mcp_contract", {}).get("servers", [])
413
+ tools = bundle.get("tool_policy", {}).get("allowed_tools", {}).get("codex", [])
414
+ lines = [
415
+ f"name: omg-{bundle['id']}",
416
+ f"description: {_yaml_string(str(bundle['description']))}",
417
+ f"allow_implicit_invocation: {'true' if invocation.get('allow_implicit_invocation') else 'false'}",
418
+ "metadata:",
419
+ f" channel: {channel}",
420
+ f" bundle_id: {bundle['id']}",
421
+ f" title: {_yaml_string(str(bundle['title']))}",
422
+ "mcp_servers:",
423
+ ]
424
+ for server in servers:
425
+ lines.append(f" - {server}")
426
+ lines.append("allowed_tools:")
427
+ for tool in tools:
428
+ lines.append(f" - {_yaml_string(str(tool))}")
429
+ return "\n".join(lines) + "\n"
430
+
431
+
432
+ def _compile_codex_outputs(
433
+ *,
434
+ output_root: Path,
435
+ bundles: list[dict[str, Any]],
436
+ channel: str,
437
+ ) -> list[Path]:
438
+ artifacts: list[Path] = []
439
+ shared_dir = output_root / ".agents" / "skills" / "omg"
440
+ shared_dir.mkdir(parents=True, exist_ok=True)
441
+
442
+ rules_fragment = (
443
+ "# OMG Codex Protection Rules\n\n"
444
+ f"- Channel: `{channel}`\n"
445
+ "- Protect `.omg/`, `.agents/`, `.codex/`, and `.claude/` from unreviewed mutation.\n"
446
+ "- Require explicit invocation for production-control-plane skills.\n"
447
+ )
448
+ _write_text(shared_dir / "AGENTS.fragment.md", rules_fragment)
449
+ artifacts.append(shared_dir / "AGENTS.fragment.md")
450
+
451
+ from runtime.mcp_config_writers import write_codex_mcp_stdio_config
452
+
453
+ codex_mcp_path = shared_dir / "codex-mcp.toml"
454
+ write_codex_mcp_stdio_config(
455
+ command="python3",
456
+ args=["-m", "runtime.omg_mcp_server"],
457
+ server_name="omg-control",
458
+ config_path=codex_mcp_path,
459
+ )
460
+ artifacts.append(codex_mcp_path)
461
+
462
+ for bundle in bundles:
463
+ if "codex" not in bundle.get("hosts", []):
464
+ continue
465
+ skill_dir = shared_dir / str(bundle["id"])
466
+ _write_text(skill_dir / "SKILL.md", _render_codex_skill(bundle, channel))
467
+ _write_text(skill_dir / "openai.yaml", _render_openai_yaml(bundle, channel))
468
+ artifacts.extend([skill_dir / "SKILL.md", skill_dir / "openai.yaml"])
469
+
470
+ return artifacts
471
+
472
+
473
+ def _copy_release_bundle(
474
+ *,
475
+ output_root: Path,
476
+ channel: str,
477
+ artifacts: list[Path],
478
+ ) -> list[Path]:
479
+ bundle_root = output_root / "dist" / channel / "bundle"
480
+ if bundle_root.exists():
481
+ shutil.rmtree(bundle_root)
482
+ copied: list[Path] = []
483
+ for path in sorted(set(artifacts)):
484
+ rel_path = path.relative_to(output_root)
485
+ dst = bundle_root / rel_path
486
+ dst.parent.mkdir(parents=True, exist_ok=True)
487
+ shutil.copy2(path, dst)
488
+ copied.append(dst)
489
+ return copied
490
+
491
+
492
+ def _build_dist_manifest(output_root: Path, *, channel: str, artifacts: list[Path]) -> Path:
493
+ dist_root = output_root / "dist" / channel
494
+ payload = {
495
+ "schema": "OmgCompiledArtifactManifest",
496
+ "channel": channel,
497
+ "contract_version": CANONICAL_VERSION,
498
+ "artifacts": [
499
+ {
500
+ "path": str(path.relative_to(dist_root)),
501
+ "sha256": _sha256_file(path),
502
+ }
503
+ for path in sorted(set(artifacts))
504
+ ],
505
+ }
506
+ out_path = dist_root / "manifest.json"
507
+ _write_json(out_path, payload)
508
+ return out_path
509
+
510
+
511
+ def compile_contract_outputs(
512
+ *,
513
+ root_dir: str | Path | None = None,
514
+ output_root: str | Path | None = None,
515
+ hosts: list[str] | tuple[str, ...] | None = None,
516
+ channel: str = "public",
517
+ ) -> dict[str, Any]:
518
+ root = _resolve_root(root_dir)
519
+ output = _resolve_output_root(root, output_root)
520
+ validation = validate_contract_registry(root)
521
+ if validation["status"] != "ok":
522
+ return {
523
+ "schema": "OmgContractCompileResult",
524
+ "status": "error",
525
+ "channel": channel,
526
+ "hosts": list(hosts or SUPPORTED_HOSTS),
527
+ "errors": validation["errors"],
528
+ "artifacts": [],
529
+ }
530
+
531
+ if channel not in SUPPORTED_CHANNELS:
532
+ return {
533
+ "schema": "OmgContractCompileResult",
534
+ "status": "error",
535
+ "channel": channel,
536
+ "hosts": list(hosts or SUPPORTED_HOSTS),
537
+ "errors": [f"unsupported channel: {channel}"],
538
+ "artifacts": [],
539
+ }
540
+
541
+ selected_hosts = list(hosts or SUPPORTED_HOSTS)
542
+ bad_hosts = [host for host in selected_hosts if host not in SUPPORTED_HOSTS]
543
+ if bad_hosts:
544
+ return {
545
+ "schema": "OmgContractCompileResult",
546
+ "status": "error",
547
+ "channel": channel,
548
+ "hosts": selected_hosts,
549
+ "errors": [f"unsupported hosts: {bad_hosts}"],
550
+ "artifacts": [],
551
+ }
552
+
553
+ bundles = load_contract_bundles(root)
554
+ artifacts = _copy_contract_inputs(root, output)
555
+
556
+ if "claude" in selected_hosts:
557
+ artifacts.extend(_compile_claude_outputs(root=root, output_root=output, bundles=bundles, channel=channel))
558
+ if "codex" in selected_hosts:
559
+ artifacts.extend(_compile_codex_outputs(output_root=output, bundles=bundles, channel=channel))
560
+
561
+ bundled_artifacts = _copy_release_bundle(output_root=output, channel=channel, artifacts=artifacts)
562
+ manifest_path = _build_dist_manifest(output, channel=channel, artifacts=bundled_artifacts)
563
+ artifacts.append(manifest_path)
564
+
565
+ return {
566
+ "schema": "OmgContractCompileResult",
567
+ "status": "ok",
568
+ "channel": channel,
569
+ "hosts": selected_hosts,
570
+ "artifacts": [str(path.relative_to(output)) for path in artifacts],
571
+ "manifest": str(manifest_path.relative_to(output)),
572
+ }
573
+
574
+
575
+ def _provider_statuses() -> dict[str, dict[str, Any]]:
576
+ ready_override = {
577
+ item.strip()
578
+ for item in os.environ.get("OMG_RELEASE_READY_PROVIDERS", "").split(",")
579
+ if item.strip()
580
+ }
581
+ statuses: dict[str, dict[str, Any]] = {}
582
+
583
+ for provider_name in ("claude", "codex"):
584
+ if provider_name in ready_override:
585
+ statuses[provider_name] = {"ready": True, "source": "env"}
586
+ continue
587
+
588
+ if provider_name == "claude":
589
+ claude_bin = os.environ.get("OMG_CLAUDE_BIN", "claude")
590
+ cmd = os.environ.get("OMG_CLAUDE_WORKER_CMD", "").strip()
591
+ ready = bool(cmd) or shutil.which(claude_bin) is not None
592
+ statuses[provider_name] = {
593
+ "ready": ready,
594
+ "source": "env-cmd" if cmd else "path",
595
+ "detail": cmd or claude_bin,
596
+ }
597
+ continue
598
+
599
+ import runtime.providers.codex_provider # noqa: F401
600
+ from runtime.cli_provider import get_provider
601
+
602
+ provider = get_provider("codex")
603
+ ready = bool(provider and provider.detect())
604
+ statuses[provider_name] = {"ready": ready, "source": "provider"}
605
+
606
+ return statuses
607
+
608
+
609
+ def _check_mcp_fabric() -> dict[str, Any]:
610
+ import runtime.omg_mcp_server as omg_mcp_server
611
+
612
+ prompts = asyncio.run(omg_mcp_server.mcp.list_prompts())
613
+ resources = asyncio.run(omg_mcp_server.mcp.list_resources())
614
+ instructions = getattr(omg_mcp_server.mcp, "instructions", "")
615
+ return {
616
+ "ready": isinstance(instructions, str) and bool(instructions.strip()) and len(prompts) >= 1 and len(resources) >= 1,
617
+ "prompt_count": len(prompts),
618
+ "resource_count": len(resources),
619
+ }
620
+
621
+
622
+ def build_release_readiness(
623
+ *,
624
+ root_dir: str | Path | None = None,
625
+ output_root: str | Path | None = None,
626
+ channel: str = "dual",
627
+ ) -> dict[str, Any]:
628
+ root = _resolve_root(root_dir)
629
+ output = _resolve_output_root(root, output_root)
630
+ blockers: list[str] = []
631
+ checks: dict[str, Any] = {}
632
+
633
+ validation = validate_contract_registry(root)
634
+ checks["contract_validation"] = validation
635
+ if validation["status"] != "ok":
636
+ blockers.extend(validation["errors"])
637
+
638
+ required_channels = ["public", "enterprise"] if channel == "dual" else [channel]
639
+ for required_channel in required_channels:
640
+ dist_root = output / "dist" / required_channel
641
+ manifest_path = dist_root / "manifest.json"
642
+ if not manifest_path.exists():
643
+ blockers.append(f"missing compiled manifest: dist/{required_channel}/manifest.json")
644
+ continue
645
+ manifest = _load_json(manifest_path)
646
+ manifest_errors: list[str] = []
647
+ for artifact in manifest.get("artifacts", []):
648
+ if not isinstance(artifact, dict):
649
+ continue
650
+ rel_path = str(artifact.get("path", ""))
651
+ expected_sha = str(artifact.get("sha256", ""))
652
+ artifact_path = dist_root / rel_path
653
+ if not artifact_path.exists():
654
+ manifest_errors.append(f"{required_channel}: missing bundled artifact {rel_path}")
655
+ continue
656
+ if _sha256_file(artifact_path) != expected_sha:
657
+ manifest_errors.append(f"{required_channel}: sha mismatch for {rel_path}")
658
+ if manifest_errors:
659
+ blockers.extend(manifest_errors)
660
+ manifest["integrity_errors"] = manifest_errors
661
+ checks[f"dist_{required_channel}"] = manifest
662
+
663
+ required_outputs = [
664
+ output / ".claude-plugin" / "plugin.json",
665
+ output / ".claude-plugin" / "marketplace.json",
666
+ output / ".mcp.json",
667
+ output / "settings.json",
668
+ output / ".agents" / "skills" / "omg" / "control-plane" / "SKILL.md",
669
+ output / ".agents" / "skills" / "omg" / "control-plane" / "openai.yaml",
670
+ ]
671
+ missing_outputs = [str(path.relative_to(output)) for path in required_outputs if not path.exists()]
672
+ if missing_outputs:
673
+ blockers.append(f"missing compiled outputs: {', '.join(missing_outputs)}")
674
+ checks["compiled_outputs"] = {"missing": missing_outputs}
675
+
676
+ providers = _provider_statuses()
677
+ checks["providers"] = providers
678
+ for provider_name, status in providers.items():
679
+ if not status.get("ready"):
680
+ blockers.append(f"provider not ready: {provider_name}")
681
+
682
+ worktree_ready = shutil.which("git") is not None and (root / ".git").exists()
683
+ checks["worktree"] = {"ready": worktree_ready}
684
+ if not worktree_ready:
685
+ blockers.append("git worktree support not available")
686
+
687
+ mcp_status = _check_mcp_fabric()
688
+ checks["mcp_fabric"] = mcp_status
689
+ if not mcp_status.get("ready"):
690
+ blockers.append("mcp fabric incomplete")
691
+
692
+ return {
693
+ "schema": "OmgReleaseReadinessResult",
694
+ "status": "ok" if not blockers else "error",
695
+ "channel": channel,
696
+ "blockers": blockers,
697
+ "checks": checks,
698
+ }