@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.
- package/.agents/skills/omg/AGENTS.fragment.md +5 -0
- package/.agents/skills/omg/codex-mcp.toml +4 -0
- package/.agents/skills/omg/control-plane/SKILL.md +11 -0
- package/.agents/skills/omg/control-plane/openai.yaml +14 -0
- package/.agents/skills/omg/hook-governor/SKILL.md +11 -0
- package/.agents/skills/omg/hook-governor/openai.yaml +11 -0
- package/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
- package/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
- package/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
- package/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
- package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
- package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +20 -4
- package/CHANGELOG.md +16 -0
- package/OMG-setup.sh +9 -3
- package/OMG_COMPAT_CONTRACT.md +92 -0
- package/README.md +26 -8
- package/SECURITY.md +6 -0
- package/commands/OMG:api-twin.md +22 -0
- package/commands/OMG:preflight.md +26 -0
- package/commands/OMG:security-check.md +28 -0
- package/commands/OMG:setup.md +1 -2
- package/dist/enterprise/bundle/.agents/skills/omg/AGENTS.fragment.md +5 -0
- package/dist/enterprise/bundle/.agents/skills/omg/codex-mcp.toml +4 -0
- package/dist/enterprise/bundle/.agents/skills/omg/control-plane/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/control-plane/openai.yaml +14 -0
- package/dist/enterprise/bundle/.agents/skills/omg/hook-governor/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/hook-governor/openai.yaml +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
- package/dist/enterprise/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
- package/dist/enterprise/bundle/.claude-plugin/marketplace.json +36 -0
- package/dist/enterprise/bundle/.claude-plugin/plugin.json +23 -0
- package/dist/enterprise/bundle/.mcp.json +40 -0
- package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +92 -0
- package/dist/enterprise/bundle/settings.json +366 -0
- package/dist/enterprise/manifest.json +99 -0
- package/dist/public/bundle/.agents/skills/omg/AGENTS.fragment.md +5 -0
- package/dist/public/bundle/.agents/skills/omg/codex-mcp.toml +4 -0
- package/dist/public/bundle/.agents/skills/omg/control-plane/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/control-plane/openai.yaml +14 -0
- package/dist/public/bundle/.agents/skills/omg/hook-governor/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/hook-governor/openai.yaml +11 -0
- package/dist/public/bundle/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
- package/dist/public/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
- package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
- package/dist/public/bundle/.claude-plugin/marketplace.json +36 -0
- package/dist/public/bundle/.claude-plugin/plugin.json +23 -0
- package/dist/public/bundle/.mcp.json +40 -0
- package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +92 -0
- package/dist/public/bundle/settings.json +366 -0
- package/dist/public/manifest.json +99 -0
- package/hooks/policy_engine.py +38 -7
- package/hooks/post-write.py +1 -1
- package/hooks/prompt-enhancer.py +2 -2
- package/hooks/security_validators.py +75 -0
- package/hooks/setup_wizard.py +44 -20
- package/hooks/shadow_manager.py +22 -2
- package/package.json +1 -1
- package/plugins/README.md +4 -2
- package/plugins/advanced/commands/OMG:deep-plan.md +1 -1
- package/plugins/advanced/commands/OMG:security-review.md +10 -113
- package/plugins/advanced/commands/OMG:ship.md +1 -1
- package/plugins/advanced/plugin.json +1 -10
- package/plugins/core/plugin.json +25 -2
- package/pyproject.toml +1 -1
- package/runtime/adoption.py +1 -1
- package/runtime/api_twin.py +130 -0
- package/runtime/compat.py +21 -1
- package/runtime/contract_compiler.py +698 -0
- package/runtime/domain_packs.py +34 -0
- package/runtime/guide_assert.py +45 -0
- package/runtime/mcp_config_writers.py +145 -39
- package/runtime/omg_compat_contract_snapshot.json +8 -7
- package/runtime/omg_contract_snapshot.json +8 -7
- package/runtime/omg_mcp_server.py +205 -0
- package/runtime/preflight.py +52 -0
- package/runtime/providers/codex_provider.py +2 -12
- package/runtime/providers/gemini_provider.py +2 -21
- package/runtime/providers/kimi_provider.py +2 -21
- package/runtime/runtime_profile.py +61 -0
- package/runtime/security_check.py +347 -0
- package/runtime/subagent_dispatcher.py +117 -10
- package/runtime/team_router.py +3 -3
- package/runtime/untrusted_content.py +102 -0
- package/scripts/omg.py +174 -1
- package/settings.json +66 -18
- package/tools/python_repl.py +33 -3
- 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
|
+
}
|