@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,1299 @@
1
+ """OMG standalone legacy-compat dispatcher.
2
+
3
+ Primary namespace is `compat/*` while legacy `omg/*` aliases remain supported.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from collections import Counter
8
+ from datetime import datetime, timezone
9
+ import json
10
+ import os
11
+ from pathlib import Path
12
+ import re
13
+ import sys
14
+ from typing import Any
15
+
16
+ from hooks.policy_engine import evaluate_bash_command
17
+ from lab.pipeline import run_pipeline
18
+ from runtime.dispatcher import dispatch_runtime
19
+ from runtime.team_router import TeamDispatchRequest, dispatch_team
20
+
21
+ CONTRACT_SNAPSHOT_SCHEMA = "OmgCompatContractSnapshot"
22
+ LEGACY_CONTRACT_SNAPSHOT_SCHEMA = "OmgCompatContractSnapshot"
23
+ CONTRACT_SNAPSHOT_VERSION = "1.0.0"
24
+ LEGACY_SNAPSHOT_VERSION = "0.9.0"
25
+ GAP_REPORT_SCHEMA = "OmgCompatGapReport"
26
+ LEGACY_GAP_REPORT_SCHEMA = "OmgCompatGapReport"
27
+ RESULT_SCHEMA = "OmgCompatResult"
28
+ LEGACY_RESULT_SCHEMA = "OmgCompatResult"
29
+ DEFAULT_CONTRACT_SNAPSHOT_PATH = "runtime/omg_compat_contract_snapshot.json"
30
+ LEGACY_CONTRACT_SNAPSHOT_PATH = "runtime/omg_contract_snapshot.json"
31
+ DEFAULT_GAP_REPORT_PATH = ".omg/evidence/omg-compat-gap.json"
32
+ LEGACY_GAP_REPORT_PATH = ".omg/evidence/compat-gap.json"
33
+ DEFAULT_AUDIT_LEDGER_PATH = ".omg/state/ledger/omg-compat-audit.jsonl"
34
+ LEGACY_AUDIT_LEDGER_PATH = ".omg/state/ledger/compat-audit.jsonl"
35
+ DEFAULT_EVENT_DISPATCH = "compat_dispatch"
36
+ DEFAULT_EVENT_REQUEST = "compat_dispatch_request"
37
+ LEGACY_EVENT_ALIASES: dict[str, str] = {
38
+ DEFAULT_EVENT_DISPATCH: "omg_dispatch",
39
+ DEFAULT_EVENT_REQUEST: "omg_dispatch_request",
40
+ }
41
+
42
+ MAX_PROBLEM_CHARS = 4000
43
+ MAX_CONTEXT_CHARS = 12000
44
+ MAX_EXPECTED_OUTCOME_CHARS = 3000
45
+ MAX_FILES_PER_REQUEST = 50
46
+ MAX_FILE_PATH_CHARS = 260
47
+ WINDOWS_ABS_PATH_RE = re.compile(r"^[A-Za-z]:[\\/]")
48
+
49
+
50
+ def _now() -> str:
51
+ return datetime.now(timezone.utc).isoformat()
52
+
53
+
54
+ def _project_dir(project_dir: str | None) -> str:
55
+ return project_dir or os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
56
+
57
+
58
+ def _is_safe_relative_path(file_path: str) -> bool:
59
+ normalized = file_path.replace("\\", "/")
60
+ if not normalized or normalized.startswith(("/", "~")) or normalized.startswith("//"):
61
+ return False
62
+ if WINDOWS_ABS_PATH_RE.match(file_path):
63
+ return False
64
+ parts = [part for part in normalized.split("/") if part not in {"", "."}]
65
+ return ".." not in parts
66
+
67
+
68
+ LEGACY_SKILL_ROUTES: dict[str, str] = {
69
+ "analyze": "maintainer",
70
+ "autopilot": "runtime_ship",
71
+ "beads": "maintainer",
72
+ "build-fix": "runtime_ship",
73
+ "cancel": "cancel",
74
+ "ccg": "ccg",
75
+ "claude-flow": "ccg",
76
+ "claude-mem": "memory",
77
+ "code-review": "review",
78
+ "compound-engineering": "ccg",
79
+ "compounding-engineering": "ccg",
80
+ "configure-notifications": "health",
81
+ "configure-openclaw": "health",
82
+ "deepinit": "init",
83
+ "external-context": "teams",
84
+ "hooks-mastery": "health",
85
+ "hud": "health",
86
+ "learn-about-omg": "learn",
87
+ "learner": "learn",
88
+ "mcp-setup": "health",
89
+ "memsearch": "memory",
90
+ "note": "memory",
91
+ "omg-doctor": "health",
92
+ "omg-help": "help",
93
+ "omg-setup": "init",
94
+ "omg-teams": "teams",
95
+ "pipeline": "pipeline",
96
+ "plan": "plan",
97
+ "planning-with-files": "plan",
98
+ "project-session-manager": "memory",
99
+ "ralph": "runtime_ship",
100
+ "ralph-wiggum": "runtime_ship",
101
+ "ralph-init": "init",
102
+ "ralplan": "plan",
103
+ "release": "runtime_ship",
104
+ "review": "review",
105
+ "sci-omg": "maintainer",
106
+ "security-review": "secure",
107
+ "skill": "learn",
108
+ "omg-superpowers": "plan",
109
+ "tdd": "plan",
110
+ "team": "teams",
111
+ "trace": "maintainer",
112
+ "ultrapilot": "runtime_ship",
113
+ "ultraqa": "review",
114
+ "ultrawork": "runtime_ship",
115
+ "writer-memory": "memory",
116
+ }
117
+ # Backward-compatible export
118
+ OMG_COMPAT_SKILL_ROUTES = LEGACY_SKILL_ROUTES
119
+
120
+ ROUTE_MATURITY: dict[str, str] = {
121
+ "teams": "native",
122
+ "ccg": "native",
123
+ "runtime_ship": "native",
124
+ "pipeline": "native",
125
+ "memory": "native",
126
+ "init": "native",
127
+ "health": "native",
128
+ "help": "native",
129
+ "review": "native",
130
+ "plan": "native",
131
+ "secure": "native",
132
+ "learn": "native",
133
+ "maintainer": "native",
134
+ "cancel": "native",
135
+ }
136
+
137
+ SKILL_MATURITY_OVERRIDES: dict[str, str] = {
138
+ # Next-phase native promotion batch
139
+ "autopilot": "native",
140
+ "ralph": "native",
141
+ "ultrapilot": "native",
142
+ "ultrawork": "native",
143
+ "review": "native",
144
+ "code-review": "native",
145
+ "ultraqa": "native",
146
+ "release": "native",
147
+ "tdd": "native",
148
+ "plan": "native",
149
+ "ralplan": "native",
150
+ # Final bridge -> native promotion batch
151
+ "analyze": "native",
152
+ "build-fix": "native",
153
+ "learn-about-omg": "native",
154
+ "learner": "native",
155
+ "note": "native",
156
+ "project-session-manager": "native",
157
+ "sci-omg": "native",
158
+ "skill": "native",
159
+ "trace": "native",
160
+ "writer-memory": "native",
161
+ # Ecosystem imports promoted as first-class native routes
162
+ "omg-superpowers": "native",
163
+ "ralph-wiggum": "native",
164
+ "claude-flow": "native",
165
+ "claude-mem": "native",
166
+ "memsearch": "native",
167
+ "beads": "native",
168
+ "planning-with-files": "native",
169
+ "hooks-mastery": "native",
170
+ "compound-engineering": "native",
171
+ "compounding-engineering": "native",
172
+ }
173
+
174
+ ROUTE_INPUTS: dict[str, dict[str, Any]] = {
175
+ "teams": {"required": ["problem"], "optional": ["context", "files", "expected_outcome"]},
176
+ "ccg": {"required": ["problem"], "optional": ["context", "files", "expected_outcome"]},
177
+ "runtime_ship": {"required": ["problem"], "optional": ["expected_outcome"]},
178
+ "pipeline": {"required": ["problem"], "optional": ["context"]},
179
+ "memory": {"required": ["problem"], "optional": ["context"]},
180
+ "init": {"required": [], "optional": ["problem"]},
181
+ "health": {"required": [], "optional": ["problem"]},
182
+ "help": {"required": [], "optional": []},
183
+ "review": {"required": ["problem"], "optional": ["context", "files"]},
184
+ "plan": {"required": ["problem"], "optional": ["expected_outcome"]},
185
+ "secure": {"required": ["problem"], "optional": []},
186
+ "learn": {"required": ["problem"], "optional": ["context"]},
187
+ "maintainer": {"required": ["problem"], "optional": ["context"]},
188
+ "cancel": {"required": [], "optional": []},
189
+ }
190
+
191
+ ROUTE_OUTPUTS: dict[str, dict[str, Any]] = {
192
+ "teams": {"schema": "TeamDispatchResult"},
193
+ "ccg": {"schema": "TeamDispatchResult"},
194
+ "runtime_ship": {"schema": "RuntimeDispatchResult"},
195
+ "pipeline": {"schema": "LabPipelineResult"},
196
+ "memory": {"schema": "StateMutationResult"},
197
+ "init": {"schema": "BootstrapResult"},
198
+ "health": {"schema": "HealthSnapshot"},
199
+ "help": {"schema": "CompatibilityHelp"},
200
+ "review": {"schema": "TeamDispatchResult"},
201
+ "plan": {"schema": "PlanningArtifacts"},
202
+ "secure": {"schema": "PolicyDecision"},
203
+ "learn": {"schema": "LearningArtifact"},
204
+ "maintainer": {"schema": "MaintainerCompatArtifact"},
205
+ "cancel": {"schema": "CancelResult"},
206
+ }
207
+
208
+ ROUTE_SIDE_EFFECTS: dict[str, list[str]] = {
209
+ "teams": [],
210
+ "ccg": [],
211
+ "runtime_ship": [],
212
+ "pipeline": [],
213
+ "memory": [".omg/state/working-memory.md", ".omg/state/session.json (psm only)"],
214
+ "init": [".omg/state/*", ".omg/idea.yml", ".omg/policy.yaml", ".omg/runtime.yaml"],
215
+ "health": [],
216
+ "help": [],
217
+ "review": [],
218
+ "plan": [".omg/state/_plan.md", ".omg/state/_checklist.md", ".omg/idea.yml"],
219
+ "secure": [],
220
+ "learn": [".omg/state/working-memory.md"],
221
+ "maintainer": [".omg/evidence/compat-*.json"],
222
+ "cancel": [".omg/shadow/active-run (removed when exists)"],
223
+ }
224
+
225
+ SKILL_OUTPUT_SCHEMA_OVERRIDES: dict[str, str] = {
226
+ "review": "ReviewSynthesis",
227
+ "code-review": "ReviewSynthesis",
228
+ "ultraqa": "ReviewSynthesis",
229
+ "analyze": "AnalysisCompatArtifact",
230
+ "trace": "AnalysisCompatArtifact",
231
+ "sci-omg": "AnalysisCompatArtifact",
232
+ "project-session-manager": "SessionState",
233
+ }
234
+
235
+ SKILL_SIDE_EFFECT_OVERRIDES: dict[str, list[str]] = {
236
+ "autopilot": [".omg/state/persistent-mode.json"],
237
+ "ralph": [".omg/state/persistent-mode.json"],
238
+ "ralph-wiggum": [".omg/state/persistent-mode.json"],
239
+ "ultrapilot": [".omg/state/persistent-mode.json"],
240
+ "ultrawork": [".omg/state/persistent-mode.json"],
241
+ "release": [".omg/evidence/release-draft.md"],
242
+ "build-fix": [".omg/state/build-fix.md"],
243
+ "analyze": [".omg/evidence/analysis-analyze.json"],
244
+ "trace": [".omg/evidence/analysis-trace.json"],
245
+ "sci-omg": [".omg/evidence/analysis-sci-omg.json"],
246
+ "project-session-manager": [".omg/state/session.json"],
247
+ "learn-about-omg": [".omg/knowledge/learning/learn-about-omg.md"],
248
+ "learner": [".omg/knowledge/learning/learner.md"],
249
+ "skill": [".omg/knowledge/learning/skill.md"],
250
+ "note": [".omg/knowledge/notes.md"],
251
+ "writer-memory": [".omg/knowledge/writer-memory.md"],
252
+ "omg-superpowers": [".omg/state/_plan.md", ".omg/state/_checklist.md"],
253
+ "planning-with-files": [".omg/state/_plan.md", ".omg/state/_checklist.md"],
254
+ "claude-mem": [".omg/state/working-memory.md"],
255
+ "memsearch": [".omg/state/working-memory.md"],
256
+ "beads": [".omg/evidence/compat-beads.json"],
257
+ "hooks-mastery": [],
258
+ "claude-flow": [],
259
+ "compound-engineering": [],
260
+ "compounding-engineering": [],
261
+ }
262
+
263
+ SKILL_ROUTE_NOTES: dict[str, str] = {
264
+ "omg-teams": "Legacy tmux worker dispatch replaced by internal Team router.",
265
+ "project-session-manager": "Session metadata maintained in .omg/state/session.json.",
266
+ "omg-setup": "Bootstraps OMG standalone state and baseline config files.",
267
+ "omg-doctor": "Health checks run against OMG standalone layout.",
268
+ "pipeline": "Routes to OMG lab policy+pipeline executor.",
269
+ "release": "Routes to runtime ship and emits release draft artifact.",
270
+ "tdd": "Generates plan/checklist scaffolding for red-green-refactor workflow.",
271
+ "build-fix": "Creates targeted fix checklist and routes execution to runtime.",
272
+ "analyze": "Writes structured analysis evidence artifact.",
273
+ "trace": "Writes trace evidence artifact for debugging chain.",
274
+ "learner": "Writes learning note into .omg/knowledge/learning.",
275
+ "writer-memory": "Writes long-form memory artifact for writing workflows.",
276
+ "omg-superpowers": "Imports TDD-first planning discipline into OMG plan route.",
277
+ "ralph-wiggum": "Persistent iteration loop via runtime persistent-mode state.",
278
+ "claude-flow": "Maps to CCG route for multi-agent orchestration semantics.",
279
+ "claude-mem": "Maps to memory route for durable working-context updates.",
280
+ "memsearch": "Maps to memory route for retrieval-oriented context search workflow.",
281
+ "beads": "Maps to maintainer route for context engineering artifacts.",
282
+ "planning-with-files": "Strengthens file-native planning artifacts in .omg/state.",
283
+ "hooks-mastery": "Maps to health route for hook quality and readiness checks.",
284
+ "compound-engineering": "Maps to CCG route for compounding, iterative orchestration.",
285
+ "compounding-engineering": "Alias to compound-engineering orchestration route.",
286
+ }
287
+
288
+
289
+ def _contract_for(skill: str, route: str) -> dict[str, Any]:
290
+ outputs = dict(ROUTE_OUTPUTS.get(route, {"schema": "Unknown"}))
291
+ if skill in SKILL_OUTPUT_SCHEMA_OVERRIDES:
292
+ outputs["schema"] = SKILL_OUTPUT_SCHEMA_OVERRIDES[skill]
293
+ side_effects = SKILL_SIDE_EFFECT_OVERRIDES.get(skill, ROUTE_SIDE_EFFECTS.get(route, []))
294
+ return {
295
+ "skill": skill,
296
+ "route": route,
297
+ "maturity": SKILL_MATURITY_OVERRIDES.get(skill, ROUTE_MATURITY.get(route, "bridge")),
298
+ "inputs": ROUTE_INPUTS.get(route, {"required": [], "optional": []}),
299
+ "outputs": outputs,
300
+ "side_effects": side_effects,
301
+ "notes": SKILL_ROUTE_NOTES.get(skill, ""),
302
+ }
303
+
304
+
305
+ LEGACY_SKILL_CONTRACTS: dict[str, dict[str, Any]] = {
306
+ skill: _contract_for(skill, route) for skill, route in LEGACY_SKILL_ROUTES.items()
307
+ }
308
+ # Backward-compatible export
309
+ OMG_COMPAT_SKILL_CONTRACTS = LEGACY_SKILL_CONTRACTS
310
+
311
+
312
+ def list_compat_skills() -> list[str]:
313
+ return sorted(LEGACY_SKILL_ROUTES.keys())
314
+
315
+
316
+ def list_compat_skill_contracts() -> list[dict[str, Any]]:
317
+ return [LEGACY_SKILL_CONTRACTS[name] for name in list_compat_skills()]
318
+
319
+
320
+ def get_compat_skill_contract(skill: str) -> dict[str, Any] | None:
321
+ return LEGACY_SKILL_CONTRACTS.get(skill)
322
+
323
+
324
+ def build_contract_snapshot_payload(*, include_generated_at: bool = True) -> dict[str, Any]:
325
+ payload: dict[str, Any] = {
326
+ "schema": CONTRACT_SNAPSHOT_SCHEMA,
327
+ "contract_version": CONTRACT_SNAPSHOT_VERSION,
328
+ "count": len(LEGACY_SKILL_CONTRACTS),
329
+ "contracts": list_compat_skill_contracts(),
330
+ }
331
+ if include_generated_at:
332
+ payload["generated_at"] = _now()
333
+ return payload
334
+
335
+
336
+ def migrate_contract_snapshot_payload(payload: dict[str, Any]) -> tuple[dict[str, Any], list[str]]:
337
+ migrated = dict(payload)
338
+ migrations: list[str] = []
339
+
340
+ if "schema" not in migrated:
341
+ migrated["schema"] = LEGACY_CONTRACT_SNAPSHOT_SCHEMA
342
+ migrations.append("assign-missing-schema:legacy-omg")
343
+
344
+ if migrated.get("schema") == LEGACY_CONTRACT_SNAPSHOT_SCHEMA:
345
+ migrated["schema"] = CONTRACT_SNAPSHOT_SCHEMA
346
+ migrations.append("migrate-schema-legacy-to-omg")
347
+
348
+ if "contract_version" not in migrated:
349
+ migrated["contract_version"] = LEGACY_SNAPSHOT_VERSION
350
+ migrations.append("assign-missing-contract-version:0.9.0")
351
+
352
+ if migrated.get("contract_version") == LEGACY_SNAPSHOT_VERSION:
353
+ # v0.9.0 lacked explicit schema/version constraints.
354
+ migrated["schema"] = CONTRACT_SNAPSHOT_SCHEMA
355
+ migrated["contract_version"] = CONTRACT_SNAPSHOT_VERSION
356
+ migrations.append("migrate-0.9.0-to-1.0.0")
357
+
358
+ return migrated, migrations
359
+
360
+
361
+ def build_compat_gap_report(project_dir: str | None = None) -> dict[str, Any]:
362
+ root = _project_dir(project_dir)
363
+ _ensure_state_layout(root)
364
+ contracts = list_compat_skill_contracts()
365
+ maturity_counts = Counter(c["maturity"] for c in contracts)
366
+ route_counts = Counter(c["route"] for c in contracts)
367
+ bridge_skills = [c["skill"] for c in contracts if c["maturity"] != "native"]
368
+ report = {
369
+ "schema": GAP_REPORT_SCHEMA,
370
+ "generated_at": _now(),
371
+ "total_skills": len(contracts),
372
+ "maturity_counts": dict(sorted(maturity_counts.items())),
373
+ "route_counts": dict(sorted(route_counts.items())),
374
+ "native_skills": sorted(c["skill"] for c in contracts if c["maturity"] == "native"),
375
+ "bridge_skills": sorted(bridge_skills),
376
+ }
377
+ out = os.path.join(root, DEFAULT_GAP_REPORT_PATH)
378
+ with open(out, "w", encoding="utf-8") as f:
379
+ json.dump(report, f, indent=2, ensure_ascii=True)
380
+ legacy_out = os.path.join(root, LEGACY_GAP_REPORT_PATH)
381
+ if legacy_out != out:
382
+ with open(legacy_out, "w", encoding="utf-8") as f:
383
+ json.dump(report, f, indent=2, ensure_ascii=True)
384
+ report["report_path"] = out
385
+ report["legacy_report_path"] = legacy_out
386
+ return report
387
+
388
+
389
+ def _result(
390
+ *,
391
+ skill: str,
392
+ route: str,
393
+ status: str = "ok",
394
+ routed_to: str = "",
395
+ findings: list[str] | None = None,
396
+ actions: list[str] | None = None,
397
+ artifacts: list[str] | None = None,
398
+ result: dict[str, Any] | None = None,
399
+ ) -> dict[str, Any]:
400
+ return {
401
+ "schema": RESULT_SCHEMA,
402
+ "status": status,
403
+ "skill": skill,
404
+ "route": route,
405
+ "routed_to": routed_to,
406
+ "contract": get_compat_skill_contract(skill) or {},
407
+ "findings": findings or [],
408
+ "actions": actions or [],
409
+ "artifacts": artifacts or [],
410
+ "result": result or {},
411
+ "generated_at": _now(),
412
+ }
413
+
414
+
415
+ def _ensure_state_layout(project_dir: str) -> None:
416
+ for rel in ["state", "knowledge", "evidence", "trust", "shadow"]:
417
+ os.makedirs(os.path.join(project_dir, ".omg", rel), exist_ok=True)
418
+
419
+
420
+ def _append_audit_event(project_dir: str, event: dict[str, Any]) -> None:
421
+ _ensure_state_layout(project_dir)
422
+ ledger_dir = os.path.join(project_dir, ".omg", "state", "ledger")
423
+ os.makedirs(ledger_dir, exist_ok=True)
424
+ payload = dict(event)
425
+ payload.setdefault("ts", _now())
426
+ payloads = [payload]
427
+ event_name = str(payload.get("event", ""))
428
+ if event_name in LEGACY_EVENT_ALIASES:
429
+ legacy_payload = dict(payload)
430
+ legacy_payload["event"] = LEGACY_EVENT_ALIASES[event_name]
431
+ legacy_payload["alias_of"] = event_name
432
+ payloads.append(legacy_payload)
433
+ for rel in (DEFAULT_AUDIT_LEDGER_PATH, LEGACY_AUDIT_LEDGER_PATH):
434
+ ledger_path = os.path.join(project_dir, rel)
435
+ os.makedirs(os.path.dirname(ledger_path), exist_ok=True)
436
+ with open(ledger_path, "a", encoding="utf-8") as f:
437
+ for one in payloads:
438
+ f.write(json.dumps(one, ensure_ascii=True) + "\n")
439
+
440
+
441
+ def validate_compat_request(
442
+ *,
443
+ skill: str,
444
+ problem: str,
445
+ context: str,
446
+ files: list[str] | None,
447
+ expected_outcome: str,
448
+ ) -> tuple[bool, str]:
449
+ if skill not in LEGACY_SKILL_ROUTES:
450
+ return False, f"Unknown skill: {skill}"
451
+
452
+ if len(problem) > MAX_PROBLEM_CHARS:
453
+ return False, f"problem too long (max {MAX_PROBLEM_CHARS})"
454
+ if len(context) > MAX_CONTEXT_CHARS:
455
+ return False, f"context too long (max {MAX_CONTEXT_CHARS})"
456
+ if len(expected_outcome) > MAX_EXPECTED_OUTCOME_CHARS:
457
+ return False, f"expected_outcome too long (max {MAX_EXPECTED_OUTCOME_CHARS})"
458
+
459
+ route = LEGACY_SKILL_ROUTES[skill]
460
+ required = set(ROUTE_INPUTS.get(route, {}).get("required", []))
461
+ if "problem" in required and not problem.strip():
462
+ return False, "problem is required for this skill"
463
+
464
+ file_list = files or []
465
+ if len(file_list) > MAX_FILES_PER_REQUEST:
466
+ return False, f"too many files (max {MAX_FILES_PER_REQUEST})"
467
+ for file_path in file_list:
468
+ if not isinstance(file_path, str) or not file_path:
469
+ return False, "invalid file path: must be non-empty string"
470
+ if "\x00" in file_path:
471
+ return False, "invalid file path: contains null byte"
472
+ if len(file_path) > MAX_FILE_PATH_CHARS:
473
+ return False, f"invalid file path: exceeds {MAX_FILE_PATH_CHARS} chars"
474
+ if file_path != file_path.strip():
475
+ return False, "invalid file path: leading/trailing whitespace is not allowed"
476
+ if not _is_safe_relative_path(file_path):
477
+ return False, "invalid file path: must be a safe relative path"
478
+
479
+ return True, "ok"
480
+
481
+
482
+ def _write_if_missing(path: str, content: str) -> None:
483
+ if os.path.exists(path):
484
+ return
485
+ os.makedirs(os.path.dirname(path), exist_ok=True)
486
+ with open(path, "w", encoding="utf-8") as f:
487
+ f.write(content)
488
+
489
+
490
+ def _append_memory(project_dir: str, message: str) -> str:
491
+ _ensure_state_layout(project_dir)
492
+ wm_path = os.path.join(project_dir, ".omg", "state", "working-memory.md")
493
+ ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%SZ")
494
+ line = f"- [{ts}] {message}\n"
495
+ with open(wm_path, "a", encoding="utf-8") as f:
496
+ f.write(line)
497
+ return wm_path
498
+
499
+
500
+ def _update_session_state(project_dir: str, message: str) -> str:
501
+ _ensure_state_layout(project_dir)
502
+ session_path = os.path.join(project_dir, ".omg", "state", "session.json")
503
+ payload: dict[str, Any] = {"last_updated": _now(), "entries": []}
504
+ if os.path.exists(session_path):
505
+ try:
506
+ with open(session_path, "r", encoding="utf-8") as f:
507
+ data = json.load(f)
508
+ if isinstance(data, dict):
509
+ payload = data
510
+ except Exception:
511
+ pass
512
+ payload.setdefault("entries", [])
513
+ if not isinstance(payload["entries"], list):
514
+ payload["entries"] = []
515
+ payload["entries"].append({"ts": _now(), "message": message})
516
+ payload["entries"] = payload["entries"][-100:]
517
+ payload["last_updated"] = _now()
518
+ with open(session_path, "w", encoding="utf-8") as f:
519
+ json.dump(payload, f, indent=2, ensure_ascii=True)
520
+ return session_path
521
+
522
+
523
+ def _append_knowledge_note(project_dir: str, rel_path: str, line: str) -> str:
524
+ _ensure_state_layout(project_dir)
525
+ full = os.path.join(project_dir, ".omg", rel_path)
526
+ os.makedirs(os.path.dirname(full), exist_ok=True)
527
+ with open(full, "a", encoding="utf-8") as f:
528
+ f.write(line.rstrip() + "\n")
529
+ return full
530
+
531
+
532
+ def _write_learning_artifact(project_dir: str, skill: str, message: str, context: str) -> str:
533
+ path = os.path.join(project_dir, ".omg", "knowledge", "learning", f"{skill}.md")
534
+ os.makedirs(os.path.dirname(path), exist_ok=True)
535
+ with open(path, "a", encoding="utf-8") as f:
536
+ f.write(f"## {_now()}\n")
537
+ f.write(f"- skill: {skill}\n")
538
+ f.write(f"- message: {message}\n")
539
+ if context:
540
+ f.write(f"- context: {context}\n")
541
+ f.write("\n")
542
+ return path
543
+
544
+
545
+ def _write_analysis_artifact(project_dir: str, skill: str, message: str, context: str, files: list[str]) -> str:
546
+ _ensure_state_layout(project_dir)
547
+ out = os.path.join(project_dir, ".omg", "evidence", f"analysis-{skill}.json")
548
+ payload = {
549
+ "schema": "AnalysisCompatArtifact",
550
+ "skill": skill,
551
+ "generated_at": _now(),
552
+ "problem": message,
553
+ "context": context,
554
+ "files": files,
555
+ "findings": [
556
+ "Structured analysis generated by OMG compat dispatcher.",
557
+ "Use review/teams routes for deeper remediation proposals.",
558
+ ],
559
+ }
560
+ with open(out, "w", encoding="utf-8") as f:
561
+ json.dump(payload, f, indent=2, ensure_ascii=True)
562
+ return out
563
+
564
+
565
+ def _write_maintainer_artifact(project_dir: str, skill: str, problem: str) -> str:
566
+ _ensure_state_layout(project_dir)
567
+ out_path = os.path.join(project_dir, ".omg", "evidence", f"compat-{skill}.json")
568
+ payload = {
569
+ "schema": "MaintainerCompatArtifact",
570
+ "skill": skill,
571
+ "generated_at": _now(),
572
+ "summary": problem or f"compat route for {skill}",
573
+ "signals": {
574
+ "triage": "unverified",
575
+ "release_notes": "unverified",
576
+ "review": "unverified",
577
+ },
578
+ }
579
+ with open(out_path, "w", encoding="utf-8") as f:
580
+ json.dump(payload, f, indent=2, ensure_ascii=True)
581
+ return out_path
582
+
583
+
584
+ def _init_bootstrap(project_dir: str, reason: str) -> list[str]:
585
+ _ensure_state_layout(project_dir)
586
+ profile_path = os.path.join(project_dir, ".omg", "state", "profile.yaml")
587
+ idea_path = os.path.join(project_dir, ".omg", "idea.yml")
588
+ policy_path = os.path.join(project_dir, ".omg", "policy.yaml")
589
+ runtime_path = os.path.join(project_dir, ".omg", "runtime.yaml")
590
+ plan_path = os.path.join(project_dir, ".omg", "state", "_plan.md")
591
+ checklist_path = os.path.join(project_dir, ".omg", "state", "_checklist.md")
592
+ qg_path = os.path.join(project_dir, ".omg", "state", "quality-gate.json")
593
+
594
+ _write_if_missing(
595
+ profile_path,
596
+ "name: omg-project\n"
597
+ "description: initialized by OMG standalone compat bootstrap\n"
598
+ "language: unknown\n"
599
+ "framework: unknown\n",
600
+ )
601
+ _write_if_missing(
602
+ idea_path,
603
+ "goal: \"\"\n"
604
+ "constraints: []\n"
605
+ "acceptance: []\n"
606
+ "risk:\n"
607
+ " security: []\n"
608
+ " performance: []\n"
609
+ " compatibility: []\n"
610
+ "evidence_required:\n"
611
+ " tests: []\n"
612
+ " security_scans: []\n"
613
+ " reproducibility: []\n"
614
+ " artifacts: []\n",
615
+ )
616
+ _write_if_missing(
617
+ policy_path,
618
+ "mode: warn_and_run\ncritical_block: true\n",
619
+ )
620
+ _write_if_missing(
621
+ runtime_path,
622
+ "default: claude\navailable:\n - claude\n - gpt\n - local\n",
623
+ )
624
+ _write_if_missing(
625
+ plan_path,
626
+ "# Compat Plan\n"
627
+ f"goal: {reason or 'bootstrap'}\n"
628
+ "CHANGE_BUDGET=small\n",
629
+ )
630
+ _write_if_missing(
631
+ checklist_path,
632
+ "- [ ] define goal\n- [ ] run verification\n- [ ] ship with evidence\n",
633
+ )
634
+ if not os.path.exists(qg_path):
635
+ with open(qg_path, "w", encoding="utf-8") as f:
636
+ json.dump({"lint": "pytest -q", "test": "pytest -q"}, f, indent=2, ensure_ascii=True)
637
+ return [
638
+ os.path.relpath(profile_path, project_dir),
639
+ os.path.relpath(idea_path, project_dir),
640
+ os.path.relpath(policy_path, project_dir),
641
+ os.path.relpath(runtime_path, project_dir),
642
+ os.path.relpath(plan_path, project_dir),
643
+ os.path.relpath(checklist_path, project_dir),
644
+ os.path.relpath(qg_path, project_dir),
645
+ ]
646
+
647
+
648
+ def _health_snapshot(project_dir: str) -> dict[str, Any]:
649
+ p = Path(project_dir)
650
+ omg_root = p / ".omg"
651
+ checks = [
652
+ {"name": "python>=3.8", "ok": sys.version_info >= (3, 8)},
653
+ {"name": ".omg exists", "ok": omg_root.exists()},
654
+ {"name": ".omg/state exists", "ok": (omg_root / "state").exists()},
655
+ {"name": ".omg/idea.yml exists", "ok": (omg_root / "idea.yml").exists()},
656
+ {"name": ".omg/policy.yaml exists", "ok": (omg_root / "policy.yaml").exists()},
657
+ ]
658
+ all_ok = all(c["ok"] for c in checks)
659
+ return {
660
+ "project_dir": str(p),
661
+ "status": "pass" if all_ok else "warn",
662
+ "checks": checks,
663
+ "omg_exists": omg_root.exists(),
664
+ "state_exists": (omg_root / "state").exists(),
665
+ "knowledge_exists": (omg_root / "knowledge").exists(),
666
+ "evidence_exists": (omg_root / "evidence").exists(),
667
+ }
668
+
669
+
670
+ def _write_release_artifact(project_dir: str, message: str) -> str:
671
+ _ensure_state_layout(project_dir)
672
+ out = os.path.join(project_dir, ".omg", "evidence", "release-draft.md")
673
+ if not os.path.exists(out):
674
+ with open(out, "w", encoding="utf-8") as f:
675
+ f.write("# Release Draft\n\n")
676
+ with open(out, "a", encoding="utf-8") as f:
677
+ f.write(f"- {_now()}: {message}\n")
678
+ return out
679
+
680
+
681
+ def _write_build_fix_artifact(project_dir: str, message: str) -> str:
682
+ _ensure_state_layout(project_dir)
683
+ out = os.path.join(project_dir, ".omg", "state", "build-fix.md")
684
+ with open(out, "a", encoding="utf-8") as f:
685
+ f.write(f"## {_now()}\n")
686
+ f.write(f"- target: {message}\n")
687
+ f.write("- checklist:\n")
688
+ f.write(" - reproduce failure\n")
689
+ f.write(" - implement minimal fix\n")
690
+ f.write(" - run focused tests\n")
691
+ f.write(" - run full regression\n\n")
692
+ return out
693
+
694
+
695
+ def _write_persistent_state(
696
+ project_dir: str,
697
+ *,
698
+ mode: str,
699
+ goal: str,
700
+ context: str,
701
+ expected_outcome: str,
702
+ runtime_result: dict[str, Any],
703
+ ) -> str:
704
+ _ensure_state_layout(project_dir)
705
+ path = os.path.join(project_dir, ".omg", "state", "persistent-mode.json")
706
+ payload: dict[str, Any] = {
707
+ "schema": "PersistentModeState",
708
+ "mode": mode,
709
+ "status": "active",
710
+ "goal": goal,
711
+ "context": context,
712
+ "expected_outcome": expected_outcome,
713
+ "started_at": _now(),
714
+ "last_updated": _now(),
715
+ "last_runtime_status": runtime_result.get("status", "unknown"),
716
+ "history": [],
717
+ }
718
+ if os.path.exists(path):
719
+ try:
720
+ with open(path, "r", encoding="utf-8") as f:
721
+ current = json.load(f)
722
+ if isinstance(current, dict):
723
+ payload.update(current)
724
+ except Exception:
725
+ pass
726
+ payload.setdefault("history", [])
727
+ if not isinstance(payload["history"], list):
728
+ payload["history"] = []
729
+ payload["mode"] = mode
730
+ payload["status"] = "active"
731
+ payload["goal"] = goal
732
+ payload["context"] = context
733
+ payload["expected_outcome"] = expected_outcome
734
+ payload["last_updated"] = _now()
735
+ payload["last_runtime_status"] = runtime_result.get("status", "unknown")
736
+ payload["history"].append(
737
+ {
738
+ "ts": _now(),
739
+ "event": "dispatch",
740
+ "goal": goal,
741
+ "runtime_status": runtime_result.get("status", "unknown"),
742
+ }
743
+ )
744
+ payload["history"] = payload["history"][-200:]
745
+ with open(path, "w", encoding="utf-8") as f:
746
+ json.dump(payload, f, indent=2, ensure_ascii=True)
747
+ return path
748
+
749
+
750
+ def _run_dual_review(
751
+ problem: str,
752
+ context: str,
753
+ files: list[str],
754
+ expected_outcome: str,
755
+ ) -> dict[str, Any]:
756
+ codex_req = TeamDispatchRequest(
757
+ target="codex",
758
+ problem=f"review: {problem}",
759
+ context=context,
760
+ files=files,
761
+ expected_outcome=expected_outcome,
762
+ )
763
+ ccg_req = TeamDispatchRequest(
764
+ target="ccg",
765
+ problem=f"cross-check: {problem}",
766
+ context=context,
767
+ files=files,
768
+ expected_outcome=expected_outcome,
769
+ )
770
+ codex = dispatch_team(codex_req).to_dict()
771
+ ccg = dispatch_team(ccg_req).to_dict()
772
+ merged_actions: list[str] = []
773
+ seen = set()
774
+ for source in (codex.get("actions", []), ccg.get("actions", [])):
775
+ for action in source:
776
+ if action not in seen:
777
+ seen.add(action)
778
+ merged_actions.append(action)
779
+ synthesis = {
780
+ "schema": "ReviewSynthesis",
781
+ "status": "ok",
782
+ "tracks": {"codex": codex, "ccg": ccg},
783
+ "summary": [
784
+ "Dual-track review executed (codex + ccg).",
785
+ f"Merged action count: {len(merged_actions)}",
786
+ ],
787
+ "actions": merged_actions,
788
+ }
789
+ return synthesis
790
+
791
+
792
+ def _ensure_plan_artifacts(project_dir: str, goal: str) -> list[str]:
793
+ _ensure_state_layout(project_dir)
794
+ plan_path = os.path.join(project_dir, ".omg", "state", "_plan.md")
795
+ checklist_path = os.path.join(project_dir, ".omg", "state", "_checklist.md")
796
+ idea_path = os.path.join(project_dir, ".omg", "idea.yml")
797
+ _write_if_missing(
798
+ plan_path,
799
+ "# Deep Plan\n"
800
+ f"goal: {goal or 'compat planning'}\n"
801
+ "CHANGE_BUDGET=small\n"
802
+ "phases:\n"
803
+ "- foundation\n- implementation\n- verification\n",
804
+ )
805
+ _write_if_missing(
806
+ checklist_path,
807
+ "- [ ] write failing test\n- [ ] implement minimal fix\n- [ ] run tests\n",
808
+ )
809
+ _write_if_missing(
810
+ idea_path,
811
+ "goal: \"compat-plan\"\n"
812
+ "constraints: []\n"
813
+ "acceptance: []\n"
814
+ "risk:\n"
815
+ " security: []\n"
816
+ " performance: []\n"
817
+ " compatibility: []\n"
818
+ "evidence_required:\n"
819
+ " tests: []\n"
820
+ " security_scans: []\n"
821
+ " reproducibility: []\n"
822
+ " artifacts: []\n",
823
+ )
824
+ return [
825
+ os.path.relpath(plan_path, project_dir),
826
+ os.path.relpath(checklist_path, project_dir),
827
+ os.path.relpath(idea_path, project_dir),
828
+ ]
829
+
830
+
831
+ def _ensure_tdd_artifacts(project_dir: str, goal: str) -> list[str]:
832
+ _ensure_state_layout(project_dir)
833
+ plan_path = os.path.join(project_dir, ".omg", "state", "_plan.md")
834
+ checklist_path = os.path.join(project_dir, ".omg", "state", "_checklist.md")
835
+ idea_path = os.path.join(project_dir, ".omg", "idea.yml")
836
+ with open(plan_path, "w", encoding="utf-8") as f:
837
+ f.write(
838
+ "# TDD Plan\n"
839
+ f"goal: {goal or 'tdd workflow'}\n"
840
+ "CHANGE_BUDGET=small\n"
841
+ "workflow:\n"
842
+ "- red: write failing test\n"
843
+ "- green: minimal implementation\n"
844
+ "- refactor: clean while tests stay green\n"
845
+ )
846
+ with open(checklist_path, "w", encoding="utf-8") as f:
847
+ f.write(
848
+ "- [ ] red: create failing test for target behavior\n"
849
+ "- [ ] red: run targeted test and confirm failure reason\n"
850
+ "- [ ] green: write minimal code to pass test\n"
851
+ "- [ ] green: re-run targeted test and confirm pass\n"
852
+ "- [ ] refactor: clean implementation without behavior change\n"
853
+ "- [ ] verify: run full test suite\n"
854
+ )
855
+ _write_if_missing(
856
+ idea_path,
857
+ "goal: \"tdd\"\n"
858
+ "constraints: []\n"
859
+ "acceptance: []\n"
860
+ "risk:\n"
861
+ " security: []\n"
862
+ " performance: []\n"
863
+ " compatibility: []\n"
864
+ "evidence_required:\n"
865
+ " tests: []\n"
866
+ " security_scans: []\n"
867
+ " reproducibility: []\n"
868
+ " artifacts: []\n",
869
+ )
870
+ return [
871
+ os.path.relpath(plan_path, project_dir),
872
+ os.path.relpath(checklist_path, project_dir),
873
+ os.path.relpath(idea_path, project_dir),
874
+ ]
875
+
876
+
877
+ def dispatch_compat_skill(
878
+ *,
879
+ skill: str,
880
+ problem: str = "",
881
+ context: str = "",
882
+ files: list[str] | None = None,
883
+ expected_outcome: str = "",
884
+ project_dir: str | None = None,
885
+ ) -> dict[str, Any]:
886
+ normalized = skill.strip()
887
+ root = _project_dir(project_dir)
888
+
889
+ def _emit(payload: dict[str, Any]) -> dict[str, Any]:
890
+ _append_audit_event(
891
+ root,
892
+ {
893
+ "event": DEFAULT_EVENT_DISPATCH,
894
+ "skill": normalized,
895
+ "route": payload.get("route", ""),
896
+ "status": payload.get("status", "unknown"),
897
+ "routed_to": payload.get("routed_to", ""),
898
+ "problem_chars": len(problem),
899
+ "context_chars": len(context),
900
+ "file_count": len(files or []),
901
+ },
902
+ )
903
+ return payload
904
+
905
+ def _res(**kwargs: Any) -> dict[str, Any]:
906
+ return _emit(_result(**kwargs))
907
+
908
+ if not normalized:
909
+ return _res(
910
+ skill=skill,
911
+ route="unknown",
912
+ status="error",
913
+ findings=["Missing skill name."],
914
+ actions=["Provide --skill value."],
915
+ )
916
+
917
+ route = LEGACY_SKILL_ROUTES.get(normalized)
918
+ if route is None:
919
+ return _res(
920
+ skill=normalized,
921
+ route="unknown",
922
+ status="error",
923
+ findings=[f"Unsupported skill: {normalized}"],
924
+ actions=["Use `omg compat list` to see supported skill names."],
925
+ )
926
+
927
+ is_valid, reason = validate_compat_request(
928
+ skill=normalized,
929
+ problem=problem,
930
+ context=context,
931
+ files=files,
932
+ expected_outcome=expected_outcome,
933
+ )
934
+ if not is_valid:
935
+ return _res(
936
+ skill=normalized,
937
+ route=route,
938
+ status="error",
939
+ findings=[f"Invalid request: {reason}"],
940
+ actions=["Adjust inputs and retry."],
941
+ )
942
+
943
+ _append_audit_event(
944
+ root,
945
+ {
946
+ "event": DEFAULT_EVENT_REQUEST,
947
+ "skill": normalized,
948
+ "route": route,
949
+ "problem_chars": len(problem),
950
+ "context_chars": len(context),
951
+ "file_count": len(files or []),
952
+ },
953
+ )
954
+
955
+ msg = problem or f"compat dispatch via {normalized}"
956
+ file_list = files or []
957
+
958
+ if route == "teams":
959
+ req = TeamDispatchRequest(
960
+ target="auto",
961
+ problem=msg,
962
+ context=context,
963
+ files=file_list,
964
+ expected_outcome=expected_outcome,
965
+ )
966
+ team = dispatch_team(req).to_dict()
967
+ return _res(
968
+ skill=normalized,
969
+ route=route,
970
+ routed_to=str(team.get("evidence", {}).get("target", "")),
971
+ findings=["Team route dispatched."],
972
+ actions=["Review findings and apply selected actions."],
973
+ result=team,
974
+ )
975
+
976
+ if route == "ccg":
977
+ req = TeamDispatchRequest(
978
+ target="ccg",
979
+ problem=msg,
980
+ context=context,
981
+ files=file_list,
982
+ expected_outcome=expected_outcome,
983
+ )
984
+ ccg = dispatch_team(req).to_dict()
985
+ return _res(
986
+ skill=normalized,
987
+ route=route,
988
+ routed_to="ccg",
989
+ findings=["CCG route dispatched."],
990
+ actions=["Review merged action plan."],
991
+ result=ccg,
992
+ )
993
+
994
+ if route == "runtime_ship":
995
+ runtime = dispatch_runtime(
996
+ "claude",
997
+ {"goal": msg, "constraints": [], "acceptance": [expected_outcome] if expected_outcome else []},
998
+ )
999
+ status = "ok" if runtime.get("status") == "ok" else "error"
1000
+ artifacts: list[str] = []
1001
+ if normalized in {"autopilot", "ralph", "ultrapilot", "ultrawork"}:
1002
+ persistent = _write_persistent_state(
1003
+ root,
1004
+ mode=normalized,
1005
+ goal=msg,
1006
+ context=context,
1007
+ expected_outcome=expected_outcome,
1008
+ runtime_result=runtime,
1009
+ )
1010
+ artifacts.append(os.path.relpath(persistent, root))
1011
+ if normalized == "release" and status == "ok":
1012
+ rel = _write_release_artifact(root, msg)
1013
+ artifacts.append(os.path.relpath(rel, root))
1014
+ if normalized == "build-fix" and status == "ok":
1015
+ build_fix = _write_build_fix_artifact(root, msg)
1016
+ artifacts.append(os.path.relpath(build_fix, root))
1017
+ return _res(
1018
+ skill=normalized,
1019
+ route=route,
1020
+ status=status,
1021
+ routed_to="claude",
1022
+ findings=["Runtime dispatch completed." if status == "ok" else "Runtime dispatch failed."],
1023
+ actions=[
1024
+ "Inspect runtime response and continue.",
1025
+ "If persistent mode is active, keep iterating until checklist completion.",
1026
+ ],
1027
+ result=runtime,
1028
+ artifacts=artifacts,
1029
+ )
1030
+
1031
+ if route == "pipeline":
1032
+ pipeline = run_pipeline(
1033
+ {
1034
+ "dataset": {"source": "clean-source", "license": "mit"},
1035
+ "base_model": {"source": "open-model", "allow_distill": True},
1036
+ "target_metric": 0.7,
1037
+ "simulated_metric": 0.8,
1038
+ "evaluation_notes": f"compat:{normalized}",
1039
+ }
1040
+ )
1041
+ status = "ok" if pipeline.get("status") in {"ready", "published"} else "error"
1042
+ return _res(
1043
+ skill=normalized,
1044
+ route=route,
1045
+ status=status,
1046
+ findings=["Pipeline route executed."],
1047
+ actions=["Use `omg lab eval` when evaluation is ready."],
1048
+ result=pipeline,
1049
+ )
1050
+
1051
+ if route == "memory":
1052
+ if normalized == "project-session-manager":
1053
+ session = _update_session_state(root, msg)
1054
+ return _res(
1055
+ skill=normalized,
1056
+ route=route,
1057
+ findings=["Session state updated."],
1058
+ actions=["Use session state to continue long-running work."],
1059
+ artifacts=[os.path.relpath(session, root)],
1060
+ )
1061
+ if normalized == "writer-memory":
1062
+ writer = _append_knowledge_note(
1063
+ root,
1064
+ "knowledge/writer-memory.md",
1065
+ f"- [{_now()}] {msg}",
1066
+ )
1067
+ return _res(
1068
+ skill=normalized,
1069
+ route=route,
1070
+ findings=["Writer memory updated."],
1071
+ actions=["Reuse writer-memory notes for long-form drafting."],
1072
+ artifacts=[os.path.relpath(writer, root)],
1073
+ )
1074
+ if normalized == "note":
1075
+ note = _append_knowledge_note(
1076
+ root,
1077
+ "knowledge/notes.md",
1078
+ f"- [{_now()}] {msg}",
1079
+ )
1080
+ return _res(
1081
+ skill=normalized,
1082
+ route=route,
1083
+ findings=["Note appended to knowledge log."],
1084
+ actions=["Review notes during planning and handoff."],
1085
+ artifacts=[os.path.relpath(note, root)],
1086
+ )
1087
+ wm_path = _append_memory(root, msg)
1088
+ return _res(
1089
+ skill=normalized,
1090
+ route=route,
1091
+ findings=["Working memory updated."],
1092
+ actions=["Continue work with refreshed context."],
1093
+ artifacts=[os.path.relpath(wm_path, root)],
1094
+ )
1095
+
1096
+ if route == "init":
1097
+ artifacts = _init_bootstrap(root, msg)
1098
+ return _res(
1099
+ skill=normalized,
1100
+ route=route,
1101
+ findings=["OMG layout initialized."],
1102
+ actions=["Run `omg compat run --skill omg-doctor` to verify health."],
1103
+ artifacts=artifacts,
1104
+ )
1105
+
1106
+ if route == "health":
1107
+ snapshot = _health_snapshot(root)
1108
+ return _res(
1109
+ skill=normalized,
1110
+ route=route,
1111
+ findings=["Health snapshot generated."],
1112
+ actions=["Create missing .omg folders if any field is false."],
1113
+ result=snapshot,
1114
+ )
1115
+
1116
+ if route == "help":
1117
+ return _res(
1118
+ skill=normalized,
1119
+ route=route,
1120
+ findings=["Compatibility help generated."],
1121
+ actions=["Run `omg compat list`, `omg compat contract --all`, then `omg compat run --skill <name>`."],
1122
+ result={
1123
+ "supported_skills": list_compat_skills(),
1124
+ "gap_report_hint": DEFAULT_GAP_REPORT_PATH,
1125
+ },
1126
+ )
1127
+
1128
+ if route == "review":
1129
+ if normalized in {"review", "code-review", "ultraqa"}:
1130
+ review = _run_dual_review(msg, context, file_list, expected_outcome)
1131
+ routed_to = "codex+ccg"
1132
+ findings = ["Dual-track review route dispatched."]
1133
+ else:
1134
+ req = TeamDispatchRequest(
1135
+ target="codex",
1136
+ problem=f"review: {msg}",
1137
+ context=context,
1138
+ files=file_list,
1139
+ expected_outcome=expected_outcome,
1140
+ )
1141
+ review = dispatch_team(req).to_dict()
1142
+ routed_to = "codex"
1143
+ findings = ["Review route dispatched."]
1144
+ return _res(
1145
+ skill=normalized,
1146
+ route=route,
1147
+ routed_to=routed_to,
1148
+ findings=findings,
1149
+ actions=["Address high-risk findings first."],
1150
+ result=review,
1151
+ )
1152
+
1153
+ if route == "plan":
1154
+ if normalized == "tdd":
1155
+ artifacts = _ensure_tdd_artifacts(root, msg)
1156
+ findings = ["TDD artifacts are ready (red-green-refactor)."]
1157
+ actions = ["Execute checklist in strict red -> green -> refactor order."]
1158
+ else:
1159
+ artifacts = _ensure_plan_artifacts(root, msg)
1160
+ findings = ["Plan artifacts are ready."]
1161
+ actions = ["Refine _plan/_checklist then execute with evidence."]
1162
+ return _res(
1163
+ skill=normalized,
1164
+ route=route,
1165
+ findings=findings,
1166
+ actions=actions,
1167
+ artifacts=artifacts,
1168
+ )
1169
+
1170
+ if route == "secure":
1171
+ decision = evaluate_bash_command(problem or "echo safe")
1172
+ return _res(
1173
+ skill=normalized,
1174
+ route=route,
1175
+ findings=["Security policy evaluation completed."],
1176
+ actions=["If action is deny, revise command and retry."],
1177
+ result=decision.to_dict(),
1178
+ )
1179
+
1180
+ if route == "learn":
1181
+ if normalized in {"learn-about-omg", "learner", "skill"}:
1182
+ learn_path = _write_learning_artifact(root, normalized, msg, context)
1183
+ return _res(
1184
+ skill=normalized,
1185
+ route=route,
1186
+ findings=["Learning artifact recorded."],
1187
+ actions=["Promote stable patterns from learning artifacts into reusable commands/skills."],
1188
+ artifacts=[os.path.relpath(learn_path, root)],
1189
+ )
1190
+ note_path = _append_memory(root, f"learn: {msg}")
1191
+ return _res(
1192
+ skill=normalized,
1193
+ route=route,
1194
+ findings=["Learning note recorded."],
1195
+ actions=["Review .omg/state/working-memory.md for accumulated insights."],
1196
+ artifacts=[os.path.relpath(note_path, root)],
1197
+ )
1198
+
1199
+ if route == "maintainer":
1200
+ if normalized in {"analyze", "trace", "sci-omg"}:
1201
+ artifact = _write_analysis_artifact(root, normalized, msg, context, file_list)
1202
+ return _res(
1203
+ skill=normalized,
1204
+ route=route,
1205
+ findings=["Analysis artifact generated."],
1206
+ actions=["Use findings to create targeted fix/review tasks."],
1207
+ artifacts=[os.path.relpath(artifact, root)],
1208
+ )
1209
+ artifact = _write_maintainer_artifact(root, normalized, msg)
1210
+ return _res(
1211
+ skill=normalized,
1212
+ route=route,
1213
+ findings=["Maintainer artifact generated."],
1214
+ actions=["Attach artifact to maintainer workflow or release notes."],
1215
+ artifacts=[os.path.relpath(artifact, root)],
1216
+ )
1217
+
1218
+ if route == "cancel":
1219
+ active_path = os.path.join(root, ".omg", "shadow", "active-run")
1220
+ if os.path.exists(active_path):
1221
+ os.remove(active_path)
1222
+ persistent_path = os.path.join(root, ".omg", "state", "persistent-mode.json")
1223
+ if os.path.exists(persistent_path):
1224
+ try:
1225
+ with open(persistent_path, "r", encoding="utf-8") as f:
1226
+ persistent = json.load(f)
1227
+ if isinstance(persistent, dict):
1228
+ persistent["status"] = "cancelled"
1229
+ persistent["last_updated"] = _now()
1230
+ with open(persistent_path, "w", encoding="utf-8") as f:
1231
+ json.dump(persistent, f, indent=2, ensure_ascii=True)
1232
+ except Exception:
1233
+ pass
1234
+ return _res(
1235
+ skill=normalized,
1236
+ route=route,
1237
+ findings=["Active run state cleared."],
1238
+ actions=["Start a new run when ready."],
1239
+ )
1240
+
1241
+ return _res(
1242
+ skill=normalized,
1243
+ route=route,
1244
+ status="error",
1245
+ findings=["Route exists but has no handler."],
1246
+ actions=["Implement missing handler."],
1247
+ )
1248
+
1249
+
1250
+ def list_omg_skills() -> list[str]:
1251
+ return list_compat_skills()
1252
+
1253
+
1254
+ def list_omg_skill_contracts() -> list[dict[str, Any]]:
1255
+ return list_compat_skill_contracts()
1256
+
1257
+
1258
+ def get_omg_skill_contract(skill: str) -> dict[str, Any] | None:
1259
+ return get_compat_skill_contract(skill)
1260
+
1261
+
1262
+ def build_omg_gap_report(project_dir: str | None = None) -> dict[str, Any]:
1263
+ return build_compat_gap_report(project_dir)
1264
+
1265
+
1266
+ def validate_omg_request(
1267
+ *,
1268
+ skill: str,
1269
+ problem: str,
1270
+ context: str,
1271
+ files: list[str] | None,
1272
+ expected_outcome: str,
1273
+ ) -> tuple[bool, str]:
1274
+ return validate_compat_request(
1275
+ skill=skill,
1276
+ problem=problem,
1277
+ context=context,
1278
+ files=files,
1279
+ expected_outcome=expected_outcome,
1280
+ )
1281
+
1282
+
1283
+ def dispatch_omg_skill(
1284
+ *,
1285
+ skill: str,
1286
+ problem: str = "",
1287
+ context: str = "",
1288
+ files: list[str] | None = None,
1289
+ expected_outcome: str = "",
1290
+ project_dir: str | None = None,
1291
+ ) -> dict[str, Any]:
1292
+ return dispatch_compat_skill(
1293
+ skill=skill,
1294
+ problem=problem,
1295
+ context=context,
1296
+ files=files,
1297
+ expected_outcome=expected_outcome,
1298
+ project_dir=project_dir,
1299
+ )