@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
package/scripts/omg.py
ADDED
|
@@ -0,0 +1,2962 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""OMG CLI entrypoint.
|
|
3
|
+
|
|
4
|
+
Implements practical command-line flows for:
|
|
5
|
+
- omg ship
|
|
6
|
+
- omg fix --issue
|
|
7
|
+
- omg secure
|
|
8
|
+
- omg security check
|
|
9
|
+
- omg maintainer
|
|
10
|
+
- omg trust review
|
|
11
|
+
- omg runtime dispatch
|
|
12
|
+
- omg lab train / omg lab eval
|
|
13
|
+
- omg forge run
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
from dataclasses import asdict
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
import base64
|
|
21
|
+
import hashlib
|
|
22
|
+
import importlib.util
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
import sys
|
|
27
|
+
from typing import Any, cast
|
|
28
|
+
|
|
29
|
+
# --- Path resolution (never relies on CWD) ---
|
|
30
|
+
SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
31
|
+
ROOT_DIR = Path(SCRIPTS_DIR).resolve().parent
|
|
32
|
+
if str(ROOT_DIR) not in sys.path:
|
|
33
|
+
sys.path.insert(0, str(ROOT_DIR))
|
|
34
|
+
|
|
35
|
+
if sys.version_info < (3, 10):
|
|
36
|
+
sys.exit(
|
|
37
|
+
"\n\u274c OMG requires Python 3.10 or newer.\n"
|
|
38
|
+
" Found: Python {}.{}\n"
|
|
39
|
+
" Install Python 3.10+ and ensure 'python3' resolves to it.\n".format(
|
|
40
|
+
sys.version_info.major, sys.version_info.minor
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
from hooks._common import bootstrap_runtime_paths
|
|
45
|
+
|
|
46
|
+
bootstrap_runtime_paths(__file__)
|
|
47
|
+
|
|
48
|
+
from hooks.policy_engine import evaluate_bash_command
|
|
49
|
+
from hooks.shadow_manager import create_evidence_pack
|
|
50
|
+
from hooks.trust_review import review_config_change, write_trust_manifest
|
|
51
|
+
from lab.pipeline import publish_artifact, run_pipeline, run_pipeline_with_evidence
|
|
52
|
+
from runtime.forge_agents import dispatch_specialists, resolve_specialists
|
|
53
|
+
from runtime.forge_contracts import load_forge_mvp, validate_forge_job
|
|
54
|
+
from runtime.forge_run_id import normalize_run_id
|
|
55
|
+
from runtime.issue_surface import IssueSurface
|
|
56
|
+
from runtime.dispatcher import dispatch_runtime
|
|
57
|
+
from runtime.api_twin import ingest_contract, record_fixture, serve_fixture, verify_fixture
|
|
58
|
+
from runtime.data_lineage import build_lineage_manifest
|
|
59
|
+
from runtime.eval_gate import evaluate_trace
|
|
60
|
+
from runtime.incident_replay import build_incident_pack
|
|
61
|
+
from runtime.domain_packs import get_domain_pack_contract
|
|
62
|
+
from runtime.doc_generator import generate_docs, check_docs, GENERATED_ARTIFACTS
|
|
63
|
+
from runtime.release_artifact_audit import (
|
|
64
|
+
format_release_audit_text,
|
|
65
|
+
resolve_github_token,
|
|
66
|
+
run_release_artifact_audit,
|
|
67
|
+
)
|
|
68
|
+
from runtime.preflight import run_preflight
|
|
69
|
+
from runtime.remote_supervisor import issue_local_supervisor_session, verify_local_supervisor_token
|
|
70
|
+
from runtime.security_check import run_security_check
|
|
71
|
+
from runtime.contract_compiler import (
|
|
72
|
+
build_release_readiness,
|
|
73
|
+
compile_contract_outputs,
|
|
74
|
+
compile_method_artifacts,
|
|
75
|
+
validate_contract_registry,
|
|
76
|
+
)
|
|
77
|
+
from runtime.tracebank import record_trace
|
|
78
|
+
from runtime.compat import (
|
|
79
|
+
DEFAULT_CONTRACT_SNAPSHOT_PATH,
|
|
80
|
+
DEFAULT_GAP_REPORT_PATH,
|
|
81
|
+
build_contract_snapshot_payload,
|
|
82
|
+
build_compat_gap_report,
|
|
83
|
+
dispatch_compat_skill,
|
|
84
|
+
get_compat_skill_contract,
|
|
85
|
+
list_compat_skill_contracts,
|
|
86
|
+
list_compat_skills,
|
|
87
|
+
run_doctor,
|
|
88
|
+
run_doctor_fix,
|
|
89
|
+
run_env_doctor,
|
|
90
|
+
)
|
|
91
|
+
from runtime.validate import run_validate, format_text as validate_format_text
|
|
92
|
+
from runtime.plugin_diagnostics import approve_plugin, run_plugin_diagnostics
|
|
93
|
+
from runtime.adoption import CANONICAL_VERSION, VALID_PRESETS
|
|
94
|
+
from runtime.install_planner import compute_install_plan, execute_plan, InstallAction
|
|
95
|
+
from runtime.canonical_surface import get_canonical_hosts
|
|
96
|
+
from runtime.ecosystem import ecosystem_status, list_ecosystem_repos, sync_ecosystem_repos
|
|
97
|
+
from runtime.team_router import TeamDispatchRequest, dispatch_team, execute_ccg_mode, execute_crazy_mode
|
|
98
|
+
from runtime.release_run_coordinator import resolve_current_run_id
|
|
99
|
+
from runtime.subscription_tiers import detect_tier
|
|
100
|
+
from runtime.policy_pack_loader import list_policy_packs, load_policy_pack
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
CANONICAL_HOST_CHOICES = tuple(get_canonical_hosts())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _parse_simple_idea_yaml(path: str) -> dict[str, Any]:
|
|
107
|
+
"""Minimal parser for `.omg/idea.yml` template shape."""
|
|
108
|
+
idea: dict[str, Any] = {
|
|
109
|
+
"goal": "",
|
|
110
|
+
"constraints": [],
|
|
111
|
+
"acceptance": [],
|
|
112
|
+
"risk": {"security": [], "performance": [], "compatibility": []},
|
|
113
|
+
"evidence_required": {"tests": [], "security_scans": [], "reproducibility": [], "artifacts": []},
|
|
114
|
+
}
|
|
115
|
+
section: str | None = None
|
|
116
|
+
subsection: str | None = None
|
|
117
|
+
|
|
118
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
119
|
+
for raw in f:
|
|
120
|
+
line = raw.rstrip("\n")
|
|
121
|
+
stripped = line.strip()
|
|
122
|
+
if not stripped or stripped.startswith("#"):
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
if stripped.startswith("goal:"):
|
|
126
|
+
idea["goal"] = stripped.split(":", 1)[1].strip().strip("\"'")
|
|
127
|
+
section = None
|
|
128
|
+
subsection = None
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
if stripped in {"constraints:", "acceptance:", "risk:", "evidence_required:"}:
|
|
132
|
+
section = stripped[:-1]
|
|
133
|
+
subsection = None
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
if section in {"risk", "evidence_required"} and stripped.endswith(":") and not stripped.startswith("- "):
|
|
137
|
+
subsection = stripped[:-1]
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
if stripped.startswith("- "):
|
|
141
|
+
value = stripped[2:].strip().strip("\"'")
|
|
142
|
+
if section in {"constraints", "acceptance"}:
|
|
143
|
+
idea[section].append(value)
|
|
144
|
+
elif section in {"risk", "evidence_required"} and subsection:
|
|
145
|
+
idea[section].setdefault(subsection, []).append(value)
|
|
146
|
+
|
|
147
|
+
return idea
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _load_json(path: str) -> dict[str, Any]:
|
|
151
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
152
|
+
data = json.load(f)
|
|
153
|
+
if not isinstance(data, dict):
|
|
154
|
+
raise ValueError(f"Expected object JSON in {path}")
|
|
155
|
+
return data
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _ensure_project_dir() -> str:
|
|
159
|
+
return os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _now_run_id() -> str:
|
|
163
|
+
return datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def cmd_ship(args: argparse.Namespace) -> int:
|
|
167
|
+
project_dir = _ensure_project_dir()
|
|
168
|
+
release_audit = run_release_artifact_audit(
|
|
169
|
+
ROOT_DIR,
|
|
170
|
+
repo="trac3r00/OMG",
|
|
171
|
+
version=CANONICAL_VERSION,
|
|
172
|
+
github_token=resolve_github_token(),
|
|
173
|
+
)
|
|
174
|
+
if release_audit.get("overall_status") != "ok":
|
|
175
|
+
print(json.dumps({
|
|
176
|
+
"status": "error",
|
|
177
|
+
"error_code": "RELEASE_AUDIT_BLOCKED",
|
|
178
|
+
"message": "Release artifact audit drift blocks omg ship.",
|
|
179
|
+
"release_audit": release_audit,
|
|
180
|
+
}, indent=2))
|
|
181
|
+
return 2
|
|
182
|
+
idea_path = args.idea
|
|
183
|
+
idea = _parse_simple_idea_yaml(idea_path) if idea_path.endswith((".yml", ".yaml")) else _load_json(idea_path)
|
|
184
|
+
|
|
185
|
+
runtime = args.runtime
|
|
186
|
+
dispatched = dispatch_runtime(runtime, idea)
|
|
187
|
+
if dispatched.get("status") != "ok":
|
|
188
|
+
print(json.dumps(dispatched, indent=2))
|
|
189
|
+
return 2
|
|
190
|
+
|
|
191
|
+
run_id = args.run_id or _now_run_id()
|
|
192
|
+
verification = dispatched.get("verification", {})
|
|
193
|
+
checks = verification.get("checks", []) if isinstance(verification, dict) else []
|
|
194
|
+
preflight = run_preflight(project_dir, goal=str(idea.get("goal", "")))
|
|
195
|
+
security_result = run_security_check(project_dir=project_dir, scope=".")
|
|
196
|
+
trace = record_trace(
|
|
197
|
+
project_dir,
|
|
198
|
+
trace_type="ship",
|
|
199
|
+
route=preflight["route"],
|
|
200
|
+
status="ok",
|
|
201
|
+
plan=dispatched.get("plan", {}),
|
|
202
|
+
verify=verification if isinstance(verification, dict) else {},
|
|
203
|
+
metadata={"runtime": runtime, "run_id": run_id},
|
|
204
|
+
)
|
|
205
|
+
eval_result = evaluate_trace(
|
|
206
|
+
project_dir,
|
|
207
|
+
trace_id=trace["trace_id"],
|
|
208
|
+
suites=["planning", "security"],
|
|
209
|
+
metrics={
|
|
210
|
+
"planning": 1.0 if dispatched.get("status") == "ok" else 0.0,
|
|
211
|
+
"security": max(float(security_result["trust_scores"].get("overall", 0.0)), 0.0),
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
lineage = build_lineage_manifest(
|
|
215
|
+
project_dir,
|
|
216
|
+
artifact_type="evidence-pack",
|
|
217
|
+
sources=[{"kind": "repo", "path": ".", "license": "MIT"}],
|
|
218
|
+
privacy="internal",
|
|
219
|
+
license="MIT",
|
|
220
|
+
derivation={"trace_id": trace["trace_id"], "route": preflight["route"], "eval_path": eval_result["path"]},
|
|
221
|
+
trace_id=trace["trace_id"],
|
|
222
|
+
)
|
|
223
|
+
evidence_path = create_evidence_pack(
|
|
224
|
+
project_dir,
|
|
225
|
+
run_id,
|
|
226
|
+
tests=checks if isinstance(checks, list) else [],
|
|
227
|
+
security_scans=security_result.get("security_scans", []),
|
|
228
|
+
diff_summary={"runtime": runtime, "goal": idea.get("goal", "")},
|
|
229
|
+
reproducibility={"command": f"omg ship --runtime {runtime} --idea {idea_path}"},
|
|
230
|
+
unresolved_risks=security_result.get("unresolved_risks", []),
|
|
231
|
+
provenance=security_result["provenance"],
|
|
232
|
+
trust_scores=security_result["trust_scores"],
|
|
233
|
+
api_twin={"recommended_route": preflight["route"] if preflight["route"] == "api-twin" else ""},
|
|
234
|
+
route_metadata=preflight,
|
|
235
|
+
trace_ids=[trace["trace_id"]],
|
|
236
|
+
lineage=lineage,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
out = {
|
|
240
|
+
"status": "ok",
|
|
241
|
+
"command": "ship",
|
|
242
|
+
"runtime": runtime,
|
|
243
|
+
"run_id": run_id,
|
|
244
|
+
"goal": idea.get("goal", ""),
|
|
245
|
+
"evidence_path": os.path.relpath(evidence_path, project_dir),
|
|
246
|
+
"trace_id": trace["trace_id"],
|
|
247
|
+
"eval_path": eval_result["path"],
|
|
248
|
+
}
|
|
249
|
+
print(json.dumps(out, indent=2))
|
|
250
|
+
return 0
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def cmd_fix(args: argparse.Namespace) -> int:
|
|
254
|
+
project_dir = _ensure_project_dir()
|
|
255
|
+
issue_surface = IssueSurface(project_dir=project_dir)
|
|
256
|
+
scan_run_id = f"fix-{_sanitize_token(args.issue)}-{_now_run_id()}"
|
|
257
|
+
issue_report = issue_surface.scan(
|
|
258
|
+
scan_run_id,
|
|
259
|
+
surfaces=["live_session", "plugin_interop", "governed_tools", "forge_runs"],
|
|
260
|
+
)
|
|
261
|
+
goal = f"Fix issue {args.issue}"
|
|
262
|
+
dispatched = dispatch_runtime(args.runtime, {"goal": goal, "acceptance": [f"issue-{args.issue}-resolved"]})
|
|
263
|
+
dispatched["issue_surface"] = {
|
|
264
|
+
"run_id": issue_report.run_id,
|
|
265
|
+
"report_path": str(issue_report.summary.get("report_path", "")),
|
|
266
|
+
"summary": issue_report.summary,
|
|
267
|
+
}
|
|
268
|
+
print(json.dumps(dispatched, indent=2))
|
|
269
|
+
return 0 if dispatched.get("status") == "ok" else 2
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def cmd_issue(args: argparse.Namespace) -> int:
|
|
273
|
+
run_id = args.run_id or _now_run_id()
|
|
274
|
+
surfaces = [item.strip() for item in str(args.surfaces).split(",") if item.strip()]
|
|
275
|
+
issue_surface = IssueSurface(project_dir=_ensure_project_dir())
|
|
276
|
+
report = issue_surface.scan(run_id, surfaces=surfaces or None)
|
|
277
|
+
|
|
278
|
+
result: dict[str, Any] = {
|
|
279
|
+
"schema": "IssueCommandResult",
|
|
280
|
+
"status": "ok",
|
|
281
|
+
"run_id": run_id,
|
|
282
|
+
"report_path": str(report.summary.get("report_path", "")),
|
|
283
|
+
"report": report.to_dict(),
|
|
284
|
+
}
|
|
285
|
+
if args.simulate_surface and args.simulate_scenario:
|
|
286
|
+
result["simulation"] = issue_surface.simulate_failure(args.simulate_surface, args.simulate_scenario)
|
|
287
|
+
print(json.dumps(result, indent=2))
|
|
288
|
+
return 0
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def cmd_secure(args: argparse.Namespace) -> int:
|
|
292
|
+
decision = evaluate_bash_command(args.command)
|
|
293
|
+
print(json.dumps(decision.to_dict(), indent=2))
|
|
294
|
+
return 0 if decision.action != "deny" else 3
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def cmd_undo(args: argparse.Namespace) -> int:
|
|
298
|
+
from runtime.interaction_journal import InteractionJournal
|
|
299
|
+
|
|
300
|
+
project_dir = _ensure_project_dir()
|
|
301
|
+
run_id = resolve_current_run_id(project_dir=project_dir)
|
|
302
|
+
result = InteractionJournal(project_dir).undo(args.step_id, run_id=run_id)
|
|
303
|
+
print(json.dumps(result, indent=2))
|
|
304
|
+
return 2 if result.get("status") == "rollback_failed" else 0
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def cmd_security_check(args: argparse.Namespace) -> int:
|
|
308
|
+
waivers = json.loads(args.waivers_json) if args.waivers_json else None
|
|
309
|
+
result = run_security_check(
|
|
310
|
+
project_dir=_ensure_project_dir(),
|
|
311
|
+
scope=args.scope,
|
|
312
|
+
include_live_enrichment=bool(args.live_enrichment),
|
|
313
|
+
waivers=waivers,
|
|
314
|
+
)
|
|
315
|
+
print(json.dumps(result, indent=2))
|
|
316
|
+
return 0
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _sanitize_token(value: str) -> str:
|
|
320
|
+
cleaned = "".join(ch if ch.isalnum() or ch in {"-", "_"} else "-" for ch in str(value).strip())
|
|
321
|
+
return cleaned or "unknown"
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def cmd_waive_tests(args: argparse.Namespace) -> int:
|
|
325
|
+
project_dir = _ensure_project_dir()
|
|
326
|
+
issued_at = datetime.now(timezone.utc).isoformat()
|
|
327
|
+
lock_id = str(args.lock_id).strip()
|
|
328
|
+
reason = str(args.reason).strip()
|
|
329
|
+
run_id = resolve_current_run_id(project_dir=project_dir)
|
|
330
|
+
|
|
331
|
+
evidence_dir = Path(project_dir) / ".omg" / "evidence"
|
|
332
|
+
evidence_dir.mkdir(parents=True, exist_ok=True)
|
|
333
|
+
|
|
334
|
+
artifact_name = f"waiver-tests-{_sanitize_token(lock_id)}-{_now_run_id()}.json"
|
|
335
|
+
artifact_path = evidence_dir / artifact_name
|
|
336
|
+
artifact_rel_path = str(artifact_path.relative_to(project_dir)).replace("\\", "/")
|
|
337
|
+
|
|
338
|
+
payload = {
|
|
339
|
+
"schema": "WaiverEvidence",
|
|
340
|
+
"schema_version": 1,
|
|
341
|
+
"lock_id": lock_id,
|
|
342
|
+
"run_id": run_id,
|
|
343
|
+
"reason": reason,
|
|
344
|
+
"issued_at": issued_at,
|
|
345
|
+
"artifact_path": artifact_rel_path,
|
|
346
|
+
}
|
|
347
|
+
artifact_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
|
|
348
|
+
|
|
349
|
+
print(json.dumps(payload, indent=2))
|
|
350
|
+
return 0
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def cmd_api_twin_ingest(args: argparse.Namespace) -> int:
|
|
354
|
+
result = ingest_contract(_ensure_project_dir(), name=args.name, source_path=args.source)
|
|
355
|
+
print(json.dumps(result, indent=2))
|
|
356
|
+
return 0
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def cmd_api_twin_record(args: argparse.Namespace) -> int:
|
|
360
|
+
result = record_fixture(
|
|
361
|
+
_ensure_project_dir(),
|
|
362
|
+
name=args.name,
|
|
363
|
+
endpoint=args.endpoint,
|
|
364
|
+
cassette_version=args.cassette_version,
|
|
365
|
+
request=json.loads(args.request_json),
|
|
366
|
+
response=json.loads(args.response_json),
|
|
367
|
+
validated=bool(args.validated),
|
|
368
|
+
redactions=json.loads(args.redactions_json) if args.redactions_json else None,
|
|
369
|
+
)
|
|
370
|
+
print(json.dumps(result, indent=2))
|
|
371
|
+
return 0
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def cmd_api_twin_serve(args: argparse.Namespace) -> int:
|
|
375
|
+
result = serve_fixture(
|
|
376
|
+
_ensure_project_dir(),
|
|
377
|
+
name=args.name,
|
|
378
|
+
endpoint=args.endpoint,
|
|
379
|
+
cassette_version=args.cassette_version,
|
|
380
|
+
latency_ms=int(args.latency_ms),
|
|
381
|
+
failure_mode=args.failure_mode,
|
|
382
|
+
schema_drift=bool(args.schema_drift),
|
|
383
|
+
)
|
|
384
|
+
print(json.dumps(result, indent=2))
|
|
385
|
+
return 0
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def cmd_api_twin_verify(args: argparse.Namespace) -> int:
|
|
389
|
+
result = verify_fixture(
|
|
390
|
+
_ensure_project_dir(),
|
|
391
|
+
name=args.name,
|
|
392
|
+
endpoint=args.endpoint,
|
|
393
|
+
cassette_version=args.cassette_version,
|
|
394
|
+
live_response=json.loads(args.live_response_json),
|
|
395
|
+
)
|
|
396
|
+
print(json.dumps(result, indent=2))
|
|
397
|
+
return 0
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def cmd_preflight(args: argparse.Namespace) -> int:
|
|
401
|
+
result = run_preflight(_ensure_project_dir(), goal=args.goal)
|
|
402
|
+
print(json.dumps(result, indent=2))
|
|
403
|
+
return 0
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _emit_not_implemented_stub(command: str, *, details: dict[str, Any] | None = None) -> int:
|
|
407
|
+
payload: dict[str, Any] = {
|
|
408
|
+
"schema": "OperatorContractStub",
|
|
409
|
+
"status": "not_implemented",
|
|
410
|
+
"error_code": "NOT_YET_IMPLEMENTED",
|
|
411
|
+
"command": command,
|
|
412
|
+
"message": f"{command} is registered but not yet implemented",
|
|
413
|
+
}
|
|
414
|
+
if details:
|
|
415
|
+
payload.update(details)
|
|
416
|
+
print(json.dumps(payload, indent=2))
|
|
417
|
+
return 2
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def cmd_resolve_policy(args: argparse.Namespace) -> int:
|
|
421
|
+
fmt = getattr(args, "format", "json")
|
|
422
|
+
provider = getattr(args, "provider", "claude")
|
|
423
|
+
|
|
424
|
+
tier_result = detect_tier(provider, project_dir=_ensure_project_dir())
|
|
425
|
+
|
|
426
|
+
pack_ids = list_policy_packs()
|
|
427
|
+
packs: list[dict[str, Any]] = []
|
|
428
|
+
for pack_id in pack_ids:
|
|
429
|
+
try:
|
|
430
|
+
pack = load_policy_pack(pack_id)
|
|
431
|
+
packs.append(dict(pack))
|
|
432
|
+
except Exception:
|
|
433
|
+
packs.append({"id": pack_id, "error": "failed_to_load"})
|
|
434
|
+
|
|
435
|
+
effective_policy: dict[str, Any] = {
|
|
436
|
+
"tool_restrictions": [],
|
|
437
|
+
"network_posture": "open",
|
|
438
|
+
"approval_threshold": 1,
|
|
439
|
+
"protected_paths": [],
|
|
440
|
+
"evidence_requirements": [],
|
|
441
|
+
"data_sharing": "allowed",
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
overrides: list[dict[str, Any]] = []
|
|
445
|
+
for pack in packs:
|
|
446
|
+
if "error" in pack:
|
|
447
|
+
continue
|
|
448
|
+
for list_field in ("tool_restrictions", "protected_paths", "evidence_requirements"):
|
|
449
|
+
pack_values = pack.get(list_field, [])
|
|
450
|
+
if not isinstance(pack_values, list):
|
|
451
|
+
continue
|
|
452
|
+
existing = effective_policy.get(list_field, [])
|
|
453
|
+
for v in pack_values:
|
|
454
|
+
if v not in existing:
|
|
455
|
+
existing.append(v)
|
|
456
|
+
overrides.append({"field": list_field, "value": v, "source": pack.get("id", "unknown")})
|
|
457
|
+
effective_policy[list_field] = existing
|
|
458
|
+
|
|
459
|
+
for scalar_field in ("network_posture", "data_sharing"):
|
|
460
|
+
pack_value = pack.get(scalar_field)
|
|
461
|
+
if pack_value and pack_value != effective_policy.get(scalar_field):
|
|
462
|
+
overrides.append({
|
|
463
|
+
"field": scalar_field,
|
|
464
|
+
"old_value": effective_policy[scalar_field],
|
|
465
|
+
"new_value": pack_value,
|
|
466
|
+
"source": pack.get("id", "unknown"),
|
|
467
|
+
})
|
|
468
|
+
effective_policy[scalar_field] = pack_value
|
|
469
|
+
|
|
470
|
+
pack_threshold = pack.get("approval_threshold", 1)
|
|
471
|
+
if isinstance(pack_threshold, int) and pack_threshold > effective_policy["approval_threshold"]:
|
|
472
|
+
overrides.append({
|
|
473
|
+
"field": "approval_threshold",
|
|
474
|
+
"old_value": effective_policy["approval_threshold"],
|
|
475
|
+
"new_value": pack_threshold,
|
|
476
|
+
"source": pack.get("id", "unknown"),
|
|
477
|
+
})
|
|
478
|
+
effective_policy["approval_threshold"] = pack_threshold
|
|
479
|
+
|
|
480
|
+
provenance: list[dict[str, Any]] = [
|
|
481
|
+
{
|
|
482
|
+
"source": "tier_detection",
|
|
483
|
+
"provider": provider,
|
|
484
|
+
"tier": tier_result["tier"],
|
|
485
|
+
"confidence": tier_result["confidence"],
|
|
486
|
+
"provenance": tier_result["provenance"],
|
|
487
|
+
},
|
|
488
|
+
]
|
|
489
|
+
for pack in packs:
|
|
490
|
+
provenance.append({
|
|
491
|
+
"source": "policy_pack",
|
|
492
|
+
"pack_id": pack.get("id", ""),
|
|
493
|
+
"description": pack.get("description", ""),
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
output: dict[str, Any] = {
|
|
497
|
+
"schema": "EffectivePolicy",
|
|
498
|
+
"tier": dict(tier_result),
|
|
499
|
+
"channel": "public",
|
|
500
|
+
"preset": "balanced",
|
|
501
|
+
"packs": [{"id": p.get("id", ""), "description": p.get("description", "")} for p in packs],
|
|
502
|
+
"effective_policy": effective_policy,
|
|
503
|
+
"overrides": overrides,
|
|
504
|
+
"provenance": provenance,
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if fmt == "json":
|
|
508
|
+
print(json.dumps(output, indent=2))
|
|
509
|
+
else:
|
|
510
|
+
print("=== Effective Policy ===")
|
|
511
|
+
print(f"Tier: {tier_result['tier']} (confidence: {tier_result['confidence']:.2f})")
|
|
512
|
+
print("Channel: public")
|
|
513
|
+
print("Preset: balanced")
|
|
514
|
+
if packs:
|
|
515
|
+
print(f"\nActive packs ({len(packs)}):")
|
|
516
|
+
for p in packs:
|
|
517
|
+
print(f" - {p.get('id', '')}: {p.get('description', '')}")
|
|
518
|
+
if overrides:
|
|
519
|
+
print(f"\nOverrides ({len(overrides)}):")
|
|
520
|
+
for o in overrides:
|
|
521
|
+
print(f" {o['field']}: {o.get('value', o.get('new_value', ''))} (from {o['source']})")
|
|
522
|
+
return 0
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def cmd_policy_pack_list(args: argparse.Namespace) -> int:
|
|
526
|
+
fmt = getattr(args, "format", "json")
|
|
527
|
+
pack_ids = list_policy_packs()
|
|
528
|
+
packs: list[dict[str, Any]] = []
|
|
529
|
+
for pack_id in pack_ids:
|
|
530
|
+
try:
|
|
531
|
+
pack = load_policy_pack(pack_id)
|
|
532
|
+
packs.append(dict(pack))
|
|
533
|
+
except Exception:
|
|
534
|
+
packs.append({"id": pack_id, "error": "failed_to_load"})
|
|
535
|
+
|
|
536
|
+
output: dict[str, Any] = {
|
|
537
|
+
"schema": "PolicyPackList",
|
|
538
|
+
"packs": packs,
|
|
539
|
+
"count": len(packs),
|
|
540
|
+
}
|
|
541
|
+
if fmt == "json":
|
|
542
|
+
print(json.dumps(output, indent=2))
|
|
543
|
+
else:
|
|
544
|
+
print(f"Policy Packs ({len(packs)}):")
|
|
545
|
+
for p in packs:
|
|
546
|
+
print(f" - {p.get('id', 'unknown')}: {p.get('description', 'N/A')}")
|
|
547
|
+
return 0
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def cmd_policy_pack_diff(args: argparse.Namespace) -> int:
|
|
551
|
+
fmt = getattr(args, "format", "json")
|
|
552
|
+
pack_id = getattr(args, "pack_id", "")
|
|
553
|
+
if not pack_id:
|
|
554
|
+
err = {"schema": "PolicyPackDiff", "status": "error", "reason": "pack_id is required"}
|
|
555
|
+
print(json.dumps(err, indent=2) if fmt == "json" else f"Error: {err['reason']}")
|
|
556
|
+
return 1
|
|
557
|
+
try:
|
|
558
|
+
pack = load_policy_pack(pack_id)
|
|
559
|
+
except Exception:
|
|
560
|
+
err = {"schema": "PolicyPackDiff", "status": "error", "reason": f"pack '{pack_id}' not found"}
|
|
561
|
+
print(json.dumps(err, indent=2) if fmt == "json" else f"Error: {err['reason']}")
|
|
562
|
+
return 1
|
|
563
|
+
pack_dict = dict(pack)
|
|
564
|
+
overrides = pack_dict.get("overrides", pack_dict.get("tool_restrictions", []))
|
|
565
|
+
output: dict[str, Any] = {
|
|
566
|
+
"schema": "PolicyPackDiff",
|
|
567
|
+
"status": "ok",
|
|
568
|
+
"pack_id": pack_id,
|
|
569
|
+
"description": pack_dict.get("description", ""),
|
|
570
|
+
"overrides": overrides,
|
|
571
|
+
"fields_affected": len(overrides) if isinstance(overrides, list) else 0,
|
|
572
|
+
}
|
|
573
|
+
if fmt == "json":
|
|
574
|
+
print(json.dumps(output, indent=2))
|
|
575
|
+
else:
|
|
576
|
+
print(f"Policy Pack Diff: {pack_id}")
|
|
577
|
+
print(f" Description: {output['description']}")
|
|
578
|
+
print(f" Overrides ({output['fields_affected']}):")
|
|
579
|
+
if isinstance(overrides, list):
|
|
580
|
+
for o in overrides:
|
|
581
|
+
print(f" - {o}")
|
|
582
|
+
return 0
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def cmd_policy_pack_scaffold(args: argparse.Namespace) -> int:
|
|
586
|
+
fmt = getattr(args, "format", "json")
|
|
587
|
+
pack_id = getattr(args, "pack_id", "new-pack")
|
|
588
|
+
scaffold: dict[str, Any] = {
|
|
589
|
+
"schema": "PolicyPackScaffold",
|
|
590
|
+
"status": "ok",
|
|
591
|
+
"pack_id": pack_id,
|
|
592
|
+
"template": {
|
|
593
|
+
"id": pack_id,
|
|
594
|
+
"description": f"Custom policy pack: {pack_id}",
|
|
595
|
+
"overrides": {},
|
|
596
|
+
"tool_restrictions": [],
|
|
597
|
+
"required_evidence": [],
|
|
598
|
+
},
|
|
599
|
+
"output_path": f"registry/policy-packs/{pack_id}.yaml",
|
|
600
|
+
}
|
|
601
|
+
if fmt == "json":
|
|
602
|
+
print(json.dumps(scaffold, indent=2))
|
|
603
|
+
else:
|
|
604
|
+
import yaml as yaml_mod
|
|
605
|
+
print(f"Scaffold for policy pack '{pack_id}':")
|
|
606
|
+
print(f" Output: {scaffold['output_path']}")
|
|
607
|
+
print(" Template:")
|
|
608
|
+
print(yaml_mod.dump(scaffold["template"], default_flow_style=False, indent=2))
|
|
609
|
+
return 0
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def cmd_policy_pack_keygen(args: argparse.Namespace) -> int:
|
|
613
|
+
fmt = getattr(args, "format", "json")
|
|
614
|
+
output_path = getattr(args, "output", None)
|
|
615
|
+
add_to_trust_root = getattr(args, "add_to_trust_root", False)
|
|
616
|
+
|
|
617
|
+
from registry.verify_artifact import _load_ed25519_backend, _key_id_from_public_key
|
|
618
|
+
|
|
619
|
+
try:
|
|
620
|
+
serialization, Ed25519PrivateKey, _ = _load_ed25519_backend()
|
|
621
|
+
except ModuleNotFoundError as exc:
|
|
622
|
+
print(json.dumps({"schema": "PolicyPackKeygen", "status": "error", "error": str(exc)}))
|
|
623
|
+
return 1
|
|
624
|
+
|
|
625
|
+
private_key = Ed25519PrivateKey.generate()
|
|
626
|
+
private_raw = private_key.private_bytes(
|
|
627
|
+
encoding=serialization.Encoding.Raw,
|
|
628
|
+
format=serialization.PrivateFormat.Raw,
|
|
629
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
630
|
+
)
|
|
631
|
+
public_key_raw = private_key.public_key().public_bytes(
|
|
632
|
+
encoding=serialization.Encoding.Raw,
|
|
633
|
+
format=serialization.PublicFormat.Raw,
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
key_id = _key_id_from_public_key(public_key_raw)
|
|
637
|
+
private_b64 = base64.b64encode(private_raw).decode("ascii")
|
|
638
|
+
public_b64 = base64.b64encode(public_key_raw).decode("ascii")
|
|
639
|
+
|
|
640
|
+
keypair: dict[str, Any] = {
|
|
641
|
+
"key_id": key_id,
|
|
642
|
+
"algorithm": "ed25519-minisign",
|
|
643
|
+
"private_key": private_b64,
|
|
644
|
+
"public_key": public_b64,
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
written_path: str | None = None
|
|
648
|
+
if output_path:
|
|
649
|
+
out = Path(output_path)
|
|
650
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
651
|
+
out.write_text(json.dumps(keypair, indent=2) + "\n", encoding="utf-8")
|
|
652
|
+
written_path = str(out)
|
|
653
|
+
|
|
654
|
+
if add_to_trust_root:
|
|
655
|
+
trust_root_path = ROOT_DIR / "registry" / "trusted_signers.json"
|
|
656
|
+
try:
|
|
657
|
+
trust_root: dict[str, Any] = json.loads(trust_root_path.read_text(encoding="utf-8"))
|
|
658
|
+
except Exception:
|
|
659
|
+
trust_root = {"version": 1, "signers": []}
|
|
660
|
+
|
|
661
|
+
signer_entry: dict[str, Any] = {
|
|
662
|
+
"key_id": key_id,
|
|
663
|
+
"algorithm": "ed25519-minisign",
|
|
664
|
+
"public_key": public_b64,
|
|
665
|
+
"status": "active",
|
|
666
|
+
"usage": ["dev", "test", "offline-attestation"],
|
|
667
|
+
"owner": "omg-local",
|
|
668
|
+
"notes": "Generated by policy-pack keygen",
|
|
669
|
+
}
|
|
670
|
+
signers_list: list[Any] = trust_root.get("signers", [])
|
|
671
|
+
existing_ids = {s.get("key_id") for s in signers_list if isinstance(s, dict)}
|
|
672
|
+
if key_id not in existing_ids:
|
|
673
|
+
signers_list.append(signer_entry)
|
|
674
|
+
trust_root["signers"] = signers_list
|
|
675
|
+
trust_root_path.write_text(json.dumps(trust_root, indent=2) + "\n", encoding="utf-8")
|
|
676
|
+
|
|
677
|
+
output: dict[str, Any] = {
|
|
678
|
+
"schema": "PolicyPackKeygen",
|
|
679
|
+
"status": "ok",
|
|
680
|
+
"key_id": key_id,
|
|
681
|
+
"algorithm": "ed25519-minisign",
|
|
682
|
+
"public_key": public_b64,
|
|
683
|
+
}
|
|
684
|
+
if written_path:
|
|
685
|
+
output["output_path"] = written_path
|
|
686
|
+
|
|
687
|
+
if fmt == "json":
|
|
688
|
+
print(json.dumps(output, indent=2))
|
|
689
|
+
else:
|
|
690
|
+
print("Generated Ed25519 keypair:")
|
|
691
|
+
print(f" Key ID: {key_id}")
|
|
692
|
+
print(" Algorithm: ed25519-minisign")
|
|
693
|
+
print(f" Public key: {public_b64}")
|
|
694
|
+
if written_path:
|
|
695
|
+
print(f" Written to: {written_path}")
|
|
696
|
+
if add_to_trust_root:
|
|
697
|
+
print(" Added to trust root: registry/trusted_signers.json")
|
|
698
|
+
return 0
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def cmd_policy_pack_sign(args: argparse.Namespace) -> int:
|
|
702
|
+
fmt = getattr(args, "format", "json")
|
|
703
|
+
pack_id = getattr(args, "pack_id", "")
|
|
704
|
+
key_id = getattr(args, "key_id", None)
|
|
705
|
+
key_path = getattr(args, "key_path", None)
|
|
706
|
+
|
|
707
|
+
from registry.approval_artifact import create_approval_artifact
|
|
708
|
+
from registry.verify_artifact import (
|
|
709
|
+
_canonical_json, _DEFAULT_DEV_SIGNER_KEY_ID, _load_trusted_signers,
|
|
710
|
+
_load_ed25519_backend, _key_id_from_public_key,
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
if not key_id:
|
|
714
|
+
signers = _load_trusted_signers()
|
|
715
|
+
key_id = next(iter(signers), _DEFAULT_DEV_SIGNER_KEY_ID) if signers else _DEFAULT_DEV_SIGNER_KEY_ID
|
|
716
|
+
|
|
717
|
+
private_key = os.environ.get("OMG_SIGNING_KEY", "")
|
|
718
|
+
if not private_key and key_path:
|
|
719
|
+
try:
|
|
720
|
+
key_data = Path(key_path).read_text(encoding="utf-8").strip()
|
|
721
|
+
try:
|
|
722
|
+
key_json = json.loads(key_data)
|
|
723
|
+
if isinstance(key_json, dict) and "private_key" in key_json:
|
|
724
|
+
private_key = key_json["private_key"]
|
|
725
|
+
if not key_id or key_id == _DEFAULT_DEV_SIGNER_KEY_ID:
|
|
726
|
+
key_id = key_json.get("key_id", key_id)
|
|
727
|
+
else:
|
|
728
|
+
private_key = key_data
|
|
729
|
+
except json.JSONDecodeError:
|
|
730
|
+
private_key = key_data
|
|
731
|
+
except Exception as exc:
|
|
732
|
+
print(json.dumps({"schema": "PolicyPackSign", "status": "error", "error": str(exc)}))
|
|
733
|
+
return 1
|
|
734
|
+
if not private_key:
|
|
735
|
+
print(json.dumps({"schema": "PolicyPackSign", "status": "error",
|
|
736
|
+
"error": "no signing key: set OMG_SIGNING_KEY or --key-path"}))
|
|
737
|
+
return 1
|
|
738
|
+
|
|
739
|
+
try:
|
|
740
|
+
pack = load_policy_pack(pack_id)
|
|
741
|
+
except (FileNotFoundError, ValueError) as exc:
|
|
742
|
+
print(json.dumps({"schema": "PolicyPackSign", "status": "error", "error": str(exc)}))
|
|
743
|
+
return 1
|
|
744
|
+
|
|
745
|
+
canonical = _canonical_json(dict(pack))
|
|
746
|
+
digest = hashlib.sha256(canonical).hexdigest()
|
|
747
|
+
|
|
748
|
+
try:
|
|
749
|
+
approval = create_approval_artifact(
|
|
750
|
+
artifact_digest=digest,
|
|
751
|
+
action="policy-pack-sign",
|
|
752
|
+
scope=f"policy-pack/{pack_id}",
|
|
753
|
+
reason=f"Signing policy pack {pack_id}",
|
|
754
|
+
signer_key_id=key_id,
|
|
755
|
+
signer_private_key=private_key,
|
|
756
|
+
)
|
|
757
|
+
except Exception as exc:
|
|
758
|
+
print(json.dumps({"schema": "PolicyPackSign", "status": "error", "error": str(exc)}))
|
|
759
|
+
return 1
|
|
760
|
+
|
|
761
|
+
packs_dir = ROOT_DIR / "registry" / "policy-packs"
|
|
762
|
+
sig_path = packs_dir / f"{pack_id}.signature.json"
|
|
763
|
+
lock_path = packs_dir / f"{pack_id}.lock.json"
|
|
764
|
+
|
|
765
|
+
signature_artifact = asdict(approval)
|
|
766
|
+
sig_path.write_text(json.dumps(signature_artifact, indent=2) + "\n", encoding="utf-8")
|
|
767
|
+
|
|
768
|
+
# Derive signer public key from private key for lockfile attestation
|
|
769
|
+
signer_public_key_b64 = ""
|
|
770
|
+
try:
|
|
771
|
+
serialization, Ed25519PrivateKey, _ = _load_ed25519_backend()
|
|
772
|
+
private_raw = base64.b64decode(private_key, validate=True)
|
|
773
|
+
priv_key_obj = Ed25519PrivateKey.from_private_bytes(private_raw)
|
|
774
|
+
public_key_raw = priv_key_obj.public_key().public_bytes(
|
|
775
|
+
encoding=serialization.Encoding.Raw,
|
|
776
|
+
format=serialization.PublicFormat.Raw,
|
|
777
|
+
)
|
|
778
|
+
signer_public_key_b64 = base64.b64encode(public_key_raw).decode("ascii")
|
|
779
|
+
except Exception:
|
|
780
|
+
pass # Best-effort; lockfile still valid without public key
|
|
781
|
+
|
|
782
|
+
lockfile = {
|
|
783
|
+
"lockfile_version": 1,
|
|
784
|
+
"pack_id": pack_id,
|
|
785
|
+
"pack_path": f"registry/policy-packs/{pack_id}.yaml",
|
|
786
|
+
"canonical_digest": digest,
|
|
787
|
+
"signer_key_id": key_id,
|
|
788
|
+
"signer_public_key": signer_public_key_b64,
|
|
789
|
+
"algorithm": "ed25519-minisign",
|
|
790
|
+
"signature_path": f"registry/policy-packs/{pack_id}.signature.json",
|
|
791
|
+
"created_at": approval.issued_at,
|
|
792
|
+
}
|
|
793
|
+
lock_path.write_text(json.dumps(lockfile, indent=2) + "\n", encoding="utf-8")
|
|
794
|
+
|
|
795
|
+
output: dict[str, Any] = {
|
|
796
|
+
"schema": "PolicyPackSign",
|
|
797
|
+
"status": "signed",
|
|
798
|
+
"pack_id": pack_id,
|
|
799
|
+
"canonical_digest": digest,
|
|
800
|
+
"signer_key_id": key_id,
|
|
801
|
+
"signature_path": str(sig_path.relative_to(ROOT_DIR)),
|
|
802
|
+
"lock_path": str(lock_path.relative_to(ROOT_DIR)),
|
|
803
|
+
}
|
|
804
|
+
if fmt == "json":
|
|
805
|
+
print(json.dumps(output, indent=2))
|
|
806
|
+
else:
|
|
807
|
+
print(f"Signed: {pack_id}")
|
|
808
|
+
print(f" Digest: {digest}")
|
|
809
|
+
print(f" Signer: {key_id}")
|
|
810
|
+
print(f" Signature: {sig_path.relative_to(ROOT_DIR)}")
|
|
811
|
+
print(f" Lock: {lock_path.relative_to(ROOT_DIR)}")
|
|
812
|
+
return 0
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def _verify_single_pack(pack_id: str) -> dict[str, Any]:
|
|
816
|
+
from registry.approval_artifact import verify_approval_artifact
|
|
817
|
+
from registry.verify_artifact import _canonical_json
|
|
818
|
+
|
|
819
|
+
packs_dir = ROOT_DIR / "registry" / "policy-packs"
|
|
820
|
+
lock_path = packs_dir / f"{pack_id}.lock.json"
|
|
821
|
+
|
|
822
|
+
if not lock_path.exists():
|
|
823
|
+
return {"status": "error", "pack_id": pack_id,
|
|
824
|
+
"error": f"lockfile not found: {lock_path.relative_to(ROOT_DIR)}"}
|
|
825
|
+
|
|
826
|
+
try:
|
|
827
|
+
lockfile = json.loads(lock_path.read_text(encoding="utf-8"))
|
|
828
|
+
except Exception as exc:
|
|
829
|
+
return {"status": "error", "pack_id": pack_id, "error": str(exc)}
|
|
830
|
+
|
|
831
|
+
sig_path = ROOT_DIR / lockfile.get("signature_path", "")
|
|
832
|
+
if not sig_path.exists():
|
|
833
|
+
return {"status": "error", "pack_id": pack_id,
|
|
834
|
+
"error": f"signature not found: {lockfile.get('signature_path', '')}"}
|
|
835
|
+
|
|
836
|
+
try:
|
|
837
|
+
sig_artifact = json.loads(sig_path.read_text(encoding="utf-8"))
|
|
838
|
+
except Exception as exc:
|
|
839
|
+
return {"status": "error", "pack_id": pack_id, "error": str(exc)}
|
|
840
|
+
|
|
841
|
+
try:
|
|
842
|
+
pack = load_policy_pack(pack_id)
|
|
843
|
+
except (FileNotFoundError, ValueError) as exc:
|
|
844
|
+
return {"status": "error", "pack_id": pack_id, "error": str(exc)}
|
|
845
|
+
|
|
846
|
+
canonical = _canonical_json(dict(pack))
|
|
847
|
+
digest = hashlib.sha256(canonical).hexdigest()
|
|
848
|
+
|
|
849
|
+
if digest != lockfile.get("canonical_digest", ""):
|
|
850
|
+
return {"status": "tampered", "pack_id": pack_id,
|
|
851
|
+
"error": "pack content changed since signing",
|
|
852
|
+
"expected_digest": lockfile.get("canonical_digest", ""),
|
|
853
|
+
"actual_digest": digest}
|
|
854
|
+
|
|
855
|
+
result = verify_approval_artifact(sig_artifact, expected_artifact_digest=digest)
|
|
856
|
+
if not result.get("valid"):
|
|
857
|
+
return {"status": "failed", "pack_id": pack_id,
|
|
858
|
+
"error": result.get("reason", "verification failed")}
|
|
859
|
+
|
|
860
|
+
return {
|
|
861
|
+
"status": "verified",
|
|
862
|
+
"pack_id": pack_id,
|
|
863
|
+
"canonical_digest": digest,
|
|
864
|
+
"signer_key_id": lockfile.get("signer_key_id", ""),
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
def cmd_policy_pack_verify(args: argparse.Namespace) -> int:
|
|
869
|
+
fmt = getattr(args, "format", "json")
|
|
870
|
+
verify_all = getattr(args, "all", False)
|
|
871
|
+
pack_id = getattr(args, "pack_id", "") or ""
|
|
872
|
+
|
|
873
|
+
if verify_all:
|
|
874
|
+
packs_dir = ROOT_DIR / "registry" / "policy-packs"
|
|
875
|
+
lock_files = sorted(packs_dir.glob("*.lock.json"))
|
|
876
|
+
if not lock_files:
|
|
877
|
+
print(json.dumps({"schema": "PolicyPackVerifyAll", "status": "error",
|
|
878
|
+
"error": "no lockfiles found", "results": []}))
|
|
879
|
+
return 1
|
|
880
|
+
|
|
881
|
+
results: list[dict[str, Any]] = []
|
|
882
|
+
all_ok = True
|
|
883
|
+
for lf in lock_files:
|
|
884
|
+
pid = lf.name.removesuffix(".lock.json")
|
|
885
|
+
r = _verify_single_pack(pid)
|
|
886
|
+
results.append(r)
|
|
887
|
+
if r["status"] != "verified":
|
|
888
|
+
all_ok = False
|
|
889
|
+
|
|
890
|
+
output: dict[str, Any] = {
|
|
891
|
+
"schema": "PolicyPackVerifyAll",
|
|
892
|
+
"status": "verified" if all_ok else "failed",
|
|
893
|
+
"total": len(results),
|
|
894
|
+
"passed": sum(1 for r in results if r["status"] == "verified"),
|
|
895
|
+
"failed": sum(1 for r in results if r["status"] != "verified"),
|
|
896
|
+
"results": results,
|
|
897
|
+
}
|
|
898
|
+
if fmt == "json":
|
|
899
|
+
print(json.dumps(output, indent=2))
|
|
900
|
+
else:
|
|
901
|
+
print(f"Verify all: {output['passed']}/{output['total']} passed")
|
|
902
|
+
for r in results:
|
|
903
|
+
status_icon = "OK" if r["status"] == "verified" else "FAIL"
|
|
904
|
+
print(f" [{status_icon}] {r['pack_id']}")
|
|
905
|
+
return 0 if all_ok else 1
|
|
906
|
+
|
|
907
|
+
if not pack_id:
|
|
908
|
+
print(json.dumps({"schema": "PolicyPackVerify", "status": "error",
|
|
909
|
+
"error": "pack_id required (or use --all)"}))
|
|
910
|
+
return 1
|
|
911
|
+
|
|
912
|
+
r = _verify_single_pack(pack_id)
|
|
913
|
+
r["schema"] = "PolicyPackVerify"
|
|
914
|
+
if fmt == "json":
|
|
915
|
+
print(json.dumps(r, indent=2))
|
|
916
|
+
else:
|
|
917
|
+
if r["status"] == "verified":
|
|
918
|
+
print(f"Verified: {pack_id}")
|
|
919
|
+
print(f" Digest: {r.get('canonical_digest', '')}")
|
|
920
|
+
print(f" Signer: {r.get('signer_key_id', '')}")
|
|
921
|
+
else:
|
|
922
|
+
print(f"Failed: {pack_id}: {r.get('error', '')}")
|
|
923
|
+
return 0 if r["status"] == "verified" else 1
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
def cmd_proof_summary(args: argparse.Namespace) -> int:
|
|
927
|
+
fmt = getattr(args, "format", "json")
|
|
928
|
+
project_dir = _ensure_project_dir()
|
|
929
|
+
|
|
930
|
+
from runtime.evidence_query import list_evidence_packs
|
|
931
|
+
from runtime.evidence_narrator import narrate
|
|
932
|
+
|
|
933
|
+
packs = list_evidence_packs(project_dir)
|
|
934
|
+
|
|
935
|
+
if not packs:
|
|
936
|
+
result: dict[str, Any] = {
|
|
937
|
+
"schema": "ProofSummary",
|
|
938
|
+
"status": "no_evidence",
|
|
939
|
+
"claims": [],
|
|
940
|
+
"evidence_list": [],
|
|
941
|
+
"missing_artifacts": [],
|
|
942
|
+
"rollback_status": "n/a",
|
|
943
|
+
"next_actions": ["Run a task to generate evidence"],
|
|
944
|
+
}
|
|
945
|
+
else:
|
|
946
|
+
latest = packs[0]
|
|
947
|
+
narrative = narrate(cast(Any, latest))
|
|
948
|
+
|
|
949
|
+
claims: list[Any] = []
|
|
950
|
+
pack_claims = latest.get("claims")
|
|
951
|
+
if isinstance(pack_claims, list):
|
|
952
|
+
claims = pack_claims
|
|
953
|
+
|
|
954
|
+
evidence_list: list[dict[str, Any]] = []
|
|
955
|
+
artifacts = latest.get("artifacts")
|
|
956
|
+
if isinstance(artifacts, list):
|
|
957
|
+
for art in artifacts:
|
|
958
|
+
if isinstance(art, dict):
|
|
959
|
+
evidence_list.append({
|
|
960
|
+
"kind": art.get("kind", ""),
|
|
961
|
+
"path": art.get("path", ""),
|
|
962
|
+
"summary": art.get("summary", ""),
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
missing: list[str] = []
|
|
966
|
+
requirements = latest.get("evidence_requirements")
|
|
967
|
+
if isinstance(requirements, list):
|
|
968
|
+
artifact_kinds = {str(a.get("kind", "")) for a in artifacts if isinstance(a, dict)} if isinstance(artifacts, list) else set()
|
|
969
|
+
for req in requirements:
|
|
970
|
+
if str(req) not in artifact_kinds:
|
|
971
|
+
missing.append(str(req))
|
|
972
|
+
|
|
973
|
+
rollback_status = "n/a"
|
|
974
|
+
unresolved = latest.get("unresolved_risks")
|
|
975
|
+
if isinstance(unresolved, list) and unresolved:
|
|
976
|
+
rollback_status = "risks_present"
|
|
977
|
+
|
|
978
|
+
result = {
|
|
979
|
+
"schema": "ProofSummary",
|
|
980
|
+
"status": str(latest.get("status", "found")),
|
|
981
|
+
"run_id": str(latest.get("run_id", "")),
|
|
982
|
+
"claims": claims,
|
|
983
|
+
"evidence_list": evidence_list,
|
|
984
|
+
"missing_artifacts": missing,
|
|
985
|
+
"rollback_status": rollback_status,
|
|
986
|
+
"narrative": dict(narrative),
|
|
987
|
+
"next_actions": narrative.get("next_actions", []),
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if fmt == "json":
|
|
991
|
+
print(json.dumps(result, indent=2))
|
|
992
|
+
elif fmt == "text":
|
|
993
|
+
from runtime.explainer_formatter import format_terminal as _fmt_term_proof
|
|
994
|
+
|
|
995
|
+
narrative_dict = result.get("narrative", {
|
|
996
|
+
"verdict_summary": result.get("status", "unknown"),
|
|
997
|
+
"blockers_section": result.get("missing_artifacts", []),
|
|
998
|
+
"provenance_note": None,
|
|
999
|
+
"evidence_paths_section": [],
|
|
1000
|
+
"next_actions": result.get("next_actions", []),
|
|
1001
|
+
})
|
|
1002
|
+
print(_fmt_term_proof(narrative_dict))
|
|
1003
|
+
else:
|
|
1004
|
+
lines = ["# Proof Summary", ""]
|
|
1005
|
+
lines.append(f"**Status:** {result['status']}")
|
|
1006
|
+
if result.get("run_id"):
|
|
1007
|
+
lines.append(f"**Run ID:** {result['run_id']}")
|
|
1008
|
+
lines.append("")
|
|
1009
|
+
if result.get("claims"):
|
|
1010
|
+
lines.append("## Claims")
|
|
1011
|
+
for claim in result["claims"]:
|
|
1012
|
+
lines.append(f"- {claim}")
|
|
1013
|
+
lines.append("")
|
|
1014
|
+
if result.get("evidence_list"):
|
|
1015
|
+
lines.append("## Evidence")
|
|
1016
|
+
for ev in result["evidence_list"]:
|
|
1017
|
+
lines.append(f"- **{ev.get('kind', 'unknown')}**: {ev.get('summary', ev.get('path', ''))}")
|
|
1018
|
+
lines.append("")
|
|
1019
|
+
if result.get("missing_artifacts"):
|
|
1020
|
+
lines.append("## Missing Artifacts")
|
|
1021
|
+
for m in result["missing_artifacts"]:
|
|
1022
|
+
lines.append(f"- {m}")
|
|
1023
|
+
lines.append("")
|
|
1024
|
+
lines.append(f"**Rollback Status:** {result.get('rollback_status', 'n/a')}")
|
|
1025
|
+
lines.append("")
|
|
1026
|
+
if result.get("next_actions"):
|
|
1027
|
+
lines.append("## Next Actions")
|
|
1028
|
+
for action in result["next_actions"]:
|
|
1029
|
+
lines.append(f"- {action}")
|
|
1030
|
+
print("\n".join(lines))
|
|
1031
|
+
|
|
1032
|
+
return 0
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def cmd_explain_run(args: argparse.Namespace) -> int:
|
|
1036
|
+
fmt = getattr(args, "format", "json")
|
|
1037
|
+
project_dir = _ensure_project_dir()
|
|
1038
|
+
run_id = str(args.run_id).strip()
|
|
1039
|
+
|
|
1040
|
+
from runtime.evidence_query import get_evidence_pack, query_evidence
|
|
1041
|
+
|
|
1042
|
+
pack = get_evidence_pack(project_dir, run_id)
|
|
1043
|
+
|
|
1044
|
+
if pack is None:
|
|
1045
|
+
records = query_evidence(project_dir, run_id=run_id)
|
|
1046
|
+
if not records:
|
|
1047
|
+
result: dict[str, Any] = {
|
|
1048
|
+
"schema": "RunExplanation",
|
|
1049
|
+
"run_id": run_id,
|
|
1050
|
+
"status": "not_found",
|
|
1051
|
+
}
|
|
1052
|
+
print(json.dumps(result, indent=2))
|
|
1053
|
+
return 0
|
|
1054
|
+
evidence: list[dict[str, Any]] = list(records)
|
|
1055
|
+
else:
|
|
1056
|
+
evidence = [dict(pack)]
|
|
1057
|
+
|
|
1058
|
+
result = {
|
|
1059
|
+
"schema": "RunExplanation",
|
|
1060
|
+
"run_id": run_id,
|
|
1061
|
+
"status": "found",
|
|
1062
|
+
"evidence_count": len(evidence),
|
|
1063
|
+
"evidence": evidence,
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if fmt == "json":
|
|
1067
|
+
print(json.dumps(result, indent=2))
|
|
1068
|
+
elif fmt == "text" or fmt == "markdown":
|
|
1069
|
+
from runtime.evidence_narrator import narrate as _narrate_run
|
|
1070
|
+
from runtime.explainer_formatter import format_markdown as _fmt_md
|
|
1071
|
+
from runtime.explainer_formatter import format_terminal as _fmt_term
|
|
1072
|
+
|
|
1073
|
+
narrative = _narrate_run(cast(Any, {"status": result["status"], "evidence_paths": {}, "blockers": [], "next_steps": []}))
|
|
1074
|
+
if fmt == "text":
|
|
1075
|
+
print(_fmt_term(dict(narrative)))
|
|
1076
|
+
else:
|
|
1077
|
+
print(_fmt_md(dict(narrative)))
|
|
1078
|
+
|
|
1079
|
+
return 0
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
def cmd_blocked_last(args: argparse.Namespace) -> int:
|
|
1083
|
+
fmt = getattr(args, "format", "text")
|
|
1084
|
+
project_dir = _ensure_project_dir()
|
|
1085
|
+
state_file = Path(project_dir) / ".omg" / "state" / "last-block-explanation.json"
|
|
1086
|
+
|
|
1087
|
+
if not state_file.exists():
|
|
1088
|
+
print("No block explanation found — try running a tool to trigger governance.")
|
|
1089
|
+
return 0
|
|
1090
|
+
|
|
1091
|
+
try:
|
|
1092
|
+
block = json.loads(state_file.read_text(encoding="utf-8"))
|
|
1093
|
+
except (OSError, json.JSONDecodeError):
|
|
1094
|
+
print("No block explanation found — state file unreadable.")
|
|
1095
|
+
return 0
|
|
1096
|
+
|
|
1097
|
+
narrative: dict[str, Any] = {
|
|
1098
|
+
"verdict_summary": block.get("explanation", ""),
|
|
1099
|
+
"blockers_section": [block.get("reason_code", "unknown")],
|
|
1100
|
+
"next_actions": ["Review the blocked tool call and address the governance concern"],
|
|
1101
|
+
"evidence_paths_section": [],
|
|
1102
|
+
"provenance_note": f"Tool: {block.get('tool', '?')} | {block.get('timestamp', '')}",
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
if fmt == "markdown":
|
|
1106
|
+
from runtime.explainer_formatter import format_markdown as _fmt_md
|
|
1107
|
+
print(_fmt_md(narrative))
|
|
1108
|
+
else:
|
|
1109
|
+
from runtime.explainer_formatter import format_terminal as _fmt_term
|
|
1110
|
+
print(_fmt_term(narrative))
|
|
1111
|
+
|
|
1112
|
+
return 0
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
def cmd_proof_open(args: argparse.Namespace) -> int:
|
|
1116
|
+
fmt = getattr(args, "format", "markdown")
|
|
1117
|
+
project_dir = _ensure_project_dir()
|
|
1118
|
+
run_id_arg = getattr(args, "run_id", None)
|
|
1119
|
+
|
|
1120
|
+
from runtime.evidence_query import list_evidence_packs
|
|
1121
|
+
from runtime.evidence_narrator import narrate as _narrate
|
|
1122
|
+
from runtime.explainer_formatter import format_markdown as _fmt_md
|
|
1123
|
+
from runtime.explainer_formatter import format_terminal as _fmt_term
|
|
1124
|
+
|
|
1125
|
+
packs = list_evidence_packs(project_dir)
|
|
1126
|
+
|
|
1127
|
+
if not packs:
|
|
1128
|
+
print("No evidence packs found. Run a governed task to generate evidence,")
|
|
1129
|
+
print("then re-run: omg proof open")
|
|
1130
|
+
return 0
|
|
1131
|
+
|
|
1132
|
+
pack = packs[0]
|
|
1133
|
+
run_id = str(run_id_arg) if run_id_arg else str(pack.get("run_id", "unknown"))
|
|
1134
|
+
|
|
1135
|
+
narrative = _narrate(cast(Any, pack))
|
|
1136
|
+
md = _fmt_md(dict(narrative))
|
|
1137
|
+
|
|
1138
|
+
out = Path(project_dir) / ".omg" / "evidence" / f"proof-open-{run_id}.md"
|
|
1139
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
1140
|
+
out.write_text(md, encoding="utf-8")
|
|
1141
|
+
|
|
1142
|
+
print(f"proof-open: {run_id}")
|
|
1143
|
+
if fmt == "text":
|
|
1144
|
+
print(_fmt_term(dict(narrative)))
|
|
1145
|
+
else:
|
|
1146
|
+
print(md)
|
|
1147
|
+
|
|
1148
|
+
return 0
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def cmd_budget_simulate(args: argparse.Namespace) -> int:
|
|
1152
|
+
import uuid as _uuid
|
|
1153
|
+
|
|
1154
|
+
project_dir = _ensure_project_dir()
|
|
1155
|
+
fmt = getattr(args, "format", "json")
|
|
1156
|
+
tier_input = str(getattr(args, "tier", "") or "").strip().lower()
|
|
1157
|
+
channel = str(getattr(args, "channel", "") or "public").strip()
|
|
1158
|
+
preset = str(getattr(args, "preset", "") or "balanced").strip()
|
|
1159
|
+
task_desc = str(getattr(args, "task", "") or "").strip()
|
|
1160
|
+
|
|
1161
|
+
tier_detection = detect_tier(str(args.provider), project_dir=project_dir)
|
|
1162
|
+
tier = tier_input or str(tier_detection.get("tier", "free"))
|
|
1163
|
+
|
|
1164
|
+
tier_token_limits: dict[str, int] = {
|
|
1165
|
+
"free": 10_000,
|
|
1166
|
+
"pro": 100_000,
|
|
1167
|
+
"team": 500_000,
|
|
1168
|
+
"enterprise_tier": 2_000_000,
|
|
1169
|
+
}
|
|
1170
|
+
token_limit = int(args.token_limit) if int(args.token_limit) > 0 else int(tier_token_limits.get(tier, 10_000))
|
|
1171
|
+
tokens_used = max(0, int(args.tokens_used))
|
|
1172
|
+
|
|
1173
|
+
if bool(args.enforce):
|
|
1174
|
+
from runtime.budget_envelopes import get_budget_envelope_manager
|
|
1175
|
+
|
|
1176
|
+
mgr = get_budget_envelope_manager(project_dir)
|
|
1177
|
+
temp_id = f"simulate-{_uuid.uuid4().hex[:8]}"
|
|
1178
|
+
|
|
1179
|
+
mgr.create_envelope(temp_id, token_limit=token_limit)
|
|
1180
|
+
mgr.record_usage(temp_id, tokens=tokens_used)
|
|
1181
|
+
envelope_check = mgr.check_envelope(temp_id)
|
|
1182
|
+
|
|
1183
|
+
try:
|
|
1184
|
+
mgr._envelope_path(temp_id).unlink(missing_ok=True)
|
|
1185
|
+
except OSError:
|
|
1186
|
+
pass
|
|
1187
|
+
|
|
1188
|
+
result: dict[str, Any] = {
|
|
1189
|
+
"schema": "BudgetSimulateResult",
|
|
1190
|
+
"status": "blocked" if envelope_check.governance_action == "block" else "ok",
|
|
1191
|
+
"reason": envelope_check.reason,
|
|
1192
|
+
"tier": tier,
|
|
1193
|
+
"provider": str(args.provider),
|
|
1194
|
+
"channel": channel,
|
|
1195
|
+
"preset": preset,
|
|
1196
|
+
"enforce": True,
|
|
1197
|
+
"usage": {"tokens_used": tokens_used},
|
|
1198
|
+
"limits": {"token_limit": token_limit},
|
|
1199
|
+
"tier_limits": tier_token_limits,
|
|
1200
|
+
"check": {
|
|
1201
|
+
"status": envelope_check.status,
|
|
1202
|
+
"breached_dimensions": list(envelope_check.breached_dimensions),
|
|
1203
|
+
"governance_action": envelope_check.governance_action,
|
|
1204
|
+
"reason": envelope_check.reason,
|
|
1205
|
+
},
|
|
1206
|
+
}
|
|
1207
|
+
if task_desc:
|
|
1208
|
+
result["task"] = task_desc
|
|
1209
|
+
|
|
1210
|
+
print(json.dumps(result, indent=2))
|
|
1211
|
+
return 2 if envelope_check.governance_action == "block" else 0
|
|
1212
|
+
|
|
1213
|
+
breached = tokens_used > token_limit
|
|
1214
|
+
check: dict[str, Any] = {
|
|
1215
|
+
"status": "breach" if breached else "ok",
|
|
1216
|
+
"breached_dimensions": ["tokens"] if breached else [],
|
|
1217
|
+
"governance_action": "block" if breached else "warn",
|
|
1218
|
+
"reason": (
|
|
1219
|
+
f"simulated tokens exceeded tier limit ({tokens_used}>{token_limit})"
|
|
1220
|
+
if breached
|
|
1221
|
+
else "simulated usage within tier limits"
|
|
1222
|
+
),
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
result = {
|
|
1226
|
+
"schema": "BudgetSimulateResult",
|
|
1227
|
+
"status": "preview",
|
|
1228
|
+
"tier": tier,
|
|
1229
|
+
"provider": str(args.provider),
|
|
1230
|
+
"channel": channel,
|
|
1231
|
+
"preset": preset,
|
|
1232
|
+
"enforce": False,
|
|
1233
|
+
"tier_detection": tier_detection,
|
|
1234
|
+
"usage": {"tokens_used": tokens_used},
|
|
1235
|
+
"limits": {"token_limit": token_limit},
|
|
1236
|
+
"tier_limits": tier_token_limits,
|
|
1237
|
+
"check": check,
|
|
1238
|
+
}
|
|
1239
|
+
if task_desc:
|
|
1240
|
+
result["task"] = task_desc
|
|
1241
|
+
|
|
1242
|
+
print(json.dumps(result, indent=2))
|
|
1243
|
+
return 0
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
def cmd_domain_pack(args: argparse.Namespace) -> int:
|
|
1247
|
+
result = get_domain_pack_contract(args.name)
|
|
1248
|
+
print(json.dumps(result, indent=2))
|
|
1249
|
+
return 0
|
|
1250
|
+
|
|
1251
|
+
|
|
1252
|
+
def _emit_vision_placeholder(command: str) -> int:
|
|
1253
|
+
print(
|
|
1254
|
+
json.dumps(
|
|
1255
|
+
{
|
|
1256
|
+
"status": "error",
|
|
1257
|
+
"error_code": "INVALID_VISION_INPUT",
|
|
1258
|
+
"message": f"{command} is registered, but runtime execution is not implemented yet",
|
|
1259
|
+
},
|
|
1260
|
+
indent=2,
|
|
1261
|
+
)
|
|
1262
|
+
)
|
|
1263
|
+
return 2
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
def cmd_vision_ocr(args: argparse.Namespace) -> int:
|
|
1267
|
+
return _emit_vision_placeholder("vision ocr")
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
def cmd_vision_compare(args: argparse.Namespace) -> int:
|
|
1271
|
+
return _emit_vision_placeholder("vision compare")
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
def cmd_vision_analyze(args: argparse.Namespace) -> int:
|
|
1275
|
+
return _emit_vision_placeholder("vision analyze")
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
def cmd_vision_batch(args: argparse.Namespace) -> int:
|
|
1279
|
+
return _emit_vision_placeholder("vision batch")
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
def cmd_vision_eval(args: argparse.Namespace) -> int:
|
|
1283
|
+
return _emit_vision_placeholder("vision eval")
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
def cmd_trace_record(args: argparse.Namespace) -> int:
|
|
1287
|
+
result = record_trace(
|
|
1288
|
+
_ensure_project_dir(),
|
|
1289
|
+
trace_type=args.trace_type,
|
|
1290
|
+
route=args.route,
|
|
1291
|
+
status=args.status,
|
|
1292
|
+
plan=json.loads(args.plan_json) if args.plan_json else {},
|
|
1293
|
+
verify=json.loads(args.verify_json) if args.verify_json else {},
|
|
1294
|
+
)
|
|
1295
|
+
print(json.dumps(result, indent=2))
|
|
1296
|
+
return 0
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def cmd_eval_gate(args: argparse.Namespace) -> int:
|
|
1300
|
+
result = evaluate_trace(
|
|
1301
|
+
_ensure_project_dir(),
|
|
1302
|
+
trace_id=args.trace_id,
|
|
1303
|
+
suites=args.suites.split(","),
|
|
1304
|
+
metrics=json.loads(args.metrics_json),
|
|
1305
|
+
)
|
|
1306
|
+
print(json.dumps(result, indent=2))
|
|
1307
|
+
return 0 if result["status"] == "ok" else 2
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
def cmd_delta_classify(args: argparse.Namespace) -> int:
|
|
1311
|
+
from runtime.delta_classifier import classify_project_changes
|
|
1312
|
+
|
|
1313
|
+
touched_files = [item for item in args.files.split(",") if item]
|
|
1314
|
+
result = classify_project_changes(_ensure_project_dir(), touched_files=touched_files or None, goal=args.goal)
|
|
1315
|
+
print(json.dumps(result, indent=2))
|
|
1316
|
+
return 0
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
def cmd_incident_replay(args: argparse.Namespace) -> int:
|
|
1320
|
+
result = build_incident_pack(
|
|
1321
|
+
_ensure_project_dir(),
|
|
1322
|
+
title=args.title,
|
|
1323
|
+
failing_tests=[item for item in args.failing_tests.split(",") if item],
|
|
1324
|
+
logs=[item for item in args.logs.split("|") if item],
|
|
1325
|
+
diff_summary=json.loads(args.diff_summary_json),
|
|
1326
|
+
trace_id=args.trace_id or None,
|
|
1327
|
+
)
|
|
1328
|
+
print(json.dumps(result, indent=2))
|
|
1329
|
+
return 0
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
def cmd_lineage(args: argparse.Namespace) -> int:
|
|
1333
|
+
result = build_lineage_manifest(
|
|
1334
|
+
_ensure_project_dir(),
|
|
1335
|
+
artifact_type=args.artifact_type,
|
|
1336
|
+
sources=json.loads(args.sources_json),
|
|
1337
|
+
privacy=args.privacy,
|
|
1338
|
+
license=args.license_name,
|
|
1339
|
+
derivation=json.loads(args.derivation_json),
|
|
1340
|
+
trace_id=args.trace_id or None,
|
|
1341
|
+
)
|
|
1342
|
+
print(json.dumps(result, indent=2))
|
|
1343
|
+
return 0 if result["status"] == "ok" else 2
|
|
1344
|
+
|
|
1345
|
+
|
|
1346
|
+
def cmd_supervisor_issue(args: argparse.Namespace) -> int:
|
|
1347
|
+
result = issue_local_supervisor_session(
|
|
1348
|
+
_ensure_project_dir(),
|
|
1349
|
+
worker_id=args.worker_id,
|
|
1350
|
+
shared_secret=args.shared_secret,
|
|
1351
|
+
)
|
|
1352
|
+
print(json.dumps(result, indent=2))
|
|
1353
|
+
return 0
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
def cmd_supervisor_verify(args: argparse.Namespace) -> int:
|
|
1357
|
+
result = verify_local_supervisor_token(args.token, shared_secret=args.shared_secret)
|
|
1358
|
+
print(json.dumps(result, indent=2))
|
|
1359
|
+
return 0 if result["status"] == "ok" else 2
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
def cmd_maintainer(args: argparse.Namespace) -> int:
|
|
1363
|
+
project_dir = _ensure_project_dir()
|
|
1364
|
+
out_dir = Path(project_dir) / ".omg" / "evidence"
|
|
1365
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
1366
|
+
out_file = out_dir / "oss-impact.json"
|
|
1367
|
+
payload = {
|
|
1368
|
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
1369
|
+
"mode": args.mode,
|
|
1370
|
+
"activity": {"commits": "unverified", "reviews": "unverified", "releases": "unverified"},
|
|
1371
|
+
"dependents": {"direct": "unverified", "transitive": "unverified"},
|
|
1372
|
+
"adoption_signals": {"downloads": "unverified", "stars": "unverified"},
|
|
1373
|
+
"summary_500_words": "",
|
|
1374
|
+
"integrity": {"metric_manipulation": "forbidden"},
|
|
1375
|
+
}
|
|
1376
|
+
out_file.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
1377
|
+
print(json.dumps({"status": "ok", "path": str(out_file)}, indent=2))
|
|
1378
|
+
return 0
|
|
1379
|
+
|
|
1380
|
+
|
|
1381
|
+
def cmd_trust_review(args: argparse.Namespace) -> int:
|
|
1382
|
+
old_cfg = _load_json(args.old)
|
|
1383
|
+
new_cfg = _load_json(args.new)
|
|
1384
|
+
review = review_config_change(args.file, old_cfg, new_cfg)
|
|
1385
|
+
manifest = write_trust_manifest(_ensure_project_dir(), review)
|
|
1386
|
+
print(json.dumps({"review": review, "manifest": manifest}, indent=2))
|
|
1387
|
+
return 0
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
def cmd_runtime_dispatch(args: argparse.Namespace) -> int:
|
|
1391
|
+
if args.idea_json:
|
|
1392
|
+
idea = json.loads(args.idea_json)
|
|
1393
|
+
elif args.idea:
|
|
1394
|
+
idea = _load_json(args.idea)
|
|
1395
|
+
else:
|
|
1396
|
+
idea = {"goal": "unspecified"}
|
|
1397
|
+
result = dispatch_runtime(args.runtime, idea)
|
|
1398
|
+
print(json.dumps(result, indent=2))
|
|
1399
|
+
return 0 if result.get("status") == "ok" else 2
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def cmd_lab_train(args: argparse.Namespace) -> int:
|
|
1403
|
+
job = json.loads(args.job_json) if args.job_json else _load_json(args.job)
|
|
1404
|
+
result = run_pipeline(job)
|
|
1405
|
+
print(json.dumps(result, indent=2))
|
|
1406
|
+
return 0 if result.get("status") in {"ready", "failed_evaluation"} else 2
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
def cmd_lab_eval(args: argparse.Namespace) -> int:
|
|
1410
|
+
result = json.loads(args.result_json) if args.result_json else _load_json(args.result)
|
|
1411
|
+
out = publish_artifact(result)
|
|
1412
|
+
print(json.dumps(out, indent=2))
|
|
1413
|
+
return 0 if out.get("status") == "published" else 2
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
def cmd_forge_run(args: argparse.Namespace) -> int:
|
|
1417
|
+
preset = args.preset
|
|
1418
|
+
if preset != "labs":
|
|
1419
|
+
print(
|
|
1420
|
+
json.dumps(
|
|
1421
|
+
{"status": "error", "message": f"forge requires labs preset, got: {preset}"},
|
|
1422
|
+
indent=2,
|
|
1423
|
+
)
|
|
1424
|
+
)
|
|
1425
|
+
return 2
|
|
1426
|
+
|
|
1427
|
+
project_dir = _ensure_project_dir()
|
|
1428
|
+
run_id = normalize_run_id(args.run_id if args.run_id else None)
|
|
1429
|
+
job = json.loads(args.job_json) if args.job_json else _load_json(args.job)
|
|
1430
|
+
|
|
1431
|
+
valid, validation_reason = validate_forge_job(job)
|
|
1432
|
+
if not valid:
|
|
1433
|
+
print(json.dumps({"status": "error", "message": validation_reason}, indent=2))
|
|
1434
|
+
return 2
|
|
1435
|
+
|
|
1436
|
+
specialist_dispatch: dict[str, Any] | None = None
|
|
1437
|
+
if "specialists" in job or "domain" in job:
|
|
1438
|
+
specialist_dispatch = dispatch_specialists(job, project_dir, run_id=run_id)
|
|
1439
|
+
if specialist_dispatch.get("status") == "blocked":
|
|
1440
|
+
print(json.dumps(specialist_dispatch, indent=2))
|
|
1441
|
+
return 2
|
|
1442
|
+
|
|
1443
|
+
result = run_pipeline_with_evidence(project_dir, job, run_id)
|
|
1444
|
+
if specialist_dispatch is not None:
|
|
1445
|
+
result = dict(result)
|
|
1446
|
+
result["specialist_dispatch"] = specialist_dispatch
|
|
1447
|
+
print(json.dumps(result, indent=2))
|
|
1448
|
+
return 0 if result.get("status") in {"ready", "failed_evaluation"} else 2
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
def cmd_forge_vision_agent(args: argparse.Namespace) -> int:
|
|
1452
|
+
preset = args.preset
|
|
1453
|
+
if preset != "labs":
|
|
1454
|
+
print(
|
|
1455
|
+
json.dumps(
|
|
1456
|
+
{"status": "error", "message": f"forge requires labs preset, got: {preset}"},
|
|
1457
|
+
indent=2,
|
|
1458
|
+
)
|
|
1459
|
+
)
|
|
1460
|
+
return 2
|
|
1461
|
+
|
|
1462
|
+
project_dir = _ensure_project_dir()
|
|
1463
|
+
run_id = normalize_run_id(args.run_id if args.run_id else None)
|
|
1464
|
+
|
|
1465
|
+
job: dict[str, Any] = {
|
|
1466
|
+
"dataset": {
|
|
1467
|
+
"name": "vision-agent",
|
|
1468
|
+
"license": "apache-2.0",
|
|
1469
|
+
"source": "internal-curated",
|
|
1470
|
+
},
|
|
1471
|
+
"base_model": {
|
|
1472
|
+
"name": "distill-base-v1",
|
|
1473
|
+
"source": "approved-registry",
|
|
1474
|
+
"allow_distill": True,
|
|
1475
|
+
},
|
|
1476
|
+
"target_metric": float(args.target_metric),
|
|
1477
|
+
"simulated_metric": float(args.simulated_metric),
|
|
1478
|
+
"specialists": resolve_specialists("vision-agent"),
|
|
1479
|
+
"domain": "vision",
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
if args.job_json:
|
|
1483
|
+
override = json.loads(args.job_json)
|
|
1484
|
+
if not isinstance(override, dict):
|
|
1485
|
+
print(json.dumps({"status": "error", "message": "--job-json must be an object"}, indent=2))
|
|
1486
|
+
return 2
|
|
1487
|
+
job.update(override)
|
|
1488
|
+
|
|
1489
|
+
specialist_dispatch = dispatch_specialists(job, project_dir, run_id=run_id)
|
|
1490
|
+
if specialist_dispatch.get("status") == "blocked":
|
|
1491
|
+
print(json.dumps(specialist_dispatch, indent=2))
|
|
1492
|
+
return 2
|
|
1493
|
+
|
|
1494
|
+
result = run_pipeline_with_evidence(project_dir, job, run_id)
|
|
1495
|
+
out = dict(result)
|
|
1496
|
+
out["specialist_dispatch"] = specialist_dispatch
|
|
1497
|
+
out["agent_path"] = "vision-agent"
|
|
1498
|
+
print(json.dumps(out, indent=2))
|
|
1499
|
+
return 0 if out.get("status") in {"ready", "failed_evaluation"} else 2
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
def _cmd_forge_domain(args: argparse.Namespace, domain: str) -> int:
|
|
1503
|
+
preset = args.preset
|
|
1504
|
+
if preset != "labs":
|
|
1505
|
+
print(
|
|
1506
|
+
json.dumps(
|
|
1507
|
+
{"status": "error", "message": f"forge requires labs preset, got: {preset}"},
|
|
1508
|
+
indent=2,
|
|
1509
|
+
)
|
|
1510
|
+
)
|
|
1511
|
+
return 2
|
|
1512
|
+
|
|
1513
|
+
project_dir = _ensure_project_dir()
|
|
1514
|
+
run_id = normalize_run_id(args.run_id if args.run_id else None)
|
|
1515
|
+
|
|
1516
|
+
mvp = load_forge_mvp()
|
|
1517
|
+
starter_templates = cast(dict[str, Any], mvp["starter_templates"])
|
|
1518
|
+
job: dict[str, Any] = dict(starter_templates[domain])
|
|
1519
|
+
|
|
1520
|
+
if args.job_json:
|
|
1521
|
+
override = json.loads(args.job_json)
|
|
1522
|
+
if not isinstance(override, dict):
|
|
1523
|
+
print(json.dumps({"status": "error", "message": "--job-json must be an object"}, indent=2))
|
|
1524
|
+
return 2
|
|
1525
|
+
job.update(override)
|
|
1526
|
+
|
|
1527
|
+
specialist_dispatch = dispatch_specialists(job, project_dir, run_id=run_id)
|
|
1528
|
+
if specialist_dispatch.get("status") == "blocked":
|
|
1529
|
+
print(json.dumps(specialist_dispatch, indent=2))
|
|
1530
|
+
return 2
|
|
1531
|
+
|
|
1532
|
+
result = run_pipeline_with_evidence(project_dir, job, run_id)
|
|
1533
|
+
out = dict(result)
|
|
1534
|
+
out["specialist_dispatch"] = specialist_dispatch
|
|
1535
|
+
out["agent_path"] = domain
|
|
1536
|
+
print(json.dumps(out, indent=2))
|
|
1537
|
+
return 0 if out.get("status") in {"ready", "failed_evaluation"} else 2
|
|
1538
|
+
|
|
1539
|
+
|
|
1540
|
+
def cmd_forge_robotics(args: argparse.Namespace) -> int:
|
|
1541
|
+
return _cmd_forge_domain(args, "robotics")
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
def cmd_forge_algorithms(args: argparse.Namespace) -> int:
|
|
1545
|
+
return _cmd_forge_domain(args, "algorithms")
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
def cmd_forge_health(args: argparse.Namespace) -> int:
|
|
1549
|
+
return _cmd_forge_domain(args, "health")
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
def cmd_forge_cybersecurity(args: argparse.Namespace) -> int:
|
|
1553
|
+
return _cmd_forge_domain(args, "cybersecurity")
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
def cmd_teams(args: argparse.Namespace) -> int:
|
|
1557
|
+
files = [f.strip() for f in args.files.split(",") if f.strip()] if args.files else []
|
|
1558
|
+
req = TeamDispatchRequest(
|
|
1559
|
+
target=args.target,
|
|
1560
|
+
problem=args.problem,
|
|
1561
|
+
context=args.context,
|
|
1562
|
+
files=files,
|
|
1563
|
+
expected_outcome=args.expected_outcome,
|
|
1564
|
+
)
|
|
1565
|
+
result = dispatch_team(req).to_dict()
|
|
1566
|
+
print(json.dumps(result, indent=2))
|
|
1567
|
+
return 0
|
|
1568
|
+
|
|
1569
|
+
|
|
1570
|
+
def cmd_team(args: argparse.Namespace) -> int:
|
|
1571
|
+
return cmd_teams(args)
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
def cmd_ccg(args: argparse.Namespace) -> int:
|
|
1575
|
+
files = [f.strip() for f in args.files.split(",") if f.strip()] if args.files else []
|
|
1576
|
+
result = execute_ccg_mode(
|
|
1577
|
+
problem=args.problem,
|
|
1578
|
+
project_dir=_ensure_project_dir(),
|
|
1579
|
+
context=args.context,
|
|
1580
|
+
files=files,
|
|
1581
|
+
)
|
|
1582
|
+
print(json.dumps(result, indent=2))
|
|
1583
|
+
return 0
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
def cmd_crazy(args: argparse.Namespace) -> int:
|
|
1587
|
+
files = [f.strip() for f in args.files.split(",") if f.strip()] if args.files else []
|
|
1588
|
+
result = execute_crazy_mode(
|
|
1589
|
+
problem=args.problem,
|
|
1590
|
+
project_dir=_ensure_project_dir(),
|
|
1591
|
+
context=args.context,
|
|
1592
|
+
files=files,
|
|
1593
|
+
)
|
|
1594
|
+
print(json.dumps(result, indent=2))
|
|
1595
|
+
return 0
|
|
1596
|
+
|
|
1597
|
+
|
|
1598
|
+
def cmd_compat_list(args: argparse.Namespace) -> int:
|
|
1599
|
+
skills = list_compat_skills()
|
|
1600
|
+
print(json.dumps({"status": "ok", "count": len(skills), "skills": skills}, indent=2))
|
|
1601
|
+
return 0
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
def cmd_compat_contract(args: argparse.Namespace) -> int:
|
|
1605
|
+
if args.all:
|
|
1606
|
+
contracts = list_compat_skill_contracts()
|
|
1607
|
+
print(json.dumps({"status": "ok", "count": len(contracts), "contracts": contracts}, indent=2))
|
|
1608
|
+
return 0
|
|
1609
|
+
if not args.skill:
|
|
1610
|
+
print(json.dumps({"status": "error", "message": "Provide --skill or --all"}, indent=2))
|
|
1611
|
+
return 2
|
|
1612
|
+
contract = get_compat_skill_contract(args.skill)
|
|
1613
|
+
if not contract:
|
|
1614
|
+
print(json.dumps({"status": "error", "message": f"Unknown skill: {args.skill}"}, indent=2))
|
|
1615
|
+
return 2
|
|
1616
|
+
print(json.dumps({"status": "ok", "contract": contract}, indent=2))
|
|
1617
|
+
return 0
|
|
1618
|
+
|
|
1619
|
+
|
|
1620
|
+
def cmd_compat_gap_report(args: argparse.Namespace) -> int:
|
|
1621
|
+
report = build_compat_gap_report(_ensure_project_dir())
|
|
1622
|
+
if args.output:
|
|
1623
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
1624
|
+
json.dump(report, f, indent=2)
|
|
1625
|
+
print(json.dumps({"status": "ok", "report": report}, indent=2))
|
|
1626
|
+
return 0
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
def cmd_compat_snapshot(args: argparse.Namespace) -> int:
|
|
1630
|
+
payload = build_contract_snapshot_payload(include_generated_at=True)
|
|
1631
|
+
out_path = args.output or DEFAULT_CONTRACT_SNAPSHOT_PATH
|
|
1632
|
+
with open(out_path, "w", encoding="utf-8") as f:
|
|
1633
|
+
json.dump(payload, f, indent=2)
|
|
1634
|
+
print(json.dumps({"status": "ok", "output": out_path, "count": payload["count"]}, indent=2))
|
|
1635
|
+
return 0
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
def cmd_compat_gate(args: argparse.Namespace) -> int:
|
|
1639
|
+
report = build_compat_gap_report(_ensure_project_dir())
|
|
1640
|
+
if args.output:
|
|
1641
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
1642
|
+
json.dump(report, f, indent=2)
|
|
1643
|
+
bridge_count = int(report.get("maturity_counts", {}).get("bridge", 0))
|
|
1644
|
+
if bridge_count > args.max_bridge:
|
|
1645
|
+
print(
|
|
1646
|
+
json.dumps(
|
|
1647
|
+
{
|
|
1648
|
+
"status": "error",
|
|
1649
|
+
"message": f"OMG compat gate failed: bridge={bridge_count} > max_bridge={args.max_bridge}",
|
|
1650
|
+
"report": report,
|
|
1651
|
+
},
|
|
1652
|
+
indent=2,
|
|
1653
|
+
)
|
|
1654
|
+
)
|
|
1655
|
+
return 3
|
|
1656
|
+
print(
|
|
1657
|
+
json.dumps(
|
|
1658
|
+
{
|
|
1659
|
+
"status": "ok",
|
|
1660
|
+
"message": f"OMG compat gate passed: bridge={bridge_count} <= max_bridge={args.max_bridge}",
|
|
1661
|
+
"report": report,
|
|
1662
|
+
},
|
|
1663
|
+
indent=2,
|
|
1664
|
+
)
|
|
1665
|
+
)
|
|
1666
|
+
return 0
|
|
1667
|
+
|
|
1668
|
+
|
|
1669
|
+
def cmd_compat_run(args: argparse.Namespace) -> int:
|
|
1670
|
+
files = [f.strip() for f in args.files.split(",") if f.strip()] if args.files else []
|
|
1671
|
+
result = dispatch_compat_skill(
|
|
1672
|
+
skill=args.skill,
|
|
1673
|
+
problem=args.problem,
|
|
1674
|
+
context=args.context,
|
|
1675
|
+
files=files,
|
|
1676
|
+
expected_outcome=args.expected_outcome,
|
|
1677
|
+
project_dir=_ensure_project_dir(),
|
|
1678
|
+
)
|
|
1679
|
+
print(json.dumps(result, indent=2))
|
|
1680
|
+
return 0 if result.get("status") == "ok" else 2
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
def cmd_ecosystem_list(args: argparse.Namespace) -> int:
|
|
1684
|
+
repos = list_ecosystem_repos()
|
|
1685
|
+
print(json.dumps({"status": "ok", "count": len(repos), "repos": repos}, indent=2))
|
|
1686
|
+
return 0
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
def cmd_ecosystem_status(args: argparse.Namespace) -> int:
|
|
1690
|
+
result = ecosystem_status(project_dir=_ensure_project_dir())
|
|
1691
|
+
print(json.dumps(result, indent=2))
|
|
1692
|
+
return 0
|
|
1693
|
+
|
|
1694
|
+
|
|
1695
|
+
def cmd_ecosystem_sync(args: argparse.Namespace) -> int:
|
|
1696
|
+
names = [name.strip() for name in args.names.split(",") if name.strip()] if args.names else []
|
|
1697
|
+
result = sync_ecosystem_repos(
|
|
1698
|
+
project_dir=_ensure_project_dir(),
|
|
1699
|
+
names=names,
|
|
1700
|
+
update=bool(args.update),
|
|
1701
|
+
depth=int(args.depth),
|
|
1702
|
+
)
|
|
1703
|
+
print(json.dumps(result, indent=2))
|
|
1704
|
+
errors = [entry for entry in result.get("entries", []) if entry.get("status") == "error"]
|
|
1705
|
+
return 0 if not errors else 2
|
|
1706
|
+
|
|
1707
|
+
|
|
1708
|
+
def _load_release_identity_validator() -> Any:
|
|
1709
|
+
validator_path = ROOT_DIR / "scripts" / "validate-release-identity.py"
|
|
1710
|
+
spec = importlib.util.spec_from_file_location("validate_release_identity", validator_path)
|
|
1711
|
+
if spec is None or spec.loader is None:
|
|
1712
|
+
raise RuntimeError("unable to load validate-release-identity.py")
|
|
1713
|
+
module = importlib.util.module_from_spec(spec)
|
|
1714
|
+
spec.loader.exec_module(module)
|
|
1715
|
+
return module
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
def cmd_contract_validate(args: argparse.Namespace) -> int:
|
|
1719
|
+
result = validate_contract_registry(ROOT_DIR)
|
|
1720
|
+
|
|
1721
|
+
release_identity_scope = getattr(args, "release_identity_scope", "all")
|
|
1722
|
+
forbid_version = getattr(args, "forbid_version", "") or None
|
|
1723
|
+
|
|
1724
|
+
try:
|
|
1725
|
+
validator = _load_release_identity_validator()
|
|
1726
|
+
canonical = validator.extract_canonical_version(ROOT_DIR / "runtime" / "adoption.py")
|
|
1727
|
+
if canonical is None:
|
|
1728
|
+
release_identity = {
|
|
1729
|
+
"canonical_version": None,
|
|
1730
|
+
"scope": release_identity_scope,
|
|
1731
|
+
"forbid_version": forbid_version,
|
|
1732
|
+
"overall_status": "fail",
|
|
1733
|
+
"error": "CANONICAL_VERSION not found",
|
|
1734
|
+
}
|
|
1735
|
+
else:
|
|
1736
|
+
authored_result = (
|
|
1737
|
+
validator.validate_authored(ROOT_DIR, canonical)
|
|
1738
|
+
if release_identity_scope in {"authored", "all"}
|
|
1739
|
+
else {"status": "skipped", "blockers": []}
|
|
1740
|
+
)
|
|
1741
|
+
derived_result = (
|
|
1742
|
+
validator.validate_derived(ROOT_DIR, canonical)
|
|
1743
|
+
if release_identity_scope in {"derived", "all"}
|
|
1744
|
+
else {"status": "skipped", "blockers": []}
|
|
1745
|
+
)
|
|
1746
|
+
residue_result = (
|
|
1747
|
+
validator.scan_scoped_residue(ROOT_DIR, forbid_version)
|
|
1748
|
+
if forbid_version
|
|
1749
|
+
else None
|
|
1750
|
+
)
|
|
1751
|
+
release_identity = validator.build_report(
|
|
1752
|
+
canonical=canonical,
|
|
1753
|
+
scope=release_identity_scope,
|
|
1754
|
+
forbid_version=forbid_version,
|
|
1755
|
+
authored=authored_result,
|
|
1756
|
+
derived=derived_result,
|
|
1757
|
+
scoped_residue=residue_result,
|
|
1758
|
+
)
|
|
1759
|
+
except Exception as exc:
|
|
1760
|
+
release_identity = {
|
|
1761
|
+
"canonical_version": None,
|
|
1762
|
+
"scope": release_identity_scope,
|
|
1763
|
+
"forbid_version": forbid_version,
|
|
1764
|
+
"overall_status": "fail",
|
|
1765
|
+
"error": f"release identity validator failed: {exc}",
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
result["release_identity"] = release_identity
|
|
1769
|
+
status_ok = result.get("status") == "ok" and release_identity.get("overall_status") == "ok"
|
|
1770
|
+
if not status_ok:
|
|
1771
|
+
result["status"] = "error"
|
|
1772
|
+
|
|
1773
|
+
print(json.dumps(result, indent=2))
|
|
1774
|
+
return 0 if status_ok else 2
|
|
1775
|
+
|
|
1776
|
+
|
|
1777
|
+
def cmd_contract_compile(args: argparse.Namespace) -> int:
|
|
1778
|
+
hosts = args.hosts or []
|
|
1779
|
+
if getattr(args, "method", False):
|
|
1780
|
+
result = compile_method_artifacts(
|
|
1781
|
+
root_dir=ROOT_DIR,
|
|
1782
|
+
output_root=args.output_root,
|
|
1783
|
+
hosts=hosts,
|
|
1784
|
+
channel=args.channel,
|
|
1785
|
+
)
|
|
1786
|
+
else:
|
|
1787
|
+
result = compile_contract_outputs(
|
|
1788
|
+
root_dir=ROOT_DIR,
|
|
1789
|
+
output_root=args.output_root,
|
|
1790
|
+
hosts=hosts,
|
|
1791
|
+
channel=args.channel,
|
|
1792
|
+
)
|
|
1793
|
+
print(json.dumps(result, indent=2))
|
|
1794
|
+
return 0 if result.get("status") == "ok" else 2
|
|
1795
|
+
|
|
1796
|
+
|
|
1797
|
+
def cmd_release_readiness(args: argparse.Namespace) -> int:
|
|
1798
|
+
if not os.environ.get("OMG_RELEASE_READY_PROVIDERS", "").strip():
|
|
1799
|
+
os.environ["OMG_RELEASE_READY_PROVIDERS"] = ",".join(get_canonical_hosts())
|
|
1800
|
+
if not os.environ.get("OMG_REQUIRE_HOST_PARITY_REPORT", "").strip():
|
|
1801
|
+
os.environ["OMG_REQUIRE_HOST_PARITY_REPORT"] = "1"
|
|
1802
|
+
result = build_release_readiness(
|
|
1803
|
+
root_dir=ROOT_DIR,
|
|
1804
|
+
output_root=args.output_root,
|
|
1805
|
+
channel=args.channel,
|
|
1806
|
+
)
|
|
1807
|
+
print(json.dumps(result, indent=2))
|
|
1808
|
+
return 0 if result.get("status") == "ok" else 2
|
|
1809
|
+
|
|
1810
|
+
|
|
1811
|
+
def _add_compat_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
1812
|
+
compat_sub = parent.add_subparsers(dest=dest, required=True)
|
|
1813
|
+
compat_list = compat_sub.add_parser("list", help="List supported legacy skill names")
|
|
1814
|
+
compat_list.set_defaults(func=cmd_compat_list)
|
|
1815
|
+
compat_contract = compat_sub.add_parser("contract", help="Show skill contract schema")
|
|
1816
|
+
compat_contract.add_argument("--skill", default="")
|
|
1817
|
+
compat_contract.add_argument("--all", action="store_true")
|
|
1818
|
+
compat_contract.set_defaults(func=cmd_compat_contract)
|
|
1819
|
+
compat_gap = compat_sub.add_parser("gap-report", help="Write compatibility maturity report")
|
|
1820
|
+
compat_gap.add_argument("--output", default=DEFAULT_GAP_REPORT_PATH)
|
|
1821
|
+
compat_gap.set_defaults(func=cmd_compat_gap_report)
|
|
1822
|
+
compat_snapshot = compat_sub.add_parser("snapshot", help="Write current skill contracts snapshot")
|
|
1823
|
+
compat_snapshot.add_argument("--output", default=DEFAULT_CONTRACT_SNAPSHOT_PATH)
|
|
1824
|
+
compat_snapshot.set_defaults(func=cmd_compat_snapshot)
|
|
1825
|
+
compat_gate = compat_sub.add_parser("gate", help="Fail if bridge skill count exceeds threshold")
|
|
1826
|
+
compat_gate.add_argument("--max-bridge", type=int, default=0)
|
|
1827
|
+
compat_gate.add_argument("--output", default=DEFAULT_GAP_REPORT_PATH)
|
|
1828
|
+
compat_gate.set_defaults(func=cmd_compat_gate)
|
|
1829
|
+
compat_run = compat_sub.add_parser("run", help="Run a legacy skill through OMG router")
|
|
1830
|
+
compat_run.add_argument("--skill", required=True)
|
|
1831
|
+
compat_run.add_argument("--problem", default="")
|
|
1832
|
+
compat_run.add_argument("--context", default="")
|
|
1833
|
+
compat_run.add_argument("--files", default="")
|
|
1834
|
+
compat_run.add_argument("--expected-outcome", default="")
|
|
1835
|
+
compat_run.set_defaults(func=cmd_compat_run)
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
def _add_ecosystem_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
1839
|
+
ecosystem_sub = parent.add_subparsers(dest=dest, required=True)
|
|
1840
|
+
ecosystem_list = ecosystem_sub.add_parser("list", help="List OMG ecosystem integration targets")
|
|
1841
|
+
ecosystem_list.set_defaults(func=cmd_ecosystem_list)
|
|
1842
|
+
|
|
1843
|
+
ecosystem_status_cmd = ecosystem_sub.add_parser("status", help="Show current ecosystem install status")
|
|
1844
|
+
ecosystem_status_cmd.set_defaults(func=cmd_ecosystem_status)
|
|
1845
|
+
|
|
1846
|
+
ecosystem_sync = ecosystem_sub.add_parser("sync", help="Clone or refresh ecosystem repositories")
|
|
1847
|
+
ecosystem_sync.add_argument("--names", default="", help="Comma-separated repo names or aliases")
|
|
1848
|
+
ecosystem_sync.add_argument("--update", action="store_true", help="Fetch latest refs for existing clones")
|
|
1849
|
+
ecosystem_sync.add_argument("--depth", type=int, default=1, help="Git depth for shallow clone/fetch")
|
|
1850
|
+
ecosystem_sync.set_defaults(func=cmd_ecosystem_sync)
|
|
1851
|
+
|
|
1852
|
+
|
|
1853
|
+
def cmd_provider_parity_eval(args: argparse.Namespace) -> int:
|
|
1854
|
+
from runtime.provider_parity_eval import run_provider_parity_eval
|
|
1855
|
+
result = run_provider_parity_eval(
|
|
1856
|
+
task_path=args.task,
|
|
1857
|
+
mode=args.mode,
|
|
1858
|
+
output_root=args.output_root or None,
|
|
1859
|
+
)
|
|
1860
|
+
print(json.dumps(result, indent=2))
|
|
1861
|
+
return 0 if result.get("status") == "ok" else 2
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
def cmd_context_compile(args: argparse.Namespace) -> int:
|
|
1865
|
+
from runtime.context_compiler import compile_context_packets
|
|
1866
|
+
hosts = args.hosts or []
|
|
1867
|
+
result = compile_context_packets(
|
|
1868
|
+
root_dir=ROOT_DIR,
|
|
1869
|
+
output_root=args.output_root,
|
|
1870
|
+
hosts=hosts,
|
|
1871
|
+
)
|
|
1872
|
+
print(json.dumps(result, indent=2))
|
|
1873
|
+
return 0 if result.get("status") == "ok" else 2
|
|
1874
|
+
|
|
1875
|
+
|
|
1876
|
+
def _add_context_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
1877
|
+
context_sub = parent.add_subparsers(dest=dest, required=True)
|
|
1878
|
+
context_compile = context_sub.add_parser("compile", help="Compile bounded context packets for canonical hosts")
|
|
1879
|
+
context_compile.add_argument(
|
|
1880
|
+
"--host",
|
|
1881
|
+
dest="hosts",
|
|
1882
|
+
action="append",
|
|
1883
|
+
choices=list(CANONICAL_HOST_CHOICES),
|
|
1884
|
+
required=True,
|
|
1885
|
+
help="Host to compile context for (repeat for multiple hosts)",
|
|
1886
|
+
)
|
|
1887
|
+
context_compile.add_argument("--output-root", default="", help="Write outputs to this root instead of the repo root")
|
|
1888
|
+
context_compile.set_defaults(func=cmd_context_compile)
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
def _add_contract_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
1892
|
+
contract_sub = parent.add_subparsers(dest=dest, required=True)
|
|
1893
|
+
|
|
1894
|
+
contract_validate = contract_sub.add_parser("validate", help="Validate contract doc, schema, and bundle registry")
|
|
1895
|
+
contract_validate.add_argument(
|
|
1896
|
+
"--release-identity-scope",
|
|
1897
|
+
default="all",
|
|
1898
|
+
choices=["authored", "derived", "all"],
|
|
1899
|
+
help="Scope for embedded release identity validation",
|
|
1900
|
+
)
|
|
1901
|
+
contract_validate.add_argument(
|
|
1902
|
+
"--forbid-version",
|
|
1903
|
+
default="",
|
|
1904
|
+
help="Optional stale version to reject in scoped residue targets",
|
|
1905
|
+
)
|
|
1906
|
+
contract_validate.set_defaults(func=cmd_contract_validate)
|
|
1907
|
+
|
|
1908
|
+
contract_compile = contract_sub.add_parser(
|
|
1909
|
+
"compile", help="Compile host artifacts from the canonical contract"
|
|
1910
|
+
)
|
|
1911
|
+
contract_compile.add_argument(
|
|
1912
|
+
"--host",
|
|
1913
|
+
dest="hosts",
|
|
1914
|
+
action="append",
|
|
1915
|
+
choices=list(CANONICAL_HOST_CHOICES),
|
|
1916
|
+
required=True,
|
|
1917
|
+
help="Host to compile (repeat for multiple hosts)",
|
|
1918
|
+
)
|
|
1919
|
+
contract_compile.add_argument("--channel", default="public", choices=["public", "enterprise"])
|
|
1920
|
+
contract_compile.add_argument("--output-root", default="", help="Write outputs to this root instead of the repo root")
|
|
1921
|
+
contract_compile.add_argument("--method", action="store_true", default=False, help="Emit signed seven-phase methodology artifacts instead of host artifacts")
|
|
1922
|
+
contract_compile.set_defaults(func=cmd_contract_compile)
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
def cmd_profile_review(args: argparse.Namespace) -> int:
|
|
1926
|
+
"""Read-only review of governed profile state."""
|
|
1927
|
+
from runtime.profile_io import load_profile, ensure_governed_preferences, profile_version_from_map, assess_profile_risk
|
|
1928
|
+
|
|
1929
|
+
project_dir = _ensure_project_dir()
|
|
1930
|
+
profile_path = os.path.join(project_dir, ".omg", "state", "profile.yaml")
|
|
1931
|
+
profile = load_profile(profile_path)
|
|
1932
|
+
|
|
1933
|
+
# Ensure governed_preferences structure exists (in-memory only, no write)
|
|
1934
|
+
ensure_governed_preferences(profile)
|
|
1935
|
+
governed = profile.get("governed_preferences", {})
|
|
1936
|
+
governed = governed if isinstance(governed, dict) else {}
|
|
1937
|
+
|
|
1938
|
+
style_entries = governed.get("style", [])
|
|
1939
|
+
style_entries = style_entries if isinstance(style_entries, list) else []
|
|
1940
|
+
safety_entries = governed.get("safety", [])
|
|
1941
|
+
safety_entries = safety_entries if isinstance(safety_entries, list) else []
|
|
1942
|
+
|
|
1943
|
+
pending = []
|
|
1944
|
+
for entry in style_entries + safety_entries:
|
|
1945
|
+
if isinstance(entry, dict) and entry.get("confirmation_state") == "pending_confirmation":
|
|
1946
|
+
pending.append({
|
|
1947
|
+
"field": entry.get("field", ""),
|
|
1948
|
+
"value": entry.get("value", ""),
|
|
1949
|
+
"section": entry.get("section", ""),
|
|
1950
|
+
})
|
|
1951
|
+
|
|
1952
|
+
decay_candidates = []
|
|
1953
|
+
for entry in style_entries:
|
|
1954
|
+
if not isinstance(entry, dict):
|
|
1955
|
+
continue
|
|
1956
|
+
dm = entry.get("decay_metadata")
|
|
1957
|
+
if isinstance(dm, dict):
|
|
1958
|
+
score = 0.0
|
|
1959
|
+
try:
|
|
1960
|
+
score = float(dm.get("decay_score", 0.0))
|
|
1961
|
+
except (TypeError, ValueError):
|
|
1962
|
+
pass
|
|
1963
|
+
if score > 0:
|
|
1964
|
+
decay_candidates.append({
|
|
1965
|
+
"field": entry.get("field", ""),
|
|
1966
|
+
"value": entry.get("value", ""),
|
|
1967
|
+
"decay_score": score,
|
|
1968
|
+
"last_seen_at": dm.get("last_seen_at", ""),
|
|
1969
|
+
"decay_reason": dm.get("decay_reason", ""),
|
|
1970
|
+
})
|
|
1971
|
+
|
|
1972
|
+
provenance_obj = profile.get("profile_provenance")
|
|
1973
|
+
provenance = provenance_obj if isinstance(provenance_obj, dict) else {}
|
|
1974
|
+
recent_updates = provenance.get("recent_updates", [])
|
|
1975
|
+
recent_updates = recent_updates if isinstance(recent_updates, list) else []
|
|
1976
|
+
provenance_summary = [
|
|
1977
|
+
{
|
|
1978
|
+
"run_id": str(u.get("run_id", "")),
|
|
1979
|
+
"source": str(u.get("source", "")),
|
|
1980
|
+
"field": str(u.get("field", "")),
|
|
1981
|
+
"updated_at": str(u.get("updated_at", "")),
|
|
1982
|
+
}
|
|
1983
|
+
for u in recent_updates
|
|
1984
|
+
if isinstance(u, dict)
|
|
1985
|
+
]
|
|
1986
|
+
|
|
1987
|
+
version = profile_version_from_map(profile) if profile else ""
|
|
1988
|
+
|
|
1989
|
+
risk = assess_profile_risk(profile)
|
|
1990
|
+
|
|
1991
|
+
result: dict[str, Any] = {
|
|
1992
|
+
"schema": "ProfileReview",
|
|
1993
|
+
"style": style_entries,
|
|
1994
|
+
"safety": safety_entries,
|
|
1995
|
+
"pending_confirmations": pending,
|
|
1996
|
+
"decay_candidates": decay_candidates,
|
|
1997
|
+
"provenance_summary": provenance_summary,
|
|
1998
|
+
"profile_version": version,
|
|
1999
|
+
"risk_assessment": risk,
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
fmt = getattr(args, "format", "json")
|
|
2003
|
+
if fmt == "json":
|
|
2004
|
+
print(json.dumps(result, indent=2))
|
|
2005
|
+
else:
|
|
2006
|
+
print("=== Profile Review ===")
|
|
2007
|
+
print(f"Profile version: {version[:16]}..." if version else "Profile version: (none)")
|
|
2008
|
+
print(f"\n--- Style preferences ({len(style_entries)}) ---")
|
|
2009
|
+
for e in style_entries:
|
|
2010
|
+
if isinstance(e, dict):
|
|
2011
|
+
print(f" {e.get('field', '?')}: {e.get('value', '?')} [{e.get('confirmation_state', '?')}]")
|
|
2012
|
+
print(f"\n--- Safety preferences ({len(safety_entries)}) ---")
|
|
2013
|
+
for e in safety_entries:
|
|
2014
|
+
if isinstance(e, dict):
|
|
2015
|
+
print(f" {e.get('field', '?')}: {e.get('value', '?')} [{e.get('confirmation_state', '?')}]")
|
|
2016
|
+
if pending:
|
|
2017
|
+
print(f"\n--- Pending confirmations ({len(pending)}) ---")
|
|
2018
|
+
for p in pending:
|
|
2019
|
+
print(f" {p['section']}/{p['field']}: {p['value']}")
|
|
2020
|
+
else:
|
|
2021
|
+
print("\n--- Pending confirmations: none ---")
|
|
2022
|
+
if decay_candidates:
|
|
2023
|
+
print(f"\n--- Decay candidates ({len(decay_candidates)}) ---")
|
|
2024
|
+
for d in decay_candidates:
|
|
2025
|
+
print(f" {d['field']}: score={d['decay_score']:.3f} reason={d['decay_reason']}")
|
|
2026
|
+
else:
|
|
2027
|
+
print("\n--- Decay candidates: none ---")
|
|
2028
|
+
if provenance_summary:
|
|
2029
|
+
print(f"\n--- Provenance ({len(provenance_summary)} recent updates) ---")
|
|
2030
|
+
for p in provenance_summary:
|
|
2031
|
+
print(f" [{p['updated_at']}] {p['source']} -> {p['field']}")
|
|
2032
|
+
else:
|
|
2033
|
+
print("\n--- Provenance: no recent updates ---")
|
|
2034
|
+
return 0
|
|
2035
|
+
|
|
2036
|
+
|
|
2037
|
+
def cmd_doctor(args: argparse.Namespace) -> int:
|
|
2038
|
+
fmt = getattr(args, "format", "text")
|
|
2039
|
+
fix_mode = getattr(args, "fix", False)
|
|
2040
|
+
dry_run = getattr(args, "dry_run", False)
|
|
2041
|
+
repair_pack_filter = getattr(args, "repair_pack", None)
|
|
2042
|
+
|
|
2043
|
+
if fix_mode:
|
|
2044
|
+
result = run_doctor_fix(root_dir=ROOT_DIR, dry_run=dry_run)
|
|
2045
|
+
for receipt in result.get("fix_receipts", []):
|
|
2046
|
+
receipt["repair_pack"] = _infer_repair_pack(receipt.get("check", ""))
|
|
2047
|
+
for check in result.get("checks", []):
|
|
2048
|
+
check["repair_pack"] = _infer_repair_pack(check.get("name", ""))
|
|
2049
|
+
|
|
2050
|
+
if repair_pack_filter:
|
|
2051
|
+
result["checks"] = [c for c in result["checks"] if c.get("repair_pack") == repair_pack_filter]
|
|
2052
|
+
result["fix_receipts"] = [r for r in result.get("fix_receipts", []) if r.get("repair_pack") == repair_pack_filter]
|
|
2053
|
+
|
|
2054
|
+
if fmt == "json":
|
|
2055
|
+
print(json.dumps(result, indent=2))
|
|
2056
|
+
else:
|
|
2057
|
+
mode_label = "DRY RUN" if dry_run else "FIX"
|
|
2058
|
+
pack_label = f" [pack: {repair_pack_filter}]" if repair_pack_filter else ""
|
|
2059
|
+
print(f"Doctor ({mode_label}){pack_label}")
|
|
2060
|
+
for check in result["checks"]:
|
|
2061
|
+
marker = "PASS" if check["status"] == "ok" else ("BLOCKER" if check["status"] == "blocker" else "WARN")
|
|
2062
|
+
fix_tag = " [fixable]" if check.get("fixable") else ""
|
|
2063
|
+
req_tag = "" if check["required"] else " (optional)"
|
|
2064
|
+
pack_tag = f" [{check.get('repair_pack', 'general')}]"
|
|
2065
|
+
print(f" {marker:>7} {check['name']}: {check['message']}{req_tag}{fix_tag}{pack_tag}")
|
|
2066
|
+
for receipt in result.get("fix_receipts", []):
|
|
2067
|
+
executed_tag = "applied" if receipt["executed"] else "planned"
|
|
2068
|
+
print(f" FIX [{executed_tag}] {receipt['check']}: {receipt['action']} [{receipt.get('repair_pack', 'general')}]")
|
|
2069
|
+
blockers = sum(1 for c in result["checks"] if c["status"] == "blocker")
|
|
2070
|
+
fixes = len(result.get("fix_receipts", []))
|
|
2071
|
+
print(f"\nBLOCKER [{blockers}] | FIXES [{fixes}]")
|
|
2072
|
+
return 0 if result["status"] == "pass" else 1
|
|
2073
|
+
|
|
2074
|
+
result = run_doctor(root_dir=ROOT_DIR)
|
|
2075
|
+
for check in result.get("checks", []):
|
|
2076
|
+
check["repair_pack"] = _infer_repair_pack(check.get("name", ""))
|
|
2077
|
+
|
|
2078
|
+
if repair_pack_filter:
|
|
2079
|
+
result["checks"] = [c for c in result["checks"] if c.get("repair_pack") == repair_pack_filter]
|
|
2080
|
+
|
|
2081
|
+
if fmt == "json":
|
|
2082
|
+
print(json.dumps(result, indent=2))
|
|
2083
|
+
else:
|
|
2084
|
+
pack_label = f" [pack: {repair_pack_filter}]" if repair_pack_filter else ""
|
|
2085
|
+
print(f"Doctor{pack_label}")
|
|
2086
|
+
for check in result["checks"]:
|
|
2087
|
+
marker = "PASS" if check["status"] == "ok" else ("BLOCKER" if check["status"] == "blocker" else "WARN")
|
|
2088
|
+
req_tag = "" if check["required"] else " (optional)"
|
|
2089
|
+
pack_tag = f" [{check.get('repair_pack', 'general')}]"
|
|
2090
|
+
print(f" {marker:>7} {check['name']}: {check['message']}{req_tag}{pack_tag}")
|
|
2091
|
+
blockers = sum(1 for c in result["checks"] if c["status"] == "blocker")
|
|
2092
|
+
warnings = sum(1 for c in result["checks"] if c["status"] == "warning")
|
|
2093
|
+
passed = sum(1 for c in result["checks"] if c["status"] == "ok")
|
|
2094
|
+
print(f"\nPASS [{passed}] | WARN [{warnings}] | BLOCKER [{blockers}]")
|
|
2095
|
+
return 0 if result["status"] == "pass" else 1
|
|
2096
|
+
|
|
2097
|
+
|
|
2098
|
+
def cmd_env_doctor(args: argparse.Namespace) -> int:
|
|
2099
|
+
fmt = getattr(args, "format", "text")
|
|
2100
|
+
result = run_env_doctor(root_dir=ROOT_DIR)
|
|
2101
|
+
for check in result.get("checks", []):
|
|
2102
|
+
check["repair_pack"] = _infer_repair_pack(check.get("name", ""))
|
|
2103
|
+
|
|
2104
|
+
if fmt == "json":
|
|
2105
|
+
print(json.dumps(result, indent=2))
|
|
2106
|
+
else:
|
|
2107
|
+
print("Env Doctor")
|
|
2108
|
+
for check in result["checks"]:
|
|
2109
|
+
marker = "PASS" if check["status"] == "ok" else ("BLOCKER" if check["status"] == "blocker" else "WARN")
|
|
2110
|
+
req_tag = "" if check["required"] else " (optional)"
|
|
2111
|
+
pack_tag = f" [{check.get('repair_pack', 'general')}]"
|
|
2112
|
+
print(f" {marker:>7} {check['name']}: {check['message']}{req_tag}{pack_tag}")
|
|
2113
|
+
blockers = sum(1 for c in result["checks"] if c["status"] == "blocker")
|
|
2114
|
+
warnings = sum(1 for c in result["checks"] if c["status"] == "warning")
|
|
2115
|
+
passed = sum(1 for c in result["checks"] if c["status"] == "ok")
|
|
2116
|
+
print(f"\nPASS [{passed}] | WARN [{warnings}] | BLOCKER [{blockers}]")
|
|
2117
|
+
return 0 if result["status"] == "pass" else 1
|
|
2118
|
+
|
|
2119
|
+
|
|
2120
|
+
def _detect_clis() -> dict[str, Any]:
|
|
2121
|
+
"""Detect which host CLIs are available on PATH."""
|
|
2122
|
+
import shutil
|
|
2123
|
+
|
|
2124
|
+
hosts = {"codex": "codex", "gemini": "gemini", "kimi": "kimi"}
|
|
2125
|
+
result: dict[str, Any] = {}
|
|
2126
|
+
for host, binary in hosts.items():
|
|
2127
|
+
result[host] = {"detected": shutil.which(binary) is not None}
|
|
2128
|
+
return result
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
def _install_action_to_dict(action: InstallAction) -> dict[str, Any]:
|
|
2132
|
+
return {
|
|
2133
|
+
"host": action.host,
|
|
2134
|
+
"target_path": action.target_path,
|
|
2135
|
+
"description": action.description,
|
|
2136
|
+
"kind": action.kind,
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
def _format_install_plan_text(plan_data: dict[str, Any]) -> str:
|
|
2141
|
+
lines: list[str] = ["Install Plan"]
|
|
2142
|
+
blockers = plan_data.get("integrity_errors", [])
|
|
2143
|
+
if blockers:
|
|
2144
|
+
lines.append(f" BLOCKERS ({len(blockers)}):")
|
|
2145
|
+
for b in blockers:
|
|
2146
|
+
lines.append(f" - {b}")
|
|
2147
|
+
actions = plan_data.get("actions", [])
|
|
2148
|
+
lines.append(f" Actions ({len(actions)}):")
|
|
2149
|
+
for a in actions:
|
|
2150
|
+
lines.append(f" [{a['host']}] {a['description']} -> {a['target_path']}")
|
|
2151
|
+
lines.append(f" Pre-checks: {', '.join(plan_data.get('pre_checks', []))}")
|
|
2152
|
+
lines.append(f" Post-checks: {', '.join(plan_data.get('post_checks', []))}")
|
|
2153
|
+
return "\n".join(lines)
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
def _format_install_dryrun_text(result_data: dict[str, Any]) -> str:
|
|
2157
|
+
lines: list[str] = ["Install Dry-Run Result"]
|
|
2158
|
+
lines.append(f" Executed: {result_data.get('executed', False)}")
|
|
2159
|
+
errors = result_data.get("errors", [])
|
|
2160
|
+
if errors:
|
|
2161
|
+
lines.append(f" Errors ({len(errors)}):")
|
|
2162
|
+
for e in errors:
|
|
2163
|
+
lines.append(f" - {e}")
|
|
2164
|
+
completed = result_data.get("actions_completed", [])
|
|
2165
|
+
skipped = result_data.get("actions_skipped", [])
|
|
2166
|
+
lines.append(f" Actions completed: {len(completed)}")
|
|
2167
|
+
lines.append(f" Actions skipped: {len(skipped)}")
|
|
2168
|
+
for s in skipped:
|
|
2169
|
+
lines.append(f" skipped: {s}")
|
|
2170
|
+
return "\n".join(lines)
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
def _format_install_apply_text(result_data: dict[str, Any]) -> str:
|
|
2174
|
+
lines: list[str] = ["Install Apply Result"]
|
|
2175
|
+
lines.append(f" Executed: {result_data.get('executed', False)}")
|
|
2176
|
+
errors = result_data.get("errors", [])
|
|
2177
|
+
if errors:
|
|
2178
|
+
lines.append(f" Errors ({len(errors)}):")
|
|
2179
|
+
for e in errors:
|
|
2180
|
+
lines.append(f" - {e}")
|
|
2181
|
+
for receipt in result_data.get("receipts", []):
|
|
2182
|
+
executed_tag = "applied" if receipt["executed"] else "planned"
|
|
2183
|
+
lines.append(f" [{executed_tag}] {receipt['check']}: {receipt['action']}")
|
|
2184
|
+
lines.append(f" backup: {receipt['backup_path']}")
|
|
2185
|
+
return "\n".join(lines)
|
|
2186
|
+
|
|
2187
|
+
|
|
2188
|
+
REPAIR_PACK_MAP: dict[str, str] = {
|
|
2189
|
+
"python_version": "runtime",
|
|
2190
|
+
"fastmcp": "runtime",
|
|
2191
|
+
"omg_control_reachable": "runtime",
|
|
2192
|
+
"managed_runtime": "runtime",
|
|
2193
|
+
"memory_reachable": "runtime",
|
|
2194
|
+
"policy_files": "governance",
|
|
2195
|
+
"metadata_drift": "governance",
|
|
2196
|
+
"compiled_bundles": "release",
|
|
2197
|
+
"host_compatibility": "host",
|
|
2198
|
+
"plugin_compatibility": "host",
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
HOST_KEYWORDS: list[tuple[str, str]] = [
|
|
2202
|
+
("claude", "claude"),
|
|
2203
|
+
("codex", "codex"),
|
|
2204
|
+
("gemini", "gemini"),
|
|
2205
|
+
("kimi", "kimi"),
|
|
2206
|
+
("opencode", "opencode"),
|
|
2207
|
+
]
|
|
2208
|
+
|
|
2209
|
+
|
|
2210
|
+
def _infer_repair_pack(check_name: str) -> str:
|
|
2211
|
+
if check_name in REPAIR_PACK_MAP:
|
|
2212
|
+
return REPAIR_PACK_MAP[check_name]
|
|
2213
|
+
name_lower = check_name.lower()
|
|
2214
|
+
for keyword, pack in HOST_KEYWORDS:
|
|
2215
|
+
if keyword in name_lower:
|
|
2216
|
+
return pack
|
|
2217
|
+
if "python" in name_lower or "runtime" in name_lower or "mcp" in name_lower:
|
|
2218
|
+
return "runtime"
|
|
2219
|
+
if "policy" in name_lower or "metadata" in name_lower:
|
|
2220
|
+
return "governance"
|
|
2221
|
+
return "general"
|
|
2222
|
+
|
|
2223
|
+
|
|
2224
|
+
def _run_install_preflight(fmt: str) -> dict[str, Any]:
|
|
2225
|
+
if os.environ.get("OMG_TEST_PREFLIGHT_BLOCK") == "1":
|
|
2226
|
+
return {
|
|
2227
|
+
"schema": "DoctorResult",
|
|
2228
|
+
"status": "fail",
|
|
2229
|
+
"checks": [
|
|
2230
|
+
{
|
|
2231
|
+
"name": "test_blocker",
|
|
2232
|
+
"status": "blocker",
|
|
2233
|
+
"message": "simulated blocking check",
|
|
2234
|
+
"required": True,
|
|
2235
|
+
"remediation": "Fix the simulated blocker",
|
|
2236
|
+
},
|
|
2237
|
+
],
|
|
2238
|
+
"version": "test",
|
|
2239
|
+
}
|
|
2240
|
+
return run_env_doctor(root_dir=ROOT_DIR)
|
|
2241
|
+
|
|
2242
|
+
|
|
2243
|
+
def _format_preflight_text(preflight: dict[str, Any]) -> str:
|
|
2244
|
+
lines: list[str] = ["Running env preflight..."]
|
|
2245
|
+
for check in preflight.get("checks", []):
|
|
2246
|
+
tag = check["status"].upper()
|
|
2247
|
+
line = f" [{tag}] {check['name']}: {check['message']}"
|
|
2248
|
+
if check["status"] == "blocker" and check.get("required"):
|
|
2249
|
+
line += f"\n Remediation: {check.get('remediation', 'N/A')}"
|
|
2250
|
+
lines.append(line)
|
|
2251
|
+
if preflight["status"] == "pass":
|
|
2252
|
+
lines.append(" Preflight: PASS")
|
|
2253
|
+
else:
|
|
2254
|
+
lines.append(" Preflight: FAIL")
|
|
2255
|
+
return "\n".join(lines)
|
|
2256
|
+
|
|
2257
|
+
|
|
2258
|
+
def cmd_install(args: argparse.Namespace) -> int:
|
|
2259
|
+
fmt = getattr(args, "format", "text")
|
|
2260
|
+
preset = getattr(args, "preset", "balanced")
|
|
2261
|
+
mode = getattr(args, "mode", "omg-only")
|
|
2262
|
+
do_plan = getattr(args, "plan", False)
|
|
2263
|
+
do_dry_run = getattr(args, "dry_run", False)
|
|
2264
|
+
do_apply = getattr(args, "apply", False)
|
|
2265
|
+
skip_preflight = getattr(args, "skip_preflight", False)
|
|
2266
|
+
|
|
2267
|
+
is_ci = getattr(args, "ci", False) or os.environ.get("OMG_CI") == "1"
|
|
2268
|
+
is_non_interactive = getattr(args, "non_interactive", False)
|
|
2269
|
+
if not do_plan and not do_dry_run and not do_apply:
|
|
2270
|
+
if is_ci or is_non_interactive:
|
|
2271
|
+
do_apply = True
|
|
2272
|
+
else:
|
|
2273
|
+
if fmt == "json":
|
|
2274
|
+
print(json.dumps({"error": "specify --plan, --dry-run, or --apply"}, indent=2))
|
|
2275
|
+
else:
|
|
2276
|
+
print("Error: specify --plan, --dry-run, or --apply")
|
|
2277
|
+
print("Run `omg install --plan` to preview, or `omg install --apply` to execute.")
|
|
2278
|
+
return 1
|
|
2279
|
+
|
|
2280
|
+
preflight_data: dict[str, Any] | None = None
|
|
2281
|
+
if (do_plan or do_apply) and not skip_preflight:
|
|
2282
|
+
preflight_data = _run_install_preflight(fmt)
|
|
2283
|
+
if fmt != "json":
|
|
2284
|
+
print(_format_preflight_text(preflight_data))
|
|
2285
|
+
required_blockers = [
|
|
2286
|
+
c for c in preflight_data.get("checks", [])
|
|
2287
|
+
if c.get("status") == "blocker" and c.get("required") is True
|
|
2288
|
+
]
|
|
2289
|
+
if required_blockers:
|
|
2290
|
+
blocked_output: dict[str, Any] = {"preflight": preflight_data}
|
|
2291
|
+
if do_plan:
|
|
2292
|
+
blocked_output["schema"] = "InstallPlan"
|
|
2293
|
+
blocked_output["status"] = "blocked"
|
|
2294
|
+
blocked_output["actions"] = []
|
|
2295
|
+
blocked_output["pre_checks"] = []
|
|
2296
|
+
blocked_output["post_checks"] = []
|
|
2297
|
+
blocked_output["integrity_errors"] = []
|
|
2298
|
+
else:
|
|
2299
|
+
blocked_output["schema"] = "InstallApplyResult"
|
|
2300
|
+
blocked_output["executed"] = False
|
|
2301
|
+
blocked_output["actions"] = []
|
|
2302
|
+
blocked_output["receipts"] = []
|
|
2303
|
+
blocked_output["errors"] = [
|
|
2304
|
+
f"env preflight blocker: {c['name']}: {c['message']}" for c in required_blockers
|
|
2305
|
+
]
|
|
2306
|
+
if fmt == "json":
|
|
2307
|
+
print(json.dumps(blocked_output, indent=2))
|
|
2308
|
+
else:
|
|
2309
|
+
for c in required_blockers:
|
|
2310
|
+
print(f" BLOCKED: {c['name']}: {c['message']}")
|
|
2311
|
+
if c.get("remediation"):
|
|
2312
|
+
print(f" Remediation: {c['remediation']}")
|
|
2313
|
+
return 1
|
|
2314
|
+
|
|
2315
|
+
preflight_inject: dict[str, Any] = {}
|
|
2316
|
+
if skip_preflight:
|
|
2317
|
+
preflight_inject = {"preflight": {"skipped": True}}
|
|
2318
|
+
elif preflight_data is not None:
|
|
2319
|
+
preflight_inject = {"preflight": preflight_data}
|
|
2320
|
+
|
|
2321
|
+
detected_clis = _detect_clis()
|
|
2322
|
+
plan = compute_install_plan(
|
|
2323
|
+
project_dir=str(ROOT_DIR),
|
|
2324
|
+
detected_clis=detected_clis,
|
|
2325
|
+
preset=preset,
|
|
2326
|
+
mode=mode,
|
|
2327
|
+
selected_ids=None,
|
|
2328
|
+
)
|
|
2329
|
+
|
|
2330
|
+
from runtime.install_planner import _verify_install_integrity
|
|
2331
|
+
integrity_errors = _verify_install_integrity(Path(plan.source_root))
|
|
2332
|
+
|
|
2333
|
+
if do_plan:
|
|
2334
|
+
plan_data: dict[str, Any] = {
|
|
2335
|
+
"schema": "InstallPlan",
|
|
2336
|
+
"actions": [_install_action_to_dict(a) for a in plan.actions],
|
|
2337
|
+
"pre_checks": plan.pre_checks,
|
|
2338
|
+
"post_checks": plan.post_checks,
|
|
2339
|
+
"source_root": plan.source_root,
|
|
2340
|
+
"integrity_errors": integrity_errors,
|
|
2341
|
+
**preflight_inject,
|
|
2342
|
+
}
|
|
2343
|
+
if integrity_errors:
|
|
2344
|
+
plan_data["status"] = "blocked"
|
|
2345
|
+
if fmt == "json":
|
|
2346
|
+
print(json.dumps(plan_data, indent=2))
|
|
2347
|
+
else:
|
|
2348
|
+
print(_format_install_plan_text(plan_data))
|
|
2349
|
+
return 1
|
|
2350
|
+
plan_data["status"] = "ok"
|
|
2351
|
+
if fmt == "json":
|
|
2352
|
+
print(json.dumps(plan_data, indent=2))
|
|
2353
|
+
else:
|
|
2354
|
+
print(_format_install_plan_text(plan_data))
|
|
2355
|
+
return 0
|
|
2356
|
+
|
|
2357
|
+
if do_apply:
|
|
2358
|
+
if integrity_errors:
|
|
2359
|
+
err_data: dict[str, Any] = {
|
|
2360
|
+
"schema": "InstallApplyResult",
|
|
2361
|
+
"executed": False,
|
|
2362
|
+
"actions": [_install_action_to_dict(a) for a in plan.actions],
|
|
2363
|
+
"receipts": [],
|
|
2364
|
+
"errors": integrity_errors,
|
|
2365
|
+
**preflight_inject,
|
|
2366
|
+
}
|
|
2367
|
+
if fmt == "json":
|
|
2368
|
+
print(json.dumps(err_data, indent=2))
|
|
2369
|
+
else:
|
|
2370
|
+
print(_format_install_apply_text(err_data))
|
|
2371
|
+
return 1
|
|
2372
|
+
|
|
2373
|
+
result = execute_plan(plan, dry_run=False)
|
|
2374
|
+
config_receipt = result.get("receipt") or {}
|
|
2375
|
+
backup_path = ""
|
|
2376
|
+
if isinstance(config_receipt, dict):
|
|
2377
|
+
backup_path = str(config_receipt.get("backup_path", ""))
|
|
2378
|
+
|
|
2379
|
+
receipts: list[dict[str, Any]] = []
|
|
2380
|
+
for action in plan.actions:
|
|
2381
|
+
receipts.append({
|
|
2382
|
+
"check": f"install_{action.host}",
|
|
2383
|
+
"action": action.description,
|
|
2384
|
+
"backup_path": backup_path,
|
|
2385
|
+
"executed": result["executed"],
|
|
2386
|
+
"rollback_ref": backup_path,
|
|
2387
|
+
})
|
|
2388
|
+
|
|
2389
|
+
apply_data: dict[str, Any] = {
|
|
2390
|
+
"schema": "InstallApplyResult",
|
|
2391
|
+
"executed": result["executed"],
|
|
2392
|
+
"actions": [_install_action_to_dict(a) for a in plan.actions],
|
|
2393
|
+
"receipts": receipts,
|
|
2394
|
+
"actions_completed": result["actions_completed"],
|
|
2395
|
+
"actions_skipped": result["actions_skipped"],
|
|
2396
|
+
"errors": result["errors"],
|
|
2397
|
+
**preflight_inject,
|
|
2398
|
+
}
|
|
2399
|
+
has_errors = bool(result["errors"])
|
|
2400
|
+
if fmt == "json":
|
|
2401
|
+
print(json.dumps(apply_data, indent=2, default=str))
|
|
2402
|
+
else:
|
|
2403
|
+
print(_format_install_apply_text(apply_data))
|
|
2404
|
+
return 1 if has_errors else 0
|
|
2405
|
+
|
|
2406
|
+
result = execute_plan(plan, dry_run=True)
|
|
2407
|
+
result_data: dict[str, Any] = {
|
|
2408
|
+
"schema": "InstallResult",
|
|
2409
|
+
"executed": result["executed"],
|
|
2410
|
+
"actions_completed": result["actions_completed"],
|
|
2411
|
+
"actions_skipped": result["actions_skipped"],
|
|
2412
|
+
"receipt": result["receipt"],
|
|
2413
|
+
"errors": result["errors"],
|
|
2414
|
+
"integrity_errors": integrity_errors,
|
|
2415
|
+
"actions": [_install_action_to_dict(a) for a in plan.actions],
|
|
2416
|
+
**preflight_inject,
|
|
2417
|
+
}
|
|
2418
|
+
has_errors = bool(result["errors"]) or bool(integrity_errors)
|
|
2419
|
+
if fmt == "json":
|
|
2420
|
+
print(json.dumps(result_data, indent=2, default=str))
|
|
2421
|
+
else:
|
|
2422
|
+
print(_format_install_dryrun_text(result_data))
|
|
2423
|
+
return 1 if has_errors else 0
|
|
2424
|
+
|
|
2425
|
+
|
|
2426
|
+
def cmd_validate(args: argparse.Namespace) -> int:
|
|
2427
|
+
fmt = getattr(args, "format", "text")
|
|
2428
|
+
result = run_validate(root_dir=ROOT_DIR)
|
|
2429
|
+
if fmt == "json":
|
|
2430
|
+
print(json.dumps(result, indent=2))
|
|
2431
|
+
else:
|
|
2432
|
+
print(validate_format_text(result))
|
|
2433
|
+
return 0 if result["status"] == "pass" else 1
|
|
2434
|
+
|
|
2435
|
+
|
|
2436
|
+
def _format_plugin_diagnostics_text(result: dict[str, Any]) -> str:
|
|
2437
|
+
summary = result.get("summary", {})
|
|
2438
|
+
lines = [
|
|
2439
|
+
"Plugin Diagnostics",
|
|
2440
|
+
f"Status: {result.get('status', 'unknown').upper()}",
|
|
2441
|
+
(
|
|
2442
|
+
"Records: "
|
|
2443
|
+
f"{summary.get('total_records', 0)} | Conflicts: {summary.get('total_conflicts', 0)} "
|
|
2444
|
+
f"(blockers={summary.get('blockers', 0)}, warnings={summary.get('warnings', 0)}, infos={summary.get('infos', 0)})"
|
|
2445
|
+
),
|
|
2446
|
+
]
|
|
2447
|
+
|
|
2448
|
+
next_actions = result.get("next_actions", [])
|
|
2449
|
+
if isinstance(next_actions, list) and next_actions:
|
|
2450
|
+
lines.append("Next Actions:")
|
|
2451
|
+
for action in next_actions:
|
|
2452
|
+
lines.append(f"- {action}")
|
|
2453
|
+
|
|
2454
|
+
elapsed_ms = result.get("elapsed_ms", 0.0)
|
|
2455
|
+
lines.append(f"Elapsed: {elapsed_ms:.2f}ms")
|
|
2456
|
+
return "\n".join(lines)
|
|
2457
|
+
|
|
2458
|
+
|
|
2459
|
+
def cmd_diagnose_plugins(args: argparse.Namespace) -> int:
|
|
2460
|
+
fmt = getattr(args, "format", "text")
|
|
2461
|
+
result = run_plugin_diagnostics(root=str(ROOT_DIR), live=bool(getattr(args, "live", False)))
|
|
2462
|
+
if fmt == "json":
|
|
2463
|
+
print(json.dumps(result, indent=2))
|
|
2464
|
+
else:
|
|
2465
|
+
print(_format_plugin_diagnostics_text(result))
|
|
2466
|
+
return 0
|
|
2467
|
+
|
|
2468
|
+
|
|
2469
|
+
def cmd_diagnose_plugins_approve(args: argparse.Namespace) -> int:
|
|
2470
|
+
fmt = getattr(args, "format", "json")
|
|
2471
|
+
result = approve_plugin(args.source, args.host, args.reason, root=str(ROOT_DIR))
|
|
2472
|
+
if fmt == "json":
|
|
2473
|
+
print(json.dumps(result, indent=2))
|
|
2474
|
+
else:
|
|
2475
|
+
print(f"Approval status: {result.get('status', 'unknown')}")
|
|
2476
|
+
print(str(result.get("message", "")))
|
|
2477
|
+
return 0 if result.get("status") == "ok" else 2
|
|
2478
|
+
|
|
2479
|
+
def _add_release_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
2480
|
+
release_sub = parent.add_subparsers(dest=dest, required=True)
|
|
2481
|
+
|
|
2482
|
+
release_audit = release_sub.add_parser("audit", help="Audit release artifacts and remote release surfaces")
|
|
2483
|
+
release_audit.add_argument("--artifact", action="store_true", help="Run the release artifact audit")
|
|
2484
|
+
release_audit.add_argument("--apply", action="store_true", help="Apply GitHub release remediation")
|
|
2485
|
+
release_audit.add_argument("--confirm", default="", help="Confirmation token that must match the target version for --apply")
|
|
2486
|
+
release_audit.add_argument("--repo", default="trac3r00/OMG", help="GitHub owner/name repo slug")
|
|
2487
|
+
release_audit.add_argument("--version", default="", help="Override target version")
|
|
2488
|
+
release_audit.add_argument("--format", default="text", choices=["text", "json"])
|
|
2489
|
+
release_audit.add_argument("--output-json", default="", help="Write JSON report to this path")
|
|
2490
|
+
release_audit.set_defaults(func=cmd_release_audit)
|
|
2491
|
+
|
|
2492
|
+
release_readiness = release_sub.add_parser("readiness", help="Check production release readiness for compiled artifacts")
|
|
2493
|
+
release_readiness.add_argument("--channel", default="dual", choices=["public", "enterprise", "dual"])
|
|
2494
|
+
release_readiness.add_argument("--output-root", default="", help="Check compiled outputs from this root instead of the repo root")
|
|
2495
|
+
release_readiness.set_defaults(func=cmd_release_readiness)
|
|
2496
|
+
|
|
2497
|
+
|
|
2498
|
+
def cmd_release_audit(args: argparse.Namespace) -> int:
|
|
2499
|
+
if not getattr(args, "artifact", False):
|
|
2500
|
+
print("release audit currently requires --artifact", file=sys.stderr)
|
|
2501
|
+
return 2
|
|
2502
|
+
|
|
2503
|
+
report = run_release_artifact_audit(
|
|
2504
|
+
ROOT_DIR,
|
|
2505
|
+
repo=str(args.repo),
|
|
2506
|
+
version=str(args.version),
|
|
2507
|
+
apply=bool(getattr(args, "apply", False)),
|
|
2508
|
+
confirm=str(getattr(args, "confirm", "")),
|
|
2509
|
+
github_token=resolve_github_token(),
|
|
2510
|
+
)
|
|
2511
|
+
if getattr(args, "output_json", ""):
|
|
2512
|
+
output_path = Path(str(args.output_json))
|
|
2513
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2514
|
+
output_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
|
2515
|
+
if getattr(args, "format", "text") == "json":
|
|
2516
|
+
print(json.dumps(report, indent=2))
|
|
2517
|
+
else:
|
|
2518
|
+
print(format_release_audit_text(report))
|
|
2519
|
+
if report.get("status") == "error":
|
|
2520
|
+
return 2
|
|
2521
|
+
return 0 if report.get("overall_status") == "ok" else 1
|
|
2522
|
+
|
|
2523
|
+
|
|
2524
|
+
def cmd_docs_generate(args: argparse.Namespace) -> int:
|
|
2525
|
+
check_root = Path(args.output_root) if args.output_root else ROOT_DIR
|
|
2526
|
+
|
|
2527
|
+
if args.check:
|
|
2528
|
+
result = check_docs(check_root)
|
|
2529
|
+
if result["status"] != "ok":
|
|
2530
|
+
print("Doc check FAILED. Drift detected:")
|
|
2531
|
+
for d in result["drift"]:
|
|
2532
|
+
print(f" - {d}")
|
|
2533
|
+
print("\nRun 'python3 scripts/omg.py docs generate' to fix.")
|
|
2534
|
+
return 1
|
|
2535
|
+
print("Doc check PASSED. No drift detected.")
|
|
2536
|
+
return 0
|
|
2537
|
+
|
|
2538
|
+
output_root = Path(args.output_root) if args.output_root else ROOT_DIR / ".sisyphus" / "tmp" / "generated-docs"
|
|
2539
|
+
result = generate_docs(output_root)
|
|
2540
|
+
if result["status"] == "ok":
|
|
2541
|
+
if not args.output_root:
|
|
2542
|
+
for name in GENERATED_ARTIFACTS:
|
|
2543
|
+
src = output_root / name
|
|
2544
|
+
dst = ROOT_DIR / name
|
|
2545
|
+
dst.write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
|
|
2546
|
+
print(f"Copied all {len(GENERATED_ARTIFACTS)} artifacts to repo root")
|
|
2547
|
+
|
|
2548
|
+
print(f"Successfully generated docs at: {result['output_root']}")
|
|
2549
|
+
for artifact in result["artifacts"]:
|
|
2550
|
+
print(f" - {artifact}")
|
|
2551
|
+
return 0
|
|
2552
|
+
print(f"Failed to generate docs: {result.get('error', 'Unknown error')}")
|
|
2553
|
+
return 1
|
|
2554
|
+
|
|
2555
|
+
|
|
2556
|
+
def _add_docs_subcommands(parent: argparse.ArgumentParser, *, dest: str) -> None:
|
|
2557
|
+
sub = parent.add_subparsers(dest=dest)
|
|
2558
|
+
|
|
2559
|
+
generate = sub.add_parser("generate", help="Generate machine-readable and human-readable docs")
|
|
2560
|
+
generate.add_argument("--output-root", default="", help="Output directory for generated docs")
|
|
2561
|
+
generate.add_argument("--check", action="store_true", help="Exit non-zero if generated docs differ from disk")
|
|
2562
|
+
generate.set_defaults(func=cmd_docs_generate)
|
|
2563
|
+
|
|
2564
|
+
|
|
2565
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
2566
|
+
parser = argparse.ArgumentParser(prog="omg", description=f"OMG {CANONICAL_VERSION} CLI")
|
|
2567
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
2568
|
+
|
|
2569
|
+
ship = sub.add_parser("ship", help="Idea -> Evidence -> PR flow")
|
|
2570
|
+
ship.add_argument("--idea", default=".omg/idea.yml")
|
|
2571
|
+
ship.add_argument("--runtime", default="claude", choices=["claude", "gpt", "local"])
|
|
2572
|
+
ship.add_argument("--run-id", default="")
|
|
2573
|
+
ship.set_defaults(func=cmd_ship)
|
|
2574
|
+
|
|
2575
|
+
fix = sub.add_parser("fix", help="Issue-driven fix flow")
|
|
2576
|
+
fix.add_argument("--issue", required=True)
|
|
2577
|
+
fix.add_argument("--runtime", default="claude", choices=["claude", "gpt", "local"])
|
|
2578
|
+
fix.set_defaults(func=cmd_fix)
|
|
2579
|
+
|
|
2580
|
+
issue = sub.add_parser("issue", help="Active issue diagnostics and red-team simulation")
|
|
2581
|
+
issue.add_argument("--run-id", default="")
|
|
2582
|
+
issue.add_argument(
|
|
2583
|
+
"--surfaces",
|
|
2584
|
+
default="",
|
|
2585
|
+
help=(
|
|
2586
|
+
"Comma-separated surfaces to scan: "
|
|
2587
|
+
"live_session,forge_runs,hooks,skills,mcps,plugin_interop,governed_tools,domain_pipelines"
|
|
2588
|
+
),
|
|
2589
|
+
)
|
|
2590
|
+
issue.add_argument("--simulate-surface", default="")
|
|
2591
|
+
issue.add_argument("--simulate-scenario", default="")
|
|
2592
|
+
issue.set_defaults(func=cmd_issue)
|
|
2593
|
+
|
|
2594
|
+
undo = sub.add_parser("undo", help="Undo last interaction step")
|
|
2595
|
+
undo.add_argument("--step-id", default="", help="Journal step ID to undo (latest if omitted)")
|
|
2596
|
+
undo.set_defaults(func=cmd_undo)
|
|
2597
|
+
|
|
2598
|
+
secure = sub.add_parser("secure", help="Evaluate command risk")
|
|
2599
|
+
secure.add_argument("--command", required=True)
|
|
2600
|
+
secure.set_defaults(func=cmd_secure)
|
|
2601
|
+
|
|
2602
|
+
security = sub.add_parser("security", help="Canonical OMG security workflows")
|
|
2603
|
+
security_sub = security.add_subparsers(dest="security_command", required=True)
|
|
2604
|
+
security_check = security_sub.add_parser("check", help="Run canonical OMG security check")
|
|
2605
|
+
security_check.add_argument("--scope", default=".")
|
|
2606
|
+
security_check.add_argument("--live-enrichment", action="store_true")
|
|
2607
|
+
security_check.add_argument("--waivers-json", default="")
|
|
2608
|
+
security_check.set_defaults(func=cmd_security_check)
|
|
2609
|
+
|
|
2610
|
+
waive_tests = sub.add_parser("waive-tests", help="Emit structured waiver evidence for test-intent lock exceptions")
|
|
2611
|
+
waive_tests.add_argument("--lock-id", required=True)
|
|
2612
|
+
waive_tests.add_argument("--reason", required=True)
|
|
2613
|
+
waive_tests.set_defaults(func=cmd_waive_tests)
|
|
2614
|
+
|
|
2615
|
+
api_twin = sub.add_parser("api-twin", help="Contract replay and fixture-based API simulation")
|
|
2616
|
+
api_twin_sub = api_twin.add_subparsers(dest="api_twin_command", required=True)
|
|
2617
|
+
api_twin_ingest = api_twin_sub.add_parser("ingest", help="Ingest OpenAPI/Postman/example contract input")
|
|
2618
|
+
api_twin_ingest.add_argument("--name", required=True)
|
|
2619
|
+
api_twin_ingest.add_argument("--source", required=True)
|
|
2620
|
+
api_twin_ingest.set_defaults(func=cmd_api_twin_ingest)
|
|
2621
|
+
api_twin_record = api_twin_sub.add_parser("record", help="Record approved fixture response")
|
|
2622
|
+
api_twin_record.add_argument("--name", required=True)
|
|
2623
|
+
api_twin_record.add_argument("--endpoint", default="default")
|
|
2624
|
+
api_twin_record.add_argument("--cassette-version", default="v1")
|
|
2625
|
+
api_twin_record.add_argument("--request-json", required=True)
|
|
2626
|
+
api_twin_record.add_argument("--response-json", required=True)
|
|
2627
|
+
api_twin_record.add_argument("--validated", action="store_true")
|
|
2628
|
+
api_twin_record.add_argument("--redactions-json", default="")
|
|
2629
|
+
api_twin_record.set_defaults(func=cmd_api_twin_record)
|
|
2630
|
+
api_twin_serve = api_twin_sub.add_parser("serve", help="Replay a fixture with optional drift/failure injection")
|
|
2631
|
+
api_twin_serve.add_argument("--name", required=True)
|
|
2632
|
+
api_twin_serve.add_argument("--endpoint", default="default")
|
|
2633
|
+
api_twin_serve.add_argument("--cassette-version", default="v1")
|
|
2634
|
+
api_twin_serve.add_argument("--latency-ms", type=int, default=0)
|
|
2635
|
+
api_twin_serve.add_argument("--failure-mode", default="")
|
|
2636
|
+
api_twin_serve.add_argument("--schema-drift", action="store_true")
|
|
2637
|
+
api_twin_serve.set_defaults(func=cmd_api_twin_serve)
|
|
2638
|
+
api_twin_verify = api_twin_sub.add_parser("verify", help="Validate a fixture against a live response")
|
|
2639
|
+
api_twin_verify.add_argument("--name", required=True)
|
|
2640
|
+
api_twin_verify.add_argument("--endpoint", default="default")
|
|
2641
|
+
api_twin_verify.add_argument("--cassette-version", default="v1")
|
|
2642
|
+
api_twin_verify.add_argument("--live-response-json", required=True)
|
|
2643
|
+
api_twin_verify.set_defaults(func=cmd_api_twin_verify)
|
|
2644
|
+
|
|
2645
|
+
preflight = sub.add_parser("preflight", help="Structured OMG preflight routing")
|
|
2646
|
+
preflight.add_argument("--goal", required=True)
|
|
2647
|
+
preflight.set_defaults(func=cmd_preflight)
|
|
2648
|
+
|
|
2649
|
+
resolve_policy = sub.add_parser("resolve-policy", help="Resolve effective policy from tier, presets, and packs")
|
|
2650
|
+
resolve_policy.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2651
|
+
resolve_policy.add_argument("--provider", default="claude")
|
|
2652
|
+
resolve_policy.set_defaults(func=cmd_resolve_policy)
|
|
2653
|
+
|
|
2654
|
+
policy_pack = sub.add_parser("policy-pack", help="Policy pack management")
|
|
2655
|
+
policy_pack_sub = policy_pack.add_subparsers(dest="policy_pack_command", required=True)
|
|
2656
|
+
policy_pack_list_cmd = policy_pack_sub.add_parser("list", help="List available policy packs")
|
|
2657
|
+
policy_pack_list_cmd.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2658
|
+
policy_pack_list_cmd.set_defaults(func=cmd_policy_pack_list)
|
|
2659
|
+
policy_pack_diff_cmd = policy_pack_sub.add_parser("diff", help="Show overrides for a policy pack")
|
|
2660
|
+
policy_pack_diff_cmd.add_argument("pack_id", help="Policy pack id to diff")
|
|
2661
|
+
policy_pack_diff_cmd.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2662
|
+
policy_pack_diff_cmd.set_defaults(func=cmd_policy_pack_diff)
|
|
2663
|
+
policy_pack_scaffold_cmd = policy_pack_sub.add_parser("scaffold", help="Generate a new policy pack template")
|
|
2664
|
+
policy_pack_scaffold_cmd.add_argument("pack_id", nargs="?", default="new-pack", help="Pack id for the scaffold")
|
|
2665
|
+
policy_pack_scaffold_cmd.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2666
|
+
policy_pack_scaffold_cmd.set_defaults(func=cmd_policy_pack_scaffold)
|
|
2667
|
+
policy_pack_keygen_cmd = policy_pack_sub.add_parser("keygen", help="Generate an Ed25519 signing keypair")
|
|
2668
|
+
policy_pack_keygen_cmd.add_argument("--output", default=None, help="Path to write keypair JSON file")
|
|
2669
|
+
policy_pack_keygen_cmd.add_argument("--add-to-trust-root", action="store_true", default=False, dest="add_to_trust_root",
|
|
2670
|
+
help="Append generated public key to trusted_signers.json")
|
|
2671
|
+
policy_pack_keygen_cmd.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2672
|
+
policy_pack_keygen_cmd.set_defaults(func=cmd_policy_pack_keygen)
|
|
2673
|
+
policy_pack_sign_cmd = policy_pack_sub.add_parser("sign", help="Sign a policy pack for attestation")
|
|
2674
|
+
policy_pack_sign_cmd.add_argument("pack_id", help="Policy pack id to sign")
|
|
2675
|
+
policy_pack_sign_cmd.add_argument("--key-id", default=None, dest="key_id", help="Signer key id")
|
|
2676
|
+
policy_pack_sign_cmd.add_argument("--key-path", default=None, dest="key_path", help="Path to private key file (raw base64 or JSON keypair)")
|
|
2677
|
+
policy_pack_sign_cmd.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2678
|
+
policy_pack_sign_cmd.set_defaults(func=cmd_policy_pack_sign)
|
|
2679
|
+
policy_pack_verify_cmd = policy_pack_sub.add_parser("verify", help="Verify a signed policy pack")
|
|
2680
|
+
policy_pack_verify_cmd.add_argument("pack_id", nargs="?", default="", help="Policy pack id to verify")
|
|
2681
|
+
policy_pack_verify_cmd.add_argument("--all", action="store_true", default=False, help="Verify all signed packs")
|
|
2682
|
+
policy_pack_verify_cmd.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2683
|
+
policy_pack_verify_cmd.set_defaults(func=cmd_policy_pack_verify)
|
|
2684
|
+
|
|
2685
|
+
proof = sub.add_parser("proof", help="Proof helpers")
|
|
2686
|
+
proof_sub = proof.add_subparsers(dest="proof_command", required=True)
|
|
2687
|
+
proof_summary = proof_sub.add_parser("summary", help="Summarize proof status")
|
|
2688
|
+
proof_summary.add_argument("--format", default="json", choices=["json", "markdown", "text"], dest="format")
|
|
2689
|
+
proof_summary.set_defaults(func=cmd_proof_summary)
|
|
2690
|
+
proof_open = proof_sub.add_parser("open", help="Open latest evidence pack as narrated proof")
|
|
2691
|
+
proof_open.add_argument("--run-id", default=None, dest="run_id")
|
|
2692
|
+
proof_open.add_argument("--format", default="markdown", choices=["markdown", "text"], dest="format")
|
|
2693
|
+
proof_open.set_defaults(func=cmd_proof_open)
|
|
2694
|
+
|
|
2695
|
+
blocked = sub.add_parser("blocked", help="Inspect governance block explanations")
|
|
2696
|
+
blocked.add_argument("--last", action="store_true", required=True, help="Show the last block explanation")
|
|
2697
|
+
blocked.add_argument("--format", default="text", choices=["text", "markdown"], dest="format")
|
|
2698
|
+
blocked.set_defaults(func=cmd_blocked_last)
|
|
2699
|
+
|
|
2700
|
+
explain = sub.add_parser("explain", help="Explain run artifacts")
|
|
2701
|
+
explain_sub = explain.add_subparsers(dest="explain_command", required=True)
|
|
2702
|
+
explain_run = explain_sub.add_parser("run", help="Explain run by id")
|
|
2703
|
+
explain_run.add_argument("--run-id", required=True)
|
|
2704
|
+
explain_run.add_argument("--format", default="json", choices=["json", "markdown", "text"], dest="format")
|
|
2705
|
+
explain_run.set_defaults(func=cmd_explain_run)
|
|
2706
|
+
|
|
2707
|
+
budget = sub.add_parser("budget", help="Budget envelope operations")
|
|
2708
|
+
budget_sub = budget.add_subparsers(dest="budget_command", required=True)
|
|
2709
|
+
budget_simulate = budget_sub.add_parser("simulate", help="Simulate budget envelope outcomes")
|
|
2710
|
+
budget_simulate.add_argument("--provider", default="claude")
|
|
2711
|
+
budget_simulate.add_argument("--tier", default="")
|
|
2712
|
+
budget_simulate.add_argument("--channel", default="public")
|
|
2713
|
+
budget_simulate.add_argument("--preset", default="balanced")
|
|
2714
|
+
budget_simulate.add_argument("--task", default="", dest="task")
|
|
2715
|
+
budget_simulate.add_argument("--tokens-used", type=int, default=0)
|
|
2716
|
+
budget_simulate.add_argument("--token-limit", type=int, default=0)
|
|
2717
|
+
budget_simulate.add_argument("--enforce", action="store_true")
|
|
2718
|
+
budget_simulate.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2719
|
+
budget_simulate.set_defaults(func=cmd_budget_simulate)
|
|
2720
|
+
|
|
2721
|
+
domain_pack = sub.add_parser("domain-pack", help="Inspect optional domain pack contracts")
|
|
2722
|
+
domain_pack.add_argument("--name", required=True, choices=["robotics", "vision", "algorithms", "health"])
|
|
2723
|
+
domain_pack.set_defaults(func=cmd_domain_pack)
|
|
2724
|
+
|
|
2725
|
+
vision = sub.add_parser("vision", help="OCR, visual diff, and semantic image analysis")
|
|
2726
|
+
vision_sub = vision.add_subparsers(dest="vision_command", required=True)
|
|
2727
|
+
vision_ocr = vision_sub.add_parser("ocr", help="Extract text from one or more images")
|
|
2728
|
+
vision_ocr.set_defaults(func=cmd_vision_ocr)
|
|
2729
|
+
vision_compare = vision_sub.add_parser("compare", help="Compare one or more images deterministically")
|
|
2730
|
+
vision_compare.set_defaults(func=cmd_vision_compare)
|
|
2731
|
+
vision_analyze = vision_sub.add_parser("analyze", help="Run semantic image analysis")
|
|
2732
|
+
vision_analyze.set_defaults(func=cmd_vision_analyze)
|
|
2733
|
+
vision_batch = vision_sub.add_parser("batch", help="Run a vision batch job")
|
|
2734
|
+
vision_batch.set_defaults(func=cmd_vision_batch)
|
|
2735
|
+
vision_eval = vision_sub.add_parser("eval", help="Evaluate vision job outputs")
|
|
2736
|
+
vision_eval.set_defaults(func=cmd_vision_eval)
|
|
2737
|
+
|
|
2738
|
+
tracebank = sub.add_parser("tracebank", help="Record structured route traces")
|
|
2739
|
+
tracebank.add_argument("--trace-type", required=True)
|
|
2740
|
+
tracebank.add_argument("--route", required=True)
|
|
2741
|
+
tracebank.add_argument("--status", default="ok")
|
|
2742
|
+
tracebank.add_argument("--plan-json", default="")
|
|
2743
|
+
tracebank.add_argument("--verify-json", default="")
|
|
2744
|
+
tracebank.set_defaults(func=cmd_trace_record)
|
|
2745
|
+
|
|
2746
|
+
eval_gate = sub.add_parser("eval-gate", help="Evaluate a trace for release gating")
|
|
2747
|
+
eval_gate.add_argument("--trace-id", required=True)
|
|
2748
|
+
eval_gate.add_argument("--suites", required=True, help="Comma-separated suite names")
|
|
2749
|
+
eval_gate.add_argument("--metrics-json", required=True)
|
|
2750
|
+
eval_gate.set_defaults(func=cmd_eval_gate)
|
|
2751
|
+
|
|
2752
|
+
delta = sub.add_parser("delta-classifier", help="Classify repo changes for routing and policy")
|
|
2753
|
+
delta.add_argument("--goal", default="")
|
|
2754
|
+
delta.add_argument("--files", default="")
|
|
2755
|
+
delta.set_defaults(func=cmd_delta_classify)
|
|
2756
|
+
|
|
2757
|
+
incident = sub.add_parser("incident-replay", help="Build an incident replay pack")
|
|
2758
|
+
incident.add_argument("--title", required=True)
|
|
2759
|
+
incident.add_argument("--failing-tests", default="")
|
|
2760
|
+
incident.add_argument("--logs", default="")
|
|
2761
|
+
incident.add_argument("--diff-summary-json", required=True)
|
|
2762
|
+
incident.add_argument("--trace-id", default="")
|
|
2763
|
+
incident.set_defaults(func=cmd_incident_replay)
|
|
2764
|
+
|
|
2765
|
+
lineage = sub.add_parser("data-lineage", help="Build lineage metadata for generated artifacts")
|
|
2766
|
+
lineage.add_argument("--artifact-type", required=True)
|
|
2767
|
+
lineage.add_argument("--sources-json", required=True)
|
|
2768
|
+
lineage.add_argument("--privacy", required=True)
|
|
2769
|
+
lineage.add_argument("--license-name", required=True)
|
|
2770
|
+
lineage.add_argument("--derivation-json", required=True)
|
|
2771
|
+
lineage.add_argument("--trace-id", default="")
|
|
2772
|
+
lineage.set_defaults(func=cmd_lineage)
|
|
2773
|
+
|
|
2774
|
+
supervisor = sub.add_parser("remote-supervisor", help="Local-only authenticated supervisor session helpers")
|
|
2775
|
+
supervisor_sub = supervisor.add_subparsers(dest="remote_supervisor_command", required=True)
|
|
2776
|
+
supervisor_issue = supervisor_sub.add_parser("issue", help="Issue a local supervisor session")
|
|
2777
|
+
supervisor_issue.add_argument("--worker-id", required=True)
|
|
2778
|
+
supervisor_issue.add_argument("--shared-secret", required=True)
|
|
2779
|
+
supervisor_issue.set_defaults(func=cmd_supervisor_issue)
|
|
2780
|
+
supervisor_verify = supervisor_sub.add_parser("verify", help="Verify a supervisor session token")
|
|
2781
|
+
supervisor_verify.add_argument("--token", required=True)
|
|
2782
|
+
supervisor_verify.add_argument("--shared-secret", required=True)
|
|
2783
|
+
supervisor_verify.set_defaults(func=cmd_supervisor_verify)
|
|
2784
|
+
|
|
2785
|
+
maintainer = sub.add_parser("maintainer", help="OSS maintainer evidence helper")
|
|
2786
|
+
maintainer.add_argument("--mode", default="impact", choices=["triage", "release", "review", "impact"])
|
|
2787
|
+
maintainer.set_defaults(func=cmd_maintainer)
|
|
2788
|
+
|
|
2789
|
+
trust = sub.add_parser("trust", help="Trust review operations")
|
|
2790
|
+
trust_sub = trust.add_subparsers(dest="trust_command", required=True)
|
|
2791
|
+
trust_review = trust_sub.add_parser("review", help="Review config change")
|
|
2792
|
+
trust_review.add_argument("--file", default="settings.json")
|
|
2793
|
+
trust_review.add_argument("--old", required=True, help="Path to old config json")
|
|
2794
|
+
trust_review.add_argument("--new", required=True, help="Path to new config json")
|
|
2795
|
+
trust_review.set_defaults(func=cmd_trust_review)
|
|
2796
|
+
|
|
2797
|
+
runtime = sub.add_parser("runtime", help="Runtime operations")
|
|
2798
|
+
runtime_sub = runtime.add_subparsers(dest="runtime_command", required=True)
|
|
2799
|
+
runtime_dispatch = runtime_sub.add_parser("dispatch", help="Dispatch runtime job")
|
|
2800
|
+
runtime_dispatch.add_argument("--runtime", required=True, choices=["claude", "gpt", "local"])
|
|
2801
|
+
runtime_dispatch.add_argument("--idea", default="", help="Path to idea json")
|
|
2802
|
+
runtime_dispatch.add_argument("--idea-json", default="", help="Inline idea json")
|
|
2803
|
+
runtime_dispatch.set_defaults(func=cmd_runtime_dispatch)
|
|
2804
|
+
|
|
2805
|
+
lab = sub.add_parser("lab", help="Lab pipeline operations")
|
|
2806
|
+
lab_sub = lab.add_subparsers(dest="lab_command", required=True)
|
|
2807
|
+
lab_train = lab_sub.add_parser("train", help="Run lab pipeline job")
|
|
2808
|
+
lab_train.add_argument("--job", default="", help="Path to job json")
|
|
2809
|
+
lab_train.add_argument("--job-json", default="", help="Inline job json")
|
|
2810
|
+
lab_train.set_defaults(func=cmd_lab_train)
|
|
2811
|
+
lab_eval = lab_sub.add_parser("eval", help="Publish lab result when eligible")
|
|
2812
|
+
lab_eval.add_argument("--result", default="", help="Path to result json")
|
|
2813
|
+
lab_eval.add_argument("--result-json", default="", help="Inline result json")
|
|
2814
|
+
lab_eval.set_defaults(func=cmd_lab_eval)
|
|
2815
|
+
|
|
2816
|
+
forge = sub.add_parser("forge", help="Labs-only domain-model prototyping and evaluation")
|
|
2817
|
+
forge_sub = forge.add_subparsers(dest="forge_command", required=True)
|
|
2818
|
+
forge_run = forge_sub.add_parser("run", help="Run a forge job through the lab pipeline")
|
|
2819
|
+
forge_run.add_argument("--job", default="", help="Path to job json")
|
|
2820
|
+
forge_run.add_argument("--job-json", default="", help="Inline job json")
|
|
2821
|
+
forge_run.add_argument("--preset", default="labs", choices=list(VALID_PRESETS), help="Adoption preset (must be labs)")
|
|
2822
|
+
forge_run.add_argument("--run-id", default="", help="Optional run id used for evidence output")
|
|
2823
|
+
forge_run.set_defaults(func=cmd_forge_run)
|
|
2824
|
+
|
|
2825
|
+
forge_vision_agent = forge_sub.add_parser(
|
|
2826
|
+
"vision-agent",
|
|
2827
|
+
help="Run bounded vision-agent forge flow with specialist dispatch",
|
|
2828
|
+
)
|
|
2829
|
+
forge_vision_agent.add_argument("--job-json", default="", help="Optional job overrides as inline JSON")
|
|
2830
|
+
forge_vision_agent.add_argument("--preset", default="labs", choices=list(VALID_PRESETS), help="Adoption preset (must be labs)")
|
|
2831
|
+
forge_vision_agent.add_argument("--target-metric", type=float, default=0.8)
|
|
2832
|
+
forge_vision_agent.add_argument("--simulated-metric", type=float, default=0.9)
|
|
2833
|
+
forge_vision_agent.add_argument("--run-id", default="", help="Optional run id used for evidence output")
|
|
2834
|
+
forge_vision_agent.set_defaults(func=cmd_forge_vision_agent)
|
|
2835
|
+
|
|
2836
|
+
for _domain, _func in [
|
|
2837
|
+
("robotics", cmd_forge_robotics),
|
|
2838
|
+
("algorithms", cmd_forge_algorithms),
|
|
2839
|
+
("health", cmd_forge_health),
|
|
2840
|
+
("cybersecurity", cmd_forge_cybersecurity),
|
|
2841
|
+
]:
|
|
2842
|
+
_p = forge_sub.add_parser(_domain, help=f"Run bounded {_domain} forge flow with specialist dispatch")
|
|
2843
|
+
_p.add_argument("--job-json", default="", help="Optional job overrides as inline JSON")
|
|
2844
|
+
_p.add_argument("--preset", default="labs", choices=list(VALID_PRESETS), help="Adoption preset (must be labs)")
|
|
2845
|
+
_p.add_argument("--run-id", default="", help="Optional run id used for evidence output")
|
|
2846
|
+
_p.set_defaults(func=_func)
|
|
2847
|
+
|
|
2848
|
+
teams = sub.add_parser("teams", help="Internal OMG team routing")
|
|
2849
|
+
teams.add_argument("--target", default="auto", choices=["auto", "codex", "gemini", "ccg"])
|
|
2850
|
+
teams.add_argument("--problem", required=True)
|
|
2851
|
+
teams.add_argument("--context", default="")
|
|
2852
|
+
teams.add_argument("--files", default="")
|
|
2853
|
+
teams.add_argument("--expected-outcome", default="")
|
|
2854
|
+
teams.set_defaults(func=cmd_teams)
|
|
2855
|
+
|
|
2856
|
+
team = sub.add_parser("team", help="Internal OMG staged team routing (canonical)")
|
|
2857
|
+
team.add_argument("--target", default="auto", choices=["auto", "codex", "gemini", "ccg"])
|
|
2858
|
+
team.add_argument("--problem", required=True)
|
|
2859
|
+
team.add_argument("--context", default="")
|
|
2860
|
+
team.add_argument("--files", default="")
|
|
2861
|
+
team.add_argument("--expected-outcome", default="")
|
|
2862
|
+
team.set_defaults(func=cmd_team)
|
|
2863
|
+
|
|
2864
|
+
ccg = sub.add_parser("ccg", help="OMG CCG (tri-track) routing")
|
|
2865
|
+
ccg.add_argument("--problem", required=True)
|
|
2866
|
+
ccg.add_argument("--context", default="")
|
|
2867
|
+
ccg.add_argument("--files", default="")
|
|
2868
|
+
ccg.add_argument("--expected-outcome", default="")
|
|
2869
|
+
ccg.set_defaults(func=cmd_ccg)
|
|
2870
|
+
|
|
2871
|
+
crazy = sub.add_parser("crazy", help="OMG CRAZY mode - parallel multi-agent orchestration")
|
|
2872
|
+
crazy.add_argument("--problem", required=True, help="Task description")
|
|
2873
|
+
crazy.add_argument("--context", default="", help="Additional context")
|
|
2874
|
+
crazy.add_argument("--files", default="", help="Comma-separated focus files")
|
|
2875
|
+
crazy.set_defaults(func=cmd_crazy)
|
|
2876
|
+
|
|
2877
|
+
compat = sub.add_parser("compat", help="OMG legacy compatibility bridge")
|
|
2878
|
+
_add_compat_subcommands(compat, dest="compat_command")
|
|
2879
|
+
|
|
2880
|
+
omc = sub.add_parser("omc", help="Alias of `compat` for legacy scripts")
|
|
2881
|
+
_add_compat_subcommands(omc, dest="omc_command")
|
|
2882
|
+
|
|
2883
|
+
ecosystem = sub.add_parser("ecosystem", help="Upstream ecosystem sync and status")
|
|
2884
|
+
_add_ecosystem_subcommands(ecosystem, dest="ecosystem_command")
|
|
2885
|
+
|
|
2886
|
+
contract = sub.add_parser("contract", help="Canonical OMG contract validation and compilation")
|
|
2887
|
+
_add_contract_subcommands(contract, dest="contract_command")
|
|
2888
|
+
|
|
2889
|
+
context_parser = sub.add_parser("context", help="Context packet compiler")
|
|
2890
|
+
_add_context_subcommands(context_parser, dest="context_command")
|
|
2891
|
+
|
|
2892
|
+
parity_eval = sub.add_parser("provider-parity-eval", help="Evaluate provider parity across canonical hosts")
|
|
2893
|
+
parity_eval.add_argument("--task", required=True, help="Path to bounded task JSON file")
|
|
2894
|
+
parity_eval.add_argument("--mode", default="recorded", choices=["recorded", "live"], help="Evaluation mode")
|
|
2895
|
+
parity_eval.add_argument("--output-root", default="", help="Write outputs here instead of repo root")
|
|
2896
|
+
parity_eval.set_defaults(func=cmd_provider_parity_eval)
|
|
2897
|
+
|
|
2898
|
+
release = sub.add_parser("release", help="OMG release audit and readiness checks")
|
|
2899
|
+
_add_release_subcommands(release, dest="release_command")
|
|
2900
|
+
|
|
2901
|
+
doctor = sub.add_parser("doctor", help="Canonical install and runtime verification")
|
|
2902
|
+
doctor.add_argument("--format", default="text", choices=["text", "json"], dest="format")
|
|
2903
|
+
doctor.add_argument("--fix", action="store_true", default=False, help="Attempt to fix failing checks")
|
|
2904
|
+
doctor.add_argument("--dry-run", action="store_true", default=False, dest="dry_run", help="Plan fixes without applying (requires --fix)")
|
|
2905
|
+
doctor.add_argument("--repair-pack", default=None, dest="repair_pack", help="Filter checks by repair pack (runtime, governance, host, release, claude, codex, gemini, kimi)")
|
|
2906
|
+
doctor.set_defaults(func=cmd_doctor)
|
|
2907
|
+
|
|
2908
|
+
validate = sub.add_parser("validate", help="Canonical validation — doctor + contract + profile + install")
|
|
2909
|
+
validate.add_argument("--format", default="text", choices=["text", "json"], dest="format")
|
|
2910
|
+
validate.set_defaults(func=cmd_validate)
|
|
2911
|
+
|
|
2912
|
+
diagnose_plugins = sub.add_parser("diagnose-plugins", help="Diagnose plugin interoperability and conflicts")
|
|
2913
|
+
diagnose_plugins.add_argument("--format", default="text", choices=["text", "json"], dest="format")
|
|
2914
|
+
diagnose_plugins.add_argument("--live", action="store_true", help="Enable live probing mode")
|
|
2915
|
+
diagnose_plugins.set_defaults(func=cmd_diagnose_plugins)
|
|
2916
|
+
diagnose_plugins_sub = diagnose_plugins.add_subparsers(dest="diagnose_plugins_command")
|
|
2917
|
+
|
|
2918
|
+
diagnose_plugins_approve = diagnose_plugins_sub.add_parser("approve", help="Approve a plugin source for host use")
|
|
2919
|
+
diagnose_plugins_approve.add_argument("--source", required=True)
|
|
2920
|
+
diagnose_plugins_approve.add_argument("--host", required=True)
|
|
2921
|
+
diagnose_plugins_approve.add_argument("--reason", required=True)
|
|
2922
|
+
diagnose_plugins_approve.add_argument("--format", default="json", choices=["text", "json"], dest="format")
|
|
2923
|
+
diagnose_plugins_approve.set_defaults(func=cmd_diagnose_plugins_approve)
|
|
2924
|
+
|
|
2925
|
+
profile_review = sub.add_parser("profile-review", help="Review governed profile state")
|
|
2926
|
+
profile_review.add_argument("--format", default="json", choices=["json", "text"], dest="format")
|
|
2927
|
+
profile_review.set_defaults(func=cmd_profile_review)
|
|
2928
|
+
|
|
2929
|
+
docs = sub.add_parser("docs", help="OMG documentation generator")
|
|
2930
|
+
_add_docs_subcommands(docs, dest="docs_command")
|
|
2931
|
+
|
|
2932
|
+
install = sub.add_parser("install", help="Compute, dry-run, or apply an install plan")
|
|
2933
|
+
install.add_argument("--plan", action="store_true", help="Emit structured action plan without mutations")
|
|
2934
|
+
install.add_argument("--dry-run", action="store_true", help="Compute actions and emit receipts without mutations")
|
|
2935
|
+
install.add_argument("--apply", action="store_true", help="Execute the install plan with real disk writes")
|
|
2936
|
+
install.add_argument("--ci", action="store_true", help="CI mode flag")
|
|
2937
|
+
install.add_argument("--non-interactive", action="store_true", dest="non_interactive", help="Non-interactive mode")
|
|
2938
|
+
install.add_argument("--skip-preflight", action="store_true", dest="skip_preflight", help="Skip env preflight checks")
|
|
2939
|
+
install.add_argument("--format", default="text", choices=["text", "json"], dest="format")
|
|
2940
|
+
install.add_argument("--preset", default="balanced", choices=list(VALID_PRESETS))
|
|
2941
|
+
install.add_argument("--mode", default="omg-only", choices=["omg-only", "coexist"])
|
|
2942
|
+
install.set_defaults(func=cmd_install)
|
|
2943
|
+
|
|
2944
|
+
env_cmd = sub.add_parser("env", help="Environment preflight and doctor alias")
|
|
2945
|
+
env_sub = env_cmd.add_subparsers(dest="env_command", required=True)
|
|
2946
|
+
env_doctor_cmd = env_sub.add_parser("doctor", help="Run env preflight doctor checks (alias for doctor --repair-pack host)")
|
|
2947
|
+
env_doctor_cmd.add_argument("--format", default="text", choices=["text", "json"], dest="format")
|
|
2948
|
+
env_doctor_cmd.add_argument("--fix", action="store_true", default=False, help="Attempt to fix failing checks")
|
|
2949
|
+
env_doctor_cmd.add_argument("--dry-run", action="store_true", default=False, dest="dry_run")
|
|
2950
|
+
env_doctor_cmd.set_defaults(func=cmd_env_doctor)
|
|
2951
|
+
|
|
2952
|
+
return parser
|
|
2953
|
+
|
|
2954
|
+
|
|
2955
|
+
def main(argv: list[str] | None = None) -> int:
|
|
2956
|
+
parser = build_parser()
|
|
2957
|
+
args = parser.parse_args(argv)
|
|
2958
|
+
return int(args.func(args))
|
|
2959
|
+
|
|
2960
|
+
|
|
2961
|
+
if __name__ == "__main__":
|
|
2962
|
+
raise SystemExit(main())
|