@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,283 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ settings-merge.py
4
+ Merges v2 hooks + permissions INTO existing ~/.claude/settings.json
5
+ without destroying existing non-OMG configuration.
6
+
7
+ Strategy:
8
+ - hooks{}: APPEND our matchers to each event (never replace existing)
9
+ - permissions.allow[]: UNION (add ours, keep theirs)
10
+ - permissions.deny[]: UNION
11
+ - permissions.ask[]: UNION
12
+ - Everything else: PRESERVE as-is
13
+ """
14
+ import json
15
+ import sys
16
+ import os
17
+ import re
18
+ import shutil
19
+ from datetime import datetime
20
+
21
+ def load_json(path):
22
+ if not os.path.exists(path):
23
+ return {}
24
+ try:
25
+ with open(path, "r") as f:
26
+ return json.load(f)
27
+ except json.JSONDecodeError as e:
28
+ print(f"ERROR: {path} contains invalid JSON (line {e.lineno}, col {e.colno}):", file=sys.stderr)
29
+ print(f" {e.msg}", file=sys.stderr)
30
+ print(f" Fix the file manually or delete it to start fresh.", file=sys.stderr)
31
+ sys.exit(1)
32
+ except PermissionError:
33
+ print(f"ERROR: Cannot read {path} — permission denied.", file=sys.stderr)
34
+ sys.exit(1)
35
+ except Exception as e:
36
+ print(f"ERROR: Failed to read {path}: {e}", file=sys.stderr)
37
+ sys.exit(1)
38
+
39
+ def merge_hooks(existing_hooks, new_hooks):
40
+ """Append new hook matchers to existing, avoiding duplicates."""
41
+ merged = dict(existing_hooks) # shallow copy
42
+
43
+ def _normalize_command_name(name):
44
+ return re.sub(r"[^a-z0-9]+", "", name.lower())
45
+
46
+ def _matcher_identity(entry):
47
+ if not isinstance(entry, dict):
48
+ return None
49
+ matcher = entry.get("matcher")
50
+ return None if matcher in ("", None) else matcher
51
+
52
+ def _command_identity(command):
53
+ if not command:
54
+ return ""
55
+ tokens = re.findall(r'"[^"]*"|\'[^\']*\'|\S+', command)
56
+ if not tokens:
57
+ return ""
58
+ candidate = tokens[-1].strip("\"'")
59
+ base = os.path.basename(candidate)
60
+ stem, _ = os.path.splitext(base)
61
+ normalized = _normalize_command_name(stem or base)
62
+ return normalized or stem or base
63
+
64
+ def extract_commands(entry):
65
+ commands = set()
66
+ if not isinstance(entry, dict):
67
+ return commands
68
+ hooks = entry.get("hooks")
69
+ if isinstance(hooks, list):
70
+ for hook in hooks:
71
+ if not isinstance(hook, dict):
72
+ continue
73
+ cmd = hook.get("command", "") or hook.get("prompt", "")
74
+ if cmd:
75
+ commands.add(_command_identity(cmd))
76
+ return commands
77
+ cmd = entry.get("command", "") or entry.get("prompt", "")
78
+ if cmd:
79
+ commands.add(_command_identity(cmd))
80
+ return commands
81
+
82
+ for event, matchers in new_hooks.items():
83
+ if event not in merged:
84
+ merged[event] = matchers
85
+ continue
86
+
87
+ existing_matchers = merged[event]
88
+
89
+ for new_matcher in matchers:
90
+ new_cmds = extract_commands(new_matcher)
91
+
92
+ overlap_indices = []
93
+ already_exists = False
94
+ for idx, em in enumerate(existing_matchers):
95
+ existing_cmds = extract_commands(em)
96
+ same_matcher = _matcher_identity(em) == _matcher_identity(new_matcher)
97
+ if new_cmds and same_matcher and (new_cmds & existing_cmds):
98
+ overlap_indices.append(idx)
99
+ continue
100
+ if not new_cmds and em == new_matcher:
101
+ already_exists = True
102
+ break
103
+
104
+ if overlap_indices:
105
+ first = overlap_indices[0]
106
+ existing_matchers[first] = new_matcher
107
+ for idx in reversed(overlap_indices[1:]):
108
+ existing_matchers.pop(idx)
109
+ continue
110
+
111
+ if not already_exists:
112
+ existing_matchers.append(new_matcher)
113
+
114
+ return merged
115
+
116
+ def merge_permission_list(existing, new):
117
+ """Union two permission lists, preserving order (existing first)."""
118
+ seen = set(existing)
119
+ merged = list(existing)
120
+ for item in new:
121
+ if item not in seen:
122
+ merged.append(item)
123
+ seen.add(item)
124
+ return merged
125
+
126
+
127
+ def merge_mcp_servers(existing, new):
128
+ merged = dict(existing or {})
129
+ for name, config in (new or {}).items():
130
+ if name not in merged:
131
+ merged[name] = config
132
+ continue
133
+ existing_cfg = merged.get(name)
134
+ if isinstance(existing_cfg, dict) and isinstance(config, dict):
135
+ merged_cfg = dict(existing_cfg)
136
+ if "args" in config:
137
+ existing_args = merged_cfg.get("args", []) if isinstance(merged_cfg.get("args", []), list) else []
138
+ new_args = config.get("args", []) if isinstance(config.get("args", []), list) else []
139
+ merged_args = list(existing_args)
140
+ for arg in new_args:
141
+ if arg not in merged_args:
142
+ merged_args.append(arg)
143
+ merged_cfg["args"] = merged_args
144
+ if "env" in config and isinstance(config.get("env"), dict):
145
+ env = dict(merged_cfg.get("env", {})) if isinstance(merged_cfg.get("env"), dict) else {}
146
+ for key, value in config["env"].items():
147
+ env.setdefault(key, value)
148
+ merged_cfg["env"] = env
149
+ for key, value in config.items():
150
+ merged_cfg.setdefault(key, value)
151
+ merged[name] = merged_cfg
152
+ return merged
153
+
154
+
155
+ def merge_omg_settings(existing, new):
156
+ """Merge the OMG namespace while preserving explicit user feature overrides."""
157
+ merged = dict(existing or {})
158
+ incoming = dict(new or {})
159
+
160
+ for key, value in incoming.items():
161
+ if key == "features":
162
+ continue
163
+ if key == "_version":
164
+ merged[key] = value
165
+ continue
166
+ if key not in merged:
167
+ merged[key] = value
168
+
169
+ existing_features = existing.get("features", {}) if isinstance(existing, dict) else {}
170
+ incoming_features = incoming.get("features", {}) if isinstance(incoming, dict) else {}
171
+ if isinstance(existing_features, dict) or isinstance(incoming_features, dict):
172
+ merged_features = dict(incoming_features) if isinstance(incoming_features, dict) else {}
173
+ if isinstance(existing_features, dict):
174
+ merged_features.update(existing_features)
175
+ merged["features"] = merged_features
176
+
177
+ return merged
178
+
179
+ def merge_settings(existing, new):
180
+ """
181
+ Merge strategy:
182
+ - hooks: append per-event
183
+ - permissions: union per-category
184
+ - everything else in existing: preserve
185
+ - $schema: use new if not present
186
+ """
187
+ merged = dict(existing)
188
+
189
+ # Schema
190
+ if "$schema" not in merged and "$schema" in new:
191
+ merged["$schema"] = new["$schema"]
192
+
193
+ # Hooks
194
+ if "hooks" in new:
195
+ existing_hooks = merged.get("hooks", {})
196
+ merged["hooks"] = merge_hooks(existing_hooks, new["hooks"])
197
+
198
+ # Permissions
199
+ if "permissions" in new:
200
+ existing_perms = merged.get("permissions", {})
201
+ new_perms = new["permissions"]
202
+ merged_perms = dict(existing_perms)
203
+ managed_rules = set()
204
+ for category in ("allow", "deny", "ask"):
205
+ managed_rules.update(new_perms.get(category, []))
206
+
207
+ for category in ("allow", "deny", "ask"):
208
+ existing_list = existing_perms.get(category, [])
209
+ filtered_existing = [item for item in existing_list if item not in managed_rules]
210
+ new_list = new_perms.get(category, [])
211
+ merged_perms[category] = merge_permission_list(filtered_existing, new_list)
212
+
213
+ merged["permissions"] = merged_perms
214
+
215
+ if "mcpServers" in new:
216
+ merged["mcpServers"] = merge_mcp_servers(merged.get("mcpServers", {}), new.get("mcpServers", {}))
217
+
218
+ if "_omg" in new:
219
+ merged["_omg"] = merge_omg_settings(merged.get("_omg", {}), new.get("_omg", {}))
220
+
221
+ return merged
222
+
223
+ def main():
224
+ if len(sys.argv) < 3:
225
+ print("Usage: settings-merge.py <existing.json> <new.json> [--dry-run]")
226
+ sys.exit(1)
227
+
228
+ existing_path = sys.argv[1]
229
+ new_path = sys.argv[2]
230
+ dry_run = "--dry-run" in sys.argv
231
+
232
+ existing = load_json(existing_path)
233
+ new = load_json(new_path)
234
+
235
+ merged = merge_settings(existing, new)
236
+
237
+ if dry_run:
238
+ print(json.dumps(merged, indent=2))
239
+ print(f"\n--- DRY RUN ---", file=sys.stderr)
240
+ # Show what was added
241
+ new_hooks = set()
242
+ for event, matchers in new.get("hooks", {}).items():
243
+ for m in matchers:
244
+ for h in m.get("hooks", []):
245
+ cmd = h.get("command", "")
246
+ if cmd:
247
+ new_hooks.add(f" {event}: {os.path.basename(cmd.split()[-1])}")
248
+ if new_hooks:
249
+ print(f"Hooks to add:", file=sys.stderr)
250
+ for h in sorted(new_hooks):
251
+ print(h, file=sys.stderr)
252
+
253
+ for cat in ("allow", "deny", "ask"):
254
+ existing_set = set(existing.get("permissions", {}).get(cat, []))
255
+ new_set = set(new.get("permissions", {}).get(cat, []))
256
+ added = new_set - existing_set
257
+ if added:
258
+ print(f"permissions.{cat} to add: {len(added)} rules", file=sys.stderr)
259
+ return
260
+
261
+ # Backup existing
262
+ if os.path.exists(existing_path):
263
+ ts = datetime.now().strftime("%Y%m%d_%H%M%S")
264
+ backup = f"{existing_path}.bak.{ts}"
265
+ shutil.copy2(existing_path, backup)
266
+ print(f"📦 Backed up: {backup}")
267
+
268
+ with open(existing_path, "w") as f:
269
+ json.dump(merged, f, indent=2)
270
+ f.write("\n")
271
+
272
+ print(f"✅ Merged into: {existing_path}")
273
+
274
+ # Summary
275
+ hook_events = list(merged.get("hooks", {}).keys())
276
+ allow_count = len(merged.get("permissions", {}).get("allow", []))
277
+ deny_count = len(merged.get("permissions", {}).get("deny", []))
278
+ ask_count = len(merged.get("permissions", {}).get("ask", []))
279
+ print(f" Hooks: {', '.join(hook_events)}")
280
+ print(f" Permissions: {allow_count} allow, {deny_count} deny, {ask_count} ask")
281
+
282
+ if __name__ == "__main__":
283
+ main()
@@ -7,11 +7,15 @@ TMP_DIR="${1:-$(mktemp -d)}"
7
7
  echo "[verify-standalone] source: $ROOT_DIR"
8
8
  echo "[verify-standalone] workdir: $TMP_DIR"
9
9
 
10
- tar --exclude="./.omc" --exclude="./node_modules" -cf - -C "$ROOT_DIR" . | (cd "$TMP_DIR" && tar -xf -)
10
+ tar --exclude="./.omc" --exclude="./.pytest_cache" -cf - -C "$ROOT_DIR" . | (cd "$TMP_DIR" && tar -xf -)
11
11
 
12
12
  cd "$TMP_DIR"
13
- bun install --ignore-scripts
14
- bun scripts/omg.ts compat gate --max-bridge 0 --output .omg/evidence/omg-compat-gap.json
15
- bun test
13
+ python3 scripts/omg.py compat gate --max-bridge 0 --output .omg/evidence/omg-compat-gap.json
14
+
15
+ if command -v pyenv >/dev/null 2>&1 && pyenv prefix 3.12.7 >/dev/null 2>&1; then
16
+ PYENV_VERSION=3.12.7 python -m pytest -q
17
+ else
18
+ python3 -m pytest -q
19
+ fi
16
20
 
17
21
  echo "[verify-standalone] passed"
package/settings.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
- "_comment": "OMG v2.0.0 Bun runtime hook registrations and feature flags.",
3
+ "_comment": "OMG 2.0.2 - project-level config with hook registrations, presets, and feature flags.",
4
4
  "permissions": {
5
5
  "allow": [
6
6
  "Agent",
@@ -60,38 +60,30 @@
60
60
  "Bash(yarn *)",
61
61
  "Bash(pnpm *)",
62
62
  "Bash(bun *)",
63
- "Bash(bunx *)",
64
63
  "Bash(node *)",
64
+ "Bash(python *)",
65
+ "Bash(python3 *)",
66
+ "Bash(pip *)",
67
+ "Bash(pip3 *)",
68
+ "Bash(uv *)",
69
+ "Bash(go *)",
70
+ "Bash(cargo *)",
71
+ "Bash(rustc *)",
65
72
  "Bash(tsc *)",
66
73
  "Bash(eslint *)",
67
74
  "Bash(prettier *)",
75
+ "Bash(mypy *)",
76
+ "Bash(ruff *)",
77
+ "Bash(ruff format *)",
78
+ "Bash(pytest *)",
68
79
  "Bash(jest *)",
69
80
  "Bash(vitest *)",
70
81
  "Bash(shellcheck *)",
71
- "Bash(docker *)",
72
- "Bash(docker-compose *)",
73
- "Bash(kubectl get *)",
74
- "Bash(kubectl describe *)",
75
- "Bash(kubectl logs *)",
76
- "Bash(kubectl apply *)",
77
- "Bash(kubectl exec *)",
78
- "Bash(kubectl edit *)",
79
- "Bash(kubectl patch *)",
80
- "Bash(kubectl rollout *)",
81
- "Bash(kubectl scale *)",
82
- "Bash(kubectl port-forward *)",
83
- "Bash(kubectl config *)",
84
- "Bash(kubectl top *)",
85
82
  "Bash(terraform validate *)",
86
83
  "Bash(terraform fmt *)",
87
84
  "Bash(terraform plan *)",
88
85
  "Bash(terraform show *)",
89
86
  "Bash(terraform state list *)",
90
- "Bash(curl *)",
91
- "Bash(wget *)",
92
- "Bash(ssh *)",
93
- "Bash(scp *)",
94
- "Bash(rsync *)",
95
87
  "Bash(tmux *)",
96
88
  "Bash(zip *)",
97
89
  "Bash(unzip *)",
@@ -101,6 +93,11 @@
101
93
  "Bash(gh *)"
102
94
  ],
103
95
  "ask": [
96
+ "Bash(curl *)",
97
+ "Bash(wget *)",
98
+ "Bash(ssh *)",
99
+ "Bash(scp *)",
100
+ "Bash(rsync *)",
104
101
  "Bash(rm *)",
105
102
  "Bash(sudo *)",
106
103
  "Bash(kill *)",
@@ -118,6 +115,20 @@
118
115
  "Bash(terraform apply *)",
119
116
  "Bash(terraform destroy *)",
120
117
  "Bash(terraform import *)",
118
+ "Bash(docker *)",
119
+ "Bash(docker-compose *)",
120
+ "Bash(kubectl get *)",
121
+ "Bash(kubectl describe *)",
122
+ "Bash(kubectl logs *)",
123
+ "Bash(kubectl apply *)",
124
+ "Bash(kubectl exec *)",
125
+ "Bash(kubectl edit *)",
126
+ "Bash(kubectl patch *)",
127
+ "Bash(kubectl rollout *)",
128
+ "Bash(kubectl scale *)",
129
+ "Bash(kubectl port-forward *)",
130
+ "Bash(kubectl config *)",
131
+ "Bash(kubectl top *)",
121
132
  "Bash(kubectl delete *)"
122
133
  ],
123
134
  "deny": [
@@ -142,6 +153,7 @@
142
153
  "Read(**/*.p12)",
143
154
  "Read(**/*.pfx)",
144
155
  "Read(**/.npmrc)",
156
+ "Read(**/.pypirc)",
145
157
  "Read(**/.netrc)",
146
158
  "Read(**/id_rsa*)",
147
159
  "Read(**/id_ed25519*)",
@@ -154,22 +166,33 @@
154
166
  ]
155
167
  },
156
168
  "hooks": {
169
+ "SessionStart": [
170
+ {
171
+ "hooks": [
172
+ {
173
+ "type": "command",
174
+ "command": "python3 \"$HOME/.claude/hooks/session-start.py\"",
175
+ "timeout": 10
176
+ }
177
+ ]
178
+ }
179
+ ],
157
180
  "SessionEnd": [
158
181
  {
159
182
  "hooks": [
160
183
  {
161
184
  "type": "command",
162
- "command": "\"$HOME/.claude/hooks/session-end-capture.ts\""
185
+ "command": "python3 \"$HOME/.claude/hooks/session-end-capture.py\""
163
186
  }
164
187
  ]
165
188
  }
166
189
  ],
167
- "SessionStart": [
190
+ "PreToolUse": [
168
191
  {
169
192
  "hooks": [
170
193
  {
171
194
  "type": "command",
172
- "command": "\"$HOME/.claude/hooks/session-start.ts\""
195
+ "command": "python3 \"$HOME/.claude/hooks/pre-tool-inject.py\""
173
196
  }
174
197
  ]
175
198
  }
@@ -180,7 +203,17 @@
180
203
  "hooks": [
181
204
  {
182
205
  "type": "command",
183
- "command": "\"$HOME/.claude/hooks/circuit-breaker.ts\"",
206
+ "command": "python3 \"$HOME/.claude/hooks/circuit-breaker.py\"",
207
+ "timeout": 10
208
+ }
209
+ ]
210
+ },
211
+ {
212
+ "matcher": "Write|Edit|MultiEdit",
213
+ "hooks": [
214
+ {
215
+ "type": "command",
216
+ "command": "python3 \"$HOME/.claude/hooks/tool-ledger.py\"",
184
217
  "timeout": 10
185
218
  }
186
219
  ]
@@ -190,7 +223,16 @@
190
223
  "hooks": [
191
224
  {
192
225
  "type": "command",
193
- "command": "\"$HOME/.claude/hooks/tool-ledger.ts\"",
226
+ "command": "python3 \"$HOME/.claude/hooks/test_generator_hook.py\"",
227
+ "timeout": 10
228
+ }
229
+ ]
230
+ },
231
+ {
232
+ "hooks": [
233
+ {
234
+ "type": "command",
235
+ "command": "python3 \"$HOME/.claude/hooks/budget_governor.py\"",
194
236
  "timeout": 10
195
237
  }
196
238
  ]
@@ -201,21 +243,76 @@
201
243
  "hooks": [
202
244
  {
203
245
  "type": "command",
204
- "command": "\"$HOME/.claude/hooks/post-tool-failure.ts\""
246
+ "command": "python3 \"$HOME/.claude/hooks/post-tool-failure.py\""
205
247
  }
206
248
  ]
207
249
  }
208
250
  ],
209
251
  "Stop": [
210
252
  {
253
+ "matcher": "",
211
254
  "hooks": [
212
255
  {
213
256
  "type": "command",
214
- "command": "\"$HOME/.claude/hooks/stop-dispatcher.ts\"",
215
- "timeout": 15
257
+ "command": "python3 \"$HOME/.claude/hooks/stop_dispatcher.py\"",
258
+ "timeout": 90
216
259
  }
217
260
  ]
218
261
  }
219
262
  ]
263
+ },
264
+ "_omg": {
265
+ "_version": "2.0.2",
266
+ "preset": "safe",
267
+ "default_mode": "ulw+ralph",
268
+ "vision_auto": true,
269
+ "false_fix_detection": true,
270
+ "cost_budget": {
271
+ "session_limit_usd": 5.0,
272
+ "thresholds": [
273
+ 50,
274
+ 80,
275
+ 95
276
+ ],
277
+ "pricing": {
278
+ "input_per_mtok": 3.0,
279
+ "output_per_mtok": 15.0
280
+ }
281
+ },
282
+ "context_budget": {
283
+ "session_start_max_chars": 2000,
284
+ "prompt_enhancer_max_chars": 800,
285
+ "prompt_enhancer_max_injections": 10,
286
+ "full_turns": 10,
287
+ "summarize_turns": 50,
288
+ "batch_size": 21
289
+ },
290
+ "credentials": {
291
+ "rotation_schedule_days": 90,
292
+ "expiry_warning_days": 14
293
+ },
294
+ "features": {
295
+ "memory": false,
296
+ "ralph_loop": true,
297
+ "planning_enforcement": true,
298
+ "compound_learning": false,
299
+ "simplifier": true,
300
+ "model_routing": true,
301
+ "agent_registry": true,
302
+ "circuit_breaker_v2": true,
303
+ "cognitive_modes": true,
304
+ "agent_routing": true,
305
+ "SETUP": false,
306
+ "SETUP_WIZARD": false,
307
+ "MEMORY_SERVER": false,
308
+ "MEMORY_AUTOSTART": false,
309
+ "COST_TRACKING": false,
310
+ "GIT_WORKFLOW": false,
311
+ "SESSION_ANALYTICS": false,
312
+ "TEST_GENERATION": false,
313
+ "DEP_HEALTH": false,
314
+ "CODEBASE_VIZ": false,
315
+ "CONTEXT_MANAGER": false
316
+ }
220
317
  }
221
318
  }
@@ -13,7 +13,7 @@ key_deps: []
13
13
 
14
14
  conventions:
15
15
  naming: "" # camelCase / snake_case / etc
16
- test_cmd: "" # bun test / npm test / cargo test
16
+ test_cmd: "" # npm test / pytest / cargo test
17
17
  lint_cmd: "" # eslint / ruff / clippy
18
18
  format_cmd: "" # prettier / ruff format / rustfmt
19
19
 
@@ -0,0 +1,2 @@
1
+ # tools package
2
+ """OMG tools — LSP client, language configs, and related utilities."""