apm-cli 0.12.1__tar.gz → 0.12.2__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.12.1/src/apm_cli.egg-info → apm_cli-0.12.2}/PKG-INFO +2 -1
- {apm_cli-0.12.1 → apm_cli-0.12.2}/README.md +1 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/pyproject.toml +1 -1
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/audit.py +144 -17
- apm_cli-0.12.2/src/apm_cli/deps/bare_cache.py +545 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/github_downloader.py +190 -155
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/shared_clone_cache.py +29 -4
- apm_cli-0.12.2/src/apm_cli/install/cache_pin.py +233 -0
- apm_cli-0.12.2/src/apm_cli/install/drift.py +731 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/lockfile.py +57 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/services.py +18 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/ci_checks.py +94 -1
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/content_hash.py +11 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/diagnostics.py +73 -0
- apm_cli-0.12.2/src/apm_cli/utils/guards.py +123 -0
- apm_cli-0.12.2/src/apm_cli/utils/normalization.py +57 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2/src/apm_cli.egg-info}/PKG-INFO +2 -1
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli.egg-info/SOURCES.txt +5 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/AUTHORS +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/LICENSE +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/NOTICE +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/setup.cfg +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/base.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/claude.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/codex.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/copilot.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/cursor.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/vscode.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/bundle/local_bundle.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/bundle/packer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/bundle/plugin_exporter.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/bundle/unpacker.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/git_cache.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/cli.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/_helpers.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/compile/cli.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/deps/cli.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/experimental.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/install.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/publish.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/outdated.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/pack.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/uninstall/cli.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/update.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/agents_compiler.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/context_optimizer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/config.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/auth.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/build_orchestrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/command_logger.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/experimental.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/safe_installer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/target_detection.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/apm_resolver.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/download_strategies.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/git_remote_ops.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/github_downloader_validation.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/lockfile.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/plugin_parser.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/context.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/errors.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/command.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/local_content.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/post_deps_local.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/resolve.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/phases/targets.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/pipeline.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/service.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/summary.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/install/validation.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/base_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/hook_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/instruction_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/mcp_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/skill_integrator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/targets.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/builder.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/client.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/models.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/pr_integration.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/publisher.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/resolver.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/tag_pattern.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/yml_editor.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/marketplace/yml_schema.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/apm_package.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/dependency/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/dependency/mcp.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/dependency/reference.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/dependency/types.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/policy_checks.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/registry/operations.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/github_host.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/utils/yaml_io.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/version.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_apm_package_models.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_apm_resolver.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_console.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_github_downloader.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_lockfile.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/tests/test_token_manager.py +0 -0
- {apm_cli-0.12.1 → apm_cli-0.12.2}/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.12.
|
|
3
|
+
Version: 0.12.2
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -146,6 +146,7 @@ Agent context is executable in effect — a prompt is a program for an LLM. APM
|
|
|
146
146
|
|
|
147
147
|
- **[Content security](https://microsoft.github.io/apm/enterprise/security/)** — `apm install` blocks compromised packages before agents read them; `apm audit` runs the same checks on demand
|
|
148
148
|
- **[Lockfile integrity](https://microsoft.github.io/apm/enterprise/governance/)** — `apm.lock` records resolved sources and content hashes for full provenance
|
|
149
|
+
- **[Drift detection](https://microsoft.github.io/apm/guides/drift-detection/)** — `apm audit` rebuilds your agent context in scratch and diffs it against your working tree to catch hand-edits before they ship
|
|
149
150
|
- **[MCP trust boundaries](https://microsoft.github.io/apm/guides/mcp-servers/)** — transitive MCP servers require explicit consent
|
|
150
151
|
|
|
151
152
|
### 3. Governed by policy
|
|
@@ -81,6 +81,7 @@ Agent context is executable in effect — a prompt is a program for an LLM. APM
|
|
|
81
81
|
|
|
82
82
|
- **[Content security](https://microsoft.github.io/apm/enterprise/security/)** — `apm install` blocks compromised packages before agents read them; `apm audit` runs the same checks on demand
|
|
83
83
|
- **[Lockfile integrity](https://microsoft.github.io/apm/enterprise/governance/)** — `apm.lock` records resolved sources and content hashes for full provenance
|
|
84
|
+
- **[Drift detection](https://microsoft.github.io/apm/guides/drift-detection/)** — `apm audit` rebuilds your agent context in scratch and diffs it against your working tree to catch hand-edits before they ship
|
|
84
85
|
- **[MCP trust boundaries](https://microsoft.github.io/apm/guides/mcp-servers/)** — transitive MCP servers require explicit consent
|
|
85
86
|
|
|
86
87
|
### 3. Governed by policy
|
|
@@ -19,7 +19,7 @@ from typing import Dict, List, Optional, Tuple # noqa: F401, UP035
|
|
|
19
19
|
import click
|
|
20
20
|
|
|
21
21
|
from ..core.command_logger import CommandLogger
|
|
22
|
-
from ..deps.lockfile import LockFile, get_lockfile_path
|
|
22
|
+
from ..deps.lockfile import LockFile, get_lockfile_path
|
|
23
23
|
from ..policy._help_text import POLICY_SOURCE_FORMS_HELP
|
|
24
24
|
from ..security.content_scanner import ContentScanner, ScanFinding
|
|
25
25
|
from ..security.file_scanner import scan_lockfile_packages
|
|
@@ -398,16 +398,17 @@ def _audit_ci_gate(
|
|
|
398
398
|
no_cache: bool,
|
|
399
399
|
no_policy: bool,
|
|
400
400
|
no_fail_fast: bool,
|
|
401
|
+
no_drift: bool = False,
|
|
401
402
|
) -> None:
|
|
402
403
|
"""Handle ``apm audit --ci`` -- lockfile consistency gate.
|
|
403
404
|
|
|
404
|
-
Runs baseline lockfile checks
|
|
405
|
-
then emits a structured report
|
|
406
|
-
(violations).
|
|
405
|
+
Runs baseline lockfile checks, drift detection (unless ``--no-drift``),
|
|
406
|
+
and (optionally) org-policy checks, then emits a structured report
|
|
407
|
+
and exits with 0 (clean) or 1 (violations).
|
|
407
408
|
"""
|
|
408
409
|
logger = cfg.logger
|
|
409
410
|
|
|
410
|
-
from ..policy.ci_checks import run_baseline_checks
|
|
411
|
+
from ..policy.ci_checks import _check_drift, run_baseline_checks
|
|
411
412
|
from ..policy.policy_checks import run_policy_checks
|
|
412
413
|
|
|
413
414
|
fail_fast = not no_fail_fast
|
|
@@ -484,6 +485,31 @@ def _audit_ci_gate(
|
|
|
484
485
|
)
|
|
485
486
|
)
|
|
486
487
|
|
|
488
|
+
# -- Drift detection (default-on per ADR-02) --------------------
|
|
489
|
+
drift_findings: list = []
|
|
490
|
+
if not no_drift and (cfg.project_root / "apm.yml").exists():
|
|
491
|
+
from ..deps.lockfile import LockFile, get_lockfile_path
|
|
492
|
+
|
|
493
|
+
lockfile_path = get_lockfile_path(cfg.project_root)
|
|
494
|
+
if lockfile_path.exists():
|
|
495
|
+
lockfile = LockFile.read(lockfile_path)
|
|
496
|
+
if lockfile is not None:
|
|
497
|
+
drift_check, drift_findings = _check_drift(
|
|
498
|
+
cfg.project_root,
|
|
499
|
+
lockfile,
|
|
500
|
+
cache_only=True,
|
|
501
|
+
verbose=cfg.verbose,
|
|
502
|
+
)
|
|
503
|
+
ci_result.checks.append(drift_check)
|
|
504
|
+
elif no_drift and cfg.output_format == "text":
|
|
505
|
+
# In structured output (json/sarif), --no-drift is implicit from
|
|
506
|
+
# the absence of the drift check entry; no need to pollute output.
|
|
507
|
+
click.echo(
|
|
508
|
+
f"{STATUS_SYMBOLS['warning']} drift detection skipped (--no-drift); "
|
|
509
|
+
"coverage reduced -- hand-edits and missing integrations will not be caught",
|
|
510
|
+
err=True,
|
|
511
|
+
)
|
|
512
|
+
|
|
487
513
|
# Resolve effective format
|
|
488
514
|
effective_format = cfg.output_format
|
|
489
515
|
if cfg.output_path and effective_format == "text":
|
|
@@ -494,7 +520,17 @@ def _audit_ci_gate(
|
|
|
494
520
|
if effective_format in ("json", "sarif"):
|
|
495
521
|
import json as _json
|
|
496
522
|
|
|
497
|
-
|
|
523
|
+
from ..install.drift import render_drift_json, render_drift_sarif
|
|
524
|
+
|
|
525
|
+
if effective_format == "sarif":
|
|
526
|
+
payload = ci_result.to_sarif()
|
|
527
|
+
if drift_findings:
|
|
528
|
+
payload["runs"][0]["results"].extend(render_drift_sarif(drift_findings))
|
|
529
|
+
else:
|
|
530
|
+
payload = ci_result.to_json()
|
|
531
|
+
if drift_findings or not no_drift:
|
|
532
|
+
payload["drift"] = render_drift_json(drift_findings)
|
|
533
|
+
|
|
498
534
|
output = _json.dumps(payload, indent=2)
|
|
499
535
|
if cfg.output_path:
|
|
500
536
|
Path(cfg.output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -504,6 +540,11 @@ def _audit_ci_gate(
|
|
|
504
540
|
click.echo(output)
|
|
505
541
|
else:
|
|
506
542
|
_render_ci_results(ci_result)
|
|
543
|
+
if drift_findings:
|
|
544
|
+
from ..install.drift import render_drift_text
|
|
545
|
+
|
|
546
|
+
click.echo("")
|
|
547
|
+
click.echo(render_drift_text(drift_findings, verbose=cfg.verbose))
|
|
507
548
|
|
|
508
549
|
sys.exit(0 if ci_result.passed else 1)
|
|
509
550
|
|
|
@@ -514,6 +555,7 @@ def _audit_content_scan(
|
|
|
514
555
|
file_path: str | None,
|
|
515
556
|
strip: bool,
|
|
516
557
|
dry_run: bool,
|
|
558
|
+
no_drift: bool = False,
|
|
517
559
|
) -> None:
|
|
518
560
|
"""Handle default ``apm audit`` -- content integrity scanning.
|
|
519
561
|
|
|
@@ -585,6 +627,54 @@ def _audit_content_scan(
|
|
|
585
627
|
logger.progress("Nothing to clean -- no strippable characters found")
|
|
586
628
|
sys.exit(0)
|
|
587
629
|
|
|
630
|
+
# -- Drift detection (default-on per ADR-02) --------------------
|
|
631
|
+
# Drift only applies to whole-project audit (not --file or --strip
|
|
632
|
+
# modes; not single-package scoped). Mutex on no_drift+strip/file
|
|
633
|
+
# is enforced earlier via UsageError.
|
|
634
|
+
drift_findings: list = []
|
|
635
|
+
drift_failed = False
|
|
636
|
+
if (
|
|
637
|
+
not no_drift
|
|
638
|
+
and not strip
|
|
639
|
+
and not file_path
|
|
640
|
+
and not package
|
|
641
|
+
and (project_root / "apm.yml").exists()
|
|
642
|
+
):
|
|
643
|
+
from ..policy.ci_checks import _check_drift
|
|
644
|
+
|
|
645
|
+
lockfile_path = get_lockfile_path(project_root)
|
|
646
|
+
if lockfile_path.exists():
|
|
647
|
+
lockfile = LockFile.read(lockfile_path)
|
|
648
|
+
if lockfile is not None:
|
|
649
|
+
drift_check, drift_findings = _check_drift(
|
|
650
|
+
project_root,
|
|
651
|
+
lockfile,
|
|
652
|
+
cache_only=True,
|
|
653
|
+
verbose=cfg.verbose,
|
|
654
|
+
)
|
|
655
|
+
drift_failed = not drift_check.passed
|
|
656
|
+
# Bare `apm audit` is advisory: drift_failed does not gate
|
|
657
|
+
# the exit code (that lives in --ci). But silence on a
|
|
658
|
+
# cache-pin / cache-miss failure is a UX trap: the user
|
|
659
|
+
# cannot tell whether drift was clean or whether it was
|
|
660
|
+
# never attempted. Surface the failure reason on stderr
|
|
661
|
+
# whenever the drift check failed without producing
|
|
662
|
+
# findings (CacheMissError, CachePinError, missing lockfile).
|
|
663
|
+
if drift_failed and not drift_findings:
|
|
664
|
+
click.echo(
|
|
665
|
+
f"{STATUS_SYMBOLS['warning']} drift check could not run: "
|
|
666
|
+
f"{drift_check.message}",
|
|
667
|
+
err=True,
|
|
668
|
+
)
|
|
669
|
+
elif no_drift and cfg.output_format == "text":
|
|
670
|
+
# In structured output (json/sarif), --no-drift is implicit from
|
|
671
|
+
# the absence of the drift check entry; no need to pollute output.
|
|
672
|
+
click.echo(
|
|
673
|
+
f"{STATUS_SYMBOLS['warning']} drift detection skipped (--no-drift); "
|
|
674
|
+
"coverage reduced -- hand-edits and missing integrations will not be caught",
|
|
675
|
+
err=True,
|
|
676
|
+
)
|
|
677
|
+
|
|
588
678
|
# -- Display findings --
|
|
589
679
|
# Determine exit code first (shared by all formats)
|
|
590
680
|
if not findings_by_file or not _has_actionable_findings(findings_by_file):
|
|
@@ -593,6 +683,11 @@ def _audit_content_scan(
|
|
|
593
683
|
all_findings = [f for ff in findings_by_file.values() for f in ff]
|
|
594
684
|
exit_code = 1 if ContentScanner.has_critical(all_findings) else 2
|
|
595
685
|
|
|
686
|
+
# Note: bare `apm audit` is advisory for drift; drift findings are
|
|
687
|
+
# rendered (text/json/sarif) but DO NOT escalate the exit code. Use
|
|
688
|
+
# `apm audit --ci` (handled in _audit_ci_gate) to gate on drift.
|
|
689
|
+
_ = drift_failed # retained for symmetry; gate path lives in --ci.
|
|
690
|
+
|
|
596
691
|
if effective_format == "text":
|
|
597
692
|
if cfg.output_path:
|
|
598
693
|
logger.error(
|
|
@@ -603,6 +698,11 @@ def _audit_content_scan(
|
|
|
603
698
|
if findings_by_file:
|
|
604
699
|
_render_findings_table(findings_by_file, verbose=cfg.verbose)
|
|
605
700
|
_render_summary(findings_by_file, files_scanned, logger)
|
|
701
|
+
if drift_findings:
|
|
702
|
+
from ..install.drift import render_drift_text
|
|
703
|
+
|
|
704
|
+
click.echo("")
|
|
705
|
+
click.echo(render_drift_text(drift_findings, verbose=cfg.verbose))
|
|
606
706
|
elif effective_format == "markdown":
|
|
607
707
|
from ..security.audit_report import findings_to_markdown
|
|
608
708
|
|
|
@@ -717,6 +817,15 @@ def _audit_content_scan(
|
|
|
717
817
|
is_flag=True,
|
|
718
818
|
help="Run all checks even after a failure (default: stop at first failure).",
|
|
719
819
|
)
|
|
820
|
+
@click.option(
|
|
821
|
+
"--no-drift",
|
|
822
|
+
"no_drift",
|
|
823
|
+
is_flag=True,
|
|
824
|
+
help=(
|
|
825
|
+
"Skip the install-replay drift check. Reduces coverage; "
|
|
826
|
+
"use only for performance-constrained CI loops."
|
|
827
|
+
),
|
|
828
|
+
)
|
|
720
829
|
@click.pass_context
|
|
721
830
|
def audit(
|
|
722
831
|
ctx,
|
|
@@ -732,6 +841,7 @@ def audit(
|
|
|
732
841
|
no_cache,
|
|
733
842
|
no_policy,
|
|
734
843
|
no_fail_fast,
|
|
844
|
+
no_drift,
|
|
735
845
|
):
|
|
736
846
|
"""Scan deployed prompt files for hidden Unicode characters.
|
|
737
847
|
|
|
@@ -739,23 +849,31 @@ def audit(
|
|
|
739
849
|
prompt, instruction, and rules files. Dangerous and suspicious
|
|
740
850
|
characters can be removed with --strip.
|
|
741
851
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
852
|
+
By default, also runs install-replay drift detection: catches
|
|
853
|
+
hand-edits to deployed files, missing integrations, and orphaned
|
|
854
|
+
files vs the lockfile. Use --no-drift to skip (reduces coverage).
|
|
855
|
+
|
|
856
|
+
With --ci, runs lockfile consistency checks AND drift in machine-
|
|
857
|
+
readable format, suitable for CI/CD pipeline gates.
|
|
745
858
|
|
|
746
859
|
\b
|
|
747
860
|
Exit codes:
|
|
748
|
-
0 Clean, info-only findings, or
|
|
749
|
-
|
|
750
|
-
|
|
861
|
+
0 Clean, info-only findings, or drift-only (advisory) in bare
|
|
862
|
+
audit, or successful strip
|
|
863
|
+
1 Critical findings detected, or --ci with violations
|
|
864
|
+
(including drift in --ci mode)
|
|
865
|
+
2 Warning-only findings (suspicious but not critical), or
|
|
866
|
+
usage error (mutually exclusive flags)
|
|
751
867
|
|
|
752
868
|
\b
|
|
753
869
|
Examples:
|
|
754
|
-
apm audit # Scan all
|
|
870
|
+
apm audit # Scan + drift (all checks)
|
|
755
871
|
apm audit my-package # Scan a specific package
|
|
756
|
-
apm audit --file .cursorrules # Scan any file
|
|
872
|
+
apm audit --file .cursorrules # Scan any file (no drift)
|
|
757
873
|
apm audit --strip # Remove dangerous/suspicious chars
|
|
758
|
-
apm audit --
|
|
874
|
+
apm audit --no-drift # Skip drift only (escape hatch)
|
|
875
|
+
apm audit --ci # CI gate (lockfile + drift)
|
|
876
|
+
apm audit --ci --no-drift # CI gate without drift (rare)
|
|
759
877
|
apm audit --ci --policy org # CI gate with org policy checks
|
|
760
878
|
apm audit --ci -f json # JSON CI report
|
|
761
879
|
apm audit --ci -f sarif # SARIF for GitHub Code Scanning
|
|
@@ -772,6 +890,15 @@ def audit(
|
|
|
772
890
|
output_path=output_path,
|
|
773
891
|
)
|
|
774
892
|
|
|
893
|
+
# --no-drift is a different audit mode from --strip / --file (those
|
|
894
|
+
# are content-scanning operations unrelated to integration drift).
|
|
895
|
+
# Click-native UsageError gives exit code 2 with "Usage:" prefix.
|
|
896
|
+
if no_drift and (strip or file_path):
|
|
897
|
+
raise click.UsageError(
|
|
898
|
+
"--no-drift cannot be combined with --strip or --file "
|
|
899
|
+
"(those modes do not run drift detection)"
|
|
900
|
+
)
|
|
901
|
+
|
|
775
902
|
# -- CI mode: lockfile consistency gate -------------------------
|
|
776
903
|
if ci:
|
|
777
904
|
if verbose:
|
|
@@ -783,7 +910,7 @@ def audit(
|
|
|
783
910
|
logger.error("--ci does not support --format markdown. Use json or sarif.")
|
|
784
911
|
sys.exit(1)
|
|
785
912
|
|
|
786
|
-
_audit_ci_gate(cfg, policy_source, no_cache, no_policy, no_fail_fast)
|
|
913
|
+
_audit_ci_gate(cfg, policy_source, no_cache, no_policy, no_fail_fast, no_drift)
|
|
787
914
|
return # _audit_ci_gate calls sys.exit; return guards against fall-through
|
|
788
915
|
|
|
789
916
|
# -- Content scan mode ------------------------------------------
|
|
@@ -793,4 +920,4 @@ def audit(
|
|
|
793
920
|
"Use 'apm audit --ci --policy <source>' to run policy checks."
|
|
794
921
|
)
|
|
795
922
|
|
|
796
|
-
_audit_content_scan(cfg, package, file_path, strip, dry_run)
|
|
923
|
+
_audit_content_scan(cfg, package, file_path, strip, dry_run, no_drift)
|