apm-cli 0.8.11__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {apm_cli-0.8.11 → apm_cli-0.9.0}/AUTHORS +1 -0
- {apm_cli-0.8.11/src/apm_cli.egg-info → apm_cli-0.9.0}/PKG-INFO +15 -3
- {apm_cli-0.8.11 → apm_cli-0.9.0}/README.md +14 -2
- {apm_cli-0.8.11 → apm_cli-0.9.0}/pyproject.toml +1 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/base.py +6 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/codex.py +3 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/copilot.py +3 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/cursor.py +2 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/opencode.py +6 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/vscode.py +26 -5
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/bundle/lockfile_enrichment.py +39 -8
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/bundle/packer.py +19 -11
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/bundle/plugin_exporter.py +13 -10
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/bundle/unpacker.py +2 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/cli.py +12 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/_helpers.py +22 -9
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/compile/cli.py +62 -15
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/config.py +99 -26
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/deps/_utils.py +1 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/deps/cli.py +61 -130
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/init.py +53 -17
- apm_cli-0.9.0/src/apm_cli/commands/install.py +1498 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/marketplace.py +128 -12
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/mcp.py +133 -14
- apm_cli-0.9.0/src/apm_cli/commands/outdated.py +474 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/pack.py +3 -2
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/uninstall/cli.py +1 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/uninstall/engine.py +2 -2
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/update.py +9 -1
- apm_cli-0.9.0/src/apm_cli/commands/view.py +499 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/agents_compiler.py +52 -8
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/config.py +54 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/auth.py +111 -27
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/command_logger.py +79 -4
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/script_runner.py +3 -3
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/target_detection.py +142 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/token_manager.py +49 -17
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/github_downloader.py +714 -294
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/lockfile.py +63 -8
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/plugin_parser.py +113 -10
- apm_cli-0.9.0/src/apm_cli/deps/transport_selection.py +331 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/drift.py +55 -8
- apm_cli-0.9.0/src/apm_cli/install/__init__.py +23 -0
- apm_cli-0.9.0/src/apm_cli/install/context.py +118 -0
- apm_cli-0.9.0/src/apm_cli/install/helpers/__init__.py +1 -0
- apm_cli-0.9.0/src/apm_cli/install/helpers/security_scan.py +51 -0
- apm_cli-0.9.0/src/apm_cli/install/insecure_policy.py +239 -0
- apm_cli-0.9.0/src/apm_cli/install/mcp_registry.py +256 -0
- apm_cli-0.9.0/src/apm_cli/install/mcp_warnings.py +122 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/__init__.py +1 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/cleanup.py +153 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/download.py +141 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/finalize.py +55 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/integrate.py +412 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/local_content.py +130 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/lockfile.py +182 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/post_deps_local.py +122 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/resolve.py +338 -0
- apm_cli-0.9.0/src/apm_cli/install/phases/targets.py +104 -0
- apm_cli-0.9.0/src/apm_cli/install/pipeline.py +311 -0
- apm_cli-0.9.0/src/apm_cli/install/presentation/__init__.py +1 -0
- apm_cli-0.9.0/src/apm_cli/install/presentation/dry_run.py +93 -0
- apm_cli-0.9.0/src/apm_cli/install/request.py +43 -0
- apm_cli-0.9.0/src/apm_cli/install/service.py +79 -0
- apm_cli-0.9.0/src/apm_cli/install/services.py +230 -0
- apm_cli-0.9.0/src/apm_cli/install/sources.py +595 -0
- apm_cli-0.9.0/src/apm_cli/install/template.py +126 -0
- apm_cli-0.9.0/src/apm_cli/install/validation.py +358 -0
- apm_cli-0.9.0/src/apm_cli/integration/cleanup.py +211 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/hook_integrator.py +34 -6
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/mcp_integrator.py +66 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/targets.py +48 -10
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/marketplace/client.py +95 -10
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/marketplace/resolver.py +123 -9
- apm_cli-0.9.0/src/apm_cli/marketplace/shadow_detector.py +75 -0
- apm_cli-0.9.0/src/apm_cli/marketplace/validator.py +80 -0
- apm_cli-0.9.0/src/apm_cli/marketplace/version_pins.py +163 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/apm_package.py +3 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/dependency/__init__.py +2 -1
- apm_cli-0.9.0/src/apm_cli/models/dependency/mcp.py +254 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/dependency/reference.py +181 -71
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/dependency/types.py +9 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/validation.py +96 -22
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/policy_checks.py +15 -9
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/primitives/discovery.py +45 -20
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/registry/client.py +75 -7
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/registry/operations.py +13 -2
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/manager.py +2 -1
- apm_cli-0.9.0/src/apm_cli/update_policy.py +51 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/content_hash.py +23 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/github_host.py +38 -8
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/path_security.py +8 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0/src/apm_cli.egg-info}/PKG-INFO +15 -3
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli.egg-info/SOURCES.txt +34 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_apm_package_models.py +127 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_github_downloader.py +341 -138
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_lockfile.py +95 -1
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_token_manager.py +66 -2
- apm_cli-0.8.11/src/apm_cli/commands/install.py +0 -2466
- apm_cli-0.8.11/src/apm_cli/models/dependency/mcp.py +0 -136
- {apm_cli-0.8.11 → apm_cli-0.9.0}/LICENSE +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/setup.cfg +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/audit.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/safe_installer.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/apm_resolver.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/collection_parser.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/base_integrator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/instruction_integrator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/skill_integrator.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/marketplace/models.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/ci_checks.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/utils/yaml_io.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/version.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_apm_resolver.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_console.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.8.11 → apm_cli-0.9.0}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apm-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -90,6 +90,10 @@ dependencies:
|
|
|
90
90
|
- github/awesome-copilot/agents/api-architect.agent.md
|
|
91
91
|
# A full APM package with instructions, skills, prompts, hooks...
|
|
92
92
|
- microsoft/apm-sample-package#v1.0.0
|
|
93
|
+
mcp:
|
|
94
|
+
# MCP servers -- installed into every detected client
|
|
95
|
+
- name: io.github.github/github-mcp-server
|
|
96
|
+
transport: http # MCP transport name, not URL scheme -- connects over HTTPS
|
|
93
97
|
```
|
|
94
98
|
|
|
95
99
|
```bash
|
|
@@ -99,7 +103,7 @@ apm install # every agent is configured
|
|
|
99
103
|
|
|
100
104
|
## Highlights
|
|
101
105
|
|
|
102
|
-
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — instructions, skills, prompts, agents, hooks, plugins, MCP servers
|
|
106
|
+
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — instructions, skills, prompts, agents, hooks, plugins, and [MCP servers](https://microsoft.github.io/apm/guides/mcp-servers/) declared in `apm.yml` and deployed across every client on install
|
|
103
107
|
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
|
|
104
108
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
105
109
|
- **[Content security](https://microsoft.github.io/apm/enterprise/security/)** — `apm audit` scans for hidden Unicode; `apm install` blocks compromised packages before agents read them
|
|
@@ -161,6 +165,14 @@ apm marketplace add github/awesome-copilot
|
|
|
161
165
|
apm install azure-cloud-development@awesome-copilot
|
|
162
166
|
```
|
|
163
167
|
|
|
168
|
+
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, and OpenCode):
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
> *Codex CLI currently does not support remote MCP servers; the install will skip Codex with a notice. Omit `--transport http` to use the local Docker variant on Codex (requires `GITHUB_PERSONAL_ACCESS_TOKEN`).*
|
|
175
|
+
|
|
164
176
|
See the **[Getting Started guide](https://microsoft.github.io/apm/getting-started/quick-start/)** for the full walkthrough.
|
|
165
177
|
|
|
166
178
|
## Works with agentrc
|
|
@@ -171,7 +183,7 @@ Use agentrc to author high-quality instructions, then package them with APM to s
|
|
|
171
183
|
|
|
172
184
|
## Community
|
|
173
185
|
|
|
174
|
-
Created
|
|
186
|
+
Created by [@danielmeppiel](https://github.com/danielmeppiel). Maintained by [@danielmeppiel](https://github.com/danielmeppiel) and [@sergio-sisternes-epam](https://github.com/sergio-sisternes-epam).
|
|
175
187
|
|
|
176
188
|
- [Roadmap & Discussions](https://github.com/microsoft/apm/discussions/116)
|
|
177
189
|
- [Contributing](CONTRIBUTING.md)
|
|
@@ -28,6 +28,10 @@ dependencies:
|
|
|
28
28
|
- github/awesome-copilot/agents/api-architect.agent.md
|
|
29
29
|
# A full APM package with instructions, skills, prompts, hooks...
|
|
30
30
|
- microsoft/apm-sample-package#v1.0.0
|
|
31
|
+
mcp:
|
|
32
|
+
# MCP servers -- installed into every detected client
|
|
33
|
+
- name: io.github.github/github-mcp-server
|
|
34
|
+
transport: http # MCP transport name, not URL scheme -- connects over HTTPS
|
|
31
35
|
```
|
|
32
36
|
|
|
33
37
|
```bash
|
|
@@ -37,7 +41,7 @@ apm install # every agent is configured
|
|
|
37
41
|
|
|
38
42
|
## Highlights
|
|
39
43
|
|
|
40
|
-
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — instructions, skills, prompts, agents, hooks, plugins, MCP servers
|
|
44
|
+
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — instructions, skills, prompts, agents, hooks, plugins, and [MCP servers](https://microsoft.github.io/apm/guides/mcp-servers/) declared in `apm.yml` and deployed across every client on install
|
|
41
45
|
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
|
|
42
46
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
43
47
|
- **[Content security](https://microsoft.github.io/apm/enterprise/security/)** — `apm audit` scans for hidden Unicode; `apm install` blocks compromised packages before agents read them
|
|
@@ -99,6 +103,14 @@ apm marketplace add github/awesome-copilot
|
|
|
99
103
|
apm install azure-cloud-development@awesome-copilot
|
|
100
104
|
```
|
|
101
105
|
|
|
106
|
+
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, and OpenCode):
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> *Codex CLI currently does not support remote MCP servers; the install will skip Codex with a notice. Omit `--transport http` to use the local Docker variant on Codex (requires `GITHUB_PERSONAL_ACCESS_TOKEN`).*
|
|
113
|
+
|
|
102
114
|
See the **[Getting Started guide](https://microsoft.github.io/apm/getting-started/quick-start/)** for the full walkthrough.
|
|
103
115
|
|
|
104
116
|
## Works with agentrc
|
|
@@ -109,7 +121,7 @@ Use agentrc to author high-quality instructions, then package them with APM to s
|
|
|
109
121
|
|
|
110
122
|
## Community
|
|
111
123
|
|
|
112
|
-
Created
|
|
124
|
+
Created by [@danielmeppiel](https://github.com/danielmeppiel). Maintained by [@danielmeppiel](https://github.com/danielmeppiel) and [@sergio-sisternes-epam](https://github.com/sergio-sisternes-epam).
|
|
113
125
|
|
|
114
126
|
- [Roadmap & Discussions](https://github.com/microsoft/apm/discussions/116)
|
|
115
127
|
- [Contributing](CONTRIBUTING.md)
|
|
@@ -9,6 +9,12 @@ _INPUT_VAR_RE = re.compile(r"\$\{input:([^}]+)\}")
|
|
|
9
9
|
class MCPClientAdapter(ABC):
|
|
10
10
|
"""Base adapter for MCP clients."""
|
|
11
11
|
|
|
12
|
+
# Whether this adapter's config path is user/global-scoped (e.g.
|
|
13
|
+
# ``~/.copilot/``) rather than workspace-scoped (e.g. ``.vscode/``).
|
|
14
|
+
# Adapters that target a global path should override this to ``True``
|
|
15
|
+
# so that ``apm install --global`` can install MCP servers to them.
|
|
16
|
+
supports_user_scope: bool = False
|
|
17
|
+
|
|
12
18
|
@abstractmethod
|
|
13
19
|
def get_config_path(self):
|
|
14
20
|
"""Get the path to the MCP configuration file."""
|
|
@@ -20,7 +20,9 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
20
20
|
a global ~/.codex/config.toml file, following the TOML format for
|
|
21
21
|
MCP server configuration.
|
|
22
22
|
"""
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
supports_user_scope: bool = True
|
|
25
|
+
|
|
24
26
|
def __init__(self, registry_url=None):
|
|
25
27
|
"""Initialize the Codex CLI client adapter.
|
|
26
28
|
|
|
@@ -23,7 +23,9 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
23
23
|
a global ~/.copilot/mcp-config.json file, following the JSON format for
|
|
24
24
|
MCP server configuration.
|
|
25
25
|
"""
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
supports_user_scope: bool = True
|
|
28
|
+
|
|
27
29
|
def __init__(self, registry_url=None):
|
|
28
30
|
"""Initialize the Copilot CLI client adapter.
|
|
29
31
|
|
|
@@ -25,6 +25,8 @@ class CursorClientAdapter(CopilotClientAdapter):
|
|
|
25
25
|
of global ``~/.copilot/mcp-config.json``.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
+
supports_user_scope: bool = False
|
|
29
|
+
|
|
28
30
|
# ------------------------------------------------------------------ #
|
|
29
31
|
# Config path
|
|
30
32
|
# ------------------------------------------------------------------ #
|
|
@@ -40,6 +40,8 @@ class OpenCodeClientAdapter(CopilotClientAdapter):
|
|
|
40
40
|
and writes to ``opencode.json`` in the project root.
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
|
+
supports_user_scope: bool = False
|
|
44
|
+
|
|
43
45
|
def get_config_path(self):
|
|
44
46
|
"""Return the path to ``opencode.json`` in the repository root."""
|
|
45
47
|
return str(Path(os.getcwd()) / "opencode.json")
|
|
@@ -149,9 +151,12 @@ class OpenCodeClientAdapter(CopilotClientAdapter):
|
|
|
149
151
|
elif "url" in copilot_entry:
|
|
150
152
|
entry["type"] = "remote"
|
|
151
153
|
entry["url"] = copilot_entry["url"]
|
|
154
|
+
headers = copilot_entry.get("headers")
|
|
155
|
+
if headers:
|
|
156
|
+
entry["headers"] = dict(headers)
|
|
152
157
|
|
|
153
158
|
env = copilot_entry.get("env") or {}
|
|
154
159
|
if env:
|
|
155
|
-
entry["environment"] = env
|
|
160
|
+
entry["environment"] = dict(env)
|
|
156
161
|
|
|
157
162
|
return entry
|
|
@@ -320,17 +320,25 @@ class VSCodeClientAdapter(MCPClientAdapter):
|
|
|
320
320
|
}
|
|
321
321
|
# Check for remotes (similar to Copilot adapter)
|
|
322
322
|
elif "remotes" in server_info and server_info["remotes"]:
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
323
|
+
remote = self._select_remote_with_url(server_info["remotes"])
|
|
324
|
+
if remote:
|
|
325
|
+
transport = (remote.get("transport_type") or "").strip()
|
|
326
|
+
# Default to "http" when transport_type is missing/empty,
|
|
327
|
+
# matching the Copilot adapter behavior (copilot.py:190-192).
|
|
328
|
+
if not transport:
|
|
329
|
+
transport = "http"
|
|
330
|
+
elif transport not in ("sse", "http", "streamable-http"):
|
|
331
|
+
raise ValueError(
|
|
332
|
+
f"Unsupported remote transport '{transport}' for VS Code. "
|
|
333
|
+
f"Server: {server_info.get('name', 'unknown')}. "
|
|
334
|
+
f"Supported transports: http, sse, streamable-http.")
|
|
327
335
|
headers = remote.get("headers", {})
|
|
328
336
|
# Normalize header list format to dict
|
|
329
337
|
if isinstance(headers, list):
|
|
330
338
|
headers = {h["name"]: h["value"] for h in headers if "name" in h and "value" in h}
|
|
331
339
|
server_config = {
|
|
332
340
|
"type": transport,
|
|
333
|
-
"url": remote
|
|
341
|
+
"url": remote["url"].strip(),
|
|
334
342
|
"headers": headers,
|
|
335
343
|
}
|
|
336
344
|
input_vars.extend(
|
|
@@ -424,6 +432,19 @@ class VSCodeClientAdapter(MCPClientAdapter):
|
|
|
424
432
|
|
|
425
433
|
return []
|
|
426
434
|
|
|
435
|
+
@staticmethod
|
|
436
|
+
def _select_remote_with_url(remotes):
|
|
437
|
+
"""Return the first remote entry that has a non-empty URL.
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
dict or None: The first usable remote, or None if none found.
|
|
441
|
+
"""
|
|
442
|
+
for remote in remotes:
|
|
443
|
+
url = (remote.get("url") or "").strip()
|
|
444
|
+
if url:
|
|
445
|
+
return remote
|
|
446
|
+
return None
|
|
447
|
+
|
|
427
448
|
def _select_best_package(self, packages):
|
|
428
449
|
"""Select the best package for VS Code installation from available packages.
|
|
429
450
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Lockfile enrichment for pack-time metadata."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime, timezone
|
|
4
|
-
from typing import Dict, List, Tuple
|
|
4
|
+
from typing import Dict, List, Tuple, Union
|
|
5
5
|
|
|
6
6
|
from ..deps.lockfile import LockFile
|
|
7
7
|
|
|
@@ -55,7 +55,7 @@ _CROSS_TARGET_MAPS: Dict[str, Dict[str, str]] = {
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def _filter_files_by_target(
|
|
58
|
-
deployed_files: List[str], target: str
|
|
58
|
+
deployed_files: List[str], target: Union[str, List[str]]
|
|
59
59
|
) -> Tuple[List[str], Dict[str, str]]:
|
|
60
60
|
"""Filter deployed file paths by target prefix, with cross-target mapping.
|
|
61
61
|
|
|
@@ -64,16 +64,38 @@ def _filter_files_by_target(
|
|
|
64
64
|
remapped to the equivalent target path. Commands, instructions, and hooks
|
|
65
65
|
are NOT remapped -- they are target-specific.
|
|
66
66
|
|
|
67
|
+
*target* may be a single string or a list of strings. For a list, the
|
|
68
|
+
union of all relevant prefixes and cross-target maps is used.
|
|
69
|
+
|
|
67
70
|
Returns:
|
|
68
71
|
A tuple of ``(filtered_files, path_mappings)`` where *path_mappings*
|
|
69
72
|
maps ``bundle_path -> disk_path`` for any file that was cross-target
|
|
70
73
|
remapped. Direct matches have no entry in the dict.
|
|
71
74
|
"""
|
|
72
|
-
|
|
75
|
+
if isinstance(target, list):
|
|
76
|
+
# Union all prefixes for the targets in the list
|
|
77
|
+
prefixes: List[str] = []
|
|
78
|
+
seen_prefixes: set = set()
|
|
79
|
+
for t in target:
|
|
80
|
+
for p in _TARGET_PREFIXES.get(t, []):
|
|
81
|
+
if p not in seen_prefixes:
|
|
82
|
+
seen_prefixes.add(p)
|
|
83
|
+
prefixes.append(p)
|
|
84
|
+
# Union all cross-target maps
|
|
85
|
+
# NOTE: dict.update() means the last target's mapping wins when
|
|
86
|
+
# multiple targets map the same source prefix. In practice this
|
|
87
|
+
# is benign -- common multi-target combos (e.g. claude+copilot)
|
|
88
|
+
# match prefixes directly without needing cross-maps.
|
|
89
|
+
cross_map: Dict[str, str] = {}
|
|
90
|
+
for t in target:
|
|
91
|
+
cross_map.update(_CROSS_TARGET_MAPS.get(t, {}))
|
|
92
|
+
else:
|
|
93
|
+
prefixes = _TARGET_PREFIXES.get(target, _TARGET_PREFIXES["all"])
|
|
94
|
+
cross_map = _CROSS_TARGET_MAPS.get(target, {})
|
|
95
|
+
|
|
73
96
|
direct = [f for f in deployed_files if any(f.startswith(p) for p in prefixes)]
|
|
74
97
|
|
|
75
98
|
path_mappings: Dict[str, str] = {}
|
|
76
|
-
cross_map = _CROSS_TARGET_MAPS.get(target, {})
|
|
77
99
|
if cross_map:
|
|
78
100
|
direct_set = set(direct)
|
|
79
101
|
for f in deployed_files:
|
|
@@ -94,7 +116,7 @@ def _filter_files_by_target(
|
|
|
94
116
|
def enrich_lockfile_for_pack(
|
|
95
117
|
lockfile: LockFile,
|
|
96
118
|
fmt: str,
|
|
97
|
-
target: str,
|
|
119
|
+
target: Union[str, List[str]],
|
|
98
120
|
) -> str:
|
|
99
121
|
"""Create an enriched copy of the lockfile YAML with a ``pack:`` section.
|
|
100
122
|
|
|
@@ -109,7 +131,8 @@ def enrich_lockfile_for_pack(
|
|
|
109
131
|
lockfile: The resolved lockfile to enrich.
|
|
110
132
|
fmt: Bundle format (``"apm"`` or ``"plugin"``).
|
|
111
133
|
target: Effective target used for packing (e.g. ``"copilot"``, ``"claude"``,
|
|
112
|
-
``"all"``).
|
|
134
|
+
``"all"``). May also be a list of target strings for multi-target
|
|
135
|
+
packing. The internal alias ``"vscode"`` is also accepted.
|
|
113
136
|
|
|
114
137
|
Returns:
|
|
115
138
|
A YAML string with the ``pack:`` block followed by the original
|
|
@@ -132,9 +155,12 @@ def enrich_lockfile_for_pack(
|
|
|
132
155
|
|
|
133
156
|
# Build the pack: metadata section (after filtering so we know if mapping
|
|
134
157
|
# occurred).
|
|
158
|
+
# Serialize target as a comma-joined string for backward compatibility
|
|
159
|
+
# with consumers that expect a plain string in pack.target.
|
|
160
|
+
target_str = ",".join(target) if isinstance(target, list) else target
|
|
135
161
|
pack_meta: Dict = {
|
|
136
162
|
"format": fmt,
|
|
137
|
-
"target":
|
|
163
|
+
"target": target_str,
|
|
138
164
|
"packed_at": datetime.now(timezone.utc).isoformat(),
|
|
139
165
|
}
|
|
140
166
|
if all_mappings:
|
|
@@ -142,7 +168,12 @@ def enrich_lockfile_for_pack(
|
|
|
142
168
|
# bundle paths differ from the original lockfile. Use the canonical
|
|
143
169
|
# prefix keys from _CROSS_TARGET_MAPS rather than reverse-engineering
|
|
144
170
|
# them from file paths.
|
|
145
|
-
|
|
171
|
+
if isinstance(target, list):
|
|
172
|
+
cross_map: Dict[str, str] = {}
|
|
173
|
+
for t in target:
|
|
174
|
+
cross_map.update(_CROSS_TARGET_MAPS.get(t, {}))
|
|
175
|
+
else:
|
|
176
|
+
cross_map = _CROSS_TARGET_MAPS.get(target, {})
|
|
146
177
|
used_src_prefixes = set()
|
|
147
178
|
for original in all_mappings.values():
|
|
148
179
|
for src_prefix in cross_map:
|
|
@@ -5,7 +5,7 @@ import shutil
|
|
|
5
5
|
import tarfile
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Dict, List, Optional
|
|
8
|
+
from typing import Dict, List, Optional, Union
|
|
9
9
|
|
|
10
10
|
from ..deps.lockfile import LockFile, get_lockfile_path, migrate_lockfile_if_needed
|
|
11
11
|
from ..models.apm_package import APMPackage
|
|
@@ -28,7 +28,7 @@ def pack_bundle(
|
|
|
28
28
|
project_root: Path,
|
|
29
29
|
output_dir: Path,
|
|
30
30
|
fmt: str = "apm",
|
|
31
|
-
target: Optional[str] = None,
|
|
31
|
+
target: Optional[Union[str, List[str]]] = None,
|
|
32
32
|
archive: bool = False,
|
|
33
33
|
dry_run: bool = False,
|
|
34
34
|
force: bool = False,
|
|
@@ -40,7 +40,8 @@ def pack_bundle(
|
|
|
40
40
|
project_root: Root of the project containing ``apm.lock.yaml`` and ``apm.yml``.
|
|
41
41
|
output_dir: Directory where the bundle will be created.
|
|
42
42
|
fmt: Bundle format -- ``"apm"`` (default) or ``"plugin"``.
|
|
43
|
-
target: Target filter -- ``"copilot"``, ``"claude"``, ``"all"``,
|
|
43
|
+
target: Target filter -- ``"copilot"``, ``"claude"``, ``"all"``, a list of
|
|
44
|
+
target strings (e.g. ``["claude", "vscode"]``), or *None*
|
|
44
45
|
(auto-detect from apm.yml / project structure).
|
|
45
46
|
archive: If *True*, produce a ``.tar.gz`` and remove the directory.
|
|
46
47
|
dry_run: If *True*, resolve the file list but write nothing to disk.
|
|
@@ -102,14 +103,21 @@ def pack_bundle(
|
|
|
102
103
|
config_target = None
|
|
103
104
|
|
|
104
105
|
# 3. Resolve effective target
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
effective_target =
|
|
106
|
+
if isinstance(target, list):
|
|
107
|
+
# List from CLI (e.g. --target claude,copilot) passes through directly
|
|
108
|
+
effective_target = target
|
|
109
|
+
elif isinstance(config_target, list) and target is None:
|
|
110
|
+
# List from apm.yml target: [claude, copilot]
|
|
111
|
+
effective_target = config_target
|
|
112
|
+
else:
|
|
113
|
+
effective_target, _reason = detect_target(
|
|
114
|
+
project_root,
|
|
115
|
+
explicit_target=target,
|
|
116
|
+
config_target=config_target if isinstance(config_target, str) else None,
|
|
117
|
+
)
|
|
118
|
+
# For packing purposes, "minimal" means nothing to pack -- treat as "all"
|
|
119
|
+
if effective_target == "minimal":
|
|
120
|
+
effective_target = "all"
|
|
113
121
|
|
|
114
122
|
# 4. Collect deployed_files from all dependencies, filtered by target
|
|
115
123
|
all_deployed: List[str] = []
|
|
@@ -9,7 +9,7 @@ import json
|
|
|
9
9
|
import os
|
|
10
10
|
import shutil
|
|
11
11
|
import tarfile
|
|
12
|
-
from pathlib import Path
|
|
12
|
+
from pathlib import Path, PurePosixPath
|
|
13
13
|
from typing import Dict, List, Optional, Set, Tuple
|
|
14
14
|
|
|
15
15
|
import yaml
|
|
@@ -63,6 +63,16 @@ def _rename_prompt(name: str) -> str:
|
|
|
63
63
|
return name
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
def _normalize_bare_skill_slug(slug: str) -> str:
|
|
67
|
+
"""Normalize bare-skill slugs derived from dependency virtual paths."""
|
|
68
|
+
normalized = slug.replace("\\", "/").strip("/")
|
|
69
|
+
while normalized.startswith("skills/"):
|
|
70
|
+
normalized = normalized[len("skills/") :].lstrip("/")
|
|
71
|
+
if normalized == "skills":
|
|
72
|
+
return ""
|
|
73
|
+
return PurePosixPath(normalized).as_posix() if normalized else ""
|
|
74
|
+
|
|
75
|
+
|
|
66
76
|
# ---------------------------------------------------------------------------
|
|
67
77
|
# Component collectors
|
|
68
78
|
# ---------------------------------------------------------------------------
|
|
@@ -130,7 +140,7 @@ def _collect_bare_skill(
|
|
|
130
140
|
return
|
|
131
141
|
# Derive a slug: prefer virtual_path (e.g. "frontend-design"), else last
|
|
132
142
|
# segment of repo_url (e.g. "my-skill" from "owner/my-skill")
|
|
133
|
-
slug = (getattr(dep, "virtual_path", "") or "")
|
|
143
|
+
slug = _normalize_bare_skill_slug(getattr(dep, "virtual_path", "") or "")
|
|
134
144
|
if not slug:
|
|
135
145
|
slug = dep.repo_url.rsplit("/", 1)[-1] if dep.repo_url else "skill"
|
|
136
146
|
for f in sorted(install_path.iterdir()):
|
|
@@ -386,14 +396,7 @@ def _update_plugin_json_paths(plugin_json: dict, output_files: List[str]) -> dic
|
|
|
386
396
|
|
|
387
397
|
def _dep_install_path(dep: LockedDependency, apm_modules_dir: Path) -> Path:
|
|
388
398
|
"""Compute the filesystem install path for a locked dependency."""
|
|
389
|
-
dep_ref =
|
|
390
|
-
repo_url=dep.repo_url,
|
|
391
|
-
host=dep.host,
|
|
392
|
-
virtual_path=dep.virtual_path,
|
|
393
|
-
is_virtual=dep.is_virtual,
|
|
394
|
-
is_local=(dep.source == "local"),
|
|
395
|
-
local_path=dep.local_path,
|
|
396
|
-
)
|
|
399
|
+
dep_ref = dep.to_dependency_ref()
|
|
397
400
|
return dep_ref.get_install_path(apm_modules_dir)
|
|
398
401
|
|
|
399
402
|
|
|
@@ -56,7 +56,8 @@ def unpack_bundle(
|
|
|
56
56
|
# 1. If archive, extract to temp dir
|
|
57
57
|
cleanup_temp = False
|
|
58
58
|
if bundle_path.is_file() and bundle_path.name.endswith(".tar.gz"):
|
|
59
|
-
|
|
59
|
+
from ..config import get_apm_temp_dir
|
|
60
|
+
temp_dir = Path(tempfile.mkdtemp(prefix="apm-unpack-", dir=get_apm_temp_dir()))
|
|
60
61
|
cleanup_temp = True
|
|
61
62
|
try:
|
|
62
63
|
with tarfile.open(bundle_path, "r:gz") as tar:
|
|
@@ -20,11 +20,13 @@ from apm_cli.commands.audit import audit
|
|
|
20
20
|
from apm_cli.commands.compile import compile as compile_cmd
|
|
21
21
|
from apm_cli.commands.config import config
|
|
22
22
|
from apm_cli.commands.deps import deps
|
|
23
|
+
from apm_cli.commands.view import view as view_cmd
|
|
23
24
|
from apm_cli.commands.init import init
|
|
24
25
|
from apm_cli.commands.install import install
|
|
25
26
|
from apm_cli.commands.list_cmd import list as list_cmd
|
|
26
27
|
from apm_cli.commands.marketplace import marketplace, search as marketplace_search
|
|
27
28
|
from apm_cli.commands.mcp import mcp
|
|
29
|
+
from apm_cli.commands.outdated import outdated as outdated_cmd
|
|
28
30
|
from apm_cli.commands.pack import pack_cmd, unpack_cmd
|
|
29
31
|
from apm_cli.commands.prune import prune
|
|
30
32
|
from apm_cli.commands.run import preview, run
|
|
@@ -57,6 +59,15 @@ def cli(ctx):
|
|
|
57
59
|
# Register command groups
|
|
58
60
|
cli.add_command(audit)
|
|
59
61
|
cli.add_command(deps)
|
|
62
|
+
cli.add_command(view_cmd)
|
|
63
|
+
# Hidden backward-compatible alias: ``apm info`` → ``apm view``
|
|
64
|
+
cli.add_command(click.Command(
|
|
65
|
+
name="info",
|
|
66
|
+
callback=view_cmd.callback,
|
|
67
|
+
params=list(view_cmd.params),
|
|
68
|
+
help=view_cmd.help,
|
|
69
|
+
hidden=True,
|
|
70
|
+
))
|
|
60
71
|
cli.add_command(pack_cmd, name="pack")
|
|
61
72
|
cli.add_command(unpack_cmd, name="unpack")
|
|
62
73
|
cli.add_command(init)
|
|
@@ -71,6 +82,7 @@ cli.add_command(list_cmd, name="list")
|
|
|
71
82
|
cli.add_command(config)
|
|
72
83
|
cli.add_command(runtime)
|
|
73
84
|
cli.add_command(mcp)
|
|
85
|
+
cli.add_command(outdated_cmd, name="outdated")
|
|
74
86
|
cli.add_command(marketplace)
|
|
75
87
|
cli.add_command(marketplace_search, name="search")
|
|
76
88
|
|
|
@@ -21,6 +21,7 @@ from ..constants import (
|
|
|
21
21
|
GITIGNORE_FILENAME,
|
|
22
22
|
)
|
|
23
23
|
from ..utils.console import _rich_echo, _rich_info, _rich_warning
|
|
24
|
+
from ..update_policy import get_update_hint_message, is_self_update_enabled
|
|
24
25
|
from ..version import get_build_sha, get_version
|
|
25
26
|
from ..utils.version_checker import check_for_updates
|
|
26
27
|
|
|
@@ -121,8 +122,6 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
|
|
|
121
122
|
(depth > 1 from ``apm.lock``), using ``get_install_path()`` for
|
|
122
123
|
consistency with how packages are actually installed.
|
|
123
124
|
"""
|
|
124
|
-
from ..models.apm_package import DependencyReference
|
|
125
|
-
|
|
126
125
|
expected = builtins.set()
|
|
127
126
|
for dep in declared_deps:
|
|
128
127
|
install_path = dep.get_install_path(apm_modules_dir)
|
|
@@ -135,12 +134,7 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
|
|
|
135
134
|
if lockfile:
|
|
136
135
|
for dep in lockfile.get_all_dependencies():
|
|
137
136
|
if dep.depth is not None and dep.depth > 1:
|
|
138
|
-
dep_ref =
|
|
139
|
-
repo_url=dep.repo_url,
|
|
140
|
-
host=dep.host,
|
|
141
|
-
virtual_path=dep.virtual_path,
|
|
142
|
-
is_virtual=dep.is_virtual,
|
|
143
|
-
)
|
|
137
|
+
dep_ref = dep.to_dependency_ref()
|
|
144
138
|
install_path = dep_ref.get_install_path(apm_modules_dir)
|
|
145
139
|
try:
|
|
146
140
|
relative_path = install_path.relative_to(apm_modules_dir)
|
|
@@ -240,6 +234,10 @@ def print_version(ctx, param, value):
|
|
|
240
234
|
def _check_and_notify_updates():
|
|
241
235
|
"""Check for updates and notify user non-blockingly."""
|
|
242
236
|
try:
|
|
237
|
+
# Skip notifications when self-update is disabled by distribution policy.
|
|
238
|
+
if not is_self_update_enabled():
|
|
239
|
+
return
|
|
240
|
+
|
|
243
241
|
# Skip version check in E2E test mode to avoid interfering with tests
|
|
244
242
|
if os.environ.get("APM_E2E_TESTS", "").lower() in ("1", "true", "yes"):
|
|
245
243
|
return
|
|
@@ -260,7 +258,7 @@ def _check_and_notify_updates():
|
|
|
260
258
|
)
|
|
261
259
|
|
|
262
260
|
# Show update command using helper for consistency
|
|
263
|
-
_rich_echo(
|
|
261
|
+
_rich_echo(get_update_hint_message(), color="yellow", bold=True)
|
|
264
262
|
|
|
265
263
|
# Add a blank line for visual separation
|
|
266
264
|
click.echo()
|
|
@@ -414,6 +412,21 @@ def _validate_plugin_name(name):
|
|
|
414
412
|
return bool(re.match(r"^[a-z][a-z0-9-]{0,63}$", name))
|
|
415
413
|
|
|
416
414
|
|
|
415
|
+
def _validate_project_name(name):
|
|
416
|
+
"""Validate that a project name is safe to use as a directory name.
|
|
417
|
+
|
|
418
|
+
Project names are used directly as directory names and must not contain
|
|
419
|
+
'/' or '\' so the name is not interpreted as a filesystem path,
|
|
420
|
+
and must not be '..' to prevent directory traversal.
|
|
421
|
+
|
|
422
|
+
Returns True if valid, False otherwise.
|
|
423
|
+
"""
|
|
424
|
+
if "/" in name or "\\" in name:
|
|
425
|
+
return False
|
|
426
|
+
if name == "..":
|
|
427
|
+
return False
|
|
428
|
+
return True
|
|
429
|
+
|
|
417
430
|
def _create_plugin_json(config):
|
|
418
431
|
"""Create plugin.json file with package metadata.
|
|
419
432
|
|