@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,423 @@
1
+ #!/usr/bin/env python3
2
+ """Agent Registry — Central dispatch table for OMG domain agents.
3
+
4
+ Maps domain keywords to agents with model preferences, skills, and MCP tools.
5
+ """
6
+ import json
7
+ import os
8
+ import shutil
9
+
10
+
11
+ def _claude_config_dir() -> str:
12
+ return os.environ.get("CLAUDE_CONFIG_DIR", os.path.expanduser("~/.claude"))
13
+
14
+
15
+ def _load_mcp_servers(path: str) -> dict[str, object]:
16
+ if not os.path.exists(path):
17
+ return {}
18
+ try:
19
+ with open(path, encoding="utf-8") as f:
20
+ config = json.load(f)
21
+ except (json.JSONDecodeError, OSError):
22
+ return {}
23
+ if not isinstance(config, dict):
24
+ return {}
25
+ servers = config.get("mcpServers", {})
26
+ return servers if isinstance(servers, dict) else {}
27
+
28
+
29
+ # Agent registry: domain → agent config
30
+ AGENT_REGISTRY = {
31
+ 'frontend-designer': {
32
+ 'preferred_model': 'gemini-cli',
33
+ 'task_category': 'visual-engineering',
34
+ 'skills': ['frontend-design', 'frontend-patterns'],
35
+ 'trigger_keywords': {'ui', 'ux', 'css', 'layout', 'responsive', 'visual', 'frontend', 'component', 'style', 'design', 'animation', 'color', 'theme'},
36
+ 'mcp_tools': ['chrome-devtools'],
37
+ 'description': 'Frontend/UI specialist. Uses Gemini for visual tasks.',
38
+ 'agent_file': 'agents/omg-frontend-designer.md',
39
+ 'model_version': 'gemini-3.1-pro-preview',
40
+ },
41
+ 'backend-engineer': {
42
+ 'preferred_model': 'codex-cli',
43
+ 'task_category': 'deep',
44
+ 'skills': ['backend-patterns', 'api-design'],
45
+ 'trigger_keywords': {'api', 'server', 'database', 'logic', 'algorithm', 'backend', 'endpoint', 'route', 'middleware', 'service'},
46
+ 'mcp_tools': [],
47
+ 'description': 'Backend/logic specialist. Uses Codex for deep reasoning.',
48
+ 'agent_file': 'agents/omg-backend-engineer.md',
49
+ 'model_version': 'gpt-5.3',
50
+ },
51
+ 'api-builder': {
52
+ 'preferred_model': 'codex-cli',
53
+ 'task_category': 'deep',
54
+ 'skills': ['api-design', 'backend-patterns'],
55
+ 'trigger_keywords': {'openapi', 'swagger', 'rest', 'graphql', 'api-spec', 'schema', 'contract', 'endpoint-design'},
56
+ 'mcp_tools': ['context7'],
57
+ 'description': 'API design/build specialist. Contracts, endpoint shape, and versioning.',
58
+ 'agent_file': 'agents/omg-api-builder.md',
59
+ 'model_version': 'gpt-5.3',
60
+ },
61
+ 'security-auditor': {
62
+ 'preferred_model': 'codex-cli',
63
+ 'task_category': 'deep',
64
+ 'skills': ['security-review'],
65
+ 'trigger_keywords': {'auth', 'encrypt', 'cors', 'jwt', 'vulnerability', 'security', 'xss', 'csrf', 'injection', 'secret', 'password', 'token'},
66
+ 'mcp_tools': ['context7', 'websearch'],
67
+ 'description': 'Security specialist. Uses Codex for deep security analysis.',
68
+ 'agent_file': 'agents/omg-security-auditor.md',
69
+ 'model_version': 'gpt-5.3',
70
+ },
71
+ 'database-engineer': {
72
+ 'preferred_model': 'codex-cli',
73
+ 'task_category': 'unspecified-high',
74
+ 'skills': [],
75
+ 'trigger_keywords': {'sql', 'migration', 'schema', 'query', 'index', 'database', 'postgres', 'mongo', 'redis', 'orm'},
76
+ 'mcp_tools': [],
77
+ 'description': 'Database specialist. Schema design, query optimization, migrations.',
78
+ 'agent_file': 'agents/omg-database-engineer.md',
79
+ 'model_version': 'gpt-5.3',
80
+ },
81
+ 'testing-engineer': {
82
+ 'preferred_model': 'claude',
83
+ 'task_category': 'unspecified-high',
84
+ 'skills': ['python-testing', 'e2e-testing'],
85
+ 'trigger_keywords': {'test', 'spec', 'coverage', 'fixture', 'mock', 'playwright', 'e2e', 'unit', 'integration', 'pytest', 'jest'},
86
+ 'mcp_tools': ['chrome-devtools'],
87
+ 'description': 'Testing specialist. Unit tests, integration tests, E2E with Playwright.',
88
+ 'agent_file': 'agents/omg-testing-engineer.md',
89
+ 'model_version': 'claude-sonnet-4-5',
90
+ },
91
+ 'infra-engineer': {
92
+ 'preferred_model': 'codex-cli',
93
+ 'task_category': 'unspecified-high',
94
+ 'skills': ['docker-patterns'],
95
+ 'trigger_keywords': {'docker', 'ci', 'cd', 'deploy', 'terraform', 'k8s', 'kubernetes', 'nginx', 'pipeline', 'container', 'cloud'},
96
+ 'mcp_tools': [],
97
+ 'description': 'Infrastructure specialist. Docker, CI/CD, deployment, cloud.',
98
+ 'agent_file': 'agents/omg-infra-engineer.md',
99
+ 'model_version': 'gpt-5.3',
100
+ },
101
+ # Cognitive modes
102
+ 'research-mode': {
103
+ 'preferred_model': 'claude',
104
+ 'task_category': None,
105
+ 'subagent_type': 'librarian',
106
+ 'skills': [],
107
+ 'trigger_keywords': {'research', 'find', 'how to', 'explain', 'documentation', 'docs', 'lookup'},
108
+ 'mcp_tools': ['websearch', 'context7', 'chrome-devtools'],
109
+ 'description': 'Research mode. Web search, docs lookup, library exploration.',
110
+ 'agent_file': 'agents/omg-research-mode.md',
111
+ 'model_version': 'claude-haiku-3-5',
112
+ },
113
+ 'architect-mode': {
114
+ 'preferred_model': 'claude',
115
+ 'task_category': None,
116
+ 'subagent_type': 'oracle',
117
+ 'skills': [],
118
+ 'trigger_keywords': {'architect', 'design', 'plan', 'structure', 'system', 'architecture', 'tradeoff'},
119
+ 'mcp_tools': [],
120
+ 'description': 'Architecture mode. System design, trade-off analysis.',
121
+ 'agent_file': 'agents/omg-architect-mode.md',
122
+ 'model_version': 'claude-sonnet-4-5',
123
+ },
124
+ 'implement-mode': {
125
+ 'preferred_model': 'domain-dependent',
126
+ 'task_category': 'deep',
127
+ 'skills': [],
128
+ 'trigger_keywords': {'implement', 'build', 'create', 'add', 'develop', 'write', 'code'},
129
+ 'mcp_tools': [],
130
+ 'description': 'Implementation mode. Model chosen based on domain of task.',
131
+ 'agent_file': 'agents/omg-implement-mode.md',
132
+ 'model_version': 'claude-sonnet-4-5',
133
+ },
134
+ # Bundled agents (Task 2.3)
135
+ 'explore': {
136
+ 'preferred_model': 'claude',
137
+ 'task_category': 'quick',
138
+ 'skills': [],
139
+ 'trigger_keywords': {'find', 'search', 'grep', 'locate', 'where', 'which', 'lookup', 'explore', 'discover'},
140
+ 'mcp_tools': [],
141
+ 'description': 'Fast codebase search agent. Read-only: grep, glob, file reading, pattern matching.',
142
+ 'agent_file': 'agents/explore.md',
143
+ 'model_version': 'claude-haiku-4-5',
144
+ 'model_role': 'smol',
145
+ 'bundled': True,
146
+ },
147
+ 'plan': {
148
+ 'preferred_model': 'claude',
149
+ 'task_category': 'unspecified-high',
150
+ 'skills': [],
151
+ 'trigger_keywords': {'plan', 'architect', 'design', 'decompose', 'strategy', 'roadmap', 'breakdown', 'structure'},
152
+ 'mcp_tools': [],
153
+ 'description': 'Strategic planning agent. Architecture design, task decomposition, risk analysis.',
154
+ 'agent_file': 'agents/plan.md',
155
+ 'model_version': 'claude-opus-4-5',
156
+ 'model_role': 'slow',
157
+ 'bundled': True,
158
+ },
159
+ 'designer': {
160
+ 'preferred_model': 'gemini-cli',
161
+ 'task_category': 'visual-engineering',
162
+ 'skills': ['frontend-design', 'frontend-patterns'],
163
+ 'trigger_keywords': {'component', 'layout', 'accessibility', 'responsive', 'tailwind', 'css', 'aria', 'wcag', 'breakpoint'},
164
+ 'mcp_tools': ['chrome-devtools'],
165
+ 'description': 'UI/UX design agent. Component design, layout, accessibility, responsive design.',
166
+ 'agent_file': 'agents/designer.md',
167
+ 'model_version': 'claude-opus-4-5',
168
+ 'model_role': 'default',
169
+ 'bundled': True,
170
+ },
171
+ 'reviewer': {
172
+ 'preferred_model': 'codex-cli',
173
+ 'task_category': 'deep',
174
+ 'skills': ['security-review'],
175
+ 'trigger_keywords': {'review', 'audit', 'check', 'inspect', 'critique', 'feedback', 'pr', 'pull-request', 'quality'},
176
+ 'mcp_tools': ['context7', 'websearch'],
177
+ 'description': 'Code review agent. Security, performance, quality, best practices, test coverage.',
178
+ 'agent_file': 'agents/reviewer.md',
179
+ 'model_version': 'claude-opus-4-5',
180
+ 'model_role': 'slow',
181
+ 'bundled': True,
182
+ },
183
+ 'task': {
184
+ 'preferred_model': 'claude',
185
+ 'task_category': 'unspecified-high',
186
+ 'skills': [],
187
+ 'trigger_keywords': {'fix', 'implement', 'feature', 'bug', 'patch', 'update', 'change', 'modify', 'refactor'},
188
+ 'mcp_tools': [],
189
+ 'description': 'General task execution agent. Implement features, fix bugs, write tests.',
190
+ 'agent_file': 'agents/task.md',
191
+ 'model_version': 'claude-opus-4-5',
192
+ 'model_role': 'default',
193
+ 'bundled': True,
194
+ },
195
+ 'quick_task': {
196
+ 'preferred_model': 'claude',
197
+ 'task_category': 'quick',
198
+ 'skills': [],
199
+ 'trigger_keywords': {'typo', 'rename', 'label', 'caption', 'spelling', 'minor', 'small', 'quick', 'simple'},
200
+ 'mcp_tools': [],
201
+ 'description': 'Fast task execution agent. Simple fixes, typo corrections, single-file changes.',
202
+ 'agent_file': 'agents/quick_task.md',
203
+ 'model_version': 'claude-haiku-4-5',
204
+ 'model_role': 'smol',
205
+ 'bundled': True,
206
+ },
207
+ }
208
+
209
+ # ═══════════════════════════════════════════════════════════
210
+ # Intent-to-Agent Routing Table (Magic Keyword Router)
211
+ # Maps LEADER_HINT intents from intentgate-keyword-detector
212
+ # to target agent names. None = halt (no agent dispatch).
213
+ # ═══════════════════════════════════════════════════════════
214
+ INTENT_ROUTING = {
215
+ "INTENT_MAX_EFFORT": "sisyphus", # ultrawork → full-effort agent
216
+ "INTENT_AUTONOMOUS": "sisyphus", # autopilot → autonomous agent
217
+ "INTENT_LOOP": "sisyphus", # ralph → loop agent
218
+ "INTENT_PLAN": "prometheus", # plan this → planning agent
219
+ "INTENT_TEST_DRIVEN": "sisyphus", # tdd → TDD agent
220
+ "INTENT_SEARCH": "librarian", # search → search agent
221
+ "INTENT_STOP": None, # stop → halt (no agent)
222
+ "INTENT_CRAZY": "sisyphus", # crazy → aggressive agent
223
+ # Bundled agent intents (Task 2.3)
224
+ "INTENT_EXPLORE": "explore", # explore/find → fast search agent
225
+ "INTENT_REVIEW": "reviewer", # review/audit → code review agent
226
+ "INTENT_QUICK": "quick_task", # quick/simple → fast task agent
227
+ }
228
+
229
+ # Core agent model preferences. NOT keyword-matched — used by orchestration pipeline only. model_version is informational (not passed to CLI).
230
+ CORE_AGENT_MODELS = {
231
+ 'architect': {
232
+ 'preferred_model': 'codex-cli',
233
+ 'model_version': 'gpt-5.2',
234
+ 'task_category': None,
235
+ 'description': 'System design + planning + delegation routing.',
236
+ 'agent_file': 'agents/omg-architect.md',
237
+ },
238
+ 'critic': {
239
+ 'preferred_model': 'codex-cli',
240
+ 'model_version': 'gpt-5.3',
241
+ 'task_category': None,
242
+ 'description': 'Code review — 3 perspectives, no LGTM allowed.',
243
+ 'agent_file': 'agents/omg-critic.md',
244
+ },
245
+ 'executor': {
246
+ 'preferred_model': 'claude',
247
+ 'model_version': 'claude-sonnet-4-5',
248
+ 'task_category': 'deep',
249
+ 'description': 'Implements code with evidence, auto-escalates when stuck.',
250
+ 'agent_file': 'agents/omg-executor.md',
251
+ },
252
+ 'qa-tester': {
253
+ 'preferred_model': 'claude',
254
+ 'model_version': 'claude-sonnet-4-5',
255
+ 'task_category': 'unspecified-high',
256
+ 'description': 'User-journey test writer — no boilerplate.',
257
+ 'agent_file': 'agents/omg-qa-tester.md',
258
+ },
259
+ 'escalation-router': {
260
+ 'preferred_model': 'claude',
261
+ 'model_version': 'claude-haiku-3-5',
262
+ 'task_category': None,
263
+ 'description': 'Routes problems to Codex/Gemini/CCG based on domain.',
264
+ 'agent_file': 'agents/omg-escalation-router.md',
265
+ },
266
+ }
267
+
268
+ # Cache for model availability (per process)
269
+ _model_cache: dict[str, bool] | None = None
270
+
271
+ def resolve_agent(prompt_keywords: set[str]):
272
+ """Match prompt keywords to best agent. Returns registry entry or None.
273
+
274
+ Scoring: count of matching trigger_keywords. Returns highest-scoring agent.
275
+ Ties broken by order in registry (first wins).
276
+ """
277
+ best_agent = None
278
+ best_score = 0
279
+ for name, config in AGENT_REGISTRY.items():
280
+ triggers_raw = config.get('trigger_keywords', set())
281
+ triggers = triggers_raw if isinstance(triggers_raw, set) else set()
282
+ score = len(prompt_keywords & triggers)
283
+ if score > best_score:
284
+ best_score = score
285
+ best_agent = dict(config)
286
+ best_agent['name'] = name
287
+ return best_agent if best_score > 0 else None
288
+
289
+
290
+ def get_dispatch_params(agent_name: str):
291
+ """Get task() parameters for dispatching this agent.
292
+
293
+ Returns dict with 'category', 'skills', and optionally 'subagent_type'.
294
+ Falls back to claude if preferred model not available.
295
+ """
296
+ config = AGENT_REGISTRY.get(agent_name, {})
297
+ available = detect_available_models()
298
+ preferred = config.get('preferred_model', 'claude')
299
+
300
+ # Resolve model availability
301
+ if preferred == 'gemini-cli' and not available.get('gemini-cli'):
302
+ preferred = 'claude'
303
+ elif preferred == 'codex-cli' and not available.get('codex-cli'):
304
+ preferred = 'claude'
305
+ elif preferred == 'domain-dependent':
306
+ preferred = 'claude'
307
+
308
+ params = {
309
+ 'category': config.get('task_category', 'unspecified-high'),
310
+ 'skills': config.get('skills', []),
311
+ 'preferred_model': preferred,
312
+ 'available_models': available,
313
+ 'model_version': config.get('model_version', 'unknown'),
314
+ }
315
+ if 'subagent_type' in config:
316
+ params['subagent_type'] = config['subagent_type']
317
+ return params
318
+
319
+
320
+ def detect_available_models() -> dict[str, bool]:
321
+ """Check which CLIs are available: codex-cli, gemini-cli.
322
+
323
+ Returns dict: {'claude': True, 'codex-cli': bool, 'gemini-cli': bool}
324
+ Caches result per process.
325
+ """
326
+ global _model_cache
327
+ if _model_cache is not None:
328
+ return _model_cache
329
+
330
+ result = {'claude': True} # Claude is always available
331
+ result['codex-cli'] = shutil.which('codex') is not None
332
+ result['gemini-cli'] = shutil.which('gemini') is not None
333
+ _model_cache = result
334
+ return result
335
+
336
+
337
+ def discover_mcp_tools() -> list[str]:
338
+ """Read MCP config to find available tool names.
339
+
340
+ Checks project-level and user-level Claude MCP configs for mcpServers keys.
341
+ Returns list of server names (not individual tool names).
342
+ """
343
+ mcp_servers = {}
344
+ project_dir = os.getcwd()
345
+
346
+ claude_dir = _claude_config_dir()
347
+ for mcp_loc in [
348
+ os.path.join(project_dir, '.mcp.json'),
349
+ os.path.join(claude_dir, '.mcp.json'),
350
+ os.path.join(claude_dir, 'settings.json'),
351
+ ]:
352
+ mcp_servers.update(_load_mcp_servers(mcp_loc))
353
+
354
+ return list(mcp_servers.keys())
355
+
356
+
357
+ # --- Custom Agent Loading (Task 2.4) ---
358
+
359
+
360
+ def load_custom_agents_into_registry(project_dir: str = ".") -> int:
361
+ """Load custom agents from user/project dirs into AGENT_REGISTRY.
362
+
363
+ If OMG_CUSTOM_AGENTS_ENABLED is disabled, does nothing.
364
+ Uses lazy import of runtime.custom_agent_loader to avoid circular deps.
365
+
366
+ Args:
367
+ project_dir: Project directory path.
368
+
369
+ Returns:
370
+ Number of custom agents loaded.
371
+ """
372
+ import sys as _sys
373
+
374
+ # Check feature flag via env var first (fast path)
375
+ env_val = os.environ.get("OMG_CUSTOM_AGENTS_ENABLED", "").lower()
376
+ if env_val in ("0", "false", "no"):
377
+ return 0
378
+
379
+ # If not explicitly enabled via env, check via _common
380
+ if env_val not in ("1", "true", "yes"):
381
+ try:
382
+ from _common import get_feature_flag # pyright: ignore[reportMissingImports]
383
+ if not get_feature_flag("CUSTOM_AGENTS", default=False):
384
+ return 0
385
+ except ImportError:
386
+ return 0 # Can't check flag → disabled
387
+
388
+ # Lazy import custom_agent_loader from runtime/
389
+ _runtime_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'runtime')
390
+ _runtime_dir = os.path.normpath(_runtime_dir)
391
+ if _runtime_dir not in _sys.path:
392
+ _sys.path.insert(0, _runtime_dir)
393
+
394
+ try:
395
+ from custom_agent_loader import load_custom_agents # pyright: ignore[reportMissingImports]
396
+ except ImportError:
397
+ return 0
398
+
399
+ custom_agents = load_custom_agents(project_dir)
400
+ count = 0
401
+
402
+ for agent in custom_agents:
403
+ if not agent.get("validated", False):
404
+ continue # Skip invalid agents
405
+
406
+ name = agent["name"]
407
+ AGENT_REGISTRY[name] = {
408
+ 'preferred_model': 'claude',
409
+ 'task_category': 'unspecified-high',
410
+ 'skills': [],
411
+ 'trigger_keywords': set(),
412
+ 'mcp_tools': [],
413
+ 'description': agent.get('description', ''),
414
+ 'agent_file': agent.get('file', ''),
415
+ 'model_version': 'claude-sonnet-4-5',
416
+ 'model_role': agent.get('model_role'),
417
+ 'source': 'custom',
418
+ 'level': agent.get('level', 'unknown'),
419
+ 'validated': True,
420
+ }
421
+ count += 1
422
+
423
+ return count