apm-cli 0.23.1__tar.gz → 0.24.1__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.23.1 → apm_cli-0.24.1}/NOTICE +35 -0
- {apm_cli-0.23.1/src/apm_cli.egg-info → apm_cli-0.24.1}/PKG-INFO +2 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/pyproject.toml +2 -2
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/claude.py +20 -5
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/codex.py +13 -10
- apm_cli-0.24.1/src/apm_cli/bundle/attest.py +64 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bundle/packer.py +43 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bundle/plugin_exporter.py +261 -24
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/git_cache.py +35 -29
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/_helpers.py +1 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/audit.py +6 -6
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/compile/cli.py +9 -3
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/config.py +76 -63
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/deps/cli.py +82 -19
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/install.py +44 -117
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/mcp.py +3 -5
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/outdated.py +8 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/prune.py +1 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/publish.py +7 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/uninstall/cli.py +3 -3
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/update.py +114 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/agents_compiler.py +81 -24
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/claude_formatter.py +4 -3
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/context_optimizer.py +12 -2
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/template_builder.py +16 -5
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/apm_yml.py +1 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/target_detection.py +39 -15
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/download_strategies.py +97 -39
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/github_downloader.py +8 -2
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/lockfile.py +23 -0
- apm_cli-0.24.1/src/apm_cli/deps/outdated_ref_cache.py +107 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/tiered_ref_resolver.py +33 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/context.py +5 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/drift.py +2 -0
- apm_cli-0.24.1/src/apm_cli/install/helpers/ref_reuse.py +96 -0
- apm_cli-0.24.1/src/apm_cli/install/helpers/ref_seed.py +66 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/__init__.py +5 -0
- apm_cli-0.24.1/src/apm_cli/install/mcp/integration.py +198 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/package_resolution.py +12 -3
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/lockfile.py +45 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/resolve.py +16 -17
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/plan.py +23 -12
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/validation.py +97 -1
- apm_cli-0.24.1/src/apm_cli/integration/hook_bundle.py +191 -0
- apm_cli-0.24.1/src/apm_cli/integration/hook_file_routing.py +217 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/hook_integrator.py +69 -39
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/instruction_integrator.py +52 -11
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/kiro_hook_integrator.py +27 -23
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/mcp_integrator.py +56 -6
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/mcp_integrator_install.py +4 -2
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/skill_integrator.py +45 -21
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/targets.py +2 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/output_mappers.py +64 -4
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/ref_resolver.py +0 -4
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/apm_package.py +9 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/identity.py +39 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/mcp.py +13 -0
- apm_cli-0.24.1/src/apm_cli/models/dependency/object_fields.py +63 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/reference.py +40 -39
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/ci_checks.py +14 -8
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/schema.py +3 -2
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/executables.py +22 -3
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/archive.py +11 -2
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/github_host.py +7 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/yaml_io.py +67 -4
- {apm_cli-0.23.1 → apm_cli-0.24.1/src/apm_cli.egg-info}/PKG-INFO +2 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli.egg-info/SOURCES.txt +7 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli.egg-info/requires.txt +1 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_apm_package_models.py +33 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_github_downloader_token_precedence.py +1 -1
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_lockfile.py +15 -0
- apm_cli-0.23.1/src/apm_cli/integration/hook_file_routing.py +0 -120
- {apm_cli-0.23.1 → apm_cli-0.24.1}/AUTHORS +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/LICENSE +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/README.md +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/setup.cfg +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/antigravity.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/base.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/copilot.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/cursor.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/hermes.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/intellij.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/kiro.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/vscode.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bootstrap_mirror.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bundle/local_bundle.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/bundle/unpacker.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/cli.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/approve.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/deps/why.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/doctor.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/experimental.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/find.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/lifecycle.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/lock.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/audit.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/pack.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/plugin/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/plugin/init.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/self_update.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/managed_section.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/compilation/user_root_context.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/config.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/auth.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/build_orchestrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/command_logger.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/errors.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/experimental.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/install_audit.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/lifecycle_scripts.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/plugin_manifest.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/safe_installer.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/script_executors.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/script_trust.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/_shared.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/apm_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/bare_cache.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/clone_engine.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/git_auth_env.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/git_file_transport.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/git_reference_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/git_remote_ops.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/git_semver_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/github_downloader_validation.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/host_backends.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/outdated_row.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/path_anchoring.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/plugin_parser.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/auth.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/client.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/config_loader.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/extractor.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/feature_gate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/outdated.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry/semver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/revision_pins.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/shared_clone_cache.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/deps/why_walker.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/authoring.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/declared_license.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/formats.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/purl.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/sbom.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/spdx.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/export/spdx_data.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/artifactory_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/deployed_paths.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/errors.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/exec_gate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/gitlab_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/integrity.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/lsp/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/lsp/integration.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/manifest_reconcile.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/command.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/package_selection.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/_redownload.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/_skip_logic.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/audit.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/local_content.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/post_deps_local.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/phases/targets.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/pipeline.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/registry_wiring.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/root_redirect.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/service.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/services.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/summary.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/target_filter.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/_shared.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/base_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/canvas_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/copilot_app_db.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/copilot_app_project.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/copilot_app_ws.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/lsp_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/_shared.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/audit.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/auth_helpers.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/builder.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/client.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/diagnostics.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/drift_check.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/models.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/output_profiles.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/tag_pattern.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/version_check.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/version_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/yml_editor.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/marketplace/yml_schema.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/lsp.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/subsets.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/dependency/types.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/format_detection.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/_constraint_pinning.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/_shared.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/policy_checks.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/registry/operations.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/runtime/utils.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/base.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/gate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/generic_sarif.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/options.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/registry.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/runner.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/sarif_ingest.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/external/skillspector.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/content_hash.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/git_sparse.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/patterns.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/perf_stats.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/version.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_apm_resolver.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_console.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_github_downloader.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_gitlab_git_transport.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_token_manager.py +0 -0
- {apm_cli-0.23.1 → apm_cli-0.24.1}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -703,6 +703,41 @@ THE SOFTWARE.
|
|
|
703
703
|
|
|
704
704
|
---
|
|
705
705
|
|
|
706
|
+
## Component. tomlkit
|
|
707
|
+
|
|
708
|
+
- Version requirement: `>=0.13`
|
|
709
|
+
- Upstream: https://github.com/python-poetry/tomlkit
|
|
710
|
+
- SPDX: `MIT`
|
|
711
|
+
|
|
712
|
+
### Open Source License/Copyright Notice.
|
|
713
|
+
|
|
714
|
+
_Copyright (c) 2018 Sébastien Eustace_
|
|
715
|
+
|
|
716
|
+
```
|
|
717
|
+
Copyright (c) 2018 Sébastien Eustace
|
|
718
|
+
|
|
719
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
720
|
+
a copy of this software and associated documentation files (the
|
|
721
|
+
"Software"), to deal in the Software without restriction, including
|
|
722
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
723
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
724
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
725
|
+
the following conditions:
|
|
726
|
+
|
|
727
|
+
The above copyright notice and this permission notice shall be
|
|
728
|
+
included in all copies or substantial portions of the Software.
|
|
729
|
+
|
|
730
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
731
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
732
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
733
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
734
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
735
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
736
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
706
741
|
## Component. rich
|
|
707
742
|
|
|
708
743
|
- Version requirement: `>=13.0.0`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apm-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.1
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -46,6 +46,7 @@ Requires-Dist: llm>=0.28
|
|
|
46
46
|
Requires-Dist: llm-github-models>=0.18.0
|
|
47
47
|
Requires-Dist: tomli>=1.2.0; python_version < "3.11"
|
|
48
48
|
Requires-Dist: toml>=0.10.2
|
|
49
|
+
Requires-Dist: tomlkit>=0.13
|
|
49
50
|
Requires-Dist: rich>=13.0.0
|
|
50
51
|
Requires-Dist: rich-click>=1.7.0
|
|
51
52
|
Requires-Dist: watchdog>=3.0.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apm-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.24.1"
|
|
8
8
|
description = "MCP configuration tool"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -31,6 +31,7 @@ dependencies = [
|
|
|
31
31
|
"llm-github-models>=0.18.0",
|
|
32
32
|
"tomli>=1.2.0; python_version<'3.11'",
|
|
33
33
|
"toml>=0.10.2",
|
|
34
|
+
"tomlkit>=0.13",
|
|
34
35
|
"rich>=13.0.0",
|
|
35
36
|
"rich-click>=1.7.0",
|
|
36
37
|
"watchdog>=3.0.0",
|
|
@@ -170,4 +171,3 @@ markers = [
|
|
|
170
171
|
"requires_inference: requires APM_RUN_INFERENCE_TESTS=1 + model API access",
|
|
171
172
|
"req: binds a test to one or more OpenAPM req-XXX ids (spec-conformance suite)",
|
|
172
173
|
]
|
|
173
|
-
|
|
@@ -4,11 +4,11 @@ Project scope: ``.mcp.json`` at the project root with top-level ``mcpServers``
|
|
|
4
4
|
(``--scope project`` in Claude Code). Writes are opt-in when ``.claude/`` exists,
|
|
5
5
|
mirroring the Cursor/OpenCode directory-presence convention.
|
|
6
6
|
|
|
7
|
-
User scope: top-level ``mcpServers`` in
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
on a multi-user host.
|
|
7
|
+
User scope: top-level ``mcpServers`` in ``$CLAUDE_CONFIG_DIR/.claude.json``
|
|
8
|
+
when configured, otherwise ``~/.claude.json`` (``--scope user``). Writes are
|
|
9
|
+
atomic and create the file with ``0o600`` permissions on first write so the
|
|
10
|
+
shared Claude Code config is not silently truncated by a concurrent writer
|
|
11
|
+
and cannot leak any embedded OAuth state to other users on a multi-user host.
|
|
12
12
|
|
|
13
13
|
Local scope (Claude Code's third scope -- the default for ``claude mcp add``,
|
|
14
14
|
storing per-project private config under ``~/.claude.json -> projects.<abs_path>
|
|
@@ -20,12 +20,17 @@ See https://code.claude.com/docs/en/mcp
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
import json
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
23
25
|
from pathlib import Path
|
|
24
26
|
|
|
25
27
|
from ...utils.atomic_io import atomic_write_text
|
|
26
28
|
from ...utils.console import _rich_error, _rich_success, _rich_warning
|
|
29
|
+
from ...utils.path_security import PathTraversalError
|
|
27
30
|
from .copilot import CopilotClientAdapter
|
|
28
31
|
|
|
32
|
+
_log = logging.getLogger(__name__)
|
|
33
|
+
|
|
29
34
|
|
|
30
35
|
class ClaudeClientAdapter(CopilotClientAdapter):
|
|
31
36
|
"""MCP configuration for Claude Code (``mcpServers`` schema).
|
|
@@ -141,6 +146,15 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
141
146
|
return self.project_root / ".mcp.json"
|
|
142
147
|
|
|
143
148
|
def _user_claude_json_path(self) -> Path:
|
|
149
|
+
config_dir = os.environ.get("CLAUDE_CONFIG_DIR", "").strip()
|
|
150
|
+
if config_dir:
|
|
151
|
+
config_path = Path(config_dir).expanduser()
|
|
152
|
+
if not config_path.is_absolute():
|
|
153
|
+
raise PathTraversalError(
|
|
154
|
+
"CLAUDE_CONFIG_DIR must be an absolute path for user-scope MCP "
|
|
155
|
+
"config; set an absolute path or unset it to use ~/.claude.json."
|
|
156
|
+
)
|
|
157
|
+
return config_path.resolve(strict=False) / ".claude.json"
|
|
144
158
|
return Path.home() / ".claude.json"
|
|
145
159
|
|
|
146
160
|
def _should_write_project(self) -> bool:
|
|
@@ -206,6 +220,7 @@ class ClaudeClientAdapter(CopilotClientAdapter):
|
|
|
206
220
|
payload = json.dumps(data, indent=2) + "\n"
|
|
207
221
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
208
222
|
atomic_write_text(path, payload, new_file_mode=0o600)
|
|
223
|
+
_log.debug("Claude user-scope MCP config written to %s", path)
|
|
209
224
|
return True
|
|
210
225
|
except OSError:
|
|
211
226
|
return False
|
|
@@ -5,10 +5,12 @@ import os
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from urllib.parse import urlparse
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import tomlkit
|
|
9
|
+
from tomlkit.exceptions import TOMLKitError
|
|
9
10
|
|
|
10
11
|
from ...registry.client import SimpleRegistryClient
|
|
11
12
|
from ...registry.integration import RegistryIntegration
|
|
13
|
+
from ...utils.atomic_io import atomic_write_text
|
|
12
14
|
from ...utils.console import _rich_success, _rich_warning
|
|
13
15
|
from ...utils.path_security import PathTraversalError
|
|
14
16
|
from ._mcp_runtime_args import process_v01_value_hint_arg
|
|
@@ -94,9 +96,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
94
96
|
# Ensure directory exists
|
|
95
97
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
toml.dump(current_config, f)
|
|
99
|
-
os.chmod(config_path, 0o600)
|
|
99
|
+
atomic_write_text(config_path, tomlkit.dumps(current_config), new_file_mode=0o600)
|
|
100
100
|
_log.debug("Codex config written to %s", config_path)
|
|
101
101
|
return True
|
|
102
102
|
|
|
@@ -104,8 +104,9 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
104
104
|
"""Get the current Codex CLI MCP configuration.
|
|
105
105
|
|
|
106
106
|
Returns:
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
MutableMapping | None: Round-trip-preserving mapping when the file
|
|
108
|
+
exists, empty mapping if it does not exist, or None when an
|
|
109
|
+
existing config cannot be parsed safely.
|
|
109
110
|
"""
|
|
110
111
|
config_path = self.get_config_path()
|
|
111
112
|
|
|
@@ -113,12 +114,14 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
113
114
|
return {}
|
|
114
115
|
|
|
115
116
|
try:
|
|
116
|
-
with open(config_path, encoding="utf-8") as
|
|
117
|
-
return
|
|
118
|
-
except
|
|
117
|
+
with open(config_path, encoding="utf-8") as config_file:
|
|
118
|
+
return tomlkit.load(config_file)
|
|
119
|
+
except (TOMLKitError, UnicodeDecodeError) as exc:
|
|
119
120
|
_log.debug("Failed to parse Codex config at %s", config_path, exc_info=True)
|
|
120
121
|
_rich_warning(
|
|
121
|
-
f"Could not parse {config_path}: {exc}
|
|
122
|
+
f"Could not read or parse {config_path}: {exc} "
|
|
123
|
+
"-- skipping config write to avoid data loss; inspect the file "
|
|
124
|
+
"or delete it to reset Codex configuration",
|
|
122
125
|
symbol="warning",
|
|
123
126
|
)
|
|
124
127
|
return None
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Shared per-file provenance verification for the pack pipelines (#2013).
|
|
2
|
+
|
|
3
|
+
Both pack formats copy dependency files that ``apm install`` recorded in
|
|
4
|
+
``apm.lock.yaml`` under ``deployed_files`` + ``deployed_file_hashes``:
|
|
5
|
+
|
|
6
|
+
* ``--format plugin`` (:mod:`apm_cli.bundle.plugin_exporter`)
|
|
7
|
+
* ``--format apm`` (:mod:`apm_cli.bundle.packer`)
|
|
8
|
+
|
|
9
|
+
Before a byte enters a bundle its on-disk copy is verified against the
|
|
10
|
+
attested SHA-256 recorded at install time. A file that was tampered or
|
|
11
|
+
corrupted after ``apm install`` must never enter a bundle silently. Keeping
|
|
12
|
+
the check in ONE place gives both pack paths identical semantics and avoids a
|
|
13
|
+
copy-pasted block that would trip the pylint R0801 duplication gate.
|
|
14
|
+
|
|
15
|
+
Tolerance rule (forward-compat): a file with *no* recorded hash -- either an
|
|
16
|
+
older lockfile with an empty ``deployed_file_hashes`` or a specific path with
|
|
17
|
+
no recorded entry -- is packed without verification. Absence of an attestation
|
|
18
|
+
is tolerated; a *mismatched* attestation is a hard error. The unverified gap
|
|
19
|
+
is surfaced as a debug diagnostic so ``apm audit``-minded users can see it.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import logging
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
from ..utils.content_hash import compute_file_hash
|
|
26
|
+
|
|
27
|
+
_logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def verify_attested_file(
|
|
31
|
+
source: Path,
|
|
32
|
+
expected_hash: str | None,
|
|
33
|
+
dep_label: str,
|
|
34
|
+
rel_display: str,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Fail loudly when *source* diverges from its attested SHA-256.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
source: On-disk file being packed.
|
|
40
|
+
expected_hash: The ``"sha256:<hex>"`` recorded in
|
|
41
|
+
``deployed_file_hashes``, or ``None`` when no hash was recorded.
|
|
42
|
+
dep_label: Dependency identifier (``repo_url``) for the error message.
|
|
43
|
+
rel_display: Human-readable path shown in diagnostics / errors.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
ValueError: When a recorded hash exists and the on-disk content does
|
|
47
|
+
not match it.
|
|
48
|
+
"""
|
|
49
|
+
if not expected_hash:
|
|
50
|
+
_logger.debug(
|
|
51
|
+
"no attested hash for %r from %s; packed without integrity "
|
|
52
|
+
"verification (older lockfile)",
|
|
53
|
+
rel_display,
|
|
54
|
+
dep_label,
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
actual = compute_file_hash(source)
|
|
58
|
+
if actual != expected_hash:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"Cannot pack dependency {dep_label}: installed file {rel_display!r} "
|
|
61
|
+
"does not match the hash recorded in apm.lock.yaml. The installed "
|
|
62
|
+
"copy may be stale or tampered. Run 'apm install' to restore "
|
|
63
|
+
"attested content, then pack again."
|
|
64
|
+
)
|
|
@@ -13,6 +13,7 @@ from ..utils.archive import (
|
|
|
13
13
|
write_tar_archive,
|
|
14
14
|
write_zip_archive,
|
|
15
15
|
)
|
|
16
|
+
from .attest import verify_attested_file
|
|
16
17
|
from .lockfile_enrichment import _filter_files_by_target, enrich_lockfile_for_pack
|
|
17
18
|
|
|
18
19
|
|
|
@@ -158,10 +159,19 @@ def pack_bundle(
|
|
|
158
159
|
# not portable and is bundled separately via the project's own files
|
|
159
160
|
# (or rejected outright at L89-97 for manifest-declared local deps).
|
|
160
161
|
all_deployed: list[str] = []
|
|
162
|
+
# Provenance map: deployed-path -> (attested SHA-256, dependency label).
|
|
163
|
+
# ``deployed_file_hashes`` is keyed by the on-disk deployed path recorded
|
|
164
|
+
# at install time (see install/phases/lockfile.compute_deployed_hashes), so
|
|
165
|
+
# verification below must look up the on-disk path, not the bundle path.
|
|
166
|
+
deployed_hashes: dict[str, str] = {}
|
|
167
|
+
hash_dep_labels: dict[str, str] = {}
|
|
161
168
|
for dep in lockfile.get_all_dependencies():
|
|
162
169
|
if dep.source == "local":
|
|
163
170
|
continue
|
|
164
171
|
all_deployed.extend(dep.deployed_files)
|
|
172
|
+
for _dpath, _dhash in dep.deployed_file_hashes.items():
|
|
173
|
+
deployed_hashes[_dpath] = _dhash
|
|
174
|
+
hash_dep_labels[_dpath] = dep.repo_url
|
|
165
175
|
|
|
166
176
|
filtered_files, path_mappings = _filter_files_by_target(all_deployed, effective_target)
|
|
167
177
|
# Deduplicate while preserving order
|
|
@@ -250,7 +260,12 @@ def pack_bundle(
|
|
|
250
260
|
bundle_dir.mkdir(parents=True, exist_ok=True)
|
|
251
261
|
bundle_dir_resolved = bundle_dir.resolve()
|
|
252
262
|
|
|
253
|
-
# 7. Copy files preserving directory structure
|
|
263
|
+
# 7. Copy files preserving directory structure.
|
|
264
|
+
# Provenance gate (#2013): each dependency file recorded in the lockfile
|
|
265
|
+
# is verified against its attested SHA-256 before it enters the bundle --
|
|
266
|
+
# the same integrity guarantee the plugin format enforces, applied to the
|
|
267
|
+
# default/most-used ``apm`` format. Files with no recorded hash (older
|
|
268
|
+
# lockfiles) pack without verification; a mismatch fails loud.
|
|
254
269
|
for rel_path in unique_files:
|
|
255
270
|
# For cross-target mapped files, read from the original disk path
|
|
256
271
|
disk_path = path_mappings.get(rel_path, rel_path)
|
|
@@ -264,8 +279,35 @@ def pack_bundle(
|
|
|
264
279
|
if src.is_dir():
|
|
265
280
|
from ..security.gate import ignore_non_content
|
|
266
281
|
|
|
282
|
+
# Verify each contained file's attested hash and re-assert
|
|
283
|
+
# containment per child. copytree already drops symlinks via
|
|
284
|
+
# ignore_non_content, but the walk guards against a directory
|
|
285
|
+
# symlink whose target escapes the project root regardless of the
|
|
286
|
+
# walker's version-specific symlink-following behaviour.
|
|
287
|
+
disk_base = disk_path.rstrip("/")
|
|
288
|
+
for child in sorted(src.rglob("*")):
|
|
289
|
+
if not child.is_file() or child.is_symlink():
|
|
290
|
+
continue
|
|
291
|
+
if not child.resolve().is_relative_to(project_root_resolved):
|
|
292
|
+
raise ValueError(
|
|
293
|
+
f"Refusing to pack path that escapes project root: "
|
|
294
|
+
f"{child.relative_to(project_root)!r}"
|
|
295
|
+
)
|
|
296
|
+
child_key = f"{disk_base}/{child.relative_to(src).as_posix()}"
|
|
297
|
+
verify_attested_file(
|
|
298
|
+
child,
|
|
299
|
+
deployed_hashes.get(child_key),
|
|
300
|
+
hash_dep_labels.get(child_key, "dependency"),
|
|
301
|
+
child_key,
|
|
302
|
+
)
|
|
267
303
|
shutil.copytree(src, dest, dirs_exist_ok=True, ignore=ignore_non_content)
|
|
268
304
|
else:
|
|
305
|
+
verify_attested_file(
|
|
306
|
+
src,
|
|
307
|
+
deployed_hashes.get(disk_path),
|
|
308
|
+
hash_dep_labels.get(disk_path, "dependency"),
|
|
309
|
+
disk_path,
|
|
310
|
+
)
|
|
269
311
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
270
312
|
shutil.copy2(src, dest, follow_symlinks=False)
|
|
271
313
|
|
|
@@ -11,7 +11,7 @@ import hashlib
|
|
|
11
11
|
import json
|
|
12
12
|
import re
|
|
13
13
|
import shutil
|
|
14
|
-
from pathlib import Path, PurePosixPath
|
|
14
|
+
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
15
15
|
|
|
16
16
|
import yaml
|
|
17
17
|
|
|
@@ -30,6 +30,7 @@ from ..utils.archive import (
|
|
|
30
30
|
)
|
|
31
31
|
from ..utils.console import _rich_warning
|
|
32
32
|
from ..utils.path_security import PathTraversalError, ensure_path_within, safe_rmtree
|
|
33
|
+
from .attest import verify_attested_file
|
|
33
34
|
from .packer import PackResult
|
|
34
35
|
|
|
35
36
|
# ---------------------------------------------------------------------------
|
|
@@ -417,6 +418,217 @@ def _dep_install_path(dep: LockedDependency, apm_modules_dir: Path) -> Path:
|
|
|
417
418
|
return dep_ref.get_install_path(apm_modules_dir)
|
|
418
419
|
|
|
419
420
|
|
|
421
|
+
def _deployed_path_parts(rel_path: str) -> tuple[str, ...]:
|
|
422
|
+
"""Return safe POSIX path parts for a lockfile deployed_files entry."""
|
|
423
|
+
rel = rel_path.replace("\\", "/")
|
|
424
|
+
pure = PurePosixPath(rel)
|
|
425
|
+
if pure.is_absolute() or PureWindowsPath(rel).is_absolute():
|
|
426
|
+
raise ValueError(f"Refusing to pack absolute deployed file path: {rel_path!r}")
|
|
427
|
+
parts = pure.parts
|
|
428
|
+
if any(part in ("", ".", "..") for part in parts):
|
|
429
|
+
raise ValueError(f"Refusing to pack unsafe deployed file path: {rel_path!r}")
|
|
430
|
+
return parts
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _skill_name_from_deployed_parts(parts: tuple[str, ...]) -> str | None:
|
|
434
|
+
"""Return the deployed skill name when *parts* point inside a skills dir."""
|
|
435
|
+
if len(parts) >= 3 and parts[0].startswith(".") and parts[1] == "skills":
|
|
436
|
+
return parts[2]
|
|
437
|
+
if len(parts) >= 2 and parts[0] == "skills":
|
|
438
|
+
return parts[1]
|
|
439
|
+
return None
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _plugin_rel_for_deployed_path(
|
|
443
|
+
rel_path: str,
|
|
444
|
+
skill_subset: set[str] | None,
|
|
445
|
+
) -> str | None:
|
|
446
|
+
"""Map an installed deployed_files path back to plugin-native output layout."""
|
|
447
|
+
parts = _deployed_path_parts(rel_path)
|
|
448
|
+
if not parts:
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
if parts[0] == "skills":
|
|
452
|
+
skill_name = _skill_name_from_deployed_parts(parts)
|
|
453
|
+
if skill_subset and (skill_name is None or skill_name not in skill_subset):
|
|
454
|
+
return None
|
|
455
|
+
plugin_parts = list(parts)
|
|
456
|
+
elif parts[0] in {"agents", "commands", "instructions", "extensions"}:
|
|
457
|
+
plugin_parts = list(parts)
|
|
458
|
+
elif len(parts) >= 3 and parts[0].startswith("."):
|
|
459
|
+
skill_name = _skill_name_from_deployed_parts(parts)
|
|
460
|
+
if skill_name is not None:
|
|
461
|
+
if skill_subset and skill_name not in skill_subset:
|
|
462
|
+
return None
|
|
463
|
+
plugin_parts = ["skills", *parts[2:]]
|
|
464
|
+
elif parts[1] == "agents":
|
|
465
|
+
plugin_parts = ["agents", *parts[2:]]
|
|
466
|
+
elif parts[1] in {"commands", "prompts"}:
|
|
467
|
+
plugin_parts = ["commands", *parts[2:]]
|
|
468
|
+
elif parts[1] in {"instructions", "rules", "steering"}:
|
|
469
|
+
plugin_parts = ["instructions", *parts[2:]]
|
|
470
|
+
elif parts[1] == "extensions":
|
|
471
|
+
plugin_parts = ["extensions", *parts[2:]]
|
|
472
|
+
elif parts[1] == "hooks":
|
|
473
|
+
plugin_parts = ["hooks", *parts[2:]]
|
|
474
|
+
else:
|
|
475
|
+
return None
|
|
476
|
+
elif len(parts) >= 2 and parts[0].startswith(".") and parts[1] == "hooks.json":
|
|
477
|
+
plugin_parts = ["hooks.json"]
|
|
478
|
+
else:
|
|
479
|
+
return None
|
|
480
|
+
|
|
481
|
+
if plugin_parts[0] == "commands" and plugin_parts[-1].endswith(".prompt.md"):
|
|
482
|
+
plugin_parts[-1] = _rename_prompt(plugin_parts[-1])
|
|
483
|
+
return PurePosixPath(*plugin_parts).as_posix()
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _verify_attested_hash(
|
|
487
|
+
source: Path,
|
|
488
|
+
project_root: Path,
|
|
489
|
+
dep: LockedDependency,
|
|
490
|
+
) -> None:
|
|
491
|
+
"""Fail loudly when a packed deployed file diverges from its attested hash.
|
|
492
|
+
|
|
493
|
+
``deployed_file_hashes`` records the SHA-256 (``"sha256:<hex>"``) of each
|
|
494
|
+
file at install time. Verifying the on-disk copy before packing closes the
|
|
495
|
+
integrity half of the provenance hole: a deployed file that was tampered
|
|
496
|
+
or corrupted after ``apm install`` must never enter the bundle silently.
|
|
497
|
+
|
|
498
|
+
Files with no recorded hash (older lockfiles predating
|
|
499
|
+
``deployed_file_hashes``) are packed without verification -- absence of an
|
|
500
|
+
attestation is forward-compat tolerated, presence of a *mismatched* one is
|
|
501
|
+
a hard error. Delegates to the shared :func:`verify_attested_file` helper so
|
|
502
|
+
the plugin and archive pack paths share one implementation.
|
|
503
|
+
"""
|
|
504
|
+
rel = source.relative_to(project_root).as_posix()
|
|
505
|
+
expected = dep.deployed_file_hashes.get(rel) if dep.deployed_file_hashes else None
|
|
506
|
+
verify_attested_file(source, expected, dep.repo_url, rel)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _append_deployed_component(
|
|
510
|
+
components: list[tuple[Path, str]],
|
|
511
|
+
source: Path,
|
|
512
|
+
output_rel: str,
|
|
513
|
+
seen_outputs: set[str],
|
|
514
|
+
project_root: Path,
|
|
515
|
+
dep: LockedDependency,
|
|
516
|
+
) -> None:
|
|
517
|
+
"""Append one deployed component unless it is unsafe or already mapped."""
|
|
518
|
+
if output_rel in seen_outputs or not _validate_output_rel(output_rel):
|
|
519
|
+
return
|
|
520
|
+
if source.is_file() and not source.is_symlink():
|
|
521
|
+
_verify_attested_hash(source, project_root, dep)
|
|
522
|
+
components.append((source, output_rel))
|
|
523
|
+
seen_outputs.add(output_rel)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _collect_deployed_components(
|
|
527
|
+
project_root: Path,
|
|
528
|
+
dep: LockedDependency,
|
|
529
|
+
) -> list[tuple[Path, str]]:
|
|
530
|
+
"""Collect dependency components from lockfile deployed_files entries."""
|
|
531
|
+
components: list[tuple[Path, str]] = []
|
|
532
|
+
missing: list[str] = []
|
|
533
|
+
seen_outputs: set[str] = set()
|
|
534
|
+
skill_subset = set(dep.skill_subset) if dep.skill_subset else None
|
|
535
|
+
|
|
536
|
+
for rel_path in dep.deployed_files:
|
|
537
|
+
try:
|
|
538
|
+
plugin_rel = _plugin_rel_for_deployed_path(rel_path, skill_subset)
|
|
539
|
+
except ValueError as exc:
|
|
540
|
+
raise ValueError(f"Cannot pack dependency {dep.repo_url}: {exc}") from exc
|
|
541
|
+
if plugin_rel is None:
|
|
542
|
+
continue
|
|
543
|
+
source = project_root / rel_path
|
|
544
|
+
try:
|
|
545
|
+
source = ensure_path_within(source, project_root)
|
|
546
|
+
except PathTraversalError as exc:
|
|
547
|
+
raise ValueError(
|
|
548
|
+
f"Cannot pack dependency {dep.repo_url}: deployed file path "
|
|
549
|
+
f"escapes the project root: {rel_path!r}"
|
|
550
|
+
) from exc
|
|
551
|
+
if not source.exists():
|
|
552
|
+
missing.append(rel_path)
|
|
553
|
+
continue
|
|
554
|
+
if source.is_symlink():
|
|
555
|
+
continue
|
|
556
|
+
if source.is_dir():
|
|
557
|
+
for child in sorted(source.rglob("*")):
|
|
558
|
+
if not child.is_file() or child.is_symlink():
|
|
559
|
+
continue
|
|
560
|
+
# Defense-in-depth: rglob's symlink-following behaviour is
|
|
561
|
+
# Python-version dependent, so re-assert containment on every
|
|
562
|
+
# expanded child rather than trusting the walk. A planted
|
|
563
|
+
# directory symlink whose target escapes ``project_root`` is
|
|
564
|
+
# rejected here even if an intermediate component was a symlink.
|
|
565
|
+
try:
|
|
566
|
+
ensure_path_within(child, project_root)
|
|
567
|
+
except PathTraversalError:
|
|
568
|
+
continue
|
|
569
|
+
child_rel = child.relative_to(source).as_posix()
|
|
570
|
+
child_output = (PurePosixPath(plugin_rel) / child_rel).as_posix()
|
|
571
|
+
_append_deployed_component(
|
|
572
|
+
components, child, child_output, seen_outputs, project_root, dep
|
|
573
|
+
)
|
|
574
|
+
else:
|
|
575
|
+
_append_deployed_component(
|
|
576
|
+
components, source, plugin_rel, seen_outputs, project_root, dep
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
if missing:
|
|
580
|
+
shown_missing = missing[:10]
|
|
581
|
+
remaining = len(missing) - len(shown_missing)
|
|
582
|
+
suffix = f"\n ... and {remaining} more" if remaining else ""
|
|
583
|
+
raise ValueError(
|
|
584
|
+
f"Cannot pack dependency {dep.repo_url}: installed files recorded "
|
|
585
|
+
"in apm.lock.yaml are missing on disk. Run 'apm install' to "
|
|
586
|
+
"restore them, then pack again:\n"
|
|
587
|
+
+ "\n".join(f" - {path}" for path in shown_missing)
|
|
588
|
+
+ suffix
|
|
589
|
+
)
|
|
590
|
+
return components
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _cache_would_contribute_primitives(install_path: Path, dep: LockedDependency) -> bool:
|
|
594
|
+
"""Return True if the unattested apm_modules cache holds packable primitives.
|
|
595
|
+
|
|
596
|
+
Used ONLY to decide whether to fail loudly when a locked dependency records
|
|
597
|
+
no ``deployed_files``. This reads the cache to DETECT a provenance gap; it
|
|
598
|
+
never copies cache bytes into the bundle. A dependency that installed
|
|
599
|
+
real components but recorded none of them in the lockfile is a stale or
|
|
600
|
+
partial install -- packing its cache would leak unattested content, so we
|
|
601
|
+
refuse rather than silently pack it.
|
|
602
|
+
"""
|
|
603
|
+
if not install_path.is_dir():
|
|
604
|
+
return False
|
|
605
|
+
probe = _collect_apm_components(install_path / ".apm")
|
|
606
|
+
probe.extend(_collect_root_plugin_components(install_path))
|
|
607
|
+
_collect_bare_skill(install_path, dep, probe)
|
|
608
|
+
return bool(probe)
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def _cache_would_contribute_hooks_or_mcp(install_path: Path) -> bool:
|
|
612
|
+
"""Return True if the unattested cache holds hooks-config or MCP-config.
|
|
613
|
+
|
|
614
|
+
Hooks/MCP *configuration* (``.apm/hooks/*.json``, root ``hooks.json`` /
|
|
615
|
+
``hooks/``, ``.mcp.json``) is merged into shared host settings by
|
|
616
|
+
``apm install`` and is never recorded in the lockfile ``deployed_files``.
|
|
617
|
+
Because plugin pack now emits only lockfile-attested content, such config
|
|
618
|
+
is dropped from the bundle. This probe drives a transition warning that
|
|
619
|
+
names the dependency whose cached config will NOT be packed; it never
|
|
620
|
+
copies cache bytes. Hook *scripts* recorded in ``deployed_files`` are
|
|
621
|
+
unaffected and still pack.
|
|
622
|
+
"""
|
|
623
|
+
if not install_path.is_dir():
|
|
624
|
+
return False
|
|
625
|
+
if _collect_mcp(install_path):
|
|
626
|
+
return True
|
|
627
|
+
if _collect_hooks_from_apm(install_path / ".apm"):
|
|
628
|
+
return True
|
|
629
|
+
return bool(_collect_hooks_from_root(install_path))
|
|
630
|
+
|
|
631
|
+
|
|
420
632
|
# ---------------------------------------------------------------------------
|
|
421
633
|
# Main exporter
|
|
422
634
|
# ---------------------------------------------------------------------------
|
|
@@ -500,34 +712,59 @@ def export_plugin_bundle(
|
|
|
500
712
|
):
|
|
501
713
|
continue
|
|
502
714
|
|
|
503
|
-
install_path = _dep_install_path(dep, apm_modules_dir)
|
|
504
|
-
if not install_path.is_dir():
|
|
505
|
-
continue
|
|
506
|
-
|
|
507
715
|
dep_name = dep.repo_url
|
|
508
716
|
|
|
509
|
-
#
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
#
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
717
|
+
# Provenance rule (issue #1999 follow-up): a dependency's content
|
|
718
|
+
# is packed EXCLUSIVELY from what the lockfile attests via
|
|
719
|
+
# ``deployed_files`` (+ per-file ``deployed_file_hashes``).
|
|
720
|
+
# ``apm_modules`` is an unattested cache -- it carries no integrity
|
|
721
|
+
# or provenance guarantee, so no bytes from it may reach the bundle.
|
|
722
|
+
install_path = _dep_install_path(dep, apm_modules_dir)
|
|
723
|
+
if dep.deployed_files:
|
|
724
|
+
dep_components = _collect_deployed_components(project_root, dep)
|
|
725
|
+
if dep.skill_subset and not dep_components:
|
|
726
|
+
declared_skills = ", ".join(dep.skill_subset)
|
|
727
|
+
raise ValueError(
|
|
728
|
+
f"Cannot pack dependency {dep.repo_url}: the skills "
|
|
729
|
+
f"recorded in apm.lock.yaml (skill_subset: {declared_skills}) "
|
|
730
|
+
"were not found among its installed files. Run 'apm install' "
|
|
731
|
+
"to re-deploy the expected skills, then pack again."
|
|
732
|
+
)
|
|
733
|
+
elif _cache_would_contribute_primitives(install_path, dep):
|
|
734
|
+
# Unattested primitives sit in the cache but the lockfile
|
|
735
|
+
# recorded none of them: a stale or partial install. Refuse
|
|
736
|
+
# to pack the cache -- fail loudly with a fix.
|
|
737
|
+
raise ValueError(
|
|
738
|
+
f"Cannot pack dependency {dep.repo_url}: the lockfile records "
|
|
739
|
+
"no deployed files for it, but installed content that cannot "
|
|
740
|
+
"be verified exists in the apm_modules cache (a stale or "
|
|
741
|
+
"partial install). Run 'apm install' to record provenance in "
|
|
742
|
+
"apm.lock.yaml, then pack again."
|
|
743
|
+
)
|
|
744
|
+
else:
|
|
745
|
+
# No attested content and nothing packable in the cache: the
|
|
746
|
+
# dependency contributes no plugin primitives (e.g. an
|
|
747
|
+
# MCP-only or hooks-config-only package). Skip it cleanly.
|
|
748
|
+
dep_components = []
|
|
749
|
+
|
|
750
|
+
# Transition warning (#2013): dependency hooks/MCP *config* lives in
|
|
751
|
+
# the unattested cache and is never packed. Name the dependency so
|
|
752
|
+
# an author who relied on it merging into shared settings is not
|
|
753
|
+
# surprised by the silent exclusion. Attested primitives are packed
|
|
754
|
+
# regardless of this warning.
|
|
755
|
+
if _cache_would_contribute_hooks_or_mcp(install_path):
|
|
756
|
+
_warn = (
|
|
757
|
+
f"dependency {dep.repo_url} contributed hooks/MCP config that "
|
|
758
|
+
"is not attested in apm.lock.yaml; it will NOT be packed. "
|
|
759
|
+
"Attested primitives (skills/agents/etc.) are unaffected."
|
|
760
|
+
)
|
|
761
|
+
if logger:
|
|
762
|
+
logger.warning(_warn)
|
|
763
|
+
else:
|
|
764
|
+
_rich_warning(_warn, symbol="warning")
|
|
518
765
|
|
|
519
766
|
_merge_file_map(file_map, dep_components, dep_name, force, collisions)
|
|
520
767
|
|
|
521
|
-
# Hooks -- deps merge (first wins among deps)
|
|
522
|
-
dep_hooks = _collect_hooks_from_apm(dep_apm_dir)
|
|
523
|
-
dep_hooks_root = _collect_hooks_from_root(install_path)
|
|
524
|
-
_deep_merge(dep_hooks, dep_hooks_root, overwrite=False)
|
|
525
|
-
_deep_merge(merged_hooks, dep_hooks, overwrite=False)
|
|
526
|
-
|
|
527
|
-
# MCP -- deps merge (first wins among deps)
|
|
528
|
-
dep_mcp = _collect_mcp(install_path)
|
|
529
|
-
_deep_merge(merged_mcp, dep_mcp, overwrite=False)
|
|
530
|
-
|
|
531
768
|
# 6. Collect own components (.apm/ and root-level)
|
|
532
769
|
own_apm_dir = project_root / ".apm"
|
|
533
770
|
own_components = _collect_apm_components(own_apm_dir)
|