@trac3er/oh-my-god 2.0.0 → 2.0.2

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 (243) hide show
  1. package/.claude-plugin/marketplace.json +8 -8
  2. package/.claude-plugin/plugin.json +5 -4
  3. package/.claude-plugin/scripts/uninstall.sh +74 -3
  4. package/.claude-plugin/scripts/update.sh +78 -3
  5. package/.coveragerc +26 -0
  6. package/.mcp.json +4 -4
  7. package/CHANGELOG.md +14 -0
  8. package/CODE_OF_CONDUCT.md +27 -0
  9. package/CONTRIBUTING.md +62 -0
  10. package/OMG-setup.sh +1201 -355
  11. package/README.md +77 -56
  12. package/SECURITY.md +25 -0
  13. package/agents/__init__.py +1 -0
  14. package/agents/model_roles.py +196 -0
  15. package/agents/omg-architect-mode.md +3 -5
  16. package/agents/omg-backend-engineer.md +3 -5
  17. package/agents/omg-database-engineer.md +3 -5
  18. package/agents/omg-frontend-designer.md +4 -5
  19. package/agents/omg-implement-mode.md +4 -5
  20. package/agents/omg-infra-engineer.md +3 -5
  21. package/agents/omg-research-mode.md +4 -6
  22. package/agents/omg-security-auditor.md +3 -5
  23. package/agents/omg-testing-engineer.md +3 -5
  24. package/build/lib/yaml.py +321 -0
  25. package/commands/OMG:ai-commit.md +101 -14
  26. package/commands/OMG:arch.md +302 -19
  27. package/commands/OMG:ccg.md +12 -7
  28. package/commands/OMG:compat.md +25 -17
  29. package/commands/OMG:cost.md +173 -13
  30. package/commands/OMG:crazy.md +1 -1
  31. package/commands/OMG:create-agent.md +170 -20
  32. package/commands/OMG:deps.md +235 -17
  33. package/commands/OMG:domain-init.md +1 -1
  34. package/commands/OMG:escalate.md +41 -12
  35. package/commands/OMG:health-check.md +37 -13
  36. package/commands/OMG:init.md +122 -14
  37. package/commands/OMG:project-init.md +1 -1
  38. package/commands/OMG:session-branch.md +76 -9
  39. package/commands/OMG:session-fork.md +42 -5
  40. package/commands/OMG:session-merge.md +124 -8
  41. package/commands/OMG:setup.md +69 -12
  42. package/commands/OMG:stats.md +215 -14
  43. package/commands/OMG:teams.md +19 -10
  44. package/config/lsp_languages.yaml +8 -0
  45. package/hooks/__init__.py +0 -0
  46. package/hooks/_agent_registry.py +423 -0
  47. package/hooks/_analytics.py +291 -0
  48. package/hooks/_budget.py +31 -0
  49. package/hooks/_common.py +569 -0
  50. package/hooks/_compression_optimizer.py +119 -0
  51. package/hooks/_cost_ledger.py +176 -0
  52. package/hooks/_learnings.py +126 -0
  53. package/hooks/_memory.py +103 -0
  54. package/hooks/_protected_context.py +150 -0
  55. package/hooks/_token_counter.py +221 -0
  56. package/hooks/branch_manager.py +236 -0
  57. package/hooks/budget_governor.py +232 -0
  58. package/hooks/circuit-breaker.py +270 -0
  59. package/hooks/compression_feedback.py +254 -0
  60. package/hooks/config-guard.py +216 -0
  61. package/hooks/context_pressure.py +53 -0
  62. package/hooks/credential_store.py +1020 -0
  63. package/hooks/fetch-rate-limits.py +212 -0
  64. package/hooks/firewall.py +48 -0
  65. package/hooks/hashline-formatter-bridge.py +224 -0
  66. package/hooks/hashline-injector.py +273 -0
  67. package/hooks/hashline-validator.py +216 -0
  68. package/hooks/idle-detector.py +95 -0
  69. package/hooks/intentgate-keyword-detector.py +188 -0
  70. package/hooks/magic-keyword-router.py +195 -0
  71. package/hooks/policy_engine.py +505 -0
  72. package/hooks/post-tool-failure.py +19 -0
  73. package/hooks/post-write.py +219 -0
  74. package/hooks/post_write.py +46 -0
  75. package/hooks/pre-compact.py +398 -0
  76. package/hooks/pre-tool-inject.py +98 -0
  77. package/hooks/prompt-enhancer.py +672 -0
  78. package/hooks/quality-runner.py +191 -0
  79. package/hooks/query.py +512 -0
  80. package/hooks/secret-guard.py +61 -0
  81. package/hooks/secret_audit.py +144 -0
  82. package/hooks/session-end-capture.py +137 -0
  83. package/hooks/session-start.py +277 -0
  84. package/hooks/setup_wizard.py +582 -0
  85. package/hooks/shadow_manager.py +297 -0
  86. package/hooks/state_migration.py +225 -0
  87. package/hooks/stop-gate.py +7 -0
  88. package/hooks/stop_dispatcher.py +945 -0
  89. package/hooks/test-validator.py +361 -0
  90. package/hooks/test_generator_hook.py +123 -0
  91. package/hooks/todo-state-tracker.py +114 -0
  92. package/hooks/tool-ledger.py +149 -0
  93. package/hooks/trust_review.py +585 -0
  94. package/hud/omg-hud.mjs +31 -1
  95. package/lab/__init__.py +1 -0
  96. package/lab/pipeline.py +75 -0
  97. package/lab/policies.py +52 -0
  98. package/package.json +7 -18
  99. package/plugins/README.md +33 -61
  100. package/plugins/advanced/commands/OMG:deep-plan.md +3 -3
  101. package/plugins/advanced/commands/OMG:learn.md +1 -1
  102. package/plugins/advanced/commands/OMG:security-review.md +3 -3
  103. package/plugins/advanced/commands/OMG:ship.md +1 -1
  104. package/plugins/advanced/plugin.json +1 -1
  105. package/plugins/core/plugin.json +8 -3
  106. package/plugins/dephealth/__init__.py +0 -0
  107. package/plugins/dephealth/cve_scanner.py +188 -0
  108. package/plugins/dephealth/license_checker.py +135 -0
  109. package/plugins/dephealth/manifest_detector.py +423 -0
  110. package/plugins/dephealth/vuln_analyzer.py +169 -0
  111. package/plugins/testgen/__init__.py +0 -0
  112. package/plugins/testgen/codamosa_engine.py +402 -0
  113. package/plugins/testgen/edge_case_synthesizer.py +184 -0
  114. package/plugins/testgen/framework_detector.py +271 -0
  115. package/plugins/testgen/skeleton_generator.py +219 -0
  116. package/plugins/viz/__init__.py +0 -0
  117. package/plugins/viz/ast_parser.py +139 -0
  118. package/plugins/viz/diagram_generator.py +192 -0
  119. package/plugins/viz/graph_builder.py +444 -0
  120. package/plugins/viz/native_parsers.py +259 -0
  121. package/plugins/viz/regex_parser.py +112 -0
  122. package/pyproject.toml +81 -0
  123. package/rules/contextual/write-verify.md +2 -2
  124. package/rules/core/00-truth.md +1 -1
  125. package/rules/core/01-surgical.md +1 -1
  126. package/rules/core/02-circuit-breaker.md +2 -2
  127. package/rules/core/03-ensemble.md +3 -3
  128. package/rules/core/04-testing.md +3 -3
  129. package/runtime/__init__.py +32 -0
  130. package/runtime/adapters/__init__.py +13 -0
  131. package/runtime/adapters/claude.py +60 -0
  132. package/runtime/adapters/gpt.py +53 -0
  133. package/runtime/adapters/local.py +53 -0
  134. package/runtime/adoption.py +212 -0
  135. package/runtime/business_workflow.py +220 -0
  136. package/runtime/cli_provider.py +85 -0
  137. package/runtime/compat.py +1299 -0
  138. package/runtime/custom_agent_loader.py +366 -0
  139. package/runtime/dispatcher.py +47 -0
  140. package/runtime/ecosystem.py +371 -0
  141. package/runtime/legacy_compat.py +7 -0
  142. package/runtime/mcp_config_writers.py +115 -0
  143. package/runtime/mcp_lifecycle.py +153 -0
  144. package/runtime/mcp_memory_server.py +135 -0
  145. package/runtime/memory_parsers/__init__.py +0 -0
  146. package/runtime/memory_parsers/chatgpt_parser.py +257 -0
  147. package/runtime/memory_parsers/claude_import.py +107 -0
  148. package/runtime/memory_parsers/export.py +97 -0
  149. package/runtime/memory_parsers/gemini_import.py +91 -0
  150. package/runtime/memory_parsers/kimi_import.py +91 -0
  151. package/runtime/memory_store.py +215 -0
  152. package/runtime/omc_compat.py +7 -0
  153. package/runtime/providers/__init__.py +0 -0
  154. package/runtime/providers/codex_provider.py +112 -0
  155. package/runtime/providers/gemini_provider.py +128 -0
  156. package/runtime/providers/kimi_provider.py +151 -0
  157. package/runtime/providers/opencode_provider.py +144 -0
  158. package/runtime/subagent_dispatcher.py +362 -0
  159. package/runtime/team_router.py +1167 -0
  160. package/runtime/tmux_session_manager.py +169 -0
  161. package/scripts/check-omg-compat-contract-snapshot.py +137 -0
  162. package/scripts/check-omg-contract-snapshot.py +12 -0
  163. package/scripts/check-omg-public-ready.py +193 -0
  164. package/scripts/check-omg-standalone-clean.py +103 -0
  165. package/scripts/legacy_to_omg_migrate.py +29 -0
  166. package/scripts/migrate-legacy.py +464 -0
  167. package/scripts/omc_to_omg_migrate.py +12 -0
  168. package/scripts/omg.py +492 -0
  169. package/scripts/settings-merge.py +283 -0
  170. package/scripts/verify-standalone.sh +8 -4
  171. package/settings.json +126 -29
  172. package/templates/profile.yaml +1 -1
  173. package/tools/__init__.py +2 -0
  174. package/tools/browser_consent.py +289 -0
  175. package/tools/browser_stealth.py +481 -0
  176. package/tools/browser_tool.py +448 -0
  177. package/tools/changelog_generator.py +347 -0
  178. package/tools/commit_splitter.py +746 -0
  179. package/tools/config_discovery.py +151 -0
  180. package/tools/config_merger.py +449 -0
  181. package/tools/dashboard_generator.py +300 -0
  182. package/tools/git_inspector.py +298 -0
  183. package/tools/lsp_client.py +275 -0
  184. package/tools/lsp_discovery.py +231 -0
  185. package/tools/lsp_operations.py +392 -0
  186. package/tools/pr_generator.py +404 -0
  187. package/tools/python_repl.py +656 -0
  188. package/tools/python_sandbox.py +609 -0
  189. package/tools/search_providers/__init__.py +77 -0
  190. package/tools/search_providers/brave.py +115 -0
  191. package/tools/search_providers/exa.py +116 -0
  192. package/tools/search_providers/jina.py +104 -0
  193. package/tools/search_providers/perplexity.py +139 -0
  194. package/tools/search_providers/synthetic.py +74 -0
  195. package/tools/session_snapshot.py +736 -0
  196. package/tools/ssh_manager.py +912 -0
  197. package/tools/theme_engine.py +294 -0
  198. package/tools/theme_selector.py +137 -0
  199. package/tools/web_search.py +622 -0
  200. package/yaml.py +321 -0
  201. package/.claude-plugin/scripts/install.sh +0 -9
  202. package/bun.lock +0 -23
  203. package/bunfig.toml +0 -3
  204. package/hooks/_budget.ts +0 -1
  205. package/hooks/_common.ts +0 -63
  206. package/hooks/circuit-breaker.ts +0 -101
  207. package/hooks/config-guard.ts +0 -4
  208. package/hooks/firewall.ts +0 -20
  209. package/hooks/policy_engine.ts +0 -156
  210. package/hooks/post-tool-failure.ts +0 -22
  211. package/hooks/post-write.ts +0 -4
  212. package/hooks/pre-tool-inject.ts +0 -4
  213. package/hooks/prompt-enhancer.ts +0 -46
  214. package/hooks/quality-runner.ts +0 -24
  215. package/hooks/secret-guard.ts +0 -4
  216. package/hooks/session-end-capture.ts +0 -19
  217. package/hooks/session-start.ts +0 -19
  218. package/hooks/shadow_manager.ts +0 -81
  219. package/hooks/stop-gate.ts +0 -22
  220. package/hooks/stop_dispatcher.ts +0 -147
  221. package/hooks/test-generator-hook.ts +0 -4
  222. package/hooks/tool-ledger.ts +0 -27
  223. package/hooks/trust_review.ts +0 -175
  224. package/lab/pipeline.ts +0 -75
  225. package/lab/policies.ts +0 -68
  226. package/runtime/common.ts +0 -111
  227. package/runtime/compat.ts +0 -174
  228. package/runtime/dispatcher.ts +0 -25
  229. package/runtime/ecosystem.ts +0 -186
  230. package/runtime/provider_bootstrap.ts +0 -99
  231. package/runtime/provider_smoke.ts +0 -34
  232. package/runtime/release_readiness.ts +0 -186
  233. package/runtime/team_router.ts +0 -144
  234. package/scripts/check-omg-compat-contract-snapshot.ts +0 -20
  235. package/scripts/check-omg-standalone-clean.ts +0 -12
  236. package/scripts/check-runtime-clean.ts +0 -94
  237. package/scripts/omg.ts +0 -352
  238. package/scripts/settings-merge.ts +0 -93
  239. package/tools/commit_splitter.ts +0 -23
  240. package/tools/git_inspector.ts +0 -18
  241. package/tools/session_snapshot.ts +0 -47
  242. package/trac3er-oh-my-god-2.0.0.tgz +0 -0
  243. package/tsconfig.json +0 -15
@@ -0,0 +1,169 @@
1
+ """tmux session lifecycle manager for persistent model invocations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import re
7
+ import shutil
8
+ import subprocess
9
+ import time
10
+ import uuid
11
+
12
+
13
+ _logger = logging.getLogger(__name__)
14
+
15
+
16
+ class TmuxSessionManager:
17
+ """Manage lightweight tmux sessions used for provider command execution."""
18
+
19
+ def __init__(self, session_prefix: str = "omg") -> None:
20
+ """Initialize manager with a session name prefix."""
21
+ self.session_prefix: str = session_prefix
22
+
23
+ def is_tmux_available(self) -> bool:
24
+ """Return True when tmux is available on PATH."""
25
+ return shutil.which("tmux") is not None
26
+
27
+ def session_exists(self, name: str) -> bool:
28
+ """Return True if a tmux session exists."""
29
+ if not self.is_tmux_available():
30
+ return False
31
+ try:
32
+ result = subprocess.run(
33
+ ["tmux", "has-session", "-t", name],
34
+ capture_output=True,
35
+ text=True,
36
+ check=False,
37
+ timeout=5,
38
+ )
39
+ return result.returncode == 0
40
+ except Exception as exc:
41
+ _logger.warning("Failed to check tmux session %r: %s", name, exc)
42
+ return False
43
+
44
+ def create_session(self, name: str) -> bool:
45
+ """Create a detached tmux session and return success state."""
46
+ if not self.is_tmux_available():
47
+ return False
48
+ try:
49
+ result = subprocess.run(
50
+ ["tmux", "new-session", "-d", "-s", name],
51
+ capture_output=True,
52
+ text=True,
53
+ check=False,
54
+ timeout=10,
55
+ )
56
+ return result.returncode == 0
57
+ except Exception as exc:
58
+ _logger.warning("Failed to create tmux session %r: %s", name, exc)
59
+ return False
60
+
61
+ def kill_session(self, name: str) -> bool:
62
+ """Kill a tmux session and return success state."""
63
+ if not self.is_tmux_available():
64
+ return False
65
+ try:
66
+ result = subprocess.run(
67
+ ["tmux", "kill-session", "-t", name],
68
+ capture_output=True,
69
+ text=True,
70
+ check=False,
71
+ timeout=10,
72
+ )
73
+ return result.returncode == 0
74
+ except Exception as exc:
75
+ _logger.warning("Failed to kill tmux session %r: %s", name, exc)
76
+ return False
77
+
78
+ def send_command(self, name: str, command: str, timeout: int = 120) -> str:
79
+ """Send a command to a tmux session and return captured pane output."""
80
+ if not self.is_tmux_available():
81
+ return ""
82
+
83
+ sentinel = f"__OMG_DONE_{uuid.uuid4().hex}__"
84
+ deadline = time.monotonic() + timeout
85
+
86
+ try:
87
+ send_main = subprocess.run(
88
+ ["tmux", "send-keys", "-t", name, command, "Enter"],
89
+ capture_output=True,
90
+ text=True,
91
+ check=False,
92
+ timeout=10,
93
+ )
94
+ if send_main.returncode != 0:
95
+ return ""
96
+
97
+ send_marker = subprocess.run(
98
+ ["tmux", "send-keys", "-t", name, f"echo {sentinel}", "Enter"],
99
+ capture_output=True,
100
+ text=True,
101
+ check=False,
102
+ timeout=10,
103
+ )
104
+ if send_marker.returncode != 0:
105
+ return ""
106
+
107
+ last_output = ""
108
+ while time.monotonic() < deadline:
109
+ captured = subprocess.run(
110
+ ["tmux", "capture-pane", "-t", name, "-p"],
111
+ capture_output=True,
112
+ text=True,
113
+ check=False,
114
+ timeout=10,
115
+ )
116
+ if captured.returncode == 0:
117
+ last_output = captured.stdout
118
+ if sentinel in last_output:
119
+ return last_output.split(sentinel, 1)[0].rstrip()
120
+ time.sleep(0.25)
121
+
122
+ return last_output.rstrip()
123
+ except Exception as exc:
124
+ _logger.warning("Failed to send tmux command to %r: %s", name, exc)
125
+ return ""
126
+
127
+ def get_or_create_session(self, name: str) -> str:
128
+ """Return a fresh tmux session name, recreating stale sessions if needed."""
129
+ if self.session_exists(name):
130
+ _ = self.kill_session(name)
131
+ if not self.create_session(name):
132
+ raise RuntimeError(f"Unable to create tmux session: {name}")
133
+ return name
134
+
135
+ def make_session_name(self, provider: str, unique_id: str | None = None) -> str:
136
+ """Build a normalized tmux session name using provider and optional id."""
137
+ provider_clean = re.sub(r"[^a-zA-Z0-9_-]", "-", provider).strip("-").lower() or "session"
138
+ base_name = f"{self.session_prefix}-{provider_clean}"
139
+ if unique_id:
140
+ unique_clean = re.sub(r"[^a-zA-Z0-9_-]", "-", unique_id).strip("-").lower()
141
+ if unique_clean:
142
+ return f"{base_name}-{unique_clean}"
143
+ return base_name
144
+
145
+ def cleanup_stale_sessions(self) -> int:
146
+ """Kill all tmux sessions matching this manager prefix and return count."""
147
+ if not self.is_tmux_available():
148
+ return 0
149
+
150
+ try:
151
+ listed = subprocess.run(
152
+ ["tmux", "list-sessions", "-F", "#{session_name}"],
153
+ capture_output=True,
154
+ text=True,
155
+ check=False,
156
+ timeout=10,
157
+ )
158
+ if listed.returncode != 0:
159
+ return 0
160
+
161
+ killed = 0
162
+ prefix = f"{self.session_prefix}-"
163
+ for session_name in listed.stdout.splitlines():
164
+ if session_name.startswith(prefix) and self.kill_session(session_name):
165
+ killed += 1
166
+ return killed
167
+ except Exception as exc:
168
+ _logger.warning("Failed to cleanup tmux sessions for prefix %r: %s", self.session_prefix, exc)
169
+ return 0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """Validate committed OMG compatibility snapshot against runtime contracts."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+ import sys
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+ if str(ROOT) not in sys.path:
12
+ sys.path.insert(0, str(ROOT))
13
+
14
+ from runtime.compat import ( # noqa: E402
15
+ CONTRACT_SNAPSHOT_SCHEMA,
16
+ CONTRACT_SNAPSHOT_VERSION,
17
+ DEFAULT_CONTRACT_SNAPSHOT_PATH,
18
+ LEGACY_CONTRACT_SNAPSHOT_PATH,
19
+ build_contract_snapshot_payload,
20
+ migrate_contract_snapshot_payload,
21
+ )
22
+
23
+
24
+ def _load_snapshot(path: Path) -> dict:
25
+ with open(path, "r", encoding="utf-8") as f:
26
+ payload = json.load(f)
27
+ if not isinstance(payload, dict):
28
+ raise ValueError("snapshot must be a JSON object")
29
+ return payload
30
+
31
+
32
+ def main() -> int:
33
+ parser = argparse.ArgumentParser(description="Check OMG compatibility contract snapshot drift")
34
+ parser.add_argument("--snapshot", default="")
35
+ parser.add_argument("--strict-version", action="store_true")
36
+ args = parser.parse_args()
37
+
38
+ if args.snapshot:
39
+ snapshot_path = Path(args.snapshot)
40
+ else:
41
+ primary = ROOT / DEFAULT_CONTRACT_SNAPSHOT_PATH
42
+ legacy = ROOT / LEGACY_CONTRACT_SNAPSHOT_PATH
43
+ snapshot_path = primary if primary.exists() else legacy
44
+
45
+ if not snapshot_path.exists():
46
+ print(
47
+ json.dumps(
48
+ {"status": "error", "message": f"snapshot not found: {snapshot_path}"},
49
+ indent=2,
50
+ )
51
+ )
52
+ return 2
53
+
54
+ try:
55
+ current = _load_snapshot(snapshot_path)
56
+ except Exception as exc:
57
+ print(json.dumps({"status": "error", "message": f"invalid snapshot json: {exc}"}, indent=2))
58
+ return 2
59
+
60
+ migrated, migrations = migrate_contract_snapshot_payload(current)
61
+ expected = build_contract_snapshot_payload(include_generated_at=False)
62
+
63
+ if migrated.get("schema") != CONTRACT_SNAPSHOT_SCHEMA:
64
+ print(
65
+ json.dumps(
66
+ {
67
+ "status": "error",
68
+ "message": "snapshot schema mismatch",
69
+ "expected_schema": CONTRACT_SNAPSHOT_SCHEMA,
70
+ "actual_schema": migrated.get("schema"),
71
+ },
72
+ indent=2,
73
+ )
74
+ )
75
+ return 3
76
+
77
+ if args.strict_version and current.get("contract_version") != CONTRACT_SNAPSHOT_VERSION:
78
+ print(
79
+ json.dumps(
80
+ {
81
+ "status": "error",
82
+ "message": "snapshot contract_version mismatch (strict)",
83
+ "expected_version": CONTRACT_SNAPSHOT_VERSION,
84
+ "actual_version": current.get("contract_version"),
85
+ },
86
+ indent=2,
87
+ )
88
+ )
89
+ return 3
90
+
91
+ if migrated.get("contract_version") != CONTRACT_SNAPSHOT_VERSION:
92
+ print(
93
+ json.dumps(
94
+ {
95
+ "status": "error",
96
+ "message": "snapshot contract_version unsupported",
97
+ "expected_version": CONTRACT_SNAPSHOT_VERSION,
98
+ "actual_version": migrated.get("contract_version"),
99
+ },
100
+ indent=2,
101
+ )
102
+ )
103
+ return 3
104
+
105
+ if migrated.get("count") != expected.get("count") or migrated.get("contracts") != expected.get("contracts"):
106
+ print(
107
+ json.dumps(
108
+ {
109
+ "status": "error",
110
+ "message": "snapshot drift detected",
111
+ "expected_count": expected.get("count"),
112
+ "actual_count": migrated.get("count"),
113
+ "migrations_applied": migrations,
114
+ },
115
+ indent=2,
116
+ )
117
+ )
118
+ return 3
119
+
120
+ print(
121
+ json.dumps(
122
+ {
123
+ "status": "ok",
124
+ "message": "snapshot matches runtime contracts",
125
+ "contract_version": CONTRACT_SNAPSHOT_VERSION,
126
+ "migrations_applied": migrations,
127
+ "snapshot": str(snapshot_path),
128
+ },
129
+ indent=2,
130
+ )
131
+ )
132
+ return 0
133
+
134
+
135
+ if __name__ == "__main__":
136
+ raise SystemExit(main())
137
+
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+ """Legacy wrapper for OMG compatibility snapshot checker."""
3
+ from __future__ import annotations
4
+
5
+ import runpy
6
+ from pathlib import Path
7
+
8
+
9
+ if __name__ == "__main__":
10
+ target = Path(__file__).resolve().with_name("check-omg-compat-contract-snapshot.py")
11
+ runpy.run_path(str(target), run_name="__main__")
12
+
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env python3
2
+ """Check that the repo is safe and polished enough for a public launch."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+ import re
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+
12
+ REQUIRED_PUBLIC_DOCS = [
13
+ "README.md",
14
+ "CONTRIBUTING.md",
15
+ "SECURITY.md",
16
+ "CODE_OF_CONDUCT.md",
17
+ "CHANGELOG.md",
18
+ ]
19
+
20
+ REQUIRED_COMMUNITY_TEMPLATES = [
21
+ ".github/ISSUE_TEMPLATE/bug_report.yml",
22
+ ".github/ISSUE_TEMPLATE/feature_request.yml",
23
+ ".github/pull_request_template.md",
24
+ ]
25
+
26
+ PUBLIC_DOC_GLOBS = [
27
+ "README.md",
28
+ "CONTRIBUTING.md",
29
+ "SECURITY.md",
30
+ "CODE_OF_CONDUCT.md",
31
+ "CHANGELOG.md",
32
+ "docs/**/*.md",
33
+ "plugins/README.md",
34
+ ".github/**/*.md",
35
+ ]
36
+
37
+ TEXT_GLOBS = [
38
+ "README.md",
39
+ "CONTRIBUTING.md",
40
+ "SECURITY.md",
41
+ "CODE_OF_CONDUCT.md",
42
+ "CHANGELOG.md",
43
+ "docs/**/*.md",
44
+ "commands/**/*.md",
45
+ "plugins/**/*.md",
46
+ "agents/**/*.md",
47
+ "rules/**/*.md",
48
+ "runtime/**/*.py",
49
+ "hooks/**/*.py",
50
+ "scripts/**/*.py",
51
+ ".github/workflows/**/*.yml",
52
+ ".github/workflows/**/*.yaml",
53
+ "OMG-setup.sh",
54
+ "install.sh",
55
+ "package.json",
56
+ "settings.json",
57
+ ".claude-plugin/**/*.json",
58
+ "plugins/**/*.json",
59
+ ]
60
+
61
+ MARKDOWN_LINK_RE = re.compile(r"!\[[^\]]*\]\(([^)]+)\)|\[[^\]]+\]\(([^)]+)\)")
62
+ ALLOW_PATTERN_REFERENCES = {
63
+ ROOT / "scripts" / "check-omg-public-ready.py",
64
+ }
65
+ ALLOW_DEPRECATED_MARKETPLACE = {
66
+ ROOT / "OMG-setup.sh",
67
+ ROOT / "scripts" / "check-omg-public-ready.py",
68
+ }
69
+
70
+
71
+ def _iter_files(root: Path, globs: list[str]) -> list[Path]:
72
+ files: list[Path] = []
73
+ for pattern in globs:
74
+ files.extend(path for path in root.glob(pattern) if path.is_file())
75
+ return sorted(set(files))
76
+
77
+
78
+ def _read(path: Path) -> str:
79
+ return path.read_text(encoding="utf-8")
80
+
81
+
82
+ def _find_text_violations(root: Path) -> list[str]:
83
+ violations: list[str] = []
84
+ for path in _iter_files(root, TEXT_GLOBS):
85
+ rel = path.relative_to(root)
86
+ content = _read(path)
87
+ if "/Users/" in content:
88
+ if path not in ALLOW_PATTERN_REFERENCES:
89
+ violations.append(f"{rel}: absolute local path found in public repo content")
90
+ if ".sisyphus/" in content:
91
+ if path not in ALLOW_PATTERN_REFERENCES:
92
+ violations.append(f"{rel}: stale internal path reference found (.sisyphus/)")
93
+ if "trac3er00/OAL" in content:
94
+ if path not in ALLOW_PATTERN_REFERENCES:
95
+ violations.append(f"{rel}: old repo identifier found (trac3er00/OAL)")
96
+ if "oh-advanced-layer" in content:
97
+ if path not in ALLOW_DEPRECATED_MARKETPLACE:
98
+ violations.append(f"{rel}: deprecated marketplace identifier found (oh-advanced-layer)")
99
+ return violations
100
+
101
+
102
+ def _find_missing_docs(root: Path) -> list[str]:
103
+ violations: list[str] = []
104
+ for rel in REQUIRED_PUBLIC_DOCS:
105
+ if not (root / rel).exists():
106
+ violations.append(f"{rel}: missing required public doc")
107
+ return violations
108
+
109
+
110
+ def _find_missing_templates(root: Path) -> list[str]:
111
+ violations: list[str] = []
112
+ for rel in REQUIRED_COMMUNITY_TEMPLATES:
113
+ if not (root / rel).exists():
114
+ violations.append(f"{rel}: missing required community template")
115
+ return violations
116
+
117
+
118
+ def _find_internal_docs(root: Path) -> list[str]:
119
+ plans_dir = root / "docs" / "plans"
120
+ if plans_dir.exists():
121
+ return [f"{plans_dir.relative_to(root)}: internal planning docs must not ship in public branch"]
122
+ return []
123
+
124
+
125
+ def _normalize_link_target(raw: str) -> str:
126
+ target = raw.strip()
127
+ if target.startswith("<") and target.endswith(">"):
128
+ target = target[1:-1].strip()
129
+ return target
130
+
131
+
132
+ def _is_relative_markdown_link(target: str) -> bool:
133
+ if not target:
134
+ return False
135
+ if target.startswith("#"):
136
+ return False
137
+ lowered = target.lower()
138
+ return not (
139
+ lowered.startswith("http://")
140
+ or lowered.startswith("https://")
141
+ or lowered.startswith("mailto:")
142
+ or lowered.startswith("data:")
143
+ or lowered.startswith("file:")
144
+ or target.startswith("/")
145
+ )
146
+
147
+
148
+ def _link_target_exists(doc_path: Path, target: str) -> bool:
149
+ file_part = target.split("#", 1)[0].split("?", 1)[0]
150
+ if not file_part:
151
+ return True
152
+ resolved = (doc_path.parent / file_part).resolve()
153
+ return resolved.exists()
154
+
155
+
156
+ def _find_broken_markdown_links(root: Path) -> list[str]:
157
+ violations: list[str] = []
158
+ for path in _iter_files(root, PUBLIC_DOC_GLOBS):
159
+ rel = path.relative_to(root)
160
+ content = _read(path)
161
+ for match in MARKDOWN_LINK_RE.finditer(content):
162
+ target = _normalize_link_target(match.group(1) or match.group(2) or "")
163
+ if not _is_relative_markdown_link(target):
164
+ continue
165
+ if not _link_target_exists(path, target):
166
+ violations.append(f"{rel}: broken markdown link -> {target}")
167
+ return violations
168
+
169
+
170
+ def main() -> int:
171
+ parser = argparse.ArgumentParser(description="Check OMG public-readiness hygiene")
172
+ parser.add_argument("--root", default=str(ROOT))
173
+ args = parser.parse_args()
174
+ root = Path(args.root).resolve()
175
+
176
+ violations = []
177
+ violations.extend(_find_missing_docs(root))
178
+ violations.extend(_find_missing_templates(root))
179
+ violations.extend(_find_internal_docs(root))
180
+ violations.extend(_find_text_violations(root))
181
+ violations.extend(_find_broken_markdown_links(root))
182
+ violations = sorted(set(violations))
183
+
184
+ if violations:
185
+ print(json.dumps({"status": "error", "violations": violations}, indent=2))
186
+ return 1
187
+
188
+ print(json.dumps({"status": "ok", "message": "public readiness check passed"}, indent=2))
189
+ return 0
190
+
191
+
192
+ if __name__ == "__main__":
193
+ raise SystemExit(main())
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python3
2
+ """Check that standalone/OMG-first naming rules are preserved."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+ import sys
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+
12
+ # Paths where legacy aliases are explicitly allowed.
13
+ ALLOW_COMPAT_CLI = {
14
+ ROOT / "commands" / "OMG:compat.md",
15
+ ROOT / "tests" / "scripts" / "test_omg_cli.py",
16
+ ROOT / "tests" / "scripts" / "test_standalone_clean_check.py",
17
+ ROOT / "scripts" / "check-omg-standalone-clean.py",
18
+ ROOT / ".github" / "workflows" / "omg-compat-gate.yml",
19
+ }
20
+
21
+ ALLOW_LEGACY_MIGRATOR = {
22
+ ROOT / "scripts" / "migrate-legacy.py",
23
+ ROOT / "tests" / "e2e" / "test_standalone_ga.py",
24
+ ROOT / "scripts" / "check-omg-standalone-clean.py",
25
+ }
26
+
27
+ ALLOW_LEGACY_SNAPSHOT_CHECKER = {
28
+ ROOT / "scripts" / "check-omg-contract-snapshot.py",
29
+ ROOT / "tests" / "scripts" / "test_compat_snapshot_check.py",
30
+ ROOT / "scripts" / "check-omg-standalone-clean.py",
31
+ }
32
+
33
+ ALLOW_LEGACY_RUNTIME_IMPORT = {
34
+ ROOT / "tests" / "scripts" / "test_standalone_clean_check.py",
35
+ ROOT / "scripts" / "check-omg-standalone-clean.py",
36
+ }
37
+
38
+ SCAN_GLOBS = [
39
+ "README.md",
40
+ "OMG-setup.sh",
41
+ "install.sh",
42
+ "runtime/**/*.py",
43
+ "hooks/**/*.py",
44
+ "scripts/**/*.py",
45
+ "commands/**/*.md",
46
+ ".github/workflows/**/*.yml",
47
+ "tests/**/*.py",
48
+ ]
49
+
50
+
51
+ def _iter_files(root: Path) -> list[Path]:
52
+ files: list[Path] = []
53
+ for pattern in SCAN_GLOBS:
54
+ files.extend(p for p in root.glob(pattern) if p.is_file())
55
+ # deterministic order
56
+ return sorted(set(files))
57
+
58
+
59
+ def _contains(path: Path, token: str) -> bool:
60
+ return token in path.read_text(encoding="utf-8")
61
+
62
+
63
+ def main() -> int:
64
+ parser = argparse.ArgumentParser(description="Check OMG standalone naming hygiene")
65
+ parser.add_argument("--root", default=str(ROOT))
66
+ args = parser.parse_args()
67
+ root = Path(args.root).resolve()
68
+
69
+ violations: list[str] = []
70
+
71
+ deprecated_workflow = root / ".github" / "workflows" / "compat-gate.yml"
72
+ if deprecated_workflow.exists():
73
+ violations.append(f"deprecated workflow exists: {deprecated_workflow.relative_to(root)}")
74
+
75
+ for path in _iter_files(root):
76
+ rel = path.relative_to(root)
77
+
78
+ if _contains(path, "python3 scripts/omg.py compat ") or _contains(path, "python3 scripts/omg.py omc "):
79
+ if path not in ALLOW_COMPAT_CLI:
80
+ violations.append(f"{rel}: legacy CLI namespace used outside allowlist")
81
+
82
+ if _contains(path, "migrate-legacy.py"):
83
+ if path not in ALLOW_LEGACY_MIGRATOR:
84
+ violations.append(f"{rel}: legacy migrator path reference outside allowlist")
85
+
86
+ if _contains(path, "check-omg-contract-snapshot.py"):
87
+ if path not in ALLOW_LEGACY_SNAPSHOT_CHECKER:
88
+ violations.append(f"{rel}: legacy snapshot checker reference outside allowlist")
89
+
90
+ if _contains(path, "runtime.legacy_compat"):
91
+ if path not in ALLOW_LEGACY_RUNTIME_IMPORT:
92
+ violations.append(f"{rel}: legacy runtime import outside allowlist")
93
+
94
+ if violations:
95
+ print(json.dumps({"status": "error", "violations": violations}, indent=2))
96
+ return 1
97
+
98
+ print(json.dumps({"status": "ok", "message": "standalone naming check passed"}, indent=2))
99
+ return 0
100
+
101
+
102
+ if __name__ == "__main__":
103
+ raise SystemExit(main())
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ """CLI utility: migrate legacy state into canonical `.omg` layout."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ ROOT = Path(__file__).resolve().parents[1]
12
+ if str(ROOT) not in sys.path:
13
+ sys.path.insert(0, str(ROOT))
14
+
15
+ from hooks.state_migration import migrate_legacy_to_omg # noqa: E402
16
+
17
+
18
+ def main() -> int:
19
+ parser = argparse.ArgumentParser(description="Migrate legacy state to .omg")
20
+ parser.add_argument("--project-dir", default=os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()))
21
+ args = parser.parse_args()
22
+
23
+ report = migrate_legacy_to_omg(args.project_dir)
24
+ print(json.dumps(report, indent=2))
25
+ return 0 if report.get("result") in {"ok", "no_legacy"} else 1
26
+
27
+
28
+ if __name__ == "__main__":
29
+ raise SystemExit(main())