apm-cli 0.17.0__tar.gz → 0.19.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.
- {apm_cli-0.17.0 → apm_cli-0.19.0}/AUTHORS +1 -0
- {apm_cli-0.17.0/src/apm_cli.egg-info → apm_cli-0.19.0}/PKG-INFO +1 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/pyproject.toml +1 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/base.py +28 -12
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/claude.py +1 -6
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/codex.py +1 -13
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/copilot.py +13 -26
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/intellij.py +11 -6
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/bundle/lockfile_enrichment.py +3 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cli.py +57 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/audit.py +82 -23
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/compile/cli.py +14 -3
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/experimental.py +2 -2
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/install.py +28 -7
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/__init__.py +37 -7
- apm_cli-0.19.0/src/apm_cli/commands/marketplace/audit.py +122 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/outdated.py +80 -8
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/publish.py +34 -34
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/agents_compiler.py +25 -8
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/context_optimizer.py +28 -9
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/link_resolver.py +18 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/auth.py +148 -18
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/command_logger.py +5 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/experimental.py +10 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/safe_installer.py +2 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/target_detection.py +31 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/apm_resolver.py +111 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/dependency_graph.py +1 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/download_strategies.py +48 -3
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/git_semver_resolver.py +9 -5
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/lockfile.py +13 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/plugin_parser.py +276 -12
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/context.py +1 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/drift.py +19 -2
- apm_cli-0.19.0/src/apm_cli/install/lsp/__init__.py +5 -0
- apm_cli-0.19.0/src/apm_cli/install/lsp/integration.py +148 -0
- apm_cli-0.19.0/src/apm_cli/install/manifest_reconcile.py +112 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/local_content.py +109 -13
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/lockfile.py +46 -10
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/post_deps_local.py +17 -3
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/resolve.py +33 -2
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/targets.py +248 -268
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/pipeline.py +3 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/services.py +88 -4
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/summary.py +4 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/__init__.py +2 -0
- apm_cli-0.19.0/src/apm_cli/integration/_shared.py +71 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/base_integrator.py +15 -2
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/hook_integrator.py +131 -4
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/instruction_integrator.py +83 -21
- apm_cli-0.19.0/src/apm_cli/integration/lsp_integrator.py +508 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/mcp_integrator.py +4 -41
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/mcp_integrator_install.py +1 -28
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/skill_integrator.py +74 -10
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/targets.py +103 -3
- apm_cli-0.19.0/src/apm_cli/marketplace/audit.py +337 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/client.py +96 -14
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/resolver.py +59 -10
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/tag_pattern.py +80 -3
- apm_cli-0.19.0/src/apm_cli/marketplace/version_resolver.py +114 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/apm_package.py +33 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/dependency/__init__.py +2 -0
- apm_cli-0.19.0/src/apm_cli/models/dependency/lsp.py +229 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/dependency/mcp.py +29 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/dependency/reference.py +135 -3
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/skillspector.py +18 -5
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/exclude.py +10 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/github_host.py +24 -5
- {apm_cli-0.17.0 → apm_cli-0.19.0/src/apm_cli.egg-info}/PKG-INFO +1 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli.egg-info/SOURCES.txt +9 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_apm_package_models.py +156 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_apm_resolver.py +192 -1
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_lockfile.py +55 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/LICENSE +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/NOTICE +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/README.md +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/setup.cfg +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/cursor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/vscode.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/bundle/local_bundle.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/bundle/packer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/bundle/plugin_exporter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/bundle/unpacker.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/git_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/_helpers.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/deps/cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/deps/why.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/doctor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/find.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/lock.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/pack.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/plugin/init.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/self_update.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/update.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/managed_section.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/config.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/apm_yml.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/build_orchestrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/errors.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/install_audit.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/plugin_manifest.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/_shared.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/bare_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/clone_engine.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/git_auth_env.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/git_reference_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/git_remote_ops.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/github_downloader.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/github_downloader_validation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/host_backends.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/outdated_row.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/path_anchoring.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/auth.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/client.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/extractor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/outdated.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry/semver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/deps/why_walker.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/errors.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/command.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/package_resolution.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/package_selection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/_redownload.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/_skip_logic.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/audit.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/plan.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/registry_wiring.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/root_redirect.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/service.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/install/validation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/_shared.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/builder.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/drift_check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/output_mappers.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/publisher.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/version_check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/yml_editor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/marketplace/yml_schema.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/dependency/types.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/format_detection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/_shared.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/ci_checks.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/policy_checks.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/registry/operations.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/runtime/utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/generic_sarif.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/options.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/registry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/runner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/external/sarif_ingest.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/content_hash.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/git_sparse.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/patterns.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/perf_stats.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/utils/yaml_io.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/version.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_console.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_github_downloader.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_token_manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.19.0}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
* Daniel Meppiel (@danielmeppiel) — Microsoft
|
|
6
6
|
* Sergio Sisternes (@sergio-sisternes-epam) — EPAM Systems
|
|
7
|
+
* Nadav Yogev (@nadav-y) — JFrog
|
|
7
8
|
|
|
8
9
|
Maintainers' work on APM is supported by their employers. Project
|
|
9
10
|
decisions are made in the interest of APM and its users, independent
|
|
@@ -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] =
|
|
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] =
|
|
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] =
|
|
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
|
|
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 =
|
|
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.
|
|
@@ -620,22 +633,25 @@ class MCPClientAdapter(ABC):
|
|
|
620
633
|
return server_info
|
|
621
634
|
|
|
622
635
|
@staticmethod
|
|
623
|
-
def _determine_config_key(server_url: str, server_name: str) -> str:
|
|
636
|
+
def _determine_config_key(server_url: str, server_name: str | None) -> str:
|
|
624
637
|
"""Return the configuration key to use for *server_url*/*server_name*.
|
|
625
638
|
|
|
626
|
-
The caller-supplied *server_name* takes precedence
|
|
627
|
-
|
|
628
|
-
convention ``owner/repo -> repo
|
|
639
|
+
The caller-supplied *server_name* takes precedence. If it is absent,
|
|
640
|
+
preserve npm-style scoped names such as ``@scope/name`` (one slash by
|
|
641
|
+
npm convention) while keeping the historical ``owner/repo -> repo``
|
|
642
|
+
fallback for registry paths.
|
|
629
643
|
|
|
630
644
|
Args:
|
|
631
645
|
server_url: Registry reference used as fallback source.
|
|
632
|
-
server_name: Explicit caller-supplied name
|
|
646
|
+
server_name: Explicit caller-supplied name, if any.
|
|
633
647
|
|
|
634
648
|
Returns:
|
|
635
649
|
Non-empty configuration key string.
|
|
636
650
|
"""
|
|
637
651
|
if server_name:
|
|
638
652
|
return server_name
|
|
653
|
+
if server_url.startswith("@") and server_url.count("/") == 1:
|
|
654
|
+
return server_url
|
|
639
655
|
if "/" in server_url:
|
|
640
656
|
return server_url.split("/")[-1]
|
|
641
657
|
return server_url
|
|
@@ -239,12 +239,7 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
239
239
|
_rich_error(f"MCP server '{server_url}' not found in registry")
|
|
240
240
|
return False
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
config_key = server_name
|
|
244
|
-
elif "/" in server_url:
|
|
245
|
-
config_key = server_url.split("/")[-1]
|
|
246
|
-
else:
|
|
247
|
-
config_key = server_url
|
|
242
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
248
243
|
|
|
249
244
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
250
245
|
ok = self.update_config({config_key: server_config})
|
|
@@ -150,19 +150,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
150
150
|
if server_info is None:
|
|
151
151
|
return False
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
if server_name:
|
|
155
|
-
# Use explicitly provided server name
|
|
156
|
-
config_key = server_name
|
|
157
|
-
else: # noqa: PLR5501
|
|
158
|
-
# Extract name from server_url (part after last slash)
|
|
159
|
-
# For URLs like "microsoft/azure-devops-mcp" -> "azure-devops-mcp"
|
|
160
|
-
# For URLs like "github/github-mcp-server" -> "github-mcp-server"
|
|
161
|
-
if "/" in server_url: # noqa: SIM108
|
|
162
|
-
config_key = server_url.split("/")[-1]
|
|
163
|
-
else:
|
|
164
|
-
# Fallback to full server_url if no slash
|
|
165
|
-
config_key = server_url
|
|
153
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
166
154
|
|
|
167
155
|
# Generate server configuration with environment variable resolution
|
|
168
156
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
@@ -26,7 +26,9 @@ from .base import (
|
|
|
26
26
|
_extract_legacy_angle_vars,
|
|
27
27
|
_has_env_placeholder,
|
|
28
28
|
_stringify_env_literal,
|
|
29
|
-
|
|
29
|
+
)
|
|
30
|
+
from .base import (
|
|
31
|
+
_translate_env_placeholder as _translate_env_placeholder,
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
|
|
@@ -204,19 +206,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
204
206
|
# Generate server configuration with environment and runtime variable resolution
|
|
205
207
|
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
206
208
|
|
|
207
|
-
|
|
208
|
-
if server_name:
|
|
209
|
-
# Use explicitly provided server name
|
|
210
|
-
config_key = server_name
|
|
211
|
-
else: # noqa: PLR5501
|
|
212
|
-
# Extract name from server_url (part after last slash)
|
|
213
|
-
# For URLs like "microsoft/azure-devops-mcp" -> "azure-devops-mcp"
|
|
214
|
-
# For URLs like "github/github-mcp-server" -> "github-mcp-server"
|
|
215
|
-
if "/" in server_url: # noqa: SIM108
|
|
216
|
-
config_key = server_url.split("/")[-1]
|
|
217
|
-
else:
|
|
218
|
-
# Fallback to full server_url if no slash
|
|
219
|
-
config_key = server_url
|
|
209
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
220
210
|
|
|
221
211
|
# Update configuration using the chosen key
|
|
222
212
|
self.update_config({config_key: server_config})
|
|
@@ -678,9 +668,9 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
678
668
|
# ({NAME: value-or-placeholder}); registry-sourced deps pass a list
|
|
679
669
|
# of {name, description, required} dicts. Translate-mode handling
|
|
680
670
|
# for the dict shape: each value is either already a placeholder
|
|
681
|
-
# (translate it to the
|
|
682
|
-
# the key as a placeholder reference and emit
|
|
683
|
-
# value never lands on disk). See issue #1152.
|
|
671
|
+
# (translate it to the adapter's runtime form) or a literal
|
|
672
|
+
# (record the key as a placeholder reference and emit a runtime
|
|
673
|
+
# placeholder so the value never lands on disk). See issue #1152.
|
|
684
674
|
if isinstance(env_vars, dict) and self._supports_runtime_env_substitution:
|
|
685
675
|
translated = {}
|
|
686
676
|
placeholder_keys = []
|
|
@@ -694,9 +684,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
694
684
|
continue
|
|
695
685
|
if _has_env_placeholder(raw_value):
|
|
696
686
|
self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(raw_value))
|
|
697
|
-
translated[name] =
|
|
698
|
-
# Record every ${VAR} in the translated value (handles
|
|
699
|
-
# both ${env:VAR} -> ${VAR} and bare ${VAR} cases).
|
|
687
|
+
translated[name] = self._translate_env_placeholder_for_runtime(raw_value)
|
|
700
688
|
for match in _ENV_VAR_RE.finditer(translated[name]):
|
|
701
689
|
placeholder_keys.append(match.group(1))
|
|
702
690
|
elif name in default_github_env and raw_value == default_github_env[name]:
|
|
@@ -704,7 +692,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
704
692
|
else:
|
|
705
693
|
# Literal value present in apm.yml -- replace with a
|
|
706
694
|
# runtime placeholder so the secret never touches disk.
|
|
707
|
-
translated[name] =
|
|
695
|
+
translated[name] = self._format_runtime_env_placeholder(name)
|
|
708
696
|
placeholder_keys.append(name)
|
|
709
697
|
self._last_env_placeholder_keys = set(placeholder_keys)
|
|
710
698
|
return translated
|
|
@@ -722,10 +710,9 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
722
710
|
# Non-secret literal default -- preserve as-is.
|
|
723
711
|
resolved[name] = default_github_env[name]
|
|
724
712
|
else:
|
|
725
|
-
# Emit a runtime-substitution placeholder;
|
|
726
|
-
#
|
|
727
|
-
|
|
728
|
-
resolved[name] = "${" + name + "}"
|
|
713
|
+
# Emit a runtime-substitution placeholder; APM never reads
|
|
714
|
+
# or stores the value.
|
|
715
|
+
resolved[name] = self._format_runtime_env_placeholder(name)
|
|
729
716
|
placeholder_keys.append(name)
|
|
730
717
|
# Record for the post-install summary line and the
|
|
731
718
|
# security-improvement notice.
|
|
@@ -783,7 +770,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
783
770
|
# them (the env-block path tracks via _resolve_environment_variables).
|
|
784
771
|
for match in _ENV_VAR_RE.finditer(value):
|
|
785
772
|
self._last_env_placeholder_keys.add(match.group(1))
|
|
786
|
-
return
|
|
773
|
+
return self._translate_env_placeholder_for_runtime(value)
|
|
787
774
|
|
|
788
775
|
import sys
|
|
789
776
|
|
|
@@ -67,9 +67,10 @@ class IntelliJClientAdapter(CopilotClientAdapter):
|
|
|
67
67
|
|
|
68
68
|
JetBrains Copilot stores server definitions in a user-scope JSON file
|
|
69
69
|
with a ``"servers"`` top-level key (not ``"mcpServers"``). This adapter
|
|
70
|
-
inherits
|
|
71
|
-
|
|
72
|
-
``mcp_servers_key
|
|
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.
|
|
73
74
|
"""
|
|
74
75
|
|
|
75
76
|
supports_user_scope: bool = True
|
|
@@ -77,9 +78,13 @@ class IntelliJClientAdapter(CopilotClientAdapter):
|
|
|
77
78
|
target_name: str = "intellij"
|
|
78
79
|
mcp_servers_key: str = "servers"
|
|
79
80
|
|
|
80
|
-
# JetBrains
|
|
81
|
-
#
|
|
82
|
-
_supports_runtime_env_substitution: bool =
|
|
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 + "}"
|
|
83
88
|
|
|
84
89
|
# ------------------------------------------------------------------ #
|
|
85
90
|
# Config path
|
|
@@ -4,6 +4,7 @@ Thin wiring layer -- all command logic lives in ``apm_cli.commands.*`` modules.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import ctypes
|
|
7
|
+
import logging
|
|
7
8
|
import os
|
|
8
9
|
import sys
|
|
9
10
|
import warnings
|
|
@@ -61,6 +62,48 @@ _CLI_EPILOG = (
|
|
|
61
62
|
)
|
|
62
63
|
|
|
63
64
|
|
|
65
|
+
def _configure_logging(verbose: bool = False) -> None:
|
|
66
|
+
"""Configure stdlib logging for the ``apm_cli`` package.
|
|
67
|
+
|
|
68
|
+
Two mechanisms activate debug-level output (either is sufficient):
|
|
69
|
+
|
|
70
|
+
* ``--verbose`` / ``-v`` flag on the ``apm`` command.
|
|
71
|
+
* ``APM_LOG_LEVEL=DEBUG`` environment variable (accepts any stdlib
|
|
72
|
+
level name: DEBUG, INFO, WARNING, ERROR, CRITICAL).
|
|
73
|
+
|
|
74
|
+
When neither is set the ``apm_cli`` logger defaults to WARNING so
|
|
75
|
+
routine runs stay silent. A :class:`~apm_cli.core.auth.SecretRedactionFilter`
|
|
76
|
+
is always installed on the ``apm_cli`` logger to strip token-bearing
|
|
77
|
+
exception strings from debug records regardless of which mechanism
|
|
78
|
+
activated debug mode.
|
|
79
|
+
"""
|
|
80
|
+
env_level_str = os.environ.get("APM_LOG_LEVEL", "").strip().upper()
|
|
81
|
+
env_level: int | None = (
|
|
82
|
+
getattr(logging, env_level_str, None) if env_level_str.isalpha() else None
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if verbose:
|
|
86
|
+
level = logging.DEBUG
|
|
87
|
+
elif env_level is not None:
|
|
88
|
+
level = env_level
|
|
89
|
+
else:
|
|
90
|
+
level = logging.WARNING
|
|
91
|
+
|
|
92
|
+
logging.basicConfig(
|
|
93
|
+
level=level,
|
|
94
|
+
format="%(levelname)s %(name)s %(message)s",
|
|
95
|
+
stream=sys.stderr,
|
|
96
|
+
)
|
|
97
|
+
apm_logger = logging.getLogger("apm_cli")
|
|
98
|
+
apm_logger.setLevel(level)
|
|
99
|
+
|
|
100
|
+
# Install secret-redaction filter (idempotent: skip if already present).
|
|
101
|
+
from apm_cli.core.auth import SecretRedactionFilter
|
|
102
|
+
|
|
103
|
+
if not any(isinstance(f, SecretRedactionFilter) for f in apm_logger.filters):
|
|
104
|
+
apm_logger.addFilter(SecretRedactionFilter())
|
|
105
|
+
|
|
106
|
+
|
|
64
107
|
@click.group(
|
|
65
108
|
help="Agent Package Manager (APM): The package manager for AI-Native Development",
|
|
66
109
|
epilog=_CLI_EPILOG,
|
|
@@ -73,10 +116,22 @@ _CLI_EPILOG = (
|
|
|
73
116
|
is_eager=True,
|
|
74
117
|
help="Show version and exit.",
|
|
75
118
|
)
|
|
119
|
+
@click.option(
|
|
120
|
+
"--verbose",
|
|
121
|
+
"-v",
|
|
122
|
+
is_flag=True,
|
|
123
|
+
default=False,
|
|
124
|
+
help="Enable debug-level logging (equivalent to APM_LOG_LEVEL=DEBUG).",
|
|
125
|
+
)
|
|
76
126
|
@click.pass_context
|
|
77
|
-
def cli(ctx):
|
|
127
|
+
def cli(ctx, verbose: bool) -> None:
|
|
78
128
|
"""Main entry point for the APM CLI."""
|
|
79
129
|
ctx.ensure_object(dict)
|
|
130
|
+
ctx.obj["verbose"] = verbose
|
|
131
|
+
|
|
132
|
+
if verbose:
|
|
133
|
+
# Upgrade to DEBUG when the flag is set; env-var path runs in main().
|
|
134
|
+
_configure_logging(verbose=True)
|
|
80
135
|
|
|
81
136
|
# Suppress only the agents-target deprecation warning so CLI users see
|
|
82
137
|
# the formatted logger.warning() in the install phase, not a double print.
|
|
@@ -267,6 +322,7 @@ def _configure_encoding() -> None:
|
|
|
267
322
|
|
|
268
323
|
def main():
|
|
269
324
|
"""Main entry point for the CLI."""
|
|
325
|
+
_configure_logging() # honours APM_LOG_LEVEL env var; --verbose upgrades in cli()
|
|
270
326
|
_configure_encoding()
|
|
271
327
|
try:
|
|
272
328
|
cli(obj={})
|
|
@@ -103,6 +103,39 @@ def _has_actionable_findings(
|
|
|
103
103
|
)
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
def _finding_source(finding: ScanFinding) -> str:
|
|
107
|
+
"""Derive the scanner source from a finding's category prefix."""
|
|
108
|
+
if "/" in finding.category:
|
|
109
|
+
prefix = finding.category.split("/", 1)[0]
|
|
110
|
+
if prefix != "apm":
|
|
111
|
+
return prefix
|
|
112
|
+
return "apm"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _has_external_findings(rows: list[ScanFinding]) -> bool:
|
|
116
|
+
"""Return True if any finding originates from an external scanner."""
|
|
117
|
+
return any(_finding_source(f) != "apm" for f in rows)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _source_counts(rows: list[ScanFinding]) -> dict[str, int]:
|
|
121
|
+
"""Count findings by source for the table title."""
|
|
122
|
+
counts: dict[str, int] = {}
|
|
123
|
+
for f in rows:
|
|
124
|
+
src = _finding_source(f)
|
|
125
|
+
counts[src] = counts.get(src, 0) + 1
|
|
126
|
+
return counts
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _findings_title(rows: list[ScanFinding], has_external: bool) -> str:
|
|
130
|
+
"""Build the findings table title, with per-source counts when mixed."""
|
|
131
|
+
base = f"{STATUS_SYMBOLS['search']} Audit Findings"
|
|
132
|
+
if not has_external:
|
|
133
|
+
return f"{STATUS_SYMBOLS['search']} Content Scan Findings"
|
|
134
|
+
counts = _source_counts(rows)
|
|
135
|
+
parts = [f"{src}: {n}" for src, n in sorted(counts.items())]
|
|
136
|
+
return f"{base} ({', '.join(parts)})"
|
|
137
|
+
|
|
138
|
+
|
|
106
139
|
def _render_findings_table(
|
|
107
140
|
findings_by_file: dict[str, list[ScanFinding]],
|
|
108
141
|
verbose: bool = False,
|
|
@@ -124,6 +157,9 @@ def _render_findings_table(
|
|
|
124
157
|
if not rows:
|
|
125
158
|
return
|
|
126
159
|
|
|
160
|
+
has_external = _has_external_findings(rows)
|
|
161
|
+
title = _findings_title(rows, has_external)
|
|
162
|
+
|
|
127
163
|
if console:
|
|
128
164
|
try:
|
|
129
165
|
from rich.table import Table
|
|
@@ -131,14 +167,19 @@ def _render_findings_table(
|
|
|
131
167
|
from ..security.audit_report import relative_path_for_report
|
|
132
168
|
|
|
133
169
|
table = Table(
|
|
134
|
-
title=
|
|
170
|
+
title=title,
|
|
135
171
|
show_header=True,
|
|
136
172
|
header_style="bold cyan",
|
|
137
173
|
)
|
|
138
174
|
table.add_column("Severity", style="bold", width=10)
|
|
175
|
+
if has_external:
|
|
176
|
+
table.add_column("Source", style="cyan", width=14)
|
|
139
177
|
table.add_column("File", style="white")
|
|
140
178
|
table.add_column("Location", style="dim", width=10)
|
|
141
|
-
|
|
179
|
+
if has_external:
|
|
180
|
+
table.add_column("Category", style="bold white")
|
|
181
|
+
else:
|
|
182
|
+
table.add_column("Codepoint", style="bold white", width=10)
|
|
142
183
|
table.add_column("Description", style="white")
|
|
143
184
|
|
|
144
185
|
sev_styles = {
|
|
@@ -147,12 +188,26 @@ def _render_findings_table(
|
|
|
147
188
|
"info": "dim",
|
|
148
189
|
}
|
|
149
190
|
for f in rows:
|
|
191
|
+
category_or_codepoint = (
|
|
192
|
+
f.category.split("/", 1)[1]
|
|
193
|
+
if has_external and "/" in f.category
|
|
194
|
+
else f.category
|
|
195
|
+
if has_external
|
|
196
|
+
else f.codepoint
|
|
197
|
+
)
|
|
198
|
+
row_cells = [f.severity.upper()]
|
|
199
|
+
if has_external:
|
|
200
|
+
row_cells.append(_finding_source(f))
|
|
201
|
+
row_cells.extend(
|
|
202
|
+
[
|
|
203
|
+
relative_path_for_report(f.file),
|
|
204
|
+
f"{f.line}:{f.column}",
|
|
205
|
+
category_or_codepoint,
|
|
206
|
+
f.description,
|
|
207
|
+
]
|
|
208
|
+
)
|
|
150
209
|
table.add_row(
|
|
151
|
-
|
|
152
|
-
relative_path_for_report(f.file),
|
|
153
|
-
f"{f.line}:{f.column}",
|
|
154
|
-
f.codepoint,
|
|
155
|
-
f.description,
|
|
210
|
+
*row_cells,
|
|
156
211
|
style=sev_styles.get(f.severity, "white"),
|
|
157
212
|
)
|
|
158
213
|
console.print()
|
|
@@ -163,18 +218,17 @@ def _render_findings_table(
|
|
|
163
218
|
|
|
164
219
|
# Fallback: plain text
|
|
165
220
|
_rich_echo("")
|
|
166
|
-
_rich_echo(
|
|
167
|
-
f"{STATUS_SYMBOLS['search']} Content Scan Findings",
|
|
168
|
-
color="cyan",
|
|
169
|
-
bold=True,
|
|
170
|
-
)
|
|
221
|
+
_rich_echo(title, color="cyan", bold=True)
|
|
171
222
|
for f in rows:
|
|
172
223
|
sev_label = f.severity.upper()
|
|
173
224
|
color = (
|
|
174
225
|
"red" if f.severity == "critical" else ("yellow" if f.severity == "warning" else "dim")
|
|
175
226
|
)
|
|
227
|
+
source_part = f" [{_finding_source(f)}]" if has_external else ""
|
|
228
|
+
detail = f.category if has_external else f.codepoint
|
|
176
229
|
_rich_echo(
|
|
177
|
-
f" {sev_label:<10} {f.file} {f.line}:{f.column} {
|
|
230
|
+
f" {sev_label:<10}{source_part} {f.file} {f.line}:{f.column} {detail} "
|
|
231
|
+
f"{f.description}",
|
|
178
232
|
color=color,
|
|
179
233
|
)
|
|
180
234
|
|
|
@@ -669,7 +723,7 @@ def _run_external_scanners(
|
|
|
669
723
|
require_external_scanners_enabled("Ingesting external scanners with --external")
|
|
670
724
|
except ExternalScannersFeatureDisabledError as exc:
|
|
671
725
|
logger.error(str(exc))
|
|
672
|
-
sys.exit(
|
|
726
|
+
sys.exit(3)
|
|
673
727
|
|
|
674
728
|
try:
|
|
675
729
|
return run_external_scanners(
|
|
@@ -681,7 +735,7 @@ def _run_external_scanners(
|
|
|
681
735
|
)
|
|
682
736
|
except ExternalScanError as exc:
|
|
683
737
|
logger.error(str(exc))
|
|
684
|
-
sys.exit(
|
|
738
|
+
sys.exit(3)
|
|
685
739
|
|
|
686
740
|
|
|
687
741
|
def _audit_content_scan(
|
|
@@ -713,8 +767,9 @@ def _audit_content_scan(
|
|
|
713
767
|
|
|
714
768
|
# --format json/sarif/markdown is incompatible with --strip / --dry-run
|
|
715
769
|
if effective_format != "text" and (strip or dry_run):
|
|
716
|
-
|
|
717
|
-
|
|
770
|
+
raise click.UsageError(
|
|
771
|
+
f"--format {effective_format} cannot be combined with --strip or --dry-run"
|
|
772
|
+
)
|
|
718
773
|
|
|
719
774
|
if file_path:
|
|
720
775
|
# -- File mode: scan a single arbitrary file --
|
|
@@ -997,6 +1052,7 @@ def _audit_content_scan(
|
|
|
997
1052
|
help=(
|
|
998
1053
|
"Ingest findings from an external SARIF-native scanner "
|
|
999
1054
|
"(repeatable). Names: skillspector, sarif. "
|
|
1055
|
+
"Not supported with --ci. "
|
|
1000
1056
|
"Requires 'apm experimental enable external-scanners'. [experimental]"
|
|
1001
1057
|
),
|
|
1002
1058
|
)
|
|
@@ -1070,6 +1126,8 @@ def audit( # noqa: PLR0913 -- Click handler
|
|
|
1070
1126
|
(including drift in --ci mode)
|
|
1071
1127
|
2 Warning-only findings (suspicious but not critical), or
|
|
1072
1128
|
usage error (mutually exclusive flags)
|
|
1129
|
+
3 Configuration or infrastructure error (experimental feature
|
|
1130
|
+
disabled, external scanner not installed or unavailable)
|
|
1073
1131
|
|
|
1074
1132
|
\b
|
|
1075
1133
|
Examples:
|
|
@@ -1084,6 +1142,8 @@ def audit( # noqa: PLR0913 -- Click handler
|
|
|
1084
1142
|
apm audit --ci -f json # JSON CI report
|
|
1085
1143
|
apm audit --ci -f sarif # SARIF for GitHub Code Scanning
|
|
1086
1144
|
apm audit -o report.sarif # Write SARIF to file
|
|
1145
|
+
apm audit --external skillspector # SkillSpector
|
|
1146
|
+
apm audit --external sarif --external-sarif r.sarif # Any SARIF
|
|
1087
1147
|
"""
|
|
1088
1148
|
project_root = Path.cwd()
|
|
1089
1149
|
logger = CommandLogger("audit", verbose=verbose)
|
|
@@ -1110,17 +1170,17 @@ def audit( # noqa: PLR0913 -- Click handler
|
|
|
1110
1170
|
if verbose:
|
|
1111
1171
|
logger.warning("--verbose has no effect in --ci mode (output is structured)")
|
|
1112
1172
|
if strip or dry_run or file_path or package:
|
|
1113
|
-
|
|
1114
|
-
|
|
1173
|
+
raise click.UsageError(
|
|
1174
|
+
"--ci cannot be combined with --strip, --dry-run, --file, or PACKAGE"
|
|
1175
|
+
)
|
|
1115
1176
|
if output_format == "markdown":
|
|
1116
1177
|
logger.error("--ci does not support --format markdown. Use json or sarif.")
|
|
1117
1178
|
sys.exit(1)
|
|
1118
1179
|
if external:
|
|
1119
|
-
|
|
1180
|
+
raise click.UsageError(
|
|
1120
1181
|
"--ci does not support --external scanners yet. "
|
|
1121
1182
|
"Run external scanners in bare 'apm audit' mode."
|
|
1122
1183
|
)
|
|
1123
|
-
sys.exit(1)
|
|
1124
1184
|
|
|
1125
1185
|
_audit_ci_gate(cfg, policy_source, no_cache, no_policy, no_fail_fast, no_drift)
|
|
1126
1186
|
return # _audit_ci_gate calls sys.exit; return guards against fall-through
|
|
@@ -1129,8 +1189,7 @@ def audit( # noqa: PLR0913 -- Click handler
|
|
|
1129
1189
|
# They cannot be combined with --strip/--dry-run (APM only knows how to
|
|
1130
1190
|
# strip the Unicode characters its own scanner detects).
|
|
1131
1191
|
if external and (strip or dry_run):
|
|
1132
|
-
|
|
1133
|
-
sys.exit(1)
|
|
1192
|
+
raise click.UsageError("--external cannot be combined with --strip or --dry-run")
|
|
1134
1193
|
if external_sarif and not external:
|
|
1135
1194
|
raise click.UsageError("--external-sarif requires '--external sarif'")
|
|
1136
1195
|
# Orphan-flag guards: scanner-config flags are meaningless without a
|
|
@@ -234,10 +234,21 @@ def _resolve_compile_target(target):
|
|
|
234
234
|
families.add("agents")
|
|
235
235
|
|
|
236
236
|
if len(families) >= 2:
|
|
237
|
-
#
|
|
238
|
-
#
|
|
237
|
+
# Collapse {"vscode","agents"} to bare "vscode" ONLY when the
|
|
238
|
+
# original target list contains no non-Copilot agents-family
|
|
239
|
+
# targets (e.g. codex, opencode, windsurf). When mixed targets
|
|
240
|
+
# like [copilot, codex] are requested, keep the frozenset so
|
|
241
|
+
# downstream dedup logic knows non-Copilot targets also consume
|
|
242
|
+
# AGENTS.md (issue #1678).
|
|
239
243
|
if families == {"vscode", "agents"}:
|
|
240
|
-
|
|
244
|
+
_vscode_names = {"copilot", "vscode", "agents"}
|
|
245
|
+
has_non_vscode_agents = any(
|
|
246
|
+
name in target_set
|
|
247
|
+
for name, profile in KNOWN_TARGETS.items()
|
|
248
|
+
if profile.compile_family == "agents" and name not in _vscode_names
|
|
249
|
+
)
|
|
250
|
+
if not has_non_vscode_agents:
|
|
251
|
+
return "vscode"
|
|
241
252
|
return frozenset(families)
|
|
242
253
|
if "claude" in families:
|
|
243
254
|
return "claude"
|
|
@@ -244,7 +244,7 @@ def enable_flag(ctx, name: str, verbose: bool):
|
|
|
244
244
|
from ..core.experimental import is_enabled
|
|
245
245
|
|
|
246
246
|
if is_enabled(normalised):
|
|
247
|
-
logger.
|
|
247
|
+
logger.progress(f"{display_name(normalised)} is already enabled.")
|
|
248
248
|
return
|
|
249
249
|
|
|
250
250
|
flag = _enable_flag(normalised)
|
|
@@ -274,7 +274,7 @@ def disable_flag(ctx, name: str, verbose: bool):
|
|
|
274
274
|
from ..core.experimental import is_enabled
|
|
275
275
|
|
|
276
276
|
if not is_enabled(normalised):
|
|
277
|
-
logger.
|
|
277
|
+
logger.progress(f"{display_name(normalised)} is already disabled.")
|
|
278
278
|
return
|
|
279
279
|
|
|
280
280
|
_disable_flag(normalised)
|