@trac3r/oh-my-god 2.2.11

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 (638) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/INSTALL-VERIFICATION-INDEX.md +51 -0
  3. package/LICENSE +21 -0
  4. package/OMG-setup.sh +2549 -0
  5. package/QUICK-REFERENCE.md +58 -0
  6. package/README.md +207 -0
  7. package/agents/__init__.py +1 -0
  8. package/agents/__pycache__/model_roles.cpython-313.pyc +0 -0
  9. package/agents/_model_roles.yaml +26 -0
  10. package/agents/designer.md +67 -0
  11. package/agents/explore.md +60 -0
  12. package/agents/model_roles.py +196 -0
  13. package/agents/omg-api-builder.md +23 -0
  14. package/agents/omg-architect-mode.md +41 -0
  15. package/agents/omg-architect.md +13 -0
  16. package/agents/omg-backend-engineer.md +41 -0
  17. package/agents/omg-critic.md +16 -0
  18. package/agents/omg-database-engineer.md +41 -0
  19. package/agents/omg-escalation-router.md +17 -0
  20. package/agents/omg-executor.md +12 -0
  21. package/agents/omg-frontend-designer.md +41 -0
  22. package/agents/omg-implement-mode.md +49 -0
  23. package/agents/omg-infra-engineer.md +41 -0
  24. package/agents/omg-qa-tester.md +16 -0
  25. package/agents/omg-research-mode.md +41 -0
  26. package/agents/omg-security-auditor.md +41 -0
  27. package/agents/omg-testing-engineer.md +41 -0
  28. package/agents/plan.md +80 -0
  29. package/agents/quick_task.md +64 -0
  30. package/agents/reviewer.md +83 -0
  31. package/agents/task.md +71 -0
  32. package/bin/omg +41 -0
  33. package/commands/OMG:ai-commit.md +113 -0
  34. package/commands/OMG:api-twin.md +22 -0
  35. package/commands/OMG:arch.md +313 -0
  36. package/commands/OMG:browser.md +29 -0
  37. package/commands/OMG:ccg.md +22 -0
  38. package/commands/OMG:compat.md +57 -0
  39. package/commands/OMG:cost.md +181 -0
  40. package/commands/OMG:crazy.md +125 -0
  41. package/commands/OMG:create-agent.md +183 -0
  42. package/commands/OMG:deep-plan.md +18 -0
  43. package/commands/OMG:deps.md +248 -0
  44. package/commands/OMG:diagnose-plugins.md +33 -0
  45. package/commands/OMG:doctor.md +37 -0
  46. package/commands/OMG:domain-init.md +11 -0
  47. package/commands/OMG:escalate.md +52 -0
  48. package/commands/OMG:forge.md +103 -0
  49. package/commands/OMG:health-check.md +48 -0
  50. package/commands/OMG:init.md +134 -0
  51. package/commands/OMG:issue.md +56 -0
  52. package/commands/OMG:mode.md +44 -0
  53. package/commands/OMG:playwright.md +17 -0
  54. package/commands/OMG:preflight.md +26 -0
  55. package/commands/OMG:preset.md +49 -0
  56. package/commands/OMG:profile-review.md +58 -0
  57. package/commands/OMG:project-init.md +11 -0
  58. package/commands/OMG:ralph-start.md +43 -0
  59. package/commands/OMG:ralph-stop.md +23 -0
  60. package/commands/OMG:security-check.md +28 -0
  61. package/commands/OMG:session-branch.md +101 -0
  62. package/commands/OMG:session-fork.md +57 -0
  63. package/commands/OMG:session-merge.md +138 -0
  64. package/commands/OMG:setup.md +82 -0
  65. package/commands/OMG:ship.md +18 -0
  66. package/commands/OMG:stats.md +225 -0
  67. package/commands/OMG:teams.md +54 -0
  68. package/commands/OMG:theme.md +44 -0
  69. package/commands/OMG:validate.md +59 -0
  70. package/commands/__init__.py +1 -0
  71. package/docs/command-surface.md +55 -0
  72. package/docs/install/claude-code.md +53 -0
  73. package/docs/install/codex.md +45 -0
  74. package/docs/install/gemini.md +43 -0
  75. package/docs/install/github-action.md +81 -0
  76. package/docs/install/github-app-required-checks.md +107 -0
  77. package/docs/install/github-app.md +161 -0
  78. package/docs/install/kimi.md +43 -0
  79. package/docs/install/opencode.md +38 -0
  80. package/docs/proof.md +182 -0
  81. package/hooks/__init__.py +0 -0
  82. package/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  83. package/hooks/__pycache__/_agent_registry.cpython-313.pyc +0 -0
  84. package/hooks/__pycache__/_analytics.cpython-313.pyc +0 -0
  85. package/hooks/__pycache__/_budget.cpython-313.pyc +0 -0
  86. package/hooks/__pycache__/_common.cpython-313.pyc +0 -0
  87. package/hooks/__pycache__/_compression_optimizer.cpython-313.pyc +0 -0
  88. package/hooks/__pycache__/_cost_ledger.cpython-313.pyc +0 -0
  89. package/hooks/__pycache__/_learnings.cpython-313.pyc +0 -0
  90. package/hooks/__pycache__/_memory.cpython-313.pyc +0 -0
  91. package/hooks/__pycache__/_post_write.cpython-313.pyc +0 -0
  92. package/hooks/__pycache__/_protected_context.cpython-313.pyc +0 -0
  93. package/hooks/__pycache__/_token_counter.cpython-313.pyc +0 -0
  94. package/hooks/__pycache__/branch_manager.cpython-313.pyc +0 -0
  95. package/hooks/__pycache__/budget_governor.cpython-313.pyc +0 -0
  96. package/hooks/__pycache__/circuit-breaker.cpython-313.pyc +0 -0
  97. package/hooks/__pycache__/compression_feedback.cpython-313.pyc +0 -0
  98. package/hooks/__pycache__/config-guard.cpython-313.pyc +0 -0
  99. package/hooks/__pycache__/context_pressure.cpython-313.pyc +0 -0
  100. package/hooks/__pycache__/credential_store.cpython-313.pyc +0 -0
  101. package/hooks/__pycache__/fetch-rate-limits.cpython-313.pyc +0 -0
  102. package/hooks/__pycache__/firewall.cpython-313.pyc +0 -0
  103. package/hooks/__pycache__/hashline-formatter-bridge.cpython-313.pyc +0 -0
  104. package/hooks/__pycache__/hashline-injector.cpython-313.pyc +0 -0
  105. package/hooks/__pycache__/hashline-validator.cpython-313.pyc +0 -0
  106. package/hooks/__pycache__/idle-detector.cpython-313.pyc +0 -0
  107. package/hooks/__pycache__/instructions-loaded.cpython-313.pyc +0 -0
  108. package/hooks/__pycache__/intentgate-keyword-detector.cpython-313.pyc +0 -0
  109. package/hooks/__pycache__/magic-keyword-router.cpython-313.pyc +0 -0
  110. package/hooks/__pycache__/policy_engine.cpython-313.pyc +0 -0
  111. package/hooks/__pycache__/post-tool-failure.cpython-313.pyc +0 -0
  112. package/hooks/__pycache__/post-write.cpython-313.pyc +0 -0
  113. package/hooks/__pycache__/post_write.cpython-313.pyc +0 -0
  114. package/hooks/__pycache__/pre-compact.cpython-313.pyc +0 -0
  115. package/hooks/__pycache__/pre-tool-inject.cpython-313.pyc +0 -0
  116. package/hooks/__pycache__/prompt-enhancer.cpython-313.pyc +0 -0
  117. package/hooks/__pycache__/quality-runner.cpython-313.pyc +0 -0
  118. package/hooks/__pycache__/query.cpython-313.pyc +0 -0
  119. package/hooks/__pycache__/secret-guard.cpython-313.pyc +0 -0
  120. package/hooks/__pycache__/secret_audit.cpython-313.pyc +0 -0
  121. package/hooks/__pycache__/security_validators.cpython-313.pyc +0 -0
  122. package/hooks/__pycache__/session-end-capture.cpython-313.pyc +0 -0
  123. package/hooks/__pycache__/session-start.cpython-313.pyc +0 -0
  124. package/hooks/__pycache__/setup_wizard.cpython-313.pyc +0 -0
  125. package/hooks/__pycache__/shadow_manager.cpython-313.pyc +0 -0
  126. package/hooks/__pycache__/state_migration.cpython-313.pyc +0 -0
  127. package/hooks/__pycache__/stop-gate.cpython-313.pyc +0 -0
  128. package/hooks/__pycache__/stop_dispatcher.cpython-313.pyc +0 -0
  129. package/hooks/__pycache__/tdd-gate.cpython-313.pyc +0 -0
  130. package/hooks/__pycache__/terms-guard.cpython-313.pyc +0 -0
  131. package/hooks/__pycache__/test-validator.cpython-313.pyc +0 -0
  132. package/hooks/__pycache__/test_generator_hook.cpython-313.pyc +0 -0
  133. package/hooks/__pycache__/todo-state-tracker.cpython-313.pyc +0 -0
  134. package/hooks/__pycache__/tool-ledger.cpython-313.pyc +0 -0
  135. package/hooks/__pycache__/trust_review.cpython-313.pyc +0 -0
  136. package/hooks/__pycache__/user-prompt-submit.cpython-313.pyc +0 -0
  137. package/hooks/_agent_registry.py +481 -0
  138. package/hooks/_analytics.py +291 -0
  139. package/hooks/_budget.py +31 -0
  140. package/hooks/_common.py +761 -0
  141. package/hooks/_compression_optimizer.py +119 -0
  142. package/hooks/_cost_ledger.py +176 -0
  143. package/hooks/_learnings.py +126 -0
  144. package/hooks/_memory.py +103 -0
  145. package/hooks/_post_write.py +46 -0
  146. package/hooks/_protected_context.py +150 -0
  147. package/hooks/_token_counter.py +221 -0
  148. package/hooks/branch_manager.py +255 -0
  149. package/hooks/budget_governor.py +326 -0
  150. package/hooks/circuit-breaker.py +270 -0
  151. package/hooks/compression_feedback.py +254 -0
  152. package/hooks/config-guard.py +193 -0
  153. package/hooks/context_pressure.py +119 -0
  154. package/hooks/credential_store.py +970 -0
  155. package/hooks/fetch-rate-limits.py +212 -0
  156. package/hooks/firewall.py +323 -0
  157. package/hooks/hashline-formatter-bridge.py +224 -0
  158. package/hooks/hashline-injector.py +273 -0
  159. package/hooks/hashline-validator.py +216 -0
  160. package/hooks/idle-detector.py +97 -0
  161. package/hooks/instructions-loaded.py +26 -0
  162. package/hooks/intentgate-keyword-detector.py +200 -0
  163. package/hooks/magic-keyword-router.py +195 -0
  164. package/hooks/policy_engine.py +767 -0
  165. package/hooks/post-tool-failure.py +19 -0
  166. package/hooks/post-write.py +233 -0
  167. package/hooks/pre-compact.py +470 -0
  168. package/hooks/pre-tool-inject.py +98 -0
  169. package/hooks/prompt-enhancer.py +879 -0
  170. package/hooks/quality-runner.py +191 -0
  171. package/hooks/query.py +512 -0
  172. package/hooks/secret-guard.py +120 -0
  173. package/hooks/secret_audit.py +144 -0
  174. package/hooks/security_validators.py +93 -0
  175. package/hooks/session-end-capture.py +505 -0
  176. package/hooks/session-start.py +261 -0
  177. package/hooks/setup_wizard.py +1101 -0
  178. package/hooks/shadow_manager.py +476 -0
  179. package/hooks/state_migration.py +228 -0
  180. package/hooks/stop-gate.py +7 -0
  181. package/hooks/stop_dispatcher.py +1259 -0
  182. package/hooks/tdd-gate.py +10 -0
  183. package/hooks/terms-guard.py +98 -0
  184. package/hooks/test-validator.py +462 -0
  185. package/hooks/test_generator_hook.py +123 -0
  186. package/hooks/todo-state-tracker.py +114 -0
  187. package/hooks/tool-ledger.py +165 -0
  188. package/hooks/trust_review.py +662 -0
  189. package/hooks/user-prompt-submit.py +12 -0
  190. package/hud/omg-hud.mjs +1571 -0
  191. package/lab/__init__.py +1 -0
  192. package/lab/__pycache__/__init__.cpython-313.pyc +0 -0
  193. package/lab/__pycache__/axolotl_adapter.cpython-313.pyc +0 -0
  194. package/lab/__pycache__/forge_runner.cpython-313.pyc +0 -0
  195. package/lab/__pycache__/gazebo_adapter.cpython-313.pyc +0 -0
  196. package/lab/__pycache__/isaac_gym_adapter.cpython-313.pyc +0 -0
  197. package/lab/__pycache__/mock_isaac_env.cpython-313.pyc +0 -0
  198. package/lab/__pycache__/pipeline.cpython-313.pyc +0 -0
  199. package/lab/__pycache__/policies.cpython-313.pyc +0 -0
  200. package/lab/__pycache__/pybullet_adapter.cpython-313.pyc +0 -0
  201. package/lab/axolotl_adapter.py +531 -0
  202. package/lab/forge_runner.py +103 -0
  203. package/lab/gazebo_adapter.py +168 -0
  204. package/lab/isaac_gym_adapter.py +190 -0
  205. package/lab/mock_isaac_env.py +47 -0
  206. package/lab/pipeline.py +712 -0
  207. package/lab/policies.py +52 -0
  208. package/lab/pybullet_adapter.py +192 -0
  209. package/package.json +61 -0
  210. package/plugins/README.md +78 -0
  211. package/plugins/__init__.py +1 -0
  212. package/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  213. package/plugins/advanced/commands/OMG-code-review.md +114 -0
  214. package/plugins/advanced/commands/OMG-deep-plan.md +266 -0
  215. package/plugins/advanced/commands/OMG-handoff.md +115 -0
  216. package/plugins/advanced/commands/OMG-learn.md +110 -0
  217. package/plugins/advanced/commands/OMG-maintainer.md +31 -0
  218. package/plugins/advanced/commands/OMG-ralph-start.md +43 -0
  219. package/plugins/advanced/commands/OMG-ralph-stop.md +23 -0
  220. package/plugins/advanced/commands/OMG-security-review.md +16 -0
  221. package/plugins/advanced/commands/OMG-sequential-thinking.md +20 -0
  222. package/plugins/advanced/commands/OMG-ship.md +46 -0
  223. package/plugins/advanced/commands/OMG:code-review.md +114 -0
  224. package/plugins/advanced/commands/OMG:deep-plan.md +266 -0
  225. package/plugins/advanced/commands/OMG:handoff.md +115 -0
  226. package/plugins/advanced/commands/OMG:learn.md +110 -0
  227. package/plugins/advanced/commands/OMG:maintainer.md +31 -0
  228. package/plugins/advanced/commands/OMG:ralph-start.md +43 -0
  229. package/plugins/advanced/commands/OMG:ralph-stop.md +23 -0
  230. package/plugins/advanced/commands/OMG:security-review.md +16 -0
  231. package/plugins/advanced/commands/OMG:sequential-thinking.md +20 -0
  232. package/plugins/advanced/commands/OMG:ship.md +46 -0
  233. package/plugins/advanced/plugin.json +104 -0
  234. package/plugins/core/plugin.json +204 -0
  235. package/plugins/dephealth/__init__.py +0 -0
  236. package/plugins/dephealth/__pycache__/__init__.cpython-313.pyc +0 -0
  237. package/plugins/dephealth/__pycache__/cve_scanner.cpython-313.pyc +0 -0
  238. package/plugins/dephealth/__pycache__/license_checker.cpython-313.pyc +0 -0
  239. package/plugins/dephealth/__pycache__/manifest_detector.cpython-313.pyc +0 -0
  240. package/plugins/dephealth/__pycache__/vuln_analyzer.cpython-313.pyc +0 -0
  241. package/plugins/dephealth/cve_scanner.py +279 -0
  242. package/plugins/dephealth/license_checker.py +135 -0
  243. package/plugins/dephealth/manifest_detector.py +423 -0
  244. package/plugins/dephealth/vuln_analyzer.py +176 -0
  245. package/plugins/testgen/__init__.py +0 -0
  246. package/plugins/testgen/__pycache__/__init__.cpython-313.pyc +0 -0
  247. package/plugins/testgen/__pycache__/codamosa_engine.cpython-313.pyc +0 -0
  248. package/plugins/testgen/__pycache__/edge_case_synthesizer.cpython-313.pyc +0 -0
  249. package/plugins/testgen/__pycache__/framework_detector.cpython-313.pyc +0 -0
  250. package/plugins/testgen/__pycache__/skeleton_generator.cpython-313.pyc +0 -0
  251. package/plugins/testgen/codamosa_engine.py +402 -0
  252. package/plugins/testgen/edge_case_synthesizer.py +184 -0
  253. package/plugins/testgen/framework_detector.py +271 -0
  254. package/plugins/testgen/skeleton_generator.py +219 -0
  255. package/plugins/viz/__init__.py +0 -0
  256. package/plugins/viz/__pycache__/__init__.cpython-313.pyc +0 -0
  257. package/plugins/viz/__pycache__/ast_parser.cpython-313.pyc +0 -0
  258. package/plugins/viz/__pycache__/diagram_generator.cpython-313.pyc +0 -0
  259. package/plugins/viz/__pycache__/graph_builder.cpython-313.pyc +0 -0
  260. package/plugins/viz/__pycache__/native_parsers.cpython-313.pyc +0 -0
  261. package/plugins/viz/__pycache__/regex_parser.cpython-313.pyc +0 -0
  262. package/plugins/viz/ast_parser.py +139 -0
  263. package/plugins/viz/diagram_generator.py +192 -0
  264. package/plugins/viz/graph_builder.py +444 -0
  265. package/plugins/viz/native_parsers.py +259 -0
  266. package/plugins/viz/regex_parser.py +112 -0
  267. package/pyproject.toml +143 -0
  268. package/registry/__init__.py +1 -0
  269. package/registry/__pycache__/__init__.cpython-313.pyc +0 -0
  270. package/registry/__pycache__/approval_artifact.cpython-313.pyc +0 -0
  271. package/registry/__pycache__/verify_artifact.cpython-313.pyc +0 -0
  272. package/registry/approval_artifact.py +236 -0
  273. package/registry/bundles/algorithms.yaml +45 -0
  274. package/registry/bundles/api-twin.yaml +48 -0
  275. package/registry/bundles/ast-pack.yaml +80 -0
  276. package/registry/bundles/claim-judge.yaml +49 -0
  277. package/registry/bundles/control-plane.yaml +192 -0
  278. package/registry/bundles/data-lineage.yaml +47 -0
  279. package/registry/bundles/delta-classifier.yaml +47 -0
  280. package/registry/bundles/eval-gate.yaml +47 -0
  281. package/registry/bundles/hash-edit.yaml +73 -0
  282. package/registry/bundles/health.yaml +45 -0
  283. package/registry/bundles/hook-governor.yaml +101 -0
  284. package/registry/bundles/incident-replay.yaml +47 -0
  285. package/registry/bundles/lsp-pack.yaml +80 -0
  286. package/registry/bundles/mcp-fabric.yaml +53 -0
  287. package/registry/bundles/plan-council.yaml +56 -0
  288. package/registry/bundles/preflight.yaml +48 -0
  289. package/registry/bundles/proof-gate.yaml +49 -0
  290. package/registry/bundles/remote-supervisor.yaml +49 -0
  291. package/registry/bundles/robotics.yaml +45 -0
  292. package/registry/bundles/secure-worktree-pipeline.yaml +69 -0
  293. package/registry/bundles/security-check.yaml +50 -0
  294. package/registry/bundles/terminal-lane.yaml +61 -0
  295. package/registry/bundles/test-intent-lock.yaml +49 -0
  296. package/registry/bundles/tracebank.yaml +47 -0
  297. package/registry/bundles/vision.yaml +45 -0
  298. package/registry/omg-capability.schema.json +378 -0
  299. package/registry/policy-packs/airgapped.lock.json +11 -0
  300. package/registry/policy-packs/airgapped.signature.json +10 -0
  301. package/registry/policy-packs/airgapped.yaml +16 -0
  302. package/registry/policy-packs/fintech.lock.json +11 -0
  303. package/registry/policy-packs/fintech.signature.json +10 -0
  304. package/registry/policy-packs/fintech.yaml +15 -0
  305. package/registry/policy-packs/locked-prod.lock.json +11 -0
  306. package/registry/policy-packs/locked-prod.signature.json +10 -0
  307. package/registry/policy-packs/locked-prod.yaml +18 -0
  308. package/registry/trusted_signers.json +44 -0
  309. package/registry/verify_artifact.py +493 -0
  310. package/runtime/__init__.py +36 -0
  311. package/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
  312. package/runtime/__pycache__/adoption.cpython-313.pyc +0 -0
  313. package/runtime/__pycache__/agent_selector.cpython-313.pyc +0 -0
  314. package/runtime/__pycache__/api_twin.cpython-313.pyc +0 -0
  315. package/runtime/__pycache__/architecture_signal.cpython-313.pyc +0 -0
  316. package/runtime/__pycache__/artifact_parsers.cpython-313.pyc +0 -0
  317. package/runtime/__pycache__/asset_loader.cpython-313.pyc +0 -0
  318. package/runtime/__pycache__/background_verification.cpython-313.pyc +0 -0
  319. package/runtime/__pycache__/budget_envelopes.cpython-313.pyc +0 -0
  320. package/runtime/__pycache__/business_workflow.cpython-313.pyc +0 -0
  321. package/runtime/__pycache__/canonical_surface.cpython-313.pyc +0 -0
  322. package/runtime/__pycache__/canonical_taxonomy.cpython-313.pyc +0 -0
  323. package/runtime/__pycache__/claim_judge.cpython-313.pyc +0 -0
  324. package/runtime/__pycache__/cli_provider.cpython-313.pyc +0 -0
  325. package/runtime/__pycache__/compat.cpython-313.pyc +0 -0
  326. package/runtime/__pycache__/complexity_scorer.cpython-313.pyc +0 -0
  327. package/runtime/__pycache__/compliance_governor.cpython-313.pyc +0 -0
  328. package/runtime/__pycache__/config_transaction.cpython-313.pyc +0 -0
  329. package/runtime/__pycache__/context_compiler.cpython-313.pyc +0 -0
  330. package/runtime/__pycache__/context_engine.cpython-313.pyc +0 -0
  331. package/runtime/__pycache__/context_limits.cpython-313.pyc +0 -0
  332. package/runtime/__pycache__/contract_compiler.cpython-313.pyc +0 -0
  333. package/runtime/__pycache__/custom_agent_loader.cpython-313.pyc +0 -0
  334. package/runtime/__pycache__/data_lineage.cpython-313.pyc +0 -0
  335. package/runtime/__pycache__/defense_state.cpython-313.pyc +0 -0
  336. package/runtime/__pycache__/delta_classifier.cpython-313.pyc +0 -0
  337. package/runtime/__pycache__/dispatcher.cpython-313.pyc +0 -0
  338. package/runtime/__pycache__/doc_generator.cpython-313.pyc +0 -0
  339. package/runtime/__pycache__/domain_packs.cpython-313.pyc +0 -0
  340. package/runtime/__pycache__/ecosystem.cpython-313.pyc +0 -0
  341. package/runtime/__pycache__/equalizer.cpython-313.pyc +0 -0
  342. package/runtime/__pycache__/eval_gate.cpython-313.pyc +0 -0
  343. package/runtime/__pycache__/evidence_narrator.cpython-313.pyc +0 -0
  344. package/runtime/__pycache__/evidence_query.cpython-313.pyc +0 -0
  345. package/runtime/__pycache__/evidence_registry.cpython-313.pyc +0 -0
  346. package/runtime/__pycache__/evidence_requirements.cpython-313.pyc +0 -0
  347. package/runtime/__pycache__/exec_kernel.cpython-313.pyc +0 -0
  348. package/runtime/__pycache__/explainer_formatter.cpython-313.pyc +0 -0
  349. package/runtime/__pycache__/feature_registry.cpython-313.pyc +0 -0
  350. package/runtime/__pycache__/forge_agents.cpython-313.pyc +0 -0
  351. package/runtime/__pycache__/forge_contracts.cpython-313.pyc +0 -0
  352. package/runtime/__pycache__/forge_domains.cpython-313.pyc +0 -0
  353. package/runtime/__pycache__/forge_run_id.cpython-313.pyc +0 -0
  354. package/runtime/__pycache__/github_integration.cpython-313.pyc +0 -0
  355. package/runtime/__pycache__/github_review_bot.cpython-313.pyc +0 -0
  356. package/runtime/__pycache__/github_review_contract.cpython-313.pyc +0 -0
  357. package/runtime/__pycache__/github_review_formatter.cpython-313.pyc +0 -0
  358. package/runtime/__pycache__/guide_assert.cpython-313.pyc +0 -0
  359. package/runtime/__pycache__/hook_governor.cpython-313.pyc +0 -0
  360. package/runtime/__pycache__/host_parity.cpython-313.pyc +0 -0
  361. package/runtime/__pycache__/incident_replay.cpython-313.pyc +0 -0
  362. package/runtime/__pycache__/install_planner.cpython-313.pyc +0 -0
  363. package/runtime/__pycache__/interaction_journal.cpython-313.pyc +0 -0
  364. package/runtime/__pycache__/issue_surface.cpython-313.pyc +0 -0
  365. package/runtime/__pycache__/legacy_compat.cpython-313.pyc +0 -0
  366. package/runtime/__pycache__/mcp_config_writers.cpython-313.pyc +0 -0
  367. package/runtime/__pycache__/mcp_lifecycle.cpython-313.pyc +0 -0
  368. package/runtime/__pycache__/mcp_memory_server.cpython-313.pyc +0 -0
  369. package/runtime/__pycache__/memory_store.cpython-313.pyc +0 -0
  370. package/runtime/__pycache__/merge_writer.cpython-313.pyc +0 -0
  371. package/runtime/__pycache__/music_omr_testbed.cpython-313.pyc +0 -0
  372. package/runtime/__pycache__/mutation_gate.cpython-313.pyc +0 -0
  373. package/runtime/__pycache__/omc_compat.cpython-313.pyc +0 -0
  374. package/runtime/__pycache__/omg_browser_cli.cpython-313.pyc +0 -0
  375. package/runtime/__pycache__/omg_mcp_server.cpython-313.pyc +0 -0
  376. package/runtime/__pycache__/opus_plan.cpython-313.pyc +0 -0
  377. package/runtime/__pycache__/playwright_adapter.cpython-313.pyc +0 -0
  378. package/runtime/__pycache__/playwright_pack.cpython-313.pyc +0 -0
  379. package/runtime/__pycache__/plugin_diagnostics.cpython-313.pyc +0 -0
  380. package/runtime/__pycache__/plugin_interop.cpython-313.pyc +0 -0
  381. package/runtime/__pycache__/policy_pack_loader.cpython-313.pyc +0 -0
  382. package/runtime/__pycache__/preflight.cpython-313.pyc +0 -0
  383. package/runtime/__pycache__/profile_io.cpython-313.pyc +0 -0
  384. package/runtime/__pycache__/prompt_compiler.cpython-313.pyc +0 -0
  385. package/runtime/__pycache__/proof_chain.cpython-313.pyc +0 -0
  386. package/runtime/__pycache__/proof_gate.cpython-313.pyc +0 -0
  387. package/runtime/__pycache__/provider_parity_eval.cpython-313.pyc +0 -0
  388. package/runtime/__pycache__/release_artifact_audit.cpython-313.pyc +0 -0
  389. package/runtime/__pycache__/release_run_coordinator.cpython-313.pyc +0 -0
  390. package/runtime/__pycache__/release_surface_compiler.cpython-313.pyc +0 -0
  391. package/runtime/__pycache__/release_surface_registry.cpython-313.pyc +0 -0
  392. package/runtime/__pycache__/release_surfaces.cpython-313.pyc +0 -0
  393. package/runtime/__pycache__/remote_supervisor.cpython-313.pyc +0 -0
  394. package/runtime/__pycache__/repro_pack.cpython-313.pyc +0 -0
  395. package/runtime/__pycache__/rollback_manifest.cpython-313.pyc +0 -0
  396. package/runtime/__pycache__/router_critics.cpython-313.pyc +0 -0
  397. package/runtime/__pycache__/router_executor.cpython-313.pyc +0 -0
  398. package/runtime/__pycache__/router_selector.cpython-313.pyc +0 -0
  399. package/runtime/__pycache__/runtime_contracts.cpython-313.pyc +0 -0
  400. package/runtime/__pycache__/runtime_profile.cpython-313.pyc +0 -0
  401. package/runtime/__pycache__/security_check.cpython-313.pyc +0 -0
  402. package/runtime/__pycache__/session_health.cpython-313.pyc +0 -0
  403. package/runtime/__pycache__/skill_evolution.cpython-313.pyc +0 -0
  404. package/runtime/__pycache__/skill_registry.cpython-313.pyc +0 -0
  405. package/runtime/__pycache__/subagent_dispatcher.cpython-313.pyc +0 -0
  406. package/runtime/__pycache__/subscription_tiers.cpython-313.pyc +0 -0
  407. package/runtime/__pycache__/team_router.cpython-313.pyc +0 -0
  408. package/runtime/__pycache__/test_intent_lock.cpython-313-pytest-9.0.2.pyc +0 -0
  409. package/runtime/__pycache__/test_intent_lock.cpython-313.pyc +0 -0
  410. package/runtime/__pycache__/tmux_session_manager.cpython-313.pyc +0 -0
  411. package/runtime/__pycache__/tool_fabric.cpython-313.pyc +0 -0
  412. package/runtime/__pycache__/tool_plan_gate.cpython-313.pyc +0 -0
  413. package/runtime/__pycache__/tool_relevance.cpython-313.pyc +0 -0
  414. package/runtime/__pycache__/tracebank.cpython-313.pyc +0 -0
  415. package/runtime/__pycache__/untrusted_content.cpython-313.pyc +0 -0
  416. package/runtime/__pycache__/validate.cpython-313.pyc +0 -0
  417. package/runtime/__pycache__/verdict_schema.cpython-313.pyc +0 -0
  418. package/runtime/__pycache__/verification_controller.cpython-313.pyc +0 -0
  419. package/runtime/__pycache__/verification_loop.cpython-313.pyc +0 -0
  420. package/runtime/__pycache__/vision_artifacts.cpython-313.pyc +0 -0
  421. package/runtime/__pycache__/vision_cache.cpython-313.pyc +0 -0
  422. package/runtime/__pycache__/vision_jobs.cpython-313.pyc +0 -0
  423. package/runtime/__pycache__/worker_watchdog.cpython-313.pyc +0 -0
  424. package/runtime/adapters/__init__.py +13 -0
  425. package/runtime/adapters/__pycache__/__init__.cpython-313.pyc +0 -0
  426. package/runtime/adapters/__pycache__/claude.cpython-313.pyc +0 -0
  427. package/runtime/adapters/__pycache__/gpt.cpython-313.pyc +0 -0
  428. package/runtime/adapters/__pycache__/local.cpython-313.pyc +0 -0
  429. package/runtime/adapters/claude.py +63 -0
  430. package/runtime/adapters/gpt.py +56 -0
  431. package/runtime/adapters/local.py +56 -0
  432. package/runtime/adoption.py +280 -0
  433. package/runtime/api_twin.py +450 -0
  434. package/runtime/architecture_signal.py +226 -0
  435. package/runtime/artifact_parsers.py +161 -0
  436. package/runtime/asset_loader.py +62 -0
  437. package/runtime/background_verification.py +178 -0
  438. package/runtime/budget_envelopes.py +398 -0
  439. package/runtime/business_workflow.py +234 -0
  440. package/runtime/canonical_surface.py +53 -0
  441. package/runtime/canonical_taxonomy.py +27 -0
  442. package/runtime/claim_judge.py +648 -0
  443. package/runtime/cli_provider.py +105 -0
  444. package/runtime/compat.py +2222 -0
  445. package/runtime/complexity_scorer.py +148 -0
  446. package/runtime/compliance_governor.py +505 -0
  447. package/runtime/config_transaction.py +304 -0
  448. package/runtime/context_compiler.py +131 -0
  449. package/runtime/context_engine.py +708 -0
  450. package/runtime/context_limits.py +363 -0
  451. package/runtime/contract_compiler.py +3664 -0
  452. package/runtime/custom_agent_loader.py +366 -0
  453. package/runtime/data_lineage.py +244 -0
  454. package/runtime/defense_state.py +261 -0
  455. package/runtime/delta_classifier.py +231 -0
  456. package/runtime/dispatcher.py +47 -0
  457. package/runtime/doc_generator.py +319 -0
  458. package/runtime/domain_packs.py +75 -0
  459. package/runtime/ecosystem.py +371 -0
  460. package/runtime/equalizer.py +268 -0
  461. package/runtime/eval_gate.py +96 -0
  462. package/runtime/evidence_narrator.py +147 -0
  463. package/runtime/evidence_query.py +303 -0
  464. package/runtime/evidence_registry.py +16 -0
  465. package/runtime/evidence_requirements.py +157 -0
  466. package/runtime/exec_kernel.py +267 -0
  467. package/runtime/explainer_formatter.py +82 -0
  468. package/runtime/feature_registry.py +109 -0
  469. package/runtime/forge_agents.py +915 -0
  470. package/runtime/forge_contracts.py +519 -0
  471. package/runtime/forge_domains.py +68 -0
  472. package/runtime/forge_run_id.py +86 -0
  473. package/runtime/guide_assert.py +135 -0
  474. package/runtime/hook_governor.py +156 -0
  475. package/runtime/host_parity.py +373 -0
  476. package/runtime/incident_replay.py +310 -0
  477. package/runtime/install_planner.py +617 -0
  478. package/runtime/interaction_journal.py +566 -0
  479. package/runtime/issue_surface.py +472 -0
  480. package/runtime/legacy_compat.py +7 -0
  481. package/runtime/mcp_config_writers.py +360 -0
  482. package/runtime/mcp_lifecycle.py +175 -0
  483. package/runtime/mcp_memory_server.py +220 -0
  484. package/runtime/memory_parsers/__init__.py +0 -0
  485. package/runtime/memory_parsers/__pycache__/__init__.cpython-313.pyc +0 -0
  486. package/runtime/memory_parsers/__pycache__/chatgpt_parser.cpython-313.pyc +0 -0
  487. package/runtime/memory_parsers/__pycache__/claude_import.cpython-313.pyc +0 -0
  488. package/runtime/memory_parsers/__pycache__/export.cpython-313.pyc +0 -0
  489. package/runtime/memory_parsers/__pycache__/gemini_import.cpython-313.pyc +0 -0
  490. package/runtime/memory_parsers/__pycache__/kimi_import.cpython-313.pyc +0 -0
  491. package/runtime/memory_parsers/chatgpt_parser.py +257 -0
  492. package/runtime/memory_parsers/claude_import.py +107 -0
  493. package/runtime/memory_parsers/export.py +97 -0
  494. package/runtime/memory_parsers/gemini_import.py +91 -0
  495. package/runtime/memory_parsers/kimi_import.py +91 -0
  496. package/runtime/memory_store.py +1182 -0
  497. package/runtime/merge_writer.py +445 -0
  498. package/runtime/music_omr_testbed.py +336 -0
  499. package/runtime/mutation_gate.py +320 -0
  500. package/runtime/omc_compat.py +7 -0
  501. package/runtime/omg_browser_cli.py +95 -0
  502. package/runtime/omg_compat_contract_snapshot.json +936 -0
  503. package/runtime/omg_contract_snapshot.json +936 -0
  504. package/runtime/omg_mcp_server.py +306 -0
  505. package/runtime/playwright_adapter.py +39 -0
  506. package/runtime/playwright_pack.py +253 -0
  507. package/runtime/plugin_diagnostics.py +308 -0
  508. package/runtime/plugin_interop.py +1060 -0
  509. package/runtime/policy_pack_loader.py +147 -0
  510. package/runtime/preflight.py +135 -0
  511. package/runtime/profile_io.py +328 -0
  512. package/runtime/proof_chain.py +472 -0
  513. package/runtime/proof_gate.py +442 -0
  514. package/runtime/provider_parity_eval.py +109 -0
  515. package/runtime/providers/__init__.py +0 -0
  516. package/runtime/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  517. package/runtime/providers/__pycache__/codex_provider.cpython-313.pyc +0 -0
  518. package/runtime/providers/__pycache__/gemini_provider.cpython-313.pyc +0 -0
  519. package/runtime/providers/__pycache__/kimi_provider.cpython-313.pyc +0 -0
  520. package/runtime/providers/__pycache__/opencode_provider.cpython-313.pyc +0 -0
  521. package/runtime/providers/codex_provider.py +129 -0
  522. package/runtime/providers/gemini_provider.py +143 -0
  523. package/runtime/providers/kimi_provider.py +167 -0
  524. package/runtime/providers/opencode_provider.py +99 -0
  525. package/runtime/release_artifact_audit.py +556 -0
  526. package/runtime/release_run_coordinator.py +574 -0
  527. package/runtime/release_surface_compiler.py +643 -0
  528. package/runtime/release_surface_registry.py +283 -0
  529. package/runtime/release_surfaces.py +320 -0
  530. package/runtime/remote_supervisor.py +79 -0
  531. package/runtime/repro_pack.py +398 -0
  532. package/runtime/rollback_manifest.py +143 -0
  533. package/runtime/router_critics.py +229 -0
  534. package/runtime/router_executor.py +142 -0
  535. package/runtime/router_selector.py +99 -0
  536. package/runtime/runtime_contracts.py +292 -0
  537. package/runtime/runtime_profile.py +133 -0
  538. package/runtime/security_check.py +1094 -0
  539. package/runtime/session_health.py +546 -0
  540. package/runtime/skill_evolution.py +221 -0
  541. package/runtime/skill_registry.py +53 -0
  542. package/runtime/subagent_dispatcher.py +604 -0
  543. package/runtime/subscription_tiers.py +258 -0
  544. package/runtime/team_router.py +1399 -0
  545. package/runtime/test_intent_lock.py +543 -0
  546. package/runtime/tmux_session_manager.py +172 -0
  547. package/runtime/tool_fabric.py +570 -0
  548. package/runtime/tool_plan_gate.py +460 -0
  549. package/runtime/tracebank.py +125 -0
  550. package/runtime/untrusted_content.py +360 -0
  551. package/runtime/validate.py +293 -0
  552. package/runtime/verdict_schema.py +198 -0
  553. package/runtime/verification_controller.py +235 -0
  554. package/runtime/verification_loop.py +73 -0
  555. package/runtime/vision_artifacts.py +31 -0
  556. package/runtime/vision_cache.py +38 -0
  557. package/runtime/vision_jobs.py +92 -0
  558. package/runtime/worker_watchdog.py +526 -0
  559. package/scripts/__pycache__/audit-published-artifact.cpython-313.pyc +0 -0
  560. package/scripts/__pycache__/check-doc-parity.cpython-313.pyc +0 -0
  561. package/scripts/__pycache__/check-omg-standalone-clean.cpython-313.pyc +0 -0
  562. package/scripts/__pycache__/github_review_helpers.cpython-313.pyc +0 -0
  563. package/scripts/__pycache__/omg.cpython-313.pyc +0 -0
  564. package/scripts/__pycache__/prepare-release-proof-fixtures.cpython-313.pyc +0 -0
  565. package/scripts/__pycache__/sync-release-identity.cpython-313.pyc +0 -0
  566. package/scripts/__pycache__/validate-release-identity.cpython-313.pyc +0 -0
  567. package/scripts/audit-published-artifact.py +59 -0
  568. package/scripts/check-omg-compat-contract-snapshot.py +137 -0
  569. package/scripts/check-omg-contract-snapshot.py +12 -0
  570. package/scripts/check-omg-public-ready.py +273 -0
  571. package/scripts/check-omg-standalone-clean.py +133 -0
  572. package/scripts/emit_host_parity.py +72 -0
  573. package/scripts/legacy_to_omg_migrate.py +29 -0
  574. package/scripts/migrate-legacy.py +464 -0
  575. package/scripts/omc_to_omg_migrate.py +12 -0
  576. package/scripts/omg.py +2962 -0
  577. package/scripts/pre-release-check.sh +38 -0
  578. package/scripts/prepare-release-proof-fixtures.py +602 -0
  579. package/scripts/print-canonical-version.py +80 -0
  580. package/scripts/settings-merge.py +289 -0
  581. package/scripts/sync-release-identity.py +481 -0
  582. package/scripts/validate-release-identity.py +632 -0
  583. package/scripts/verify-no-omc.sh +5 -0
  584. package/scripts/verify-standalone.sh +35 -0
  585. package/settings.json +751 -0
  586. package/tools/__init__.py +2 -0
  587. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  588. package/tools/__pycache__/browser_consent.cpython-313.pyc +0 -0
  589. package/tools/__pycache__/browser_stealth.cpython-313.pyc +0 -0
  590. package/tools/__pycache__/browser_tool.cpython-313.pyc +0 -0
  591. package/tools/__pycache__/changelog_generator.cpython-313.pyc +0 -0
  592. package/tools/__pycache__/commit_splitter.cpython-313.pyc +0 -0
  593. package/tools/__pycache__/config_discovery.cpython-313.pyc +0 -0
  594. package/tools/__pycache__/config_merger.cpython-313.pyc +0 -0
  595. package/tools/__pycache__/dashboard_generator.cpython-313.pyc +0 -0
  596. package/tools/__pycache__/git_inspector.cpython-313.pyc +0 -0
  597. package/tools/__pycache__/lsp_client.cpython-313.pyc +0 -0
  598. package/tools/__pycache__/lsp_operations.cpython-313.pyc +0 -0
  599. package/tools/__pycache__/pr_generator.cpython-313.pyc +0 -0
  600. package/tools/__pycache__/python_repl.cpython-313.pyc +0 -0
  601. package/tools/__pycache__/python_sandbox.cpython-313.pyc +0 -0
  602. package/tools/__pycache__/session_snapshot.cpython-313.pyc +0 -0
  603. package/tools/__pycache__/ssh_manager.cpython-313.pyc +0 -0
  604. package/tools/__pycache__/theme_engine.cpython-313.pyc +0 -0
  605. package/tools/__pycache__/theme_selector.cpython-313.pyc +0 -0
  606. package/tools/__pycache__/web_search.cpython-313.pyc +0 -0
  607. package/tools/browser_consent.py +289 -0
  608. package/tools/browser_stealth.py +481 -0
  609. package/tools/browser_tool.py +448 -0
  610. package/tools/changelog_generator.py +347 -0
  611. package/tools/commit_splitter.py +749 -0
  612. package/tools/config_discovery.py +151 -0
  613. package/tools/config_merger.py +449 -0
  614. package/tools/dashboard_generator.py +300 -0
  615. package/tools/git_inspector.py +298 -0
  616. package/tools/lsp_client.py +275 -0
  617. package/tools/lsp_discovery.py +231 -0
  618. package/tools/lsp_operations.py +392 -0
  619. package/tools/pr_generator.py +404 -0
  620. package/tools/python_repl.py +712 -0
  621. package/tools/python_sandbox.py +768 -0
  622. package/tools/search_providers/__init__.py +77 -0
  623. package/tools/search_providers/__pycache__/__init__.cpython-313.pyc +0 -0
  624. package/tools/search_providers/__pycache__/brave.cpython-313.pyc +0 -0
  625. package/tools/search_providers/__pycache__/exa.cpython-313.pyc +0 -0
  626. package/tools/search_providers/__pycache__/jina.cpython-313.pyc +0 -0
  627. package/tools/search_providers/__pycache__/perplexity.cpython-313.pyc +0 -0
  628. package/tools/search_providers/__pycache__/synthetic.cpython-313.pyc +0 -0
  629. package/tools/search_providers/brave.py +115 -0
  630. package/tools/search_providers/exa.py +116 -0
  631. package/tools/search_providers/jina.py +104 -0
  632. package/tools/search_providers/perplexity.py +139 -0
  633. package/tools/search_providers/synthetic.py +74 -0
  634. package/tools/session_snapshot.py +851 -0
  635. package/tools/ssh_manager.py +912 -0
  636. package/tools/theme_engine.py +296 -0
  637. package/tools/theme_selector.py +137 -0
  638. package/tools/web_search.py +675 -0
@@ -0,0 +1,1101 @@
1
+ """OMG Setup Wizard — interactive CLI detection, auth verification, and MCP configuration.
2
+
3
+ Feature-gated: requires OMG_SETUP_ENABLED=1 (default off).
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ import importlib
9
+ import logging
10
+ import os
11
+ import re
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Any, cast
15
+
16
+ import yaml
17
+
18
+ _HOOKS_DIR = Path(__file__).resolve().parent
19
+ _PROJECT_ROOT = _HOOKS_DIR.parent
20
+ _PORTABLE_RUNTIME_ROOT = _PROJECT_ROOT / "omg-runtime"
21
+ for path in (_HOOKS_DIR, _PROJECT_ROOT, _PORTABLE_RUNTIME_ROOT):
22
+ path_str = str(path)
23
+ if path_str not in sys.path:
24
+ sys.path.insert(0, path_str)
25
+
26
+ from hooks._common import bootstrap_runtime_paths, get_feature_flag
27
+
28
+ bootstrap_runtime_paths(__file__)
29
+
30
+ from runtime.cli_provider import get_provider, list_available_providers # noqa: E402
31
+
32
+ # Trigger provider auto-registration on import
33
+ import runtime.providers.codex_provider # noqa: E402, F401
34
+ import runtime.providers.gemini_provider # noqa: E402, F401
35
+ import runtime.providers.kimi_provider # noqa: E402, F401
36
+ import runtime.providers.opencode_provider # noqa: E402, F401
37
+ from runtime.mcp_config_writers import ( # noqa: E402
38
+ write_claude_mcp_config,
39
+ write_claude_mcp_stdio_config,
40
+ write_codex_mcp_config,
41
+ write_codex_mcp_stdio_config,
42
+ write_gemini_mcp_config,
43
+ write_gemini_mcp_stdio_config,
44
+ write_kimi_mcp_config,
45
+ write_kimi_mcp_stdio_config,
46
+ )
47
+ from runtime.plugin_interop import discover_host_plugin_state # noqa: E402
48
+ from runtime.validate import run_validate as _run_post_install_validate # noqa: E402
49
+ from runtime.adoption import ( # noqa: E402
50
+ CANONICAL_MODE_NAMES,
51
+ CANONICAL_VERSION,
52
+ PRESET_LEVEL,
53
+ PRESET_ORDER,
54
+ build_adoption_report,
55
+ get_mode_profile,
56
+ get_preset_features,
57
+ detect_ecosystems,
58
+ resolve_preset,
59
+ write_adoption_report,
60
+ )
61
+
62
+ _logger = logging.getLogger(__name__)
63
+
64
+ OMG_CONTROL_COMMAND = "python3"
65
+ OMG_CONTROL_ARGS = ["-m", "runtime.omg_mcp_server"]
66
+ OMG_CONTROL_SERVER_NAME = "omg-control"
67
+
68
+ _INSTALL_HINTS: dict[str, str] = {
69
+ "codex": "npm install -g @openai/codex",
70
+ "gemini": "npm install -g @google/gemini-cli",
71
+ "kimi": "uv tool install --python 3.13 kimi-cli",
72
+ }
73
+
74
+ BYPASS_ALL_WARNING = (
75
+ "⚠️ BYPASS-ALL / FULL VIBE-CODE MODE WARNING ⚠️\n\n"
76
+ "Enabling bypass-all grants Claude Code unrestricted write access to your filesystem "
77
+ "without asking for confirmation on individual file edits.\n\n"
78
+ "IMPORTANT DISCLAIMER: The author takes NO responsibility for any data loss, "
79
+ "unintended file modifications, or system changes that occur while bypass-all is enabled. "
80
+ "Use at your own risk.\n\n"
81
+ "Note: Some safety measures remain active even in bypass-all mode:\n"
82
+ " • Firewall deny rules still block dangerous commands (rm -rf, sudo, etc.)\n"
83
+ " • Secret-guard still protects credentials and API keys\n"
84
+ " • You can disable bypass-all at any time via settings.json\n\n"
85
+ "Do you want to enable bypass-all mode? (y/N): "
86
+ )
87
+
88
+ _PRESET_LEVEL = PRESET_LEVEL
89
+
90
+ MCP_CATALOG: list[dict[str, Any]] = [
91
+ {
92
+ "id": "context7",
93
+ "name": "Context7",
94
+ "description": "Upstash Context7 MCP server for context management",
95
+ "command": "npx",
96
+ "args": ["@upstash/context7-mcp@2.1.3"],
97
+ "default": False,
98
+ "min_preset": "balanced",
99
+ "category": "productivity",
100
+ },
101
+ {
102
+ "id": "filesystem",
103
+ "name": "Filesystem",
104
+ "description": "ModelContextProtocol filesystem server for file operations",
105
+ "command": "npx",
106
+ "args": ["@modelcontextprotocol/server-filesystem@2026.1.14", "."],
107
+ "default": True,
108
+ "min_preset": "safe",
109
+ "category": "system",
110
+ },
111
+ {
112
+ "id": "websearch",
113
+ "name": "Web Search",
114
+ "description": "Web search MCP server for internet queries",
115
+ "command": "npx",
116
+ "args": ["@zhafron/mcp-web-search@1.2.2"],
117
+ "default": False,
118
+ "min_preset": "interop",
119
+ "category": "search",
120
+ },
121
+ {
122
+ "id": "chrome-devtools",
123
+ "name": "Chrome DevTools",
124
+ "description": "Chrome DevTools MCP server for browser automation",
125
+ "command": "npx",
126
+ "args": ["chrome-devtools-mcp@0.19.0"],
127
+ "default": False,
128
+ "min_preset": "labs",
129
+ "category": "browser",
130
+ },
131
+ {
132
+ "id": "omg-memory",
133
+ "name": "OMG Memory",
134
+ "description": "OMG shared memory server via HTTP",
135
+ "command": None,
136
+ "args": [],
137
+ "type": "http",
138
+ "url": "http://127.0.0.1:8765/mcp",
139
+ "default": False,
140
+ "min_preset": "interop",
141
+ "category": "memory",
142
+ },
143
+ {
144
+ "id": OMG_CONTROL_SERVER_NAME,
145
+ "name": "OMG Control",
146
+ "description": "OMG control plane MCP server via stdio",
147
+ "command": OMG_CONTROL_COMMAND,
148
+ "args": OMG_CONTROL_ARGS,
149
+ "default": True,
150
+ "min_preset": "safe",
151
+ "category": "control",
152
+ },
153
+ {
154
+ "id": "github",
155
+ "name": "GitHub",
156
+ "description": "ModelContextProtocol GitHub server for repository operations",
157
+ "command": "npx",
158
+ "args": ["@modelcontextprotocol/server-github"],
159
+ "default": False,
160
+ "category": "vcs",
161
+ },
162
+ {
163
+ "id": "puppeteer",
164
+ "name": "Puppeteer",
165
+ "description": "ModelContextProtocol Puppeteer server for browser automation",
166
+ "command": "npx",
167
+ "args": ["@modelcontextprotocol/server-puppeteer"],
168
+ "default": False,
169
+ "category": "browser",
170
+ },
171
+ {
172
+ "id": "brave-search",
173
+ "name": "Brave Search",
174
+ "description": "ModelContextProtocol Brave Search server",
175
+ "command": "npx",
176
+ "args": ["@modelcontextprotocol/server-brave-search"],
177
+ "default": False,
178
+ "category": "search",
179
+ },
180
+ {
181
+ "id": "sequential-thinking",
182
+ "name": "Sequential Thinking",
183
+ "description": "ModelContextProtocol Sequential Thinking server for reasoning",
184
+ "command": "npx",
185
+ "args": ["@modelcontextprotocol/server-sequential-thinking"],
186
+ "default": False,
187
+ "category": "reasoning",
188
+ },
189
+ {
190
+ "id": "grep-app",
191
+ "name": "Grep App",
192
+ "description": "Grep App MCP server for code search",
193
+ "command": "npx",
194
+ "args": ["grep-app-mcp"],
195
+ "default": False,
196
+ "category": "search",
197
+ },
198
+ {
199
+ "id": "memory-graph",
200
+ "name": "Memory Graph",
201
+ "description": "ModelContextProtocol Memory server for knowledge graphs",
202
+ "command": "npx",
203
+ "args": ["@modelcontextprotocol/server-memory"],
204
+ "default": False,
205
+ "category": "memory",
206
+ },
207
+ {
208
+ "id": "notebooklm",
209
+ "name": "NotebookLM",
210
+ "description": "NotebookLM MCP server for AI-powered research and note-taking",
211
+ "command": "npx",
212
+ "args": ["-y", "notebooklm-mcp@latest"],
213
+ "default": False,
214
+ "category": "productivity",
215
+ "warning": (
216
+ "NOTEBOOKLM MCP WARNING\n\n"
217
+ "NotebookLM requires browser automation and has several important considerations:\n\n"
218
+ "BROWSER AUTOMATION: NotebookLM uses Puppeteer for browser automation. "
219
+ "This requires a Chromium/Chrome installation and may consume significant system resources.\n\n"
220
+ "FIRST-RUN DOWNLOAD: On first run, NotebookLM will download a full browser instance "
221
+ "(typically 100-300 MB). This is a one-time operation but may take several minutes.\n\n"
222
+ "DEDICATED ACCOUNT: It is strongly recommended to use a dedicated Google account "
223
+ "for NotebookLM MCP. Do NOT use your primary account, as the MCP will store credentials locally.\n\n"
224
+ "AUTH EXPIRY: Google authentication tokens expire periodically. "
225
+ "You may need to re-authenticate if the MCP fails with auth errors.\n\n"
226
+ "Enable NotebookLM only if you understand these requirements and accept the risks."
227
+ ),
228
+ },
229
+ ]
230
+
231
+ _MCP_ID_ALIASES: dict[str, str] = {
232
+ "file-system": "filesystem",
233
+ "file_system": "filesystem",
234
+ "grep": "grep-app",
235
+ "grep_app": "grep-app",
236
+ }
237
+
238
+
239
+ def get_mcp_catalog() -> list[dict[str, Any]]:
240
+ """Return the MCP catalog.
241
+
242
+ Returns:
243
+ List of MCP server definitions with id, name, description, command, args, default, and category.
244
+ """
245
+ return MCP_CATALOG
246
+
247
+
248
+ def get_default_mcps_for_preset(preset: str) -> list[str]:
249
+ """Return the list of MCP server IDs enabled by default for *preset*.
250
+
251
+ Each catalog entry carries a ``min_preset`` field that specifies the
252
+ lowest preset tier at which the MCP is included. The preset order is
253
+ ``safe < balanced < interop < labs``.
254
+
255
+ Returns:
256
+ Sorted list of MCP server IDs whose ``min_preset`` is at or below
257
+ the requested *preset* level.
258
+ """
259
+ level = _PRESET_LEVEL.get(preset, 0)
260
+ default_ids = [
261
+ m["id"]
262
+ for m in MCP_CATALOG
263
+ if _PRESET_LEVEL.get(m.get("min_preset", ""), -1) <= level
264
+ and m.get("min_preset") is not None
265
+ ]
266
+ if preset == "buffet" and os.environ.get("OMG_SETUP_ENABLED", "0").strip() != "1":
267
+ default_ids.append("notebooklm")
268
+ return default_ids
269
+
270
+
271
+ def _normalize_mcp_ids(selected_ids: list[str]) -> list[str]:
272
+ valid_ids = {cast(str, m["id"]) for m in MCP_CATALOG}
273
+ normalized_ids: list[str] = []
274
+ seen: set[str] = set()
275
+ unknown_ids: list[str] = []
276
+
277
+ for raw_id in selected_ids:
278
+ normalized = _MCP_ID_ALIASES.get(raw_id, raw_id)
279
+ if normalized not in valid_ids:
280
+ unknown_ids.append(raw_id)
281
+ continue
282
+ if normalized in seen:
283
+ continue
284
+ seen.add(normalized)
285
+ normalized_ids.append(normalized)
286
+
287
+ if unknown_ids:
288
+ raise ValueError(
289
+ "Unsupported MCP server IDs: " + ", ".join(unknown_ids)
290
+ )
291
+
292
+ return normalized_ids
293
+
294
+
295
+ def build_mcp_config(selected_ids: list[str]) -> dict[str, Any]:
296
+ """Build .mcp.json configuration from selected MCP server IDs.
297
+
298
+ Args:
299
+ selected_ids: List of MCP server IDs to include in the config.
300
+
301
+ Returns:
302
+ Dict with 'mcpServers' key containing the MCP server configurations.
303
+ """
304
+ normalized_ids = _normalize_mcp_ids(selected_ids)
305
+ mcp_servers: dict[str, Any] = {}
306
+
307
+ for mcp in MCP_CATALOG:
308
+ if mcp["id"] not in normalized_ids:
309
+ continue
310
+
311
+ mcp_id = mcp["id"]
312
+
313
+ # HTTP-type MCPs (like omg-memory)
314
+ if mcp.get("type") == "http":
315
+ mcp_servers[mcp_id] = {
316
+ "type": "http",
317
+ "url": mcp["url"],
318
+ }
319
+ # NPX-type MCPs
320
+ else:
321
+ mcp_servers[mcp_id] = {
322
+ "command": mcp["command"],
323
+ "args": mcp["args"],
324
+ }
325
+
326
+ return {"mcpServers": mcp_servers}
327
+
328
+
329
+ def configure_plan_type(plan_type: str) -> dict[str, Any]:
330
+ """Configure Claude plan type and model routing.
331
+
332
+ Args:
333
+ plan_type: "max" or "pro"
334
+
335
+ Returns:
336
+ dict with plan_type and optionally model_routing
337
+ """
338
+ result: dict[str, Any] = {"plan_type": plan_type}
339
+ if plan_type == "pro":
340
+ result["model_routing"] = {
341
+ "planning": "claude-opus-4-5",
342
+ "coding": "claude-sonnet-4-5",
343
+ "review": "claude-opus-4-5",
344
+ "commit": "claude-haiku-4-5",
345
+ }
346
+ return result
347
+
348
+
349
+ def select_mcps(selected_ids: list[str] | None = None, preset: str = "safe") -> dict[str, Any]:
350
+ """Build MCP config from selected IDs.
351
+
352
+ Args:
353
+ selected_ids: List of MCP IDs to include. If None, uses preset defaults.
354
+ preset: Preset tier that controls which MCPs are included by default.
355
+
356
+ Returns:
357
+ dict with mcpServers key (ready to write as .mcp.json)
358
+ """
359
+ if selected_ids is None:
360
+ selected_ids = get_default_mcps_for_preset(preset)
361
+ else:
362
+ selected_ids = _normalize_mcp_ids(selected_ids)
363
+ return build_mcp_config(selected_ids)
364
+
365
+
366
+ def configure_bypass_all(enabled: bool) -> dict[str, Any]:
367
+ """Configure bypass_all mode.
368
+
369
+ Args:
370
+ enabled: True to enable bypass-all, False to disable
371
+
372
+ Returns:
373
+ dict with enabled status and warning_shown flag
374
+ """
375
+ result: dict[str, Any] = {"enabled": enabled}
376
+ if enabled:
377
+ result["warning_shown"] = True
378
+ return result
379
+
380
+
381
+ def is_setup_enabled() -> bool:
382
+ """Check if the setup wizard feature is enabled.
383
+
384
+ Uses get_feature_flag("SETUP", default=False) — disabled by default.
385
+ Enable via OMG_SETUP_ENABLED=1 env var or settings.json._omg.features.SETUP: true.
386
+ """
387
+ return get_feature_flag("SETUP", default=False)
388
+
389
+
390
+ def detect_clis() -> dict[str, Any]:
391
+ """Detect installed CLI tools using the provider registry.
392
+
393
+ Iterates over all registered providers, calling ``detect()`` and
394
+ ``check_auth()`` on each. Returns a dict keyed by provider name::
395
+
396
+ {
397
+ "codex": {"detected": True, "auth_ok": True, "message": "..."},
398
+ "gemini": {"detected": False, "auth_ok": None,
399
+ "message": "Not found. Install: npm install -g @google/gemini-cli"},
400
+ ...
401
+ }
402
+ """
403
+ results: dict[str, Any] = {}
404
+ host_config_paths = {
405
+ "codex": Path.home() / ".codex" / "config.toml",
406
+ "gemini": Path.home() / ".gemini" / "settings.json",
407
+ "kimi": Path.home() / ".kimi" / "mcp.json",
408
+ }
409
+
410
+ for name in list_available_providers():
411
+ provider = get_provider(name)
412
+ if provider is None:
413
+ continue
414
+
415
+ config_path = host_config_paths.get(name)
416
+ configured = bool(config_path and config_path.exists())
417
+
418
+ try:
419
+ detected = provider.detect()
420
+ except Exception as exc:
421
+ _logger.warning("detect() failed for %s: %s", name, exc)
422
+ detected = False
423
+
424
+ auth_ok: bool | None = None
425
+ message = ""
426
+
427
+ if detected:
428
+ try:
429
+ auth_ok, message = provider.check_auth()
430
+ except Exception as exc:
431
+ _logger.warning("check_auth() failed for %s: %s", name, exc)
432
+ auth_ok = None
433
+ message = f"Auth check error: {exc}"
434
+ else:
435
+ hint = _INSTALL_HINTS.get(name, f"Install the '{name}' CLI")
436
+ message = f"Not found. Install: {hint}"
437
+ if configured and config_path is not None:
438
+ message = f"{message} (config detected at {config_path})"
439
+
440
+ results[name] = {
441
+ "detected": detected,
442
+ "configured": configured,
443
+ "auth_ok": auth_ok,
444
+ "message": message,
445
+ }
446
+
447
+ return results
448
+
449
+
450
+ def get_cli_auth_instructions(provider: str) -> dict[str, str]:
451
+ """Return install, auth, and verify instructions for a CLI provider.
452
+
453
+ This function returns command strings only — it does NOT execute anything
454
+ or store credentials.
455
+
456
+ Args:
457
+ provider: CLI provider name (e.g. "codex", "gemini", or "kimi").
458
+
459
+ Returns:
460
+ Dict with keys: install, auth, verify, subscription.
461
+ Unknown providers return placeholder strings.
462
+ """
463
+ instructions: dict[str, dict[str, str]] = {
464
+ "codex": {
465
+ "install": "npm install -g @openai/codex",
466
+ "auth": "codex login",
467
+ "verify": "codex --version",
468
+ "subscription": "Requires ChatGPT Plus, Team, or Enterprise subscription (or OpenAI API key)",
469
+ },
470
+ "gemini": {
471
+ "install": "npm install -g @google/gemini-cli",
472
+ "auth": "gemini auth login",
473
+ "verify": "gemini --version",
474
+ "subscription": "Requires Google account with Gemini API access (free tier available)",
475
+ },
476
+ "kimi": {
477
+ "install": "uv tool install --python 3.13 kimi-cli",
478
+ "auth": "Add token to ~/.kimi/config.toml",
479
+ "verify": "kimi --version",
480
+ "subscription": "Requires Kimi API key from platform.moonshot.cn",
481
+ },
482
+ }
483
+
484
+ return instructions.get(provider, {
485
+ "install": "Unknown provider",
486
+ "auth": "Unknown provider",
487
+ "verify": "Unknown provider",
488
+ "subscription": "Unknown",
489
+ })
490
+
491
+
492
+ def check_auth() -> dict[str, Any]:
493
+ """Verify authentication for detected CLI tools.
494
+
495
+ Stub — returns pending status. T16 will implement real auth checks
496
+ using each provider's check_auth() method.
497
+ """
498
+ return {"status": "pending", "results": {}}
499
+
500
+
501
+ _PROFILE_ARCH_REQUEST_MAX = 8
502
+ _PROFILE_TAG_MAX = 12
503
+ _PROFILE_SUMMARY_MAX_CHARS = 240
504
+ _PROFILE_RECENT_UPDATES_MAX = 5
505
+ _PROFILE_GOVERNED_MAX = 24
506
+
507
+
508
+ def get_mode_choices() -> list[str]:
509
+ return list(CANONICAL_MODE_NAMES)
510
+
511
+
512
+ def select_setup_mode(mode: str | None) -> str:
513
+ candidate = (mode or "").strip().lower()
514
+ if candidate in CANONICAL_MODE_NAMES:
515
+ return candidate
516
+ return "focused"
517
+
518
+
519
+ def configure_mcp(
520
+ project_dir: str,
521
+ detected_clis: dict[str, Any],
522
+ server_url: str = "http://127.0.0.1:8765/mcp",
523
+ server_name: str = "omg-memory",
524
+ control_command: str = OMG_CONTROL_COMMAND,
525
+ control_args: list[str] | None = None,
526
+ control_server_name: str = OMG_CONTROL_SERVER_NAME,
527
+ preset: str = "safe",
528
+ selected_ids: list[str] | None = None,
529
+ ) -> dict[str, Any]:
530
+ """Configure OMG MCP servers for authenticated CLIs.
531
+
532
+ For each CLI in detected_clis where detected_clis[cli]["detected"] == True,
533
+ calls the appropriate writer from runtime.mcp_config_writers.
534
+
535
+ HTTP memory surfaces are only written when the *preset* is at or above
536
+ ``interop`` level. ``safe`` and ``balanced`` presets write only the
537
+ stdio ``omg-control`` surface.
538
+
539
+ Args:
540
+ project_dir: Path to the project directory.
541
+ detected_clis: Dict of CLI detection results from detect_clis().
542
+ server_url: MCP server URL (default: http://127.0.0.1:8765/mcp).
543
+ server_name: MCP server name (default: omg-memory).
544
+ control_command: stdio command for the OMG control MCP server.
545
+ control_args: stdio args for the OMG control MCP server.
546
+ control_server_name: MCP server name for the OMG control surface.
547
+ preset: Active preset tier (safe, balanced, interop, labs).
548
+
549
+ Returns:
550
+ Dict with keys:
551
+ - status: "ok" on success
552
+ - configured: List of CLI names that were successfully configured
553
+ - errors: Dict of CLI name → error message for failures
554
+ """
555
+ resolved_control_args = list(control_args or OMG_CONTROL_ARGS)
556
+ selected_config = select_mcps(selected_ids=selected_ids, preset=preset)
557
+ selected_servers = cast(
558
+ dict[str, dict[str, Any]],
559
+ selected_config.get("mcpServers", {}),
560
+ )
561
+ install_planner = importlib.import_module("runtime.install_planner")
562
+ plan = install_planner.compute_install_plan(
563
+ project_dir=project_dir,
564
+ detected_clis=detected_clis,
565
+ preset=preset,
566
+ mode="focused",
567
+ selected_ids=selected_ids,
568
+ server_url=server_url,
569
+ server_name=server_name,
570
+ control_command=control_command,
571
+ control_args=resolved_control_args,
572
+ control_server_name=control_server_name,
573
+ selected_servers=selected_servers,
574
+ source_root=_PROJECT_ROOT,
575
+ )
576
+ install_result = cast(dict[str, Any], install_planner.execute_plan(plan))
577
+
578
+ configured = [action.host for action in plan.actions if action.host != "claude"]
579
+ errors: dict[str, str] = {}
580
+ if install_result["errors"]:
581
+ joined = "; ".join(install_result["errors"])
582
+ if not configured:
583
+ errors["claude"] = joined
584
+ else:
585
+ for host in configured:
586
+ errors[host] = joined
587
+ configured = []
588
+
589
+ return {
590
+ "status": "ok",
591
+ "configured": configured,
592
+ "errors": errors,
593
+ }
594
+
595
+
596
+ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str, Any]:
597
+ """Set user preferences for CLI routing and save to .omg/state/cli-config.yaml.
598
+
599
+ Args:
600
+ project_dir: Path to the project directory.
601
+ preferences: Dict with optional 'cli_configs' key. If empty, uses defaults.
602
+ Expected structure:
603
+ {
604
+ "cli_configs": {
605
+ "codex": {"subscription": "free", "max_parallel_agents": 1},
606
+ "gemini": {"subscription": "free", "max_parallel_agents": 1},
607
+ ...
608
+ }
609
+ }
610
+
611
+ Returns:
612
+ Dict with keys:
613
+ - status: "ok" on success
614
+ - path: Full path to saved config file
615
+ - config: The saved config dict (version + cli_configs)
616
+ """
617
+ # Default config structure
618
+ default_cli_configs: dict[str, Any] = {
619
+ "codex": {"subscription": "free", "max_parallel_agents": 1},
620
+ "gemini": {"subscription": "free", "max_parallel_agents": 1},
621
+ "kimi": {"subscription": "free", "max_parallel_agents": 1},
622
+ }
623
+ preset = resolve_preset(cast(str, preferences.get("preset") or "safe"))
624
+ default_config: dict[str, Any] = {
625
+ "version": CANONICAL_VERSION,
626
+ "preset": preset,
627
+ "resolved_features": get_preset_features(preset),
628
+ "cli_configs": default_cli_configs,
629
+ "detected_clis": {},
630
+ "selected_mcps": get_default_mcps_for_preset(preset),
631
+ "browser_capability": {"enabled": False},
632
+ }
633
+
634
+ # Merge custom preferences if provided
635
+ if preferences and isinstance(preferences, dict):
636
+ cli_configs = preferences.get("cli_configs")
637
+ if isinstance(cli_configs, dict):
638
+ default_cli_configs.update(cast(dict[str, Any], cli_configs))
639
+ selected_mcps = preferences.get("selected_mcps")
640
+ if isinstance(selected_mcps, list):
641
+ default_config["selected_mcps"] = _normalize_mcp_ids(
642
+ [str(item) for item in selected_mcps]
643
+ )
644
+ browser_capability = preferences.get("browser_capability")
645
+ if isinstance(browser_capability, dict):
646
+ default_config["browser_capability"] = {
647
+ "enabled": bool(browser_capability.get("enabled", False))
648
+ }
649
+ install_planner = importlib.import_module("runtime.install_planner")
650
+ normalize_detected = cast(
651
+ Any,
652
+ getattr(install_planner, "normalize_detected_clis", None),
653
+ )
654
+ if callable(normalize_detected):
655
+ raw_detected_clis = preferences.get("detected_clis")
656
+ normalized_detected_clis = cast(dict[str, Any], normalize_detected(raw_detected_clis))
657
+ persisted_detected: dict[str, Any] = {}
658
+ for host, host_state in normalized_detected_clis.items():
659
+ if not isinstance(host_state, dict):
660
+ continue
661
+ persisted_detected[host] = {
662
+ "installed": bool(host_state.get("detected", False)),
663
+ "configured": bool(host_state.get("configured", False)),
664
+ }
665
+ default_config["detected_clis"] = persisted_detected
666
+
667
+ _write_project_settings_preset(project_dir, preset)
668
+
669
+ # Create .omg/state directory if needed
670
+ state_dir = os.path.join(project_dir, ".omg", "state")
671
+ os.makedirs(state_dir, exist_ok=True)
672
+
673
+ _write_profile_learning_sections(state_dir, preferences)
674
+
675
+ # Write config to YAML file
676
+ config_path = os.path.join(state_dir, "cli-config.yaml")
677
+ with open(config_path, "w") as f:
678
+ yaml.dump(default_config, f, default_flow_style=False, sort_keys=False)
679
+
680
+ _logger.info("Saved CLI config to %s", config_path)
681
+
682
+ return {
683
+ "status": "ok",
684
+ "path": config_path,
685
+ "config": default_config,
686
+ }
687
+
688
+
689
+ def _write_profile_learning_sections(state_dir: str, preferences: dict[str, Any]) -> None:
690
+ profile_path = os.path.join(state_dir, "profile.yaml")
691
+ profile_data = _load_profile_yaml(profile_path)
692
+ _ensure_profile_baseline(profile_data)
693
+
694
+ profile_data["preferences"] = _normalize_preferences_block(preferences.get("preferences"))
695
+ profile_data["user_vector"] = _normalize_user_vector_block(preferences.get("user_vector"))
696
+ profile_data["profile_provenance"] = _normalize_provenance_block(preferences.get("profile_provenance"))
697
+ profile_data["governed_preferences"] = _normalize_governed_preferences_block(preferences.get("governed_preferences"))
698
+
699
+ from runtime.profile_io import save_profile
700
+ save_profile(profile_path, profile_data)
701
+
702
+
703
+ def _render_explicit_empty_collections(dumped: str) -> str:
704
+ lines = dumped.splitlines()
705
+ output: list[str] = []
706
+
707
+ def _next_line(start: int) -> str:
708
+ if start + 1 < len(lines):
709
+ return lines[start + 1]
710
+ return ""
711
+
712
+ for idx, line in enumerate(lines):
713
+ next_line = _next_line(idx)
714
+ stripped = line.strip()
715
+
716
+ if stripped == "stack:" and (not next_line or not next_line.startswith("- ")):
717
+ output.append("stack: []")
718
+ continue
719
+ if stripped == "conventions:" and (not next_line or not next_line.startswith(" ")):
720
+ output.append("conventions: {}")
721
+ continue
722
+ if stripped == "ai_behavior:" and (not next_line or not next_line.startswith(" ")):
723
+ output.append("ai_behavior: {}")
724
+ continue
725
+ if stripped == "architecture_requests:" and (not next_line or not next_line.startswith(" - ")):
726
+ output.append(" architecture_requests: []")
727
+ continue
728
+ if stripped == "constraints:" and (not next_line or not next_line.startswith(" ")):
729
+ output.append(" constraints: {}")
730
+ continue
731
+ if stripped == "tags:" and (not next_line or not next_line.startswith(" - ")):
732
+ output.append(" tags: []")
733
+ continue
734
+ if stripped == "recent_updates:" and (not next_line or not next_line.startswith(" - ")):
735
+ output.append(" recent_updates: []")
736
+ continue
737
+
738
+ output.append(line)
739
+
740
+ return "\n".join(output) + "\n"
741
+
742
+
743
+ def _load_profile_yaml(profile_path: str) -> dict[str, Any]:
744
+ from runtime.profile_io import load_profile
745
+ return load_profile(profile_path)
746
+
747
+
748
+ def _ensure_profile_baseline(profile_data: dict[str, Any]) -> None:
749
+ profile_data.setdefault("name", "omg-project")
750
+ profile_data.setdefault("description", "initialized by OMG standalone compat bootstrap")
751
+ profile_data.setdefault("language", "unknown")
752
+ profile_data.setdefault("framework", "unknown")
753
+ profile_data.setdefault("stack", [])
754
+ profile_data.setdefault("conventions", {})
755
+ profile_data.setdefault("ai_behavior", {})
756
+ profile_data.setdefault("governed_preferences", {"style": [], "safety": []})
757
+
758
+
759
+ def _normalize_preferences_block(raw: Any) -> dict[str, Any]:
760
+ block = raw if isinstance(raw, dict) else {}
761
+
762
+ requests_obj = block.get("architecture_requests")
763
+ architecture_requests: list[str] = []
764
+ if isinstance(requests_obj, list):
765
+ for value in requests_obj:
766
+ text = str(value).strip()
767
+ if text:
768
+ architecture_requests.append(text)
769
+ if len(architecture_requests) >= _PROFILE_ARCH_REQUEST_MAX:
770
+ break
771
+
772
+ constraints_obj = block.get("constraints")
773
+ constraints: dict[str, Any] = {}
774
+ if isinstance(constraints_obj, dict):
775
+ for key, value in constraints_obj.items():
776
+ normalized_key = _normalize_constraint_key(str(key))
777
+ normalized_value = _normalize_constraint_value(value)
778
+ if normalized_key and normalized_value is not None:
779
+ constraints[normalized_key] = normalized_value
780
+
781
+ routing_obj = block.get("routing")
782
+ routing_map = routing_obj if isinstance(routing_obj, dict) else {}
783
+ routing = {
784
+ "prefer_clarification": bool(routing_map.get("prefer_clarification", False)),
785
+ }
786
+
787
+ return {
788
+ "architecture_requests": architecture_requests,
789
+ "constraints": constraints,
790
+ "routing": routing,
791
+ }
792
+
793
+
794
+ def _normalize_constraint_key(value: str) -> str:
795
+ normalized = value.strip().lower()
796
+ normalized = re.sub(r"\s+", "_", normalized)
797
+ normalized = re.sub(r"[^a-z0-9_]+", "", normalized)
798
+ return normalized
799
+
800
+
801
+ def _normalize_constraint_value(value: Any) -> str | int | float | bool | None:
802
+ if isinstance(value, bool):
803
+ return value
804
+ if isinstance(value, int):
805
+ return value
806
+ if isinstance(value, float):
807
+ return value
808
+ if value is None:
809
+ return None
810
+ text = str(value).strip().lower()
811
+ return text if text else None
812
+
813
+
814
+ def _normalize_user_vector_block(raw: Any) -> dict[str, Any]:
815
+ block = raw if isinstance(raw, dict) else {}
816
+
817
+ tags_obj = block.get("tags")
818
+ tags: list[str] = []
819
+ if isinstance(tags_obj, list):
820
+ for value in tags_obj:
821
+ token = _normalize_tag_token(str(value))
822
+ if token:
823
+ tags.append(token)
824
+ if len(tags) >= _PROFILE_TAG_MAX:
825
+ break
826
+
827
+ summary = ""
828
+ summary_obj = block.get("summary")
829
+ if isinstance(summary_obj, str):
830
+ summary = " ".join(summary_obj.strip().split())[:_PROFILE_SUMMARY_MAX_CHARS]
831
+
832
+ confidence = 0.0
833
+ confidence_obj = block.get("confidence")
834
+ if isinstance(confidence_obj, (int, float, str)):
835
+ try:
836
+ confidence = float(confidence_obj)
837
+ except (TypeError, ValueError):
838
+ confidence = 0.0
839
+ confidence = max(0.0, min(1.0, confidence))
840
+
841
+ return {
842
+ "tags": tags,
843
+ "summary": summary,
844
+ "confidence": confidence,
845
+ }
846
+
847
+
848
+ def _normalize_tag_token(value: str) -> str:
849
+ normalized = value.strip().lower()
850
+ normalized = re.sub(r"\s+", "_", normalized)
851
+ normalized = re.sub(r"[^a-z0-9_\-]+", "", normalized)
852
+ return normalized
853
+
854
+
855
+ def _normalize_provenance_block(raw: Any) -> dict[str, Any]:
856
+ block = raw if isinstance(raw, dict) else {}
857
+ updates_obj = block.get("recent_updates")
858
+ updates: list[dict[str, str]] = []
859
+ if isinstance(updates_obj, list):
860
+ for entry in updates_obj:
861
+ if not isinstance(entry, dict):
862
+ continue
863
+ run_id = str(entry.get("run_id", "")).strip()
864
+ source = str(entry.get("source", "")).strip()
865
+ field = str(entry.get("field", "")).strip()
866
+ updated_at = str(entry.get("updated_at", "")).strip()
867
+ if not (run_id and source and field and updated_at):
868
+ continue
869
+ updates.append(
870
+ {
871
+ "run_id": run_id,
872
+ "source": source,
873
+ "field": field,
874
+ "updated_at": updated_at,
875
+ }
876
+ )
877
+ if len(updates) >= _PROFILE_RECENT_UPDATES_MAX:
878
+ break
879
+ return {"recent_updates": updates}
880
+
881
+
882
+ def _normalize_governed_preferences_block(raw: Any) -> dict[str, list[dict[str, Any]]]:
883
+ block = raw if isinstance(raw, dict) else {}
884
+ style_raw = block.get("style")
885
+ safety_raw = block.get("safety")
886
+ return {
887
+ "style": _normalize_governed_section_entries(style_raw, section="style"),
888
+ "safety": _normalize_governed_section_entries(safety_raw, section="safety"),
889
+ }
890
+
891
+
892
+ def _normalize_governed_section_entries(raw: Any, *, section: str) -> list[dict[str, Any]]:
893
+ if not isinstance(raw, list):
894
+ return []
895
+ entries: list[dict[str, Any]] = []
896
+ for item in raw:
897
+ if not isinstance(item, dict):
898
+ continue
899
+ field = str(item.get("field", "")).strip()
900
+ value = " ".join(str(item.get("value", "")).strip().split())
901
+ source = str(item.get("source", "")).strip()
902
+ learned_at = str(item.get("learned_at", "")).strip()
903
+ updated_at = str(item.get("updated_at", "")).strip()
904
+ candidate_section = str(item.get("section", section)).strip().lower()
905
+ confirmation_state = str(item.get("confirmation_state", "")).strip().lower()
906
+
907
+ if not (field and value and source and learned_at and updated_at):
908
+ continue
909
+ if candidate_section != section:
910
+ continue
911
+ if confirmation_state not in ("confirmed", "pending_confirmation", "inferred"):
912
+ continue
913
+
914
+ entry: dict[str, Any] = {
915
+ "field": field,
916
+ "value": value,
917
+ "source": source,
918
+ "learned_at": learned_at,
919
+ "updated_at": updated_at,
920
+ "section": section,
921
+ "confirmation_state": confirmation_state,
922
+ }
923
+
924
+ if section == "style" and confirmation_state == "inferred":
925
+ decay_raw = item.get("decay_metadata")
926
+ decay = decay_raw if isinstance(decay_raw, dict) else {}
927
+ try:
928
+ score = float(decay.get("decay_score", 0.0))
929
+ except (TypeError, ValueError):
930
+ score = 0.0
931
+ entry["decay_metadata"] = {
932
+ "decay_score": max(0.0, min(1.0, score)),
933
+ "last_seen_at": str(decay.get("last_seen_at", updated_at)).strip() or updated_at,
934
+ "decay_reason": str(decay.get("decay_reason", "inferred_signal")).strip() or "inferred_signal",
935
+ }
936
+
937
+ entries.append(entry)
938
+ if len(entries) >= _PROFILE_GOVERNED_MAX:
939
+ break
940
+ return entries
941
+
942
+
943
+ def _write_project_settings_preset(project_dir: str, preset: str) -> None:
944
+ """Persist preset metadata into project settings when settings.json exists."""
945
+ settings_path = os.path.join(project_dir, "settings.json")
946
+ if not os.path.exists(settings_path):
947
+ return
948
+
949
+ try:
950
+ with open(settings_path, "r", encoding="utf-8") as f:
951
+ settings = json.load(f)
952
+ except Exception:
953
+ return
954
+
955
+ if not isinstance(settings, dict):
956
+ return
957
+
958
+ omg = settings.get("_omg")
959
+ if not isinstance(omg, dict):
960
+ omg = {}
961
+ features = omg.get("features")
962
+ if not isinstance(features, dict):
963
+ features = {}
964
+
965
+ features.update(get_preset_features(preset))
966
+ omg["features"] = features
967
+ omg["preset"] = preset
968
+ omg["_version"] = CANONICAL_VERSION
969
+ settings["_omg"] = omg
970
+
971
+ with open(settings_path, "w", encoding="utf-8") as f:
972
+ json.dump(settings, f, indent=2, ensure_ascii=True)
973
+ f.write("\n")
974
+
975
+
976
+ def run_setup_wizard(
977
+ project_dir: str,
978
+ non_interactive: bool = False,
979
+ *,
980
+ mode: str | None = None,
981
+ setup_mode: str | None = None,
982
+ adopt: str = "auto",
983
+ preset: str | None = None,
984
+ selected_mcps: list[str] | None = None,
985
+ browser_enabled: bool = False,
986
+ ) -> dict[str, Any]:
987
+ """Run the OMG setup wizard.
988
+
989
+ Args:
990
+ project_dir: Path to the project directory.
991
+ non_interactive: If True, skip prompts and use defaults (for CI).
992
+ mode: Optional adoption mode override (`omg-only` or `coexist`).
993
+ adopt: Adoption detection mode (currently only `auto` is meaningful).
994
+ preset: Optional preset override.
995
+
996
+ Returns:
997
+ Dict with wizard results including status and step outcomes.
998
+ If feature is disabled, returns {"status": "disabled", "message": "..."}.
999
+ """
1000
+ if not is_setup_enabled():
1001
+ return {
1002
+ "status": "disabled",
1003
+ "message": "Setup wizard disabled. Set OMG_SETUP_ENABLED=1 to enable.",
1004
+ }
1005
+
1006
+ selected_preset = resolve_preset(preset or ("balanced" if non_interactive else "safe"))
1007
+ selected_setup_mode = select_setup_mode(setup_mode)
1008
+ requested_mode = mode
1009
+ if requested_mode is None and non_interactive:
1010
+ detected = detect_ecosystems(project_dir)
1011
+ if not detected:
1012
+ requested_mode = "omg-only"
1013
+
1014
+ adoption = build_adoption_report(
1015
+ project_dir,
1016
+ requested_mode=requested_mode,
1017
+ preset=selected_preset,
1018
+ adopt=adopt,
1019
+ )
1020
+ foreign_plugins_discovered = 0
1021
+ if adoption.get("selected_mode") == "coexist" or selected_preset == "interop":
1022
+ host_plugin_state = discover_host_plugin_state(project_dir)
1023
+ foreign_plugins_discovered = len(host_plugin_state.records)
1024
+ adoption["foreign_plugins_discovered"] = foreign_plugins_discovered
1025
+
1026
+ clis = detect_clis()
1027
+ auth = check_auth()
1028
+ mcp = configure_mcp(project_dir, clis, preset=selected_preset, selected_ids=selected_mcps)
1029
+ prefs = set_preferences(
1030
+ project_dir,
1031
+ {
1032
+ "preset": selected_preset,
1033
+ "selected_mcps": selected_mcps,
1034
+ "browser_capability": {"enabled": browser_enabled},
1035
+ "detected_clis": clis,
1036
+ },
1037
+ )
1038
+ report_path = write_adoption_report(project_dir, adoption)
1039
+ adoption["report_path"] = report_path
1040
+
1041
+ result: dict[str, Any] = {
1042
+ "status": "complete",
1043
+ "setup_mode": {
1044
+ "choices": get_mode_choices(),
1045
+ "selected": selected_setup_mode,
1046
+ "profile": get_mode_profile(selected_setup_mode),
1047
+ },
1048
+ "clis_detected": clis,
1049
+ "auth_status": auth,
1050
+ "mcp_configured": mcp,
1051
+ "preferences": prefs,
1052
+ "adoption": adoption,
1053
+ }
1054
+
1055
+ # --- Post-install validation (runs AFTER all setup writes are complete) ---
1056
+ try:
1057
+ validation_result = _run_post_install_validate(root_dir=Path(project_dir))
1058
+ except Exception as exc:
1059
+ validation_result = {
1060
+ "schema": "ValidateResult",
1061
+ "status": "fail",
1062
+ "checks": [
1063
+ {
1064
+ "name": "validation_error",
1065
+ "status": "blocker",
1066
+ "message": str(exc),
1067
+ "required": True,
1068
+ },
1069
+ ],
1070
+ "version": "",
1071
+ }
1072
+
1073
+ # Persist machine-readable validation artifact
1074
+ artifact_dir = os.path.join(project_dir, ".omg", "state")
1075
+ os.makedirs(artifact_dir, exist_ok=True)
1076
+ artifact_path = os.path.join(artifact_dir, "post-install-validation.json")
1077
+ try:
1078
+ with open(artifact_path, "w", encoding="utf-8") as f:
1079
+ json.dump(validation_result, f, indent=2, ensure_ascii=True)
1080
+ f.write("\n")
1081
+ except OSError:
1082
+ artifact_path = ""
1083
+
1084
+ checks = validation_result.get("checks", [])
1085
+ blockers = [
1086
+ c
1087
+ for c in checks
1088
+ if isinstance(c, dict) and c.get("status") == "blocker"
1089
+ ]
1090
+
1091
+ result["post_install_validation"] = {
1092
+ "status": validation_result.get("status", "fail"),
1093
+ "artifact_path": artifact_path,
1094
+ "blockers": [{"name": c["name"], "message": c["message"]} for c in blockers],
1095
+ }
1096
+
1097
+ has_detected_cli = any(bool(v.get("detected", False)) for v in clis.values() if isinstance(v, dict))
1098
+ if blockers and not non_interactive and not has_detected_cli:
1099
+ result["status"] = "validation_failed"
1100
+
1101
+ return result