apm-cli 0.12.3__tar.gz → 0.13.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 (317) hide show
  1. {apm_cli-0.12.3 → apm_cli-0.13.0}/NOTICE +1 -1
  2. {apm_cli-0.12.3/src/apm_cli.egg-info → apm_cli-0.13.0}/PKG-INFO +4 -3
  3. {apm_cli-0.12.3 → apm_cli-0.13.0}/README.md +1 -1
  4. {apm_cli-0.12.3 → apm_cli-0.13.0}/pyproject.toml +14 -3
  5. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/copilot.py +27 -16
  6. apm_cli-0.13.0/src/apm_cli/adapters/client/cursor.py +326 -0
  7. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/bundle/local_bundle.py +18 -1
  8. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/bundle/plugin_exporter.py +8 -1
  9. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/bundle/unpacker.py +16 -4
  10. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/git_cache.py +3 -0
  11. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cli.py +2 -0
  12. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/_apm_yml_writer.py +12 -1
  13. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/config.py +6 -1
  14. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/deps/cli.py +43 -34
  15. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/install.py +183 -114
  16. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/__init__.py +39 -12
  17. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/outdated.py +1 -1
  18. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/pack.py +35 -6
  19. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/run.py +1 -1
  20. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/runtime.py +1 -1
  21. apm_cli-0.12.3/src/apm_cli/commands/update.py → apm_cli-0.13.0/src/apm_cli/commands/self_update.py +17 -8
  22. apm_cli-0.13.0/src/apm_cli/commands/update.py +319 -0
  23. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/link_resolver.py +22 -1
  24. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/apm_yml.py +10 -0
  25. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/auth.py +254 -88
  26. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/command_logger.py +20 -2
  27. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/errors.py +30 -4
  28. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/token_manager.py +149 -7
  29. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/apm_resolver.py +83 -15
  30. apm_cli-0.13.0/src/apm_cli/deps/artifactory_orchestrator.py +319 -0
  31. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/bare_cache.py +252 -7
  32. apm_cli-0.13.0/src/apm_cli/deps/clone_engine.py +342 -0
  33. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/download_strategies.py +475 -126
  34. apm_cli-0.13.0/src/apm_cli/deps/git_auth_env.py +152 -0
  35. apm_cli-0.13.0/src/apm_cli/deps/git_reference_resolver.py +417 -0
  36. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/github_downloader.py +196 -909
  37. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/github_downloader_validation.py +13 -0
  38. apm_cli-0.13.0/src/apm_cli/deps/host_backends.py +623 -0
  39. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/package_validator.py +47 -1
  40. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/registry_proxy.py +14 -0
  41. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/shared_clone_cache.py +75 -0
  42. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/errors.py +23 -0
  43. apm_cli-0.13.0/src/apm_cli/install/gitlab_resolver.py +41 -0
  44. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/local_bundle_handler.py +180 -3
  45. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/command.py +5 -2
  46. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/entry.py +5 -0
  47. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/writer.py +5 -0
  48. apm_cli-0.13.0/src/apm_cli/install/package_resolution.py +162 -0
  49. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/targets.py +14 -4
  50. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/pipeline.py +145 -35
  51. apm_cli-0.13.0/src/apm_cli/install/plan.py +425 -0
  52. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/request.py +15 -2
  53. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/service.py +65 -0
  54. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/services.py +91 -9
  55. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/validation.py +71 -33
  56. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/agent_integrator.py +20 -22
  57. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/base_integrator.py +9 -5
  58. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/prompt_integrator.py +3 -12
  59. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/targets.py +15 -5
  60. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/client.py +80 -48
  61. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/models.py +3 -3
  62. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/resolver.py +268 -21
  63. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/apm_package.py +24 -4
  64. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/dependency/reference.py +373 -47
  65. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/discovery.py +1 -1
  66. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/inheritance.py +7 -1
  67. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/parser.py +1 -1
  68. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/policy_checks.py +2 -2
  69. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/schema.py +6 -1
  70. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/console.py +3 -0
  71. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/github_host.py +203 -13
  72. {apm_cli-0.12.3 → apm_cli-0.13.0/src/apm_cli.egg-info}/PKG-INFO +4 -3
  73. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli.egg-info/SOURCES.txt +9 -0
  74. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli.egg-info/requires.txt +2 -1
  75. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_github_downloader.py +576 -30
  76. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_token_manager.py +270 -18
  77. apm_cli-0.12.3/src/apm_cli/adapters/client/cursor.py +0 -144
  78. {apm_cli-0.12.3 → apm_cli-0.13.0}/AUTHORS +0 -0
  79. {apm_cli-0.12.3 → apm_cli-0.13.0}/LICENSE +0 -0
  80. {apm_cli-0.12.3 → apm_cli-0.13.0}/setup.cfg +0 -0
  81. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/__init__.py +0 -0
  82. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/__init__.py +0 -0
  83. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/__init__.py +0 -0
  84. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/base.py +0 -0
  85. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/claude.py +0 -0
  86. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/codex.py +0 -0
  87. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/gemini.py +0 -0
  88. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/opencode.py +0 -0
  89. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/vscode.py +0 -0
  90. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
  91. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  92. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
  93. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  94. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/bundle/__init__.py +0 -0
  95. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
  96. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/bundle/packer.py +0 -0
  97. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/__init__.py +0 -0
  98. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/http_cache.py +0 -0
  99. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/integrity.py +0 -0
  100. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/locking.py +0 -0
  101. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/paths.py +0 -0
  102. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/cache/url_normalize.py +0 -0
  103. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/__init__.py +0 -0
  104. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/_helpers.py +0 -0
  105. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/audit.py +0 -0
  106. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/cache.py +0 -0
  107. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/compile/__init__.py +0 -0
  108. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/compile/cli.py +0 -0
  109. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/compile/watcher.py +0 -0
  110. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/deps/__init__.py +0 -0
  111. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/deps/_utils.py +0 -0
  112. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/experimental.py +0 -0
  113. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/init.py +0 -0
  114. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/list_cmd.py +0 -0
  115. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/check.py +0 -0
  116. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
  117. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/init.py +0 -0
  118. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
  119. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
  120. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
  121. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
  122. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
  123. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
  124. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
  125. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
  126. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/mcp.py +0 -0
  127. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/policy.py +0 -0
  128. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/prune.py +0 -0
  129. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/targets.py +0 -0
  130. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
  131. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
  132. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
  133. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/commands/view.py +0 -0
  134. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/__init__.py +0 -0
  135. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/agents_compiler.py +0 -0
  136. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/build_id.py +0 -0
  137. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/claude_formatter.py +0 -0
  138. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/constants.py +0 -0
  139. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/constitution.py +0 -0
  140. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/constitution_block.py +0 -0
  141. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
  142. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/distributed_compiler.py +0 -0
  143. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
  144. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/injector.py +0 -0
  145. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/output_writer.py +0 -0
  146. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/compilation/template_builder.py +0 -0
  147. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/config.py +0 -0
  148. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/constants.py +0 -0
  149. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/__init__.py +0 -0
  150. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/azure_cli.py +0 -0
  151. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/build_orchestrator.py +0 -0
  152. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/conflict_detector.py +0 -0
  153. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/docker_args.py +0 -0
  154. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/experimental.py +0 -0
  155. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/null_logger.py +0 -0
  156. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/operations.py +0 -0
  157. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/safe_installer.py +0 -0
  158. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/scope.py +0 -0
  159. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/script_runner.py +0 -0
  160. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/core/target_detection.py +0 -0
  161. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/__init__.py +0 -0
  162. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/aggregator.py +0 -0
  163. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
  164. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/dependency_graph.py +0 -0
  165. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/git_remote_ops.py +0 -0
  166. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/installed_package.py +0 -0
  167. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/lockfile.py +0 -0
  168. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/plugin_parser.py +0 -0
  169. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/transport_selection.py +0 -0
  170. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/deps/verifier.py +0 -0
  171. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/drift.py +0 -0
  172. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/factory.py +0 -0
  173. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/__init__.py +0 -0
  174. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/cache_pin.py +0 -0
  175. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/context.py +0 -0
  176. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/drift.py +0 -0
  177. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/heals/__init__.py +0 -0
  178. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/heals/base.py +0 -0
  179. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
  180. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
  181. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/helpers/__init__.py +0 -0
  182. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
  183. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/insecure_policy.py +0 -0
  184. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/__init__.py +0 -0
  185. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/args.py +0 -0
  186. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
  187. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/registry.py +0 -0
  188. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/mcp/warnings.py +0 -0
  189. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/__init__.py +0 -0
  190. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/cleanup.py +0 -0
  191. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/download.py +0 -0
  192. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/finalize.py +0 -0
  193. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/heal.py +0 -0
  194. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/integrate.py +0 -0
  195. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/local_content.py +0 -0
  196. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/lockfile.py +0 -0
  197. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
  198. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
  199. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
  200. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/phases/resolve.py +0 -0
  201. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/presentation/__init__.py +0 -0
  202. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
  203. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/skill_path_migration.py +0 -0
  204. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/sources.py +0 -0
  205. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/summary.py +0 -0
  206. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/install/template.py +0 -0
  207. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/__init__.py +0 -0
  208. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/cleanup.py +0 -0
  209. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/command_integrator.py +0 -0
  210. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
  211. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/coverage.py +0 -0
  212. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/dispatch.py +0 -0
  213. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/hook_integrator.py +0 -0
  214. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/instruction_integrator.py +0 -0
  215. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/mcp_integrator.py +0 -0
  216. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/skill_integrator.py +0 -0
  217. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/skill_transformer.py +0 -0
  218. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/integration/utils.py +0 -0
  219. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/__init__.py +0 -0
  220. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
  221. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/_io.py +0 -0
  222. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/builder.py +0 -0
  223. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/errors.py +0 -0
  224. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
  225. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/init_template.py +0 -0
  226. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/migration.py +0 -0
  227. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
  228. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/publisher.py +0 -0
  229. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
  230. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/registry.py +0 -0
  231. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/semver.py +0 -0
  232. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
  233. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
  234. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/validator.py +0 -0
  235. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/version_pins.py +0 -0
  236. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/yml_editor.py +0 -0
  237. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/marketplace/yml_schema.py +0 -0
  238. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/__init__.py +0 -0
  239. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/dependency/__init__.py +0 -0
  240. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/dependency/mcp.py +0 -0
  241. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/dependency/types.py +0 -0
  242. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/plugin.py +0 -0
  243. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/results.py +0 -0
  244. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/models/validation.py +0 -0
  245. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/output/__init__.py +0 -0
  246. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/output/formatters.py +0 -0
  247. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/output/models.py +0 -0
  248. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/output/script_formatters.py +0 -0
  249. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/__init__.py +0 -0
  250. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/_help_text.py +0 -0
  251. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/ci_checks.py +0 -0
  252. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/install_preflight.py +0 -0
  253. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/matcher.py +0 -0
  254. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/models.py +0 -0
  255. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/outcome_routing.py +0 -0
  256. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/policy/project_config.py +0 -0
  257. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/primitives/__init__.py +0 -0
  258. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/primitives/discovery.py +0 -0
  259. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/primitives/models.py +0 -0
  260. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/primitives/parser.py +0 -0
  261. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/registry/__init__.py +0 -0
  262. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/registry/client.py +0 -0
  263. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/registry/integration.py +0 -0
  264. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/registry/operations.py +0 -0
  265. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/__init__.py +0 -0
  266. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/base.py +0 -0
  267. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
  268. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
  269. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/factory.py +0 -0
  270. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
  271. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/runtime/manager.py +0 -0
  272. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/security/__init__.py +0 -0
  273. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/security/audit_report.py +0 -0
  274. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/security/content_scanner.py +0 -0
  275. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/security/file_scanner.py +0 -0
  276. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/security/gate.py +0 -0
  277. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/update_policy.py +0 -0
  278. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/__init__.py +0 -0
  279. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/atomic_io.py +0 -0
  280. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/content_hash.py +0 -0
  281. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/diagnostics.py +0 -0
  282. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/exclude.py +0 -0
  283. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/file_ops.py +0 -0
  284. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/git_env.py +0 -0
  285. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/guards.py +0 -0
  286. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/helpers.py +0 -0
  287. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/install_tui.py +0 -0
  288. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/normalization.py +0 -0
  289. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/path_security.py +0 -0
  290. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/paths.py +0 -0
  291. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/reflink.py +0 -0
  292. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/short_sha.py +0 -0
  293. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/subprocess_env.py +0 -0
  294. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/version_checker.py +0 -0
  295. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/utils/yaml_io.py +0 -0
  296. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/version.py +0 -0
  297. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/workflow/__init__.py +0 -0
  298. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/workflow/discovery.py +0 -0
  299. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/workflow/parser.py +0 -0
  300. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli/workflow/runner.py +0 -0
  301. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  302. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
  303. {apm_cli-0.12.3 → apm_cli-0.13.0}/src/apm_cli.egg-info/top_level.txt +0 -0
  304. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_apm_package_models.py +0 -0
  305. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_apm_resolver.py +0 -0
  306. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_codex_docker_args_fix.py +0 -0
  307. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
  308. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_collision_integration.py +0 -0
  309. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_console.py +0 -0
  310. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_distributed_compilation.py +0 -0
  311. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_empty_string_and_defaults.py +0 -0
  312. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_enhanced_discovery.py +0 -0
  313. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_github_downloader_token_precedence.py +0 -0
  314. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_lockfile.py +0 -0
  315. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_runnable_prompts.py +0 -0
  316. {apm_cli-0.12.3 → apm_cli-0.13.0}/tests/test_runtime_manager_token_precedence.py +0 -0
  317. {apm_cli-0.12.3 → apm_cli-0.13.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.3
3
+ Version: 0.13.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,6 +56,7 @@ 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"
@@ -133,7 +134,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
133
134
  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
135
 
135
136
  - **[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
137
+ - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
137
138
  - **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
138
139
  - **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
139
140
  - **[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.3"
7
+ version = "0.13.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,6 +44,7 @@ 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",
@@ -128,11 +129,21 @@ warn_return_any = true
128
129
  warn_unused_configs = true
129
130
 
130
131
  [tool.pytest.ini_options]
131
- addopts = "-m 'not benchmark'"
132
+ addopts = "-m 'not benchmark and not live'"
132
133
  markers = [
133
134
  "integration: marks tests as integration tests that may require network access",
134
135
  "live: marks tests that hit real GitHub repos (requires network + optional GITHUB_TOKEN)",
135
136
  "slow: marks tests as slow running tests",
136
137
  "benchmark: marks performance benchmark tests (deselected by default, run with -m benchmark)",
138
+ "requires_e2e_mode: requires APM_E2E_TESTS=1",
139
+ "requires_github_token: requires GITHUB_APM_PAT or GITHUB_TOKEN",
140
+ "requires_ado_pat: requires ADO_APM_PAT",
141
+ "requires_ado_bearer: requires az CLI logged in + APM_TEST_ADO_BEARER=1",
142
+ "requires_network_integration: requires APM_RUN_INTEGRATION_TESTS=1",
143
+ "requires_apm_binary: requires built apm binary on PATH (or APM_BINARY_PATH)",
144
+ "requires_runtime_codex: requires codex runtime installed",
145
+ "requires_runtime_copilot: requires GitHub Copilot CLI runtime installed",
146
+ "requires_runtime_llm: requires llm runtime installed",
147
+ "requires_inference: requires APM_RUN_INFERENCE_TESTS=1 + model API access",
137
148
  ]
138
149
 
@@ -1208,8 +1208,10 @@ class CopilotClientAdapter(MCPClientAdapter):
1208
1208
  def _is_github_server(self, server_name, url):
1209
1209
  """Securely determine if a server is a GitHub MCP server.
1210
1210
 
1211
- This method uses proper URL parsing and hostname validation to prevent
1212
- security vulnerabilities from substring-based checks.
1211
+ Uses proper URL parsing and hostname validation to prevent token
1212
+ injection via poisoned registry entries. Both the server name and
1213
+ the URL hostname must match the GitHub allowlists before a GitHub
1214
+ token is injected.
1213
1215
 
1214
1216
  Args:
1215
1217
  server_name (str): Name of the MCP server.
@@ -1220,7 +1222,6 @@ class CopilotClientAdapter(MCPClientAdapter):
1220
1222
  """
1221
1223
  from urllib.parse import urlparse
1222
1224
 
1223
- # Check server name against an allowlist of known GitHub MCP servers
1224
1225
  github_server_names = [
1225
1226
  "github-mcp-server",
1226
1227
  "github",
@@ -1228,23 +1229,33 @@ class CopilotClientAdapter(MCPClientAdapter):
1228
1229
  "github-copilot-mcp-server",
1229
1230
  ]
1230
1231
 
1231
- # Exact match check for server names (case-insensitive)
1232
- if server_name and server_name.lower() in [name.lower() for name in github_server_names]:
1233
- return True
1234
-
1235
- # If URL is provided, validate the hostname
1232
+ def _is_github_mcp_hostname(hostname: str) -> bool:
1233
+ """Check if *hostname* belongs to GitHub (cloud, enterprise, or Copilot API)."""
1234
+ if is_github_hostname(hostname):
1235
+ return True
1236
+ h = hostname.lower()
1237
+ # Subdomains of github.com (e.g. api.github.com)
1238
+ if h.endswith(".github.com"):
1239
+ return True
1240
+ # Copilot API hosts (e.g. api.githubcopilot.com, api.business.githubcopilot.com)
1241
+ return h == "githubcopilot.com" or h.endswith(".githubcopilot.com")
1242
+
1243
+ name_matches = bool(
1244
+ server_name and server_name.lower() in [n.lower() for n in github_server_names]
1245
+ )
1246
+
1247
+ # Parse and validate hostname from URL
1248
+ hostname = None
1236
1249
  if url:
1237
1250
  try:
1238
1251
  parsed_url = urlparse(url)
1252
+ # Reject non-HTTPS URLs to prevent cleartext token leakage
1253
+ if parsed_url.scheme and parsed_url.scheme.lower() != "https":
1254
+ return False
1239
1255
  hostname = parsed_url.hostname
1240
-
1241
- if hostname:
1242
- # Use helper to determine whether hostname is a GitHub host (cloud or enterprise)
1243
- if is_github_hostname(hostname):
1244
- return True
1245
-
1246
1256
  except Exception:
1247
- # If URL parsing fails, assume it's not a GitHub server
1248
1257
  return False
1249
1258
 
1250
- return False
1259
+ host_matches = bool(hostname and _is_github_mcp_hostname(hostname))
1260
+
1261
+ return name_matches and host_matches
@@ -0,0 +1,326 @@
1
+ """Cursor IDE implementation of MCP client adapter.
2
+
3
+ Cursor uses the standard ``mcpServers`` JSON format at ``.cursor/mcp.json``
4
+ (repo-local). Unlike the Copilot adapter, this adapter emits Cursor-native
5
+ transport discriminators (``type: stdio`` / ``type: http``) and omits
6
+ Copilot-only fields (``tools``, ``id``).
7
+
8
+ APM only writes to ``.cursor/mcp.json`` when the ``.cursor/`` directory
9
+ already exists -- Cursor support is opt-in.
10
+ """
11
+
12
+ import json
13
+ import os
14
+ from pathlib import Path
15
+
16
+ from ...core.docker_args import DockerArgsProcessor
17
+ from ...core.token_manager import GitHubTokenManager
18
+ from .copilot import CopilotClientAdapter
19
+
20
+
21
+ class CursorClientAdapter(CopilotClientAdapter):
22
+ """Cursor IDE MCP client adapter.
23
+
24
+ Inherits config-path and read/write logic from
25
+ :class:`CopilotClientAdapter` but overrides ``_format_server_config`` to
26
+ emit Cursor-native transport discriminators instead of Copilot-only fields.
27
+ """
28
+
29
+ supports_user_scope: bool = False
30
+ target_name: str = "cursor"
31
+ mcp_servers_key: str = "mcpServers"
32
+
33
+ # Cursor's mcp.json runtime-substitution support has not yet been
34
+ # individually audited (see #1152). Pin to the legacy install-time
35
+ # resolution behaviour so this adapter is unchanged by the Copilot
36
+ # security fix; revisit in a follow-up.
37
+ _supports_runtime_env_substitution: bool = False
38
+
39
+ # ------------------------------------------------------------------ #
40
+ # Config path
41
+ # ------------------------------------------------------------------ #
42
+
43
+ def get_config_path(self):
44
+ """Return the path to ``.cursor/mcp.json`` in the repository root.
45
+
46
+ Unlike the Copilot adapter this is a **repo-local** path. The
47
+ ``.cursor/`` directory is *not* created automatically -- APM only
48
+ writes here when the directory already exists.
49
+ """
50
+ cursor_dir = self.project_root / ".cursor"
51
+ return str(cursor_dir / "mcp.json")
52
+
53
+ # ------------------------------------------------------------------ #
54
+ # Config read / write -- override to avoid auto-creating the directory
55
+ # ------------------------------------------------------------------ #
56
+
57
+ def update_config(self, config_updates):
58
+ """Merge *config_updates* into the ``mcpServers`` section.
59
+
60
+ The ``.cursor/`` directory must already exist; if it does not, this
61
+ method returns silently (opt-in behaviour).
62
+ """
63
+ config_path = Path(self.get_config_path())
64
+
65
+ # Opt-in: only write when .cursor/ already exists
66
+ if not config_path.parent.exists():
67
+ return
68
+
69
+ current_config = self.get_current_config()
70
+ if "mcpServers" not in current_config:
71
+ current_config["mcpServers"] = {}
72
+
73
+ current_config["mcpServers"].update(config_updates)
74
+
75
+ with open(config_path, "w", encoding="utf-8") as f:
76
+ json.dump(current_config, f, indent=2)
77
+
78
+ def get_current_config(self):
79
+ """Read the current ``.cursor/mcp.json`` contents."""
80
+ config_path = self.get_config_path()
81
+
82
+ if not os.path.exists(config_path):
83
+ return {}
84
+
85
+ try:
86
+ with open(config_path, encoding="utf-8") as f:
87
+ return json.load(f)
88
+ except (OSError, json.JSONDecodeError):
89
+ return {}
90
+
91
+ # ------------------------------------------------------------------ #
92
+ # _format_server_config -- Cursor-native schema
93
+ # ------------------------------------------------------------------ #
94
+
95
+ def _format_server_config(self, server_info, env_overrides=None, runtime_vars=None):
96
+ """Format server info into Cursor MCP configuration.
97
+
98
+ Cursor uses a transport discriminator field ``type`` to determine how
99
+ to launch an MCP server:
100
+
101
+ - ``"type": "stdio"`` for local process servers (raw stdio or packages)
102
+ - ``"type": "http"`` for remote HTTP/SSE servers
103
+
104
+ Copilot-only fields ``tools`` and ``id`` are never emitted.
105
+
106
+ Args:
107
+ server_info: Server information from registry.
108
+ env_overrides: Pre-collected environment variable overrides.
109
+ runtime_vars: Pre-collected runtime variable values.
110
+
111
+ Returns:
112
+ dict suitable for writing to ``.cursor/mcp.json``.
113
+ """
114
+ if runtime_vars is None:
115
+ runtime_vars = {}
116
+
117
+ config: dict = {}
118
+
119
+ # --- raw stdio (self-defined deps) ---
120
+ raw = server_info.get("_raw_stdio")
121
+ if raw:
122
+ config["type"] = "stdio"
123
+ config["command"] = raw["command"]
124
+ resolved_env_for_args: dict = {}
125
+ if raw.get("env"):
126
+ resolved_env_for_args = self._resolve_environment_variables(
127
+ raw["env"], env_overrides=env_overrides
128
+ )
129
+ config["env"] = resolved_env_for_args
130
+ self._warn_input_variables(raw["env"], server_info.get("name", ""), "Cursor")
131
+ args = raw.get("args") or []
132
+ config["args"] = [
133
+ self._resolve_variable_placeholders(arg, resolved_env_for_args, runtime_vars)
134
+ if isinstance(arg, str)
135
+ else arg
136
+ for arg in args
137
+ ]
138
+ return config
139
+
140
+ # --- remote endpoints ---
141
+ remotes = server_info.get("remotes", [])
142
+ if remotes:
143
+ remote = self._select_remote_with_url(remotes) or remotes[0]
144
+
145
+ transport = (remote.get("transport_type") or "").strip()
146
+ if not transport:
147
+ transport = "http"
148
+ elif transport not in ("sse", "http", "streamable-http"):
149
+ raise ValueError(
150
+ f"Unsupported remote transport '{transport}' for Cursor. "
151
+ f"Server: {server_info.get('name', 'unknown')}. "
152
+ f"Supported transports: http, sse, streamable-http."
153
+ )
154
+
155
+ config["type"] = "http"
156
+ config["url"] = (remote.get("url") or "").strip()
157
+
158
+ # Add authentication headers for GitHub MCP server
159
+ server_name = server_info.get("name", "")
160
+ is_github_server = self._is_github_server(server_name, remote.get("url", ""))
161
+
162
+ if is_github_server:
163
+ _tm = GitHubTokenManager()
164
+ github_token = _tm.get_token_for_purpose("copilot") or os.getenv(
165
+ "GITHUB_PERSONAL_ACCESS_TOKEN"
166
+ )
167
+ if github_token:
168
+ config["headers"] = {"Authorization": f"Bearer {github_token}"}
169
+
170
+ # Add any additional headers from registry if present
171
+ headers = remote.get("headers", [])
172
+ if headers:
173
+ if "headers" not in config:
174
+ config["headers"] = {}
175
+ for header in headers:
176
+ header_name = header.get("name", "")
177
+ header_value = header.get("value", "")
178
+ if header_name and header_value:
179
+ # Prevent registry-supplied headers from overriding
180
+ # the injected GitHub token
181
+ if header_name == "Authorization" and is_github_server:
182
+ continue
183
+ resolved_value = self._resolve_env_variable(
184
+ header_name, header_value, env_overrides
185
+ )
186
+ config["headers"][header_name] = resolved_value
187
+
188
+ # Warn about unresolvable ${input:...} references in headers
189
+ if config.get("headers"):
190
+ self._warn_input_variables(config["headers"], server_info.get("name", ""), "Cursor")
191
+
192
+ return config
193
+
194
+ # --- local packages ---
195
+ packages = server_info.get("packages", [])
196
+
197
+ if not packages and not remotes:
198
+ raise ValueError(
199
+ f"MCP server has incomplete configuration in registry - "
200
+ f"no package information or remote endpoints available. "
201
+ f"This appears to be a temporary registry issue. "
202
+ f"Server: {server_info.get('name', 'unknown')}"
203
+ )
204
+
205
+ if packages:
206
+ package = self._select_best_package(packages)
207
+
208
+ if package:
209
+ registry_name = self._infer_registry_name(package)
210
+ package_name = package.get("name", "")
211
+ runtime_hint = package.get("runtime_hint", "")
212
+ runtime_arguments = package.get("runtime_arguments", [])
213
+ package_arguments = package.get("package_arguments", [])
214
+ env_vars = package.get("environment_variables", [])
215
+
216
+ resolved_env = self._resolve_environment_variables(env_vars, env_overrides)
217
+ processed_runtime_args = self._process_arguments(
218
+ runtime_arguments, resolved_env, runtime_vars
219
+ )
220
+ processed_package_args = self._process_arguments(
221
+ package_arguments, resolved_env, runtime_vars
222
+ )
223
+
224
+ config["type"] = "stdio"
225
+
226
+ if registry_name == "npm":
227
+ config["command"] = runtime_hint or "npx"
228
+ config["args"] = (
229
+ ["-y", package_name] + processed_runtime_args + processed_package_args # noqa: RUF005
230
+ )
231
+ if resolved_env:
232
+ config["env"] = resolved_env
233
+ elif registry_name == "docker":
234
+ config["command"] = "docker"
235
+ if processed_runtime_args:
236
+ config["args"] = self._inject_env_vars_into_docker_args(
237
+ processed_runtime_args, resolved_env
238
+ )
239
+ else:
240
+ config["args"] = DockerArgsProcessor.process_docker_args(
241
+ ["run", "-i", "--rm", package_name], resolved_env
242
+ )
243
+ elif registry_name == "pypi":
244
+ config["command"] = runtime_hint or "uvx"
245
+ config["args"] = (
246
+ [package_name] + processed_runtime_args + processed_package_args # noqa: RUF005
247
+ )
248
+ if resolved_env:
249
+ config["env"] = resolved_env
250
+ elif registry_name == "homebrew":
251
+ config["command"] = (
252
+ package_name.split("/")[-1] if "/" in package_name else package_name
253
+ )
254
+ config["args"] = processed_runtime_args + processed_package_args
255
+ if resolved_env:
256
+ config["env"] = resolved_env
257
+ else:
258
+ config["command"] = runtime_hint or package_name
259
+ config["args"] = processed_runtime_args + processed_package_args
260
+ if resolved_env:
261
+ config["env"] = resolved_env
262
+ else:
263
+ raise ValueError(
264
+ f"No supported package type found for Cursor. "
265
+ f"Server: {server_info.get('name', 'unknown')}. "
266
+ f"Available packages: "
267
+ f"{[p.get('registry_name', 'unknown') for p in packages]}."
268
+ )
269
+
270
+ return config
271
+
272
+ # ------------------------------------------------------------------ #
273
+ # configure_mcp_server -- thin override for the print label
274
+ # ------------------------------------------------------------------ #
275
+
276
+ def configure_mcp_server(
277
+ self,
278
+ server_url,
279
+ server_name=None,
280
+ enabled=True,
281
+ env_overrides=None,
282
+ server_info_cache=None,
283
+ runtime_vars=None,
284
+ ):
285
+ """Configure an MCP server in Cursor's ``.cursor/mcp.json``.
286
+
287
+ Delegates entirely to the parent implementation but prints a
288
+ Cursor-specific success message.
289
+ """
290
+ if not server_url:
291
+ print("Error: server_url cannot be empty")
292
+ return False
293
+
294
+ # Opt-in: skip silently when .cursor/ does not exist
295
+ cursor_dir = self.project_root / ".cursor"
296
+ if not cursor_dir.exists():
297
+ return True # nothing to do, not an error
298
+
299
+ try:
300
+ # Use cached server info if available, otherwise fetch from registry
301
+ if server_info_cache and server_url in server_info_cache:
302
+ server_info = server_info_cache[server_url]
303
+ else:
304
+ server_info = self.registry_client.find_server_by_reference(server_url)
305
+
306
+ if not server_info:
307
+ print(f"Error: MCP server '{server_url}' not found in registry")
308
+ return False
309
+
310
+ # Determine config key
311
+ if server_name:
312
+ config_key = server_name
313
+ elif "/" in server_url:
314
+ config_key = server_url.split("/")[-1]
315
+ else:
316
+ config_key = server_url
317
+
318
+ server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
319
+ self.update_config({config_key: server_config})
320
+
321
+ print(f"Successfully configured MCP server '{config_key}' for Cursor")
322
+ return True
323
+
324
+ except Exception as e:
325
+ print(f"Error configuring MCP server: {e}")
326
+ return False
@@ -152,10 +152,21 @@ def _looks_like_legacy_apm_bundle(path: Path) -> bool:
152
152
  for member in tar.getmembers():
153
153
  if member.issym() or member.islnk():
154
154
  return False
155
+ name = member.name
156
+ if (
157
+ name.startswith("/")
158
+ or PureWindowsPath(name).drive
159
+ or PureWindowsPath(name).is_absolute()
160
+ ):
161
+ return False
162
+ try:
163
+ validate_path_segments(name, context="tar member")
164
+ except PathTraversalError:
165
+ return False
155
166
  if sys.version_info >= (3, 12):
156
167
  tar.extractall(tmp, filter="data")
157
168
  else:
158
- tar.extractall(tmp) # noqa: S202
169
+ tar.extractall(tmp) # noqa: S202 -- validated above
159
170
  # Locate the inner directory (apm pack uses arcname=<bundle-name>)
160
171
  root = tmp
161
172
  children = [p for p in tmp.iterdir() if p.is_dir()]
@@ -365,6 +376,7 @@ def check_target_mismatch(
365
376
  Returns ``None`` when:
366
377
 
367
378
  - ``bundle_targets`` is empty (pre-constraint bundle, no metadata), OR
379
+ - ``bundle_targets`` contains ``"all"`` (target-agnostic bundle), OR
368
380
  - ``install_targets`` is a superset of ``bundle_targets``.
369
381
 
370
382
  Otherwise returns a human-readable warning naming the missing targets.
@@ -372,6 +384,11 @@ def check_target_mismatch(
372
384
  if not bundle_targets:
373
385
  return None
374
386
  bundle_set = {t.strip() for t in bundle_targets if t and t.strip()}
387
+ # Issue #1207: ``"all"`` (or empty after stripping) means target-agnostic.
388
+ # Such bundles cover any install target, so no mismatch warning is
389
+ # appropriate.
390
+ if "all" in bundle_set:
391
+ return None
375
392
  install_set = {t.strip() for t in install_targets if t and t.strip()}
376
393
  missing = sorted(bundle_set - install_set)
377
394
  if not missing:
@@ -636,10 +636,17 @@ def export_plugin_bundle(
636
636
  if rel == "apm.lock.yaml":
637
637
  continue
638
638
  bundle_files[rel] = hashlib.sha256(fp.read_bytes()).hexdigest()
639
+ # Issue #1207 D1: do NOT silently substitute ``"copilot"`` when
640
+ # ``target`` is missing. Bundles are target-agnostic at install
641
+ # time; ``pack.target`` is recorded as informational metadata only.
642
+ # Falling back to ``"all"`` preserves the lockfile-filter shape
643
+ # (which uses target prefixes to narrow each dep's deployed_files
644
+ # list to the union of supported targets) without locking the
645
+ # bundle to a single client.
639
646
  enriched_yaml = enrich_lockfile_for_pack(
640
647
  lockfile,
641
648
  "plugin",
642
- target or "copilot",
649
+ target or "all",
643
650
  bundle_files=bundle_files,
644
651
  )
645
652
  (bundle_dir / "apm.lock.yaml").write_text(enriched_yaml, encoding="utf-8")
@@ -5,10 +5,11 @@ import sys
5
5
  import tarfile
6
6
  import tempfile
7
7
  from dataclasses import dataclass, field
8
- from pathlib import Path
8
+ from pathlib import Path, PureWindowsPath
9
9
  from typing import Dict, List # noqa: F401, UP035
10
10
 
11
11
  from ..deps.lockfile import LEGACY_LOCKFILE_NAME, LOCKFILE_NAME, LockFile
12
+ from ..utils.path_security import PathTraversalError, validate_path_segments
12
13
 
13
14
 
14
15
  @dataclass
@@ -64,10 +65,21 @@ def unpack_bundle(
64
65
  with tarfile.open(bundle_path, "r:gz") as tar:
65
66
  # Security: prevent path traversal and special entries
66
67
  for member in tar.getmembers():
67
- if member.name.startswith("/") or ".." in member.name:
68
- raise ValueError(f"Refusing to extract path-traversal entry: {member.name}")
68
+ name = member.name
69
+ if (
70
+ name.startswith("/")
71
+ or PureWindowsPath(name).drive
72
+ or PureWindowsPath(name).is_absolute()
73
+ ):
74
+ raise ValueError(f"Refusing to extract path-traversal entry: {name}")
75
+ try:
76
+ validate_path_segments(name, context="tar member")
77
+ except PathTraversalError:
78
+ raise ValueError(
79
+ f"Refusing to extract path-traversal entry: {name}"
80
+ ) from None
69
81
  if member.issym() or member.islnk():
70
- raise ValueError(f"Refusing to extract symlink/hardlink: {member.name}")
82
+ raise ValueError(f"Refusing to extract symlink/hardlink: {name}")
71
83
  # filter="data" was added in Python 3.12; use it when available
72
84
  if sys.version_info >= (3, 12):
73
85
  tar.extractall(temp_dir, filter="data")
@@ -158,6 +158,9 @@ class GitCache:
158
158
  from ..utils.git_env import get_git_executable, git_subprocess_env
159
159
 
160
160
  git_exe = get_git_executable()
161
+ # auth-delegated: cache-layer ref resolution runs after lockfile
162
+ # already pinned the commit; no PAT->bearer fallback applies here
163
+ # (env is sanitized, no embedded creds).
161
164
  cmd = [git_exe, "ls-remote", url]
162
165
  if ref:
163
166
  cmd.append(ref)
@@ -35,6 +35,7 @@ from apm_cli.commands.policy import policy
35
35
  from apm_cli.commands.prune import prune
36
36
  from apm_cli.commands.run import preview, run
37
37
  from apm_cli.commands.runtime import runtime
38
+ from apm_cli.commands.self_update import self_update
38
39
  from apm_cli.commands.targets import targets
39
40
  from apm_cli.commands.uninstall import uninstall
40
41
  from apm_cli.commands.update import update
@@ -90,6 +91,7 @@ cli.add_command(install)
90
91
  cli.add_command(uninstall)
91
92
  cli.add_command(prune)
92
93
  cli.add_command(update)
94
+ cli.add_command(self_update)
93
95
  cli.add_command(compile_cmd, name="compile")
94
96
  cli.add_command(run)
95
97
  cli.add_command(preview)
@@ -25,7 +25,18 @@ def set_skill_subset_for_entry(
25
25
  Returns True if file was modified.
26
26
  """
27
27
  data = load_yaml(manifest_path) or {}
28
- deps_section = data.get("dependencies", {})
28
+ deps_section = data.get("dependencies")
29
+ if deps_section is None:
30
+ deps_section = {}
31
+ if not isinstance(deps_section, dict):
32
+ raise ValueError(
33
+ f"Invalid 'dependencies' in {manifest_path}: expected a mapping "
34
+ f"with 'apm:' key, got {type(deps_section).__name__}. "
35
+ "Use the structured format:\n"
36
+ " dependencies:\n"
37
+ " apm:\n"
38
+ " - owner/repo"
39
+ )
29
40
  apm_deps = deps_section.get("apm", [])
30
41
  if not apm_deps:
31
42
  return False
@@ -53,7 +53,12 @@ def _get_config_getters():
53
53
 
54
54
  def _valid_config_keys() -> str:
55
55
  """Return valid config keys for messages."""
56
- return ", ".join(["auto-integrate", "temp-dir", "copilot-cowork-skills-dir"])
56
+ from ..core.experimental import is_enabled
57
+
58
+ keys = ["auto-integrate", "temp-dir"]
59
+ if is_enabled("copilot_cowork"):
60
+ keys.append("copilot-cowork-skills-dir")
61
+ return ", ".join(keys)
57
62
 
58
63
 
59
64
  @click.group(help="Configure APM CLI", invoke_without_command=True)