@trac3er/oh-my-god 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.agents/skills/omg/AGENTS.fragment.md +5 -0
  2. package/.agents/skills/omg/codex-mcp.toml +4 -0
  3. package/.agents/skills/omg/control-plane/SKILL.md +11 -0
  4. package/.agents/skills/omg/control-plane/openai.yaml +14 -0
  5. package/.agents/skills/omg/hook-governor/SKILL.md +11 -0
  6. package/.agents/skills/omg/hook-governor/openai.yaml +11 -0
  7. package/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
  8. package/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
  9. package/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
  10. package/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
  11. package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
  12. package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
  13. package/.claude-plugin/marketplace.json +3 -3
  14. package/.claude-plugin/plugin.json +1 -1
  15. package/.mcp.json +20 -4
  16. package/CHANGELOG.md +16 -0
  17. package/OMG-setup.sh +9 -3
  18. package/OMG_COMPAT_CONTRACT.md +92 -0
  19. package/README.md +26 -8
  20. package/SECURITY.md +6 -0
  21. package/commands/OMG:api-twin.md +22 -0
  22. package/commands/OMG:preflight.md +26 -0
  23. package/commands/OMG:security-check.md +28 -0
  24. package/commands/OMG:setup.md +1 -2
  25. package/dist/enterprise/bundle/.agents/skills/omg/AGENTS.fragment.md +5 -0
  26. package/dist/enterprise/bundle/.agents/skills/omg/codex-mcp.toml +4 -0
  27. package/dist/enterprise/bundle/.agents/skills/omg/control-plane/SKILL.md +11 -0
  28. package/dist/enterprise/bundle/.agents/skills/omg/control-plane/openai.yaml +14 -0
  29. package/dist/enterprise/bundle/.agents/skills/omg/hook-governor/SKILL.md +11 -0
  30. package/dist/enterprise/bundle/.agents/skills/omg/hook-governor/openai.yaml +11 -0
  31. package/dist/enterprise/bundle/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
  32. package/dist/enterprise/bundle/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
  33. package/dist/enterprise/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
  34. package/dist/enterprise/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
  35. package/dist/enterprise/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
  36. package/dist/enterprise/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
  37. package/dist/enterprise/bundle/.claude-plugin/marketplace.json +36 -0
  38. package/dist/enterprise/bundle/.claude-plugin/plugin.json +23 -0
  39. package/dist/enterprise/bundle/.mcp.json +40 -0
  40. package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +92 -0
  41. package/dist/enterprise/bundle/settings.json +366 -0
  42. package/dist/enterprise/manifest.json +99 -0
  43. package/dist/public/bundle/.agents/skills/omg/AGENTS.fragment.md +5 -0
  44. package/dist/public/bundle/.agents/skills/omg/codex-mcp.toml +4 -0
  45. package/dist/public/bundle/.agents/skills/omg/control-plane/SKILL.md +11 -0
  46. package/dist/public/bundle/.agents/skills/omg/control-plane/openai.yaml +14 -0
  47. package/dist/public/bundle/.agents/skills/omg/hook-governor/SKILL.md +11 -0
  48. package/dist/public/bundle/.agents/skills/omg/hook-governor/openai.yaml +11 -0
  49. package/dist/public/bundle/.agents/skills/omg/lsp-pack/SKILL.md +11 -0
  50. package/dist/public/bundle/.agents/skills/omg/lsp-pack/openai.yaml +11 -0
  51. package/dist/public/bundle/.agents/skills/omg/mcp-fabric/SKILL.md +11 -0
  52. package/dist/public/bundle/.agents/skills/omg/mcp-fabric/openai.yaml +13 -0
  53. package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +11 -0
  54. package/dist/public/bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +12 -0
  55. package/dist/public/bundle/.claude-plugin/marketplace.json +36 -0
  56. package/dist/public/bundle/.claude-plugin/plugin.json +23 -0
  57. package/dist/public/bundle/.mcp.json +40 -0
  58. package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +92 -0
  59. package/dist/public/bundle/settings.json +366 -0
  60. package/dist/public/manifest.json +99 -0
  61. package/hooks/policy_engine.py +38 -7
  62. package/hooks/post-write.py +1 -1
  63. package/hooks/prompt-enhancer.py +2 -2
  64. package/hooks/security_validators.py +75 -0
  65. package/hooks/setup_wizard.py +44 -20
  66. package/hooks/shadow_manager.py +22 -2
  67. package/package.json +1 -1
  68. package/plugins/README.md +4 -2
  69. package/plugins/advanced/commands/OMG:deep-plan.md +1 -1
  70. package/plugins/advanced/commands/OMG:security-review.md +10 -113
  71. package/plugins/advanced/commands/OMG:ship.md +1 -1
  72. package/plugins/advanced/plugin.json +1 -10
  73. package/plugins/core/plugin.json +25 -2
  74. package/pyproject.toml +1 -1
  75. package/runtime/adoption.py +1 -1
  76. package/runtime/api_twin.py +130 -0
  77. package/runtime/compat.py +21 -1
  78. package/runtime/contract_compiler.py +698 -0
  79. package/runtime/domain_packs.py +34 -0
  80. package/runtime/guide_assert.py +45 -0
  81. package/runtime/mcp_config_writers.py +145 -39
  82. package/runtime/omg_compat_contract_snapshot.json +8 -7
  83. package/runtime/omg_contract_snapshot.json +8 -7
  84. package/runtime/omg_mcp_server.py +205 -0
  85. package/runtime/preflight.py +52 -0
  86. package/runtime/providers/codex_provider.py +2 -12
  87. package/runtime/providers/gemini_provider.py +2 -21
  88. package/runtime/providers/kimi_provider.py +2 -21
  89. package/runtime/runtime_profile.py +61 -0
  90. package/runtime/security_check.py +347 -0
  91. package/runtime/subagent_dispatcher.py +117 -10
  92. package/runtime/team_router.py +3 -3
  93. package/runtime/untrusted_content.py +102 -0
  94. package/scripts/omg.py +174 -1
  95. package/settings.json +66 -18
  96. package/tools/python_repl.py +33 -3
  97. package/runtime/providers/opencode_provider.py +0 -144
@@ -0,0 +1,366 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
+ "_comment": "OMG 2.0.4 - project-level config with hook registrations, presets, and feature flags.",
4
+ "permissions": {
5
+ "allow": [
6
+ "Agent",
7
+ "Read",
8
+ "Write",
9
+ "Edit",
10
+ "MultiEdit",
11
+ "Grep",
12
+ "Glob",
13
+ "Bash(ls *)",
14
+ "Bash(cat *)",
15
+ "Bash(head *)",
16
+ "Bash(tail *)",
17
+ "Bash(wc *)",
18
+ "Bash(grep *)",
19
+ "Bash(find *)",
20
+ "Bash(which *)",
21
+ "Bash(echo *)",
22
+ "Bash(printf *)",
23
+ "Bash(pwd)",
24
+ "Bash(whoami)",
25
+ "Bash(date *)",
26
+ "Bash(basename *)",
27
+ "Bash(dirname *)",
28
+ "Bash(realpath *)",
29
+ "Bash(stat *)",
30
+ "Bash(file *)",
31
+ "Bash(diff *)",
32
+ "Bash(sort *)",
33
+ "Bash(uniq *)",
34
+ "Bash(awk *)",
35
+ "Bash(sed *)",
36
+ "Bash(tr *)",
37
+ "Bash(cut *)",
38
+ "Bash(tee *)",
39
+ "Bash(xargs *)",
40
+ "Bash(jq *)",
41
+ "Bash(yq *)",
42
+ "Bash(tree *)",
43
+ "Bash(du *)",
44
+ "Bash(df *)",
45
+ "Bash(type *)",
46
+ "Bash(command *)",
47
+ "Bash(test *)",
48
+ "Bash([ *)",
49
+ "Bash(mkdir *)",
50
+ "Bash(touch *)",
51
+ "Bash(ln *)",
52
+ "Bash(cp *)",
53
+ "Bash(mv *)",
54
+ "Bash(git *)",
55
+ "Bash(npm *)",
56
+ "Bash(npx *)",
57
+ "Bash(yarn *)",
58
+ "Bash(pnpm *)",
59
+ "Bash(bun *)",
60
+ "Bash(pip *)",
61
+ "Bash(pip3 *)",
62
+ "Bash(uv *)",
63
+ "Bash(go *)",
64
+ "Bash(cargo *)",
65
+ "Bash(rustc *)",
66
+ "Bash(tsc *)",
67
+ "Bash(eslint *)",
68
+ "Bash(prettier *)",
69
+ "Bash(mypy *)",
70
+ "Bash(ruff *)",
71
+ "Bash(ruff format *)",
72
+ "Bash(pytest *)",
73
+ "Bash(jest *)",
74
+ "Bash(vitest *)",
75
+ "Bash(shellcheck *)",
76
+ "Bash(terraform validate *)",
77
+ "Bash(terraform fmt *)",
78
+ "Bash(terraform plan *)",
79
+ "Bash(terraform show *)",
80
+ "Bash(terraform state list *)",
81
+ "Bash(tmux *)",
82
+ "Bash(zip *)",
83
+ "Bash(unzip *)",
84
+ "Bash(tar *)",
85
+ "Bash(gzip *)",
86
+ "Bash(rg *)",
87
+ "Bash(gh *)"
88
+ ],
89
+ "ask": [
90
+ "Bash(curl *)",
91
+ "Bash(wget *)",
92
+ "Bash(ssh *)",
93
+ "Bash(scp *)",
94
+ "Bash(rsync *)",
95
+ "Bash(rm *)",
96
+ "Bash(sudo *)",
97
+ "Bash(kill *)",
98
+ "Bash(pkill *)",
99
+ "Bash(systemctl *)",
100
+ "Bash(reboot)",
101
+ "Bash(shutdown *)",
102
+ "Bash(dd *)",
103
+ "Bash(fdisk *)",
104
+ "Bash(mkfs *)",
105
+ "Bash(mount *)",
106
+ "Bash(umount *)",
107
+ "Bash(iptables *)",
108
+ "Bash(ufw *)",
109
+ "Bash(terraform apply *)",
110
+ "Bash(terraform destroy *)",
111
+ "Bash(terraform import *)",
112
+ "Bash(env *)",
113
+ "Bash(node *)",
114
+ "Bash(python *)",
115
+ "Bash(python3 *)",
116
+ "Bash(chmod *)",
117
+ "Bash(chown *)",
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 *)",
132
+ "Bash(kubectl delete *)"
133
+ ],
134
+ "deny": [
135
+ "Bash(rm -rf /)",
136
+ "Bash(rm -rf /*)",
137
+ "Bash(rm -rf ~)",
138
+ "Bash(rm -rf ~/*)",
139
+ "Bash(sudo rm *)",
140
+ "Bash(sudo dd *)",
141
+ "Bash(sudo mkfs *)",
142
+ "Bash(:(){ :|:& };:)",
143
+ "Read(./.env)",
144
+ "Read(./secrets/**)",
145
+ "Read(**/.env)",
146
+ "Read(**/secrets/**)",
147
+ "Read(**/.aws/credentials)",
148
+ "Read(**/.aws/config)",
149
+ "Read(**/.kube/config)",
150
+ "Read(**/.ssh/*)",
151
+ "Read(**/*.pem)",
152
+ "Read(**/*.key)",
153
+ "Read(**/*.p12)",
154
+ "Read(**/*.pfx)",
155
+ "Read(**/.npmrc)",
156
+ "Read(**/.pypirc)",
157
+ "Read(**/.netrc)",
158
+ "Read(**/id_rsa*)",
159
+ "Read(**/id_ed25519*)",
160
+ "Read(**/id_ecdsa*)",
161
+ "Write(./.env)",
162
+ "Read(./.env.*)",
163
+ "Read(**/.env.*)",
164
+ "Write(./.env.*)",
165
+ "Write(./secrets/**)"
166
+ ]
167
+ },
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
+ ],
180
+ "SessionEnd": [
181
+ {
182
+ "hooks": [
183
+ {
184
+ "type": "command",
185
+ "command": "python3 \"$HOME/.claude/hooks/session-end-capture.py\""
186
+ }
187
+ ]
188
+ }
189
+ ],
190
+ "PreToolUse": [
191
+ {
192
+ "hooks": [
193
+ {
194
+ "type": "command",
195
+ "command": "python3 \"$HOME/.claude/hooks/firewall.py\"",
196
+ "timeout": 10
197
+ }
198
+ ],
199
+ "matcher": "Bash"
200
+ },
201
+ {
202
+ "hooks": [
203
+ {
204
+ "type": "command",
205
+ "command": "python3 \"$HOME/.claude/hooks/secret-guard.py\"",
206
+ "timeout": 10
207
+ }
208
+ ],
209
+ "matcher": "Read|Write|Edit|MultiEdit"
210
+ },
211
+ {
212
+ "hooks": [
213
+ {
214
+ "type": "command",
215
+ "command": "python3 \"$HOME/.claude/hooks/pre-tool-inject.py\""
216
+ }
217
+ ],
218
+ "matcher": ""
219
+ }
220
+ ],
221
+ "PostToolUse": [
222
+ {
223
+ "hooks": [
224
+ {
225
+ "type": "command",
226
+ "command": "python3 \"$HOME/.claude/hooks/circuit-breaker.py\"",
227
+ "timeout": 10
228
+ }
229
+ ],
230
+ "matcher": "Bash"
231
+ },
232
+ {
233
+ "hooks": [
234
+ {
235
+ "type": "command",
236
+ "command": "python3 \"$HOME/.claude/hooks/tool-ledger.py\"",
237
+ "timeout": 10
238
+ }
239
+ ],
240
+ "matcher": "Write|Edit|MultiEdit"
241
+ },
242
+ {
243
+ "hooks": [
244
+ {
245
+ "type": "command",
246
+ "command": "python3 \"$HOME/.claude/hooks/test_generator_hook.py\"",
247
+ "timeout": 10
248
+ }
249
+ ],
250
+ "matcher": "Write|Edit|MultiEdit"
251
+ },
252
+ {
253
+ "hooks": [
254
+ {
255
+ "type": "command",
256
+ "command": "python3 \"$HOME/.claude/hooks/budget_governor.py\"",
257
+ "timeout": 10
258
+ }
259
+ ],
260
+ "matcher": ""
261
+ }
262
+ ],
263
+ "PostToolUseFailure": [
264
+ {
265
+ "hooks": [
266
+ {
267
+ "type": "command",
268
+ "command": "python3 \"$HOME/.claude/hooks/post-tool-failure.py\""
269
+ }
270
+ ]
271
+ }
272
+ ],
273
+ "Stop": [
274
+ {
275
+ "hooks": [
276
+ {
277
+ "type": "command",
278
+ "command": "python3 \"$HOME/.claude/hooks/stop_dispatcher.py\"",
279
+ "timeout": 90
280
+ }
281
+ ],
282
+ "matcher": ""
283
+ }
284
+ ]
285
+ },
286
+ "_omg": {
287
+ "_version": "2.0.4",
288
+ "preset": "safe",
289
+ "default_mode": "ulw+ralph",
290
+ "vision_auto": true,
291
+ "false_fix_detection": true,
292
+ "cost_budget": {
293
+ "session_limit_usd": 5.0,
294
+ "thresholds": [
295
+ 50,
296
+ 80,
297
+ 95
298
+ ],
299
+ "pricing": {
300
+ "input_per_mtok": 3.0,
301
+ "output_per_mtok": 15.0
302
+ }
303
+ },
304
+ "context_budget": {
305
+ "session_start_max_chars": 2000,
306
+ "prompt_enhancer_max_chars": 800,
307
+ "prompt_enhancer_max_injections": 10,
308
+ "full_turns": 10,
309
+ "summarize_turns": 50,
310
+ "batch_size": 21
311
+ },
312
+ "credentials": {
313
+ "rotation_schedule_days": 90,
314
+ "expiry_warning_days": 14
315
+ },
316
+ "features": {
317
+ "memory": false,
318
+ "ralph_loop": true,
319
+ "planning_enforcement": true,
320
+ "compound_learning": false,
321
+ "simplifier": true,
322
+ "model_routing": true,
323
+ "agent_registry": true,
324
+ "circuit_breaker_v2": true,
325
+ "cognitive_modes": true,
326
+ "agent_routing": true,
327
+ "SETUP": false,
328
+ "SETUP_WIZARD": false,
329
+ "MEMORY_SERVER": false,
330
+ "MEMORY_AUTOSTART": false,
331
+ "COST_TRACKING": false,
332
+ "GIT_WORKFLOW": false,
333
+ "SESSION_ANALYTICS": false,
334
+ "TEST_GENERATION": false,
335
+ "DEP_HEALTH": false,
336
+ "CODEBASE_VIZ": false,
337
+ "CONTEXT_MANAGER": false
338
+ },
339
+ "generated": {
340
+ "contract_version": "2.0.4",
341
+ "channel": "public",
342
+ "required_bundles": [
343
+ "control-plane",
344
+ "hook-governor",
345
+ "mcp-fabric",
346
+ "lsp-pack",
347
+ "secure-worktree-pipeline"
348
+ ],
349
+ "protected_paths": [
350
+ ".omg/**",
351
+ ".agents/**",
352
+ ".codex/**",
353
+ ".claude/**"
354
+ ],
355
+ "emulated_events": [
356
+ "PreCompact",
357
+ "ConfigChange",
358
+ "WorktreeCreate",
359
+ "WorktreeRemove",
360
+ "SubagentStart",
361
+ "SubagentStop",
362
+ "TaskCompleted"
363
+ ]
364
+ }
365
+ }
366
+ }
@@ -0,0 +1,99 @@
1
+ {
2
+ "schema": "OmgCompiledArtifactManifest",
3
+ "channel": "public",
4
+ "contract_version": "2.0.4",
5
+ "artifacts": [
6
+ {
7
+ "path": "bundle/.agents/skills/omg/AGENTS.fragment.md",
8
+ "sha256": "603430cb291632105fda7444ae018da521cc3a57c71c1a1c98179efea42ce0f2"
9
+ },
10
+ {
11
+ "path": "bundle/.agents/skills/omg/codex-mcp.toml",
12
+ "sha256": "a56de208a369a2b318d2e66e150eef4cba1fac1ecf32bda6db1a1e4b65db7311"
13
+ },
14
+ {
15
+ "path": "bundle/.agents/skills/omg/control-plane/SKILL.md",
16
+ "sha256": "fe281985ffbfb4d324d0ae421653c96d501bfda4ef1462865e2868276c40a3c2"
17
+ },
18
+ {
19
+ "path": "bundle/.agents/skills/omg/control-plane/openai.yaml",
20
+ "sha256": "a7ba723d34839c6a444b6ee4416223103cd9b2bbd9caed0d4c0aa1e7d5a523fc"
21
+ },
22
+ {
23
+ "path": "bundle/.agents/skills/omg/hook-governor/SKILL.md",
24
+ "sha256": "852df4b5f787f2a885e87cb8ad8dbb8d877e018e14083cfd5e5b98deb9dd19b5"
25
+ },
26
+ {
27
+ "path": "bundle/.agents/skills/omg/hook-governor/openai.yaml",
28
+ "sha256": "8b4e219662bd9a12e89cdcd9a1828c1ff14810fa09cbfff9a6d9f932fb42ec85"
29
+ },
30
+ {
31
+ "path": "bundle/.agents/skills/omg/lsp-pack/SKILL.md",
32
+ "sha256": "86890c6671441f8687ad7d71dedd7d61574c42aa6e109aa8dd6ceb4b6a139879"
33
+ },
34
+ {
35
+ "path": "bundle/.agents/skills/omg/lsp-pack/openai.yaml",
36
+ "sha256": "14000aec9cc9ff630b7354ae101cc43ed90c4e1ecd0fa719013741ca14598142"
37
+ },
38
+ {
39
+ "path": "bundle/.agents/skills/omg/mcp-fabric/SKILL.md",
40
+ "sha256": "e741baf4fa5f09957fe37349b79fe6831afe0352eefa945f3e53514ea313bc36"
41
+ },
42
+ {
43
+ "path": "bundle/.agents/skills/omg/mcp-fabric/openai.yaml",
44
+ "sha256": "18dc35b4d6fb598b572ec933153e80de5449be9e07ef07178e4f875e39a8915c"
45
+ },
46
+ {
47
+ "path": "bundle/.agents/skills/omg/secure-worktree-pipeline/SKILL.md",
48
+ "sha256": "1eebd557d77cc084161c3ffe11ed3a9ea5d78caa65b1021a992ac8b835fc6a0d"
49
+ },
50
+ {
51
+ "path": "bundle/.agents/skills/omg/secure-worktree-pipeline/openai.yaml",
52
+ "sha256": "1f82a05b2fcad17c126e34541c995dedf3ed242fa499539ab693cc60a0d41c77"
53
+ },
54
+ {
55
+ "path": "bundle/.claude-plugin/marketplace.json",
56
+ "sha256": "cf1a17ce1e8db6814209126a79d5a05d057f354ef3ee49a74da4f18f2ec04597"
57
+ },
58
+ {
59
+ "path": "bundle/.claude-plugin/plugin.json",
60
+ "sha256": "6ad6db257f9e46528d94760ef895b6ffc87c78a8ec5ca05b430a46b46ee3dc08"
61
+ },
62
+ {
63
+ "path": "bundle/.mcp.json",
64
+ "sha256": "d8249fdd26e0df0a9b7cabcb34256d7e33578e1d0e9878cc91d43452a10f7c24"
65
+ },
66
+ {
67
+ "path": "bundle/OMG_COMPAT_CONTRACT.md",
68
+ "sha256": "fa91ec0dbff58d543df5a9e3a86a1fe1629104a383d4e1bd440a36cb5522189c"
69
+ },
70
+ {
71
+ "path": "bundle/registry/bundles/control-plane.yaml",
72
+ "sha256": "bb58e1d21a7f545548da10e7f9b83898d6ece8010a921b66d0f94a08c8ae0d8e"
73
+ },
74
+ {
75
+ "path": "bundle/registry/bundles/hook-governor.yaml",
76
+ "sha256": "93b13ea1e2098328349d33373595144f2f863274548bef47fd266d6cab210260"
77
+ },
78
+ {
79
+ "path": "bundle/registry/bundles/lsp-pack.yaml",
80
+ "sha256": "6c2ca235bdca31635d89c428c48a35a5414250e98c7349cd67ba900870203d9a"
81
+ },
82
+ {
83
+ "path": "bundle/registry/bundles/mcp-fabric.yaml",
84
+ "sha256": "2cb71fc331820b19c60c3275f521fb49d099ed7c31539fab9959a55d29ae0fe9"
85
+ },
86
+ {
87
+ "path": "bundle/registry/bundles/secure-worktree-pipeline.yaml",
88
+ "sha256": "b93e752cef8b5cbc76fb7aa32ebe80d30ce390193183bb0c79dedfa1ce89c4e9"
89
+ },
90
+ {
91
+ "path": "bundle/registry/omg-capability.schema.json",
92
+ "sha256": "b5a52c03c6d42c0ce0297a2d8c22f34ed1075062cc5434d3a85b9f4fa6a0f121"
93
+ },
94
+ {
95
+ "path": "bundle/settings.json",
96
+ "sha256": "60fb4f9e2bedd4d67af41a2e442ef37c9a89cd2077bf6dfe40761eec01586824"
97
+ }
98
+ ]
99
+ }
@@ -115,6 +115,23 @@ ASK_PATTERNS = [
115
115
  (r"node\s+-e\s+", "Inline Node execution"),
116
116
  ]
117
117
 
118
+ UNTRUSTED_MUTATION_PATTERNS = [
119
+ r"\bgit\s+(commit|push|tag)\b",
120
+ r"\bnpm\s+(install|publish)\b",
121
+ r"\bpython[23]?\s+.*\b(setup\.py|manage\.py)\b",
122
+ r"\b(mv|cp|tee|sed\s+-i|touch|mkdir)\b",
123
+ ]
124
+
125
+
126
+ def _is_untrusted_content_mode_active() -> bool:
127
+ try:
128
+ from runtime.untrusted_content import is_untrusted_content_mode_active
129
+
130
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
131
+ return is_untrusted_content_mode_active(project_dir)
132
+ except Exception:
133
+ return False
134
+
118
135
 
119
136
  def evaluate_bash_command(cmd: str) -> PolicyDecision:
120
137
  if not cmd:
@@ -158,6 +175,15 @@ def evaluate_bash_command(cmd: str) -> PolicyDecision:
158
175
  if re.search(pat, cmd):
159
176
  return ask(f"{label}: {cmd[:120]}", "med", ["human-approval"])
160
177
 
178
+ if _is_untrusted_content_mode_active():
179
+ for pat in UNTRUSTED_MUTATION_PATTERNS:
180
+ if re.search(pat, cmd):
181
+ return ask(
182
+ "Untrusted external content mode is active. Review before running state-changing commands.",
183
+ "high",
184
+ ["manual-approval", "review-provenance"],
185
+ )
186
+
161
187
  return allow("command allowed")
162
188
 
163
189
 
@@ -409,8 +435,8 @@ def evaluate_file_access(
409
435
  ) -> PolicyDecision:
410
436
  """Evaluate file access policy.
411
437
 
412
- If an allowlist is provided, matching entries override deny decisions
413
- for the given path and tool combination.
438
+ If an allowlist is provided, matching entries may override non-secret-file
439
+ deny decisions for the given path and tool combination.
414
440
  """
415
441
  if not file_path:
416
442
  return allow("no file")
@@ -424,11 +450,6 @@ def evaluate_file_access(
424
450
  basename = os.path.basename(normalized).lower()
425
451
  lowpath = normalized.lower()
426
452
 
427
- # --- Allowlist check (before deny rules) ---
428
- # Check allowlist early: if path+tool is allowlisted, override deny.
429
- if allowlist and is_allowlisted(file_path, tool, allowlist):
430
- return allow(f"Allowlisted: {file_path}")
431
-
432
453
  if basename in EXAMPLE_FILES and tool in ("Write", "Edit", "MultiEdit"):
433
454
  return deny(
434
455
  f"Modifying example env file blocked (Read is allowed): {file_path}",
@@ -451,6 +472,16 @@ def evaluate_file_access(
451
472
  if re.search(pat, lowpath):
452
473
  return deny(f"Sensitive path blocked: {file_path}", "critical", ["secret-access"])
453
474
 
475
+ if tool in {"Write", "Edit", "MultiEdit"} and _is_untrusted_content_mode_active():
476
+ return ask(
477
+ "Untrusted external content mode is active. Review before mutating files.",
478
+ "high",
479
+ ["manual-approval", "review-provenance"],
480
+ )
481
+
482
+ if allowlist and is_allowlisted(file_path, tool, allowlist):
483
+ return allow(f"Allowlisted: {file_path}")
484
+
454
485
  return allow("file allowed")
455
486
 
456
487
 
@@ -213,7 +213,7 @@ for i, line in enumerate(content.split("\n"), 1):
213
213
 
214
214
  if sec_warnings:
215
215
  msg = f"SECURITY WARNINGS in {file_path}:\n" + "\n".join(sec_warnings[:5])
216
- msg += "\n\nConsider running /OMG:security-review for a full audit."
216
+ msg += "\n\nConsider running /OMG:security-check for the canonical audit pipeline."
217
217
  print(msg, file=sys.stderr)
218
218
 
219
219
  sys.exit(0)
@@ -2,7 +2,7 @@
2
2
  """
3
3
  UserPromptSubmit Hook — OMG v1
4
4
 
5
- Inspired by oh-my-opencode's Sisyphus agent system. Key upgrades:
5
+ Inspired by earlier OMG routing experiments. Key upgrades:
6
6
  1. Intent classification BEFORE acting (IntentGate)
7
7
  2. Discipline enforcement — never stop halfway
8
8
  3. Agent-aware routing — Codex/Gemini/Claude orchestration
@@ -386,7 +386,7 @@ SECURITY_SIGNALS = [
386
386
  ]
387
387
  if not route_lock and any(signal_matches_text(sig, prompt) for sig in SECURITY_SIGNALS) and budget_ok():
388
388
  if detected_intent in ("fix", "implement", "refactor"):
389
- add("@security: CRITICAL DOMAIN — No hardcoded secrets. Run /OMG:security-review after.")
389
+ add("@security: CRITICAL DOMAIN — No hardcoded secrets. Run /OMG:security-check after.")
390
390
 
391
391
  # ═══════════════════════════════════════════════════════════
392
392
  # 5. VISION DETECTION
@@ -0,0 +1,75 @@
1
+ """Shared validation helpers for security-sensitive filesystem and config writes."""
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ from pathlib import Path
6
+ from urllib.parse import urlparse
7
+
8
+
9
+ _OPAQUE_IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
10
+ _SERVER_NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]*$")
11
+
12
+
13
+ def validate_opaque_identifier(value: str, field_name: str, max_length: int = 64) -> str:
14
+ """Validate an opaque identifier used in filenames or paths."""
15
+ if not isinstance(value, str):
16
+ raise ValueError(f"Invalid {field_name}: must be a string")
17
+
18
+ normalized = value.strip()
19
+ if not normalized:
20
+ raise ValueError(f"Invalid {field_name}: value is required")
21
+ if len(normalized) > max_length:
22
+ raise ValueError(f"Invalid {field_name}: exceeds {max_length} characters")
23
+ if ".." in normalized or not _OPAQUE_IDENTIFIER_RE.fullmatch(normalized):
24
+ raise ValueError(
25
+ f"Invalid {field_name}: use only ASCII letters, numbers, dot, underscore, and dash"
26
+ )
27
+ return normalized
28
+
29
+
30
+ def ensure_path_within_dir(base_dir: str | Path, candidate_path: str | Path) -> str:
31
+ """Return a resolved path and reject traversal outside the intended base directory."""
32
+ base = Path(base_dir).resolve(strict=False)
33
+ candidate = Path(candidate_path).resolve(strict=False)
34
+ try:
35
+ candidate.relative_to(base)
36
+ except ValueError as exc:
37
+ raise ValueError(f"Resolved path escapes base directory: {candidate}") from exc
38
+ return str(candidate)
39
+
40
+
41
+ def validate_server_name(server_name: str, max_length: int = 64) -> str:
42
+ """Validate an MCP server identifier suitable for JSON keys and TOML table names."""
43
+ if not isinstance(server_name, str):
44
+ raise ValueError("Invalid server_name: must be a string")
45
+
46
+ normalized = server_name.strip()
47
+ if not normalized:
48
+ raise ValueError("Invalid server_name: value is required")
49
+ if len(normalized) > max_length:
50
+ raise ValueError(f"Invalid server_name: exceeds {max_length} characters")
51
+ if not _SERVER_NAME_RE.fullmatch(normalized):
52
+ raise ValueError("Invalid server_name: use only ASCII letters, numbers, underscore, and dash")
53
+ return normalized
54
+
55
+
56
+ def validate_server_url(server_url: str) -> str:
57
+ """Validate an MCP server URL and reject newline/control injection."""
58
+ if not isinstance(server_url, str):
59
+ raise ValueError("Invalid server_url: must be a string")
60
+
61
+ normalized = server_url.strip()
62
+ if not normalized:
63
+ raise ValueError("Invalid server_url: value is required")
64
+ if "\n" in normalized or "\r" in normalized:
65
+ raise ValueError("Invalid server_url: newline characters are not allowed")
66
+
67
+ parsed = urlparse(normalized)
68
+ if parsed.scheme not in {"http", "https"} or not parsed.netloc:
69
+ raise ValueError("Invalid server_url: must be an http or https URL")
70
+ return normalized
71
+
72
+
73
+ def toml_quote_string(value: str) -> str:
74
+ """Escape TOML basic string content."""
75
+ return value.replace("\\", "\\\\").replace('"', '\\"')