apm-cli 0.12.4__tar.gz → 0.14.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.
Files changed (334) hide show
  1. {apm_cli-0.12.4 → apm_cli-0.14.0}/NOTICE +1 -1
  2. {apm_cli-0.12.4/src/apm_cli.egg-info → apm_cli-0.14.0}/PKG-INFO +5 -3
  3. {apm_cli-0.12.4 → apm_cli-0.14.0}/README.md +1 -1
  4. {apm_cli-0.12.4 → apm_cli-0.14.0}/pyproject.toml +15 -3
  5. apm_cli-0.14.0/src/apm_cli/adapters/client/base.py +451 -0
  6. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/codex.py +14 -102
  7. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/copilot.py +176 -194
  8. apm_cli-0.14.0/src/apm_cli/adapters/client/cursor.py +240 -0
  9. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/gemini.py +42 -19
  10. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/opencode.py +3 -13
  11. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/bundle/local_bundle.py +18 -1
  12. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/bundle/plugin_exporter.py +43 -9
  13. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/bundle/unpacker.py +16 -4
  14. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/git_cache.py +3 -0
  15. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cli.py +4 -0
  16. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/_apm_yml_writer.py +12 -1
  17. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/_helpers.py +1 -24
  18. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/audit.py +15 -7
  19. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/compile/cli.py +83 -2
  20. apm_cli-0.14.0/src/apm_cli/commands/compile/watcher.py +249 -0
  21. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/config.py +6 -1
  22. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/deps/cli.py +43 -34
  23. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/init.py +64 -5
  24. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/install.py +243 -119
  25. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/__init__.py +53 -27
  26. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/doctor.py +59 -1
  27. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/init.py +2 -1
  28. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/outdated.py +34 -1
  29. apm_cli-0.14.0/src/apm_cli/commands/pack.py +765 -0
  30. apm_cli-0.14.0/src/apm_cli/commands/plugin/__init__.py +21 -0
  31. apm_cli-0.14.0/src/apm_cli/commands/plugin/init.py +44 -0
  32. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/policy.py +3 -3
  33. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/run.py +1 -1
  34. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/runtime.py +1 -1
  35. apm_cli-0.12.4/src/apm_cli/commands/update.py → apm_cli-0.14.0/src/apm_cli/commands/self_update.py +17 -8
  36. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/uninstall/cli.py +14 -6
  37. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/uninstall/engine.py +222 -12
  38. apm_cli-0.14.0/src/apm_cli/commands/update.py +353 -0
  39. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/claude_formatter.py +4 -15
  40. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/distributed_compiler.py +4 -13
  41. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/template_builder.py +40 -0
  42. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/apm_yml.py +10 -0
  43. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/auth.py +254 -88
  44. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/build_orchestrator.py +59 -18
  45. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/command_logger.py +20 -2
  46. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/errors.py +30 -4
  47. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/script_runner.py +11 -11
  48. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/token_manager.py +149 -7
  49. apm_cli-0.14.0/src/apm_cli/deps/_shared.py +40 -0
  50. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/apm_resolver.py +83 -15
  51. apm_cli-0.14.0/src/apm_cli/deps/artifactory_orchestrator.py +309 -0
  52. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/bare_cache.py +252 -7
  53. apm_cli-0.14.0/src/apm_cli/deps/clone_engine.py +342 -0
  54. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/download_strategies.py +475 -126
  55. apm_cli-0.14.0/src/apm_cli/deps/git_auth_env.py +152 -0
  56. apm_cli-0.14.0/src/apm_cli/deps/git_reference_resolver.py +417 -0
  57. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/github_downloader.py +213 -927
  58. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/github_downloader_validation.py +13 -0
  59. apm_cli-0.14.0/src/apm_cli/deps/host_backends.py +623 -0
  60. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/package_validator.py +47 -1
  61. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/registry_proxy.py +14 -0
  62. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/shared_clone_cache.py +75 -0
  63. apm_cli-0.14.0/src/apm_cli/deps/tiered_ref_resolver.py +491 -0
  64. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/context.py +1 -0
  65. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/errors.py +23 -0
  66. apm_cli-0.14.0/src/apm_cli/install/gitlab_resolver.py +41 -0
  67. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/local_bundle_handler.py +180 -3
  68. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/command.py +5 -2
  69. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/entry.py +5 -0
  70. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/writer.py +5 -0
  71. apm_cli-0.14.0/src/apm_cli/install/package_resolution.py +162 -0
  72. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/resolve.py +26 -0
  73. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/targets.py +14 -4
  74. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/pipeline.py +145 -35
  75. apm_cli-0.14.0/src/apm_cli/install/plan.py +425 -0
  76. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/request.py +15 -2
  77. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/service.py +65 -0
  78. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/services.py +129 -14
  79. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/validation.py +113 -32
  80. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/agent_integrator.py +102 -43
  81. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/base_integrator.py +181 -5
  82. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/command_integrator.py +17 -10
  83. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/hook_integrator.py +190 -11
  84. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/instruction_integrator.py +12 -10
  85. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/mcp_integrator.py +166 -545
  86. apm_cli-0.14.0/src/apm_cli/integration/mcp_integrator_install.py +601 -0
  87. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/prompt_integrator.py +27 -12
  88. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/targets.py +35 -7
  89. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/__init__.py +2 -0
  90. apm_cli-0.14.0/src/apm_cli/marketplace/_shared.py +48 -0
  91. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/builder.py +271 -308
  92. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/client.py +80 -48
  93. apm_cli-0.14.0/src/apm_cli/marketplace/diagnostics.py +13 -0
  94. apm_cli-0.14.0/src/apm_cli/marketplace/drift_check.py +258 -0
  95. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/init_template.py +22 -1
  96. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/models.py +3 -3
  97. apm_cli-0.14.0/src/apm_cli/marketplace/output_mappers.py +342 -0
  98. apm_cli-0.14.0/src/apm_cli/marketplace/output_profiles.py +100 -0
  99. apm_cli-0.14.0/src/apm_cli/marketplace/resolver.py +770 -0
  100. apm_cli-0.14.0/src/apm_cli/marketplace/version_check.py +248 -0
  101. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/yml_schema.py +356 -4
  102. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/apm_package.py +49 -5
  103. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/dependency/reference.py +395 -50
  104. apm_cli-0.14.0/src/apm_cli/policy/_shared.py +40 -0
  105. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/ci_checks.py +40 -22
  106. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/discovery.py +6 -6
  107. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/inheritance.py +63 -6
  108. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/matcher.py +1 -1
  109. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/models.py +1 -0
  110. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/parser.py +28 -10
  111. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/policy_checks.py +11 -23
  112. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/schema.py +34 -7
  113. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/registry/client.py +206 -64
  114. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/base.py +40 -0
  115. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/codex_runtime.py +4 -3
  116. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/copilot_runtime.py +4 -24
  117. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/llm_runtime.py +2 -20
  118. apm_cli-0.14.0/src/apm_cli/runtime/utils.py +66 -0
  119. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/console.py +22 -4
  120. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/github_host.py +203 -13
  121. {apm_cli-0.12.4 → apm_cli-0.14.0/src/apm_cli.egg-info}/PKG-INFO +5 -3
  122. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli.egg-info/SOURCES.txt +22 -0
  123. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli.egg-info/requires.txt +3 -1
  124. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_github_downloader.py +576 -30
  125. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_token_manager.py +270 -18
  126. apm_cli-0.12.4/src/apm_cli/adapters/client/base.py +0 -198
  127. apm_cli-0.12.4/src/apm_cli/adapters/client/cursor.py +0 -144
  128. apm_cli-0.12.4/src/apm_cli/commands/compile/watcher.py +0 -170
  129. apm_cli-0.12.4/src/apm_cli/commands/pack.py +0 -388
  130. apm_cli-0.12.4/src/apm_cli/marketplace/resolver.py +0 -370
  131. {apm_cli-0.12.4 → apm_cli-0.14.0}/AUTHORS +0 -0
  132. {apm_cli-0.12.4 → apm_cli-0.14.0}/LICENSE +0 -0
  133. {apm_cli-0.12.4 → apm_cli-0.14.0}/setup.cfg +0 -0
  134. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/__init__.py +0 -0
  135. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/__init__.py +0 -0
  136. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/__init__.py +0 -0
  137. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/claude.py +0 -0
  138. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/vscode.py +0 -0
  139. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
  140. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  141. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
  142. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  143. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/bundle/__init__.py +0 -0
  144. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
  145. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/bundle/packer.py +0 -0
  146. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/__init__.py +0 -0
  147. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/http_cache.py +0 -0
  148. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/integrity.py +0 -0
  149. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/locking.py +0 -0
  150. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/paths.py +0 -0
  151. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/cache/url_normalize.py +0 -0
  152. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/__init__.py +0 -0
  153. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/cache.py +0 -0
  154. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/compile/__init__.py +0 -0
  155. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/deps/__init__.py +0 -0
  156. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/deps/_utils.py +0 -0
  157. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/experimental.py +0 -0
  158. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/list_cmd.py +0 -0
  159. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/check.py +0 -0
  160. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
  161. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
  162. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
  163. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
  164. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
  165. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
  166. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
  167. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
  168. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/mcp.py +0 -0
  169. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/prune.py +0 -0
  170. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/targets.py +0 -0
  171. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
  172. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/commands/view.py +0 -0
  173. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/__init__.py +0 -0
  174. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/agents_compiler.py +0 -0
  175. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/build_id.py +0 -0
  176. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/constants.py +0 -0
  177. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/constitution.py +0 -0
  178. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/constitution_block.py +0 -0
  179. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
  180. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
  181. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/injector.py +0 -0
  182. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/link_resolver.py +0 -0
  183. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/compilation/output_writer.py +0 -0
  184. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/config.py +0 -0
  185. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/constants.py +0 -0
  186. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/__init__.py +0 -0
  187. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/azure_cli.py +0 -0
  188. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/conflict_detector.py +0 -0
  189. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/docker_args.py +0 -0
  190. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/experimental.py +0 -0
  191. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/null_logger.py +0 -0
  192. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/operations.py +0 -0
  193. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/safe_installer.py +0 -0
  194. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/scope.py +0 -0
  195. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/core/target_detection.py +0 -0
  196. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/__init__.py +0 -0
  197. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/aggregator.py +0 -0
  198. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
  199. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/dependency_graph.py +0 -0
  200. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/git_remote_ops.py +0 -0
  201. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/installed_package.py +0 -0
  202. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/lockfile.py +0 -0
  203. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/plugin_parser.py +0 -0
  204. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/transport_selection.py +0 -0
  205. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/deps/verifier.py +0 -0
  206. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/drift.py +0 -0
  207. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/factory.py +0 -0
  208. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/__init__.py +0 -0
  209. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/cache_pin.py +0 -0
  210. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/drift.py +0 -0
  211. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/heals/__init__.py +0 -0
  212. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/heals/base.py +0 -0
  213. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
  214. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
  215. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/helpers/__init__.py +0 -0
  216. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
  217. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/insecure_policy.py +0 -0
  218. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/__init__.py +0 -0
  219. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/args.py +0 -0
  220. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
  221. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/registry.py +0 -0
  222. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/mcp/warnings.py +0 -0
  223. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/__init__.py +0 -0
  224. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/cleanup.py +0 -0
  225. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/download.py +0 -0
  226. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/finalize.py +0 -0
  227. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/heal.py +0 -0
  228. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/integrate.py +0 -0
  229. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/local_content.py +0 -0
  230. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/lockfile.py +0 -0
  231. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
  232. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
  233. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
  234. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/presentation/__init__.py +0 -0
  235. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
  236. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/skill_path_migration.py +0 -0
  237. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/sources.py +0 -0
  238. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/summary.py +0 -0
  239. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/install/template.py +0 -0
  240. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/__init__.py +0 -0
  241. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/cleanup.py +0 -0
  242. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
  243. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/coverage.py +0 -0
  244. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/dispatch.py +0 -0
  245. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/skill_integrator.py +0 -0
  246. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/skill_transformer.py +0 -0
  247. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/integration/utils.py +0 -0
  248. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
  249. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/_io.py +0 -0
  250. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/errors.py +0 -0
  251. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
  252. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/migration.py +0 -0
  253. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
  254. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/publisher.py +0 -0
  255. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
  256. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/registry.py +0 -0
  257. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/semver.py +0 -0
  258. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
  259. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
  260. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/validator.py +0 -0
  261. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/version_pins.py +0 -0
  262. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/marketplace/yml_editor.py +0 -0
  263. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/__init__.py +0 -0
  264. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/dependency/__init__.py +0 -0
  265. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/dependency/mcp.py +0 -0
  266. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/dependency/types.py +0 -0
  267. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/plugin.py +0 -0
  268. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/results.py +0 -0
  269. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/models/validation.py +0 -0
  270. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/output/__init__.py +0 -0
  271. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/output/formatters.py +0 -0
  272. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/output/models.py +0 -0
  273. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/output/script_formatters.py +0 -0
  274. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/__init__.py +0 -0
  275. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/_help_text.py +0 -0
  276. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/install_preflight.py +0 -0
  277. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/outcome_routing.py +0 -0
  278. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/policy/project_config.py +0 -0
  279. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/primitives/__init__.py +0 -0
  280. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/primitives/discovery.py +0 -0
  281. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/primitives/models.py +0 -0
  282. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/primitives/parser.py +0 -0
  283. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/registry/__init__.py +0 -0
  284. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/registry/integration.py +0 -0
  285. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/registry/operations.py +0 -0
  286. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/__init__.py +0 -0
  287. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/factory.py +0 -0
  288. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/runtime/manager.py +0 -0
  289. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/security/__init__.py +0 -0
  290. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/security/audit_report.py +0 -0
  291. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/security/content_scanner.py +0 -0
  292. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/security/file_scanner.py +0 -0
  293. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/security/gate.py +0 -0
  294. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/update_policy.py +0 -0
  295. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/__init__.py +0 -0
  296. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/atomic_io.py +0 -0
  297. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/content_hash.py +0 -0
  298. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/diagnostics.py +0 -0
  299. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/exclude.py +0 -0
  300. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/file_ops.py +0 -0
  301. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/git_env.py +0 -0
  302. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/guards.py +0 -0
  303. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/helpers.py +0 -0
  304. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/install_tui.py +0 -0
  305. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/normalization.py +0 -0
  306. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/path_security.py +0 -0
  307. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/paths.py +0 -0
  308. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/reflink.py +0 -0
  309. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/short_sha.py +0 -0
  310. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/subprocess_env.py +0 -0
  311. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/version_checker.py +0 -0
  312. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/utils/yaml_io.py +0 -0
  313. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/version.py +0 -0
  314. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/workflow/__init__.py +0 -0
  315. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/workflow/discovery.py +0 -0
  316. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/workflow/parser.py +0 -0
  317. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli/workflow/runner.py +0 -0
  318. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  319. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
  320. {apm_cli-0.12.4 → apm_cli-0.14.0}/src/apm_cli.egg-info/top_level.txt +0 -0
  321. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_apm_package_models.py +0 -0
  322. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_apm_resolver.py +0 -0
  323. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_codex_docker_args_fix.py +0 -0
  324. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
  325. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_collision_integration.py +0 -0
  326. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_console.py +0 -0
  327. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_distributed_compilation.py +0 -0
  328. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_empty_string_and_defaults.py +0 -0
  329. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_enhanced_discovery.py +0 -0
  330. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_github_downloader_token_precedence.py +0 -0
  331. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_lockfile.py +0 -0
  332. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_runnable_prompts.py +0 -0
  333. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_runtime_manager_token_precedence.py +0 -0
  334. {apm_cli-0.12.4 → apm_cli-0.14.0}/tests/test_virtual_package_multi_install.py +0 -0
@@ -139,7 +139,7 @@ SOFTWARE.
139
139
 
140
140
  ## Component. requests
141
141
 
142
- - Version requirement: `>=2.28.0`
142
+ - Version requirement: `>=2.31.0`
143
143
  - Upstream: https://github.com/psf/requests
144
144
  - SPDX: `Apache-2.0`
145
145
  - Notes: Apache-2.0 requires forwarding the upstream NOTICE file verbatim.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apm-cli
3
- Version: 0.12.4
3
+ Version: 0.14.0
4
4
  Summary: MCP configuration tool
5
5
  Author-email: Daniel Meppiel <user@example.com>
6
6
  License: MIT License
@@ -40,7 +40,7 @@ License-File: AUTHORS
40
40
  Requires-Dist: click>=8.0.0
41
41
  Requires-Dist: colorama>=0.4.6
42
42
  Requires-Dist: pyyaml>=6.0.0
43
- Requires-Dist: requests>=2.28.0
43
+ Requires-Dist: requests>=2.31.0
44
44
  Requires-Dist: python-frontmatter>=1.0.0
45
45
  Requires-Dist: llm>=0.17.0
46
46
  Requires-Dist: llm-github-models>=0.1.0
@@ -56,9 +56,11 @@ Provides-Extra: dev
56
56
  Requires-Dist: pytest>=7.0.0; extra == "dev"
57
57
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
58
58
  Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
59
+ Requires-Dist: pytest-split>=0.9.0; extra == "dev"
59
60
  Requires-Dist: ruff>=0.11.0; extra == "dev"
60
61
  Requires-Dist: mypy>=1.0.0; extra == "dev"
61
62
  Requires-Dist: jsonschema>=4.0.0; extra == "dev"
63
+ Requires-Dist: pylint>=3.0.0; extra == "dev"
62
64
  Provides-Extra: build
63
65
  Requires-Dist: pyinstaller>=6.0.0; extra == "build"
64
66
  Dynamic: license-file
@@ -133,7 +135,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
133
135
  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.
134
136
 
135
137
  - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
136
- - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
138
+ - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
137
139
  - **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
138
140
  - **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
139
141
  - **[Marketplaces](https://microsoft.github.io/apm/guides/marketplaces/)** — install plugins from curated registries in one command, deployed across all targets and locked
@@ -68,7 +68,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
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
70
  - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
71
- - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
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`
74
74
  - **[Marketplaces](https://microsoft.github.io/apm/guides/marketplaces/)** — install plugins from curated registries in one command, deployed across all targets and locked
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apm-cli"
7
- version = "0.12.4"
7
+ version = "0.14.0"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -25,7 +25,7 @@ dependencies = [
25
25
  "click>=8.0.0",
26
26
  "colorama>=0.4.6",
27
27
  "pyyaml>=6.0.0",
28
- "requests>=2.28.0",
28
+ "requests>=2.31.0",
29
29
  "python-frontmatter>=1.0.0",
30
30
  "llm>=0.17.0",
31
31
  "llm-github-models>=0.1.0",
@@ -44,9 +44,11 @@ dev = [
44
44
  "pytest>=7.0.0",
45
45
  "pytest-cov>=4.0.0",
46
46
  "pytest-xdist>=3.0.0",
47
+ "pytest-split>=0.9.0",
47
48
  "ruff>=0.11.0",
48
49
  "mypy>=1.0.0",
49
50
  "jsonschema>=4.0.0",
51
+ "pylint>=3.0.0",
50
52
  ]
51
53
  build = [
52
54
  "pyinstaller>=6.0.0",
@@ -128,11 +130,21 @@ warn_return_any = true
128
130
  warn_unused_configs = true
129
131
 
130
132
  [tool.pytest.ini_options]
131
- addopts = "-m 'not benchmark'"
133
+ addopts = "-m 'not benchmark and not live'"
132
134
  markers = [
133
135
  "integration: marks tests as integration tests that may require network access",
134
136
  "live: marks tests that hit real GitHub repos (requires network + optional GITHUB_TOKEN)",
135
137
  "slow: marks tests as slow running tests",
136
138
  "benchmark: marks performance benchmark tests (deselected by default, run with -m benchmark)",
139
+ "requires_e2e_mode: requires APM_E2E_TESTS=1",
140
+ "requires_github_token: requires GITHUB_APM_PAT or GITHUB_TOKEN",
141
+ "requires_ado_pat: requires ADO_APM_PAT",
142
+ "requires_ado_bearer: requires az CLI logged in + APM_TEST_ADO_BEARER=1",
143
+ "requires_network_integration: requires APM_RUN_INTEGRATION_TESTS=1",
144
+ "requires_apm_binary: requires built apm binary on PATH (or APM_BINARY_PATH)",
145
+ "requires_runtime_codex: requires codex runtime installed",
146
+ "requires_runtime_copilot: requires GitHub Copilot CLI runtime installed",
147
+ "requires_runtime_llm: requires llm runtime installed",
148
+ "requires_inference: requires APM_RUN_INFERENCE_TESTS=1 + model API access",
137
149
  ]
138
150
 
@@ -0,0 +1,451 @@
1
+ """Base adapter interface for MCP clients."""
2
+
3
+ import os
4
+ import re
5
+ from abc import ABC, abstractmethod
6
+ from pathlib import Path
7
+
8
+ from ...utils.console import _rich_error, _rich_warning
9
+
10
+ _INPUT_VAR_RE = re.compile(r"\$\{input:([^}]+)\}")
11
+
12
+ # Matches ${VAR} and ${env:VAR}, capturing VAR. Intentionally does NOT match
13
+ # ${input:VAR} (the optional ``env:`` group cannot also satisfy ``input:``),
14
+ # nor GitHub Actions ``${{ ... }}`` templates (the second ``{`` fails the
15
+ # identifier class). This keeps env-var handling fully disjoint from input
16
+ # variable handling, so existing _INPUT_VAR_RE call sites are unaffected.
17
+ _ENV_VAR_RE = re.compile(r"\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)\}")
18
+
19
+
20
+ class MCPClientAdapter(ABC):
21
+ """Base adapter for MCP clients."""
22
+
23
+ # Identifier matching the corresponding ``KNOWN_TARGETS`` entry name.
24
+ # Subclasses MUST override this so target-aware code can look up
25
+ # per-target metadata via ``KNOWN_TARGETS[adapter.target_name]``
26
+ # instead of sniffing class names. The ``vscode`` adapter is the
27
+ # only MCP-only pseudo-target (no entry in ``KNOWN_TARGETS``), so
28
+ # downstream code that joins on this field must tolerate misses.
29
+ target_name: str = ""
30
+
31
+ # Top-level config key under which this adapter's MCP server entries
32
+ # live (``"mcpServers"``, ``"mcp_servers"``, ``"servers"``, ...).
33
+ # Subclasses MUST override this; ``MCPConflictDetector`` reads it to
34
+ # extract existing server configs without classname dispatch.
35
+ # The adapter is the canonical owner of its config schema, so this
36
+ # field lives here rather than on ``TargetProfile`` (which is
37
+ # primitive-focused) and applies uniformly to MCP-only adapters
38
+ # (e.g. ``VSCodeClientAdapter``) that have no ``KNOWN_TARGETS`` entry.
39
+ mcp_servers_key: str = ""
40
+
41
+ # Whether this adapter's config path is user/global-scoped (e.g.
42
+ # ``~/.copilot/``) rather than workspace-scoped (e.g. ``.vscode/``).
43
+ # Adapters that target a global path should override this to ``True``
44
+ # so that ``apm install --global`` can install MCP servers to them.
45
+ supports_user_scope: bool = False
46
+
47
+ def __init__(
48
+ self,
49
+ project_root: Path | str | None = None,
50
+ user_scope: bool = False,
51
+ ):
52
+ """Initialize the adapter with optional scope-aware path context.
53
+
54
+ Args:
55
+ project_root: Project root used to resolve project-local config paths.
56
+ When not provided, adapters fall back to the current working
57
+ directory for project-scoped paths.
58
+ user_scope: Whether the adapter should resolve user-scope config
59
+ paths instead of project-local paths when supported.
60
+ """
61
+ self._project_root = Path(project_root) if project_root is not None else None
62
+ self.user_scope = user_scope
63
+
64
+ @property
65
+ def project_root(self) -> Path:
66
+ """Return the explicit project root or the current working directory."""
67
+ if self._project_root is not None:
68
+ return self._project_root
69
+ return Path(os.getcwd())
70
+
71
+ @abstractmethod
72
+ def get_config_path(self):
73
+ """Get the path to the MCP configuration file."""
74
+ pass
75
+
76
+ @abstractmethod
77
+ def update_config(self, config_updates) -> bool | None:
78
+ """Update the MCP configuration.
79
+
80
+ Returns ``False`` or ``None`` when the config write was skipped
81
+ (for example because the existing file could not be parsed safely).
82
+ """
83
+ pass
84
+
85
+ @abstractmethod
86
+ def get_current_config(self):
87
+ """Get the current MCP configuration."""
88
+ pass
89
+
90
+ @abstractmethod
91
+ def configure_mcp_server(
92
+ self,
93
+ server_url,
94
+ server_name=None,
95
+ enabled=True,
96
+ env_overrides=None,
97
+ server_info_cache=None,
98
+ runtime_vars=None,
99
+ ):
100
+ """Configure an MCP server in the client configuration.
101
+
102
+ Args:
103
+ server_url (str): URL of the MCP server.
104
+ server_name (str, optional): Name of the server. Defaults to None.
105
+ enabled (bool, optional): Whether to enable the server. Defaults to True.
106
+ env_overrides (dict, optional): Environment variable overrides. Defaults to None.
107
+ server_info_cache (dict, optional): Pre-fetched server info to avoid duplicate registry calls.
108
+ runtime_vars (dict, optional): Runtime variable values. Defaults to None.
109
+
110
+ Returns:
111
+ bool: True if successful, False otherwise.
112
+ """
113
+ pass
114
+
115
+ @staticmethod
116
+ def _infer_registry_name(package):
117
+ """Infer the registry type from package metadata.
118
+
119
+ The MCP registry API often returns empty ``registry_name``. This
120
+ method derives the registry from explicit fields first, then falls
121
+ back to heuristics on the package name.
122
+
123
+ Args:
124
+ package (dict): A single package entry from the registry.
125
+
126
+ Returns:
127
+ str: Inferred registry name (e.g. "npm", "pypi", "docker") or "".
128
+ """
129
+ if not package:
130
+ return ""
131
+
132
+ explicit = package.get("registry_name", "")
133
+ if explicit:
134
+ return explicit
135
+
136
+ name = package.get("name", "")
137
+ runtime_hint = package.get("runtime_hint", "")
138
+
139
+ # Infer from runtime_hint
140
+ if runtime_hint in ("npx", "npm"):
141
+ return "npm"
142
+ if runtime_hint in ("uvx", "pip", "pipx"):
143
+ return "pypi"
144
+ if runtime_hint == "docker":
145
+ return "docker"
146
+ if runtime_hint in ("dotnet", "dnx"):
147
+ return "nuget"
148
+
149
+ # Infer from package name patterns
150
+ if name.startswith("@") and "/" in name:
151
+ return "npm" # scoped npm package, e.g. @azure/mcp
152
+ if name.startswith(("ghcr.io/", "mcr.microsoft.com/", "docker.io/")):
153
+ return "docker"
154
+ if name.startswith("https://") and name.endswith(".mcpb"):
155
+ return "mcpb"
156
+ # PascalCase with dots usually means nuget (e.g. Azure.Mcp)
157
+ if "." in name and not name.startswith("http") and name[0].isupper():
158
+ return "nuget"
159
+
160
+ return ""
161
+
162
+ @staticmethod
163
+ def _warn_input_variables(mapping, server_name, runtime_label):
164
+ """Emit a warning for each ``${input:...}`` reference found in *mapping*.
165
+
166
+ Runtimes that do not support VS Code-style input prompts (Copilot CLI,
167
+ Codex CLI, etc.) should call this so users know their placeholders
168
+ will not be resolved at runtime.
169
+
170
+ Args:
171
+ mapping (dict): Header or env dict to scan.
172
+ server_name (str): Server name for the warning message.
173
+ runtime_label (str): Human-readable runtime name (e.g. "Copilot CLI").
174
+ """
175
+ if not mapping:
176
+ return
177
+ seen: set = set()
178
+ for value in mapping.values():
179
+ if not isinstance(value, str):
180
+ continue
181
+ for match in _INPUT_VAR_RE.finditer(value):
182
+ var_id = match.group(1)
183
+ if var_id in seen:
184
+ continue
185
+ seen.add(var_id)
186
+ _rich_warning(
187
+ f"${{input:{var_id}}} in server "
188
+ f"'{server_name}' will not be resolved -- "
189
+ f"{runtime_label} does not support input variable prompts"
190
+ )
191
+
192
+ def normalize_project_arg(self, value):
193
+ """Normalize workspace placeholders for project-local runtimes."""
194
+ if (
195
+ not self.user_scope
196
+ and isinstance(value, str)
197
+ and value in {"${workspaceFolder}", "${projectRoot}", "${workspaceRoot}"}
198
+ ):
199
+ return "."
200
+ return value
201
+
202
+ # ------------------------------------------------------------------
203
+ # Shared server-info helpers (used by all adapter subclasses)
204
+ # ------------------------------------------------------------------
205
+
206
+ def _fetch_server_info(self, server_url: str, server_info_cache: dict | None) -> dict | None:
207
+ """Look up *server_url* in *server_info_cache* or fetch from registry.
208
+
209
+ Prints a user-visible error and returns ``None`` when the server is
210
+ not found, so callers can do a simple ``if server_info is None: return False``
211
+ guard and the error message stays consistent across adapters.
212
+
213
+ Args:
214
+ server_url: Registry reference (``owner/repo`` or full URL).
215
+ server_info_cache: Optional pre-fetched cache; ``None`` skips
216
+ the cache lookup.
217
+
218
+ Returns:
219
+ Server-info dict on success; ``None`` when not found.
220
+ """
221
+ if server_info_cache and server_url in server_info_cache:
222
+ return server_info_cache[server_url]
223
+ server_info = self.registry_client.find_server_by_reference(server_url)
224
+ if not server_info:
225
+ _rich_error(f"Error: MCP server '{server_url}' not found in registry")
226
+ return None
227
+ return server_info
228
+
229
+ @staticmethod
230
+ def _determine_config_key(server_url: str, server_name: str) -> str:
231
+ """Return the configuration key to use for *server_url*/*server_name*.
232
+
233
+ The caller-supplied *server_name* takes precedence; if empty the last
234
+ path segment of *server_url* is used as a fallback, which mirrors the
235
+ convention ``owner/repo -> repo``.
236
+
237
+ Args:
238
+ server_url: Registry reference used as fallback source.
239
+ server_name: Explicit caller-supplied name (may be empty string).
240
+
241
+ Returns:
242
+ Non-empty configuration key string.
243
+ """
244
+ if server_name:
245
+ return server_name
246
+ if "/" in server_url:
247
+ return server_url.split("/")[-1]
248
+ return server_url
249
+
250
+ @staticmethod
251
+ def _apply_pypi_homebrew_generic_config(
252
+ config: dict,
253
+ registry_name: str,
254
+ package_name: str,
255
+ runtime_hint: str,
256
+ processed_runtime_args: list,
257
+ processed_package_args: list,
258
+ resolved_env: dict,
259
+ ) -> None:
260
+ """Apply pypi / homebrew / generic (uvx / brew / npx) run config to *config*.
261
+
262
+ Mutates *config* in-place with ``command``, ``args``, and optionally
263
+ ``env`` keys appropriate for the detected registry type.
264
+
265
+ Args:
266
+ config: Mutable server-config dict to populate.
267
+ registry_name: Registry identifier (``"pypi"``, ``"homebrew"``,
268
+ ``"npm"``, or any other string treated as generic).
269
+ package_name: Base package / formula / module name.
270
+ runtime_hint: Caller-specified runtime hint (e.g. ``"uvx"``).
271
+ processed_runtime_args: Fully resolved positional args for the
272
+ runtime launcher.
273
+ processed_package_args: Fully resolved positional args appended
274
+ after the package name.
275
+ resolved_env: Pre-resolved environment variables dict; an empty
276
+ dict is omitted.
277
+ """
278
+ if registry_name == "pypi":
279
+ launcher = runtime_hint or "uvx"
280
+ config["command"] = launcher
281
+ config["args"] = [package_name] + processed_runtime_args + processed_package_args # noqa: RUF005
282
+ elif registry_name == "homebrew":
283
+ formula_name = package_name.split("/")[-1] if "/" in package_name else package_name
284
+ config["command"] = formula_name
285
+ config["args"] = processed_runtime_args + processed_package_args
286
+ else:
287
+ # Generic / npm-compatible fallback
288
+ config["command"] = "npx"
289
+ config["args"] = processed_runtime_args + ["-y", package_name] + processed_package_args # noqa: RUF005
290
+ if resolved_env:
291
+ config["env"] = resolved_env
292
+
293
+ def _apply_auth_and_headers_impl(
294
+ self,
295
+ config: dict,
296
+ remote: dict,
297
+ server_info: dict,
298
+ env_overrides: dict,
299
+ runtime_label: str,
300
+ token_manager_class,
301
+ ) -> None:
302
+ """Core implementation of GitHub-token injection and header merging.
303
+
304
+ Factored out so that each concrete adapter subclass can supply its own
305
+ *token_manager_class* (looked up from the subclass module's namespace),
306
+ allowing :func:`unittest.mock.patch` to intercept the class at the
307
+ right module scope in tests.
308
+
309
+ Args:
310
+ config: Mutable config dict updated in place.
311
+ remote: Registry remote entry (may contain a ``"headers"`` list).
312
+ server_info: Registry server metadata used for name / URL lookup.
313
+ env_overrides: Caller-supplied env-var override mapping.
314
+ runtime_label: Label for diagnostic messages.
315
+ token_manager_class: The ``GitHubTokenManager`` class (or mock) to
316
+ instantiate. Passed by the caller so tests can patch the right
317
+ module-level name.
318
+ """
319
+ server_name = server_info.get("name", "")
320
+ is_github_server = self._is_github_server(server_name, remote.get("url", ""))
321
+ local_token_injected = False
322
+ if is_github_server:
323
+ _tm = token_manager_class()
324
+ github_token = _tm.get_token_for_purpose("copilot") or os.getenv(
325
+ "GITHUB_PERSONAL_ACCESS_TOKEN"
326
+ )
327
+ if github_token:
328
+ config["headers"] = {"Authorization": f"Bearer {github_token}"}
329
+ local_token_injected = True
330
+ headers = remote.get("headers", [])
331
+ if headers:
332
+ if "headers" not in config:
333
+ config["headers"] = {}
334
+ for header in headers:
335
+ header_name = header.get("name", "")
336
+ header_value = header.get("value", "")
337
+ if header_name and header_value:
338
+ if header_name == "Authorization" and local_token_injected:
339
+ continue
340
+ resolved_value = self._resolve_env_variable(
341
+ header_name, header_value, env_overrides
342
+ )
343
+ config["headers"][header_name] = resolved_value
344
+ if config.get("headers"):
345
+ self._warn_input_variables(
346
+ config["headers"], server_info.get("name", ""), runtime_label
347
+ )
348
+
349
+ @staticmethod
350
+ def _resolve_env_vars_with_prompting(
351
+ env_vars: list,
352
+ env_overrides: dict,
353
+ default_github_env: dict,
354
+ ) -> dict:
355
+ """Resolve *env_vars* from overrides, environment, or interactive prompts.
356
+
357
+ Identical logic shared between
358
+ :meth:`CopilotClientAdapter._process_environment_variables` and
359
+ :meth:`CodexClientAdapter._process_environment_variables`.
360
+
361
+ All imports are deferred so that ``rich.prompt`` (an optional
362
+ dependency) is never imported at module load time.
363
+
364
+ Args:
365
+ env_vars: List of env-var descriptor dicts from the registry.
366
+ env_overrides: Pre-collected ``{name: value}`` overrides (empty
367
+ dict when none).
368
+ default_github_env: Mapping of well-known GitHub variable names
369
+ to their preferred environment-variable lookup names.
370
+
371
+ Returns:
372
+ ``resolved`` dict mapping each env-var name to its resolved value
373
+ (empty string when unresolvable).
374
+ """
375
+ import sys
376
+
377
+ env_overrides = env_overrides or {}
378
+ resolved: dict = {}
379
+
380
+ # Determine whether interactive prompting is available.
381
+ # If env_overrides is provided the CLI has already collected variables -- never prompt again.
382
+ skip_prompting = (
383
+ bool(env_overrides)
384
+ or bool(os.getenv("CI"))
385
+ or bool(os.getenv("APM_E2E_TESTS"))
386
+ or not sys.stdout.isatty()
387
+ or not sys.stdin.isatty()
388
+ )
389
+
390
+ # First pass: identify variables with empty values to warn the user.
391
+ empty_value_vars = [ev for ev in env_vars if ev.get("required") and not ev.get("value")]
392
+ if empty_value_vars and skip_prompting:
393
+ var_names = [ev.get("name") for ev in empty_value_vars]
394
+ _rich_warning(
395
+ f"Warning: The following required environment variables have no default "
396
+ f"value and cannot be prompted in non-interactive mode: {var_names}"
397
+ )
398
+
399
+ for env_var in env_vars:
400
+ name = env_var.get("name", "")
401
+ if not name:
402
+ continue
403
+
404
+ # Priority 1: caller-supplied override
405
+ if name in env_overrides:
406
+ resolved[name] = env_overrides[name]
407
+ continue
408
+
409
+ # Priority 2: check GitHub-specific defaults (values are literal defaults, not env-var names)
410
+ if name in default_github_env:
411
+ resolved[name] = os.getenv(name) or default_github_env[name]
412
+ continue
413
+
414
+ # Priority 3: environment variable with the same name
415
+ env_val = os.getenv(name, "")
416
+ if env_val:
417
+ resolved[name] = env_val
418
+ continue
419
+
420
+ # Priority 4: interactive prompt
421
+ default_value = env_var.get("value", "")
422
+ required = env_var.get("required", False)
423
+
424
+ if not skip_prompting:
425
+ from rich.prompt import Prompt
426
+
427
+ description = env_var.get("description", "")
428
+ prompt_text = f"Enter value for {name}"
429
+ if description:
430
+ prompt_text += f" ({description})"
431
+ is_secret = "token" in name.lower() or "key" in name.lower()
432
+ user_input = Prompt.ask(
433
+ prompt_text,
434
+ default=default_value,
435
+ password=True # noqa: SIM210
436
+ if is_secret
437
+ else False,
438
+ )
439
+ resolved[name] = user_input
440
+ elif default_value:
441
+ resolved[name] = default_value
442
+ elif required:
443
+ _rich_warning(
444
+ f"Warning: Required environment variable '{name}' could not be resolved. "
445
+ f"The MCP server may not function correctly."
446
+ )
447
+ resolved[name] = ""
448
+ else:
449
+ resolved[name] = default_value
450
+
451
+ return resolved