apm-cli 0.14.1__tar.gz → 0.14.2__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.
Files changed (336) hide show
  1. {apm_cli-0.14.1 → apm_cli-0.14.2}/NOTICE +40 -0
  2. {apm_cli-0.14.1/src/apm_cli.egg-info → apm_cli-0.14.2}/PKG-INFO +2 -3
  3. {apm_cli-0.14.1 → apm_cli-0.14.2}/README.md +0 -2
  4. {apm_cli-0.14.1 → apm_cli-0.14.2}/pyproject.toml +3 -1
  5. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/base.py +405 -3
  6. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/codex.py +93 -87
  7. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/copilot.py +22 -207
  8. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/cursor.py +1 -0
  9. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/gemini.py +17 -2
  10. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/vscode.py +41 -8
  11. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/git_cache.py +248 -33
  12. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/locking.py +26 -10
  13. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/url_normalize.py +30 -15
  14. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/install.py +29 -0
  15. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/view.py +21 -16
  16. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/context_optimizer.py +23 -5
  17. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/azure_cli.py +32 -3
  18. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/command_logger.py +75 -0
  19. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/conflict_detector.py +3 -1
  20. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/errors.py +3 -1
  21. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/target_detection.py +4 -0
  22. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/bare_cache.py +15 -0
  23. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/github_downloader.py +110 -3
  24. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/plugin_parser.py +85 -23
  25. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/drift.py +8 -0
  26. apm_cli-0.14.2/src/apm_cli/install/phases/_redownload.py +37 -0
  27. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/download.py +3 -4
  28. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/integrate.py +7 -12
  29. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/resolve.py +17 -0
  30. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/targets.py +57 -21
  31. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/pipeline.py +74 -21
  32. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/services.py +15 -3
  33. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/agent_integrator.py +1 -0
  34. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/command_integrator.py +1 -0
  35. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/copilot_app_db.py +163 -96
  36. apm_cli-0.14.2/src/apm_cli/integration/copilot_app_project.py +404 -0
  37. apm_cli-0.14.1/src/apm_cli/integration/prompt_integrator.py → apm_cli-0.14.2/src/apm_cli/integration/copilot_app_workflow_integrator.py +387 -433
  38. apm_cli-0.14.2/src/apm_cli/integration/copilot_app_ws.py +531 -0
  39. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/hook_integrator.py +379 -21
  40. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/instruction_integrator.py +22 -11
  41. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/mcp_integrator.py +0 -6
  42. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/mcp_integrator_install.py +198 -139
  43. apm_cli-0.14.2/src/apm_cli/integration/prompt_integrator.py +366 -0
  44. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/ci_checks.py +9 -3
  45. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/primitives/discovery.py +44 -6
  46. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/atomic_io.py +10 -4
  47. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/git_env.py +17 -0
  48. apm_cli-0.14.2/src/apm_cli/utils/git_sparse.py +62 -0
  49. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/github_host.py +48 -0
  50. apm_cli-0.14.2/src/apm_cli/utils/patterns.py +91 -0
  51. {apm_cli-0.14.1 → apm_cli-0.14.2/src/apm_cli.egg-info}/PKG-INFO +2 -3
  52. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli.egg-info/SOURCES.txt +6 -0
  53. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli.egg-info/requires.txt +1 -0
  54. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_codex_empty_string_and_defaults.py +89 -0
  55. {apm_cli-0.14.1 → apm_cli-0.14.2}/AUTHORS +0 -0
  56. {apm_cli-0.14.1 → apm_cli-0.14.2}/LICENSE +0 -0
  57. {apm_cli-0.14.1 → apm_cli-0.14.2}/setup.cfg +0 -0
  58. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/__init__.py +0 -0
  59. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/__init__.py +0 -0
  60. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/__init__.py +0 -0
  61. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/claude.py +0 -0
  62. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/opencode.py +0 -0
  63. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/client/windsurf.py +0 -0
  64. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  65. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/package_manager/base.py +0 -0
  66. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  67. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/bundle/__init__.py +0 -0
  68. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/bundle/local_bundle.py +0 -0
  69. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
  70. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/bundle/packer.py +0 -0
  71. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/bundle/plugin_exporter.py +0 -0
  72. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/bundle/unpacker.py +0 -0
  73. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/__init__.py +0 -0
  74. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/http_cache.py +0 -0
  75. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/integrity.py +0 -0
  76. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cache/paths.py +0 -0
  77. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/cli.py +0 -0
  78. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/__init__.py +0 -0
  79. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
  80. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/_helpers.py +0 -0
  81. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/audit.py +0 -0
  82. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/cache.py +0 -0
  83. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/compile/__init__.py +0 -0
  84. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/compile/cli.py +0 -0
  85. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/compile/watcher.py +0 -0
  86. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/config.py +0 -0
  87. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/deps/__init__.py +0 -0
  88. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/deps/_utils.py +0 -0
  89. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/deps/cli.py +0 -0
  90. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/experimental.py +0 -0
  91. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/init.py +0 -0
  92. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/list_cmd.py +0 -0
  93. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/__init__.py +0 -0
  94. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/check.py +0 -0
  95. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/doctor.py +0 -0
  96. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/init.py +0 -0
  97. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/migrate.py +0 -0
  98. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/outdated.py +0 -0
  99. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
  100. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
  101. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
  102. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
  103. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/publish.py +0 -0
  104. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/marketplace/validate.py +0 -0
  105. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/mcp.py +0 -0
  106. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/outdated.py +0 -0
  107. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/pack.py +0 -0
  108. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/plugin/__init__.py +0 -0
  109. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/plugin/init.py +0 -0
  110. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/policy.py +0 -0
  111. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/prune.py +0 -0
  112. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/run.py +0 -0
  113. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/runtime.py +0 -0
  114. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/self_update.py +0 -0
  115. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/targets.py +0 -0
  116. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/uninstall/__init__.py +0 -0
  117. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/uninstall/cli.py +0 -0
  118. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/uninstall/engine.py +0 -0
  119. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/commands/update.py +0 -0
  120. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/__init__.py +0 -0
  121. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/agents_compiler.py +0 -0
  122. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/build_id.py +0 -0
  123. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/claude_formatter.py +0 -0
  124. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/constants.py +0 -0
  125. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/constitution.py +0 -0
  126. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/constitution_block.py +0 -0
  127. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/distributed_compiler.py +0 -0
  128. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/gemini_formatter.py +0 -0
  129. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/injector.py +0 -0
  130. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/link_resolver.py +0 -0
  131. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/output_writer.py +0 -0
  132. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/compilation/template_builder.py +0 -0
  133. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/config.py +0 -0
  134. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/constants.py +0 -0
  135. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/__init__.py +0 -0
  136. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/apm_yml.py +0 -0
  137. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/auth.py +0 -0
  138. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/build_orchestrator.py +0 -0
  139. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/docker_args.py +0 -0
  140. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/experimental.py +0 -0
  141. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/null_logger.py +0 -0
  142. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/operations.py +0 -0
  143. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/safe_installer.py +0 -0
  144. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/scope.py +0 -0
  145. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/script_runner.py +0 -0
  146. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/core/token_manager.py +0 -0
  147. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/__init__.py +0 -0
  148. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/_shared.py +0 -0
  149. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/aggregator.py +0 -0
  150. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/apm_resolver.py +0 -0
  151. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/artifactory_entry.py +0 -0
  152. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
  153. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/clone_engine.py +0 -0
  154. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/dependency_graph.py +0 -0
  155. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/download_strategies.py +0 -0
  156. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/git_auth_env.py +0 -0
  157. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/git_reference_resolver.py +0 -0
  158. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/git_remote_ops.py +0 -0
  159. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/github_downloader_validation.py +0 -0
  160. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/host_backends.py +0 -0
  161. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/installed_package.py +0 -0
  162. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/lockfile.py +0 -0
  163. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/package_validator.py +0 -0
  164. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/registry_proxy.py +0 -0
  165. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/shared_clone_cache.py +0 -0
  166. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
  167. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/transport_selection.py +0 -0
  168. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/deps/verifier.py +0 -0
  169. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/drift.py +0 -0
  170. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/factory.py +0 -0
  171. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/__init__.py +0 -0
  172. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/cache_pin.py +0 -0
  173. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/context.py +0 -0
  174. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/errors.py +0 -0
  175. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/gitlab_resolver.py +0 -0
  176. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/heals/__init__.py +0 -0
  177. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/heals/base.py +0 -0
  178. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
  179. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
  180. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/helpers/__init__.py +0 -0
  181. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/helpers/security_scan.py +0 -0
  182. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/insecure_policy.py +0 -0
  183. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/local_bundle_handler.py +0 -0
  184. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/__init__.py +0 -0
  185. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/args.py +0 -0
  186. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/command.py +0 -0
  187. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/conflicts.py +0 -0
  188. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/entry.py +0 -0
  189. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/registry.py +0 -0
  190. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/warnings.py +0 -0
  191. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/mcp/writer.py +0 -0
  192. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/package_resolution.py +0 -0
  193. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/__init__.py +0 -0
  194. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/cleanup.py +0 -0
  195. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/finalize.py +0 -0
  196. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/heal.py +0 -0
  197. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/local_content.py +0 -0
  198. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/lockfile.py +0 -0
  199. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/policy_gate.py +0 -0
  200. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/policy_target_check.py +0 -0
  201. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/phases/post_deps_local.py +0 -0
  202. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/plan.py +0 -0
  203. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/presentation/__init__.py +0 -0
  204. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/presentation/dry_run.py +0 -0
  205. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/request.py +0 -0
  206. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/service.py +0 -0
  207. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/skill_path_migration.py +0 -0
  208. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/sources.py +0 -0
  209. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/summary.py +0 -0
  210. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/template.py +0 -0
  211. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/install/validation.py +0 -0
  212. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/__init__.py +0 -0
  213. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/base_integrator.py +0 -0
  214. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/cleanup.py +0 -0
  215. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
  216. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/coverage.py +0 -0
  217. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/dispatch.py +0 -0
  218. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/skill_integrator.py +0 -0
  219. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/skill_transformer.py +0 -0
  220. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/targets.py +0 -0
  221. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/integration/utils.py +0 -0
  222. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/__init__.py +0 -0
  223. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/_git_utils.py +0 -0
  224. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/_io.py +0 -0
  225. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/_shared.py +0 -0
  226. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/builder.py +0 -0
  227. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/client.py +0 -0
  228. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/diagnostics.py +0 -0
  229. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/drift_check.py +0 -0
  230. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/errors.py +0 -0
  231. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/git_stderr.py +0 -0
  232. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/init_template.py +0 -0
  233. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/migration.py +0 -0
  234. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/models.py +0 -0
  235. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/output_mappers.py +0 -0
  236. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/output_profiles.py +0 -0
  237. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/pr_integration.py +0 -0
  238. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/publisher.py +0 -0
  239. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/ref_resolver.py +0 -0
  240. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/registry.py +0 -0
  241. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/resolver.py +0 -0
  242. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/semver.py +0 -0
  243. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/shadow_detector.py +0 -0
  244. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/tag_pattern.py +0 -0
  245. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/validator.py +0 -0
  246. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/version_check.py +0 -0
  247. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/version_pins.py +0 -0
  248. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/yml_editor.py +0 -0
  249. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/marketplace/yml_schema.py +0 -0
  250. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/__init__.py +0 -0
  251. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/apm_package.py +0 -0
  252. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/dependency/__init__.py +0 -0
  253. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/dependency/mcp.py +0 -0
  254. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/dependency/reference.py +0 -0
  255. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/dependency/types.py +0 -0
  256. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/plugin.py +0 -0
  257. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/results.py +0 -0
  258. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/models/validation.py +0 -0
  259. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/output/__init__.py +0 -0
  260. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/output/formatters.py +0 -0
  261. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/output/models.py +0 -0
  262. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/output/script_formatters.py +0 -0
  263. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/__init__.py +0 -0
  264. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/_help_text.py +0 -0
  265. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/_shared.py +0 -0
  266. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/discovery.py +0 -0
  267. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/inheritance.py +0 -0
  268. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/install_preflight.py +0 -0
  269. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/matcher.py +0 -0
  270. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/models.py +0 -0
  271. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/outcome_routing.py +0 -0
  272. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/parser.py +0 -0
  273. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/policy_checks.py +0 -0
  274. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/project_config.py +0 -0
  275. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/policy/schema.py +0 -0
  276. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/primitives/__init__.py +0 -0
  277. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/primitives/models.py +0 -0
  278. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/primitives/parser.py +0 -0
  279. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/registry/__init__.py +0 -0
  280. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/registry/client.py +0 -0
  281. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/registry/integration.py +0 -0
  282. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/registry/operations.py +0 -0
  283. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/__init__.py +0 -0
  284. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/base.py +0 -0
  285. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/codex_runtime.py +0 -0
  286. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/copilot_runtime.py +0 -0
  287. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/factory.py +0 -0
  288. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/llm_runtime.py +0 -0
  289. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/manager.py +0 -0
  290. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/runtime/utils.py +0 -0
  291. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/security/__init__.py +0 -0
  292. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/security/audit_report.py +0 -0
  293. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/security/content_scanner.py +0 -0
  294. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/security/file_scanner.py +0 -0
  295. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/security/gate.py +0 -0
  296. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/update_policy.py +0 -0
  297. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/__init__.py +0 -0
  298. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/console.py +0 -0
  299. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/content_hash.py +0 -0
  300. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/diagnostics.py +0 -0
  301. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/exclude.py +0 -0
  302. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/file_ops.py +0 -0
  303. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/guards.py +0 -0
  304. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/helpers.py +0 -0
  305. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/install_tui.py +0 -0
  306. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/normalization.py +0 -0
  307. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/path_security.py +0 -0
  308. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/paths.py +0 -0
  309. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/reflink.py +0 -0
  310. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/short_sha.py +0 -0
  311. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/subprocess_env.py +0 -0
  312. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/version_checker.py +0 -0
  313. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/utils/yaml_io.py +0 -0
  314. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/version.py +0 -0
  315. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/workflow/__init__.py +0 -0
  316. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/workflow/discovery.py +0 -0
  317. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/workflow/parser.py +0 -0
  318. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli/workflow/runner.py +0 -0
  319. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  320. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli.egg-info/entry_points.txt +0 -0
  321. {apm_cli-0.14.1 → apm_cli-0.14.2}/src/apm_cli.egg-info/top_level.txt +0 -0
  322. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_apm_package_models.py +0 -0
  323. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_apm_resolver.py +0 -0
  324. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_codex_docker_args_fix.py +0 -0
  325. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_collision_integration.py +0 -0
  326. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_console.py +0 -0
  327. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_distributed_compilation.py +0 -0
  328. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_empty_string_and_defaults.py +0 -0
  329. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_enhanced_discovery.py +0 -0
  330. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_github_downloader.py +0 -0
  331. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_github_downloader_token_precedence.py +0 -0
  332. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_lockfile.py +0 -0
  333. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_runnable_prompts.py +0 -0
  334. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_runtime_manager_token_precedence.py +0 -0
  335. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_token_manager.py +0 -0
  336. {apm_cli-0.14.1 → apm_cli-0.14.2}/tests/test_virtual_package_multi_install.py +0 -0
@@ -1252,6 +1252,46 @@ SOFTWARE.
1252
1252
 
1253
1253
  ---
1254
1254
 
1255
+ ## Component. websockets
1256
+
1257
+ - Version requirement: `>=12,<17`
1258
+ - Upstream: https://github.com/python-websockets/websockets
1259
+ - SPDX: `BSD-3-Clause`
1260
+ - Notes: Used by the copilot-app integrator to nudge the Copilot desktop App over its local WebSocket IPC channel after writing workflows to the App DB.
1261
+
1262
+ ### Open Source License/Copyright Notice.
1263
+
1264
+ _Copyright (c) Aymeric Augustin and contributors_
1265
+
1266
+ ```
1267
+ Copyright (c) Aymeric Augustin and contributors
1268
+
1269
+ Redistribution and use in source and binary forms, with or without
1270
+ modification, are permitted provided that the following conditions are met:
1271
+
1272
+ * Redistributions of source code must retain the above copyright notice,
1273
+ this list of conditions and the following disclaimer.
1274
+ * Redistributions in binary form must reproduce the above copyright notice,
1275
+ this list of conditions and the following disclaimer in the documentation
1276
+ and/or other materials provided with the distribution.
1277
+ * Neither the name of the copyright holder nor the names of its contributors
1278
+ may be used to endorse or promote products derived from this software
1279
+ without specific prior written permission.
1280
+
1281
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1282
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1283
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1284
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
1285
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1286
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1287
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
1288
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1289
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1290
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1291
+ ```
1292
+
1293
+ ---
1294
+
1255
1295
  Submitted on behalf of a third-party
1256
1296
 
1257
1297
  The contributions below are identified as submitted on behalf of a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apm-cli
3
- Version: 0.14.1
3
+ Version: 0.14.2
4
4
  Summary: MCP configuration tool
5
5
  Author-email: Daniel Meppiel <user@example.com>
6
6
  License: MIT License
@@ -52,6 +52,7 @@ Requires-Dist: watchdog>=3.0.0
52
52
  Requires-Dist: GitPython>=3.1.0
53
53
  Requires-Dist: ruamel.yaml>=0.18.0
54
54
  Requires-Dist: filelock>=3.12
55
+ Requires-Dist: websockets<17,>=12
55
56
  Provides-Extra: dev
56
57
  Requires-Dist: pytest>=7.0.0; extra == "dev"
57
58
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -219,8 +220,6 @@ Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemin
219
220
  apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
220
221
  ```
221
222
 
222
- > *Codex CLI currently does not support remote MCP servers; the install will skip Codex with a notice. Omit `--transport http` to use the local Docker variant on Codex (requires `GITHUB_PERSONAL_ACCESS_TOKEN`).*
223
-
224
223
  See the **[Getting Started guide](https://microsoft.github.io/apm/getting-started/quick-start/)** for the full walkthrough.
225
224
 
226
225
  ## Works with agentrc
@@ -152,8 +152,6 @@ Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemin
152
152
  apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
153
153
  ```
154
154
 
155
- > *Codex CLI currently does not support remote MCP servers; the install will skip Codex with a notice. Omit `--transport http` to use the local Docker variant on Codex (requires `GITHUB_PERSONAL_ACCESS_TOKEN`).*
156
-
157
155
  See the **[Getting Started guide](https://microsoft.github.io/apm/getting-started/quick-start/)** for the full walkthrough.
158
156
 
159
157
  ## Works with agentrc
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apm-cli"
7
- version = "0.14.1"
7
+ version = "0.14.2"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -37,6 +37,7 @@ dependencies = [
37
37
  "GitPython>=3.1.0",
38
38
  "ruamel.yaml>=0.18.0",
39
39
  "filelock>=3.12",
40
+ "websockets>=12,<17",
40
41
  ]
41
42
 
42
43
  [project.optional-dependencies]
@@ -137,6 +138,7 @@ parallel = true
137
138
  [tool.coverage.report]
138
139
  show_missing = true
139
140
  skip_empty = true
141
+ fail_under = 80
140
142
  # Exclude lines that are not meant to be covered
141
143
  exclude_lines = [
142
144
  "pragma: no cover",
@@ -4,6 +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
8
 
8
9
  from ...utils.console import _rich_error, _rich_warning
9
10
 
@@ -16,6 +17,80 @@ _INPUT_VAR_RE = re.compile(r"\$\{input:([^}]+)\}")
16
17
  # variable handling, so existing _INPUT_VAR_RE call sites are unaffected.
17
18
  _ENV_VAR_RE = re.compile(r"\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)\}")
18
19
 
20
+ # Superset of _ENV_VAR_RE that also matches the legacy ``<VAR>`` syntax
21
+ # (uppercase identifier only). Used as the single-pass translation target so
22
+ # resolved values are NOT re-scanned -- a literal value whose text happens to
23
+ # contain ``${...}`` does not get recursively expanded. ``${input:...}`` is
24
+ # intentionally not matched here so input-variable handling stays disjoint.
25
+ _ENV_PLACEHOLDER_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>|" + _ENV_VAR_RE.pattern)
26
+
27
+ # Detects the legacy ``<VAR>`` placeholder syntax only. Used to aggregate
28
+ # deprecation warnings across all servers in a single install run.
29
+ _LEGACY_ANGLE_VAR_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>")
30
+
31
+
32
+ def _translate_env_placeholder(value):
33
+ """Pure-textual translation of env-var placeholders to the canonical
34
+ ``${VAR}`` runtime-substitution syntax.
35
+
36
+ Security-critical helper for issue #1152: MUST NOT read ``os.environ``
37
+ and MUST NOT resolve placeholders to literal values. Runtimes that
38
+ support runtime substitution (Copilot CLI) resolve ``${VAR}`` from the
39
+ host environment at server-start, so APM emits placeholders verbatim
40
+ rather than baking secrets to disk.
41
+
42
+ Translations:
43
+ ``${env:VAR}`` -> ``${VAR}`` (strip ``env:`` prefix)
44
+ ``${VAR}`` -> ``${VAR}`` (no-op)
45
+ ``<VAR>`` -> ``${VAR}`` (legacy syntax migration)
46
+ ``${VAR:-default}``-> passthrough (regex doesn't match)
47
+ ``$VAR`` (bare) -> passthrough (regex doesn't match)
48
+ ``${input:foo}`` -> passthrough (regex doesn't match)
49
+ non-string -> passthrough
50
+
51
+ Idempotent: applying twice yields the same result as applying once.
52
+ """
53
+ if not isinstance(value, str):
54
+ return value
55
+
56
+ def _to_brace(match):
57
+ # group(1) = legacy <VAR>; group(2) = ${VAR} / ${env:VAR}
58
+ var_name = match.group(1) or match.group(2)
59
+ return "${" + var_name + "}"
60
+
61
+ return _ENV_PLACEHOLDER_RE.sub(_to_brace, value)
62
+
63
+
64
+ def _extract_legacy_angle_vars(value):
65
+ """Return the set of legacy ``<VAR>`` names present in *value*.
66
+
67
+ Used to aggregate deprecation warnings across all servers in a single
68
+ install run, so authors see one helpful list instead of one warning per
69
+ occurrence.
70
+ """
71
+ if not isinstance(value, str):
72
+ return set()
73
+ return set(_LEGACY_ANGLE_VAR_RE.findall(value))
74
+
75
+
76
+ def _has_env_placeholder(value):
77
+ """True if *value* is a string containing any recognised env-var
78
+ placeholder syntax (``${VAR}``, ``${env:VAR}``, or legacy ``<VAR>``).
79
+
80
+ Used to distinguish placeholder-sourced env values (which translate)
81
+ from hardcoded literal defaults (which stay literal).
82
+ """
83
+ if not isinstance(value, str):
84
+ return False
85
+ return bool(_ENV_PLACEHOLDER_RE.search(value))
86
+
87
+
88
+ def _stringify_env_literal(value):
89
+ """Return MCP env literal values in the manifest ``map<string, string>`` shape."""
90
+ if isinstance(value, bool):
91
+ return str(value).lower()
92
+ return str(value)
93
+
19
94
 
20
95
  class MCPClientAdapter(ABC):
21
96
  """Base adapter for MCP clients."""
@@ -44,6 +119,13 @@ class MCPClientAdapter(ABC):
44
119
  # so that ``apm install --global`` can install MCP servers to them.
45
120
  supports_user_scope: bool = False
46
121
 
122
+ # Whether the target runtime resolves ``${VAR}`` placeholders from the
123
+ # host environment at server-start time. Adapters that opt in (Copilot
124
+ # CLI) emit placeholders verbatim so secrets never touch disk; legacy
125
+ # adapters resolve to literal values at install time via env_overrides
126
+ # -> os.environ -> optional interactive prompt. See issue #1152.
127
+ _supports_runtime_env_substitution: bool = False
128
+
47
129
  def __init__(
48
130
  self,
49
131
  project_root: Path | str | None = None,
@@ -60,6 +142,13 @@ class MCPClientAdapter(ABC):
60
142
  """
61
143
  self._project_root = Path(project_root) if project_root is not None else None
62
144
  self.user_scope = user_scope
145
+ # Per-server tracking populated by the env-resolution helpers and
146
+ # consumed by ``configure_mcp_server`` for the post-install summary
147
+ # and the aggregated legacy-syntax deprecation warning. Defined on
148
+ # the base so every adapter has the attributes regardless of which
149
+ # subclass path constructed it.
150
+ self._last_env_placeholder_keys: set[str] = set()
151
+ self._last_legacy_angle_vars: set[str] = set()
63
152
 
64
153
  @property
65
154
  def project_root(self) -> Path:
@@ -159,6 +248,46 @@ class MCPClientAdapter(ABC):
159
248
 
160
249
  return ""
161
250
 
251
+ @classmethod
252
+ def _select_best_package(cls, packages):
253
+ """Select the best package for installation from available packages.
254
+
255
+ Prioritizes packages in order: npm, docker, pypi, homebrew, others.
256
+ Uses ``_infer_registry_name`` so selection works even when the
257
+ registry API returns empty ``registry_name``.
258
+
259
+ Args:
260
+ packages (list): List of package dictionaries.
261
+
262
+ Returns:
263
+ dict: Best package to use, or None if no suitable package found.
264
+ """
265
+ priority_order = ["npm", "docker", "pypi", "homebrew"]
266
+
267
+ for target in priority_order:
268
+ for package in packages:
269
+ if cls._infer_registry_name(package) == target:
270
+ return package
271
+
272
+ # If no priority package found, return the first one
273
+ return packages[0] if packages else None
274
+
275
+ @staticmethod
276
+ def _select_remote_with_url(remotes):
277
+ """Return the first remote entry that has a non-empty URL.
278
+
279
+ Args:
280
+ remotes (list): Candidate remote entries from the registry.
281
+
282
+ Returns:
283
+ dict or None: The first usable remote, or None if none qualify.
284
+ """
285
+ for remote in remotes:
286
+ url = (remote.get("url") or "").strip()
287
+ if url:
288
+ return remote
289
+ return None
290
+
162
291
  @staticmethod
163
292
  def _warn_input_variables(mapping, server_name, runtime_label):
164
293
  """Emit a warning for each ``${input:...}`` reference found in *mapping*.
@@ -199,6 +328,270 @@ class MCPClientAdapter(ABC):
199
328
  return "."
200
329
  return value
201
330
 
331
+ # -- Env-var placeholder resolution -------------------------------------
332
+ # GitHub MCP server defaults: not secrets, preserved literal in translate
333
+ # mode and used as fallbacks in legacy mode. The defaults apply regardless
334
+ # of which client CLI runs the server, so they live on the base.
335
+ _DEFAULT_GITHUB_ENV: ClassVar[dict[str, str]] = {
336
+ "GITHUB_TOOLSETS": "context",
337
+ "GITHUB_DYNAMIC_TOOLSETS": "1",
338
+ }
339
+
340
+ @staticmethod
341
+ def _should_skip_env_prompts(env_overrides):
342
+ """True when the caller has already collected env vars (managed mode),
343
+ when APM_E2E_TESTS is set, or when stdin/stdout is not a TTY.
344
+
345
+ Centralising this policy keeps the resolver paths consistent and
346
+ avoids subtle drift between ``_resolve_environment_variables`` and
347
+ ``_resolve_env_variable``.
348
+ """
349
+ import sys
350
+
351
+ if env_overrides:
352
+ return True
353
+ if os.getenv("APM_E2E_TESTS") == "1":
354
+ return True
355
+ return not (sys.stdin.isatty() and sys.stdout.isatty())
356
+
357
+ def _resolve_environment_variables(self, env_vars, env_overrides=None):
358
+ """Resolve (or translate) declared environment variables.
359
+
360
+ Behaviour follows ``self._supports_runtime_env_substitution``:
361
+ translate-mode (Copilot CLI) emits ``${VAR}`` placeholders verbatim
362
+ so the runtime resolves them at server-start (see issue #1152);
363
+ legacy-mode resolves placeholders to literal values via env_overrides
364
+ -> os.environ -> optional interactive prompt.
365
+
366
+ Args:
367
+ env_vars: Either a ``dict[name, value-or-placeholder]`` from a
368
+ self-defined stdio dep (``_raw_stdio["env"]``), or a
369
+ ``list[{name, description, required}]`` from the registry.
370
+ env_overrides: Pre-collected env-var overrides (ignored in
371
+ translate mode).
372
+
373
+ Returns:
374
+ dict: ``{name: value}`` -- placeholder string in translate
375
+ mode, literal value in legacy mode.
376
+ """
377
+ # ---- translate mode, dict shape (self-defined stdio in apm.yml) ----
378
+ if isinstance(env_vars, dict) and self._supports_runtime_env_substitution:
379
+ # Value type is intentionally untyped: most entries are translated
380
+ # placeholder strings, but non-string values (e.g. an int/bool
381
+ # YAML scalar) are passed through verbatim and serialised by the
382
+ # adapter's config writer (JSON/TOML).
383
+ translated: dict = {}
384
+ placeholder_keys: list[str] = []
385
+ for name, raw_value in env_vars.items():
386
+ if not name:
387
+ continue
388
+ if not isinstance(raw_value, str):
389
+ translated[name] = raw_value
390
+ continue
391
+ if _has_env_placeholder(raw_value):
392
+ self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(raw_value))
393
+ translated[name] = _translate_env_placeholder(raw_value)
394
+ # Record every ${VAR} in the translated value (handles
395
+ # both ${env:VAR} -> ${VAR} and bare ${VAR} cases).
396
+ placeholder_keys.extend(
397
+ m.group(1) for m in _ENV_VAR_RE.finditer(translated[name])
398
+ )
399
+ elif (
400
+ name in self._DEFAULT_GITHUB_ENV and raw_value == self._DEFAULT_GITHUB_ENV[name]
401
+ ):
402
+ translated[name] = raw_value
403
+ else:
404
+ # Literal value present in apm.yml -- replace with a
405
+ # runtime placeholder so the secret never touches disk.
406
+ translated[name] = "${" + name + "}"
407
+ placeholder_keys.append(name)
408
+ self._last_env_placeholder_keys = set(placeholder_keys)
409
+ return translated
410
+
411
+ # ---- translate mode, registry list shape ----
412
+ if self._supports_runtime_env_substitution:
413
+ resolved: dict[str, str] = {}
414
+ placeholder_keys: list[str] = []
415
+ for env_var in env_vars:
416
+ if not isinstance(env_var, dict):
417
+ continue
418
+ name = env_var.get("name", "")
419
+ if not name:
420
+ continue
421
+ if name in self._DEFAULT_GITHUB_ENV:
422
+ resolved[name] = self._DEFAULT_GITHUB_ENV[name]
423
+ else:
424
+ resolved[name] = "${" + name + "}"
425
+ placeholder_keys.append(name)
426
+ self._last_env_placeholder_keys = set(placeholder_keys)
427
+ return resolved
428
+
429
+ # ---- legacy mode, dict shape (self-defined stdio in apm.yml) ----
430
+ # Issue #1266 / #1222: ``_raw_stdio["env"]`` is a plain dict. Each
431
+ # value is resolved via the same single-value pipeline used for
432
+ # header values so all three placeholder syntaxes (``<VAR>``,
433
+ # ``${VAR}``, ``${env:VAR}``) behave consistently across adapters.
434
+ #
435
+ # Note the deliberate semantic divergence from the legacy-list branch
436
+ # below: empty strings authored in apm.yml are preserved as-is and
437
+ # ``_DEFAULT_GITHUB_ENV`` fallbacks are NOT applied, because a value
438
+ # explicitly written by the user expresses intent, whereas an empty
439
+ # value coming from ``env_overrides`` / ``os.environ`` for a
440
+ # registry-declared schema entry means "no value supplied, use the
441
+ # default if one exists".
442
+ if isinstance(env_vars, dict):
443
+ resolved = {}
444
+ for name, value in env_vars.items():
445
+ if not name:
446
+ continue
447
+ if isinstance(value, str):
448
+ resolved[name] = self._resolve_env_variable(
449
+ name, value, env_overrides=env_overrides
450
+ )
451
+ elif value is not None:
452
+ resolved[name] = str(value)
453
+ return resolved
454
+
455
+ # ---- legacy mode, registry list shape ----
456
+ from rich.prompt import Prompt
457
+
458
+ env_overrides = env_overrides or {}
459
+ skip_prompting = self._should_skip_env_prompts(env_overrides)
460
+
461
+ # Variables explicitly provided with empty values mean "use the default".
462
+ empty_value_vars = {k for k, v in env_overrides.items() if not v or not v.strip()}
463
+
464
+ resolved = {}
465
+ for env_var in env_vars:
466
+ if not isinstance(env_var, dict):
467
+ continue
468
+ name = env_var.get("name", "")
469
+ if not name:
470
+ continue
471
+ required = env_var.get("required", True)
472
+
473
+ value = env_overrides.get(name) or os.getenv(name)
474
+ if not value and required and not skip_prompting:
475
+ prompt_text = f"Enter value for {name}"
476
+ if description := env_var.get("description", ""):
477
+ prompt_text += f" ({description})"
478
+ value = Prompt.ask(
479
+ prompt_text,
480
+ password="token" in name.lower() or "key" in name.lower(),
481
+ )
482
+
483
+ if value and value.strip():
484
+ resolved[name] = value
485
+ elif name in self._DEFAULT_GITHUB_ENV and (
486
+ name in empty_value_vars or not required or skip_prompting
487
+ ):
488
+ resolved[name] = self._DEFAULT_GITHUB_ENV[name]
489
+
490
+ return resolved
491
+
492
+ def _resolve_env_variable(self, name, value, env_overrides=None):
493
+ """Resolve (or translate) a single env-var value.
494
+
495
+ Used for header values and for individual entries in dict-shape
496
+ env blocks. The ``name`` parameter is currently unused by the
497
+ method body but kept in the signature because every call site
498
+ (headers, dict iteration) already has the name in hand, and
499
+ passing it preserves call-site symmetry with future hooks that
500
+ may want to dispatch on it.
501
+
502
+ Args:
503
+ name: Env-var name (currently unused, see above).
504
+ value: Env-var value possibly containing placeholders.
505
+ env_overrides: Pre-collected overrides (ignored in translate mode).
506
+ """
507
+ if self._supports_runtime_env_substitution:
508
+ legacy_keys = _extract_legacy_angle_vars(value)
509
+ self._last_legacy_angle_vars.update(legacy_keys)
510
+ self._last_env_placeholder_keys.update(legacy_keys)
511
+ for match in _ENV_VAR_RE.finditer(value):
512
+ self._last_env_placeholder_keys.add(match.group(1))
513
+ return _translate_env_placeholder(value)
514
+
515
+ from rich.prompt import Prompt
516
+
517
+ env_overrides = env_overrides or {}
518
+ skip_prompting = self._should_skip_env_prompts(env_overrides)
519
+
520
+ # Three accepted placeholder syntaxes resolved against
521
+ # env_overrides -> os.environ -> optional interactive prompt.
522
+ # Single-pass substitution preserves the legacy ``<VAR>`` semantics:
523
+ # resolved values are NOT re-scanned for further expansion.
524
+ def _replace(match):
525
+ env_name = match.group(1) or match.group(2)
526
+ env_value = env_overrides.get(env_name) or os.getenv(env_name)
527
+ if not env_value and not skip_prompting:
528
+ env_value = Prompt.ask(
529
+ f"Enter value for {env_name}",
530
+ password="token" in env_name.lower() or "key" in env_name.lower(),
531
+ )
532
+ return env_value if env_value else match.group(0)
533
+
534
+ return _ENV_PLACEHOLDER_RE.sub(_replace, value)
535
+
536
+ def _resolve_variable_placeholders(self, value, resolved_env, runtime_vars):
537
+ """Resolve env-var and APM template placeholders in argument strings.
538
+
539
+ Translate mode rewrites all three env-var placeholder syntaxes to
540
+ ``${VAR}`` (so the runtime can resolve them at server-start); legacy
541
+ mode resolves only the legacy ``<VAR>`` form against ``resolved_env``
542
+ and leaves the newer ``${VAR}`` / ``${env:VAR}`` syntaxes untouched
543
+ for backward compatibility. APM template variables (``{runtime_var}``)
544
+ are always resolved at install time because they are an APM-internal
545
+ concept the target runtime cannot interpret.
546
+
547
+ Args:
548
+ value: String possibly containing placeholders.
549
+ resolved_env: Resolved env-var literals (legacy mode) or
550
+ placeholder strings (translate mode).
551
+ runtime_vars: Resolved APM template variables.
552
+
553
+ Returns:
554
+ str: ``value`` with placeholders translated or resolved.
555
+ """
556
+ if not value:
557
+ return value
558
+
559
+ processed = str(value)
560
+
561
+ if self._supports_runtime_env_substitution:
562
+ self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(processed))
563
+ processed = _translate_env_placeholder(processed)
564
+ else:
565
+ # Resolve only the legacy ``<VAR>`` form; newer syntaxes are
566
+ # preserved verbatim for backward compatibility.
567
+ def _replace_legacy_angle(match):
568
+ return resolved_env.get(match.group(1), match.group(0))
569
+
570
+ processed = _LEGACY_ANGLE_VAR_RE.sub(_replace_legacy_angle, processed)
571
+
572
+ # Resolve APM ``{runtime_var}`` template variables. The negative
573
+ # lookbehind on ``$`` ensures we never accidentally match the brace
574
+ # of an already-translated ``${VAR}`` env placeholder.
575
+ if runtime_vars:
576
+ runtime_pattern = re.compile(r"(?<!\$)\{([a-zA-Z_][a-zA-Z0-9_]*)\}")
577
+
578
+ def _replace_runtime(match):
579
+ return runtime_vars.get(match.group(1), match.group(0))
580
+
581
+ processed = runtime_pattern.sub(_replace_runtime, processed)
582
+
583
+ return processed
584
+
585
+ def _resolve_env_placeholders(self, value, resolved_env):
586
+ """Legacy thin wrapper for backward compatibility.
587
+
588
+ Kept because external callers and the phase-3 test suite invoke
589
+ the pre-#1277 name. Delegates to ``_resolve_variable_placeholders``
590
+ with an empty ``runtime_vars`` map. New code should call
591
+ ``_resolve_variable_placeholders`` directly.
592
+ """
593
+ return self._resolve_variable_placeholders(value, resolved_env, {})
594
+
202
595
  # ------------------------------------------------------------------
203
596
  # Shared server-info helpers (used by all adapter subclasses)
204
597
  # ------------------------------------------------------------------
@@ -401,10 +794,19 @@ class MCPClientAdapter(ABC):
401
794
  if not name:
402
795
  continue
403
796
 
404
- # Priority 1: caller-supplied override
797
+ # Priority 1: caller-supplied override.
798
+ # An explicit empty (or whitespace-only) value is treated as
799
+ # "user cleared this". For names with a GitHub-style default the
800
+ # logic falls through so the literal default wins; for names
801
+ # without a default the entry is dropped from the resolved map.
405
802
  if name in env_overrides:
406
- resolved[name] = env_overrides[name]
407
- continue
803
+ override_value = env_overrides[name]
804
+ if isinstance(override_value, str) and not override_value.strip():
805
+ if name not in default_github_env:
806
+ continue
807
+ else:
808
+ resolved[name] = override_value
809
+ continue
408
810
 
409
811
  # Priority 2: check GitHub-specific defaults (values are literal defaults, not env-var names)
410
812
  if name in default_github_env: