apm-cli 0.19.0__tar.gz → 0.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. {apm_cli-0.19.0/src/apm_cli.egg-info → apm_cli-0.20.0}/PKG-INFO +4 -4
  2. {apm_cli-0.19.0 → apm_cli-0.20.0}/README.md +3 -3
  3. {apm_cli-0.19.0 → apm_cli-0.20.0}/pyproject.toml +1 -1
  4. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/base.py +18 -11
  5. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/copilot.py +10 -2
  6. apm_cli-0.20.0/src/apm_cli/adapters/client/hermes.py +210 -0
  7. apm_cli-0.20.0/src/apm_cli/adapters/client/kiro.py +226 -0
  8. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/vscode.py +115 -30
  9. apm_cli-0.20.0/src/apm_cli/bootstrap_mirror.py +101 -0
  10. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/local_bundle.py +57 -62
  11. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/lockfile_enrichment.py +3 -0
  12. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/packer.py +25 -7
  13. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/plugin_exporter.py +21 -13
  14. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/unpacker.py +46 -29
  15. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/cli.py +11 -3
  16. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/cli.py +7 -2
  17. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/install.py +16 -16
  18. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/__init__.py +151 -22
  19. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/outdated.py +57 -6
  20. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/pack.py +79 -5
  21. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/self_update.py +87 -26
  22. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/update.py +203 -28
  23. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/agents_compiler.py +190 -5
  24. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/claude_formatter.py +14 -7
  25. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/distributed_compiler.py +241 -26
  26. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/apm_yml.py +1 -0
  27. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/auth.py +66 -11
  28. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/build_orchestrator.py +2 -0
  29. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/experimental.py +10 -0
  30. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/target_detection.py +34 -5
  31. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/apm_resolver.py +155 -43
  32. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/download_strategies.py +215 -54
  33. apm_cli-0.20.0/src/apm_cli/deps/git_file_transport.py +246 -0
  34. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_reference_resolver.py +18 -3
  35. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_remote_ops.py +24 -3
  36. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/github_downloader.py +22 -4
  37. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/github_downloader_validation.py +5 -1
  38. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/host_backends.py +18 -6
  39. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/lockfile.py +64 -5
  40. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/path_anchoring.py +4 -4
  41. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/client.py +9 -9
  42. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/extractor.py +7 -33
  43. apm_cli-0.20.0/src/apm_cli/deps/revision_pins.py +262 -0
  44. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/factory.py +4 -0
  45. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/local_content.py +7 -7
  46. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/lockfile.py +20 -0
  47. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/targets.py +59 -8
  48. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/validation.py +8 -1
  49. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/hook_integrator.py +39 -1
  50. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/instruction_integrator.py +52 -0
  51. apm_cli-0.20.0/src/apm_cli/integration/kiro_hook_integrator.py +322 -0
  52. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/mcp_integrator.py +24 -3
  53. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/mcp_integrator_install.py +142 -111
  54. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/targets.py +92 -11
  55. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/builder.py +225 -46
  56. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/client.py +250 -15
  57. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/models.py +48 -4
  58. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/output_mappers.py +118 -55
  59. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/resolver.py +18 -0
  60. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/yml_editor.py +19 -22
  61. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/yml_schema.py +122 -17
  62. apm_cli-0.20.0/src/apm_cli/models/dependency/identity.py +112 -0
  63. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/reference.py +151 -43
  64. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/types.py +2 -1
  65. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/policy_checks.py +1 -0
  66. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/operations.py +47 -18
  67. apm_cli-0.20.0/src/apm_cli/utils/archive.py +380 -0
  68. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/version_checker.py +66 -21
  69. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/yaml_io.py +40 -0
  70. {apm_cli-0.19.0 → apm_cli-0.20.0/src/apm_cli.egg-info}/PKG-INFO +4 -4
  71. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/SOURCES.txt +9 -0
  72. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_apm_resolver.py +141 -1
  73. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_github_downloader.py +123 -41
  74. apm_cli-0.20.0/tests/test_gitlab_git_transport.py +663 -0
  75. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_lockfile.py +71 -1
  76. {apm_cli-0.19.0 → apm_cli-0.20.0}/AUTHORS +0 -0
  77. {apm_cli-0.19.0 → apm_cli-0.20.0}/LICENSE +0 -0
  78. {apm_cli-0.19.0 → apm_cli-0.20.0}/NOTICE +0 -0
  79. {apm_cli-0.19.0 → apm_cli-0.20.0}/setup.cfg +0 -0
  80. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/__init__.py +0 -0
  81. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/__init__.py +0 -0
  82. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/__init__.py +0 -0
  83. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
  84. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/claude.py +0 -0
  85. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/codex.py +0 -0
  86. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/cursor.py +0 -0
  87. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/gemini.py +0 -0
  88. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/intellij.py +0 -0
  89. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/opencode.py +0 -0
  90. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
  91. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  92. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
  93. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  94. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/__init__.py +0 -0
  95. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/__init__.py +0 -0
  96. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/git_cache.py +0 -0
  97. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/http_cache.py +0 -0
  98. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/integrity.py +0 -0
  99. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/locking.py +0 -0
  100. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/paths.py +0 -0
  101. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/url_normalize.py +0 -0
  102. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cli.py +0 -0
  103. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/__init__.py +0 -0
  104. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
  105. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/_helpers.py +0 -0
  106. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/audit.py +0 -0
  107. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/cache.py +0 -0
  108. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/__init__.py +0 -0
  109. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/watcher.py +0 -0
  110. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/config.py +0 -0
  111. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/__init__.py +0 -0
  112. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/_utils.py +0 -0
  113. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/why.py +0 -0
  114. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/doctor.py +0 -0
  115. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/experimental.py +0 -0
  116. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/find.py +0 -0
  117. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/init.py +0 -0
  118. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/list_cmd.py +0 -0
  119. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/lock.py +0 -0
  120. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/audit.py +0 -0
  121. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/check.py +0 -0
  122. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
  123. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/init.py +0 -0
  124. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
  125. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
  126. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
  127. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
  128. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
  129. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
  130. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
  131. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
  132. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/mcp.py +0 -0
  133. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
  134. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/plugin/init.py +0 -0
  135. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/policy.py +0 -0
  136. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/prune.py +0 -0
  137. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/publish.py +0 -0
  138. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/run.py +0 -0
  139. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/runtime.py +0 -0
  140. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/targets.py +0 -0
  141. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
  142. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
  143. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
  144. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/view.py +0 -0
  145. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/__init__.py +0 -0
  146. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/build_id.py +0 -0
  147. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constants.py +0 -0
  148. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constitution.py +0 -0
  149. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constitution_block.py +0 -0
  150. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
  151. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
  152. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/injector.py +0 -0
  153. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/link_resolver.py +0 -0
  154. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/managed_section.py +0 -0
  155. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/output_writer.py +0 -0
  156. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/template_builder.py +0 -0
  157. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/config.py +0 -0
  158. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/constants.py +0 -0
  159. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/__init__.py +0 -0
  160. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/azure_cli.py +0 -0
  161. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/command_logger.py +0 -0
  162. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/conflict_detector.py +0 -0
  163. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/docker_args.py +0 -0
  164. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/errors.py +0 -0
  165. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/install_audit.py +0 -0
  166. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/null_logger.py +0 -0
  167. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/operations.py +0 -0
  168. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/plugin_manifest.py +0 -0
  169. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/safe_installer.py +0 -0
  170. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/scope.py +0 -0
  171. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/script_runner.py +0 -0
  172. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/token_manager.py +0 -0
  173. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/__init__.py +0 -0
  174. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/_shared.py +0 -0
  175. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/aggregator.py +0 -0
  176. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
  177. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
  178. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/bare_cache.py +0 -0
  179. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/clone_engine.py +0 -0
  180. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/dependency_graph.py +0 -0
  181. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_auth_env.py +0 -0
  182. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_semver_resolver.py +0 -0
  183. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/installed_package.py +0 -0
  184. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/outdated_row.py +0 -0
  185. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/package_validator.py +0 -0
  186. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/plugin_parser.py +0 -0
  187. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/__init__.py +0 -0
  188. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/auth.py +0 -0
  189. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
  190. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
  191. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/outdated.py +0 -0
  192. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/resolver.py +0 -0
  193. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/semver.py +0 -0
  194. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry_proxy.py +0 -0
  195. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
  196. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
  197. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/transport_selection.py +0 -0
  198. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/verifier.py +0 -0
  199. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/why_walker.py +0 -0
  200. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/drift.py +0 -0
  201. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/__init__.py +0 -0
  202. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
  203. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/cache_pin.py +0 -0
  204. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/context.py +0 -0
  205. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/drift.py +0 -0
  206. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/errors.py +0 -0
  207. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
  208. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/__init__.py +0 -0
  209. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/base.py +0 -0
  210. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
  211. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
  212. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/helpers/__init__.py +0 -0
  213. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
  214. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/insecure_policy.py +0 -0
  215. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
  216. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/lsp/__init__.py +0 -0
  217. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/lsp/integration.py +0 -0
  218. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/manifest_reconcile.py +0 -0
  219. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/__init__.py +0 -0
  220. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/args.py +0 -0
  221. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/command.py +0 -0
  222. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
  223. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/entry.py +0 -0
  224. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/registry.py +0 -0
  225. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/warnings.py +0 -0
  226. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/writer.py +0 -0
  227. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/package_resolution.py +0 -0
  228. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/package_selection.py +0 -0
  229. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/__init__.py +0 -0
  230. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/_redownload.py +0 -0
  231. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/_skip_logic.py +0 -0
  232. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/audit.py +0 -0
  233. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/cleanup.py +0 -0
  234. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/download.py +0 -0
  235. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/finalize.py +0 -0
  236. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/heal.py +0 -0
  237. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/integrate.py +0 -0
  238. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
  239. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
  240. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
  241. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/resolve.py +0 -0
  242. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/pipeline.py +0 -0
  243. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/plan.py +0 -0
  244. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/presentation/__init__.py +0 -0
  245. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
  246. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/registry_wiring.py +0 -0
  247. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/request.py +0 -0
  248. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/root_redirect.py +0 -0
  249. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/service.py +0 -0
  250. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/services.py +0 -0
  251. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/skill_path_migration.py +0 -0
  252. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/sources.py +0 -0
  253. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/summary.py +0 -0
  254. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/template.py +0 -0
  255. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/__init__.py +0 -0
  256. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/_shared.py +0 -0
  257. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/agent_integrator.py +0 -0
  258. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/base_integrator.py +0 -0
  259. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/cleanup.py +0 -0
  260. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/command_integrator.py +0 -0
  261. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
  262. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
  263. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
  264. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
  265. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
  266. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/coverage.py +0 -0
  267. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/dispatch.py +0 -0
  268. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/lsp_integrator.py +0 -0
  269. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
  270. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/prompt_integrator.py +0 -0
  271. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/skill_integrator.py +0 -0
  272. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/skill_transformer.py +0 -0
  273. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/utils.py +0 -0
  274. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/__init__.py +0 -0
  275. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
  276. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_io.py +0 -0
  277. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_shared.py +0 -0
  278. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/audit.py +0 -0
  279. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
  280. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/drift_check.py +0 -0
  281. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/errors.py +0 -0
  282. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
  283. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/init_template.py +0 -0
  284. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/migration.py +0 -0
  285. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
  286. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
  287. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/publisher.py +0 -0
  288. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
  289. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/registry.py +0 -0
  290. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/semver.py +0 -0
  291. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
  292. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
  293. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/validator.py +0 -0
  294. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_check.py +0 -0
  295. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_pins.py +0 -0
  296. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_resolver.py +0 -0
  297. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/__init__.py +0 -0
  298. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/apm_package.py +0 -0
  299. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/__init__.py +0 -0
  300. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/lsp.py +0 -0
  301. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/mcp.py +0 -0
  302. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/format_detection.py +0 -0
  303. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/plugin.py +0 -0
  304. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/results.py +0 -0
  305. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/validation.py +0 -0
  306. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/__init__.py +0 -0
  307. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/formatters.py +0 -0
  308. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/models.py +0 -0
  309. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/script_formatters.py +0 -0
  310. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/__init__.py +0 -0
  311. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
  312. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/_help_text.py +0 -0
  313. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/_shared.py +0 -0
  314. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/ci_checks.py +0 -0
  315. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/discovery.py +0 -0
  316. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/inheritance.py +0 -0
  317. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/install_preflight.py +0 -0
  318. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/matcher.py +0 -0
  319. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/models.py +0 -0
  320. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/outcome_routing.py +0 -0
  321. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/parser.py +0 -0
  322. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/project_config.py +0 -0
  323. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/schema.py +0 -0
  324. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/__init__.py +0 -0
  325. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/discovery.py +0 -0
  326. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/models.py +0 -0
  327. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/parser.py +0 -0
  328. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/__init__.py +0 -0
  329. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/client.py +0 -0
  330. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/integration.py +0 -0
  331. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/__init__.py +0 -0
  332. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/base.py +0 -0
  333. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
  334. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
  335. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/factory.py +0 -0
  336. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
  337. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/manager.py +0 -0
  338. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/utils.py +0 -0
  339. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/__init__.py +0 -0
  340. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/audit_report.py +0 -0
  341. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/content_scanner.py +0 -0
  342. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/__init__.py +0 -0
  343. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/base.py +0 -0
  344. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/gate.py +0 -0
  345. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/generic_sarif.py +0 -0
  346. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/options.py +0 -0
  347. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/registry.py +0 -0
  348. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/runner.py +0 -0
  349. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/sarif_ingest.py +0 -0
  350. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/skillspector.py +0 -0
  351. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/file_scanner.py +0 -0
  352. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/gate.py +0 -0
  353. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/update_policy.py +0 -0
  354. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/__init__.py +0 -0
  355. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/atomic_io.py +0 -0
  356. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/console.py +0 -0
  357. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/content_hash.py +0 -0
  358. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/diagnostics.py +0 -0
  359. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/exclude.py +0 -0
  360. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/file_ops.py +0 -0
  361. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/git_env.py +0 -0
  362. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/git_sparse.py +0 -0
  363. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/github_host.py +0 -0
  364. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/guards.py +0 -0
  365. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/helpers.py +0 -0
  366. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/install_tui.py +0 -0
  367. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/normalization.py +0 -0
  368. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/path_security.py +0 -0
  369. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/paths.py +0 -0
  370. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/patterns.py +0 -0
  371. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/perf_stats.py +0 -0
  372. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/reflink.py +0 -0
  373. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/short_sha.py +0 -0
  374. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/subprocess_env.py +0 -0
  375. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/version.py +0 -0
  376. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/__init__.py +0 -0
  377. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/discovery.py +0 -0
  378. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/parser.py +0 -0
  379. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/runner.py +0 -0
  380. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  381. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
  382. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/requires.txt +0 -0
  383. {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/top_level.txt +0 -0
  384. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_apm_package_models.py +0 -0
  385. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_codex_docker_args_fix.py +0 -0
  386. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
  387. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_collision_integration.py +0 -0
  388. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_console.py +0 -0
  389. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_distributed_compilation.py +0 -0
  390. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_empty_string_and_defaults.py +0 -0
  391. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_enhanced_discovery.py +0 -0
  392. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_github_downloader_token_precedence.py +0 -0
  393. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_runnable_prompts.py +0 -0
  394. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_runtime_manager_token_precedence.py +0 -0
  395. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_token_manager.py +0 -0
  396. {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_virtual_package_multi_install.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apm-cli
3
- Version: 0.19.0
3
+ Version: 0.20.0
4
4
  Summary: MCP configuration tool
5
5
  Author-email: Daniel Meppiel <user@example.com>
6
6
  License: MIT License
@@ -72,7 +72,7 @@ Dynamic: license-file
72
72
 
73
73
  Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
74
74
 
75
- GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf
75
+ GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf · Kiro
76
76
 
77
77
  **[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)** · **[Roadmap](https://github.com/orgs/microsoft/projects/2304)**
78
78
 
@@ -135,7 +135,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
135
135
 
136
136
  One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
137
137
 
138
- - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
138
+ - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf, Kiro
139
139
  - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
140
140
  - **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
141
141
  - **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
@@ -214,7 +214,7 @@ apm marketplace add github/awesome-copilot
214
214
  apm install azure-cloud-development@awesome-copilot
215
215
  ```
216
216
 
217
- Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, and Windsurf):
217
+ Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, Windsurf, and Kiro):
218
218
 
219
219
  ```bash
220
220
  apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
@@ -4,7 +4,7 @@
4
4
 
5
5
  Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
6
6
 
7
- GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf
7
+ GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf · Kiro
8
8
 
9
9
  **[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)** · **[Roadmap](https://github.com/orgs/microsoft/projects/2304)**
10
10
 
@@ -67,7 +67,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
67
67
 
68
68
  One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
69
69
 
70
- - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
70
+ - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf, Kiro
71
71
  - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
72
72
  - **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
73
73
  - **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
@@ -146,7 +146,7 @@ apm marketplace add github/awesome-copilot
146
146
  apm install azure-cloud-development@awesome-copilot
147
147
  ```
148
148
 
149
- Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, and Windsurf):
149
+ Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, Windsurf, and Kiro):
150
150
 
151
151
  ```bash
152
152
  apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apm-cli"
7
- version = "0.19.0"
7
+ version = "0.20.0"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,7 +4,7 @@ import os
4
4
  import re
5
5
  from abc import ABC, abstractmethod
6
6
  from pathlib import Path
7
- from typing import ClassVar
7
+ from typing import Any, ClassVar
8
8
 
9
9
  from ...utils.console import _rich_error, _rich_warning
10
10
 
@@ -29,6 +29,11 @@ _ENV_PLACEHOLDER_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>|" + _ENV_VAR_RE.pattern)
29
29
  _LEGACY_ANGLE_VAR_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>")
30
30
 
31
31
 
32
+ def registry_field_is_required(field: dict[str, Any]) -> bool:
33
+ """Return True unless registry metadata explicitly marks a field optional."""
34
+ return field.get("required", field.get("is_required", True)) is not False
35
+
36
+
32
37
  def _translate_env_placeholder(value):
33
38
  """Pure-textual translation of env-var placeholders to the canonical
34
39
  ``${VAR}`` runtime-substitution syntax.
@@ -481,7 +486,7 @@ class MCPClientAdapter(ABC):
481
486
  name = env_var.get("name", "")
482
487
  if not name:
483
488
  continue
484
- required = env_var.get("required", True)
489
+ required = registry_field_is_required(env_var)
485
490
 
486
491
  value = env_overrides.get(name) or os.getenv(name)
487
492
  if not value and required and not skip_prompting:
@@ -797,12 +802,15 @@ class MCPClientAdapter(ABC):
797
802
  )
798
803
 
799
804
  # First pass: identify variables with empty values to warn the user.
800
- empty_value_vars = [ev for ev in env_vars if ev.get("required") and not ev.get("value")]
805
+ empty_value_vars = [
806
+ ev for ev in env_vars if registry_field_is_required(ev) and not ev.get("value")
807
+ ]
801
808
  if empty_value_vars and skip_prompting:
802
809
  var_names = [ev.get("name") for ev in empty_value_vars]
803
810
  _rich_warning(
804
- f"Warning: The following required environment variables have no default "
805
- f"value and cannot be prompted in non-interactive mode: {var_names}"
811
+ f"Required environment variables have no default value and cannot be "
812
+ f"prompted in non-interactive mode: {var_names}. Set them in your "
813
+ "environment and rerun `apm install`."
806
814
  )
807
815
 
808
816
  for env_var in env_vars:
@@ -837,9 +845,9 @@ class MCPClientAdapter(ABC):
837
845
 
838
846
  # Priority 4: interactive prompt
839
847
  default_value = env_var.get("value", "")
840
- required = env_var.get("required", False)
848
+ required = registry_field_is_required(env_var)
841
849
 
842
- if not skip_prompting:
850
+ if not skip_prompting and required:
843
851
  from rich.prompt import Prompt
844
852
 
845
853
  description = env_var.get("description", "")
@@ -859,11 +867,10 @@ class MCPClientAdapter(ABC):
859
867
  resolved[name] = default_value
860
868
  elif required:
861
869
  _rich_warning(
862
- f"Warning: Required environment variable '{name}' could not be resolved. "
863
- f"The MCP server may not function correctly."
870
+ f"Required environment variable '{name}' could not be resolved. "
871
+ f"The MCP server may not function correctly. Set {name} in your "
872
+ "environment and rerun `apm install`."
864
873
  )
865
874
  resolved[name] = ""
866
- else:
867
- resolved[name] = default_value
868
875
 
869
876
  return resolved
@@ -26,6 +26,7 @@ from .base import (
26
26
  _extract_legacy_angle_vars,
27
27
  _has_env_placeholder,
28
28
  _stringify_env_literal,
29
+ registry_field_is_required,
29
30
  )
30
31
  from .base import (
31
32
  _translate_env_placeholder as _translate_env_placeholder,
@@ -698,6 +699,7 @@ class CopilotClientAdapter(MCPClientAdapter):
698
699
  return translated
699
700
 
700
701
  if self._supports_runtime_env_substitution:
702
+ env_overrides = env_overrides or {}
701
703
  resolved = {}
702
704
  placeholder_keys = []
703
705
  for env_var in env_vars:
@@ -706,12 +708,18 @@ class CopilotClientAdapter(MCPClientAdapter):
706
708
  name = env_var.get("name", "")
707
709
  if not name:
708
710
  continue
711
+ required = registry_field_is_required(env_var)
712
+ override_value = env_overrides.get(name)
713
+ has_override = bool(
714
+ override_value.strip() if isinstance(override_value, str) else override_value
715
+ )
709
716
  if name in default_github_env:
710
717
  # Non-secret literal default -- preserve as-is.
711
718
  resolved[name] = default_github_env[name]
712
- else:
719
+ elif required or has_override:
713
720
  # Emit a runtime-substitution placeholder; APM never reads
714
- # or stores the value.
721
+ # or stores the value. Optional variables are included only
722
+ # when install-time collection observed a value.
715
723
  resolved[name] = self._format_runtime_env_placeholder(name)
716
724
  placeholder_keys.append(name)
717
725
  # Record for the post-install summary line and the
@@ -0,0 +1,210 @@
1
+ """Hermes agent MCP client adapter.
2
+
3
+ Hermes (Nous Research) reads MCP servers from a YAML ``mcp_servers:`` block
4
+ in ``~/.hermes/config.yaml`` (snake_case key -- distinct from the JSON
5
+ ``mcpServers`` schema used by Claude/Copilot). ``$HERMES_HOME`` overrides
6
+ the home directory (default ``~/.hermes``).
7
+
8
+ Scope: Hermes has a single home-directory config, so MCP writes are always
9
+ user-scope -- ``config.yaml`` is the same file regardless of whether the
10
+ install was triggered at project or user scope. Unrelated top-level config
11
+ keys (model provider, telegram settings, ...) are preserved on every write;
12
+ a malformed existing file is left untouched rather than overwritten.
13
+
14
+ Per-server shape:
15
+ * stdio -> ``command`` / ``args`` / ``env`` (+ ``enabled``)
16
+ * http -> ``url`` / ``headers`` (+ ``enabled``)
17
+
18
+ YAML serialization goes through ``utils.yaml_io`` (lint forbids raw
19
+ ``yaml.dump``); the document is written atomically with ``0o600`` perms via
20
+ ``utils.atomic_io`` because ``config.yaml`` carries literal credentials.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import contextlib
26
+ import os
27
+ from pathlib import Path
28
+
29
+ import yaml
30
+
31
+ from ...utils.atomic_io import atomic_write_text
32
+ from ...utils.console import _rich_error, _rich_success
33
+ from ...utils.yaml_io import load_yaml, yaml_to_str
34
+ from .copilot import CopilotClientAdapter
35
+
36
+ # Credential-bearing config file mode: owner read/write only. Hermes' config.yaml
37
+ # holds literal MCP env values plus native model-provider keys / messaging tokens,
38
+ # so it must never be group/world-readable (parity with claude/codex/gemini/cursor).
39
+ _CONFIG_FILE_MODE = 0o600
40
+
41
+
42
+ class _MalformedHermesConfig(Exception):
43
+ """Raised when ``config.yaml`` exists but is not a YAML mapping.
44
+
45
+ Signals write paths to refuse the overwrite so a user's native Hermes
46
+ credentials (model-provider keys, Telegram tokens) are never discarded.
47
+ """
48
+
49
+
50
+ class HermesClientAdapter(CopilotClientAdapter):
51
+ """MCP configuration for the Hermes agent (YAML ``mcp_servers`` schema).
52
+
53
+ Registry formatting reuses :class:`CopilotClientAdapter`, then entries are
54
+ converted to Hermes' on-disk shape via :meth:`_to_hermes_format`.
55
+ """
56
+
57
+ supports_user_scope: bool = True
58
+ target_name: str = "hermes"
59
+ mcp_servers_key: str = "mcp_servers"
60
+
61
+ # Hermes' config.yaml does NOT support runtime env-var substitution; the
62
+ # value in ``env`` must be a literal string, so install-time resolution
63
+ # is kept (mirrors Claude -- see #1152 supply-chain analysis).
64
+ _supports_runtime_env_substitution: bool = False
65
+
66
+ def _config_path(self) -> Path:
67
+ """Resolve ``<hermes-home>/config.yaml`` honouring ``$HERMES_HOME``."""
68
+ from ...integration.targets import resolve_hermes_root
69
+
70
+ return resolve_hermes_root() / "config.yaml"
71
+
72
+ @staticmethod
73
+ def _to_hermes_format(copilot_entry: dict, *, enabled: bool = True) -> dict:
74
+ """Convert a Copilot-format server entry to Hermes' on-disk shape.
75
+
76
+ Drops Copilot-CLI-only fields (``type: "local"``, default
77
+ ``tools: ["*"]``, empty ``id``) and stamps an explicit ``enabled``.
78
+ Required transport fields (``url`` for remote, ``command`` for stdio)
79
+ are only emitted when truthy so a malformed entry never serializes as
80
+ ``url: null`` / ``command: null`` into Hermes' config.
81
+ """
82
+ if not isinstance(copilot_entry, dict):
83
+ return copilot_entry
84
+
85
+ url = copilot_entry.get("url")
86
+ t = copilot_entry.get("type")
87
+ is_remote = bool(url) or t in ("http", "sse", "streamable-http")
88
+
89
+ out: dict = {}
90
+ if is_remote:
91
+ if url:
92
+ out["url"] = url
93
+ headers = copilot_entry.get("headers")
94
+ if headers:
95
+ out["headers"] = headers
96
+ else:
97
+ command = copilot_entry.get("command")
98
+ if command:
99
+ out["command"] = command
100
+ args = copilot_entry.get("args")
101
+ if args:
102
+ out["args"] = list(args)
103
+ env = copilot_entry.get("env")
104
+ if env:
105
+ out["env"] = dict(env)
106
+ out["enabled"] = enabled
107
+ return out
108
+
109
+ def get_config_path(self):
110
+ """Path to the Hermes config file (``<hermes-home>/config.yaml``)."""
111
+ return str(self._config_path())
112
+
113
+ def _load_document(self) -> dict:
114
+ """Load the full ``config.yaml`` document (preserving siblings).
115
+
116
+ Returns ``{}`` when the file is absent or empty. Raises
117
+ :class:`_MalformedHermesConfig` when the file exists but is not a YAML
118
+ mapping (parse error or non-dict root) so write paths can refuse to
119
+ overwrite and silently discard the user's native Hermes credentials.
120
+ """
121
+ path = self._config_path()
122
+ if not path.is_file():
123
+ return {}
124
+ try:
125
+ data = load_yaml(path)
126
+ except (OSError, yaml.YAMLError) as exc:
127
+ raise _MalformedHermesConfig(str(path)) from exc
128
+ if data is None:
129
+ return {}
130
+ if not isinstance(data, dict):
131
+ raise _MalformedHermesConfig(str(path))
132
+ return data
133
+
134
+ def get_current_config(self):
135
+ """Return ``{"mcp_servers": {...}}`` for the on-disk config."""
136
+ try:
137
+ data = self._load_document()
138
+ except _MalformedHermesConfig:
139
+ return {self.mcp_servers_key: {}}
140
+ servers = data.get(self.mcp_servers_key)
141
+ return {self.mcp_servers_key: dict(servers) if isinstance(servers, dict) else {}}
142
+
143
+ def update_config(self, config_updates, enabled=True):
144
+ """Merge *config_updates* into the ``mcp_servers:`` block.
145
+
146
+ Entries are normalized to Hermes' shape. Per-server entries are
147
+ replaced on key conflict; unrelated servers and all other top-level
148
+ config keys are preserved. The file is written atomically with
149
+ ``0o600`` permissions so the credential-bearing config is never left
150
+ group/world-readable. A malformed existing ``config.yaml`` is left
151
+ untouched (returns ``False``) rather than overwritten.
152
+ """
153
+ path = self._config_path()
154
+ try:
155
+ data = self._load_document()
156
+ except _MalformedHermesConfig:
157
+ _rich_error(
158
+ f"{path} is malformed YAML; refusing to overwrite. "
159
+ "Fix or remove the file manually, then retry."
160
+ )
161
+ return False
162
+ try:
163
+ servers = data.get(self.mcp_servers_key)
164
+ if not isinstance(servers, dict):
165
+ servers = {}
166
+ for name, cfg in config_updates.items():
167
+ servers[name] = self._to_hermes_format(cfg, enabled=enabled)
168
+ data[self.mcp_servers_key] = servers
169
+ path.parent.mkdir(parents=True, exist_ok=True)
170
+ atomic_write_text(path, yaml_to_str(data), new_file_mode=_CONFIG_FILE_MODE)
171
+ # Tighten perms even when the file pre-existed with a looser mode
172
+ # (atomic_write_text only applies new_file_mode on first create).
173
+ with contextlib.suppress(OSError, NotImplementedError):
174
+ os.chmod(path, _CONFIG_FILE_MODE)
175
+ return True
176
+ except OSError:
177
+ return False
178
+
179
+ def configure_mcp_server(
180
+ self,
181
+ server_url,
182
+ server_name=None,
183
+ enabled=True,
184
+ env_overrides=None,
185
+ server_info_cache=None,
186
+ runtime_vars=None,
187
+ ):
188
+ if not server_url:
189
+ _rich_error("server_url cannot be empty")
190
+ return False
191
+
192
+ try:
193
+ server_info = self._fetch_server_info(server_url, server_info_cache)
194
+ if server_info is None:
195
+ return False
196
+
197
+ config_key = self._determine_config_key(server_url, server_name)
198
+ server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
199
+ ok = self.update_config({config_key: server_config}, enabled=enabled)
200
+ if not ok:
201
+ _rich_error(f"Failed to write MCP config for '{config_key}' to Hermes")
202
+ return False
203
+
204
+ _rich_success(f"Successfully configured MCP server '{config_key}' for Hermes")
205
+ return True
206
+ except Exception:
207
+ # Do not interpolate the exception message: registry URLs and
208
+ # other inputs may carry embedded credentials.
209
+ _rich_error("Error configuring MCP server")
210
+ return False
@@ -0,0 +1,226 @@
1
+ """Kiro IDE implementation of MCP client adapter.
2
+
3
+ Kiro reads MCP configuration from ``.kiro/settings/mcp.json`` at project
4
+ scope and ``~/.kiro/settings/mcp.json`` at user scope. The schema uses a
5
+ ``mcpServers`` object whose server entries support stdio ``command`` /
6
+ ``args`` / ``env`` and remote ``url`` / ``headers`` entries. Kiro resolves
7
+ ``${VAR}`` environment placeholders at runtime, so this adapter preserves
8
+ placeholders instead of writing host secrets to disk.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import logging
15
+ import os
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from ...utils.atomic_io import atomic_write_text
20
+ from ...utils.console import _rich_error, _rich_success
21
+ from .copilot import CopilotClientAdapter
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class KiroClientAdapter(CopilotClientAdapter):
27
+ """Kiro IDE MCP client adapter."""
28
+
29
+ supports_user_scope: bool = True
30
+ _client_label: str = "Kiro"
31
+ target_name: str = "kiro"
32
+ mcp_servers_key: str = "mcpServers"
33
+ _supports_runtime_env_substitution: bool = True
34
+
35
+ def _get_kiro_root(self) -> Path:
36
+ """Return the ``.kiro`` directory for the active scope."""
37
+ if self.user_scope:
38
+ return Path.home() / ".kiro"
39
+ return self.project_root / ".kiro"
40
+
41
+ def get_config_path(self) -> str:
42
+ """Return the Kiro MCP config path for the active scope."""
43
+ return str(self._get_kiro_root() / "settings" / "mcp.json")
44
+
45
+ def get_current_config(self) -> dict[str, Any]:
46
+ """Read the current Kiro MCP config."""
47
+ config_path = Path(self.get_config_path())
48
+ if not config_path.exists():
49
+ return {}
50
+ try:
51
+ with open(config_path, encoding="utf-8") as f:
52
+ data = json.load(f)
53
+ return data if isinstance(data, dict) else {}
54
+ except (OSError, json.JSONDecodeError) as exc:
55
+ logger.warning("Could not read %s: %s", config_path, exc)
56
+ return {}
57
+
58
+ def update_config(self, config_updates: dict[str, dict[str, Any]]) -> bool | None:
59
+ """Merge *config_updates* into Kiro's ``mcpServers`` object.
60
+
61
+ Project scope is opt-in: the workspace must already contain ``.kiro/``.
62
+ User scope creates ``~/.kiro/settings/`` on demand.
63
+ """
64
+ kiro_root = self._get_kiro_root()
65
+ if not self.user_scope and not kiro_root.is_dir():
66
+ logger.debug("Skipping Kiro project-scope write -- %s does not exist", kiro_root)
67
+ return None
68
+
69
+ config_path = Path(self.get_config_path())
70
+ current_config = self.get_current_config()
71
+ if not isinstance(current_config.get(self.mcp_servers_key), dict):
72
+ current_config[self.mcp_servers_key] = {}
73
+ current_config[self.mcp_servers_key].update(config_updates)
74
+
75
+ config_path.parent.mkdir(parents=True, exist_ok=True)
76
+ atomic_write_text(
77
+ config_path,
78
+ json.dumps(current_config, indent=2),
79
+ new_file_mode=0o600,
80
+ )
81
+ # Keep existing config files private after updates too.
82
+ os.chmod(config_path, 0o600)
83
+ return True
84
+
85
+ @staticmethod
86
+ def _header_mapping(remote: dict[str, Any]) -> dict[str, str]:
87
+ """Return registry remote headers as string key-value pairs."""
88
+ headers = remote.get("headers", {})
89
+ if isinstance(headers, list):
90
+ return {
91
+ str(h["name"]): str(h["value"])
92
+ for h in headers
93
+ if isinstance(h, dict) and "name" in h and "value" in h
94
+ }
95
+ if isinstance(headers, dict):
96
+ return {str(name): str(value) for name, value in headers.items()}
97
+ return {}
98
+
99
+ @staticmethod
100
+ def _copy_kiro_extensions(config: dict[str, Any], server_info: dict[str, Any]) -> None:
101
+ """Carry Kiro-specific MCP fields when the registry supplies them."""
102
+ for key in ("autoApprove", "disabledTools", "disabled"):
103
+ if key in server_info and server_info[key] is not None:
104
+ config[key] = server_info[key]
105
+
106
+ def _format_server_config(
107
+ self,
108
+ server_info: dict[str, Any],
109
+ env_overrides: dict[str, str] | None = None,
110
+ runtime_vars: dict[str, str] | None = None,
111
+ ) -> dict[str, Any]:
112
+ """Format registry or self-defined server info for Kiro."""
113
+ if runtime_vars is None:
114
+ runtime_vars = {}
115
+
116
+ raw = server_info.get("_raw_stdio")
117
+ if raw:
118
+ config: dict[str, Any] = {"command": raw["command"]}
119
+ resolved_env_for_args: dict[str, Any] = {}
120
+ if raw.get("env"):
121
+ resolved_env_for_args = self._resolve_environment_variables(
122
+ raw["env"], env_overrides=env_overrides
123
+ )
124
+ config["env"] = resolved_env_for_args
125
+ self._warn_input_variables(raw["env"], server_info.get("name", ""), "Kiro")
126
+ config["args"] = [
127
+ self._resolve_variable_placeholders(arg, resolved_env_for_args, runtime_vars)
128
+ if isinstance(arg, str)
129
+ else arg
130
+ for arg in raw.get("args") or []
131
+ ]
132
+ self._copy_kiro_extensions(config, server_info)
133
+ return config
134
+
135
+ remotes = server_info.get("remotes", [])
136
+ if remotes:
137
+ remote = self._select_remote_with_url(remotes) or remotes[0]
138
+ transport = (remote.get("transport_type") or "").strip()
139
+ if not transport:
140
+ transport = "http"
141
+ elif transport not in ("sse", "http", "streamable-http"):
142
+ raise ValueError(
143
+ f"Unsupported remote transport '{transport}' for Kiro. "
144
+ f"Server: {server_info.get('name', 'unknown')}. "
145
+ "Supported transports: http, sse, streamable-http."
146
+ )
147
+
148
+ config = {"url": (remote.get("url") or "").strip()}
149
+ headers = {
150
+ name: self._resolve_env_variable(name, value, env_overrides)
151
+ if isinstance(value, str)
152
+ else value
153
+ for name, value in self._header_mapping(remote).items()
154
+ if name
155
+ }
156
+ if headers:
157
+ config["headers"] = headers
158
+ self._warn_input_variables(headers, server_info.get("name", ""), "Kiro")
159
+ self._copy_kiro_extensions(config, server_info)
160
+ return config
161
+
162
+ packages = server_info.get("packages", [])
163
+ if not packages:
164
+ raise ValueError(
165
+ "MCP server has incomplete configuration in registry - "
166
+ "no package information or remote endpoints available. "
167
+ f"Server: {server_info.get('name', 'unknown')}"
168
+ )
169
+
170
+ config = {}
171
+ package = self._select_and_dispatch_best_package(
172
+ config,
173
+ packages,
174
+ env_overrides,
175
+ runtime_vars,
176
+ )
177
+ if not package:
178
+ raise ValueError(
179
+ f"No supported package type found for Kiro. "
180
+ f"Server: {server_info.get('name', 'unknown')}."
181
+ )
182
+ self._copy_kiro_extensions(config, server_info)
183
+ return config
184
+
185
+ def configure_mcp_server(
186
+ self,
187
+ server_url: str,
188
+ server_name: str | None = None,
189
+ enabled: bool = True,
190
+ env_overrides: dict[str, str] | None = None,
191
+ server_info_cache: dict[str, Any] | None = None,
192
+ runtime_vars: dict[str, str] | None = None,
193
+ ) -> bool:
194
+ """Configure an MCP server in Kiro's MCP config."""
195
+ if not server_url:
196
+ _rich_error("server_url cannot be empty", symbol="error")
197
+ return False
198
+
199
+ if not self.user_scope and not self._get_kiro_root().is_dir():
200
+ logger.debug(
201
+ "Kiro opt-in gate: %s absent, skipping configure_mcp_server",
202
+ self._get_kiro_root(),
203
+ )
204
+ return True
205
+
206
+ config_key = self._determine_config_key(server_url, server_name)
207
+
208
+ try:
209
+ server_info = self._fetch_server_info(server_url, server_info_cache)
210
+ if server_info is None:
211
+ return False
212
+
213
+ self._last_env_placeholder_keys = set()
214
+ self._last_legacy_angle_vars = set()
215
+
216
+ server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
217
+ if not enabled:
218
+ server_config["disabled"] = True
219
+ self.update_config({config_key: server_config})
220
+
221
+ _rich_success(f"Configured MCP server '{config_key}' for Kiro", symbol="success")
222
+ return True
223
+ except Exception as exc:
224
+ logger.debug("Kiro MCP configuration failed: %s", exc)
225
+ _rich_error(f"Failed to configure MCP server '{config_key}' for Kiro", symbol="error")
226
+ return False