@trac3er/oh-my-god 2.0.8 → 2.0.9

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 (118) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.gemini/settings.json +11 -0
  4. package/.kimi/mcp.json +11 -0
  5. package/CHANGELOG.md +10 -0
  6. package/OMG-setup.sh +1 -1
  7. package/OMG_COMPAT_CONTRACT.md +10 -4
  8. package/README.md +1 -0
  9. package/build/lib/commands/OMG:forge.md +92 -0
  10. package/build/lib/commands/OMG:mode.md +13 -13
  11. package/build/lib/commands/OMG:session-branch.md +17 -1
  12. package/build/lib/commands/OMG:session-fork.md +5 -1
  13. package/build/lib/commands/OMG:session-merge.md +5 -1
  14. package/build/lib/control_plane/server.py +4 -0
  15. package/build/lib/control_plane/service.py +55 -0
  16. package/build/lib/hooks/setup_wizard.py +21 -1
  17. package/build/lib/hooks/shadow_manager.py +25 -2
  18. package/build/lib/hooks/state_migration.py +3 -0
  19. package/build/lib/plugins/dephealth/cve_scanner.py +91 -0
  20. package/build/lib/plugins/dephealth/vuln_analyzer.py +7 -0
  21. package/build/lib/registry/omg-capability.schema.json +83 -1
  22. package/build/lib/runtime/adoption.py +12 -4
  23. package/build/lib/runtime/artifact_parsers.py +161 -0
  24. package/build/lib/runtime/background_verification.py +48 -0
  25. package/build/lib/runtime/claim_judge.py +184 -7
  26. package/build/lib/runtime/contract_compiler.py +118 -9
  27. package/build/lib/runtime/evidence_query.py +203 -0
  28. package/build/lib/runtime/omg_mcp_server.py +19 -0
  29. package/build/lib/runtime/playwright_adapter.py +39 -0
  30. package/build/lib/runtime/proof_chain.py +136 -8
  31. package/build/lib/runtime/proof_gate.py +102 -0
  32. package/build/lib/runtime/providers/gemini_provider.py +7 -0
  33. package/build/lib/runtime/providers/kimi_provider.py +7 -0
  34. package/build/lib/runtime/repro_pack.py +292 -0
  35. package/build/lib/runtime/runtime_profile.py +87 -15
  36. package/build/lib/runtime/security_check.py +86 -3
  37. package/build/lib/runtime/test_intent_lock.py +47 -0
  38. package/build/lib/runtime/tracebank.py +33 -3
  39. package/build/lib/runtime/verification_loop.py +73 -0
  40. package/commands/OMG:forge.md +92 -0
  41. package/commands/OMG:mode.md +13 -13
  42. package/commands/OMG:session-branch.md +17 -1
  43. package/commands/OMG:session-fork.md +5 -1
  44. package/commands/OMG:session-merge.md +5 -1
  45. package/control_plane/server.py +4 -0
  46. package/control_plane/service.py +55 -0
  47. package/dist/enterprise/bundle/.gemini/settings.json +11 -0
  48. package/dist/enterprise/bundle/.kimi/mcp.json +11 -0
  49. package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +9 -3
  50. package/dist/enterprise/bundle/registry/omg-capability.schema.json +83 -1
  51. package/dist/enterprise/bundle/settings.json +1 -0
  52. package/dist/enterprise/manifest.json +17 -3
  53. package/dist/public/bundle/.agents/skills/omg/incident-replay/SKILL.md +1 -1
  54. package/dist/public/bundle/.agents/skills/omg/incident-replay/openai.yaml +1 -1
  55. package/dist/public/bundle/.agents/skills/omg/lsp-pack/SKILL.md +1 -1
  56. package/dist/public/bundle/.agents/skills/omg/lsp-pack/openai.yaml +1 -1
  57. package/dist/public/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +1 -1
  58. package/dist/public/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +1 -1
  59. package/dist/public/bundle/.agents/skills/omg/plan-council/SKILL.md +1 -1
  60. package/dist/public/bundle/.agents/skills/omg/plan-council/openai.yaml +1 -1
  61. package/dist/public/bundle/.agents/skills/omg/preflight/SKILL.md +1 -1
  62. package/dist/public/bundle/.agents/skills/omg/preflight/openai.yaml +1 -1
  63. package/dist/public/bundle/.agents/skills/omg/proof-gate/SKILL.md +1 -1
  64. package/dist/public/bundle/.agents/skills/omg/proof-gate/openai.yaml +1 -1
  65. package/dist/public/bundle/.agents/skills/omg/remote-supervisor/SKILL.md +1 -1
  66. package/dist/public/bundle/.agents/skills/omg/remote-supervisor/openai.yaml +1 -1
  67. package/dist/public/bundle/.agents/skills/omg/robotics/SKILL.md +1 -1
  68. package/dist/public/bundle/.agents/skills/omg/robotics/openai.yaml +1 -1
  69. package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +1 -1
  70. package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +1 -1
  71. package/dist/public/bundle/.agents/skills/omg/security-check/SKILL.md +1 -1
  72. package/dist/public/bundle/.agents/skills/omg/security-check/openai.yaml +1 -1
  73. package/dist/public/bundle/.agents/skills/omg/test-intent-lock/SKILL.md +1 -1
  74. package/dist/public/bundle/.agents/skills/omg/test-intent-lock/openai.yaml +1 -1
  75. package/dist/public/bundle/.agents/skills/omg/tracebank/SKILL.md +1 -1
  76. package/dist/public/bundle/.agents/skills/omg/tracebank/openai.yaml +1 -1
  77. package/dist/public/bundle/.agents/skills/omg/vision/SKILL.md +1 -1
  78. package/dist/public/bundle/.agents/skills/omg/vision/openai.yaml +1 -1
  79. package/dist/public/bundle/.gemini/settings.json +11 -0
  80. package/dist/public/bundle/.kimi/mcp.json +11 -0
  81. package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +9 -3
  82. package/dist/public/bundle/registry/omg-capability.schema.json +83 -1
  83. package/dist/public/bundle/settings.json +2 -1
  84. package/dist/public/manifest.json +43 -29
  85. package/docs/proof.md +1 -0
  86. package/hooks/setup_wizard.py +21 -1
  87. package/hooks/shadow_manager.py +25 -2
  88. package/hooks/state_migration.py +3 -0
  89. package/hud/omg-hud.mjs +66 -3
  90. package/package.json +1 -1
  91. package/plugins/advanced/plugin.json +1 -1
  92. package/plugins/core/plugin.json +1 -1
  93. package/plugins/dephealth/cve_scanner.py +91 -0
  94. package/plugins/dephealth/vuln_analyzer.py +7 -0
  95. package/pyproject.toml +1 -1
  96. package/registry/omg-capability.schema.json +83 -1
  97. package/runtime/adoption.py +13 -5
  98. package/runtime/artifact_parsers.py +161 -0
  99. package/runtime/background_verification.py +48 -0
  100. package/runtime/claim_judge.py +184 -7
  101. package/runtime/contract_compiler.py +118 -9
  102. package/runtime/evidence_query.py +203 -0
  103. package/runtime/omg_mcp_server.py +19 -0
  104. package/runtime/playwright_adapter.py +39 -0
  105. package/runtime/proof_chain.py +136 -8
  106. package/runtime/proof_gate.py +102 -0
  107. package/runtime/providers/gemini_provider.py +7 -0
  108. package/runtime/providers/kimi_provider.py +7 -0
  109. package/runtime/repro_pack.py +292 -0
  110. package/runtime/runtime_profile.py +87 -15
  111. package/runtime/security_check.py +86 -3
  112. package/runtime/test_intent_lock.py +47 -0
  113. package/runtime/tracebank.py +33 -3
  114. package/runtime/verification_loop.py +73 -0
  115. package/scripts/omg.py +30 -3
  116. package/settings.json +4 -3
  117. package/tools/python_sandbox.py +9 -6
  118. package/tools/session_snapshot.py +146 -40
@@ -6,6 +6,7 @@ Maintains overlay-style shadow writes and evidence artifacts.
6
6
  from __future__ import annotations
7
7
 
8
8
  import hashlib
9
+ import importlib
9
10
  import json
10
11
  import os
11
12
  import platform
@@ -18,8 +19,15 @@ from typing import Any
18
19
  HOOKS_DIR = os.path.dirname(__file__)
19
20
  if HOOKS_DIR not in sys.path:
20
21
  sys.path.insert(0, HOOKS_DIR)
21
- from _common import _resolve_project_dir
22
- from security_validators import ensure_path_within_dir, validate_opaque_identifier
22
+ try:
23
+ from hooks._common import _resolve_project_dir
24
+ from hooks.security_validators import ensure_path_within_dir, validate_opaque_identifier
25
+ except ImportError:
26
+ _common = importlib.import_module("_common")
27
+ security_validators = importlib.import_module("security_validators")
28
+ _resolve_project_dir = _common._resolve_project_dir
29
+ ensure_path_within_dir = security_validators.ensure_path_within_dir
30
+ validate_opaque_identifier = security_validators.validate_opaque_identifier
23
31
 
24
32
 
25
33
  def _project_dir() -> str:
@@ -189,6 +197,11 @@ def create_evidence_pack(
189
197
  lineage: dict[str, Any] | None = None,
190
198
  executor: dict[str, Any] | None = None,
191
199
  environment: dict[str, Any] | None = None,
200
+ artifacts: list[dict[str, Any]] | None = None,
201
+ claims: list[dict[str, Any]] | None = None,
202
+ test_delta: dict[str, Any] | None = None,
203
+ browser_evidence_path: str | None = None,
204
+ repro_pack_path: str | None = None,
192
205
  ) -> str:
193
206
  ensure_shadow_dirs(project_dir)
194
207
  run_id = _validated_run_id(run_id)
@@ -209,6 +222,7 @@ def create_evidence_pack(
209
222
 
210
223
  evidence = {
211
224
  "schema": "EvidencePack",
225
+ "schema_version": 2,
212
226
  "run_id": run_id,
213
227
  "created_at": _utc_now(),
214
228
  "tests": tests or [],
@@ -224,7 +238,16 @@ def create_evidence_pack(
224
238
  "lineage": lineage or {},
225
239
  "executor": executor,
226
240
  "environment": environment,
241
+ "artifacts": artifacts or [],
227
242
  }
243
+ if claims is not None:
244
+ evidence["claims"] = claims
245
+ if test_delta is not None:
246
+ evidence["test_delta"] = test_delta
247
+ if browser_evidence_path is not None:
248
+ evidence["browser_evidence_path"] = browser_evidence_path
249
+ if repro_pack_path is not None:
250
+ evidence["repro_pack_path"] = repro_pack_path
228
251
  evidence_path = ensure_path_within_dir(
229
252
  _evidence_root(project_dir),
230
253
  os.path.join(_evidence_root(project_dir), f"{run_id}.json"),
@@ -115,6 +115,9 @@ MIGRATION_MAP: list[tuple[str, str, str]] = [
115
115
  ("dir", "repl_sessions", "state/repl_sessions"),
116
116
  ("dir", "sessions", "state/sessions"),
117
117
  ("dir", "snapshots", "state/snapshots"),
118
+ ("file", "current_branch.json", "state/current_branch.json"),
119
+ ("file", "background-verification.json", "state/background-verification.json"),
120
+ ("dir", "branches", "state/branches"),
118
121
  ("dir", "knowledge", "knowledge"),
119
122
  ]
120
123
 
package/hud/omg-hud.mjs CHANGED
@@ -55,7 +55,7 @@ function readOmgVersion() {
55
55
  const OMG_VERSION = readOmgVersion();
56
56
 
57
57
  const DEFAULT_HUD_CONFIG = {
58
- preset: "focused",
58
+ preset: "standard",
59
59
  elements: {
60
60
  cwd: true,
61
61
  cwdFormat: "relative",
@@ -129,7 +129,7 @@ const PRESET_CONFIGS = {
129
129
  safeMode: true,
130
130
  inventory: true,
131
131
  },
132
- focused: {
132
+ standard: {
133
133
  cwd: true,
134
134
  cwdFormat: "relative",
135
135
  gitRepo: false,
@@ -251,6 +251,17 @@ const PRESET_CONFIGS = {
251
251
  },
252
252
  };
253
253
 
254
+ const HUD_PRESET_ALIASES = {
255
+ focused: "standard",
256
+ };
257
+
258
+ function resolveHudPreset(preset) {
259
+ const requested = typeof preset === "string" ? preset : DEFAULT_HUD_CONFIG.preset;
260
+ const canonical = HUD_PRESET_ALIASES[requested] || requested;
261
+ if (PRESET_CONFIGS[canonical]) return canonical;
262
+ return DEFAULT_HUD_CONFIG.preset;
263
+ }
264
+
254
265
  function countByExt(dirPath, ext) {
255
266
  if (!existsSync(dirPath)) return 0;
256
267
  try {
@@ -373,7 +384,7 @@ function readRawHudConfig() {
373
384
 
374
385
  function readHudConfig() {
375
386
  const source = readRawHudConfig();
376
- const preset = source.preset || DEFAULT_HUD_CONFIG.preset;
387
+ const preset = resolveHudPreset(source.preset);
377
388
  const presetElements = PRESET_CONFIGS[preset] || {};
378
389
  const elements = {
379
390
  ...DEFAULT_HUD_CONFIG.elements,
@@ -530,6 +541,7 @@ function readOmgState(cwd) {
530
541
  const stateDir = join(cwd, ".omg", "state");
531
542
  const result = {
532
543
  modes: [],
544
+ currentMode: null,
533
545
  hookCount: 0,
534
546
  profile: null,
535
547
  ralph: null,
@@ -564,6 +576,14 @@ function readOmgState(cwd) {
564
576
  }
565
577
  }
566
578
 
579
+ const modeState = readJsonSafe(join(stateDir, "mode.json"));
580
+ if (modeState && typeof modeState === "object") {
581
+ const candidate = modeState.mode || modeState.current_mode || modeState.name;
582
+ if (typeof candidate === "string" && candidate.trim()) {
583
+ result.currentMode = candidate.trim().toLowerCase();
584
+ }
585
+ }
586
+
567
587
  const ralphState = readJsonSafe(join(stateDir, "ralph-state.json"));
568
588
  if (ralphState?.active) {
569
589
  result.ralph = {
@@ -627,6 +647,19 @@ function readOmgState(cwd) {
627
647
  return result;
628
648
  }
629
649
 
650
+ function readBackgroundVerificationState(stateDir) {
651
+ const filePath = join(stateDir, "background-verification.json");
652
+ const data = readJsonSafe(filePath);
653
+ if (!data || data.schema !== "BackgroundVerificationState") return null;
654
+ return {
655
+ status: data.status || "unknown",
656
+ blockers: Array.isArray(data.blockers) ? data.blockers : [],
657
+ evidence_links: Array.isArray(data.evidence_links) ? data.evidence_links : [],
658
+ progress: data.progress || {},
659
+ updated_at: data.updated_at || null,
660
+ };
661
+ }
662
+
630
663
  function parseTranscript(transcriptPath) {
631
664
  const result = {
632
665
  tools: 0,
@@ -814,6 +847,29 @@ function renderBackgroundTasks(tasks) {
814
847
  return `bg:${colorFn(`${running}/${MAX}`)}`;
815
848
  }
816
849
 
850
+ function renderVerificationStatus(state) {
851
+ if (!state) return dim("verification: unknown");
852
+ const { status, blockers, evidence_links } = state;
853
+ const blockerCount = blockers.length;
854
+ const latestEvidence = evidence_links.length > 0
855
+ ? evidence_links[evidence_links.length - 1]
856
+ : null;
857
+ const evidenceSuffix = latestEvidence
858
+ ? ` ${dim(`evidence:${basename(latestEvidence)}`)}`
859
+ : "";
860
+ if (status === "ok") {
861
+ return green("\u2713 verification ok") + evidenceSuffix;
862
+ }
863
+ if (status === "running") {
864
+ return yellow("\u27F3 verification running") + evidenceSuffix;
865
+ }
866
+ if (status === "error" || status === "blocked") {
867
+ const blockerSuffix = blockerCount > 0 ? ` (${blockerCount} blockers)` : "";
868
+ return red(`\u2717 verification blocked${blockerSuffix}`) + evidenceSuffix;
869
+ }
870
+ return dim("verification: unknown");
871
+ }
872
+
817
873
  function renderTodos(todos) {
818
874
  if (!todos || todos.length === 0) return null;
819
875
  const completed = todos.filter((t) => t.status === "completed").length;
@@ -1136,6 +1192,9 @@ async function main() {
1136
1192
 
1137
1193
  // Active skills (modes) + last skill
1138
1194
  if (cfg.elements.activeSkills !== false) {
1195
+ if (omgState.currentMode) {
1196
+ els.push(`mode:${cyan(omgState.currentMode)}`);
1197
+ }
1139
1198
  const modeBadges = renderModeBadges(omgState.modes, {
1140
1199
  hideRalph: !!omgState.ralph,
1141
1200
  hideAutopilot: !!omgState.autopilot,
@@ -1181,6 +1240,10 @@ async function main() {
1181
1240
  if (bgEl) els.push(bgEl);
1182
1241
  }
1183
1242
 
1243
+ const verificationStateDir = join(cwd, ".omg", "state");
1244
+ const verificationState = readBackgroundVerificationState(verificationStateDir);
1245
+ els.push(renderVerificationStatus(verificationState));
1246
+
1184
1247
  // Model name
1185
1248
  if (cfg.elements.model !== false && model && model !== "?") {
1186
1249
  els.push(dim(model));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trac3er/oh-my-god",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "OMG (Oh My God) — Multi-agent orchestration, evidence-backed verification, and durable session state for Claude Code, Codex, and supported agent hosts",
5
5
  "main": "OMG-setup.sh",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omg-advanced",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "Advanced OMG commands - deep planning, learning, code review, and specialized workflows",
5
5
  "type": "omg-plugin",
6
6
  "commands": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omg-core",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "Core OMG commands - native setup, routing, orchestration, and verification surfaces",
5
5
  "type": "omg-plugin",
6
6
  "commands": {
@@ -10,7 +10,11 @@ import urllib.request
10
10
 
11
11
 
12
12
  OSV_BATCH_URL = "https://api.osv.dev/v1/querybatch"
13
+ KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
14
+ EPSS_URL_TEMPLATE = "https://api.first.org/data/v1/epss?cve={cve_id}"
13
15
  CACHE_REL_PATH = Path(".omg") / "state" / "cve-cache.json"
16
+ KEV_CACHE_REL_PATH = Path(".omg") / "state" / "kev-cache.json"
17
+ EPSS_CACHE_REL_PATH = Path(".omg") / "state" / "epss-cache.json"
14
18
  CACHE_TTL_HOURS = 24
15
19
 
16
20
 
@@ -72,6 +76,93 @@ def scan_for_cves(dependency_list: list[dict[str, str]], project_dir: str = ".")
72
76
  return scan_result
73
77
 
74
78
 
79
+ def enrich_with_kev(finding: dict[str, Any], cache_dir: str) -> dict[str, Any]:
80
+ result = dict(finding)
81
+ if not _dep_health_enabled():
82
+ result["kev_listed"] = False
83
+ return result
84
+
85
+ cve_id = str(finding.get("id", "")).strip()
86
+ if not cve_id:
87
+ result["kev_listed"] = False
88
+ return result
89
+
90
+ cache_path = Path(cache_dir) / KEV_CACHE_REL_PATH
91
+ cached = _load_cache(cache_path)
92
+
93
+ if cached and _is_cache_fresh(cached.get("fetched_at")):
94
+ result["kev_listed"] = cve_id in cached.get("cve_ids", [])
95
+ return result
96
+
97
+ try:
98
+ kev_data = _fetch_kev_catalog()
99
+ kev_ids = [v.get("cveID", "") for v in kev_data.get("vulnerabilities", [])]
100
+ _save_cache(cache_path, {
101
+ "fetched_at": datetime.now(timezone.utc).isoformat(),
102
+ "cve_ids": kev_ids,
103
+ })
104
+ result["kev_listed"] = cve_id in kev_ids
105
+ except (urllib.error.URLError, OSError, json.JSONDecodeError):
106
+ if cached:
107
+ result["kev_listed"] = cve_id in cached.get("cve_ids", [])
108
+ else:
109
+ result["kev_listed"] = False
110
+ return result
111
+
112
+
113
+ def enrich_with_epss(finding: dict[str, Any], cache_dir: str) -> dict[str, Any]:
114
+ result = dict(finding)
115
+ if not _dep_health_enabled():
116
+ result["epss_score"] = None
117
+ return result
118
+
119
+ cve_id = str(finding.get("id", "")).strip()
120
+ if not cve_id:
121
+ result["epss_score"] = None
122
+ return result
123
+
124
+ cache_path = Path(cache_dir) / EPSS_CACHE_REL_PATH
125
+ cached = _load_cache(cache_path) or {}
126
+ entries = cached.get("entries", {})
127
+
128
+ entry = entries.get(cve_id)
129
+ if entry and _is_cache_fresh(entry.get("fetched_at")):
130
+ result["epss_score"] = entry.get("epss")
131
+ return result
132
+
133
+ try:
134
+ epss_data = _fetch_epss_score(cve_id)
135
+ data_list = epss_data.get("data", [])
136
+ score = float(data_list[0].get("epss", 0)) if data_list else None
137
+ entries[cve_id] = {
138
+ "epss": score,
139
+ "fetched_at": datetime.now(timezone.utc).isoformat(),
140
+ }
141
+ _save_cache(cache_path, {"entries": entries})
142
+ result["epss_score"] = score
143
+ except (urllib.error.URLError, OSError, json.JSONDecodeError, ValueError, TypeError):
144
+ if entry:
145
+ result["epss_score"] = entry.get("epss")
146
+ else:
147
+ result["epss_score"] = None
148
+ return result
149
+
150
+
151
+ def _fetch_kev_catalog() -> dict[str, Any]:
152
+ request = urllib.request.Request(KEV_URL, headers={"Accept": "application/json"})
153
+ with urllib.request.urlopen(request, timeout=15) as response:
154
+ body = response.read().decode("utf-8")
155
+ return json.loads(body)
156
+
157
+
158
+ def _fetch_epss_score(cve_id: str) -> dict[str, Any]:
159
+ url = EPSS_URL_TEMPLATE.format(cve_id=cve_id)
160
+ request = urllib.request.Request(url, headers={"Accept": "application/json"})
161
+ with urllib.request.urlopen(request, timeout=15) as response:
162
+ body = response.read().decode("utf-8")
163
+ return json.loads(body)
164
+
165
+
75
166
  def _query_osv_batch(dependency_list: list[dict[str, str]]) -> dict[str, Any]:
76
167
  payload = {
77
168
  "queries": [
@@ -3,6 +3,8 @@ import re
3
3
  from pathlib import Path
4
4
  from typing import Any
5
5
 
6
+ from plugins.dephealth.cve_scanner import enrich_with_kev, enrich_with_epss
7
+
6
8
 
7
9
  _CRITICAL_PATTERN = re.compile(r"\bcritical\b", re.IGNORECASE)
8
10
  _HIGH_PATTERN = re.compile(r"\bhigh\b", re.IGNORECASE)
@@ -33,6 +35,9 @@ def analyze_reachability(cve_result: dict[str, Any], project_dir: str) -> dict[s
33
35
  risk_level = _classify_risk(reachability, summary)
34
36
  recommendation = _build_recommendation(package, fixed_version, reachability)
35
37
 
38
+ kev_enriched = enrich_with_kev({"id": cve_id}, project_dir)
39
+ epss_enriched = enrich_with_epss({"id": cve_id}, project_dir)
40
+
36
41
  return {
37
42
  "package": package,
38
43
  "cve_id": cve_id,
@@ -40,6 +45,8 @@ def analyze_reachability(cve_result: dict[str, Any], project_dir: str) -> dict[s
40
45
  "import_locations": sorted(imported_files),
41
46
  "risk_level": risk_level,
42
47
  "recommendation": recommendation,
48
+ "kev_listed": kev_enriched.get("kev_listed", False),
49
+ "epss_score": epss_enriched.get("epss_score"),
43
50
  }
44
51
 
45
52
 
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "oh-my-god"
7
- version = "2.0.8"
7
+ version = "2.0.9"
8
8
  description = "OMG (Oh My God) — Multi-agent orchestration, evidence-backed verification, and durable session state for Claude Code, Codex, and supported agent hosts"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -43,7 +43,9 @@
43
43
  "type": "string",
44
44
  "enum": [
45
45
  "claude",
46
- "codex"
46
+ "codex",
47
+ "gemini",
48
+ "kimi"
47
49
  ]
48
50
  },
49
51
  "minItems": 1
@@ -281,6 +283,86 @@
281
283
  }
282
284
  },
283
285
  "additionalProperties": true
286
+ },
287
+ "gemini": {
288
+ "type": "object",
289
+ "required": [
290
+ "compilation_targets",
291
+ "mcp",
292
+ "skills",
293
+ "automations"
294
+ ],
295
+ "properties": {
296
+ "compilation_targets": {
297
+ "type": "array",
298
+ "minItems": 1,
299
+ "items": {
300
+ "type": "string"
301
+ }
302
+ },
303
+ "mcp": {
304
+ "type": "array",
305
+ "minItems": 1,
306
+ "items": {
307
+ "type": "string"
308
+ }
309
+ },
310
+ "skills": {
311
+ "type": "array",
312
+ "minItems": 1,
313
+ "items": {
314
+ "type": "string"
315
+ }
316
+ },
317
+ "automations": {
318
+ "type": "array",
319
+ "minItems": 1,
320
+ "items": {
321
+ "type": "string"
322
+ }
323
+ }
324
+ },
325
+ "additionalProperties": true
326
+ },
327
+ "kimi": {
328
+ "type": "object",
329
+ "required": [
330
+ "compilation_targets",
331
+ "mcp",
332
+ "skills",
333
+ "automations"
334
+ ],
335
+ "properties": {
336
+ "compilation_targets": {
337
+ "type": "array",
338
+ "minItems": 1,
339
+ "items": {
340
+ "type": "string"
341
+ }
342
+ },
343
+ "mcp": {
344
+ "type": "array",
345
+ "minItems": 1,
346
+ "items": {
347
+ "type": "string"
348
+ }
349
+ },
350
+ "skills": {
351
+ "type": "array",
352
+ "minItems": 1,
353
+ "items": {
354
+ "type": "string"
355
+ }
356
+ },
357
+ "automations": {
358
+ "type": "array",
359
+ "minItems": 1,
360
+ "items": {
361
+ "type": "string"
362
+ }
363
+ }
364
+ },
365
+ "additionalProperties": true
284
366
  }
285
367
  },
286
368
  "additionalProperties": true
@@ -2,8 +2,8 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import json
5
+ import importlib
5
6
  from pathlib import Path
6
- from typing import Any
7
7
 
8
8
 
9
9
  CANONICAL_BRAND = "OMG"
@@ -11,10 +11,12 @@ CANONICAL_REPO_URL = "https://github.com/trac3er00/OMG"
11
11
  CANONICAL_PACKAGE_NAME = "@trac3er/oh-my-god"
12
12
  CANONICAL_PLUGIN_ID = "omg"
13
13
  CANONICAL_MARKETPLACE_ID = "omg"
14
- CANONICAL_VERSION = "2.0.8"
14
+ CANONICAL_VERSION = "2.0.9"
15
15
 
16
16
  VALID_ADOPTION_MODES = ("omg-only", "coexist")
17
17
  VALID_PRESETS = ("safe", "balanced", "interop", "labs")
18
+ CANONICAL_MODE_NAMES = ("chill", "focused", "exploratory")
19
+ """Cross-surface user-facing mode names reserved for the shared mode system."""
18
20
 
19
21
  MANAGED_PRESET_FLAGS = (
20
22
  "SETUP",
@@ -91,6 +93,12 @@ def get_preset_features(preset: str | None) -> dict[str, bool]:
91
93
  return dict(PRESET_FEATURES[resolved])
92
94
 
93
95
 
96
+ def get_mode_profile(mode: str) -> dict[str, object]:
97
+ runtime_profile = importlib.import_module("runtime.runtime_profile")
98
+ loader = getattr(runtime_profile, "load_canonical_mode_profile")
99
+ return loader(mode)
100
+
101
+
94
102
  def _resolve_claude_dir(base_dir: Path) -> Path:
95
103
  nested = base_dir / ".claude"
96
104
  if nested.exists():
@@ -180,7 +188,7 @@ def build_adoption_report(
180
188
  requested_mode: str | None = None,
181
189
  preset: str | None = None,
182
190
  adopt: str = "auto",
183
- ) -> dict[str, Any]:
191
+ ) -> dict[str, object]:
184
192
  detected_ecosystems = detect_ecosystems(project_dir) if adopt == "auto" else []
185
193
  recommended_mode = recommend_mode(detected_ecosystems)
186
194
  selected_mode = requested_mode if requested_mode in VALID_ADOPTION_MODES else recommended_mode
@@ -204,9 +212,9 @@ def build_adoption_report(
204
212
  }
205
213
 
206
214
 
207
- def write_adoption_report(project_dir: str | Path, report: dict[str, Any]) -> str:
215
+ def write_adoption_report(project_dir: str | Path, report: dict[str, object]) -> str:
208
216
  state_dir = Path(project_dir) / ".omg" / "state"
209
217
  state_dir.mkdir(parents=True, exist_ok=True)
210
218
  report_path = state_dir / "adoption-report.json"
211
- report_path.write_text(json.dumps(report, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
219
+ _ = report_path.write_text(json.dumps(report, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
212
220
  return str(report_path)