apm-cli 0.34.0__tar.gz → 0.35.0__tar.gz
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.
- {apm_cli-0.34.0/src/apm_cli.egg-info → apm_cli-0.35.0}/PKG-INFO +4 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/README.md +3 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/pyproject.toml +2 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/base.py +46 -3
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/claude.py +74 -10
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/codex.py +132 -12
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/cursor.py +1 -14
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/opencode.py +41 -5
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/plugin_exporter.py +78 -15
- apm_cli-0.35.0/src/apm_cli/bundle/plugin_hooks.py +236 -0
- apm_cli-0.35.0/src/apm_cli/codex_plugins/__init__.py +1 -0
- apm_cli-0.35.0/src/apm_cli/codex_plugins/admission.py +138 -0
- apm_cli-0.35.0/src/apm_cli/codex_plugins/client.py +270 -0
- apm_cli-0.35.0/src/apm_cli/codex_plugins/manifest.py +84 -0
- apm_cli-0.35.0/src/apm_cli/codex_plugins/registrar.py +561 -0
- apm_cli-0.35.0/src/apm_cli/codex_plugins/snapshot.py +190 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/_helpers.py +2 -45
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/audit.py +71 -5
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/deps/cli.py +26 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/deps/why.py +8 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/install.py +35 -65
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/lock.py +5 -5
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/outdated.py +45 -8
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/uninstall/cli.py +42 -13
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/uninstall/engine.py +108 -90
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/uninstall/lockfile_state.py +10 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/update.py +3 -3
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/command_logger.py +20 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/conflict_detector.py +8 -5
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/deployment_ledger.py +468 -6
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/deployment_state.py +121 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/operations.py +4 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/safe_installer.py +8 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/target_detection.py +35 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/apm_resolver.py +152 -9
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/github_downloader.py +15 -74
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/github_downloader_validation.py +20 -7
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/immutable_requirements.py +17 -4
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/lockfile.py +238 -12
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/package_validator.py +3 -3
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/path_anchoring.py +16 -3
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/reachability.py +40 -5
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/revision_pins.py +3 -1
- apm_cli-0.35.0/src/apm_cli/deps/virtual_file.py +73 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/why_walker.py +39 -88
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/drift.py +94 -10
- apm_cli-0.35.0/src/apm_cli/entrypoint.py +34 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/hook_contract.py +9 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/audit_replay.py +16 -9
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/audit_target_roots.py +52 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/context.py +9 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/deployable_source_plan.py +103 -26
- apm_cli-0.35.0/src/apm_cli/install/deployment_plan.py +114 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/drift.py +229 -211
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/drift_render.py +21 -4
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/dry_run_plan.py +117 -5
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/errors.py +34 -1
- apm_cli-0.35.0/src/apm_cli/install/helpers/absence_observation.py +56 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/helpers/no_work.py +35 -9
- apm_cli-0.35.0/src/apm_cli/install/helpers/plan_inputs.py +28 -0
- apm_cli-0.35.0/src/apm_cli/install/helpers/reconciliation_scope.py +92 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/helpers/ref_reuse.py +2 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/helpers/ref_seed.py +9 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/lsp/integration.py +112 -63
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/manifest_reconcile.py +181 -143
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/command.py +54 -20
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/integration.py +180 -58
- apm_cli-0.35.0/src/apm_cli/install/mcp/ownership.py +278 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/native_plugin_admission.py +45 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/outcome.py +4 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/package_resolution.py +62 -13
- apm_cli-0.35.0/src/apm_cli/install/package_selection.py +108 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/cleanup.py +40 -7
- apm_cli-0.35.0/src/apm_cli/install/phases/codex_plugins.py +49 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/integrate.py +37 -14
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/local_content.py +134 -27
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/lockfile.py +98 -7
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/resolve.py +64 -84
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/pipeline.py +63 -76
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/plan.py +64 -13
- apm_cli-0.35.0/src/apm_cli/install/presentation/__init__.py +27 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/presentation/dry_run.py +14 -4
- apm_cli-0.35.0/src/apm_cli/install/presentation/resolution.py +48 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/resolution_staging.py +120 -38
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/service.py +328 -102
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/service_integration.py +79 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/services.py +72 -71
- apm_cli-0.35.0/src/apm_cli/install/source_helpers.py +122 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/sources.py +36 -8
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/template.py +100 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/transaction.py +63 -22
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/validation.py +14 -31
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/base_integrator.py +128 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/cleanup.py +15 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_bundle.py +3 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_command_paths.py +33 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_integrator.py +8 -9
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_native_formats.py +36 -6
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_source_selection.py +25 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/mcp_config_view.py +327 -52
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/mcp_integrator.py +59 -11
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/mcp_integrator_install.py +257 -49
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/prompt_integrator.py +107 -20
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/skill_integrator.py +377 -366
- apm_cli-0.35.0/src/apm_cli/integration/skill_preflight.py +198 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/skill_support.py +12 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/targets.py +65 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/resolver.py +43 -6
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/apm_package.py +46 -0
- apm_cli-0.35.0/src/apm_cli/models/dependency/__init__.py +51 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/materialization.py +2 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/reference.py +26 -6
- apm_cli-0.35.0/src/apm_cli/models/dependency/replacement.py +674 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/format_detection.py +2 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/manifest_contract.py +15 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/results.py +2 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/validation.py +78 -12
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/ci_checks.py +62 -26
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/discovery.py +29 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/registry/operations.py +3 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/file_scanner.py +5 -7
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/atomic_io.py +11 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/content_hash.py +37 -19
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/file_ops.py +13 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/helpers.py +6 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/yaml_io.py +10 -7
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/version.py +38 -6
- apm_cli-0.35.0/src/apm_cli/version_output.py +57 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0/src/apm_cli.egg-info}/PKG-INFO +4 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli.egg-info/SOURCES.txt +18 -0
- apm_cli-0.35.0/src/apm_cli.egg-info/entry_points.txt +2 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_apm_package_models.py +11 -7
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_distributed_compilation.py +2 -1
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_empty_string_and_defaults.py +68 -12
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_github_downloader.py +20 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_lockfile.py +34 -9
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_token_manager.py +1 -1
- apm_cli-0.34.0/src/apm_cli/install/mcp/ownership.py +0 -114
- apm_cli-0.34.0/src/apm_cli/install/package_selection.py +0 -45
- apm_cli-0.34.0/src/apm_cli/install/presentation/__init__.py +0 -1
- apm_cli-0.34.0/src/apm_cli/install/source_helpers.py +0 -56
- apm_cli-0.34.0/src/apm_cli/models/dependency/__init__.py +0 -23
- apm_cli-0.34.0/src/apm_cli.egg-info/entry_points.txt +0 -2
- {apm_cli-0.34.0 → apm_cli-0.35.0}/AUTHORS +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/LICENSE +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/NOTICE +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/setup.cfg +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/antigravity.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/copilot.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/hermes.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/intellij.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/kiro.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/vscode.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adopt/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adopt/discovery.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adopt/manifest_edit.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adopt/materialize.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adopt/render.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/adopt/safety.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/assets.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/constants.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/errors.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/io.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/ir.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/loader.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/projection.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/agent_plugins/validation.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bootstrap_mirror.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/agent_plugin_exporter.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/attest.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/claude_plugin_exporter.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/export_common.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/formats.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/local_bundle.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/packer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/plugin_layout.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/reproducible_archive.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/bundle/unpacker.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/cleanup.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/git_cache.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/cli.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/approve.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/auth.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/compile/cli.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/discover.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/doctor.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/experimental.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/find.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/lifecycle.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/audit.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/pack.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/plugin/init.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/publish.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/self_update.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/agents_compiler.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/footer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/inventory.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/managed_section.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/root_context_protection.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/compilation/user_root_context.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/config.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/copilot_plugins/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/copilot_plugins/capability.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/copilot_plugins/catalog.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/copilot_plugins/constants.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/copilot_plugins/registrar.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/copilot_plugins/settings.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/_child_tls/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/_child_tls/_apm_tls.pth +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/_child_tls/_apm_tls_bootstrap.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/apm_yml.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/auth.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/build_orchestrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/errors.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/experimental.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/host_providers.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/install_audit.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/lifecycle_scripts.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/output_mode.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/plugin_manifest.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/project_name.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/script_executors.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/script_trust.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/target_catalog.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/tls_trust.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/_shared.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/bare_cache.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/clone_engine.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/download_strategies.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/git_auth_env.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/git_file_transport.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/git_reference_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/git_remote_ops.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/git_semver_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/github_rate_limit.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/host_backends.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/outdated_ref_cache.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/outdated_row.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/plugin_parser.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/auth.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/client.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/extractor.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/outdated.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry/semver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/authoring.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/declared_license.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/formats.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/purl.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/sbom.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/spdx.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/export/spdx_data.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/argv.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/deployed_paths.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/exec_gate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/finalization.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/integrity.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/legacy_plugin_compat.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/local_bundle_paths.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/locking.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/lsp/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/manifest_helpers.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/_redownload.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/_skip_logic.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/audit.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/copilot_plugins.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/phases/targets.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/primitive_classification.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/project_package.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/registry_wiring.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/root_redirect.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/summary.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/target_filter.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/target_hints.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/install/target_warnings.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/_hook_dropped_targets.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/_shared.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/canvas_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_command_warnings.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_cursor_preflight.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_file_routing.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_ir.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_ownership.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/hook_reintegration.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/instruction_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/kiro_hook_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/lsp_integrator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/skill_package_routing.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/_shared.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/audit.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/auth_helpers.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/builder.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/client.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/drift_check.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/models.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/output_mappers.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/source_identity.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/version_check.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/version_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/yml_editor.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/marketplace/yml_schema.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/host_virtual.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/identity.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/lsp.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/mcp.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/object_fields.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/provider_coordinates.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/registry_entry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/selection.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/subsets.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/dependency/types.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/_gitlab.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/_shared.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/policy_checks.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/registry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/runtime/utils.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/executables.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/base.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/gate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/generic_sarif.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/options.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/registry.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/runner.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/sarif_ingest.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/external/skillspector.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/security/primitive_discovery.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/archive.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/git_sparse.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/github_host.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/net.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/patterns.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/perf_stats.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/staging_guard.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_apm_resolver.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_console.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_gitlab_git_transport.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.34.0 → apm_cli-0.35.0}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apm-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -95,6 +95,8 @@ AI coding agents need context to be useful — standards, prompts, skills, plugi
|
|
|
95
95
|
# apm.yml — ships with your project
|
|
96
96
|
name: your-project
|
|
97
97
|
version: 1.0.0
|
|
98
|
+
targets:
|
|
99
|
+
- copilot # choose the harness(es) this project supports
|
|
98
100
|
dependencies:
|
|
99
101
|
apm:
|
|
100
102
|
# Skills from any repository
|
|
@@ -106,7 +108,7 @@ dependencies:
|
|
|
106
108
|
# A full APM package with instructions, skills, prompts, hooks...
|
|
107
109
|
- microsoft/apm-sample-package#v1.0.0
|
|
108
110
|
mcp:
|
|
109
|
-
# MCP servers -- installed into every
|
|
111
|
+
# MCP servers -- installed into every selected target
|
|
110
112
|
- name: io.github.github/github-mcp-server
|
|
111
113
|
transport: http # MCP transport name, not URL scheme -- connects over HTTPS
|
|
112
114
|
```
|
|
@@ -23,6 +23,8 @@ AI coding agents need context to be useful — standards, prompts, skills, plugi
|
|
|
23
23
|
# apm.yml — ships with your project
|
|
24
24
|
name: your-project
|
|
25
25
|
version: 1.0.0
|
|
26
|
+
targets:
|
|
27
|
+
- copilot # choose the harness(es) this project supports
|
|
26
28
|
dependencies:
|
|
27
29
|
apm:
|
|
28
30
|
# Skills from any repository
|
|
@@ -34,7 +36,7 @@ dependencies:
|
|
|
34
36
|
# A full APM package with instructions, skills, prompts, hooks...
|
|
35
37
|
- microsoft/apm-sample-package#v1.0.0
|
|
36
38
|
mcp:
|
|
37
|
-
# MCP servers -- installed into every
|
|
39
|
+
# MCP servers -- installed into every selected target
|
|
38
40
|
- name: io.github.github/github-mcp-server
|
|
39
41
|
transport: http # MCP transport name, not URL scheme -- connects over HTTPS
|
|
40
42
|
```
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apm-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.35.0"
|
|
8
8
|
description = "MCP configuration tool"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -60,7 +60,7 @@ build = [
|
|
|
60
60
|
]
|
|
61
61
|
|
|
62
62
|
[project.scripts]
|
|
63
|
-
apm = "apm_cli.
|
|
63
|
+
apm = "apm_cli.entrypoint:main"
|
|
64
64
|
|
|
65
65
|
[tool.setuptools.packages.find]
|
|
66
66
|
where = ["src"]
|
|
@@ -337,6 +337,7 @@ class MCPClientAdapter(ABC):
|
|
|
337
337
|
"""
|
|
338
338
|
self._project_root = Path(project_root) if project_root is not None else None
|
|
339
339
|
self.user_scope = user_scope
|
|
340
|
+
self.written_mcp_hashes: dict[str, str] = {}
|
|
340
341
|
# Per-server tracking populated by the env-resolution helpers and
|
|
341
342
|
# consumed by ``configure_mcp_server`` for the post-install summary
|
|
342
343
|
# and the aggregated legacy-syntax deprecation warning. Defined on
|
|
@@ -367,12 +368,18 @@ class MCPClientAdapter(ABC):
|
|
|
367
368
|
return self._project_root
|
|
368
369
|
return Path(os.getcwd())
|
|
369
370
|
|
|
370
|
-
def render_server_config(
|
|
371
|
+
def render_server_config(
|
|
372
|
+
self,
|
|
373
|
+
server_info: dict,
|
|
374
|
+
*,
|
|
375
|
+
env_overrides: dict | None = None,
|
|
376
|
+
runtime_vars: dict | None = None,
|
|
377
|
+
) -> dict:
|
|
371
378
|
"""Render one native server entry for exact baseline comparisons."""
|
|
372
379
|
rendered = self._format_server_config(
|
|
373
380
|
server_info,
|
|
374
|
-
env_overrides={},
|
|
375
|
-
runtime_vars={},
|
|
381
|
+
env_overrides=env_overrides or {},
|
|
382
|
+
runtime_vars=runtime_vars or {},
|
|
376
383
|
)
|
|
377
384
|
if isinstance(rendered, tuple):
|
|
378
385
|
rendered = rendered[0]
|
|
@@ -380,6 +387,20 @@ class MCPClientAdapter(ABC):
|
|
|
380
387
|
raise ValueError("Adapter did not produce a native MCP server mapping")
|
|
381
388
|
return rendered
|
|
382
389
|
|
|
390
|
+
def matches_managed_server_config(self, native: object, expected: dict) -> bool:
|
|
391
|
+
"""Compare the native connection with the adapter's rendered baseline."""
|
|
392
|
+
return native == expected
|
|
393
|
+
|
|
394
|
+
def managed_server_config(self, native: dict) -> dict:
|
|
395
|
+
"""Return connection data for an actual-write fingerprint."""
|
|
396
|
+
return dict(native)
|
|
397
|
+
|
|
398
|
+
def project_managed_server_config(
|
|
399
|
+
self, native: object, *, baseline: dict, requested: dict
|
|
400
|
+
) -> object:
|
|
401
|
+
"""Return a comparison-only view for an already recorded managed connection."""
|
|
402
|
+
return native
|
|
403
|
+
|
|
383
404
|
@abstractmethod
|
|
384
405
|
def get_config_path(self):
|
|
385
406
|
"""Get the path to the MCP configuration file."""
|
|
@@ -1118,6 +1139,28 @@ class MCPClientAdapter(ABC):
|
|
|
1118
1139
|
return True
|
|
1119
1140
|
return not (sys.stdin.isatty() and sys.stdout.isatty())
|
|
1120
1141
|
|
|
1142
|
+
def _resolve_explicit_environment_values(
|
|
1143
|
+
self,
|
|
1144
|
+
env_vars: dict[str, object],
|
|
1145
|
+
env_overrides: dict[str, str] | None = None,
|
|
1146
|
+
) -> dict[str, str]:
|
|
1147
|
+
"""Translate authored references while preserving explicit env literals.
|
|
1148
|
+
|
|
1149
|
+
Resets placeholder tracking; mid-render callers must preserve prior keys.
|
|
1150
|
+
"""
|
|
1151
|
+
resolved: dict[str, str] = {}
|
|
1152
|
+
self._last_env_placeholder_keys = set()
|
|
1153
|
+
for name, value in env_vars.items():
|
|
1154
|
+
if not name or value is None:
|
|
1155
|
+
continue
|
|
1156
|
+
if isinstance(value, TrustedEnvLiteral):
|
|
1157
|
+
resolved[name] = value
|
|
1158
|
+
elif isinstance(value, str):
|
|
1159
|
+
resolved[name] = self._resolve_env_variable(name, value, env_overrides)
|
|
1160
|
+
else:
|
|
1161
|
+
resolved[name] = _stringify_env_literal(value)
|
|
1162
|
+
return resolved
|
|
1163
|
+
|
|
1121
1164
|
def _resolve_environment_variables(self, env_vars, env_overrides=None):
|
|
1122
1165
|
"""Resolve (or translate) declared environment variables.
|
|
1123
1166
|
|
|
@@ -26,7 +26,7 @@ from pathlib import Path
|
|
|
26
26
|
|
|
27
27
|
from ...utils.atomic_io import atomic_write_text
|
|
28
28
|
from ...utils.console import _rich_error, _rich_success, _rich_warning
|
|
29
|
-
from ...utils.path_security import PathTraversalError
|
|
29
|
+
from ...utils.path_security import PathTraversalError, validate_path_segments
|
|
30
30
|
from .copilot import CopilotClientAdapter
|
|
31
31
|
|
|
32
32
|
_log = logging.getLogger(__name__)
|
|
@@ -83,6 +83,51 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
83
83
|
config["command"] = self._rewrite_self_defined_skill_command(config["command"])
|
|
84
84
|
return config
|
|
85
85
|
|
|
86
|
+
def render_server_config(
|
|
87
|
+
self,
|
|
88
|
+
server_info: dict,
|
|
89
|
+
*,
|
|
90
|
+
env_overrides: dict | None = None,
|
|
91
|
+
runtime_vars: dict | None = None,
|
|
92
|
+
) -> dict:
|
|
93
|
+
"""Compare against the normalized shape actually written to Claude."""
|
|
94
|
+
return self._normalize_mcp_entry_for_claude_code(
|
|
95
|
+
super().render_server_config(
|
|
96
|
+
server_info, env_overrides=env_overrides, runtime_vars=runtime_vars
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def matches_managed_server_config(self, native: object, expected: dict) -> bool:
|
|
101
|
+
"""OAuth metadata is runtime-owned, not part of an APM connection declaration."""
|
|
102
|
+
if not isinstance(native, dict):
|
|
103
|
+
return False
|
|
104
|
+
return self.managed_server_config(native) == expected
|
|
105
|
+
|
|
106
|
+
def managed_server_config(self, native: dict) -> dict:
|
|
107
|
+
"""Exclude only the runtime-owned OAuth account from write fingerprints."""
|
|
108
|
+
return {key: value for key, value in native.items() if key != "oauthAccount"}
|
|
109
|
+
|
|
110
|
+
def project_managed_server_config(
|
|
111
|
+
self, native: object, *, baseline: dict, requested: dict
|
|
112
|
+
) -> object:
|
|
113
|
+
"""Exclude a valid native-only cwd from managed reconciliation, never cleanup."""
|
|
114
|
+
if (
|
|
115
|
+
not isinstance(native, dict)
|
|
116
|
+
or native.get("type") != "stdio"
|
|
117
|
+
or baseline.get("type") != "stdio"
|
|
118
|
+
or "cwd" in baseline
|
|
119
|
+
or "cwd" in requested
|
|
120
|
+
):
|
|
121
|
+
return native
|
|
122
|
+
cwd = native.get("cwd")
|
|
123
|
+
if not isinstance(cwd, str) or not cwd.strip() or "\x00" in cwd:
|
|
124
|
+
return native
|
|
125
|
+
try:
|
|
126
|
+
validate_path_segments(cwd, context="MCP cwd", allow_current_dir=True)
|
|
127
|
+
except PathTraversalError:
|
|
128
|
+
return native
|
|
129
|
+
return {key: value for key, value in native.items() if key != "cwd"}
|
|
130
|
+
|
|
86
131
|
@classmethod
|
|
87
132
|
def _is_remote_mcp_entry(cls, entry: dict) -> bool:
|
|
88
133
|
"""Return whether *entry* describes a remote (URL-addressed) server."""
|
|
@@ -148,7 +193,9 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
148
193
|
}
|
|
149
194
|
|
|
150
195
|
@classmethod
|
|
151
|
-
def _merge_mcp_server_dicts(
|
|
196
|
+
def _merge_mcp_server_dicts(
|
|
197
|
+
cls, existing_servers: dict, config_updates: dict, managed_fields: set[str] | None = None
|
|
198
|
+
) -> None:
|
|
152
199
|
"""Merge *config_updates* into *existing_servers* in place.
|
|
153
200
|
|
|
154
201
|
Per-server entries are shallow-merged: ``{**old, **new}`` so keys present
|
|
@@ -164,15 +211,23 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
164
211
|
continue
|
|
165
212
|
prev = existing_servers.get(name)
|
|
166
213
|
if isinstance(prev, dict):
|
|
214
|
+
if managed_fields:
|
|
215
|
+
prev = {
|
|
216
|
+
key: value
|
|
217
|
+
for key, value in prev.items()
|
|
218
|
+
if key not in managed_fields or key in new_cfg
|
|
219
|
+
}
|
|
167
220
|
merged = {**cls._retained_previous_entry(prev, new_cfg), **new_cfg}
|
|
168
221
|
existing_servers[name] = merged
|
|
169
222
|
else:
|
|
170
223
|
existing_servers[name] = dict(new_cfg)
|
|
171
224
|
|
|
172
|
-
def _merge_and_normalize_updates(
|
|
225
|
+
def _merge_and_normalize_updates(
|
|
226
|
+
self, data: dict, config_updates: dict, managed_fields: set[str] | None = None
|
|
227
|
+
) -> None:
|
|
173
228
|
if "mcpServers" not in data:
|
|
174
229
|
data["mcpServers"] = {}
|
|
175
|
-
self._merge_mcp_server_dicts(data["mcpServers"], config_updates)
|
|
230
|
+
self._merge_mcp_server_dicts(data["mcpServers"], config_updates, managed_fields)
|
|
176
231
|
for name in config_updates:
|
|
177
232
|
ent = data["mcpServers"].get(name)
|
|
178
233
|
if isinstance(ent, dict):
|
|
@@ -234,9 +289,11 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
234
289
|
except (json.JSONDecodeError, OSError):
|
|
235
290
|
return {"mcpServers": {}}
|
|
236
291
|
|
|
237
|
-
def update_config(
|
|
292
|
+
def update_config(
|
|
293
|
+
self, config_updates, enabled=True, *, managed_fields: set[str] | None = None
|
|
294
|
+
):
|
|
238
295
|
if self._is_user_scope():
|
|
239
|
-
return self._merge_user_mcp(config_updates)
|
|
296
|
+
return self._merge_user_mcp(config_updates, managed_fields=managed_fields)
|
|
240
297
|
if not self._should_write_project():
|
|
241
298
|
_rich_warning(
|
|
242
299
|
f"Skipped Claude Code project MCP -- .claude/ not found in {self.project_root}"
|
|
@@ -254,13 +311,13 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
254
311
|
data = {}
|
|
255
312
|
else:
|
|
256
313
|
data = {}
|
|
257
|
-
self._merge_and_normalize_updates(data, config_updates)
|
|
314
|
+
self._merge_and_normalize_updates(data, config_updates, managed_fields)
|
|
258
315
|
path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
259
316
|
return True
|
|
260
317
|
except OSError:
|
|
261
318
|
return False
|
|
262
319
|
|
|
263
|
-
def _merge_user_mcp(self, config_updates) -> bool:
|
|
320
|
+
def _merge_user_mcp(self, config_updates, *, managed_fields: set[str] | None = None) -> bool:
|
|
264
321
|
path = self._user_claude_json_path()
|
|
265
322
|
try:
|
|
266
323
|
if path.is_file():
|
|
@@ -273,7 +330,7 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
273
330
|
data = {}
|
|
274
331
|
else:
|
|
275
332
|
data = {}
|
|
276
|
-
self._merge_and_normalize_updates(data, config_updates)
|
|
333
|
+
self._merge_and_normalize_updates(data, config_updates, managed_fields)
|
|
277
334
|
payload = json.dumps(data, indent=2) + "\n"
|
|
278
335
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
279
336
|
atomic_write_text(path, payload, new_file_mode=0o600)
|
|
@@ -290,6 +347,7 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
290
347
|
env_overrides=None,
|
|
291
348
|
server_info_cache=None,
|
|
292
349
|
runtime_vars=None,
|
|
350
|
+
managed_fields: set[str] | None = None,
|
|
293
351
|
):
|
|
294
352
|
if not server_url:
|
|
295
353
|
_rich_error("server_url cannot be empty")
|
|
@@ -314,10 +372,16 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
314
372
|
config_key = self._determine_config_key(server_url, server_name)
|
|
315
373
|
|
|
316
374
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
317
|
-
|
|
375
|
+
from apm_cli.install.mcp.ownership import mcp_native_hash
|
|
376
|
+
|
|
377
|
+
fingerprint = mcp_native_hash(
|
|
378
|
+
self.managed_server_config(self._normalize_mcp_entry_for_claude_code(server_config))
|
|
379
|
+
)
|
|
380
|
+
ok = self.update_config({config_key: server_config}, managed_fields=managed_fields)
|
|
318
381
|
if not ok:
|
|
319
382
|
_rich_error(f"Failed to write MCP config for '{config_key}' to Claude Code")
|
|
320
383
|
return False
|
|
384
|
+
self.written_mcp_hashes[server_url] = fingerprint
|
|
321
385
|
|
|
322
386
|
_rich_success(f"Successfully configured MCP server '{config_key}' for Claude Code")
|
|
323
387
|
return True
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""OpenAI Codex CLI implementation of MCP client adapter."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
@@ -8,6 +9,7 @@ from urllib.parse import urlparse
|
|
|
8
9
|
|
|
9
10
|
import tomlkit
|
|
10
11
|
from tomlkit.exceptions import TOMLKitError
|
|
12
|
+
from tomlkit.items import InlineTable, Item, String, Table
|
|
11
13
|
|
|
12
14
|
from ...registry.client import SimpleRegistryClient
|
|
13
15
|
from ...registry.integration import RegistryIntegration
|
|
@@ -31,6 +33,8 @@ _log = logging.getLogger(__name__)
|
|
|
31
33
|
# ``_resolve_variable_placeholders`` untouched.
|
|
32
34
|
# See https://learn.chatgpt.com/docs/extend/mcp?surface=cli
|
|
33
35
|
_CODEX_BEARER_HEADER = "authorization"
|
|
36
|
+
_CODEX_REGISTRY_ID_COMMENT = "# apm-registry-id: "
|
|
37
|
+
_JSON_DECODER = json.JSONDecoder()
|
|
34
38
|
# Reuse the canonical placeholder syntax so the two spellings APM accepts
|
|
35
39
|
# (``${VAR}`` and ``${env:VAR}``) cannot drift apart here. The auth scheme is
|
|
36
40
|
# case-insensitive per RFC 7235; Codex always writes it as ``Bearer``.
|
|
@@ -102,13 +106,125 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
102
106
|
"""
|
|
103
107
|
return str(self._get_codex_dir() / "config.toml")
|
|
104
108
|
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _registry_id_item(server_config: object) -> Item | None:
|
|
111
|
+
"""Locate a comment that survives standard, dotted and inline TOML layouts.
|
|
112
|
+
|
|
113
|
+
Inline values cannot contain comments, so annotate the whole inline
|
|
114
|
+
entry. Other layouts expose command/url scalar trivia even when their
|
|
115
|
+
parent tables are implicit or split across out-of-order fragments.
|
|
116
|
+
"""
|
|
117
|
+
if isinstance(server_config, InlineTable):
|
|
118
|
+
return server_config
|
|
119
|
+
if isinstance(server_config, dict):
|
|
120
|
+
for key in ("command", "url"):
|
|
121
|
+
value = server_config.get(key)
|
|
122
|
+
if isinstance(value, String):
|
|
123
|
+
return value
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def get_registry_id(cls, server_config: dict) -> str | None:
|
|
128
|
+
"""Read APM identity metadata without exposing it as a Codex setting.
|
|
129
|
+
|
|
130
|
+
Keep reading legacy native IDs for conflict detection; their presence
|
|
131
|
+
alone is not ownership evidence and never authorizes a migration.
|
|
132
|
+
"""
|
|
133
|
+
legacy_id = server_config.get("id")
|
|
134
|
+
if isinstance(legacy_id, str) and legacy_id:
|
|
135
|
+
return legacy_id
|
|
136
|
+
item = cls._registry_id_item(server_config)
|
|
137
|
+
if item is None:
|
|
138
|
+
return None
|
|
139
|
+
comment = item.trivia.comment
|
|
140
|
+
if not comment.startswith(_CODEX_REGISTRY_ID_COMMENT):
|
|
141
|
+
return None
|
|
142
|
+
try:
|
|
143
|
+
registry_id, _ = _JSON_DECODER.raw_decode(comment[len(_CODEX_REGISTRY_ID_COMMENT) :])
|
|
144
|
+
except ValueError:
|
|
145
|
+
return None
|
|
146
|
+
return registry_id if isinstance(registry_id, str) and registry_id else None
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def _annotate_registry_id(cls, server_config: dict, registry_id: str) -> None:
|
|
150
|
+
"""Keep UUID matching metadata in an escaped, round-trip TOML comment."""
|
|
151
|
+
item = cls._registry_id_item(server_config)
|
|
152
|
+
if item is None:
|
|
153
|
+
raise ValueError("Codex MCP server must have a string command or url")
|
|
154
|
+
original_comment = item.trivia.comment
|
|
155
|
+
suffix = f" {original_comment}" if original_comment else ""
|
|
156
|
+
item.comment(_CODEX_REGISTRY_ID_COMMENT[2:] + json.dumps(registry_id) + suffix)
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _expand_inline_table(entry: InlineTable) -> Table:
|
|
160
|
+
"""Expand an inline container without discarding its values or comments.
|
|
161
|
+
|
|
162
|
+
Nested inline comments are invalid TOML, and deleting a middle key can
|
|
163
|
+
leave stray separators in tomlkit. Expand only containers being written
|
|
164
|
+
or owned entries being repaired; keep unrelated child items intact.
|
|
165
|
+
"""
|
|
166
|
+
table = tomlkit.table(is_super_table=False)
|
|
167
|
+
for key, value in entry.items():
|
|
168
|
+
table.add(key, value)
|
|
169
|
+
if entry.trivia.comment:
|
|
170
|
+
table.comment(entry.trivia.comment)
|
|
171
|
+
return table
|
|
172
|
+
|
|
173
|
+
def _write_config(self, config: dict) -> None:
|
|
174
|
+
"""Validate serialized TOML before atomically replacing the native file."""
|
|
175
|
+
serialized = tomlkit.dumps(config)
|
|
176
|
+
tomlkit.parse(serialized)
|
|
177
|
+
config_path = Path(self.get_config_path())
|
|
178
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
atomic_write_text(config_path, serialized, new_file_mode=0o600)
|
|
180
|
+
_log.debug("Codex config written to %s", config_path)
|
|
181
|
+
|
|
182
|
+
def migrate_legacy_managed_servers(self, managed_names: set[str]) -> set[str]:
|
|
183
|
+
"""Remove unsupported IDs only from entries with recorded APM ownership."""
|
|
184
|
+
if not managed_names:
|
|
185
|
+
return set()
|
|
186
|
+
config = self.get_current_config()
|
|
187
|
+
if config is None:
|
|
188
|
+
return set()
|
|
189
|
+
servers = config.get("mcp_servers", {})
|
|
190
|
+
migrated: set[str] = set()
|
|
191
|
+
for name in sorted(managed_names):
|
|
192
|
+
config_key = self._determine_config_key(name, None)
|
|
193
|
+
entry = servers.get(config_key)
|
|
194
|
+
if not isinstance(entry, dict) or "id" not in entry:
|
|
195
|
+
continue
|
|
196
|
+
registry_id = entry["id"]
|
|
197
|
+
if not isinstance(registry_id, str):
|
|
198
|
+
continue
|
|
199
|
+
if isinstance(servers, InlineTable):
|
|
200
|
+
servers = self._expand_inline_table(servers)
|
|
201
|
+
config["mcp_servers"] = servers
|
|
202
|
+
if isinstance(entry, InlineTable):
|
|
203
|
+
entry = self._expand_inline_table(entry)
|
|
204
|
+
servers[config_key] = entry
|
|
205
|
+
if registry_id:
|
|
206
|
+
self._annotate_registry_id(entry, registry_id)
|
|
207
|
+
del entry["id"]
|
|
208
|
+
migrated.add(name)
|
|
209
|
+
if migrated:
|
|
210
|
+
self._write_config(config)
|
|
211
|
+
return migrated
|
|
212
|
+
|
|
213
|
+
def matches_managed_server_config(self, native: object, expected: dict) -> bool:
|
|
214
|
+
"""Accept the historical empty-ID spelling without accepting foreign identity."""
|
|
215
|
+
if not isinstance(native, dict) or self.get_registry_id(native):
|
|
216
|
+
return False
|
|
217
|
+
comparable = dict(native)
|
|
218
|
+
if comparable.get("id") == "":
|
|
219
|
+
comparable.pop("id")
|
|
220
|
+
return comparable == expected
|
|
221
|
+
|
|
105
222
|
def update_config(self, config_updates):
|
|
106
223
|
"""Update the Codex CLI MCP configuration.
|
|
107
224
|
|
|
108
225
|
Args:
|
|
109
226
|
config_updates (dict): Configuration updates to apply.
|
|
110
227
|
"""
|
|
111
|
-
config_path = Path(self.get_config_path())
|
|
112
228
|
current_config = self.get_current_config()
|
|
113
229
|
if current_config is None:
|
|
114
230
|
return False
|
|
@@ -116,15 +232,13 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
116
232
|
# Ensure mcp_servers section exists
|
|
117
233
|
if "mcp_servers" not in current_config:
|
|
118
234
|
current_config["mcp_servers"] = {}
|
|
235
|
+
elif isinstance(current_config["mcp_servers"], InlineTable):
|
|
236
|
+
current_config["mcp_servers"] = self._expand_inline_table(current_config["mcp_servers"])
|
|
119
237
|
|
|
120
238
|
# Apply updates to mcp_servers section
|
|
121
239
|
current_config["mcp_servers"].update(config_updates)
|
|
122
240
|
|
|
123
|
-
|
|
124
|
-
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
-
|
|
126
|
-
atomic_write_text(config_path, tomlkit.dumps(current_config), new_file_mode=0o600)
|
|
127
|
-
_log.debug("Codex config written to %s", config_path)
|
|
241
|
+
self._write_config(current_config)
|
|
128
242
|
return True
|
|
129
243
|
|
|
130
244
|
def get_current_config(self):
|
|
@@ -199,9 +313,19 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
199
313
|
if server_config is None:
|
|
200
314
|
return False
|
|
201
315
|
|
|
316
|
+
# Registry identity is APM metadata, not a supported native field.
|
|
317
|
+
registry_id = server_info.get("id")
|
|
318
|
+
if isinstance(registry_id, str) and registry_id:
|
|
319
|
+
server_config = tomlkit.item(server_config)
|
|
320
|
+
self._annotate_registry_id(server_config, registry_id)
|
|
321
|
+
|
|
202
322
|
# Update configuration using the chosen key
|
|
323
|
+
from apm_cli.install.mcp.ownership import mcp_native_hash
|
|
324
|
+
|
|
325
|
+
fingerprint = mcp_native_hash(self.managed_server_config(server_config))
|
|
203
326
|
if not self.update_config({config_key: server_config}):
|
|
204
327
|
return False
|
|
328
|
+
self.written_mcp_hashes[server_url] = fingerprint
|
|
205
329
|
|
|
206
330
|
_rich_success(
|
|
207
331
|
f"Configured MCP server '{config_key}' for Codex CLI",
|
|
@@ -227,12 +351,11 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
227
351
|
if runtime_vars is None:
|
|
228
352
|
runtime_vars = {}
|
|
229
353
|
|
|
230
|
-
#
|
|
354
|
+
# Only fields accepted by Codex belong in the native server mapping.
|
|
231
355
|
config = {
|
|
232
356
|
"command": "unknown",
|
|
233
357
|
"args": [],
|
|
234
358
|
"env": {},
|
|
235
|
-
"id": server_info.get("id", ""), # Add registry UUID for conflict detection
|
|
236
359
|
}
|
|
237
360
|
|
|
238
361
|
# Self-defined stdio deps carry raw command/args. Route ``env`` and
|
|
@@ -313,10 +436,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
313
436
|
)
|
|
314
437
|
return None
|
|
315
438
|
|
|
316
|
-
remote_config = {
|
|
317
|
-
"url": remote_url,
|
|
318
|
-
"id": server_info.get("id", ""),
|
|
319
|
-
}
|
|
439
|
+
remote_config = {"url": remote_url}
|
|
320
440
|
http_headers: dict[str, str] = {}
|
|
321
441
|
env_http_headers: dict[str, str] = {}
|
|
322
442
|
bearer_token_env_var = ""
|
|
@@ -13,8 +13,6 @@ import os
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
from ...core.token_manager import GitHubTokenManager
|
|
16
|
-
from ...models.dependency.mcp import TrustedEnvLiteral
|
|
17
|
-
from .base import _stringify_env_literal
|
|
18
16
|
from .copilot import CopilotClientAdapter
|
|
19
17
|
|
|
20
18
|
|
|
@@ -55,18 +53,7 @@ class CursorClientAdapter(CopilotClientAdapter):
|
|
|
55
53
|
if not isinstance(env_vars, dict):
|
|
56
54
|
return super()._resolve_environment_variables(env_vars, env_overrides)
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
self._last_env_placeholder_keys = set()
|
|
60
|
-
for name, value in env_vars.items():
|
|
61
|
-
if not name or value is None:
|
|
62
|
-
continue
|
|
63
|
-
if isinstance(value, TrustedEnvLiteral):
|
|
64
|
-
resolved[name] = value
|
|
65
|
-
elif isinstance(value, str):
|
|
66
|
-
resolved[name] = self._resolve_env_variable(name, value, env_overrides)
|
|
67
|
-
else:
|
|
68
|
-
resolved[name] = _stringify_env_literal(value)
|
|
69
|
-
return resolved
|
|
56
|
+
return self._resolve_explicit_environment_values(env_vars, env_overrides)
|
|
70
57
|
|
|
71
58
|
# ------------------------------------------------------------------ #
|
|
72
59
|
# Auth-header injection override (for testability)
|
|
@@ -29,12 +29,16 @@ At project scope APM only writes to ``opencode.json`` when the
|
|
|
29
29
|
|
|
30
30
|
import json
|
|
31
31
|
import os
|
|
32
|
+
import re
|
|
32
33
|
from pathlib import Path
|
|
33
34
|
from typing import Any
|
|
34
35
|
|
|
35
36
|
from ...models.dependency.mcp import _EXTRA_DENYLIST, opencode_enabled_value
|
|
36
37
|
from .copilot import CopilotClientAdapter
|
|
37
38
|
|
|
39
|
+
_ENV_REF_TEMPLATE = "{env:%s}"
|
|
40
|
+
_ENV_REF_RE = re.compile(re.escape(_ENV_REF_TEMPLATE) % r"([A-Za-z_][A-Za-z0-9_]*)")
|
|
41
|
+
|
|
38
42
|
|
|
39
43
|
class OpenCodeClientAdapter(CopilotClientAdapter):
|
|
40
44
|
"""OpenCode MCP client adapter.
|
|
@@ -47,11 +51,43 @@ class OpenCodeClientAdapter(CopilotClientAdapter):
|
|
|
47
51
|
target_name: str = "opencode"
|
|
48
52
|
mcp_servers_key: str = "mcp"
|
|
49
53
|
|
|
50
|
-
# OpenCode
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
# OpenCode resolves {env:NAME} throughout config strings, including MCP
|
|
55
|
+
# environment, headers and command args (https://opencode.ai/docs/config/#env-vars).
|
|
56
|
+
# Keep referenced secrets out of generated configuration; see #3051.
|
|
57
|
+
_supports_runtime_env_substitution: bool = True
|
|
58
|
+
|
|
59
|
+
def _format_runtime_env_placeholder(self, name: str) -> str:
|
|
60
|
+
"""Return OpenCode's native environment reference."""
|
|
61
|
+
return _ENV_REF_TEMPLATE % name
|
|
62
|
+
|
|
63
|
+
def _resolve_environment_variables(
|
|
64
|
+
self,
|
|
65
|
+
env_vars: dict[str, object] | list[dict[str, object]],
|
|
66
|
+
env_overrides: dict[str, str] | None = None,
|
|
67
|
+
) -> dict[str, str]:
|
|
68
|
+
"""Preserve explicit literals; translate references for runtime lookup."""
|
|
69
|
+
if not isinstance(env_vars, dict):
|
|
70
|
+
return super()._resolve_environment_variables(env_vars, env_overrides)
|
|
71
|
+
return self._resolve_explicit_environment_values(env_vars, env_overrides)
|
|
72
|
+
|
|
73
|
+
def _format_server_config(self, server_info, env_overrides=None, runtime_vars=None):
|
|
74
|
+
"""Preserve per-server authored registry env values after normal rendering."""
|
|
75
|
+
config = super()._format_server_config(server_info, env_overrides, runtime_vars)
|
|
76
|
+
overlay = server_info.get("_apm_env_overlay")
|
|
77
|
+
env = config.get("env")
|
|
78
|
+
if server_info.get("_raw_stdio") or not overlay or not isinstance(env, dict):
|
|
79
|
+
return config
|
|
80
|
+
|
|
81
|
+
# Only declared/emitted keys participate, as on the legacy registry path.
|
|
82
|
+
# Never recover authored values from the shared collected overrides.
|
|
83
|
+
authored = {name: value for name, value in overlay.items() if name in env}
|
|
84
|
+
previous_keys = self._last_env_placeholder_keys - set(env)
|
|
85
|
+
# Deliberately omit collected overrides: only authored values belong here.
|
|
86
|
+
env.update(self._resolve_explicit_environment_values(authored))
|
|
87
|
+
self._last_env_placeholder_keys = previous_keys | {
|
|
88
|
+
name for value in env.values() for name in _ENV_REF_RE.findall(value)
|
|
89
|
+
}
|
|
90
|
+
return config
|
|
55
91
|
|
|
56
92
|
@staticmethod
|
|
57
93
|
def config_path_for(project_root: Path, user_scope: bool) -> Path:
|