@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
package/runtime/team_router.py
CHANGED
|
@@ -26,11 +26,12 @@ _logger = logging.getLogger(__name__)
|
|
|
26
26
|
try:
|
|
27
27
|
import runtime.providers.codex_provider # noqa: F401 # pyright: ignore[reportUnusedImport]
|
|
28
28
|
import runtime.providers.gemini_provider # noqa: F401 # pyright: ignore[reportUnusedImport]
|
|
29
|
-
import runtime.providers.opencode_provider # noqa: F401 # pyright: ignore[reportUnusedImport]
|
|
30
29
|
import runtime.providers.kimi_provider # noqa: F401 # pyright: ignore[reportUnusedImport]
|
|
31
30
|
except ImportError:
|
|
32
31
|
pass
|
|
33
32
|
|
|
33
|
+
from runtime.runtime_profile import resolve_parallel_workers
|
|
34
|
+
|
|
34
35
|
@dataclass
|
|
35
36
|
class TeamDispatchRequest:
|
|
36
37
|
target: str # codex | gemini | ccg | auto
|
|
@@ -550,7 +551,6 @@ def dispatch_to_model(agent_name: str, user_prompt: str, project_dir: str) -> di
|
|
|
550
551
|
provider_name_map = {
|
|
551
552
|
"codex-cli": "codex",
|
|
552
553
|
"gemini-cli": "gemini",
|
|
553
|
-
"opencode-cli": "opencode",
|
|
554
554
|
"kimi-cli": "kimi",
|
|
555
555
|
}
|
|
556
556
|
provider_name = provider_name_map.get(preferred)
|
|
@@ -661,7 +661,7 @@ def execute_agents_parallel(
|
|
|
661
661
|
if not sorted_tasks:
|
|
662
662
|
return []
|
|
663
663
|
|
|
664
|
-
max_workers = min(len(sorted_tasks), 5)
|
|
664
|
+
max_workers = resolve_parallel_workers(project_dir, requested_workers=min(len(sorted_tasks), 5))
|
|
665
665
|
results_by_index: dict[int, dict[str, Any]] = {}
|
|
666
666
|
|
|
667
667
|
with ThreadPoolExecutor(max_workers=max_workers) as pool:
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""State and provenance tracking for untrusted external content."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
STATE_REL_PATH = Path(".omg") / "state" / "untrusted-content.json"
|
|
11
|
+
_INSTRUCTION_PATTERNS: tuple[re.Pattern[str], ...] = (
|
|
12
|
+
re.compile(r"ignore\s+previous\s+instructions", re.IGNORECASE),
|
|
13
|
+
re.compile(r"\b(system|assistant|developer)\s*:", re.IGNORECASE),
|
|
14
|
+
re.compile(r"\b(run|execute|commit|push|apply_patch|edit)\b", re.IGNORECASE),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def mark_untrusted_content(
|
|
19
|
+
project_dir: str,
|
|
20
|
+
*,
|
|
21
|
+
source_type: str,
|
|
22
|
+
content: str,
|
|
23
|
+
source_ref: str = "",
|
|
24
|
+
) -> dict[str, Any]:
|
|
25
|
+
state_path = _state_path(project_dir)
|
|
26
|
+
state = get_untrusted_content_state(project_dir)
|
|
27
|
+
sanitized, quarantined = quarantine_instruction_like_text(content)
|
|
28
|
+
entry = {
|
|
29
|
+
"source_type": source_type,
|
|
30
|
+
"source_ref": source_ref,
|
|
31
|
+
"quarantined_fragments": quarantined,
|
|
32
|
+
"trust_score": 0.0,
|
|
33
|
+
}
|
|
34
|
+
provenance = list(state.get("provenance", []))
|
|
35
|
+
provenance.append(entry)
|
|
36
|
+
next_state = {
|
|
37
|
+
"active": True,
|
|
38
|
+
"last_source_type": source_type,
|
|
39
|
+
"last_source_ref": source_ref,
|
|
40
|
+
"sanitized_content": sanitized,
|
|
41
|
+
"quarantined_fragments": quarantined,
|
|
42
|
+
"provenance": provenance[-20:],
|
|
43
|
+
"trust_scores": {"external_content": 0.0},
|
|
44
|
+
}
|
|
45
|
+
_write_state(state_path, next_state)
|
|
46
|
+
return next_state
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def clear_untrusted_content(project_dir: str, *, reason: str) -> dict[str, Any]:
|
|
50
|
+
state_path = _state_path(project_dir)
|
|
51
|
+
existing = get_untrusted_content_state(project_dir)
|
|
52
|
+
next_state = {
|
|
53
|
+
**existing,
|
|
54
|
+
"active": False,
|
|
55
|
+
"cleared_reason": reason,
|
|
56
|
+
}
|
|
57
|
+
_write_state(state_path, next_state)
|
|
58
|
+
return next_state
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_untrusted_content_state(project_dir: str) -> dict[str, Any]:
|
|
62
|
+
state_path = _state_path(project_dir)
|
|
63
|
+
if not state_path.exists():
|
|
64
|
+
return {
|
|
65
|
+
"active": False,
|
|
66
|
+
"provenance": [],
|
|
67
|
+
"trust_scores": {},
|
|
68
|
+
}
|
|
69
|
+
try:
|
|
70
|
+
payload = json.loads(state_path.read_text(encoding="utf-8"))
|
|
71
|
+
except (OSError, json.JSONDecodeError):
|
|
72
|
+
return {
|
|
73
|
+
"active": False,
|
|
74
|
+
"provenance": [],
|
|
75
|
+
"trust_scores": {},
|
|
76
|
+
}
|
|
77
|
+
return payload if isinstance(payload, dict) else {"active": False, "provenance": [], "trust_scores": {}}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def is_untrusted_content_mode_active(project_dir: str) -> bool:
|
|
81
|
+
return bool(get_untrusted_content_state(project_dir).get("active", False))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def quarantine_instruction_like_text(content: str) -> tuple[str, list[str]]:
|
|
85
|
+
sanitized_lines: list[str] = []
|
|
86
|
+
quarantined: list[str] = []
|
|
87
|
+
for raw_line in content.splitlines():
|
|
88
|
+
line = raw_line.strip()
|
|
89
|
+
if any(pattern.search(line) for pattern in _INSTRUCTION_PATTERNS):
|
|
90
|
+
quarantined.append(raw_line)
|
|
91
|
+
continue
|
|
92
|
+
sanitized_lines.append(raw_line)
|
|
93
|
+
return "\n".join(sanitized_lines).strip(), quarantined
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _state_path(project_dir: str) -> Path:
|
|
97
|
+
return Path(project_dir) / STATE_REL_PATH
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _write_state(path: Path, payload: dict[str, Any]) -> None:
|
|
101
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
102
|
+
path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
package/scripts/omg.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""OMG 2.0.
|
|
2
|
+
"""OMG 2.0.4 CLI entrypoint.
|
|
3
3
|
|
|
4
4
|
Implements practical command-line flows for:
|
|
5
5
|
- omg ship
|
|
6
6
|
- omg fix --issue
|
|
7
7
|
- omg secure
|
|
8
|
+
- omg security check
|
|
8
9
|
- omg maintainer
|
|
9
10
|
- omg trust review
|
|
10
11
|
- omg runtime dispatch
|
|
@@ -31,6 +32,15 @@ from hooks.shadow_manager import create_evidence_pack
|
|
|
31
32
|
from hooks.trust_review import review_config_change, write_trust_manifest
|
|
32
33
|
from lab.pipeline import publish_artifact, run_pipeline
|
|
33
34
|
from runtime.dispatcher import dispatch_runtime
|
|
35
|
+
from runtime.api_twin import ingest_contract, record_fixture, serve_fixture, verify_fixture
|
|
36
|
+
from runtime.domain_packs import get_domain_pack_contract
|
|
37
|
+
from runtime.preflight import run_preflight
|
|
38
|
+
from runtime.security_check import run_security_check
|
|
39
|
+
from runtime.contract_compiler import (
|
|
40
|
+
build_release_readiness,
|
|
41
|
+
compile_contract_outputs,
|
|
42
|
+
validate_contract_registry,
|
|
43
|
+
)
|
|
34
44
|
from runtime.compat import (
|
|
35
45
|
DEFAULT_CONTRACT_SNAPSHOT_PATH,
|
|
36
46
|
DEFAULT_GAP_REPORT_PATH,
|
|
@@ -155,6 +165,68 @@ def cmd_secure(args: argparse.Namespace) -> int:
|
|
|
155
165
|
return 0 if decision.action != "deny" else 3
|
|
156
166
|
|
|
157
167
|
|
|
168
|
+
def cmd_security_check(args: argparse.Namespace) -> int:
|
|
169
|
+
result = run_security_check(
|
|
170
|
+
project_dir=_ensure_project_dir(),
|
|
171
|
+
scope=args.scope,
|
|
172
|
+
include_live_enrichment=bool(args.live_enrichment),
|
|
173
|
+
)
|
|
174
|
+
print(json.dumps(result, indent=2))
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def cmd_api_twin_ingest(args: argparse.Namespace) -> int:
|
|
179
|
+
result = ingest_contract(_ensure_project_dir(), name=args.name, source_path=args.source)
|
|
180
|
+
print(json.dumps(result, indent=2))
|
|
181
|
+
return 0
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def cmd_api_twin_record(args: argparse.Namespace) -> int:
|
|
185
|
+
result = record_fixture(
|
|
186
|
+
_ensure_project_dir(),
|
|
187
|
+
name=args.name,
|
|
188
|
+
request=json.loads(args.request_json),
|
|
189
|
+
response=json.loads(args.response_json),
|
|
190
|
+
validated=bool(args.validated),
|
|
191
|
+
)
|
|
192
|
+
print(json.dumps(result, indent=2))
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def cmd_api_twin_serve(args: argparse.Namespace) -> int:
|
|
197
|
+
result = serve_fixture(
|
|
198
|
+
_ensure_project_dir(),
|
|
199
|
+
name=args.name,
|
|
200
|
+
latency_ms=int(args.latency_ms),
|
|
201
|
+
failure_mode=args.failure_mode,
|
|
202
|
+
schema_drift=bool(args.schema_drift),
|
|
203
|
+
)
|
|
204
|
+
print(json.dumps(result, indent=2))
|
|
205
|
+
return 0
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def cmd_api_twin_verify(args: argparse.Namespace) -> int:
|
|
209
|
+
result = verify_fixture(
|
|
210
|
+
_ensure_project_dir(),
|
|
211
|
+
name=args.name,
|
|
212
|
+
live_response=json.loads(args.live_response_json),
|
|
213
|
+
)
|
|
214
|
+
print(json.dumps(result, indent=2))
|
|
215
|
+
return 0
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def cmd_preflight(args: argparse.Namespace) -> int:
|
|
219
|
+
result = run_preflight(_ensure_project_dir(), goal=args.goal)
|
|
220
|
+
print(json.dumps(result, indent=2))
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def cmd_domain_pack(args: argparse.Namespace) -> int:
|
|
225
|
+
result = get_domain_pack_contract(args.name)
|
|
226
|
+
print(json.dumps(result, indent=2))
|
|
227
|
+
return 0
|
|
228
|
+
|
|
229
|
+
|
|
158
230
|
def cmd_maintainer(args: argparse.Namespace) -> int:
|
|
159
231
|
project_dir = _ensure_project_dir()
|
|
160
232
|
out_dir = Path(project_dir) / ".omg" / "evidence"
|
|
@@ -357,6 +429,34 @@ def cmd_ecosystem_sync(args: argparse.Namespace) -> int:
|
|
|
357
429
|
return 0 if not errors else 2
|
|
358
430
|
|
|
359
431
|
|
|
432
|
+
def cmd_contract_validate(args: argparse.Namespace) -> int:
|
|
433
|
+
result = validate_contract_registry(ROOT_DIR)
|
|
434
|
+
print(json.dumps(result, indent=2))
|
|
435
|
+
return 0 if result.get("status") == "ok" else 2
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def cmd_contract_compile(args: argparse.Namespace) -> int:
|
|
439
|
+
hosts = args.hosts or []
|
|
440
|
+
result = compile_contract_outputs(
|
|
441
|
+
root_dir=ROOT_DIR,
|
|
442
|
+
output_root=args.output_root,
|
|
443
|
+
hosts=hosts,
|
|
444
|
+
channel=args.channel,
|
|
445
|
+
)
|
|
446
|
+
print(json.dumps(result, indent=2))
|
|
447
|
+
return 0 if result.get("status") == "ok" else 2
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def cmd_release_readiness(args: argparse.Namespace) -> int:
|
|
451
|
+
result = build_release_readiness(
|
|
452
|
+
root_dir=ROOT_DIR,
|
|
453
|
+
output_root=args.output_root,
|
|
454
|
+
channel=args.channel,
|
|
455
|
+
)
|
|
456
|
+
print(json.dumps(result, indent=2))
|
|
457
|
+
return 0 if result.get("status") == "ok" else 2
|
|
458
|
+
|
|
459
|
+
|
|
360
460
|
def _add_compat_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
361
461
|
compat_sub = parent.add_subparsers(dest=dest, required=True)
|
|
362
462
|
compat_list = compat_sub.add_parser("list", help="List supported legacy skill names")
|
|
@@ -399,6 +499,35 @@ def _add_ecosystem_subcommands(parent: argparse.ArgumentParser, *, dest: str) ->
|
|
|
399
499
|
ecosystem_sync.set_defaults(func=cmd_ecosystem_sync)
|
|
400
500
|
|
|
401
501
|
|
|
502
|
+
def _add_contract_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
503
|
+
contract_sub = parent.add_subparsers(dest=dest, required=True)
|
|
504
|
+
|
|
505
|
+
contract_validate = contract_sub.add_parser("validate", help="Validate contract doc, schema, and bundle registry")
|
|
506
|
+
contract_validate.set_defaults(func=cmd_contract_validate)
|
|
507
|
+
|
|
508
|
+
contract_compile = contract_sub.add_parser("compile", help="Compile Claude/Codex artifacts from the canonical contract")
|
|
509
|
+
contract_compile.add_argument(
|
|
510
|
+
"--host",
|
|
511
|
+
dest="hosts",
|
|
512
|
+
action="append",
|
|
513
|
+
choices=["claude", "codex"],
|
|
514
|
+
required=True,
|
|
515
|
+
help="Host to compile (repeat for multiple hosts)",
|
|
516
|
+
)
|
|
517
|
+
contract_compile.add_argument("--channel", default="public", choices=["public", "enterprise"])
|
|
518
|
+
contract_compile.add_argument("--output-root", default="", help="Write outputs to this root instead of the repo root")
|
|
519
|
+
contract_compile.set_defaults(func=cmd_contract_compile)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _add_release_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
523
|
+
release_sub = parent.add_subparsers(dest=dest, required=True)
|
|
524
|
+
|
|
525
|
+
release_readiness = release_sub.add_parser("readiness", help="Check production release readiness for compiled artifacts")
|
|
526
|
+
release_readiness.add_argument("--channel", default="dual", choices=["public", "enterprise", "dual"])
|
|
527
|
+
release_readiness.add_argument("--output-root", default="", help="Check compiled outputs from this root instead of the repo root")
|
|
528
|
+
release_readiness.set_defaults(func=cmd_release_readiness)
|
|
529
|
+
|
|
530
|
+
|
|
402
531
|
def build_parser() -> argparse.ArgumentParser:
|
|
403
532
|
parser = argparse.ArgumentParser(prog="omg", description=f"OMG {CANONICAL_VERSION} CLI")
|
|
404
533
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -418,6 +547,44 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
418
547
|
secure.add_argument("--command", required=True)
|
|
419
548
|
secure.set_defaults(func=cmd_secure)
|
|
420
549
|
|
|
550
|
+
security = sub.add_parser("security", help="Canonical OMG security workflows")
|
|
551
|
+
security_sub = security.add_subparsers(dest="security_command", required=True)
|
|
552
|
+
security_check = security_sub.add_parser("check", help="Run canonical OMG security check")
|
|
553
|
+
security_check.add_argument("--scope", default=".")
|
|
554
|
+
security_check.add_argument("--live-enrichment", action="store_true")
|
|
555
|
+
security_check.set_defaults(func=cmd_security_check)
|
|
556
|
+
|
|
557
|
+
api_twin = sub.add_parser("api-twin", help="Contract replay and fixture-based API simulation")
|
|
558
|
+
api_twin_sub = api_twin.add_subparsers(dest="api_twin_command", required=True)
|
|
559
|
+
api_twin_ingest = api_twin_sub.add_parser("ingest", help="Ingest OpenAPI/Postman/example contract input")
|
|
560
|
+
api_twin_ingest.add_argument("--name", required=True)
|
|
561
|
+
api_twin_ingest.add_argument("--source", required=True)
|
|
562
|
+
api_twin_ingest.set_defaults(func=cmd_api_twin_ingest)
|
|
563
|
+
api_twin_record = api_twin_sub.add_parser("record", help="Record approved fixture response")
|
|
564
|
+
api_twin_record.add_argument("--name", required=True)
|
|
565
|
+
api_twin_record.add_argument("--request-json", required=True)
|
|
566
|
+
api_twin_record.add_argument("--response-json", required=True)
|
|
567
|
+
api_twin_record.add_argument("--validated", action="store_true")
|
|
568
|
+
api_twin_record.set_defaults(func=cmd_api_twin_record)
|
|
569
|
+
api_twin_serve = api_twin_sub.add_parser("serve", help="Replay a fixture with optional drift/failure injection")
|
|
570
|
+
api_twin_serve.add_argument("--name", required=True)
|
|
571
|
+
api_twin_serve.add_argument("--latency-ms", type=int, default=0)
|
|
572
|
+
api_twin_serve.add_argument("--failure-mode", default="")
|
|
573
|
+
api_twin_serve.add_argument("--schema-drift", action="store_true")
|
|
574
|
+
api_twin_serve.set_defaults(func=cmd_api_twin_serve)
|
|
575
|
+
api_twin_verify = api_twin_sub.add_parser("verify", help="Validate a fixture against a live response")
|
|
576
|
+
api_twin_verify.add_argument("--name", required=True)
|
|
577
|
+
api_twin_verify.add_argument("--live-response-json", required=True)
|
|
578
|
+
api_twin_verify.set_defaults(func=cmd_api_twin_verify)
|
|
579
|
+
|
|
580
|
+
preflight = sub.add_parser("preflight", help="Structured OMG preflight routing")
|
|
581
|
+
preflight.add_argument("--goal", required=True)
|
|
582
|
+
preflight.set_defaults(func=cmd_preflight)
|
|
583
|
+
|
|
584
|
+
domain_pack = sub.add_parser("domain-pack", help="Inspect optional domain pack contracts")
|
|
585
|
+
domain_pack.add_argument("--name", required=True, choices=["robotics", "vision", "algorithms", "health"])
|
|
586
|
+
domain_pack.set_defaults(func=cmd_domain_pack)
|
|
587
|
+
|
|
421
588
|
maintainer = sub.add_parser("maintainer", help="OSS maintainer evidence helper")
|
|
422
589
|
maintainer.add_argument("--mode", default="impact", choices=["triage", "release", "review", "impact"])
|
|
423
590
|
maintainer.set_defaults(func=cmd_maintainer)
|
|
@@ -479,6 +646,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
479
646
|
ecosystem = sub.add_parser("ecosystem", help="Upstream ecosystem sync and status")
|
|
480
647
|
_add_ecosystem_subcommands(ecosystem, dest="ecosystem_command")
|
|
481
648
|
|
|
649
|
+
contract = sub.add_parser("contract", help="Canonical OMG contract validation and compilation")
|
|
650
|
+
_add_contract_subcommands(contract, dest="contract_command")
|
|
651
|
+
|
|
652
|
+
release = sub.add_parser("release", help="OMG release-readiness checks")
|
|
653
|
+
_add_release_subcommands(release, dest="release_command")
|
|
654
|
+
|
|
482
655
|
return parser
|
|
483
656
|
|
|
484
657
|
|
package/settings.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
-
"_comment": "OMG 2.0.
|
|
3
|
+
"_comment": "OMG 2.0.4 - project-level config with hook registrations, presets, and feature flags.",
|
|
4
4
|
"permissions": {
|
|
5
5
|
"allow": [
|
|
6
6
|
"Agent",
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"Bash(tree *)",
|
|
43
43
|
"Bash(du *)",
|
|
44
44
|
"Bash(df *)",
|
|
45
|
-
"Bash(env *)",
|
|
46
45
|
"Bash(type *)",
|
|
47
46
|
"Bash(command *)",
|
|
48
47
|
"Bash(test *)",
|
|
@@ -52,17 +51,12 @@
|
|
|
52
51
|
"Bash(ln *)",
|
|
53
52
|
"Bash(cp *)",
|
|
54
53
|
"Bash(mv *)",
|
|
55
|
-
"Bash(chmod *)",
|
|
56
|
-
"Bash(chown *)",
|
|
57
54
|
"Bash(git *)",
|
|
58
55
|
"Bash(npm *)",
|
|
59
56
|
"Bash(npx *)",
|
|
60
57
|
"Bash(yarn *)",
|
|
61
58
|
"Bash(pnpm *)",
|
|
62
59
|
"Bash(bun *)",
|
|
63
|
-
"Bash(node *)",
|
|
64
|
-
"Bash(python *)",
|
|
65
|
-
"Bash(python3 *)",
|
|
66
60
|
"Bash(pip *)",
|
|
67
61
|
"Bash(pip3 *)",
|
|
68
62
|
"Bash(uv *)",
|
|
@@ -115,6 +109,12 @@
|
|
|
115
109
|
"Bash(terraform apply *)",
|
|
116
110
|
"Bash(terraform destroy *)",
|
|
117
111
|
"Bash(terraform import *)",
|
|
112
|
+
"Bash(env *)",
|
|
113
|
+
"Bash(node *)",
|
|
114
|
+
"Bash(python *)",
|
|
115
|
+
"Bash(python3 *)",
|
|
116
|
+
"Bash(chmod *)",
|
|
117
|
+
"Bash(chown *)",
|
|
118
118
|
"Bash(docker *)",
|
|
119
119
|
"Bash(docker-compose *)",
|
|
120
120
|
"Bash(kubectl get *)",
|
|
@@ -188,45 +188,66 @@
|
|
|
188
188
|
}
|
|
189
189
|
],
|
|
190
190
|
"PreToolUse": [
|
|
191
|
+
{
|
|
192
|
+
"hooks": [
|
|
193
|
+
{
|
|
194
|
+
"type": "command",
|
|
195
|
+
"command": "python3 \"$HOME/.claude/hooks/firewall.py\"",
|
|
196
|
+
"timeout": 10
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
"matcher": "Bash"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"hooks": [
|
|
203
|
+
{
|
|
204
|
+
"type": "command",
|
|
205
|
+
"command": "python3 \"$HOME/.claude/hooks/secret-guard.py\"",
|
|
206
|
+
"timeout": 10
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"matcher": "Read|Write|Edit|MultiEdit"
|
|
210
|
+
},
|
|
191
211
|
{
|
|
192
212
|
"hooks": [
|
|
193
213
|
{
|
|
194
214
|
"type": "command",
|
|
195
215
|
"command": "python3 \"$HOME/.claude/hooks/pre-tool-inject.py\""
|
|
196
216
|
}
|
|
197
|
-
]
|
|
217
|
+
],
|
|
218
|
+
"matcher": ""
|
|
198
219
|
}
|
|
199
220
|
],
|
|
200
221
|
"PostToolUse": [
|
|
201
222
|
{
|
|
202
|
-
"matcher": "Bash",
|
|
203
223
|
"hooks": [
|
|
204
224
|
{
|
|
205
225
|
"type": "command",
|
|
206
226
|
"command": "python3 \"$HOME/.claude/hooks/circuit-breaker.py\"",
|
|
207
227
|
"timeout": 10
|
|
208
228
|
}
|
|
209
|
-
]
|
|
229
|
+
],
|
|
230
|
+
"matcher": "Bash"
|
|
210
231
|
},
|
|
211
232
|
{
|
|
212
|
-
"matcher": "Write|Edit|MultiEdit",
|
|
213
233
|
"hooks": [
|
|
214
234
|
{
|
|
215
235
|
"type": "command",
|
|
216
236
|
"command": "python3 \"$HOME/.claude/hooks/tool-ledger.py\"",
|
|
217
237
|
"timeout": 10
|
|
218
238
|
}
|
|
219
|
-
]
|
|
239
|
+
],
|
|
240
|
+
"matcher": "Write|Edit|MultiEdit"
|
|
220
241
|
},
|
|
221
242
|
{
|
|
222
|
-
"matcher": "Write|Edit|MultiEdit",
|
|
223
243
|
"hooks": [
|
|
224
244
|
{
|
|
225
245
|
"type": "command",
|
|
226
246
|
"command": "python3 \"$HOME/.claude/hooks/test_generator_hook.py\"",
|
|
227
247
|
"timeout": 10
|
|
228
248
|
}
|
|
229
|
-
]
|
|
249
|
+
],
|
|
250
|
+
"matcher": "Write|Edit|MultiEdit"
|
|
230
251
|
},
|
|
231
252
|
{
|
|
232
253
|
"hooks": [
|
|
@@ -235,7 +256,8 @@
|
|
|
235
256
|
"command": "python3 \"$HOME/.claude/hooks/budget_governor.py\"",
|
|
236
257
|
"timeout": 10
|
|
237
258
|
}
|
|
238
|
-
]
|
|
259
|
+
],
|
|
260
|
+
"matcher": ""
|
|
239
261
|
}
|
|
240
262
|
],
|
|
241
263
|
"PostToolUseFailure": [
|
|
@@ -250,19 +272,19 @@
|
|
|
250
272
|
],
|
|
251
273
|
"Stop": [
|
|
252
274
|
{
|
|
253
|
-
"matcher": "",
|
|
254
275
|
"hooks": [
|
|
255
276
|
{
|
|
256
277
|
"type": "command",
|
|
257
278
|
"command": "python3 \"$HOME/.claude/hooks/stop_dispatcher.py\"",
|
|
258
279
|
"timeout": 90
|
|
259
280
|
}
|
|
260
|
-
]
|
|
281
|
+
],
|
|
282
|
+
"matcher": ""
|
|
261
283
|
}
|
|
262
284
|
]
|
|
263
285
|
},
|
|
264
286
|
"_omg": {
|
|
265
|
-
"_version": "2.0.
|
|
287
|
+
"_version": "2.0.4",
|
|
266
288
|
"preset": "safe",
|
|
267
289
|
"default_mode": "ulw+ralph",
|
|
268
290
|
"vision_auto": true,
|
|
@@ -313,6 +335,32 @@
|
|
|
313
335
|
"DEP_HEALTH": false,
|
|
314
336
|
"CODEBASE_VIZ": false,
|
|
315
337
|
"CONTEXT_MANAGER": false
|
|
338
|
+
},
|
|
339
|
+
"generated": {
|
|
340
|
+
"contract_version": "2.0.4",
|
|
341
|
+
"channel": "public",
|
|
342
|
+
"required_bundles": [
|
|
343
|
+
"control-plane",
|
|
344
|
+
"hook-governor",
|
|
345
|
+
"mcp-fabric",
|
|
346
|
+
"lsp-pack",
|
|
347
|
+
"secure-worktree-pipeline"
|
|
348
|
+
],
|
|
349
|
+
"protected_paths": [
|
|
350
|
+
".omg/**",
|
|
351
|
+
".agents/**",
|
|
352
|
+
".codex/**",
|
|
353
|
+
".claude/**"
|
|
354
|
+
],
|
|
355
|
+
"emulated_events": [
|
|
356
|
+
"PreCompact",
|
|
357
|
+
"ConfigChange",
|
|
358
|
+
"WorktreeCreate",
|
|
359
|
+
"WorktreeRemove",
|
|
360
|
+
"SubagentStart",
|
|
361
|
+
"SubagentStop",
|
|
362
|
+
"TaskCompleted"
|
|
363
|
+
]
|
|
316
364
|
}
|
|
317
365
|
}
|
|
318
366
|
}
|
package/tools/python_repl.py
CHANGED
|
@@ -25,11 +25,13 @@ from typing import Any, Dict, Generator, List, Optional, Union
|
|
|
25
25
|
|
|
26
26
|
_get_feature_flag = None
|
|
27
27
|
_atomic_json_write = None
|
|
28
|
+
_validate_opaque_identifier = None
|
|
29
|
+
_ensure_path_within_dir = None
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def _ensure_imports():
|
|
31
33
|
"""Lazy import feature flag and atomic write from hooks/_common.py."""
|
|
32
|
-
global _get_feature_flag, _atomic_json_write
|
|
34
|
+
global _get_feature_flag, _atomic_json_write, _validate_opaque_identifier, _ensure_path_within_dir
|
|
33
35
|
if _get_feature_flag is not None:
|
|
34
36
|
return
|
|
35
37
|
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
@@ -38,8 +40,12 @@ def _ensure_imports():
|
|
|
38
40
|
try:
|
|
39
41
|
from hooks._common import get_feature_flag as _gff
|
|
40
42
|
from hooks._common import atomic_json_write as _ajw
|
|
43
|
+
from hooks.security_validators import ensure_path_within_dir as _epwd
|
|
44
|
+
from hooks.security_validators import validate_opaque_identifier as _voi
|
|
41
45
|
_get_feature_flag = _gff
|
|
42
46
|
_atomic_json_write = _ajw
|
|
47
|
+
_validate_opaque_identifier = _voi
|
|
48
|
+
_ensure_path_within_dir = _epwd
|
|
43
49
|
except ImportError:
|
|
44
50
|
pass
|
|
45
51
|
|
|
@@ -217,6 +223,23 @@ def _now_iso() -> str:
|
|
|
217
223
|
return datetime.now(timezone.utc).isoformat()
|
|
218
224
|
|
|
219
225
|
|
|
226
|
+
def _validate_session_id(session_id: str) -> str:
|
|
227
|
+
"""Validate a caller-supplied session identifier."""
|
|
228
|
+
_ensure_imports()
|
|
229
|
+
if _validate_opaque_identifier is None:
|
|
230
|
+
raise ValueError("Invalid session_id: validator unavailable")
|
|
231
|
+
return _validate_opaque_identifier(session_id, "session_id")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _resolve_session_path(session_id: str) -> str:
|
|
235
|
+
"""Resolve the on-disk session path and reject directory escapes."""
|
|
236
|
+
_ensure_imports()
|
|
237
|
+
if _ensure_path_within_dir is None:
|
|
238
|
+
raise ValueError("Invalid session_id: path validator unavailable")
|
|
239
|
+
state_dir = os.path.abspath(_STATE_DIR)
|
|
240
|
+
return _ensure_path_within_dir(state_dir, os.path.join(state_dir, f"{session_id}.json"))
|
|
241
|
+
|
|
242
|
+
|
|
220
243
|
def _persist_session(session_id: str) -> None:
|
|
221
244
|
"""Persist session metadata to disk (best-effort)."""
|
|
222
245
|
if session_id not in _sessions:
|
|
@@ -232,10 +255,11 @@ def _persist_session(session_id: str) -> None:
|
|
|
232
255
|
"exec_count": session["exec_count"],
|
|
233
256
|
"backend": session.get("backend", "stdlib"),
|
|
234
257
|
}
|
|
235
|
-
path = os.path.join(_STATE_DIR, f"{session_id}.json")
|
|
236
258
|
try:
|
|
259
|
+
safe_session_id = _validate_session_id(session_id)
|
|
260
|
+
path = _resolve_session_path(safe_session_id)
|
|
237
261
|
_atomic_json_write(path, meta)
|
|
238
|
-
except
|
|
262
|
+
except (OSError, ValueError):
|
|
239
263
|
pass # best-effort
|
|
240
264
|
|
|
241
265
|
|
|
@@ -411,6 +435,12 @@ def start_repl_session(session_id: Optional[str] = None) -> Dict[str, Any]:
|
|
|
411
435
|
if not _is_enabled():
|
|
412
436
|
return {"error": _DISABLED_MSG}
|
|
413
437
|
|
|
438
|
+
if session_id is not None:
|
|
439
|
+
try:
|
|
440
|
+
session_id = _validate_session_id(session_id)
|
|
441
|
+
except ValueError as exc:
|
|
442
|
+
return {"error": str(exc)}
|
|
443
|
+
|
|
414
444
|
# Resume existing session
|
|
415
445
|
if session_id and session_id in _sessions:
|
|
416
446
|
session = _sessions[session_id]
|