apm-cli 0.18.0__tar.gz → 0.20.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.18.0 → apm_cli-0.20.0}/AUTHORS +1 -0
- {apm_cli-0.18.0/src/apm_cli.egg-info → apm_cli-0.20.0}/PKG-INFO +4 -4
- {apm_cli-0.18.0 → apm_cli-0.20.0}/README.md +3 -3
- {apm_cli-0.18.0 → apm_cli-0.20.0}/pyproject.toml +1 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/base.py +26 -16
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/claude.py +1 -6
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/codex.py +1 -13
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/copilot.py +11 -15
- apm_cli-0.20.0/src/apm_cli/adapters/client/hermes.py +210 -0
- apm_cli-0.20.0/src/apm_cli/adapters/client/kiro.py +226 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/vscode.py +115 -30
- apm_cli-0.20.0/src/apm_cli/bootstrap_mirror.py +101 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/bundle/local_bundle.py +57 -62
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/bundle/lockfile_enrichment.py +6 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/bundle/packer.py +25 -7
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/bundle/plugin_exporter.py +21 -13
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/bundle/unpacker.py +46 -29
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cli.py +57 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/audit.py +82 -23
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/cli.py +25 -6
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/cli.py +7 -2
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/experimental.py +2 -2
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/install.py +16 -16
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/__init__.py +151 -22
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/outdated.py +57 -6
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/pack.py +79 -5
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/publish.py +34 -34
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/self_update.py +87 -26
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/update.py +203 -28
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/agents_compiler.py +215 -13
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/claude_formatter.py +14 -7
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/distributed_compiler.py +241 -26
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/apm_yml.py +1 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/auth.py +213 -28
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/build_orchestrator.py +2 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/experimental.py +20 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/safe_installer.py +2 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/target_detection.py +64 -5
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/apm_resolver.py +155 -43
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/download_strategies.py +263 -57
- apm_cli-0.20.0/src/apm_cli/deps/git_file_transport.py +246 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_reference_resolver.py +18 -3
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_remote_ops.py +24 -3
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/github_downloader.py +22 -4
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/github_downloader_validation.py +5 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/host_backends.py +18 -6
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/lockfile.py +64 -5
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/path_anchoring.py +4 -4
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/plugin_parser.py +123 -15
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/client.py +9 -9
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/extractor.py +7 -33
- apm_cli-0.20.0/src/apm_cli/deps/revision_pins.py +262 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/factory.py +4 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/drift.py +19 -2
- apm_cli-0.20.0/src/apm_cli/install/manifest_reconcile.py +112 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/local_content.py +116 -20
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/lockfile.py +66 -10
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/post_deps_local.py +17 -3
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/targets.py +297 -266
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/services.py +88 -4
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/validation.py +8 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/base_integrator.py +7 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/hook_integrator.py +170 -5
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/instruction_integrator.py +135 -21
- apm_cli-0.20.0/src/apm_cli/integration/kiro_hook_integrator.py +322 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/mcp_integrator.py +24 -3
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/mcp_integrator_install.py +142 -111
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/skill_integrator.py +33 -7
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/targets.py +193 -12
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/builder.py +225 -46
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/client.py +250 -15
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/models.py +48 -4
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/output_mappers.py +118 -55
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/resolver.py +18 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/yml_editor.py +19 -22
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/yml_schema.py +122 -17
- apm_cli-0.20.0/src/apm_cli/models/dependency/identity.py +112 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/mcp.py +29 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/reference.py +160 -46
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/types.py +2 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/policy_checks.py +1 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/registry/operations.py +47 -18
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/skillspector.py +18 -5
- apm_cli-0.20.0/src/apm_cli/utils/archive.py +380 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/github_host.py +24 -5
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/version_checker.py +66 -21
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/yaml_io.py +40 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0/src/apm_cli.egg-info}/PKG-INFO +4 -4
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/SOURCES.txt +10 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_apm_resolver.py +141 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_github_downloader.py +123 -41
- apm_cli-0.20.0/tests/test_gitlab_git_transport.py +663 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_lockfile.py +71 -1
- {apm_cli-0.18.0 → apm_cli-0.20.0}/LICENSE +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/NOTICE +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/setup.cfg +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/cursor.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/intellij.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/git_cache.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/_helpers.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/why.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/doctor.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/find.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/lock.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/audit.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/plugin/init.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/managed_section.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/config.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/command_logger.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/errors.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/install_audit.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/plugin_manifest.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/_shared.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/bare_cache.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/clone_engine.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_auth_env.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_semver_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/outdated_row.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/auth.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/outdated.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/semver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/deps/why_walker.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/context.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/errors.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/lsp/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/lsp/integration.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/command.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/package_resolution.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/package_selection.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/_redownload.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/_skip_logic.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/audit.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/resolve.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/pipeline.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/plan.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/registry_wiring.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/root_redirect.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/service.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/summary.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/_shared.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/lsp_integrator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_shared.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/audit.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/drift_check.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/publisher.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_check.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_resolver.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/apm_package.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/lsp.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/format_detection.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/_shared.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/ci_checks.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/runtime/utils.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/base.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/gate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/generic_sarif.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/options.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/registry.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/runner.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/external/sarif_ingest.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/content_hash.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/git_sparse.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/patterns.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/perf_stats.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/version.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_apm_package_models.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_console.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_token_manager.py +0 -0
- {apm_cli-0.18.0 → apm_cli-0.20.0}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
* Daniel Meppiel (@danielmeppiel) — Microsoft
|
|
6
6
|
* Sergio Sisternes (@sergio-sisternes-epam) — EPAM Systems
|
|
7
|
+
* Nadav Yogev (@nadav-y) — JFrog
|
|
7
8
|
|
|
8
9
|
Maintainers' work on APM is supported by their employers. Project
|
|
9
10
|
decisions are made in the interest of APM and its users, independent
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apm-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -72,7 +72,7 @@ Dynamic: license-file
|
|
|
72
72
|
|
|
73
73
|
Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
|
|
74
74
|
|
|
75
|
-
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf
|
|
75
|
+
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf · Kiro
|
|
76
76
|
|
|
77
77
|
**[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)** · **[Roadmap](https://github.com/orgs/microsoft/projects/2304)**
|
|
78
78
|
|
|
@@ -135,7 +135,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
|
|
|
135
135
|
|
|
136
136
|
One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
|
|
137
137
|
|
|
138
|
-
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
|
|
138
|
+
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf, Kiro
|
|
139
139
|
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
|
|
140
140
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
141
141
|
- **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
|
|
@@ -214,7 +214,7 @@ apm marketplace add github/awesome-copilot
|
|
|
214
214
|
apm install azure-cloud-development@awesome-copilot
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, and
|
|
217
|
+
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, Windsurf, and Kiro):
|
|
218
218
|
|
|
219
219
|
```bash
|
|
220
220
|
apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
|
|
6
6
|
|
|
7
|
-
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf
|
|
7
|
+
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf · Kiro
|
|
8
8
|
|
|
9
9
|
**[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)** · **[Roadmap](https://github.com/orgs/microsoft/projects/2304)**
|
|
10
10
|
|
|
@@ -67,7 +67,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
|
|
|
67
67
|
|
|
68
68
|
One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
|
|
69
69
|
|
|
70
|
-
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
|
|
70
|
+
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf, Kiro
|
|
71
71
|
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
|
|
72
72
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
73
73
|
- **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
|
|
@@ -146,7 +146,7 @@ apm marketplace add github/awesome-copilot
|
|
|
146
146
|
apm install azure-cloud-development@awesome-copilot
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, and
|
|
149
|
+
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, Windsurf, and Kiro):
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
|
|
@@ -4,7 +4,7 @@ import os
|
|
|
4
4
|
import re
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import ClassVar
|
|
7
|
+
from typing import Any, ClassVar
|
|
8
8
|
|
|
9
9
|
from ...utils.console import _rich_error, _rich_warning
|
|
10
10
|
|
|
@@ -29,6 +29,11 @@ _ENV_PLACEHOLDER_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>|" + _ENV_VAR_RE.pattern)
|
|
|
29
29
|
_LEGACY_ANGLE_VAR_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>")
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def registry_field_is_required(field: dict[str, Any]) -> bool:
|
|
33
|
+
"""Return True unless registry metadata explicitly marks a field optional."""
|
|
34
|
+
return field.get("required", field.get("is_required", True)) is not False
|
|
35
|
+
|
|
36
|
+
|
|
32
37
|
def _translate_env_placeholder(value):
|
|
33
38
|
"""Pure-textual translation of env-var placeholders to the canonical
|
|
34
39
|
``${VAR}`` runtime-substitution syntax.
|
|
@@ -481,7 +486,7 @@ class MCPClientAdapter(ABC):
|
|
|
481
486
|
name = env_var.get("name", "")
|
|
482
487
|
if not name:
|
|
483
488
|
continue
|
|
484
|
-
required = env_var
|
|
489
|
+
required = registry_field_is_required(env_var)
|
|
485
490
|
|
|
486
491
|
value = env_overrides.get(name) or os.getenv(name)
|
|
487
492
|
if not value and required and not skip_prompting:
|
|
@@ -633,22 +638,25 @@ class MCPClientAdapter(ABC):
|
|
|
633
638
|
return server_info
|
|
634
639
|
|
|
635
640
|
@staticmethod
|
|
636
|
-
def _determine_config_key(server_url: str, server_name: str) -> str:
|
|
641
|
+
def _determine_config_key(server_url: str, server_name: str | None) -> str:
|
|
637
642
|
"""Return the configuration key to use for *server_url*/*server_name*.
|
|
638
643
|
|
|
639
|
-
The caller-supplied *server_name* takes precedence
|
|
640
|
-
|
|
641
|
-
convention ``owner/repo -> repo
|
|
644
|
+
The caller-supplied *server_name* takes precedence. If it is absent,
|
|
645
|
+
preserve npm-style scoped names such as ``@scope/name`` (one slash by
|
|
646
|
+
npm convention) while keeping the historical ``owner/repo -> repo``
|
|
647
|
+
fallback for registry paths.
|
|
642
648
|
|
|
643
649
|
Args:
|
|
644
650
|
server_url: Registry reference used as fallback source.
|
|
645
|
-
server_name: Explicit caller-supplied name
|
|
651
|
+
server_name: Explicit caller-supplied name, if any.
|
|
646
652
|
|
|
647
653
|
Returns:
|
|
648
654
|
Non-empty configuration key string.
|
|
649
655
|
"""
|
|
650
656
|
if server_name:
|
|
651
657
|
return server_name
|
|
658
|
+
if server_url.startswith("@") and server_url.count("/") == 1:
|
|
659
|
+
return server_url
|
|
652
660
|
if "/" in server_url:
|
|
653
661
|
return server_url.split("/")[-1]
|
|
654
662
|
return server_url
|
|
@@ -794,12 +802,15 @@ class MCPClientAdapter(ABC):
|
|
|
794
802
|
)
|
|
795
803
|
|
|
796
804
|
# First pass: identify variables with empty values to warn the user.
|
|
797
|
-
empty_value_vars = [
|
|
805
|
+
empty_value_vars = [
|
|
806
|
+
ev for ev in env_vars if registry_field_is_required(ev) and not ev.get("value")
|
|
807
|
+
]
|
|
798
808
|
if empty_value_vars and skip_prompting:
|
|
799
809
|
var_names = [ev.get("name") for ev in empty_value_vars]
|
|
800
810
|
_rich_warning(
|
|
801
|
-
f"
|
|
802
|
-
f"
|
|
811
|
+
f"Required environment variables have no default value and cannot be "
|
|
812
|
+
f"prompted in non-interactive mode: {var_names}. Set them in your "
|
|
813
|
+
"environment and rerun `apm install`."
|
|
803
814
|
)
|
|
804
815
|
|
|
805
816
|
for env_var in env_vars:
|
|
@@ -834,9 +845,9 @@ class MCPClientAdapter(ABC):
|
|
|
834
845
|
|
|
835
846
|
# Priority 4: interactive prompt
|
|
836
847
|
default_value = env_var.get("value", "")
|
|
837
|
-
required = env_var
|
|
848
|
+
required = registry_field_is_required(env_var)
|
|
838
849
|
|
|
839
|
-
if not skip_prompting:
|
|
850
|
+
if not skip_prompting and required:
|
|
840
851
|
from rich.prompt import Prompt
|
|
841
852
|
|
|
842
853
|
description = env_var.get("description", "")
|
|
@@ -856,11 +867,10 @@ class MCPClientAdapter(ABC):
|
|
|
856
867
|
resolved[name] = default_value
|
|
857
868
|
elif required:
|
|
858
869
|
_rich_warning(
|
|
859
|
-
f"
|
|
860
|
-
f"The MCP server may not function correctly."
|
|
870
|
+
f"Required environment variable '{name}' could not be resolved. "
|
|
871
|
+
f"The MCP server may not function correctly. Set {name} in your "
|
|
872
|
+
"environment and rerun `apm install`."
|
|
861
873
|
)
|
|
862
874
|
resolved[name] = ""
|
|
863
|
-
else:
|
|
864
|
-
resolved[name] = default_value
|
|
865
875
|
|
|
866
876
|
return resolved
|
|
@@ -239,12 +239,7 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
239
239
|
_rich_error(f"MCP server '{server_url}' not found in registry")
|
|
240
240
|
return False
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
config_key = server_name
|
|
244
|
-
elif "/" in server_url:
|
|
245
|
-
config_key = server_url.split("/")[-1]
|
|
246
|
-
else:
|
|
247
|
-
config_key = server_url
|
|
242
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
248
243
|
|
|
249
244
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
250
245
|
ok = self.update_config({config_key: server_config})
|
|
@@ -150,19 +150,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
150
150
|
if server_info is None:
|
|
151
151
|
return False
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
if server_name:
|
|
155
|
-
# Use explicitly provided server name
|
|
156
|
-
config_key = server_name
|
|
157
|
-
else: # noqa: PLR5501
|
|
158
|
-
# Extract name from server_url (part after last slash)
|
|
159
|
-
# For URLs like "microsoft/azure-devops-mcp" -> "azure-devops-mcp"
|
|
160
|
-
# For URLs like "github/github-mcp-server" -> "github-mcp-server"
|
|
161
|
-
if "/" in server_url: # noqa: SIM108
|
|
162
|
-
config_key = server_url.split("/")[-1]
|
|
163
|
-
else:
|
|
164
|
-
# Fallback to full server_url if no slash
|
|
165
|
-
config_key = server_url
|
|
153
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
166
154
|
|
|
167
155
|
# Generate server configuration with environment variable resolution
|
|
168
156
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
@@ -26,6 +26,7 @@ from .base import (
|
|
|
26
26
|
_extract_legacy_angle_vars,
|
|
27
27
|
_has_env_placeholder,
|
|
28
28
|
_stringify_env_literal,
|
|
29
|
+
registry_field_is_required,
|
|
29
30
|
)
|
|
30
31
|
from .base import (
|
|
31
32
|
_translate_env_placeholder as _translate_env_placeholder,
|
|
@@ -206,19 +207,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
206
207
|
# Generate server configuration with environment and runtime variable resolution
|
|
207
208
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
208
209
|
|
|
209
|
-
|
|
210
|
-
if server_name:
|
|
211
|
-
# Use explicitly provided server name
|
|
212
|
-
config_key = server_name
|
|
213
|
-
else: # noqa: PLR5501
|
|
214
|
-
# Extract name from server_url (part after last slash)
|
|
215
|
-
# For URLs like "microsoft/azure-devops-mcp" -> "azure-devops-mcp"
|
|
216
|
-
# For URLs like "github/github-mcp-server" -> "github-mcp-server"
|
|
217
|
-
if "/" in server_url: # noqa: SIM108
|
|
218
|
-
config_key = server_url.split("/")[-1]
|
|
219
|
-
else:
|
|
220
|
-
# Fallback to full server_url if no slash
|
|
221
|
-
config_key = server_url
|
|
210
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
222
211
|
|
|
223
212
|
# Update configuration using the chosen key
|
|
224
213
|
self.update_config({config_key: server_config})
|
|
@@ -710,6 +699,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
710
699
|
return translated
|
|
711
700
|
|
|
712
701
|
if self._supports_runtime_env_substitution:
|
|
702
|
+
env_overrides = env_overrides or {}
|
|
713
703
|
resolved = {}
|
|
714
704
|
placeholder_keys = []
|
|
715
705
|
for env_var in env_vars:
|
|
@@ -718,12 +708,18 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
718
708
|
name = env_var.get("name", "")
|
|
719
709
|
if not name:
|
|
720
710
|
continue
|
|
711
|
+
required = registry_field_is_required(env_var)
|
|
712
|
+
override_value = env_overrides.get(name)
|
|
713
|
+
has_override = bool(
|
|
714
|
+
override_value.strip() if isinstance(override_value, str) else override_value
|
|
715
|
+
)
|
|
721
716
|
if name in default_github_env:
|
|
722
717
|
# Non-secret literal default -- preserve as-is.
|
|
723
718
|
resolved[name] = default_github_env[name]
|
|
724
|
-
|
|
719
|
+
elif required or has_override:
|
|
725
720
|
# Emit a runtime-substitution placeholder; APM never reads
|
|
726
|
-
# or stores the value.
|
|
721
|
+
# or stores the value. Optional variables are included only
|
|
722
|
+
# when install-time collection observed a value.
|
|
727
723
|
resolved[name] = self._format_runtime_env_placeholder(name)
|
|
728
724
|
placeholder_keys.append(name)
|
|
729
725
|
# Record for the post-install summary line and the
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Hermes agent MCP client adapter.
|
|
2
|
+
|
|
3
|
+
Hermes (Nous Research) reads MCP servers from a YAML ``mcp_servers:`` block
|
|
4
|
+
in ``~/.hermes/config.yaml`` (snake_case key -- distinct from the JSON
|
|
5
|
+
``mcpServers`` schema used by Claude/Copilot). ``$HERMES_HOME`` overrides
|
|
6
|
+
the home directory (default ``~/.hermes``).
|
|
7
|
+
|
|
8
|
+
Scope: Hermes has a single home-directory config, so MCP writes are always
|
|
9
|
+
user-scope -- ``config.yaml`` is the same file regardless of whether the
|
|
10
|
+
install was triggered at project or user scope. Unrelated top-level config
|
|
11
|
+
keys (model provider, telegram settings, ...) are preserved on every write;
|
|
12
|
+
a malformed existing file is left untouched rather than overwritten.
|
|
13
|
+
|
|
14
|
+
Per-server shape:
|
|
15
|
+
* stdio -> ``command`` / ``args`` / ``env`` (+ ``enabled``)
|
|
16
|
+
* http -> ``url`` / ``headers`` (+ ``enabled``)
|
|
17
|
+
|
|
18
|
+
YAML serialization goes through ``utils.yaml_io`` (lint forbids raw
|
|
19
|
+
``yaml.dump``); the document is written atomically with ``0o600`` perms via
|
|
20
|
+
``utils.atomic_io`` because ``config.yaml`` carries literal credentials.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import contextlib
|
|
26
|
+
import os
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
import yaml
|
|
30
|
+
|
|
31
|
+
from ...utils.atomic_io import atomic_write_text
|
|
32
|
+
from ...utils.console import _rich_error, _rich_success
|
|
33
|
+
from ...utils.yaml_io import load_yaml, yaml_to_str
|
|
34
|
+
from .copilot import CopilotClientAdapter
|
|
35
|
+
|
|
36
|
+
# Credential-bearing config file mode: owner read/write only. Hermes' config.yaml
|
|
37
|
+
# holds literal MCP env values plus native model-provider keys / messaging tokens,
|
|
38
|
+
# so it must never be group/world-readable (parity with claude/codex/gemini/cursor).
|
|
39
|
+
_CONFIG_FILE_MODE = 0o600
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _MalformedHermesConfig(Exception):
|
|
43
|
+
"""Raised when ``config.yaml`` exists but is not a YAML mapping.
|
|
44
|
+
|
|
45
|
+
Signals write paths to refuse the overwrite so a user's native Hermes
|
|
46
|
+
credentials (model-provider keys, Telegram tokens) are never discarded.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class HermesClientAdapter(CopilotClientAdapter):
|
|
51
|
+
"""MCP configuration for the Hermes agent (YAML ``mcp_servers`` schema).
|
|
52
|
+
|
|
53
|
+
Registry formatting reuses :class:`CopilotClientAdapter`, then entries are
|
|
54
|
+
converted to Hermes' on-disk shape via :meth:`_to_hermes_format`.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
supports_user_scope: bool = True
|
|
58
|
+
target_name: str = "hermes"
|
|
59
|
+
mcp_servers_key: str = "mcp_servers"
|
|
60
|
+
|
|
61
|
+
# Hermes' config.yaml does NOT support runtime env-var substitution; the
|
|
62
|
+
# value in ``env`` must be a literal string, so install-time resolution
|
|
63
|
+
# is kept (mirrors Claude -- see #1152 supply-chain analysis).
|
|
64
|
+
_supports_runtime_env_substitution: bool = False
|
|
65
|
+
|
|
66
|
+
def _config_path(self) -> Path:
|
|
67
|
+
"""Resolve ``<hermes-home>/config.yaml`` honouring ``$HERMES_HOME``."""
|
|
68
|
+
from ...integration.targets import resolve_hermes_root
|
|
69
|
+
|
|
70
|
+
return resolve_hermes_root() / "config.yaml"
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _to_hermes_format(copilot_entry: dict, *, enabled: bool = True) -> dict:
|
|
74
|
+
"""Convert a Copilot-format server entry to Hermes' on-disk shape.
|
|
75
|
+
|
|
76
|
+
Drops Copilot-CLI-only fields (``type: "local"``, default
|
|
77
|
+
``tools: ["*"]``, empty ``id``) and stamps an explicit ``enabled``.
|
|
78
|
+
Required transport fields (``url`` for remote, ``command`` for stdio)
|
|
79
|
+
are only emitted when truthy so a malformed entry never serializes as
|
|
80
|
+
``url: null`` / ``command: null`` into Hermes' config.
|
|
81
|
+
"""
|
|
82
|
+
if not isinstance(copilot_entry, dict):
|
|
83
|
+
return copilot_entry
|
|
84
|
+
|
|
85
|
+
url = copilot_entry.get("url")
|
|
86
|
+
t = copilot_entry.get("type")
|
|
87
|
+
is_remote = bool(url) or t in ("http", "sse", "streamable-http")
|
|
88
|
+
|
|
89
|
+
out: dict = {}
|
|
90
|
+
if is_remote:
|
|
91
|
+
if url:
|
|
92
|
+
out["url"] = url
|
|
93
|
+
headers = copilot_entry.get("headers")
|
|
94
|
+
if headers:
|
|
95
|
+
out["headers"] = headers
|
|
96
|
+
else:
|
|
97
|
+
command = copilot_entry.get("command")
|
|
98
|
+
if command:
|
|
99
|
+
out["command"] = command
|
|
100
|
+
args = copilot_entry.get("args")
|
|
101
|
+
if args:
|
|
102
|
+
out["args"] = list(args)
|
|
103
|
+
env = copilot_entry.get("env")
|
|
104
|
+
if env:
|
|
105
|
+
out["env"] = dict(env)
|
|
106
|
+
out["enabled"] = enabled
|
|
107
|
+
return out
|
|
108
|
+
|
|
109
|
+
def get_config_path(self):
|
|
110
|
+
"""Path to the Hermes config file (``<hermes-home>/config.yaml``)."""
|
|
111
|
+
return str(self._config_path())
|
|
112
|
+
|
|
113
|
+
def _load_document(self) -> dict:
|
|
114
|
+
"""Load the full ``config.yaml`` document (preserving siblings).
|
|
115
|
+
|
|
116
|
+
Returns ``{}`` when the file is absent or empty. Raises
|
|
117
|
+
:class:`_MalformedHermesConfig` when the file exists but is not a YAML
|
|
118
|
+
mapping (parse error or non-dict root) so write paths can refuse to
|
|
119
|
+
overwrite and silently discard the user's native Hermes credentials.
|
|
120
|
+
"""
|
|
121
|
+
path = self._config_path()
|
|
122
|
+
if not path.is_file():
|
|
123
|
+
return {}
|
|
124
|
+
try:
|
|
125
|
+
data = load_yaml(path)
|
|
126
|
+
except (OSError, yaml.YAMLError) as exc:
|
|
127
|
+
raise _MalformedHermesConfig(str(path)) from exc
|
|
128
|
+
if data is None:
|
|
129
|
+
return {}
|
|
130
|
+
if not isinstance(data, dict):
|
|
131
|
+
raise _MalformedHermesConfig(str(path))
|
|
132
|
+
return data
|
|
133
|
+
|
|
134
|
+
def get_current_config(self):
|
|
135
|
+
"""Return ``{"mcp_servers": {...}}`` for the on-disk config."""
|
|
136
|
+
try:
|
|
137
|
+
data = self._load_document()
|
|
138
|
+
except _MalformedHermesConfig:
|
|
139
|
+
return {self.mcp_servers_key: {}}
|
|
140
|
+
servers = data.get(self.mcp_servers_key)
|
|
141
|
+
return {self.mcp_servers_key: dict(servers) if isinstance(servers, dict) else {}}
|
|
142
|
+
|
|
143
|
+
def update_config(self, config_updates, enabled=True):
|
|
144
|
+
"""Merge *config_updates* into the ``mcp_servers:`` block.
|
|
145
|
+
|
|
146
|
+
Entries are normalized to Hermes' shape. Per-server entries are
|
|
147
|
+
replaced on key conflict; unrelated servers and all other top-level
|
|
148
|
+
config keys are preserved. The file is written atomically with
|
|
149
|
+
``0o600`` permissions so the credential-bearing config is never left
|
|
150
|
+
group/world-readable. A malformed existing ``config.yaml`` is left
|
|
151
|
+
untouched (returns ``False``) rather than overwritten.
|
|
152
|
+
"""
|
|
153
|
+
path = self._config_path()
|
|
154
|
+
try:
|
|
155
|
+
data = self._load_document()
|
|
156
|
+
except _MalformedHermesConfig:
|
|
157
|
+
_rich_error(
|
|
158
|
+
f"{path} is malformed YAML; refusing to overwrite. "
|
|
159
|
+
"Fix or remove the file manually, then retry."
|
|
160
|
+
)
|
|
161
|
+
return False
|
|
162
|
+
try:
|
|
163
|
+
servers = data.get(self.mcp_servers_key)
|
|
164
|
+
if not isinstance(servers, dict):
|
|
165
|
+
servers = {}
|
|
166
|
+
for name, cfg in config_updates.items():
|
|
167
|
+
servers[name] = self._to_hermes_format(cfg, enabled=enabled)
|
|
168
|
+
data[self.mcp_servers_key] = servers
|
|
169
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
170
|
+
atomic_write_text(path, yaml_to_str(data), new_file_mode=_CONFIG_FILE_MODE)
|
|
171
|
+
# Tighten perms even when the file pre-existed with a looser mode
|
|
172
|
+
# (atomic_write_text only applies new_file_mode on first create).
|
|
173
|
+
with contextlib.suppress(OSError, NotImplementedError):
|
|
174
|
+
os.chmod(path, _CONFIG_FILE_MODE)
|
|
175
|
+
return True
|
|
176
|
+
except OSError:
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
def configure_mcp_server(
|
|
180
|
+
self,
|
|
181
|
+
server_url,
|
|
182
|
+
server_name=None,
|
|
183
|
+
enabled=True,
|
|
184
|
+
env_overrides=None,
|
|
185
|
+
server_info_cache=None,
|
|
186
|
+
runtime_vars=None,
|
|
187
|
+
):
|
|
188
|
+
if not server_url:
|
|
189
|
+
_rich_error("server_url cannot be empty")
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
server_info = self._fetch_server_info(server_url, server_info_cache)
|
|
194
|
+
if server_info is None:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
198
|
+
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
199
|
+
ok = self.update_config({config_key: server_config}, enabled=enabled)
|
|
200
|
+
if not ok:
|
|
201
|
+
_rich_error(f"Failed to write MCP config for '{config_key}' to Hermes")
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
_rich_success(f"Successfully configured MCP server '{config_key}' for Hermes")
|
|
205
|
+
return True
|
|
206
|
+
except Exception:
|
|
207
|
+
# Do not interpolate the exception message: registry URLs and
|
|
208
|
+
# other inputs may carry embedded credentials.
|
|
209
|
+
_rich_error("Error configuring MCP server")
|
|
210
|
+
return False
|