apm-cli 0.16.1__tar.gz → 0.18.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 (388) hide show
  1. {apm_cli-0.16.1/src/apm_cli.egg-info → apm_cli-0.18.0}/PKG-INFO +1 -1
  2. {apm_cli-0.16.1 → apm_cli-0.18.0}/pyproject.toml +9 -9
  3. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/base.py +20 -7
  4. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/copilot.py +17 -18
  5. apm_cli-0.18.0/src/apm_cli/adapters/client/intellij.py +104 -0
  6. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/vscode.py +0 -1
  7. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/bundle/lockfile_enrichment.py +0 -1
  8. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/bundle/packer.py +0 -2
  9. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/bundle/plugin_exporter.py +12 -49
  10. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/bundle/unpacker.py +0 -1
  11. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cli.py +5 -0
  12. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -1
  13. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/_helpers.py +91 -2
  14. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/audit.py +199 -24
  15. apm_cli-0.18.0/src/apm_cli/commands/compile/cli.py +1002 -0
  16. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/config.py +179 -1
  17. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/deps/_utils.py +1 -1
  18. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/deps/cli.py +26 -32
  19. apm_cli-0.18.0/src/apm_cli/commands/find.py +241 -0
  20. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/init.py +42 -1
  21. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/install.py +118 -47
  22. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/list_cmd.py +0 -1
  23. apm_cli-0.18.0/src/apm_cli/commands/lock.py +206 -0
  24. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/__init__.py +37 -8
  25. apm_cli-0.18.0/src/apm_cli/commands/marketplace/audit.py +122 -0
  26. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -2
  27. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/remove.py +1 -1
  28. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/mcp.py +17 -7
  29. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/outdated.py +80 -9
  30. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/pack.py +115 -72
  31. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/policy.py +1 -3
  32. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/prune.py +2 -2
  33. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/publish.py +36 -10
  34. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/self_update.py +37 -3
  35. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/uninstall/cli.py +1 -2
  36. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/uninstall/engine.py +2 -2
  37. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/update.py +135 -71
  38. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/view.py +1 -2
  39. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/agents_compiler.py +194 -45
  40. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/claude_formatter.py +34 -19
  41. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/constitution.py +0 -1
  42. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/constitution_block.py +0 -1
  43. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/context_optimizer.py +28 -11
  44. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/distributed_compiler.py +72 -27
  45. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/gemini_formatter.py +0 -1
  46. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/injector.py +1 -2
  47. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/link_resolver.py +18 -2
  48. apm_cli-0.18.0/src/apm_cli/compilation/managed_section.py +96 -0
  49. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/template_builder.py +17 -7
  50. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/config.py +222 -1
  51. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/auth.py +10 -3
  52. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/azure_cli.py +0 -1
  53. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/build_orchestrator.py +129 -10
  54. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/command_logger.py +5 -2
  55. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/conflict_detector.py +1 -1
  56. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/docker_args.py +0 -2
  57. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/experimental.py +13 -0
  58. apm_cli-0.18.0/src/apm_cli/core/install_audit.py +176 -0
  59. apm_cli-0.18.0/src/apm_cli/core/plugin_manifest.py +467 -0
  60. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/safe_installer.py +1 -1
  61. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/scope.py +109 -9
  62. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/script_runner.py +0 -3
  63. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/target_detection.py +1 -1
  64. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/token_manager.py +1 -2
  65. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/aggregator.py +0 -3
  66. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/apm_resolver.py +112 -2
  67. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/artifactory_entry.py +1 -1
  68. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/dependency_graph.py +2 -2
  69. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/download_strategies.py +0 -2
  70. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/git_remote_ops.py +0 -1
  71. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/git_semver_resolver.py +9 -5
  72. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/github_downloader.py +4 -4
  73. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/lockfile.py +17 -2
  74. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/package_validator.py +1 -2
  75. apm_cli-0.18.0/src/apm_cli/deps/path_anchoring.py +151 -0
  76. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/plugin_parser.py +181 -4
  77. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry_proxy.py +1 -1
  78. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/transport_selection.py +2 -2
  79. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/verifier.py +0 -3
  80. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/drift.py +5 -3
  81. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/factory.py +2 -0
  82. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/context.py +23 -4
  83. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/drift.py +29 -13
  84. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/errors.py +1 -1
  85. apm_cli-0.18.0/src/apm_cli/install/lsp/__init__.py +5 -0
  86. apm_cli-0.18.0/src/apm_cli/install/lsp/integration.py +148 -0
  87. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/args.py +0 -1
  88. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/command.py +6 -4
  89. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/conflicts.py +7 -6
  90. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/entry.py +1 -1
  91. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/registry.py +21 -7
  92. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/warnings.py +0 -2
  93. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/writer.py +1 -1
  94. apm_cli-0.18.0/src/apm_cli/install/phases/_skip_logic.py +57 -0
  95. apm_cli-0.18.0/src/apm_cli/install/phases/audit.py +135 -0
  96. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/integrate.py +38 -6
  97. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/lockfile.py +7 -1
  98. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/resolve.py +175 -75
  99. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/pipeline.py +126 -46
  100. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/presentation/dry_run.py +1 -1
  101. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/request.py +7 -1
  102. apm_cli-0.18.0/src/apm_cli/install/root_redirect.py +102 -0
  103. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/service.py +2 -0
  104. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/services.py +69 -21
  105. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/sources.py +22 -9
  106. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/summary.py +4 -1
  107. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/template.py +9 -9
  108. apm_cli-0.18.0/src/apm_cli/install/validation.py +785 -0
  109. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/__init__.py +2 -0
  110. apm_cli-0.18.0/src/apm_cli/integration/_shared.py +71 -0
  111. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/agent_integrator.py +4 -7
  112. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/base_integrator.py +26 -3
  113. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/cleanup.py +0 -1
  114. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/command_integrator.py +1 -1
  115. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/dispatch.py +0 -1
  116. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/hook_integrator.py +80 -6
  117. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/instruction_integrator.py +158 -13
  118. apm_cli-0.18.0/src/apm_cli/integration/lsp_integrator.py +508 -0
  119. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/mcp_integrator.py +227 -239
  120. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/mcp_integrator_install.py +323 -215
  121. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/prompt_integrator.py +2 -5
  122. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/skill_integrator.py +353 -39
  123. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/skill_transformer.py +0 -1
  124. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/targets.py +33 -4
  125. apm_cli-0.18.0/src/apm_cli/marketplace/audit.py +337 -0
  126. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/builder.py +3 -7
  127. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/client.py +96 -14
  128. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/migration.py +0 -1
  129. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/pr_integration.py +0 -1
  130. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/publisher.py +160 -102
  131. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/ref_resolver.py +1 -2
  132. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/registry.py +0 -1
  133. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/resolver.py +59 -10
  134. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/semver.py +17 -24
  135. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/shadow_detector.py +0 -1
  136. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/tag_pattern.py +80 -3
  137. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/validator.py +0 -1
  138. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/version_pins.py +0 -1
  139. apm_cli-0.18.0/src/apm_cli/marketplace/version_resolver.py +114 -0
  140. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/yml_editor.py +0 -2
  141. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/yml_schema.py +1 -1
  142. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/__init__.py +27 -0
  143. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/apm_package.py +33 -0
  144. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/dependency/__init__.py +2 -0
  145. apm_cli-0.18.0/src/apm_cli/models/dependency/lsp.py +229 -0
  146. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/dependency/mcp.py +1 -1
  147. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/dependency/reference.py +134 -5
  148. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/dependency/types.py +0 -1
  149. apm_cli-0.18.0/src/apm_cli/models/format_detection.py +408 -0
  150. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/plugin.py +1 -1
  151. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/results.py +0 -1
  152. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/models/validation.py +47 -67
  153. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/output/formatters.py +0 -5
  154. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/output/models.py +0 -1
  155. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/output/script_formatters.py +0 -1
  156. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/ci_checks.py +14 -1
  157. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/discovery.py +0 -1
  158. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/inheritance.py +73 -2
  159. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/install_preflight.py +0 -3
  160. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/matcher.py +0 -1
  161. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/models.py +0 -1
  162. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/outcome_routing.py +1 -1
  163. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/parser.py +99 -1
  164. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/policy_checks.py +29 -41
  165. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/project_config.py +1 -1
  166. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/schema.py +72 -1
  167. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/primitives/discovery.py +0 -2
  168. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/primitives/models.py +2 -1
  169. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/primitives/parser.py +39 -4
  170. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/registry/client.py +1 -1
  171. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/registry/integration.py +1 -3
  172. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/registry/operations.py +0 -1
  173. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/base.py +1 -1
  174. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/codex_runtime.py +1 -3
  175. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/copilot_runtime.py +1 -2
  176. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/factory.py +1 -1
  177. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/llm_runtime.py +1 -3
  178. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/manager.py +0 -1
  179. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/security/audit_report.py +1 -1
  180. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/security/content_scanner.py +1 -2
  181. apm_cli-0.18.0/src/apm_cli/security/external/__init__.py +27 -0
  182. apm_cli-0.18.0/src/apm_cli/security/external/base.py +76 -0
  183. apm_cli-0.18.0/src/apm_cli/security/external/gate.py +35 -0
  184. apm_cli-0.18.0/src/apm_cli/security/external/generic_sarif.py +85 -0
  185. apm_cli-0.18.0/src/apm_cli/security/external/options.py +179 -0
  186. apm_cli-0.18.0/src/apm_cli/security/external/registry.py +41 -0
  187. apm_cli-0.18.0/src/apm_cli/security/external/runner.py +100 -0
  188. apm_cli-0.18.0/src/apm_cli/security/external/sarif_ingest.py +149 -0
  189. apm_cli-0.18.0/src/apm_cli/security/external/skillspector.py +175 -0
  190. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/security/file_scanner.py +0 -1
  191. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/security/gate.py +1 -1
  192. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/console.py +1 -2
  193. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/content_hash.py +0 -1
  194. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/diagnostics.py +2 -3
  195. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/exclude.py +10 -1
  196. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/helpers.py +0 -2
  197. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/paths.py +23 -0
  198. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/version_checker.py +83 -8
  199. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/yaml_io.py +1 -1
  200. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/workflow/runner.py +0 -2
  201. {apm_cli-0.16.1 → apm_cli-0.18.0/src/apm_cli.egg-info}/PKG-INFO +1 -1
  202. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli.egg-info/SOURCES.txt +28 -0
  203. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_apm_package_models.py +156 -0
  204. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_apm_resolver.py +192 -1
  205. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_distributed_compilation.py +48 -0
  206. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_lockfile.py +55 -0
  207. apm_cli-0.16.1/src/apm_cli/commands/compile/cli.py +0 -919
  208. apm_cli-0.16.1/src/apm_cli/install/validation.py +0 -701
  209. {apm_cli-0.16.1 → apm_cli-0.18.0}/AUTHORS +0 -0
  210. {apm_cli-0.16.1 → apm_cli-0.18.0}/LICENSE +0 -0
  211. {apm_cli-0.16.1 → apm_cli-0.18.0}/NOTICE +0 -0
  212. {apm_cli-0.16.1 → apm_cli-0.18.0}/README.md +0 -0
  213. {apm_cli-0.16.1 → apm_cli-0.18.0}/setup.cfg +0 -0
  214. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/__init__.py +0 -0
  215. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/__init__.py +0 -0
  216. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/__init__.py +0 -0
  217. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
  218. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/claude.py +0 -0
  219. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/codex.py +0 -0
  220. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/cursor.py +0 -0
  221. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/gemini.py +0 -0
  222. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/opencode.py +0 -0
  223. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
  224. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  225. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
  226. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  227. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/bundle/__init__.py +0 -0
  228. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/bundle/local_bundle.py +0 -0
  229. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/__init__.py +0 -0
  230. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/git_cache.py +0 -0
  231. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/http_cache.py +0 -0
  232. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/integrity.py +0 -0
  233. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/locking.py +0 -0
  234. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/paths.py +0 -0
  235. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/cache/url_normalize.py +0 -0
  236. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/__init__.py +0 -0
  237. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/cache.py +0 -0
  238. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/compile/__init__.py +0 -0
  239. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/compile/watcher.py +0 -0
  240. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/deps/__init__.py +0 -0
  241. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/deps/why.py +0 -0
  242. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/doctor.py +0 -0
  243. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/experimental.py +0 -0
  244. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/check.py +0 -0
  245. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
  246. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/init.py +0 -0
  247. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
  248. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
  249. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
  250. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
  251. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
  252. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
  253. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
  254. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/plugin/init.py +0 -0
  255. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/run.py +0 -0
  256. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/runtime.py +0 -0
  257. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/targets.py +0 -0
  258. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
  259. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/__init__.py +0 -0
  260. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/build_id.py +0 -0
  261. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/constants.py +0 -0
  262. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/compilation/output_writer.py +0 -0
  263. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/constants.py +0 -0
  264. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/__init__.py +0 -0
  265. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/apm_yml.py +0 -0
  266. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/errors.py +0 -0
  267. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/null_logger.py +0 -0
  268. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/core/operations.py +0 -0
  269. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/__init__.py +0 -0
  270. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/_shared.py +0 -0
  271. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
  272. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/bare_cache.py +0 -0
  273. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/clone_engine.py +0 -0
  274. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/git_auth_env.py +0 -0
  275. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/git_reference_resolver.py +0 -0
  276. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/github_downloader_validation.py +0 -0
  277. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/host_backends.py +0 -0
  278. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/installed_package.py +0 -0
  279. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/outdated_row.py +0 -0
  280. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/__init__.py +0 -0
  281. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/auth.py +0 -0
  282. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/client.py +0 -0
  283. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
  284. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/extractor.py +0 -0
  285. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
  286. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/outdated.py +0 -0
  287. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/resolver.py +0 -0
  288. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/registry/semver.py +0 -0
  289. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
  290. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
  291. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/deps/why_walker.py +0 -0
  292. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/__init__.py +0 -0
  293. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
  294. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/cache_pin.py +0 -0
  295. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
  296. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/heals/__init__.py +0 -0
  297. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/heals/base.py +0 -0
  298. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
  299. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
  300. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/helpers/__init__.py +0 -0
  301. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
  302. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/insecure_policy.py +0 -0
  303. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
  304. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/mcp/__init__.py +0 -0
  305. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/package_resolution.py +0 -0
  306. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/package_selection.py +0 -0
  307. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/__init__.py +0 -0
  308. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/_redownload.py +0 -0
  309. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/cleanup.py +0 -0
  310. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/download.py +0 -0
  311. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/finalize.py +0 -0
  312. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/heal.py +0 -0
  313. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/local_content.py +0 -0
  314. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
  315. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
  316. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
  317. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/phases/targets.py +0 -0
  318. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/plan.py +0 -0
  319. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/presentation/__init__.py +0 -0
  320. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/registry_wiring.py +0 -0
  321. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/install/skill_path_migration.py +0 -0
  322. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
  323. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
  324. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
  325. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
  326. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
  327. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/coverage.py +0 -0
  328. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
  329. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/integration/utils.py +0 -0
  330. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/__init__.py +0 -0
  331. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
  332. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/_io.py +0 -0
  333. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/_shared.py +0 -0
  334. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
  335. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/drift_check.py +0 -0
  336. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/errors.py +0 -0
  337. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
  338. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/init_template.py +0 -0
  339. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/models.py +0 -0
  340. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/output_mappers.py +0 -0
  341. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
  342. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/marketplace/version_check.py +0 -0
  343. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/output/__init__.py +0 -0
  344. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/__init__.py +0 -0
  345. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
  346. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/_help_text.py +0 -0
  347. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/policy/_shared.py +0 -0
  348. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/primitives/__init__.py +0 -0
  349. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/registry/__init__.py +0 -0
  350. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/__init__.py +0 -0
  351. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/runtime/utils.py +0 -0
  352. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/security/__init__.py +0 -0
  353. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/update_policy.py +0 -0
  354. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/__init__.py +0 -0
  355. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/atomic_io.py +0 -0
  356. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/file_ops.py +0 -0
  357. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/git_env.py +0 -0
  358. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/git_sparse.py +0 -0
  359. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/github_host.py +0 -0
  360. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/guards.py +0 -0
  361. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/install_tui.py +0 -0
  362. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/normalization.py +0 -0
  363. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/path_security.py +0 -0
  364. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/patterns.py +0 -0
  365. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/perf_stats.py +0 -0
  366. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/reflink.py +0 -0
  367. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/short_sha.py +0 -0
  368. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/utils/subprocess_env.py +0 -0
  369. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/version.py +0 -0
  370. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/workflow/__init__.py +0 -0
  371. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/workflow/discovery.py +0 -0
  372. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli/workflow/parser.py +0 -0
  373. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  374. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
  375. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli.egg-info/requires.txt +0 -0
  376. {apm_cli-0.16.1 → apm_cli-0.18.0}/src/apm_cli.egg-info/top_level.txt +0 -0
  377. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_codex_docker_args_fix.py +0 -0
  378. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
  379. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_collision_integration.py +0 -0
  380. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_console.py +0 -0
  381. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_empty_string_and_defaults.py +0 -0
  382. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_enhanced_discovery.py +0 -0
  383. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_github_downloader.py +0 -0
  384. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_github_downloader_token_precedence.py +0 -0
  385. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_runnable_prompts.py +0 -0
  386. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_runtime_manager_token_precedence.py +0 -0
  387. {apm_cli-0.16.1 → apm_cli-0.18.0}/tests/test_token_manager.py +0 -0
  388. {apm_cli-0.16.1 → apm_cli-0.18.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.16.1
3
+ Version: 0.18.0
4
4
  Summary: MCP configuration tool
5
5
  Author-email: Daniel Meppiel <user@example.com>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apm-cli"
7
- version = "0.16.1"
7
+ version = "0.18.0"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -88,21 +88,22 @@ ignore = [
88
88
  "S602", # subprocess-popen-with-shell -- intentional in CLI wrapping git/pip
89
89
  # --- Deferred rules: enable in dedicated future PRs ---
90
90
  "SIM102", # collapsible-if -- 28 violations, auto-fixable style improvement
91
- "RUF003", # ambiguous-unicode-character-comment -- 14 violations, review against ASCII convention
92
- "RUF002", # ambiguous-unicode-character-docstring -- 8 violations, same as above
93
91
  ]
94
92
 
95
93
  [tool.ruff.lint.pylint]
96
94
  # High initial thresholds set just above current codebase maximums.
97
95
  # Prevents new code from exceeding the worst existing violations.
98
96
  # Tighten these over time via dedicated refactoring PRs.
99
- max-statements = 275 # current max: 269 (mcp_integrator.py::install)
100
- max-args = 18 # current max: 16 (commands/install.py)
101
- max-branches = 115 # current max: 108 (mcp_integrator.py::install)
102
- max-returns = 18 # current max: 16 (marketplace/publisher.py)
97
+ # Stage 1 thresholds (PR #1464, issue #1077).
98
+ # Roadmap: Stage 2 targets max-complexity<=20, max-branches<=25 (McCabe standard).
99
+ max-statements = 200 # Stage 1 (was 275)
100
+ max-args = 15 # Stage 1 (was 18)
101
+ max-branches = 60 # Stage 1 (was 115)
102
+ max-returns = 12 # Stage 1 (was 18)
103
103
 
104
104
  [tool.ruff.lint.mccabe]
105
- max-complexity = 100 # current max: 97 (mcp_integrator.py::install)
105
+ # Stage 1 (was 100). Stage 2 target: <=20 (McCabe industry standard).
106
+ max-complexity = 50
106
107
 
107
108
  [tool.ruff.lint.per-file-ignores]
108
109
  # Subprocess calls are intentional in a CLI tool
@@ -120,7 +121,6 @@ max-complexity = 100 # current max: 97 (mcp_integrator.py::install)
120
121
  "src/apm_cli/commands/init.py" = ["F821", "S603", "S607"]
121
122
  "src/apm_cli/install/pipeline.py" = ["F821", "S603", "S607"]
122
123
  "src/apm_cli/integration/skill_integrator.py" = ["F821", "S603", "S607"]
123
-
124
124
  [tool.ruff.format]
125
125
  quote-style = "double"
126
126
  indent-style = "space"
@@ -150,6 +150,21 @@ class MCPClientAdapter(ABC):
150
150
  self._last_env_placeholder_keys: set[str] = set()
151
151
  self._last_legacy_angle_vars: set[str] = set()
152
152
 
153
+ def _format_runtime_env_placeholder(self, name: str) -> str:
154
+ """Return the target runtime's env-var placeholder syntax for *name*."""
155
+ return "${" + name + "}"
156
+
157
+ def _translate_env_placeholder_for_runtime(self, value):
158
+ """Translate env-var placeholders to this adapter's runtime syntax."""
159
+ if not isinstance(value, str):
160
+ return value
161
+
162
+ def _to_runtime(match):
163
+ var_name = match.group(1) or match.group(2)
164
+ return self._format_runtime_env_placeholder(var_name)
165
+
166
+ return _ENV_PLACEHOLDER_RE.sub(_to_runtime, value)
167
+
153
168
  @property
154
169
  def project_root(self) -> Path:
155
170
  """Return the explicit project root or the current working directory."""
@@ -390,9 +405,7 @@ class MCPClientAdapter(ABC):
390
405
  continue
391
406
  if _has_env_placeholder(raw_value):
392
407
  self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(raw_value))
393
- translated[name] = _translate_env_placeholder(raw_value)
394
- # Record every ${VAR} in the translated value (handles
395
- # both ${env:VAR} -> ${VAR} and bare ${VAR} cases).
408
+ translated[name] = self._translate_env_placeholder_for_runtime(raw_value)
396
409
  placeholder_keys.extend(
397
410
  m.group(1) for m in _ENV_VAR_RE.finditer(translated[name])
398
411
  )
@@ -403,7 +416,7 @@ class MCPClientAdapter(ABC):
403
416
  else:
404
417
  # Literal value present in apm.yml -- replace with a
405
418
  # runtime placeholder so the secret never touches disk.
406
- translated[name] = "${" + name + "}"
419
+ translated[name] = self._format_runtime_env_placeholder(name)
407
420
  placeholder_keys.append(name)
408
421
  self._last_env_placeholder_keys = set(placeholder_keys)
409
422
  return translated
@@ -421,7 +434,7 @@ class MCPClientAdapter(ABC):
421
434
  if name in self._DEFAULT_GITHUB_ENV:
422
435
  resolved[name] = self._DEFAULT_GITHUB_ENV[name]
423
436
  else:
424
- resolved[name] = "${" + name + "}"
437
+ resolved[name] = self._format_runtime_env_placeholder(name)
425
438
  placeholder_keys.append(name)
426
439
  self._last_env_placeholder_keys = set(placeholder_keys)
427
440
  return resolved
@@ -510,7 +523,7 @@ class MCPClientAdapter(ABC):
510
523
  self._last_env_placeholder_keys.update(legacy_keys)
511
524
  for match in _ENV_VAR_RE.finditer(value):
512
525
  self._last_env_placeholder_keys.add(match.group(1))
513
- return _translate_env_placeholder(value)
526
+ return self._translate_env_placeholder_for_runtime(value)
514
527
 
515
528
  from rich.prompt import Prompt
516
529
 
@@ -560,7 +573,7 @@ class MCPClientAdapter(ABC):
560
573
 
561
574
  if self._supports_runtime_env_substitution:
562
575
  self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(processed))
563
- processed = _translate_env_placeholder(processed)
576
+ processed = self._translate_env_placeholder_for_runtime(processed)
564
577
  else:
565
578
  # Resolve only the legacy ``<VAR>`` form; newer syntaxes are
566
579
  # preserved verbatim for backward compatibility.
@@ -26,7 +26,9 @@ from .base import (
26
26
  _extract_legacy_angle_vars,
27
27
  _has_env_placeholder,
28
28
  _stringify_env_literal,
29
- _translate_env_placeholder,
29
+ )
30
+ from .base import (
31
+ _translate_env_placeholder as _translate_env_placeholder,
30
32
  )
31
33
 
32
34
 
@@ -117,12 +119,12 @@ class CopilotClientAdapter(MCPClientAdapter):
117
119
  """
118
120
  current_config = self.get_current_config()
119
121
 
120
- # Ensure mcpServers section exists
121
- if "mcpServers" not in current_config:
122
- current_config["mcpServers"] = {}
122
+ # Ensure servers section exists and is a dict (guard against malformed config).
123
+ if not isinstance(current_config.get(self.mcp_servers_key), dict):
124
+ current_config[self.mcp_servers_key] = {}
123
125
 
124
126
  # Apply updates
125
- current_config["mcpServers"].update(config_updates)
127
+ current_config[self.mcp_servers_key].update(config_updates)
126
128
 
127
129
  # Write back to file
128
130
  config_path = Path(self.get_config_path())
@@ -259,7 +261,7 @@ class CopilotClientAdapter(MCPClientAdapter):
259
261
  current = self.get_current_config()
260
262
  except Exception:
261
263
  return set(), False
262
- servers = current.get("mcpServers") or {}
264
+ servers = current.get(self.mcp_servers_key) or {}
263
265
  # Match the same key resolution rule used below.
264
266
  if server_name:
265
267
  key = server_name
@@ -678,9 +680,9 @@ class CopilotClientAdapter(MCPClientAdapter):
678
680
  # ({NAME: value-or-placeholder}); registry-sourced deps pass a list
679
681
  # of {name, description, required} dicts. Translate-mode handling
680
682
  # for the dict shape: each value is either already a placeholder
681
- # (translate it to the canonical ${VAR} form) or a literal (record
682
- # the key as a placeholder reference and emit ${NAME} so the
683
- # value never lands on disk). See issue #1152.
683
+ # (translate it to the adapter's runtime form) or a literal
684
+ # (record the key as a placeholder reference and emit a runtime
685
+ # placeholder so the value never lands on disk). See issue #1152.
684
686
  if isinstance(env_vars, dict) and self._supports_runtime_env_substitution:
685
687
  translated = {}
686
688
  placeholder_keys = []
@@ -694,9 +696,7 @@ class CopilotClientAdapter(MCPClientAdapter):
694
696
  continue
695
697
  if _has_env_placeholder(raw_value):
696
698
  self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(raw_value))
697
- translated[name] = _translate_env_placeholder(raw_value)
698
- # Record every ${VAR} in the translated value (handles
699
- # both ${env:VAR} -> ${VAR} and bare ${VAR} cases).
699
+ translated[name] = self._translate_env_placeholder_for_runtime(raw_value)
700
700
  for match in _ENV_VAR_RE.finditer(translated[name]):
701
701
  placeholder_keys.append(match.group(1))
702
702
  elif name in default_github_env and raw_value == default_github_env[name]:
@@ -704,7 +704,7 @@ class CopilotClientAdapter(MCPClientAdapter):
704
704
  else:
705
705
  # Literal value present in apm.yml -- replace with a
706
706
  # runtime placeholder so the secret never touches disk.
707
- translated[name] = "${" + name + "}"
707
+ translated[name] = self._format_runtime_env_placeholder(name)
708
708
  placeholder_keys.append(name)
709
709
  self._last_env_placeholder_keys = set(placeholder_keys)
710
710
  return translated
@@ -722,10 +722,9 @@ class CopilotClientAdapter(MCPClientAdapter):
722
722
  # Non-secret literal default -- preserve as-is.
723
723
  resolved[name] = default_github_env[name]
724
724
  else:
725
- # Emit a runtime-substitution placeholder; Copilot CLI
726
- # resolves ``${NAME}`` from the host environment at
727
- # server-start. APM never reads or stores the value.
728
- resolved[name] = "${" + name + "}"
725
+ # Emit a runtime-substitution placeholder; APM never reads
726
+ # or stores the value.
727
+ resolved[name] = self._format_runtime_env_placeholder(name)
729
728
  placeholder_keys.append(name)
730
729
  # Record for the post-install summary line and the
731
730
  # security-improvement notice.
@@ -783,7 +782,7 @@ class CopilotClientAdapter(MCPClientAdapter):
783
782
  # them (the env-block path tracks via _resolve_environment_variables).
784
783
  for match in _ENV_VAR_RE.finditer(value):
785
784
  self._last_env_placeholder_keys.add(match.group(1))
786
- return _translate_env_placeholder(value)
785
+ return self._translate_env_placeholder_for_runtime(value)
787
786
 
788
787
  import sys
789
788
 
@@ -0,0 +1,104 @@
1
+ """JetBrains Copilot implementation of the MCP client adapter.
2
+
3
+ GitHub Copilot for JetBrains IDEs stores MCP server configuration under
4
+ a user-scope directory whose location is OS-dependent:
5
+
6
+ Windows : %LOCALAPPDATA%\\github-copilot\\intellij\\mcp.json
7
+ macOS : ~/Library/Application Support/github-copilot/intellij/mcp.json
8
+ Linux : $XDG_DATA_HOME/github-copilot/intellij/mcp.json
9
+ (defaults to ~/.local/share/github-copilot/intellij/mcp.json)
10
+
11
+ The configuration file uses a top-level ``"servers"`` key (unlike most
12
+ other Copilot-family adapters which use ``"mcpServers"``).
13
+
14
+ Ref: https://github.com/orgs/community/discussions/139762
15
+ """
16
+
17
+ import os
18
+ import sys
19
+ from pathlib import Path
20
+
21
+ from ...utils.path_security import PathTraversalError, ensure_path_within
22
+ from .copilot import CopilotClientAdapter
23
+
24
+
25
+ def _intellij_config_dir() -> Path:
26
+ """Return the OS-specific JetBrains Copilot config directory.
27
+
28
+ Does not guarantee the directory exists; callers that need to write
29
+ to it should call ``mkdir(parents=True, exist_ok=True)`` first.
30
+
31
+ Raises
32
+ ------
33
+ PathTraversalError
34
+ If the environment variable the location is derived from
35
+ (``LOCALAPPDATA`` on Windows, ``XDG_DATA_HOME`` on Linux) is unset
36
+ or not absolute. Without this guard an empty ``LOCALAPPDATA``
37
+ would yield ``Path("")`` -- a *relative* path -- causing APM to
38
+ silently read/write ``./github-copilot/intellij/mcp.json`` in the
39
+ current working directory and to falsely auto-detect the runtime.
40
+ """
41
+ if sys.platform == "win32":
42
+ local_app_data = os.environ.get("LOCALAPPDATA", "")
43
+ if not local_app_data or not os.path.isabs(local_app_data):
44
+ raise PathTraversalError(
45
+ "LOCALAPPDATA is unset or not an absolute path; cannot locate the "
46
+ "JetBrains Copilot configuration directory."
47
+ )
48
+ return Path(local_app_data) / "github-copilot" / "intellij"
49
+
50
+ if sys.platform == "darwin":
51
+ return Path.home() / "Library" / "Application Support" / "github-copilot" / "intellij"
52
+
53
+ # Linux: honour $XDG_DATA_HOME, fall back to ~/.local/share
54
+ xdg_data = os.environ.get("XDG_DATA_HOME", "")
55
+ if xdg_data:
56
+ if not os.path.isabs(xdg_data):
57
+ raise PathTraversalError(
58
+ "XDG_DATA_HOME is set to a non-absolute path; cannot locate the "
59
+ "JetBrains Copilot configuration directory."
60
+ )
61
+ return Path(xdg_data) / "github-copilot" / "intellij"
62
+ return Path.home() / ".local" / "share" / "github-copilot" / "intellij"
63
+
64
+
65
+ class IntelliJClientAdapter(CopilotClientAdapter):
66
+ """MCP client adapter for GitHub Copilot inside JetBrains IDEs.
67
+
68
+ JetBrains Copilot stores server definitions in a user-scope JSON file
69
+ with a ``"servers"`` top-level key (not ``"mcpServers"``). This adapter
70
+ inherits registry-resolution and config read/write from
71
+ :class:`CopilotClientAdapter` and overrides the config path,
72
+ ``mcp_servers_key``, and runtime env-var placeholder syntax so the parent
73
+ methods operate on the correct schema.
74
+ """
75
+
76
+ supports_user_scope: bool = True
77
+ _client_label: str = "JetBrains Copilot"
78
+ target_name: str = "intellij"
79
+ mcp_servers_key: str = "servers"
80
+
81
+ # JetBrains Copilot resolves ${env:VAR} placeholders in mcp.json at
82
+ # server-start, so APM must not bake matching host secrets to disk.
83
+ _supports_runtime_env_substitution: bool = True
84
+
85
+ def _format_runtime_env_placeholder(self, name: str) -> str:
86
+ """Return JetBrains Copilot's native env-var placeholder syntax."""
87
+ return "${env:" + name + "}"
88
+
89
+ # ------------------------------------------------------------------ #
90
+ # Config path
91
+ # ------------------------------------------------------------------ #
92
+
93
+ def get_config_path(self) -> str:
94
+ """Return the OS-specific path to ``mcp.json`` for JetBrains Copilot.
95
+
96
+ The config directory is derived from an environment variable
97
+ (``LOCALAPPDATA`` / ``XDG_DATA_HOME``). Validate it resolves
98
+ inside the user's home directory before any read/write so a
99
+ tampered or unexpected environment cannot redirect APM's writes
100
+ outside the user-scope tree (supply-chain hardening).
101
+ """
102
+ config_dir = _intellij_config_dir()
103
+ ensure_path_within(config_dir, Path.home())
104
+ return str(config_dir / "mcp.json")
@@ -6,7 +6,6 @@ https://code.visualstudio.com/docs/copilot/chat/mcp-servers
6
6
  """
7
7
 
8
8
  import json
9
- import os # noqa: F401
10
9
  import re
11
10
  from pathlib import Path
12
11
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  import posixpath
4
4
  from datetime import datetime, timezone
5
- from typing import Dict, List, Tuple, Union # noqa: F401, UP035
6
5
 
7
6
  from ..deps.lockfile import LockFile
8
7
  from ..integration.targets import KNOWN_TARGETS
@@ -1,11 +1,9 @@
1
1
  """Bundle packer -- creates self-contained APM bundles from the resolved dependency tree."""
2
2
 
3
- import os # noqa: F401
4
3
  import shutil
5
4
  import tarfile
6
5
  from dataclasses import dataclass, field
7
6
  from pathlib import Path
8
- from typing import Dict, List, Optional, Union # noqa: F401, UP035
9
7
 
10
8
  from ..core.target_detection import detect_target
11
9
  from ..deps.lockfile import LockFile, get_lockfile_path, migrate_lockfile_if_needed
@@ -9,12 +9,10 @@ under ``pack.bundle_files`` (issue #1098).
9
9
 
10
10
  import hashlib
11
11
  import json
12
- import os # noqa: F401
13
12
  import re
14
13
  import shutil
15
14
  import tarfile
16
15
  from pathlib import Path, PurePosixPath
17
- from typing import Dict, List, Optional, Set, Tuple # noqa: F401, UP035
18
16
 
19
17
  import yaml
20
18
 
@@ -25,7 +23,7 @@ from ..deps.lockfile import (
25
23
  migrate_lockfile_if_needed,
26
24
  )
27
25
  from ..models.apm_package import APMPackage, DependencyReference
28
- from ..utils.console import _rich_info, _rich_warning
26
+ from ..utils.console import _rich_warning
29
27
  from ..utils.path_security import PathTraversalError, ensure_path_within, safe_rmtree
30
28
  from .packer import PackResult
31
29
 
@@ -270,17 +268,9 @@ def _collect_hooks_from_root(package_root: Path) -> dict:
270
268
 
271
269
  def _collect_mcp(package_root: Path) -> dict:
272
270
  """Return ``mcpServers`` dict from ``.mcp.json``."""
273
- mcp_file = package_root / ".mcp.json"
274
- if not mcp_file.is_file() or mcp_file.is_symlink():
275
- return {}
276
- try:
277
- data = json.loads(mcp_file.read_text(encoding="utf-8"))
278
- if isinstance(data, dict):
279
- servers = data.get("mcpServers", {})
280
- return dict(servers) if isinstance(servers, dict) else {}
281
- except (json.JSONDecodeError, OSError):
282
- pass
283
- return {}
271
+ from ..core.plugin_manifest import collect_mcp_servers
272
+
273
+ return collect_mcp_servers(package_root)
284
274
 
285
275
 
286
276
  # ---------------------------------------------------------------------------
@@ -339,42 +329,15 @@ def _find_or_synthesize_plugin_json(
339
329
  suppress_missing_warning: bool = False,
340
330
  logger=None,
341
331
  ) -> dict:
342
- """Locate an existing ``plugin.json`` or synthesise one from ``apm.yml``.
343
-
344
- ``suppress_missing_warning`` silences the "no plugin.json on disk"
345
- message when the caller knows synthesis is the expected path -- for
346
- example a marketplace-publishing run that happens to have
347
- ``dependencies:`` for local dev. Genuine parse errors on an existing
348
- file are always surfaced.
349
- """
350
- from ..deps.plugin_parser import synthesize_plugin_json_from_apm_yml
351
- from ..utils.helpers import find_plugin_json
352
-
353
- plugin_json_path = find_plugin_json(project_root)
354
- if plugin_json_path is not None:
355
- try:
356
- return json.loads(plugin_json_path.read_text(encoding="utf-8"))
357
- except (json.JSONDecodeError, OSError) as exc:
358
- _warn_msg = (
359
- f"Found plugin.json at {plugin_json_path} but could not parse it: {exc}. "
360
- "Falling back to synthesis from apm.yml."
361
- )
362
- if logger:
363
- logger.warning(_warn_msg)
364
- else:
365
- _rich_warning(_warn_msg)
332
+ """Locate an existing ``plugin.json`` or synthesise one from ``apm.yml``."""
333
+ from ..core.plugin_manifest import find_or_synthesize_plugin_json
366
334
 
367
- elif not suppress_missing_warning:
368
- # Demoted from warning to info: synthesis from apm.yml is the
369
- # APM-native happy path for plugin authoring, not a defect.
370
- _info_msg = (
371
- "No plugin.json on disk; deriving it from apm.yml (the APM-native source of truth)."
372
- )
373
- if logger:
374
- logger.info(_info_msg)
375
- else:
376
- _rich_info(_info_msg)
377
- return synthesize_plugin_json_from_apm_yml(apm_yml_path)
335
+ return find_or_synthesize_plugin_json(
336
+ project_root,
337
+ apm_yml_path,
338
+ suppress_missing_warning=suppress_missing_warning,
339
+ logger=logger,
340
+ )
378
341
 
379
342
 
380
343
  def _has_marketplace_block(apm_yml_path: Path) -> bool:
@@ -6,7 +6,6 @@ import tarfile
6
6
  import tempfile
7
7
  from dataclasses import dataclass, field
8
8
  from pathlib import Path, PureWindowsPath
9
- from typing import Dict, List # noqa: F401, UP035
10
9
 
11
10
  from ..deps.lockfile import LEGACY_LOCKFILE_NAME, LOCKFILE_NAME, LockFile
12
11
  from ..utils.path_security import PathTraversalError, validate_path_segments
@@ -24,9 +24,11 @@ from apm_cli.commands.config import config
24
24
  from apm_cli.commands.deps import deps
25
25
  from apm_cli.commands.doctor import doctor
26
26
  from apm_cli.commands.experimental import experimental
27
+ from apm_cli.commands.find import find as find_cmd
27
28
  from apm_cli.commands.init import init
28
29
  from apm_cli.commands.install import install
29
30
  from apm_cli.commands.list_cmd import list as list_cmd
31
+ from apm_cli.commands.lock import lock
30
32
  from apm_cli.commands.marketplace import marketplace
31
33
  from apm_cli.commands.marketplace import search as marketplace_search
32
34
  from apm_cli.commands.mcp import mcp
@@ -50,6 +52,7 @@ _CLI_EPILOG = (
50
52
  " apm init Scaffold a new project\n"
51
53
  " apm install Install dependencies from apm.yml\n"
52
54
  " apm install --frozen Reproduce lockfile exactly (CI-safe)\n"
55
+ " apm lock Resolve deps and write lockfile only\n"
53
56
  " apm outdated See what's drifted from upstream\n"
54
57
  " apm update Refresh refs and rewrite the lockfile\n"
55
58
  " apm audit --ci Validate lockfile integrity for CI gates\n"
@@ -108,6 +111,7 @@ cli.add_command(unpack_cmd, name="unpack")
108
111
  cli.add_command(publish_cmd, name="publish")
109
112
  cli.add_command(init)
110
113
  cli.add_command(install)
114
+ cli.add_command(lock)
111
115
  cli.add_command(uninstall)
112
116
  cli.add_command(prune)
113
117
  cli.add_command(update)
@@ -126,6 +130,7 @@ cli.add_command(policy)
126
130
  cli.add_command(outdated_cmd, name="outdated")
127
131
  cli.add_command(doctor)
128
132
  cli.add_command(marketplace)
133
+ cli.add_command(find_cmd)
129
134
  cli.add_command(marketplace_search, name="search")
130
135
 
131
136
 
@@ -6,7 +6,6 @@ field. Keeps write-back logic isolated and unit-testable.
6
6
  """
7
7
 
8
8
  from pathlib import Path
9
- from typing import List, Optional # noqa: F401, UP035
10
9
 
11
10
  from ..models.dependency.reference import DependencyReference
12
11
  from ..utils.yaml_io import dump_yaml, load_yaml
@@ -14,14 +14,15 @@ from colorama import Fore, Style
14
14
  from colorama import init as colorama_init
15
15
 
16
16
  from ..constants import (
17
- APM_LOCK_FILENAME, # noqa: F401
18
17
  APM_MODULES_DIR,
19
18
  APM_MODULES_GITIGNORE_PATTERN,
20
19
  APM_YML_FILENAME,
21
20
  GITIGNORE_FILENAME,
22
21
  )
23
22
  from ..update_policy import get_update_hint_message, is_self_update_enabled
24
- from ..utils.atomic_io import atomic_write_text as _atomic_write # noqa: F401
23
+ from ..utils.atomic_io import (
24
+ atomic_write_text as _atomic_write, # noqa: F401 -- re-exported; tests import from apm_cli.commands._helpers
25
+ )
25
26
  from ..utils.console import _rich_echo, _rich_info, _rich_warning
26
27
  from ..utils.path_security import PathTraversalError, validate_path_segments
27
28
  from ..utils.version_checker import check_for_updates
@@ -331,6 +332,74 @@ def _check_orphaned_packages():
331
332
  return []
332
333
 
333
334
 
335
+ # ------------------------------------------------------------------
336
+ # Dependency-update helpers (shared by `apm update` and `apm deps update`)
337
+ # ------------------------------------------------------------------
338
+
339
+
340
+ class UnknownPackageError(Exception):
341
+ """A requested package token matched no declared dependency.
342
+
343
+ Carries the offending *token* and the list of *available* display
344
+ names so the CLI caller can render a consistent error message.
345
+ """
346
+
347
+ def __init__(self, token: str, available: list[str]):
348
+ self.token = token
349
+ self.available = available
350
+ super().__init__(f"Package '{token}' not found")
351
+
352
+
353
+ def resolve_requested_packages(
354
+ packages: tuple[str, ...] | list[str],
355
+ all_deps: list,
356
+ ) -> list[str] | None:
357
+ """Map requested package tokens to canonical dependency keys.
358
+
359
+ The install engine matches ``only_packages`` by ``DependencyReference``
360
+ identity (e.g. ``owner/repo``), so short names like ``compliance-rules``
361
+ must be normalised to their canonical key before the engine is called.
362
+
363
+ Args:
364
+ packages: Raw tokens from the CLI ``[PACKAGES]...`` argument.
365
+ all_deps: Declared dependencies (direct + dev) to match against.
366
+
367
+ Returns:
368
+ An order-preserving, de-duplicated list of canonical keys, or
369
+ ``None`` when *packages* is empty (meaning "refresh everything").
370
+
371
+ Raises:
372
+ UnknownPackageError: when a token matches no declared dependency.
373
+ """
374
+ if not packages:
375
+ return None
376
+
377
+ token_to_canonical: dict[str, str] = {}
378
+ for dep in all_deps:
379
+ canonical_key = dep.get_unique_key() or dep.repo_url or dep.get_display_name()
380
+ tokens = {canonical_key, dep.get_display_name(), dep.repo_url}
381
+ if getattr(dep, "alias", None):
382
+ tokens.add(dep.alias)
383
+ parts = dep.repo_url.split("/")
384
+ if len(parts) >= 2:
385
+ tokens.add(parts[-1])
386
+ for token in tokens:
387
+ if token and token not in token_to_canonical:
388
+ token_to_canonical[token] = canonical_key
389
+
390
+ resolved: list[str] = []
391
+ seen: set[str] = set()
392
+ for pkg in packages:
393
+ canonical = token_to_canonical.get(pkg)
394
+ if not canonical:
395
+ available = [dep.get_display_name() for dep in all_deps]
396
+ raise UnknownPackageError(pkg, available)
397
+ if canonical not in seen:
398
+ seen.add(canonical)
399
+ resolved.append(canonical)
400
+ return resolved
401
+
402
+
334
403
  def print_version(ctx, param, value):
335
404
  """Print version and exit."""
336
405
  if not value or ctx.resilient_parsing:
@@ -656,3 +725,23 @@ def _create_minimal_apm_yml(config, plugin=False, target_path=None):
656
725
  content = content.replace("dependencies:", skeleton + "\ndependencies:", 1)
657
726
 
658
727
  out_file.write_text(content, encoding="utf-8")
728
+
729
+
730
+ def _find_apm_yml(start: Path | None = None) -> Path | None:
731
+ """Walk parent directories from ``start`` (or cwd) to find ``apm.yml``.
732
+
733
+ Matches the npm / cargo / poetry ergonomic: a developer running
734
+ an ``apm`` command from a subdirectory of their project (``src/``,
735
+ ``docs/``, ``scripts/``) finds the manifest and operates on it.
736
+
737
+ The walk stops at the filesystem root or when an ``apm.yml`` is
738
+ found, whichever comes first. Returns the absolute path to the
739
+ ``apm.yml`` file when found; ``None`` when no project root is
740
+ discoverable from ``start`` upward.
741
+ """
742
+ cwd = (start or Path.cwd()).resolve()
743
+ for candidate in (cwd, *cwd.parents):
744
+ manifest = candidate / "apm.yml"
745
+ if manifest.is_file():
746
+ return manifest
747
+ return None