@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.
- package/CHANGELOG.md +188 -0
- package/INSTALL-VERIFICATION-INDEX.md +51 -0
- package/LICENSE +21 -0
- package/OMG-setup.sh +2549 -0
- package/QUICK-REFERENCE.md +58 -0
- package/README.md +207 -0
- package/agents/__init__.py +1 -0
- package/agents/__pycache__/model_roles.cpython-313.pyc +0 -0
- package/agents/_model_roles.yaml +26 -0
- package/agents/designer.md +67 -0
- package/agents/explore.md +60 -0
- package/agents/model_roles.py +196 -0
- package/agents/omg-api-builder.md +23 -0
- package/agents/omg-architect-mode.md +41 -0
- package/agents/omg-architect.md +13 -0
- package/agents/omg-backend-engineer.md +41 -0
- package/agents/omg-critic.md +16 -0
- package/agents/omg-database-engineer.md +41 -0
- package/agents/omg-escalation-router.md +17 -0
- package/agents/omg-executor.md +12 -0
- package/agents/omg-frontend-designer.md +41 -0
- package/agents/omg-implement-mode.md +49 -0
- package/agents/omg-infra-engineer.md +41 -0
- package/agents/omg-qa-tester.md +16 -0
- package/agents/omg-research-mode.md +41 -0
- package/agents/omg-security-auditor.md +41 -0
- package/agents/omg-testing-engineer.md +41 -0
- package/agents/plan.md +80 -0
- package/agents/quick_task.md +64 -0
- package/agents/reviewer.md +83 -0
- package/agents/task.md +71 -0
- package/bin/omg +41 -0
- package/commands/OMG:ai-commit.md +113 -0
- package/commands/OMG:api-twin.md +22 -0
- package/commands/OMG:arch.md +313 -0
- package/commands/OMG:browser.md +29 -0
- package/commands/OMG:ccg.md +22 -0
- package/commands/OMG:compat.md +57 -0
- package/commands/OMG:cost.md +181 -0
- package/commands/OMG:crazy.md +125 -0
- package/commands/OMG:create-agent.md +183 -0
- package/commands/OMG:deep-plan.md +18 -0
- package/commands/OMG:deps.md +248 -0
- package/commands/OMG:diagnose-plugins.md +33 -0
- package/commands/OMG:doctor.md +37 -0
- package/commands/OMG:domain-init.md +11 -0
- package/commands/OMG:escalate.md +52 -0
- package/commands/OMG:forge.md +103 -0
- package/commands/OMG:health-check.md +48 -0
- package/commands/OMG:init.md +134 -0
- package/commands/OMG:issue.md +56 -0
- package/commands/OMG:mode.md +44 -0
- package/commands/OMG:playwright.md +17 -0
- package/commands/OMG:preflight.md +26 -0
- package/commands/OMG:preset.md +49 -0
- package/commands/OMG:profile-review.md +58 -0
- package/commands/OMG:project-init.md +11 -0
- package/commands/OMG:ralph-start.md +43 -0
- package/commands/OMG:ralph-stop.md +23 -0
- package/commands/OMG:security-check.md +28 -0
- package/commands/OMG:session-branch.md +101 -0
- package/commands/OMG:session-fork.md +57 -0
- package/commands/OMG:session-merge.md +138 -0
- package/commands/OMG:setup.md +82 -0
- package/commands/OMG:ship.md +18 -0
- package/commands/OMG:stats.md +225 -0
- package/commands/OMG:teams.md +54 -0
- package/commands/OMG:theme.md +44 -0
- package/commands/OMG:validate.md +59 -0
- package/commands/__init__.py +1 -0
- package/docs/command-surface.md +55 -0
- package/docs/install/claude-code.md +53 -0
- package/docs/install/codex.md +45 -0
- package/docs/install/gemini.md +43 -0
- package/docs/install/github-action.md +81 -0
- package/docs/install/github-app-required-checks.md +107 -0
- package/docs/install/github-app.md +161 -0
- package/docs/install/kimi.md +43 -0
- package/docs/install/opencode.md +38 -0
- package/docs/proof.md +182 -0
- package/hooks/__init__.py +0 -0
- package/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_agent_registry.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_analytics.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_budget.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_common.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_compression_optimizer.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_cost_ledger.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_learnings.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_memory.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_post_write.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_protected_context.cpython-313.pyc +0 -0
- package/hooks/__pycache__/_token_counter.cpython-313.pyc +0 -0
- package/hooks/__pycache__/branch_manager.cpython-313.pyc +0 -0
- package/hooks/__pycache__/budget_governor.cpython-313.pyc +0 -0
- package/hooks/__pycache__/circuit-breaker.cpython-313.pyc +0 -0
- package/hooks/__pycache__/compression_feedback.cpython-313.pyc +0 -0
- package/hooks/__pycache__/config-guard.cpython-313.pyc +0 -0
- package/hooks/__pycache__/context_pressure.cpython-313.pyc +0 -0
- package/hooks/__pycache__/credential_store.cpython-313.pyc +0 -0
- package/hooks/__pycache__/fetch-rate-limits.cpython-313.pyc +0 -0
- package/hooks/__pycache__/firewall.cpython-313.pyc +0 -0
- package/hooks/__pycache__/hashline-formatter-bridge.cpython-313.pyc +0 -0
- package/hooks/__pycache__/hashline-injector.cpython-313.pyc +0 -0
- package/hooks/__pycache__/hashline-validator.cpython-313.pyc +0 -0
- package/hooks/__pycache__/idle-detector.cpython-313.pyc +0 -0
- package/hooks/__pycache__/instructions-loaded.cpython-313.pyc +0 -0
- package/hooks/__pycache__/intentgate-keyword-detector.cpython-313.pyc +0 -0
- package/hooks/__pycache__/magic-keyword-router.cpython-313.pyc +0 -0
- package/hooks/__pycache__/policy_engine.cpython-313.pyc +0 -0
- package/hooks/__pycache__/post-tool-failure.cpython-313.pyc +0 -0
- package/hooks/__pycache__/post-write.cpython-313.pyc +0 -0
- package/hooks/__pycache__/post_write.cpython-313.pyc +0 -0
- package/hooks/__pycache__/pre-compact.cpython-313.pyc +0 -0
- package/hooks/__pycache__/pre-tool-inject.cpython-313.pyc +0 -0
- package/hooks/__pycache__/prompt-enhancer.cpython-313.pyc +0 -0
- package/hooks/__pycache__/quality-runner.cpython-313.pyc +0 -0
- package/hooks/__pycache__/query.cpython-313.pyc +0 -0
- package/hooks/__pycache__/secret-guard.cpython-313.pyc +0 -0
- package/hooks/__pycache__/secret_audit.cpython-313.pyc +0 -0
- package/hooks/__pycache__/security_validators.cpython-313.pyc +0 -0
- package/hooks/__pycache__/session-end-capture.cpython-313.pyc +0 -0
- package/hooks/__pycache__/session-start.cpython-313.pyc +0 -0
- package/hooks/__pycache__/setup_wizard.cpython-313.pyc +0 -0
- package/hooks/__pycache__/shadow_manager.cpython-313.pyc +0 -0
- package/hooks/__pycache__/state_migration.cpython-313.pyc +0 -0
- package/hooks/__pycache__/stop-gate.cpython-313.pyc +0 -0
- package/hooks/__pycache__/stop_dispatcher.cpython-313.pyc +0 -0
- package/hooks/__pycache__/tdd-gate.cpython-313.pyc +0 -0
- package/hooks/__pycache__/terms-guard.cpython-313.pyc +0 -0
- package/hooks/__pycache__/test-validator.cpython-313.pyc +0 -0
- package/hooks/__pycache__/test_generator_hook.cpython-313.pyc +0 -0
- package/hooks/__pycache__/todo-state-tracker.cpython-313.pyc +0 -0
- package/hooks/__pycache__/tool-ledger.cpython-313.pyc +0 -0
- package/hooks/__pycache__/trust_review.cpython-313.pyc +0 -0
- package/hooks/__pycache__/user-prompt-submit.cpython-313.pyc +0 -0
- package/hooks/_agent_registry.py +481 -0
- package/hooks/_analytics.py +291 -0
- package/hooks/_budget.py +31 -0
- package/hooks/_common.py +761 -0
- package/hooks/_compression_optimizer.py +119 -0
- package/hooks/_cost_ledger.py +176 -0
- package/hooks/_learnings.py +126 -0
- package/hooks/_memory.py +103 -0
- package/hooks/_post_write.py +46 -0
- package/hooks/_protected_context.py +150 -0
- package/hooks/_token_counter.py +221 -0
- package/hooks/branch_manager.py +255 -0
- package/hooks/budget_governor.py +326 -0
- package/hooks/circuit-breaker.py +270 -0
- package/hooks/compression_feedback.py +254 -0
- package/hooks/config-guard.py +193 -0
- package/hooks/context_pressure.py +119 -0
- package/hooks/credential_store.py +970 -0
- package/hooks/fetch-rate-limits.py +212 -0
- package/hooks/firewall.py +323 -0
- package/hooks/hashline-formatter-bridge.py +224 -0
- package/hooks/hashline-injector.py +273 -0
- package/hooks/hashline-validator.py +216 -0
- package/hooks/idle-detector.py +97 -0
- package/hooks/instructions-loaded.py +26 -0
- package/hooks/intentgate-keyword-detector.py +200 -0
- package/hooks/magic-keyword-router.py +195 -0
- package/hooks/policy_engine.py +767 -0
- package/hooks/post-tool-failure.py +19 -0
- package/hooks/post-write.py +233 -0
- package/hooks/pre-compact.py +470 -0
- package/hooks/pre-tool-inject.py +98 -0
- package/hooks/prompt-enhancer.py +879 -0
- package/hooks/quality-runner.py +191 -0
- package/hooks/query.py +512 -0
- package/hooks/secret-guard.py +120 -0
- package/hooks/secret_audit.py +144 -0
- package/hooks/security_validators.py +93 -0
- package/hooks/session-end-capture.py +505 -0
- package/hooks/session-start.py +261 -0
- package/hooks/setup_wizard.py +1101 -0
- package/hooks/shadow_manager.py +476 -0
- package/hooks/state_migration.py +228 -0
- package/hooks/stop-gate.py +7 -0
- package/hooks/stop_dispatcher.py +1259 -0
- package/hooks/tdd-gate.py +10 -0
- package/hooks/terms-guard.py +98 -0
- package/hooks/test-validator.py +462 -0
- package/hooks/test_generator_hook.py +123 -0
- package/hooks/todo-state-tracker.py +114 -0
- package/hooks/tool-ledger.py +165 -0
- package/hooks/trust_review.py +662 -0
- package/hooks/user-prompt-submit.py +12 -0
- package/hud/omg-hud.mjs +1571 -0
- package/lab/__init__.py +1 -0
- package/lab/__pycache__/__init__.cpython-313.pyc +0 -0
- package/lab/__pycache__/axolotl_adapter.cpython-313.pyc +0 -0
- package/lab/__pycache__/forge_runner.cpython-313.pyc +0 -0
- package/lab/__pycache__/gazebo_adapter.cpython-313.pyc +0 -0
- package/lab/__pycache__/isaac_gym_adapter.cpython-313.pyc +0 -0
- package/lab/__pycache__/mock_isaac_env.cpython-313.pyc +0 -0
- package/lab/__pycache__/pipeline.cpython-313.pyc +0 -0
- package/lab/__pycache__/policies.cpython-313.pyc +0 -0
- package/lab/__pycache__/pybullet_adapter.cpython-313.pyc +0 -0
- package/lab/axolotl_adapter.py +531 -0
- package/lab/forge_runner.py +103 -0
- package/lab/gazebo_adapter.py +168 -0
- package/lab/isaac_gym_adapter.py +190 -0
- package/lab/mock_isaac_env.py +47 -0
- package/lab/pipeline.py +712 -0
- package/lab/policies.py +52 -0
- package/lab/pybullet_adapter.py +192 -0
- package/package.json +61 -0
- package/plugins/README.md +78 -0
- package/plugins/__init__.py +1 -0
- package/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
- package/plugins/advanced/commands/OMG-code-review.md +114 -0
- package/plugins/advanced/commands/OMG-deep-plan.md +266 -0
- package/plugins/advanced/commands/OMG-handoff.md +115 -0
- package/plugins/advanced/commands/OMG-learn.md +110 -0
- package/plugins/advanced/commands/OMG-maintainer.md +31 -0
- package/plugins/advanced/commands/OMG-ralph-start.md +43 -0
- package/plugins/advanced/commands/OMG-ralph-stop.md +23 -0
- package/plugins/advanced/commands/OMG-security-review.md +16 -0
- package/plugins/advanced/commands/OMG-sequential-thinking.md +20 -0
- package/plugins/advanced/commands/OMG-ship.md +46 -0
- package/plugins/advanced/commands/OMG:code-review.md +114 -0
- package/plugins/advanced/commands/OMG:deep-plan.md +266 -0
- package/plugins/advanced/commands/OMG:handoff.md +115 -0
- package/plugins/advanced/commands/OMG:learn.md +110 -0
- package/plugins/advanced/commands/OMG:maintainer.md +31 -0
- package/plugins/advanced/commands/OMG:ralph-start.md +43 -0
- package/plugins/advanced/commands/OMG:ralph-stop.md +23 -0
- package/plugins/advanced/commands/OMG:security-review.md +16 -0
- package/plugins/advanced/commands/OMG:sequential-thinking.md +20 -0
- package/plugins/advanced/commands/OMG:ship.md +46 -0
- package/plugins/advanced/plugin.json +104 -0
- package/plugins/core/plugin.json +204 -0
- package/plugins/dephealth/__init__.py +0 -0
- package/plugins/dephealth/__pycache__/__init__.cpython-313.pyc +0 -0
- package/plugins/dephealth/__pycache__/cve_scanner.cpython-313.pyc +0 -0
- package/plugins/dephealth/__pycache__/license_checker.cpython-313.pyc +0 -0
- package/plugins/dephealth/__pycache__/manifest_detector.cpython-313.pyc +0 -0
- package/plugins/dephealth/__pycache__/vuln_analyzer.cpython-313.pyc +0 -0
- package/plugins/dephealth/cve_scanner.py +279 -0
- package/plugins/dephealth/license_checker.py +135 -0
- package/plugins/dephealth/manifest_detector.py +423 -0
- package/plugins/dephealth/vuln_analyzer.py +176 -0
- package/plugins/testgen/__init__.py +0 -0
- package/plugins/testgen/__pycache__/__init__.cpython-313.pyc +0 -0
- package/plugins/testgen/__pycache__/codamosa_engine.cpython-313.pyc +0 -0
- package/plugins/testgen/__pycache__/edge_case_synthesizer.cpython-313.pyc +0 -0
- package/plugins/testgen/__pycache__/framework_detector.cpython-313.pyc +0 -0
- package/plugins/testgen/__pycache__/skeleton_generator.cpython-313.pyc +0 -0
- package/plugins/testgen/codamosa_engine.py +402 -0
- package/plugins/testgen/edge_case_synthesizer.py +184 -0
- package/plugins/testgen/framework_detector.py +271 -0
- package/plugins/testgen/skeleton_generator.py +219 -0
- package/plugins/viz/__init__.py +0 -0
- package/plugins/viz/__pycache__/__init__.cpython-313.pyc +0 -0
- package/plugins/viz/__pycache__/ast_parser.cpython-313.pyc +0 -0
- package/plugins/viz/__pycache__/diagram_generator.cpython-313.pyc +0 -0
- package/plugins/viz/__pycache__/graph_builder.cpython-313.pyc +0 -0
- package/plugins/viz/__pycache__/native_parsers.cpython-313.pyc +0 -0
- package/plugins/viz/__pycache__/regex_parser.cpython-313.pyc +0 -0
- package/plugins/viz/ast_parser.py +139 -0
- package/plugins/viz/diagram_generator.py +192 -0
- package/plugins/viz/graph_builder.py +444 -0
- package/plugins/viz/native_parsers.py +259 -0
- package/plugins/viz/regex_parser.py +112 -0
- package/pyproject.toml +143 -0
- package/registry/__init__.py +1 -0
- package/registry/__pycache__/__init__.cpython-313.pyc +0 -0
- package/registry/__pycache__/approval_artifact.cpython-313.pyc +0 -0
- package/registry/__pycache__/verify_artifact.cpython-313.pyc +0 -0
- package/registry/approval_artifact.py +236 -0
- package/registry/bundles/algorithms.yaml +45 -0
- package/registry/bundles/api-twin.yaml +48 -0
- package/registry/bundles/ast-pack.yaml +80 -0
- package/registry/bundles/claim-judge.yaml +49 -0
- package/registry/bundles/control-plane.yaml +192 -0
- package/registry/bundles/data-lineage.yaml +47 -0
- package/registry/bundles/delta-classifier.yaml +47 -0
- package/registry/bundles/eval-gate.yaml +47 -0
- package/registry/bundles/hash-edit.yaml +73 -0
- package/registry/bundles/health.yaml +45 -0
- package/registry/bundles/hook-governor.yaml +101 -0
- package/registry/bundles/incident-replay.yaml +47 -0
- package/registry/bundles/lsp-pack.yaml +80 -0
- package/registry/bundles/mcp-fabric.yaml +53 -0
- package/registry/bundles/plan-council.yaml +56 -0
- package/registry/bundles/preflight.yaml +48 -0
- package/registry/bundles/proof-gate.yaml +49 -0
- package/registry/bundles/remote-supervisor.yaml +49 -0
- package/registry/bundles/robotics.yaml +45 -0
- package/registry/bundles/secure-worktree-pipeline.yaml +69 -0
- package/registry/bundles/security-check.yaml +50 -0
- package/registry/bundles/terminal-lane.yaml +61 -0
- package/registry/bundles/test-intent-lock.yaml +49 -0
- package/registry/bundles/tracebank.yaml +47 -0
- package/registry/bundles/vision.yaml +45 -0
- package/registry/omg-capability.schema.json +378 -0
- package/registry/policy-packs/airgapped.lock.json +11 -0
- package/registry/policy-packs/airgapped.signature.json +10 -0
- package/registry/policy-packs/airgapped.yaml +16 -0
- package/registry/policy-packs/fintech.lock.json +11 -0
- package/registry/policy-packs/fintech.signature.json +10 -0
- package/registry/policy-packs/fintech.yaml +15 -0
- package/registry/policy-packs/locked-prod.lock.json +11 -0
- package/registry/policy-packs/locked-prod.signature.json +10 -0
- package/registry/policy-packs/locked-prod.yaml +18 -0
- package/registry/trusted_signers.json +44 -0
- package/registry/verify_artifact.py +493 -0
- package/runtime/__init__.py +36 -0
- package/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
- package/runtime/__pycache__/adoption.cpython-313.pyc +0 -0
- package/runtime/__pycache__/agent_selector.cpython-313.pyc +0 -0
- package/runtime/__pycache__/api_twin.cpython-313.pyc +0 -0
- package/runtime/__pycache__/architecture_signal.cpython-313.pyc +0 -0
- package/runtime/__pycache__/artifact_parsers.cpython-313.pyc +0 -0
- package/runtime/__pycache__/asset_loader.cpython-313.pyc +0 -0
- package/runtime/__pycache__/background_verification.cpython-313.pyc +0 -0
- package/runtime/__pycache__/budget_envelopes.cpython-313.pyc +0 -0
- package/runtime/__pycache__/business_workflow.cpython-313.pyc +0 -0
- package/runtime/__pycache__/canonical_surface.cpython-313.pyc +0 -0
- package/runtime/__pycache__/canonical_taxonomy.cpython-313.pyc +0 -0
- package/runtime/__pycache__/claim_judge.cpython-313.pyc +0 -0
- package/runtime/__pycache__/cli_provider.cpython-313.pyc +0 -0
- package/runtime/__pycache__/compat.cpython-313.pyc +0 -0
- package/runtime/__pycache__/complexity_scorer.cpython-313.pyc +0 -0
- package/runtime/__pycache__/compliance_governor.cpython-313.pyc +0 -0
- package/runtime/__pycache__/config_transaction.cpython-313.pyc +0 -0
- package/runtime/__pycache__/context_compiler.cpython-313.pyc +0 -0
- package/runtime/__pycache__/context_engine.cpython-313.pyc +0 -0
- package/runtime/__pycache__/context_limits.cpython-313.pyc +0 -0
- package/runtime/__pycache__/contract_compiler.cpython-313.pyc +0 -0
- package/runtime/__pycache__/custom_agent_loader.cpython-313.pyc +0 -0
- package/runtime/__pycache__/data_lineage.cpython-313.pyc +0 -0
- package/runtime/__pycache__/defense_state.cpython-313.pyc +0 -0
- package/runtime/__pycache__/delta_classifier.cpython-313.pyc +0 -0
- package/runtime/__pycache__/dispatcher.cpython-313.pyc +0 -0
- package/runtime/__pycache__/doc_generator.cpython-313.pyc +0 -0
- package/runtime/__pycache__/domain_packs.cpython-313.pyc +0 -0
- package/runtime/__pycache__/ecosystem.cpython-313.pyc +0 -0
- package/runtime/__pycache__/equalizer.cpython-313.pyc +0 -0
- package/runtime/__pycache__/eval_gate.cpython-313.pyc +0 -0
- package/runtime/__pycache__/evidence_narrator.cpython-313.pyc +0 -0
- package/runtime/__pycache__/evidence_query.cpython-313.pyc +0 -0
- package/runtime/__pycache__/evidence_registry.cpython-313.pyc +0 -0
- package/runtime/__pycache__/evidence_requirements.cpython-313.pyc +0 -0
- package/runtime/__pycache__/exec_kernel.cpython-313.pyc +0 -0
- package/runtime/__pycache__/explainer_formatter.cpython-313.pyc +0 -0
- package/runtime/__pycache__/feature_registry.cpython-313.pyc +0 -0
- package/runtime/__pycache__/forge_agents.cpython-313.pyc +0 -0
- package/runtime/__pycache__/forge_contracts.cpython-313.pyc +0 -0
- package/runtime/__pycache__/forge_domains.cpython-313.pyc +0 -0
- package/runtime/__pycache__/forge_run_id.cpython-313.pyc +0 -0
- package/runtime/__pycache__/github_integration.cpython-313.pyc +0 -0
- package/runtime/__pycache__/github_review_bot.cpython-313.pyc +0 -0
- package/runtime/__pycache__/github_review_contract.cpython-313.pyc +0 -0
- package/runtime/__pycache__/github_review_formatter.cpython-313.pyc +0 -0
- package/runtime/__pycache__/guide_assert.cpython-313.pyc +0 -0
- package/runtime/__pycache__/hook_governor.cpython-313.pyc +0 -0
- package/runtime/__pycache__/host_parity.cpython-313.pyc +0 -0
- package/runtime/__pycache__/incident_replay.cpython-313.pyc +0 -0
- package/runtime/__pycache__/install_planner.cpython-313.pyc +0 -0
- package/runtime/__pycache__/interaction_journal.cpython-313.pyc +0 -0
- package/runtime/__pycache__/issue_surface.cpython-313.pyc +0 -0
- package/runtime/__pycache__/legacy_compat.cpython-313.pyc +0 -0
- package/runtime/__pycache__/mcp_config_writers.cpython-313.pyc +0 -0
- package/runtime/__pycache__/mcp_lifecycle.cpython-313.pyc +0 -0
- package/runtime/__pycache__/mcp_memory_server.cpython-313.pyc +0 -0
- package/runtime/__pycache__/memory_store.cpython-313.pyc +0 -0
- package/runtime/__pycache__/merge_writer.cpython-313.pyc +0 -0
- package/runtime/__pycache__/music_omr_testbed.cpython-313.pyc +0 -0
- package/runtime/__pycache__/mutation_gate.cpython-313.pyc +0 -0
- package/runtime/__pycache__/omc_compat.cpython-313.pyc +0 -0
- package/runtime/__pycache__/omg_browser_cli.cpython-313.pyc +0 -0
- package/runtime/__pycache__/omg_mcp_server.cpython-313.pyc +0 -0
- package/runtime/__pycache__/opus_plan.cpython-313.pyc +0 -0
- package/runtime/__pycache__/playwright_adapter.cpython-313.pyc +0 -0
- package/runtime/__pycache__/playwright_pack.cpython-313.pyc +0 -0
- package/runtime/__pycache__/plugin_diagnostics.cpython-313.pyc +0 -0
- package/runtime/__pycache__/plugin_interop.cpython-313.pyc +0 -0
- package/runtime/__pycache__/policy_pack_loader.cpython-313.pyc +0 -0
- package/runtime/__pycache__/preflight.cpython-313.pyc +0 -0
- package/runtime/__pycache__/profile_io.cpython-313.pyc +0 -0
- package/runtime/__pycache__/prompt_compiler.cpython-313.pyc +0 -0
- package/runtime/__pycache__/proof_chain.cpython-313.pyc +0 -0
- package/runtime/__pycache__/proof_gate.cpython-313.pyc +0 -0
- package/runtime/__pycache__/provider_parity_eval.cpython-313.pyc +0 -0
- package/runtime/__pycache__/release_artifact_audit.cpython-313.pyc +0 -0
- package/runtime/__pycache__/release_run_coordinator.cpython-313.pyc +0 -0
- package/runtime/__pycache__/release_surface_compiler.cpython-313.pyc +0 -0
- package/runtime/__pycache__/release_surface_registry.cpython-313.pyc +0 -0
- package/runtime/__pycache__/release_surfaces.cpython-313.pyc +0 -0
- package/runtime/__pycache__/remote_supervisor.cpython-313.pyc +0 -0
- package/runtime/__pycache__/repro_pack.cpython-313.pyc +0 -0
- package/runtime/__pycache__/rollback_manifest.cpython-313.pyc +0 -0
- package/runtime/__pycache__/router_critics.cpython-313.pyc +0 -0
- package/runtime/__pycache__/router_executor.cpython-313.pyc +0 -0
- package/runtime/__pycache__/router_selector.cpython-313.pyc +0 -0
- package/runtime/__pycache__/runtime_contracts.cpython-313.pyc +0 -0
- package/runtime/__pycache__/runtime_profile.cpython-313.pyc +0 -0
- package/runtime/__pycache__/security_check.cpython-313.pyc +0 -0
- package/runtime/__pycache__/session_health.cpython-313.pyc +0 -0
- package/runtime/__pycache__/skill_evolution.cpython-313.pyc +0 -0
- package/runtime/__pycache__/skill_registry.cpython-313.pyc +0 -0
- package/runtime/__pycache__/subagent_dispatcher.cpython-313.pyc +0 -0
- package/runtime/__pycache__/subscription_tiers.cpython-313.pyc +0 -0
- package/runtime/__pycache__/team_router.cpython-313.pyc +0 -0
- package/runtime/__pycache__/test_intent_lock.cpython-313-pytest-9.0.2.pyc +0 -0
- package/runtime/__pycache__/test_intent_lock.cpython-313.pyc +0 -0
- package/runtime/__pycache__/tmux_session_manager.cpython-313.pyc +0 -0
- package/runtime/__pycache__/tool_fabric.cpython-313.pyc +0 -0
- package/runtime/__pycache__/tool_plan_gate.cpython-313.pyc +0 -0
- package/runtime/__pycache__/tool_relevance.cpython-313.pyc +0 -0
- package/runtime/__pycache__/tracebank.cpython-313.pyc +0 -0
- package/runtime/__pycache__/untrusted_content.cpython-313.pyc +0 -0
- package/runtime/__pycache__/validate.cpython-313.pyc +0 -0
- package/runtime/__pycache__/verdict_schema.cpython-313.pyc +0 -0
- package/runtime/__pycache__/verification_controller.cpython-313.pyc +0 -0
- package/runtime/__pycache__/verification_loop.cpython-313.pyc +0 -0
- package/runtime/__pycache__/vision_artifacts.cpython-313.pyc +0 -0
- package/runtime/__pycache__/vision_cache.cpython-313.pyc +0 -0
- package/runtime/__pycache__/vision_jobs.cpython-313.pyc +0 -0
- package/runtime/__pycache__/worker_watchdog.cpython-313.pyc +0 -0
- package/runtime/adapters/__init__.py +13 -0
- package/runtime/adapters/__pycache__/__init__.cpython-313.pyc +0 -0
- package/runtime/adapters/__pycache__/claude.cpython-313.pyc +0 -0
- package/runtime/adapters/__pycache__/gpt.cpython-313.pyc +0 -0
- package/runtime/adapters/__pycache__/local.cpython-313.pyc +0 -0
- package/runtime/adapters/claude.py +63 -0
- package/runtime/adapters/gpt.py +56 -0
- package/runtime/adapters/local.py +56 -0
- package/runtime/adoption.py +280 -0
- package/runtime/api_twin.py +450 -0
- package/runtime/architecture_signal.py +226 -0
- package/runtime/artifact_parsers.py +161 -0
- package/runtime/asset_loader.py +62 -0
- package/runtime/background_verification.py +178 -0
- package/runtime/budget_envelopes.py +398 -0
- package/runtime/business_workflow.py +234 -0
- package/runtime/canonical_surface.py +53 -0
- package/runtime/canonical_taxonomy.py +27 -0
- package/runtime/claim_judge.py +648 -0
- package/runtime/cli_provider.py +105 -0
- package/runtime/compat.py +2222 -0
- package/runtime/complexity_scorer.py +148 -0
- package/runtime/compliance_governor.py +505 -0
- package/runtime/config_transaction.py +304 -0
- package/runtime/context_compiler.py +131 -0
- package/runtime/context_engine.py +708 -0
- package/runtime/context_limits.py +363 -0
- package/runtime/contract_compiler.py +3664 -0
- package/runtime/custom_agent_loader.py +366 -0
- package/runtime/data_lineage.py +244 -0
- package/runtime/defense_state.py +261 -0
- package/runtime/delta_classifier.py +231 -0
- package/runtime/dispatcher.py +47 -0
- package/runtime/doc_generator.py +319 -0
- package/runtime/domain_packs.py +75 -0
- package/runtime/ecosystem.py +371 -0
- package/runtime/equalizer.py +268 -0
- package/runtime/eval_gate.py +96 -0
- package/runtime/evidence_narrator.py +147 -0
- package/runtime/evidence_query.py +303 -0
- package/runtime/evidence_registry.py +16 -0
- package/runtime/evidence_requirements.py +157 -0
- package/runtime/exec_kernel.py +267 -0
- package/runtime/explainer_formatter.py +82 -0
- package/runtime/feature_registry.py +109 -0
- package/runtime/forge_agents.py +915 -0
- package/runtime/forge_contracts.py +519 -0
- package/runtime/forge_domains.py +68 -0
- package/runtime/forge_run_id.py +86 -0
- package/runtime/guide_assert.py +135 -0
- package/runtime/hook_governor.py +156 -0
- package/runtime/host_parity.py +373 -0
- package/runtime/incident_replay.py +310 -0
- package/runtime/install_planner.py +617 -0
- package/runtime/interaction_journal.py +566 -0
- package/runtime/issue_surface.py +472 -0
- package/runtime/legacy_compat.py +7 -0
- package/runtime/mcp_config_writers.py +360 -0
- package/runtime/mcp_lifecycle.py +175 -0
- package/runtime/mcp_memory_server.py +220 -0
- package/runtime/memory_parsers/__init__.py +0 -0
- package/runtime/memory_parsers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/runtime/memory_parsers/__pycache__/chatgpt_parser.cpython-313.pyc +0 -0
- package/runtime/memory_parsers/__pycache__/claude_import.cpython-313.pyc +0 -0
- package/runtime/memory_parsers/__pycache__/export.cpython-313.pyc +0 -0
- package/runtime/memory_parsers/__pycache__/gemini_import.cpython-313.pyc +0 -0
- package/runtime/memory_parsers/__pycache__/kimi_import.cpython-313.pyc +0 -0
- package/runtime/memory_parsers/chatgpt_parser.py +257 -0
- package/runtime/memory_parsers/claude_import.py +107 -0
- package/runtime/memory_parsers/export.py +97 -0
- package/runtime/memory_parsers/gemini_import.py +91 -0
- package/runtime/memory_parsers/kimi_import.py +91 -0
- package/runtime/memory_store.py +1182 -0
- package/runtime/merge_writer.py +445 -0
- package/runtime/music_omr_testbed.py +336 -0
- package/runtime/mutation_gate.py +320 -0
- package/runtime/omc_compat.py +7 -0
- package/runtime/omg_browser_cli.py +95 -0
- package/runtime/omg_compat_contract_snapshot.json +936 -0
- package/runtime/omg_contract_snapshot.json +936 -0
- package/runtime/omg_mcp_server.py +306 -0
- package/runtime/playwright_adapter.py +39 -0
- package/runtime/playwright_pack.py +253 -0
- package/runtime/plugin_diagnostics.py +308 -0
- package/runtime/plugin_interop.py +1060 -0
- package/runtime/policy_pack_loader.py +147 -0
- package/runtime/preflight.py +135 -0
- package/runtime/profile_io.py +328 -0
- package/runtime/proof_chain.py +472 -0
- package/runtime/proof_gate.py +442 -0
- package/runtime/provider_parity_eval.py +109 -0
- package/runtime/providers/__init__.py +0 -0
- package/runtime/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/runtime/providers/__pycache__/codex_provider.cpython-313.pyc +0 -0
- package/runtime/providers/__pycache__/gemini_provider.cpython-313.pyc +0 -0
- package/runtime/providers/__pycache__/kimi_provider.cpython-313.pyc +0 -0
- package/runtime/providers/__pycache__/opencode_provider.cpython-313.pyc +0 -0
- package/runtime/providers/codex_provider.py +129 -0
- package/runtime/providers/gemini_provider.py +143 -0
- package/runtime/providers/kimi_provider.py +167 -0
- package/runtime/providers/opencode_provider.py +99 -0
- package/runtime/release_artifact_audit.py +556 -0
- package/runtime/release_run_coordinator.py +574 -0
- package/runtime/release_surface_compiler.py +643 -0
- package/runtime/release_surface_registry.py +283 -0
- package/runtime/release_surfaces.py +320 -0
- package/runtime/remote_supervisor.py +79 -0
- package/runtime/repro_pack.py +398 -0
- package/runtime/rollback_manifest.py +143 -0
- package/runtime/router_critics.py +229 -0
- package/runtime/router_executor.py +142 -0
- package/runtime/router_selector.py +99 -0
- package/runtime/runtime_contracts.py +292 -0
- package/runtime/runtime_profile.py +133 -0
- package/runtime/security_check.py +1094 -0
- package/runtime/session_health.py +546 -0
- package/runtime/skill_evolution.py +221 -0
- package/runtime/skill_registry.py +53 -0
- package/runtime/subagent_dispatcher.py +604 -0
- package/runtime/subscription_tiers.py +258 -0
- package/runtime/team_router.py +1399 -0
- package/runtime/test_intent_lock.py +543 -0
- package/runtime/tmux_session_manager.py +172 -0
- package/runtime/tool_fabric.py +570 -0
- package/runtime/tool_plan_gate.py +460 -0
- package/runtime/tracebank.py +125 -0
- package/runtime/untrusted_content.py +360 -0
- package/runtime/validate.py +293 -0
- package/runtime/verdict_schema.py +198 -0
- package/runtime/verification_controller.py +235 -0
- package/runtime/verification_loop.py +73 -0
- package/runtime/vision_artifacts.py +31 -0
- package/runtime/vision_cache.py +38 -0
- package/runtime/vision_jobs.py +92 -0
- package/runtime/worker_watchdog.py +526 -0
- package/scripts/__pycache__/audit-published-artifact.cpython-313.pyc +0 -0
- package/scripts/__pycache__/check-doc-parity.cpython-313.pyc +0 -0
- package/scripts/__pycache__/check-omg-standalone-clean.cpython-313.pyc +0 -0
- package/scripts/__pycache__/github_review_helpers.cpython-313.pyc +0 -0
- package/scripts/__pycache__/omg.cpython-313.pyc +0 -0
- package/scripts/__pycache__/prepare-release-proof-fixtures.cpython-313.pyc +0 -0
- package/scripts/__pycache__/sync-release-identity.cpython-313.pyc +0 -0
- package/scripts/__pycache__/validate-release-identity.cpython-313.pyc +0 -0
- package/scripts/audit-published-artifact.py +59 -0
- package/scripts/check-omg-compat-contract-snapshot.py +137 -0
- package/scripts/check-omg-contract-snapshot.py +12 -0
- package/scripts/check-omg-public-ready.py +273 -0
- package/scripts/check-omg-standalone-clean.py +133 -0
- package/scripts/emit_host_parity.py +72 -0
- package/scripts/legacy_to_omg_migrate.py +29 -0
- package/scripts/migrate-legacy.py +464 -0
- package/scripts/omc_to_omg_migrate.py +12 -0
- package/scripts/omg.py +2962 -0
- package/scripts/pre-release-check.sh +38 -0
- package/scripts/prepare-release-proof-fixtures.py +602 -0
- package/scripts/print-canonical-version.py +80 -0
- package/scripts/settings-merge.py +289 -0
- package/scripts/sync-release-identity.py +481 -0
- package/scripts/validate-release-identity.py +632 -0
- package/scripts/verify-no-omc.sh +5 -0
- package/scripts/verify-standalone.sh +35 -0
- package/settings.json +751 -0
- package/tools/__init__.py +2 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/browser_consent.cpython-313.pyc +0 -0
- package/tools/__pycache__/browser_stealth.cpython-313.pyc +0 -0
- package/tools/__pycache__/browser_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/changelog_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/commit_splitter.cpython-313.pyc +0 -0
- package/tools/__pycache__/config_discovery.cpython-313.pyc +0 -0
- package/tools/__pycache__/config_merger.cpython-313.pyc +0 -0
- package/tools/__pycache__/dashboard_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/git_inspector.cpython-313.pyc +0 -0
- package/tools/__pycache__/lsp_client.cpython-313.pyc +0 -0
- package/tools/__pycache__/lsp_operations.cpython-313.pyc +0 -0
- package/tools/__pycache__/pr_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/python_repl.cpython-313.pyc +0 -0
- package/tools/__pycache__/python_sandbox.cpython-313.pyc +0 -0
- package/tools/__pycache__/session_snapshot.cpython-313.pyc +0 -0
- package/tools/__pycache__/ssh_manager.cpython-313.pyc +0 -0
- package/tools/__pycache__/theme_engine.cpython-313.pyc +0 -0
- package/tools/__pycache__/theme_selector.cpython-313.pyc +0 -0
- package/tools/__pycache__/web_search.cpython-313.pyc +0 -0
- package/tools/browser_consent.py +289 -0
- package/tools/browser_stealth.py +481 -0
- package/tools/browser_tool.py +448 -0
- package/tools/changelog_generator.py +347 -0
- package/tools/commit_splitter.py +749 -0
- package/tools/config_discovery.py +151 -0
- package/tools/config_merger.py +449 -0
- package/tools/dashboard_generator.py +300 -0
- package/tools/git_inspector.py +298 -0
- package/tools/lsp_client.py +275 -0
- package/tools/lsp_discovery.py +231 -0
- package/tools/lsp_operations.py +392 -0
- package/tools/pr_generator.py +404 -0
- package/tools/python_repl.py +712 -0
- package/tools/python_sandbox.py +768 -0
- package/tools/search_providers/__init__.py +77 -0
- package/tools/search_providers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/search_providers/__pycache__/brave.cpython-313.pyc +0 -0
- package/tools/search_providers/__pycache__/exa.cpython-313.pyc +0 -0
- package/tools/search_providers/__pycache__/jina.cpython-313.pyc +0 -0
- package/tools/search_providers/__pycache__/perplexity.cpython-313.pyc +0 -0
- package/tools/search_providers/__pycache__/synthetic.cpython-313.pyc +0 -0
- package/tools/search_providers/brave.py +115 -0
- package/tools/search_providers/exa.py +116 -0
- package/tools/search_providers/jina.py +104 -0
- package/tools/search_providers/perplexity.py +139 -0
- package/tools/search_providers/synthetic.py +74 -0
- package/tools/session_snapshot.py +851 -0
- package/tools/ssh_manager.py +912 -0
- package/tools/theme_engine.py +296 -0
- package/tools/theme_selector.py +137 -0
- 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
|