apm-cli 0.9.2__tar.gz → 0.9.3__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 (234) hide show
  1. {apm_cli-0.9.2/src/apm_cli.egg-info → apm_cli-0.9.3}/PKG-INFO +5 -5
  2. {apm_cli-0.9.2 → apm_cli-0.9.3}/README.md +4 -4
  3. {apm_cli-0.9.2 → apm_cli-0.9.3}/pyproject.toml +1 -1
  4. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/copilot.py +41 -6
  5. apm_cli-0.9.3/src/apm_cli/adapters/client/gemini.py +271 -0
  6. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/bundle/lockfile_enrichment.py +11 -0
  7. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/bundle/packer.py +33 -1
  8. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/cli.py +2 -0
  9. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/_helpers.py +26 -1
  10. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/compile/cli.py +9 -4
  11. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/deps/_utils.py +16 -1
  12. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/deps/cli.py +1 -1
  13. apm_cli-0.9.3/src/apm_cli/commands/experimental.py +349 -0
  14. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/install.py +6 -2
  15. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/runtime.py +2 -2
  16. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/uninstall/engine.py +3 -3
  17. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/update.py +11 -1
  18. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/agents_compiler.py +65 -10
  19. apm_cli-0.9.3/src/apm_cli/compilation/gemini_formatter.py +119 -0
  20. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/constants.py +22 -0
  21. apm_cli-0.9.3/src/apm_cli/core/experimental.py +266 -0
  22. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/script_runner.py +37 -16
  23. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/target_detection.py +35 -73
  24. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/lockfile.py +53 -19
  25. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/package_validator.py +45 -37
  26. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/factory.py +2 -0
  27. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/context.py +7 -0
  28. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/errors.py +10 -0
  29. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/cleanup.py +7 -0
  30. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/integrate.py +25 -0
  31. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/policy_gate.py +12 -0
  32. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/pipeline.py +17 -1
  33. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/base_integrator.py +7 -1
  34. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/command_integrator.py +40 -3
  35. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/hook_integrator.py +70 -1
  36. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/instruction_integrator.py +2 -0
  37. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/mcp_integrator.py +63 -16
  38. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/skill_integrator.py +17 -4
  39. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/targets.py +26 -0
  40. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/apm_package.py +18 -1
  41. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/validation.py +93 -3
  42. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/ci_checks.py +140 -13
  43. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/discovery.py +4 -0
  44. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/parser.py +45 -5
  45. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/policy_checks.py +76 -0
  46. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/schema.py +1 -0
  47. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/primitives/discovery.py +120 -50
  48. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/manager.py +14 -5
  49. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/path_security.py +15 -2
  50. apm_cli-0.9.3/src/apm_cli/utils/subprocess_env.py +83 -0
  51. {apm_cli-0.9.2 → apm_cli-0.9.3/src/apm_cli.egg-info}/PKG-INFO +5 -5
  52. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli.egg-info/SOURCES.txt +5 -0
  53. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_apm_package_models.py +173 -0
  54. {apm_cli-0.9.2 → apm_cli-0.9.3}/AUTHORS +0 -0
  55. {apm_cli-0.9.2 → apm_cli-0.9.3}/LICENSE +0 -0
  56. {apm_cli-0.9.2 → apm_cli-0.9.3}/setup.cfg +0 -0
  57. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/__init__.py +0 -0
  58. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/__init__.py +0 -0
  59. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/__init__.py +0 -0
  60. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/base.py +0 -0
  61. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/codex.py +0 -0
  62. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/cursor.py +0 -0
  63. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/opencode.py +0 -0
  64. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/client/vscode.py +0 -0
  65. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  66. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/package_manager/base.py +0 -0
  67. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  68. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/bundle/__init__.py +0 -0
  69. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/bundle/plugin_exporter.py +0 -0
  70. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/bundle/unpacker.py +0 -0
  71. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/__init__.py +0 -0
  72. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/audit.py +0 -0
  73. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/compile/__init__.py +0 -0
  74. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/compile/watcher.py +0 -0
  75. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/config.py +0 -0
  76. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/deps/__init__.py +0 -0
  77. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/init.py +0 -0
  78. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/list_cmd.py +0 -0
  79. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/marketplace.py +0 -0
  80. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/mcp.py +0 -0
  81. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/outdated.py +0 -0
  82. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/pack.py +0 -0
  83. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/policy.py +0 -0
  84. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/prune.py +0 -0
  85. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/run.py +0 -0
  86. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/uninstall/__init__.py +0 -0
  87. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/uninstall/cli.py +0 -0
  88. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/commands/view.py +0 -0
  89. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/__init__.py +0 -0
  90. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/claude_formatter.py +0 -0
  91. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/constants.py +0 -0
  92. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/constitution.py +0 -0
  93. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/constitution_block.py +0 -0
  94. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/context_optimizer.py +0 -0
  95. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/distributed_compiler.py +0 -0
  96. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/injector.py +0 -0
  97. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/link_resolver.py +0 -0
  98. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/compilation/template_builder.py +0 -0
  99. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/config.py +0 -0
  100. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/__init__.py +0 -0
  101. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/auth.py +0 -0
  102. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/azure_cli.py +0 -0
  103. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/command_logger.py +0 -0
  104. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/conflict_detector.py +0 -0
  105. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/docker_args.py +0 -0
  106. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/operations.py +0 -0
  107. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/safe_installer.py +0 -0
  108. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/scope.py +0 -0
  109. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/core/token_manager.py +0 -0
  110. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/__init__.py +0 -0
  111. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/aggregator.py +0 -0
  112. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/apm_resolver.py +0 -0
  113. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/artifactory_entry.py +0 -0
  114. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/collection_parser.py +0 -0
  115. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/dependency_graph.py +0 -0
  116. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/github_downloader.py +0 -0
  117. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/installed_package.py +0 -0
  118. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/plugin_parser.py +0 -0
  119. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/registry_proxy.py +0 -0
  120. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/transport_selection.py +0 -0
  121. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/deps/verifier.py +0 -0
  122. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/drift.py +0 -0
  123. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/__init__.py +0 -0
  124. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/helpers/__init__.py +0 -0
  125. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/helpers/security_scan.py +0 -0
  126. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/insecure_policy.py +0 -0
  127. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/mcp_registry.py +0 -0
  128. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/mcp_warnings.py +0 -0
  129. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/__init__.py +0 -0
  130. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/download.py +0 -0
  131. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/finalize.py +0 -0
  132. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/local_content.py +0 -0
  133. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/lockfile.py +0 -0
  134. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/policy_target_check.py +0 -0
  135. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/post_deps_local.py +0 -0
  136. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/resolve.py +0 -0
  137. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/phases/targets.py +0 -0
  138. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/presentation/__init__.py +0 -0
  139. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/presentation/dry_run.py +0 -0
  140. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/request.py +0 -0
  141. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/service.py +0 -0
  142. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/services.py +0 -0
  143. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/sources.py +0 -0
  144. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/template.py +0 -0
  145. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/install/validation.py +0 -0
  146. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/__init__.py +0 -0
  147. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/agent_integrator.py +0 -0
  148. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/cleanup.py +0 -0
  149. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/coverage.py +0 -0
  150. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/dispatch.py +0 -0
  151. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/prompt_integrator.py +0 -0
  152. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/skill_transformer.py +0 -0
  153. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/integration/utils.py +0 -0
  154. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/__init__.py +0 -0
  155. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/client.py +0 -0
  156. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/errors.py +0 -0
  157. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/models.py +0 -0
  158. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/registry.py +0 -0
  159. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/resolver.py +0 -0
  160. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/shadow_detector.py +0 -0
  161. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/validator.py +0 -0
  162. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/marketplace/version_pins.py +0 -0
  163. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/__init__.py +0 -0
  164. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/dependency/__init__.py +0 -0
  165. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/dependency/mcp.py +0 -0
  166. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/dependency/reference.py +0 -0
  167. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/dependency/types.py +0 -0
  168. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/plugin.py +0 -0
  169. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/models/results.py +0 -0
  170. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/output/__init__.py +0 -0
  171. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/output/formatters.py +0 -0
  172. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/output/models.py +0 -0
  173. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/output/script_formatters.py +0 -0
  174. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/__init__.py +0 -0
  175. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/inheritance.py +0 -0
  176. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/install_preflight.py +0 -0
  177. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/matcher.py +0 -0
  178. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/models.py +0 -0
  179. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/outcome_routing.py +0 -0
  180. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/policy/project_config.py +0 -0
  181. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/primitives/__init__.py +0 -0
  182. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/primitives/models.py +0 -0
  183. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/primitives/parser.py +0 -0
  184. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/registry/__init__.py +0 -0
  185. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/registry/client.py +0 -0
  186. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/registry/integration.py +0 -0
  187. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/registry/operations.py +0 -0
  188. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/__init__.py +0 -0
  189. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/base.py +0 -0
  190. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/codex_runtime.py +0 -0
  191. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/copilot_runtime.py +0 -0
  192. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/factory.py +0 -0
  193. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/runtime/llm_runtime.py +0 -0
  194. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/security/__init__.py +0 -0
  195. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/security/audit_report.py +0 -0
  196. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/security/content_scanner.py +0 -0
  197. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/security/file_scanner.py +0 -0
  198. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/security/gate.py +0 -0
  199. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/update_policy.py +0 -0
  200. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/__init__.py +0 -0
  201. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/console.py +0 -0
  202. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/content_hash.py +0 -0
  203. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/diagnostics.py +0 -0
  204. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/exclude.py +0 -0
  205. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/file_ops.py +0 -0
  206. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/github_host.py +0 -0
  207. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/helpers.py +0 -0
  208. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/paths.py +0 -0
  209. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/version_checker.py +0 -0
  210. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/utils/yaml_io.py +0 -0
  211. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/version.py +0 -0
  212. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/workflow/__init__.py +0 -0
  213. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/workflow/discovery.py +0 -0
  214. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/workflow/parser.py +0 -0
  215. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli/workflow/runner.py +0 -0
  216. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  217. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli.egg-info/entry_points.txt +0 -0
  218. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli.egg-info/requires.txt +0 -0
  219. {apm_cli-0.9.2 → apm_cli-0.9.3}/src/apm_cli.egg-info/top_level.txt +0 -0
  220. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_apm_resolver.py +0 -0
  221. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_codex_docker_args_fix.py +0 -0
  222. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_codex_empty_string_and_defaults.py +0 -0
  223. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_collision_integration.py +0 -0
  224. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_console.py +0 -0
  225. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_distributed_compilation.py +0 -0
  226. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_empty_string_and_defaults.py +0 -0
  227. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_enhanced_discovery.py +0 -0
  228. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_github_downloader.py +0 -0
  229. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_github_downloader_token_precedence.py +0 -0
  230. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_lockfile.py +0 -0
  231. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_runnable_prompts.py +0 -0
  232. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_runtime_manager_token_precedence.py +0 -0
  233. {apm_cli-0.9.2 → apm_cli-0.9.3}/tests/test_token_manager.py +0 -0
  234. {apm_cli-0.9.2 → apm_cli-0.9.3}/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.9.2
3
+ Version: 0.9.3
4
4
  Summary: MCP configuration tool
5
5
  Author-email: Daniel Meppiel <user@example.com>
6
6
  License: MIT License
@@ -66,9 +66,9 @@ Dynamic: license-file
66
66
 
67
67
  Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
68
68
 
69
- GitHub Copilot · Claude Code · Cursor · OpenCode · Codex
69
+ GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini
70
70
 
71
- **[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/)**
71
+ **[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)**
72
72
 
73
73
  ---
74
74
 
@@ -112,7 +112,7 @@ apm install # every agent is configured
112
112
 
113
113
  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.
114
114
 
115
- - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex
115
+ - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini
116
116
  - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
117
117
  - **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
118
118
  - **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
@@ -190,7 +190,7 @@ apm marketplace add github/awesome-copilot
190
190
  apm install azure-cloud-development@awesome-copilot
191
191
  ```
192
192
 
193
- Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, and OpenCode):
193
+ Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, and Gemini):
194
194
 
195
195
  ```bash
196
196
  apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
@@ -4,9 +4,9 @@
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
7
+ GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini
8
8
 
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/)**
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
 
11
11
  ---
12
12
 
@@ -50,7 +50,7 @@ apm install # every agent is configured
50
50
 
51
51
  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.
52
52
 
53
- - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex
53
+ - **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini
54
54
  - **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
55
55
  - **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
56
56
  - **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
@@ -128,7 +128,7 @@ apm marketplace add github/awesome-copilot
128
128
  apm install azure-cloud-development@awesome-copilot
129
129
  ```
130
130
 
131
- Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, and OpenCode):
131
+ Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, and Gemini):
132
132
 
133
133
  ```bash
134
134
  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.9.2"
7
+ version = "0.9.3"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -186,17 +186,36 @@ class CopilotClientAdapter(MCPClientAdapter):
186
186
  # Check for remote endpoints first (registry-defined priority)
187
187
  remotes = server_info.get("remotes", [])
188
188
  if remotes:
189
- # Use remote endpoint if available
190
- remote = remotes[0] # Take the first remote
191
-
192
- # All remote servers use http type for proper authentication support
189
+ # Select the first remote with a non-empty URL; fall back to the
190
+ # first entry so downstream code still emits the historical empty
191
+ # URL error path when no remote is usable.
192
+ remote = self._select_remote_with_url(remotes) or remotes[0]
193
+
194
+ # Validate transport_type from registry: default to "http" when
195
+ # missing/empty, raise ValueError for unsupported values. Mirrors
196
+ # the VS Code adapter check introduced in PR #656 so registry data
197
+ # with, e.g. transport_type="grpc" fails loudly instead of silently
198
+ # producing a garbage config.
199
+ transport = (remote.get("transport_type") or "").strip()
200
+ if not transport:
201
+ transport = "http"
202
+ elif transport not in ("sse", "http", "streamable-http"):
203
+ raise ValueError(
204
+ f"Unsupported remote transport '{transport}' for Copilot. "
205
+ f"Server: {server_info.get('name', 'unknown')}. "
206
+ f"Supported transports: http, sse, streamable-http."
207
+ )
208
+
209
+ # Copilot CLI writes "type": "http" for all remote servers so
210
+ # authentication flows (headers) are consistent regardless of the
211
+ # underlying transport advertised by the registry.
193
212
  config = {
194
213
  "type": "http",
195
- "url": remote.get("url", ""),
214
+ "url": (remote.get("url") or "").strip(),
196
215
  "tools": ["*"], # Required by Copilot CLI specification
197
216
  "id": server_info.get("id", "") # Add registry UUID for conflict detection
198
217
  }
199
-
218
+
200
219
  # Add authentication headers for GitHub MCP server
201
220
  server_name = server_info.get("name", "")
202
221
  is_github_server = self._is_github_server(server_name, remote.get("url", ""))
@@ -649,6 +668,22 @@ class CopilotClientAdapter(MCPClientAdapter):
649
668
  """Legacy method for backward compatibility. Use _resolve_variable_placeholders instead."""
650
669
  return self._resolve_variable_placeholders(value, resolved_env, {})
651
670
 
671
+ @staticmethod
672
+ def _select_remote_with_url(remotes):
673
+ """Return the first remote entry that has a non-empty URL.
674
+
675
+ Args:
676
+ remotes (list): Candidate remote entries from the registry.
677
+
678
+ Returns:
679
+ dict or None: The first usable remote, or None if none qualify.
680
+ """
681
+ for remote in remotes:
682
+ url = (remote.get("url") or "").strip()
683
+ if url:
684
+ return remote
685
+ return None
686
+
652
687
  def _select_best_package(self, packages):
653
688
  """Select the best package for installation from available packages.
654
689
 
@@ -0,0 +1,271 @@
1
+ """Gemini CLI implementation of MCP client adapter.
2
+
3
+ Gemini CLI uses ``.gemini/settings.json`` at the project root with an
4
+ ``mcpServers`` key. Unlike Copilot, Gemini infers transport from which
5
+ key is present (``command`` for stdio, ``url`` for SSE, ``httpUrl`` for
6
+ streamable HTTP) and does not use ``type``, ``tools``, or ``id`` fields.
7
+
8
+ .. code-block:: json
9
+
10
+ {
11
+ "mcpServers": {
12
+ "server-name": {
13
+ "command": "npx",
14
+ "args": ["-y", "@modelcontextprotocol/server-foo"],
15
+ "env": { "KEY": "value" }
16
+ }
17
+ }
18
+ }
19
+
20
+ APM only writes to ``.gemini/settings.json`` when the ``.gemini/``
21
+ directory already exists -- Gemini CLI support is opt-in.
22
+
23
+ Ref: https://geminicli.com/docs/reference/configuration/
24
+ """
25
+
26
+ import json
27
+ import logging
28
+ import os
29
+ from pathlib import Path
30
+
31
+ from .copilot import CopilotClientAdapter
32
+ from ...core.docker_args import DockerArgsProcessor
33
+ from ...utils.console import _rich_error, _rich_success
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ class GeminiClientAdapter(CopilotClientAdapter):
39
+ """Gemini CLI MCP client adapter.
40
+
41
+ Inherits Copilot's helper methods for package selection, env-var
42
+ resolution, and argument processing but fully reimplements
43
+ ``_format_server_config`` to emit Gemini-valid JSON.
44
+ """
45
+
46
+ supports_user_scope: bool = True
47
+
48
+ def get_config_path(self):
49
+ """Return the path to ``.gemini/settings.json`` in the repository root."""
50
+ return str(Path(os.getcwd()) / ".gemini" / "settings.json")
51
+
52
+ def update_config(self, config_updates):
53
+ """Merge *config_updates* into the ``mcpServers`` section of settings.json.
54
+
55
+ The ``.gemini/`` directory must already exist; if it does not, this
56
+ method returns silently (opt-in behaviour).
57
+
58
+ Preserves all other top-level keys in settings.json (theme, tools,
59
+ hooks, etc.).
60
+ """
61
+ gemini_dir = Path(os.getcwd()) / ".gemini"
62
+ if not gemini_dir.is_dir():
63
+ return
64
+
65
+ config_path = Path(self.get_config_path())
66
+ current_config = self.get_current_config()
67
+ if "mcpServers" not in current_config:
68
+ current_config["mcpServers"] = {}
69
+
70
+ for name, entry in config_updates.items():
71
+ current_config["mcpServers"][name] = entry
72
+
73
+ config_path.parent.mkdir(parents=True, exist_ok=True)
74
+ with open(config_path, "w", encoding="utf-8") as f:
75
+ json.dump(current_config, f, indent=2)
76
+
77
+ def get_current_config(self):
78
+ """Read the current ``.gemini/settings.json`` contents."""
79
+ config_path = self.get_config_path()
80
+ if not os.path.exists(config_path):
81
+ return {}
82
+ try:
83
+ with open(config_path, "r", encoding="utf-8") as f:
84
+ return json.load(f)
85
+ except (json.JSONDecodeError, IOError):
86
+ return {}
87
+
88
+ def _format_server_config(self, server_info, env_overrides=None, runtime_vars=None):
89
+ """Format server info into Gemini CLI MCP configuration.
90
+
91
+ Gemini's schema differs from Copilot's:
92
+ - No ``type``, ``tools``, or ``id`` fields.
93
+ - Transport inferred from key: ``command`` (stdio), ``url`` (SSE),
94
+ ``httpUrl`` (streamable HTTP).
95
+ - Tool filtering via ``includeTools``/``excludeTools``.
96
+
97
+ Args:
98
+ server_info: Server information from registry.
99
+ env_overrides: Pre-collected environment variable overrides.
100
+ runtime_vars: Pre-collected runtime variable values.
101
+
102
+ Returns:
103
+ dict suitable for writing to ``.gemini/settings.json``.
104
+ """
105
+ if runtime_vars is None:
106
+ runtime_vars = {}
107
+
108
+ config: dict = {}
109
+
110
+ # --- raw stdio (self-defined deps) ---
111
+ raw = server_info.get("_raw_stdio")
112
+ if raw:
113
+ config["command"] = raw["command"]
114
+ config["args"] = raw["args"]
115
+ if raw.get("env"):
116
+ config["env"] = raw["env"]
117
+ self._warn_input_variables(
118
+ raw["env"], server_info.get("name", ""), "Gemini CLI"
119
+ )
120
+ return config
121
+
122
+ # --- remote endpoints ---
123
+ remotes = server_info.get("remotes", [])
124
+ if remotes:
125
+ remote = self._select_remote_with_url(remotes) or remotes[0]
126
+
127
+ transport = (remote.get("transport_type") or "").strip()
128
+ if not transport:
129
+ transport = "http"
130
+ elif transport not in ("sse", "http", "streamable-http"):
131
+ raise ValueError(
132
+ f"Unsupported remote transport '{transport}' for Gemini. "
133
+ f"Server: {server_info.get('name', 'unknown')}. "
134
+ f"Supported transports: http, sse, streamable-http."
135
+ )
136
+
137
+ url = (remote.get("url") or "").strip()
138
+ if transport == "sse":
139
+ config["url"] = url
140
+ else:
141
+ config["httpUrl"] = url
142
+
143
+ # Registry-supplied headers
144
+ for header in remote.get("headers", []):
145
+ name = header.get("name", "")
146
+ value = header.get("value", "")
147
+ if name and value:
148
+ config.setdefault("headers", {})[name] = (
149
+ self._resolve_env_variable(name, value, env_overrides)
150
+ )
151
+
152
+ if config.get("headers"):
153
+ self._warn_input_variables(
154
+ config["headers"], server_info.get("name", ""), "Gemini CLI"
155
+ )
156
+
157
+ return config
158
+
159
+ # --- local packages ---
160
+ packages = server_info.get("packages", [])
161
+
162
+ if not packages:
163
+ raise ValueError(
164
+ f"MCP server has no package information or remote endpoints. "
165
+ f"Server: {server_info.get('name', 'unknown')}"
166
+ )
167
+
168
+ package = self._select_best_package(packages)
169
+ if not package:
170
+ return config
171
+
172
+ registry_name = self._infer_registry_name(package)
173
+ package_name = package.get("name", "")
174
+ runtime_hint = package.get("runtime_hint", "")
175
+ runtime_arguments = package.get("runtime_arguments", [])
176
+ package_arguments = package.get("package_arguments", [])
177
+ env_vars = package.get("environment_variables", [])
178
+
179
+ resolved_env = self._resolve_environment_variables(
180
+ env_vars, env_overrides
181
+ )
182
+ processed_rt = self._process_arguments(
183
+ runtime_arguments, resolved_env, runtime_vars
184
+ )
185
+ processed_pkg = self._process_arguments(
186
+ package_arguments, resolved_env, runtime_vars
187
+ )
188
+
189
+ if registry_name == "npm":
190
+ config["command"] = runtime_hint or "npx"
191
+ config["args"] = ["-y", package_name] + processed_rt + processed_pkg
192
+ elif registry_name == "docker":
193
+ config["command"] = "docker"
194
+ if processed_rt:
195
+ config["args"] = self._inject_env_vars_into_docker_args(
196
+ processed_rt, resolved_env
197
+ )
198
+ else:
199
+ config["args"] = DockerArgsProcessor.process_docker_args(
200
+ ["run", "-i", "--rm", package_name], resolved_env
201
+ )
202
+ elif registry_name == "pypi":
203
+ config["command"] = runtime_hint or "uvx"
204
+ config["args"] = [package_name] + processed_rt + processed_pkg
205
+ elif registry_name == "homebrew":
206
+ config["command"] = (
207
+ package_name.split("/")[-1] if "/" in package_name else package_name
208
+ )
209
+ config["args"] = processed_rt + processed_pkg
210
+ else:
211
+ config["command"] = runtime_hint or package_name
212
+ config["args"] = processed_rt + processed_pkg
213
+
214
+ if resolved_env:
215
+ config["env"] = resolved_env
216
+
217
+ return config
218
+
219
+ def configure_mcp_server(
220
+ self,
221
+ server_url,
222
+ server_name=None,
223
+ enabled=True,
224
+ env_overrides=None,
225
+ server_info_cache=None,
226
+ runtime_vars=None,
227
+ ):
228
+ """Configure an MCP server in ``.gemini/settings.json``.
229
+
230
+ Delegates to the parent for config formatting, then writes to
231
+ the Gemini CLI settings file.
232
+ """
233
+ if not server_url:
234
+ _rich_error("server_url cannot be empty", symbol="error")
235
+ return False
236
+
237
+ gemini_dir = Path(os.getcwd()) / ".gemini"
238
+ if not gemini_dir.is_dir():
239
+ return True
240
+
241
+ try:
242
+ if server_info_cache and server_url in server_info_cache:
243
+ server_info = server_info_cache[server_url]
244
+ else:
245
+ server_info = self.registry_client.find_server_by_reference(server_url)
246
+
247
+ if not server_info:
248
+ _rich_error(f"MCP server '{server_url}' not found in registry", symbol="error")
249
+ return False
250
+
251
+ if server_name:
252
+ config_key = server_name
253
+ elif "/" in server_url:
254
+ config_key = server_url.split("/")[-1]
255
+ else:
256
+ config_key = server_url
257
+
258
+ server_config = self._format_server_config(
259
+ server_info, env_overrides, runtime_vars
260
+ )
261
+ self.update_config({config_key: server_config})
262
+
263
+ _rich_success(
264
+ f"Configured MCP server '{config_key}' for Gemini CLI", symbol="success"
265
+ )
266
+ return True
267
+
268
+ except Exception as e:
269
+ logger.debug("Gemini MCP configuration failed: %s", e)
270
+ _rich_error("Failed to configure MCP server for Gemini CLI", symbol="error")
271
+ return False
@@ -153,6 +153,17 @@ def enrich_lockfile_for_pack(
153
153
  dep["deployed_files"] = filtered
154
154
  all_mappings.update(mappings)
155
155
 
156
+ # Issue #887: strip packaging-time local-content fields from the bundle
157
+ # lockfile. ``local_deployed_files`` / ``local_deployed_file_hashes``
158
+ # describe the packager's own repo content, which is intentionally NOT
159
+ # shipped in the bundle (see packer.py source-local guard). Leaving them
160
+ # in the bundle lockfile would cause ``LockFile.from_yaml()`` on the
161
+ # consumer side to synthesize a self-entry whose ``deployed_files`` do
162
+ # not exist under the bundle source dir, breaking unpacker verification.
163
+ if isinstance(data, dict):
164
+ data.pop("local_deployed_files", None)
165
+ data.pop("local_deployed_file_hashes", None)
166
+
156
167
  # Build the pack: metadata section (after filtering so we know if mapping
157
168
  # occurred).
158
169
  # Serialize target as a comma-joined string for backward compatibility
@@ -80,12 +80,38 @@ def pack_bundle(
80
80
 
81
81
  # 2. Read apm.yml for name / version / config target
82
82
  apm_yml_path = project_root / "apm.yml"
83
+ skill_md_path = project_root / "SKILL.md"
84
+ is_hybrid_root = apm_yml_path.exists() and skill_md_path.exists()
83
85
  try:
84
86
  package = APMPackage.from_apm_yml(apm_yml_path)
85
87
  pkg_name = package.name
86
88
  pkg_version = package.version or "0.0.0"
87
89
  config_target = package.target
88
90
 
91
+ # HYBRID author guard: apm.yml.description and SKILL.md
92
+ # description serve different consumers (human-facing CLI/search
93
+ # vs. agent-runtime invocation matcher) and are NOT merged. If
94
+ # the author shipped a SKILL.md description but left
95
+ # apm.yml.description blank, the human-facing surfaces (apm view,
96
+ # apm search, marketplace listings) will degrade silently while
97
+ # Claude/Copilot still invoke the skill correctly. Warn loudly
98
+ # at pack time -- this is the publish gate for the AUTHOR.
99
+ if is_hybrid_root and not package.description and logger:
100
+ try:
101
+ import frontmatter as _frontmatter
102
+ with open(skill_md_path, "r", encoding="utf-8") as _f:
103
+ _skill_post = _frontmatter.load(_f)
104
+ _skill_desc = _skill_post.metadata.get("description")
105
+ except Exception:
106
+ _skill_desc = None
107
+ if _skill_desc:
108
+ logger.warning(
109
+ "apm.yml is missing 'description'. SKILL.md has its own "
110
+ "description, but that is for agent invocation -- not "
111
+ "for 'apm view' or search. Add a short tagline to "
112
+ "apm.yml: description: \"One-line human summary\""
113
+ )
114
+
89
115
  # Guard: reject local-path dependencies (non-portable)
90
116
  for dep_ref in package.get_apm_dependencies():
91
117
  if dep_ref.is_local:
@@ -119,9 +145,15 @@ def pack_bundle(
119
145
  if effective_target == "minimal":
120
146
  effective_target = "all"
121
147
 
122
- # 4. Collect deployed_files from all dependencies, filtered by target
148
+ # 4. Collect deployed_files from all dependencies, filtered by target.
149
+ # Skip local-source entries: these include the synthesized root self-entry
150
+ # (local_path == ".") and any local-path manifest deps. Local content is
151
+ # not portable and is bundled separately via the project's own files
152
+ # (or rejected outright at L89-97 for manifest-declared local deps).
123
153
  all_deployed: List[str] = []
124
154
  for dep in lockfile.get_all_dependencies():
155
+ if dep.source == "local":
156
+ continue
125
157
  all_deployed.extend(dep.deployed_files)
126
158
 
127
159
  filtered_files, path_mappings = _filter_files_by_target(all_deployed, effective_target)
@@ -20,6 +20,7 @@ from apm_cli.commands.audit import audit
20
20
  from apm_cli.commands.compile import compile as compile_cmd
21
21
  from apm_cli.commands.config import config
22
22
  from apm_cli.commands.deps import deps
23
+ from apm_cli.commands.experimental import experimental
23
24
  from apm_cli.commands.view import view as view_cmd
24
25
  from apm_cli.commands.init import init
25
26
  from apm_cli.commands.install import install
@@ -81,6 +82,7 @@ cli.add_command(run)
81
82
  cli.add_command(preview)
82
83
  cli.add_command(list_cmd, name="list")
83
84
  cli.add_command(config)
85
+ cli.add_command(experimental)
84
86
  cli.add_command(runtime)
85
87
  cli.add_command(mcp)
86
88
  cli.add_command(policy)
@@ -132,7 +132,7 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
132
132
  expected.add(str(install_path))
133
133
 
134
134
  if lockfile:
135
- for dep in lockfile.get_all_dependencies():
135
+ for dep in lockfile.get_package_dependencies():
136
136
  if dep.depth is not None and dep.depth > 1:
137
137
  dep_ref = dep.to_dependency_ref()
138
138
  install_path = dep_ref.get_install_path(apm_modules_dir)
@@ -228,6 +228,25 @@ def print_version(ctx, param, value):
228
228
  f"{TITLE}Agent Package Manager (APM) CLI{RESET} version {version_str}"
229
229
  )
230
230
 
231
+ # Gated verbose-version output (experimental flag)
232
+ try:
233
+ from ..core.experimental import is_enabled
234
+
235
+ if is_enabled("verbose_version"):
236
+ import platform
237
+ import sys
238
+
239
+ python_ver = platform.python_version()
240
+ plat = f"{sys.platform}-{platform.machine()}"
241
+ install_path = str(Path(__file__).resolve().parent.parent)
242
+
243
+ _rich_echo(f" {'Python:':<14}{python_ver}", color="dim")
244
+ _rich_echo(f" {'Platform:':<14}{plat}", color="dim")
245
+ _rich_echo(f" {'Install path:':<14}{install_path}", color="dim")
246
+ except Exception:
247
+ # Never let experimental flag logic break --version
248
+ pass
249
+
231
250
  ctx.exit()
232
251
 
233
252
 
@@ -462,6 +481,12 @@ def _create_minimal_apm_yml(config, plugin=False, target_path=None):
462
481
  "description": config["description"],
463
482
  "author": config["author"],
464
483
  "dependencies": {"apm": [], "mcp": []},
484
+ # Issue #887: scaffold with explicit consent for local content
485
+ # deployment so day-2 audit doesn't surprise the maintainer with
486
+ # an "includes not declared" advisory the moment they drop a
487
+ # primitive in .apm/. Override with an explicit path list to
488
+ # gate what gets deployed.
489
+ "includes": "auto",
465
490
  }
466
491
 
467
492
  if plugin:
@@ -166,8 +166,10 @@ def _get_validation_suggestion(error_msg):
166
166
  def _resolve_compile_target(target):
167
167
  """Map CLI target input to compiler-understood target string.
168
168
 
169
- The compiler only understands ``"vscode"``, ``"claude"``, and ``"all"``.
170
- Multi-target lists are mapped to the narrowest equivalent.
169
+ The compiler understands ``"vscode"``, ``"claude"``, ``"gemini"``,
170
+ and ``"all"``. Multi-target lists are mapped to the narrowest
171
+ equivalent; any combination of two or more distinct compiler
172
+ families collapses to ``"all"``.
171
173
 
172
174
  Args:
173
175
  target: A single target string, a list of target strings, or ``None``.
@@ -179,15 +181,18 @@ def _resolve_compile_target(target):
179
181
  return None # will trigger detect_target() auto-detection
180
182
  if isinstance(target, list):
181
183
  target_set = set(target)
182
- # Any target that produces AGENTS.md (copilot/vscode/agents/cursor/opencode/codex)
183
184
  has_agents_family = bool(
184
185
  target_set & {"copilot", "vscode", "agents", "cursor", "opencode", "codex"}
185
186
  )
186
187
  has_claude = "claude" in target_set
187
- if has_agents_family and has_claude:
188
+ has_gemini = "gemini" in target_set
189
+ distinct = sum([has_agents_family, has_claude, has_gemini])
190
+ if distinct >= 2:
188
191
  return "all"
189
192
  elif has_claude:
190
193
  return "claude"
194
+ elif has_gemini:
195
+ return "gemini"
191
196
  else:
192
197
  return "vscode" # agents-family only
193
198
  return target # single string pass-through
@@ -183,10 +183,25 @@ def _get_detailed_package_info(package_path: Path) -> Dict[str, Any]:
183
183
  package = APMPackage.from_apm_yml(apm_yml_path)
184
184
  context_count, workflow_count = _count_package_files(package_path)
185
185
  primitives = _count_primitives(package_path)
186
+ # HYBRID-aware description rendering: when apm.yml omits its
187
+ # tagline but a SKILL.md sits alongside, surface the empty
188
+ # apm.yml.description as `--` plus an inline annotation. The
189
+ # SKILL.md description is intentionally NOT borrowed -- it is
190
+ # an agent invocation matcher, not a human tagline.
191
+ is_hybrid = (package_path / "SKILL.md").exists()
192
+ if package.description:
193
+ desc = package.description
194
+ elif is_hybrid:
195
+ desc = (
196
+ "-- (set 'description' in apm.yml; SKILL.md "
197
+ "description is for agent runtime)"
198
+ )
199
+ else:
200
+ desc = 'No description'
186
201
  return {
187
202
  'name': package.name,
188
203
  'version': package.version or 'unknown',
189
- 'description': package.description or 'No description',
204
+ 'description': desc,
190
205
  'author': package.author or 'Unknown',
191
206
  'source': package.source or 'local',
192
207
  'install_path': str(package_path.resolve()),
@@ -336,7 +336,7 @@ def tree(global_):
336
336
  if lockfile_path.exists():
337
337
  lockfile = LockFile.read(lockfile_path)
338
338
  if lockfile:
339
- lockfile_deps = lockfile.get_all_dependencies()
339
+ lockfile_deps = lockfile.get_package_dependencies()
340
340
  except Exception:
341
341
  pass
342
342