apm-cli 0.19.0__tar.gz → 0.20.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.19.0/src/apm_cli.egg-info → apm_cli-0.20.0}/PKG-INFO +4 -4
- {apm_cli-0.19.0 → apm_cli-0.20.0}/README.md +3 -3
- {apm_cli-0.19.0 → apm_cli-0.20.0}/pyproject.toml +1 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/base.py +18 -11
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/copilot.py +10 -2
- apm_cli-0.20.0/src/apm_cli/adapters/client/hermes.py +210 -0
- apm_cli-0.20.0/src/apm_cli/adapters/client/kiro.py +226 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/vscode.py +115 -30
- apm_cli-0.20.0/src/apm_cli/bootstrap_mirror.py +101 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/local_bundle.py +57 -62
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/lockfile_enrichment.py +3 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/packer.py +25 -7
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/plugin_exporter.py +21 -13
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/unpacker.py +46 -29
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/cli.py +11 -3
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/cli.py +7 -2
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/install.py +16 -16
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/__init__.py +151 -22
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/outdated.py +57 -6
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/pack.py +79 -5
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/self_update.py +87 -26
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/update.py +203 -28
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/agents_compiler.py +190 -5
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/claude_formatter.py +14 -7
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/distributed_compiler.py +241 -26
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/apm_yml.py +1 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/auth.py +66 -11
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/build_orchestrator.py +2 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/experimental.py +10 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/target_detection.py +34 -5
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/apm_resolver.py +155 -43
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/download_strategies.py +215 -54
- apm_cli-0.20.0/src/apm_cli/deps/git_file_transport.py +246 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_reference_resolver.py +18 -3
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_remote_ops.py +24 -3
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/github_downloader.py +22 -4
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/github_downloader_validation.py +5 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/host_backends.py +18 -6
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/lockfile.py +64 -5
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/path_anchoring.py +4 -4
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/client.py +9 -9
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/extractor.py +7 -33
- apm_cli-0.20.0/src/apm_cli/deps/revision_pins.py +262 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/factory.py +4 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/local_content.py +7 -7
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/lockfile.py +20 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/targets.py +59 -8
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/validation.py +8 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/hook_integrator.py +39 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/instruction_integrator.py +52 -0
- apm_cli-0.20.0/src/apm_cli/integration/kiro_hook_integrator.py +322 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/mcp_integrator.py +24 -3
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/mcp_integrator_install.py +142 -111
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/targets.py +92 -11
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/builder.py +225 -46
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/client.py +250 -15
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/models.py +48 -4
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/output_mappers.py +118 -55
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/resolver.py +18 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/yml_editor.py +19 -22
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/yml_schema.py +122 -17
- apm_cli-0.20.0/src/apm_cli/models/dependency/identity.py +112 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/reference.py +151 -43
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/types.py +2 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/policy_checks.py +1 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/operations.py +47 -18
- apm_cli-0.20.0/src/apm_cli/utils/archive.py +380 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/version_checker.py +66 -21
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/yaml_io.py +40 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0/src/apm_cli.egg-info}/PKG-INFO +4 -4
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/SOURCES.txt +9 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_apm_resolver.py +141 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_github_downloader.py +123 -41
- apm_cli-0.20.0/tests/test_gitlab_git_transport.py +663 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_lockfile.py +71 -1
- {apm_cli-0.19.0 → apm_cli-0.20.0}/AUTHORS +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/LICENSE +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/NOTICE +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/setup.cfg +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/claude.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/codex.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/cursor.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/intellij.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/git_cache.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/cli.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/_helpers.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/audit.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/deps/why.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/doctor.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/experimental.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/find.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/lock.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/audit.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/plugin/init.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/publish.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/managed_section.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/config.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/command_logger.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/errors.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/install_audit.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/plugin_manifest.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/safe_installer.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/_shared.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/bare_cache.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/clone_engine.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_auth_env.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/git_semver_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/outdated_row.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/plugin_parser.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/auth.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/outdated.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry/semver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/deps/why_walker.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/context.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/drift.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/errors.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/lsp/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/lsp/integration.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/manifest_reconcile.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/command.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/package_resolution.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/package_selection.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/_redownload.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/_skip_logic.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/audit.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/phases/resolve.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/pipeline.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/plan.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/registry_wiring.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/root_redirect.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/service.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/services.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/summary.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/_shared.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/base_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/lsp_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/skill_integrator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/_shared.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/audit.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/drift_check.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/publisher.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_check.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/marketplace/version_resolver.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/apm_package.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/lsp.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/dependency/mcp.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/format_detection.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/_shared.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/ci_checks.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/runtime/utils.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/base.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/gate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/generic_sarif.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/options.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/registry.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/runner.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/sarif_ingest.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/external/skillspector.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/content_hash.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/git_sparse.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/github_host.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/patterns.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/perf_stats.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/version.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_apm_package_models.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_console.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.0}/tests/test_token_manager.py +0 -0
- {apm_cli-0.19.0 → apm_cli-0.20.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.20.0
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -72,7 +72,7 @@ Dynamic: license-file
|
|
|
72
72
|
|
|
73
73
|
Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
|
|
74
74
|
|
|
75
|
-
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf
|
|
75
|
+
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf · Kiro
|
|
76
76
|
|
|
77
77
|
**[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)** · **[Roadmap](https://github.com/orgs/microsoft/projects/2304)**
|
|
78
78
|
|
|
@@ -135,7 +135,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
|
|
|
135
135
|
|
|
136
136
|
One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
|
|
137
137
|
|
|
138
|
-
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
|
|
138
|
+
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf, Kiro
|
|
139
139
|
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
|
|
140
140
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
141
141
|
- **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
|
|
@@ -214,7 +214,7 @@ apm marketplace add github/awesome-copilot
|
|
|
214
214
|
apm install azure-cloud-development@awesome-copilot
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, and
|
|
217
|
+
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, Windsurf, and Kiro):
|
|
218
218
|
|
|
219
219
|
```bash
|
|
220
220
|
apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
|
|
6
6
|
|
|
7
|
-
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf
|
|
7
|
+
GitHub Copilot · Claude Code · Cursor · OpenCode · Codex · Gemini · Windsurf · Kiro
|
|
8
8
|
|
|
9
9
|
**[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)** · **[Roadmap](https://github.com/orgs/microsoft/projects/2304)**
|
|
10
10
|
|
|
@@ -67,7 +67,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
|
|
|
67
67
|
|
|
68
68
|
One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
|
|
69
69
|
|
|
70
|
-
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
|
|
70
|
+
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf, Kiro
|
|
71
71
|
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
|
|
72
72
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
73
73
|
- **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
|
|
@@ -146,7 +146,7 @@ apm marketplace add github/awesome-copilot
|
|
|
146
146
|
apm install azure-cloud-development@awesome-copilot
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, and
|
|
149
|
+
Or add an MCP server (wired into Copilot, Claude, Cursor, Codex, OpenCode, Gemini, Windsurf, and Kiro):
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
apm install --mcp io.github.github/github-mcp-server --transport http # connects over HTTPS
|
|
@@ -4,7 +4,7 @@ import os
|
|
|
4
4
|
import re
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import ClassVar
|
|
7
|
+
from typing import Any, ClassVar
|
|
8
8
|
|
|
9
9
|
from ...utils.console import _rich_error, _rich_warning
|
|
10
10
|
|
|
@@ -29,6 +29,11 @@ _ENV_PLACEHOLDER_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>|" + _ENV_VAR_RE.pattern)
|
|
|
29
29
|
_LEGACY_ANGLE_VAR_RE = re.compile(r"<([A-Z_][A-Z0-9_]*)>")
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def registry_field_is_required(field: dict[str, Any]) -> bool:
|
|
33
|
+
"""Return True unless registry metadata explicitly marks a field optional."""
|
|
34
|
+
return field.get("required", field.get("is_required", True)) is not False
|
|
35
|
+
|
|
36
|
+
|
|
32
37
|
def _translate_env_placeholder(value):
|
|
33
38
|
"""Pure-textual translation of env-var placeholders to the canonical
|
|
34
39
|
``${VAR}`` runtime-substitution syntax.
|
|
@@ -481,7 +486,7 @@ class MCPClientAdapter(ABC):
|
|
|
481
486
|
name = env_var.get("name", "")
|
|
482
487
|
if not name:
|
|
483
488
|
continue
|
|
484
|
-
required = env_var
|
|
489
|
+
required = registry_field_is_required(env_var)
|
|
485
490
|
|
|
486
491
|
value = env_overrides.get(name) or os.getenv(name)
|
|
487
492
|
if not value and required and not skip_prompting:
|
|
@@ -797,12 +802,15 @@ class MCPClientAdapter(ABC):
|
|
|
797
802
|
)
|
|
798
803
|
|
|
799
804
|
# First pass: identify variables with empty values to warn the user.
|
|
800
|
-
empty_value_vars = [
|
|
805
|
+
empty_value_vars = [
|
|
806
|
+
ev for ev in env_vars if registry_field_is_required(ev) and not ev.get("value")
|
|
807
|
+
]
|
|
801
808
|
if empty_value_vars and skip_prompting:
|
|
802
809
|
var_names = [ev.get("name") for ev in empty_value_vars]
|
|
803
810
|
_rich_warning(
|
|
804
|
-
f"
|
|
805
|
-
f"
|
|
811
|
+
f"Required environment variables have no default value and cannot be "
|
|
812
|
+
f"prompted in non-interactive mode: {var_names}. Set them in your "
|
|
813
|
+
"environment and rerun `apm install`."
|
|
806
814
|
)
|
|
807
815
|
|
|
808
816
|
for env_var in env_vars:
|
|
@@ -837,9 +845,9 @@ class MCPClientAdapter(ABC):
|
|
|
837
845
|
|
|
838
846
|
# Priority 4: interactive prompt
|
|
839
847
|
default_value = env_var.get("value", "")
|
|
840
|
-
required = env_var
|
|
848
|
+
required = registry_field_is_required(env_var)
|
|
841
849
|
|
|
842
|
-
if not skip_prompting:
|
|
850
|
+
if not skip_prompting and required:
|
|
843
851
|
from rich.prompt import Prompt
|
|
844
852
|
|
|
845
853
|
description = env_var.get("description", "")
|
|
@@ -859,11 +867,10 @@ class MCPClientAdapter(ABC):
|
|
|
859
867
|
resolved[name] = default_value
|
|
860
868
|
elif required:
|
|
861
869
|
_rich_warning(
|
|
862
|
-
f"
|
|
863
|
-
f"The MCP server may not function correctly."
|
|
870
|
+
f"Required environment variable '{name}' could not be resolved. "
|
|
871
|
+
f"The MCP server may not function correctly. Set {name} in your "
|
|
872
|
+
"environment and rerun `apm install`."
|
|
864
873
|
)
|
|
865
874
|
resolved[name] = ""
|
|
866
|
-
else:
|
|
867
|
-
resolved[name] = default_value
|
|
868
875
|
|
|
869
876
|
return resolved
|
|
@@ -26,6 +26,7 @@ from .base import (
|
|
|
26
26
|
_extract_legacy_angle_vars,
|
|
27
27
|
_has_env_placeholder,
|
|
28
28
|
_stringify_env_literal,
|
|
29
|
+
registry_field_is_required,
|
|
29
30
|
)
|
|
30
31
|
from .base import (
|
|
31
32
|
_translate_env_placeholder as _translate_env_placeholder,
|
|
@@ -698,6 +699,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
698
699
|
return translated
|
|
699
700
|
|
|
700
701
|
if self._supports_runtime_env_substitution:
|
|
702
|
+
env_overrides = env_overrides or {}
|
|
701
703
|
resolved = {}
|
|
702
704
|
placeholder_keys = []
|
|
703
705
|
for env_var in env_vars:
|
|
@@ -706,12 +708,18 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
706
708
|
name = env_var.get("name", "")
|
|
707
709
|
if not name:
|
|
708
710
|
continue
|
|
711
|
+
required = registry_field_is_required(env_var)
|
|
712
|
+
override_value = env_overrides.get(name)
|
|
713
|
+
has_override = bool(
|
|
714
|
+
override_value.strip() if isinstance(override_value, str) else override_value
|
|
715
|
+
)
|
|
709
716
|
if name in default_github_env:
|
|
710
717
|
# Non-secret literal default -- preserve as-is.
|
|
711
718
|
resolved[name] = default_github_env[name]
|
|
712
|
-
|
|
719
|
+
elif required or has_override:
|
|
713
720
|
# Emit a runtime-substitution placeholder; APM never reads
|
|
714
|
-
# or stores the value.
|
|
721
|
+
# or stores the value. Optional variables are included only
|
|
722
|
+
# when install-time collection observed a value.
|
|
715
723
|
resolved[name] = self._format_runtime_env_placeholder(name)
|
|
716
724
|
placeholder_keys.append(name)
|
|
717
725
|
# Record for the post-install summary line and the
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Hermes agent MCP client adapter.
|
|
2
|
+
|
|
3
|
+
Hermes (Nous Research) reads MCP servers from a YAML ``mcp_servers:`` block
|
|
4
|
+
in ``~/.hermes/config.yaml`` (snake_case key -- distinct from the JSON
|
|
5
|
+
``mcpServers`` schema used by Claude/Copilot). ``$HERMES_HOME`` overrides
|
|
6
|
+
the home directory (default ``~/.hermes``).
|
|
7
|
+
|
|
8
|
+
Scope: Hermes has a single home-directory config, so MCP writes are always
|
|
9
|
+
user-scope -- ``config.yaml`` is the same file regardless of whether the
|
|
10
|
+
install was triggered at project or user scope. Unrelated top-level config
|
|
11
|
+
keys (model provider, telegram settings, ...) are preserved on every write;
|
|
12
|
+
a malformed existing file is left untouched rather than overwritten.
|
|
13
|
+
|
|
14
|
+
Per-server shape:
|
|
15
|
+
* stdio -> ``command`` / ``args`` / ``env`` (+ ``enabled``)
|
|
16
|
+
* http -> ``url`` / ``headers`` (+ ``enabled``)
|
|
17
|
+
|
|
18
|
+
YAML serialization goes through ``utils.yaml_io`` (lint forbids raw
|
|
19
|
+
``yaml.dump``); the document is written atomically with ``0o600`` perms via
|
|
20
|
+
``utils.atomic_io`` because ``config.yaml`` carries literal credentials.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import contextlib
|
|
26
|
+
import os
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
import yaml
|
|
30
|
+
|
|
31
|
+
from ...utils.atomic_io import atomic_write_text
|
|
32
|
+
from ...utils.console import _rich_error, _rich_success
|
|
33
|
+
from ...utils.yaml_io import load_yaml, yaml_to_str
|
|
34
|
+
from .copilot import CopilotClientAdapter
|
|
35
|
+
|
|
36
|
+
# Credential-bearing config file mode: owner read/write only. Hermes' config.yaml
|
|
37
|
+
# holds literal MCP env values plus native model-provider keys / messaging tokens,
|
|
38
|
+
# so it must never be group/world-readable (parity with claude/codex/gemini/cursor).
|
|
39
|
+
_CONFIG_FILE_MODE = 0o600
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _MalformedHermesConfig(Exception):
|
|
43
|
+
"""Raised when ``config.yaml`` exists but is not a YAML mapping.
|
|
44
|
+
|
|
45
|
+
Signals write paths to refuse the overwrite so a user's native Hermes
|
|
46
|
+
credentials (model-provider keys, Telegram tokens) are never discarded.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class HermesClientAdapter(CopilotClientAdapter):
|
|
51
|
+
"""MCP configuration for the Hermes agent (YAML ``mcp_servers`` schema).
|
|
52
|
+
|
|
53
|
+
Registry formatting reuses :class:`CopilotClientAdapter`, then entries are
|
|
54
|
+
converted to Hermes' on-disk shape via :meth:`_to_hermes_format`.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
supports_user_scope: bool = True
|
|
58
|
+
target_name: str = "hermes"
|
|
59
|
+
mcp_servers_key: str = "mcp_servers"
|
|
60
|
+
|
|
61
|
+
# Hermes' config.yaml does NOT support runtime env-var substitution; the
|
|
62
|
+
# value in ``env`` must be a literal string, so install-time resolution
|
|
63
|
+
# is kept (mirrors Claude -- see #1152 supply-chain analysis).
|
|
64
|
+
_supports_runtime_env_substitution: bool = False
|
|
65
|
+
|
|
66
|
+
def _config_path(self) -> Path:
|
|
67
|
+
"""Resolve ``<hermes-home>/config.yaml`` honouring ``$HERMES_HOME``."""
|
|
68
|
+
from ...integration.targets import resolve_hermes_root
|
|
69
|
+
|
|
70
|
+
return resolve_hermes_root() / "config.yaml"
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _to_hermes_format(copilot_entry: dict, *, enabled: bool = True) -> dict:
|
|
74
|
+
"""Convert a Copilot-format server entry to Hermes' on-disk shape.
|
|
75
|
+
|
|
76
|
+
Drops Copilot-CLI-only fields (``type: "local"``, default
|
|
77
|
+
``tools: ["*"]``, empty ``id``) and stamps an explicit ``enabled``.
|
|
78
|
+
Required transport fields (``url`` for remote, ``command`` for stdio)
|
|
79
|
+
are only emitted when truthy so a malformed entry never serializes as
|
|
80
|
+
``url: null`` / ``command: null`` into Hermes' config.
|
|
81
|
+
"""
|
|
82
|
+
if not isinstance(copilot_entry, dict):
|
|
83
|
+
return copilot_entry
|
|
84
|
+
|
|
85
|
+
url = copilot_entry.get("url")
|
|
86
|
+
t = copilot_entry.get("type")
|
|
87
|
+
is_remote = bool(url) or t in ("http", "sse", "streamable-http")
|
|
88
|
+
|
|
89
|
+
out: dict = {}
|
|
90
|
+
if is_remote:
|
|
91
|
+
if url:
|
|
92
|
+
out["url"] = url
|
|
93
|
+
headers = copilot_entry.get("headers")
|
|
94
|
+
if headers:
|
|
95
|
+
out["headers"] = headers
|
|
96
|
+
else:
|
|
97
|
+
command = copilot_entry.get("command")
|
|
98
|
+
if command:
|
|
99
|
+
out["command"] = command
|
|
100
|
+
args = copilot_entry.get("args")
|
|
101
|
+
if args:
|
|
102
|
+
out["args"] = list(args)
|
|
103
|
+
env = copilot_entry.get("env")
|
|
104
|
+
if env:
|
|
105
|
+
out["env"] = dict(env)
|
|
106
|
+
out["enabled"] = enabled
|
|
107
|
+
return out
|
|
108
|
+
|
|
109
|
+
def get_config_path(self):
|
|
110
|
+
"""Path to the Hermes config file (``<hermes-home>/config.yaml``)."""
|
|
111
|
+
return str(self._config_path())
|
|
112
|
+
|
|
113
|
+
def _load_document(self) -> dict:
|
|
114
|
+
"""Load the full ``config.yaml`` document (preserving siblings).
|
|
115
|
+
|
|
116
|
+
Returns ``{}`` when the file is absent or empty. Raises
|
|
117
|
+
:class:`_MalformedHermesConfig` when the file exists but is not a YAML
|
|
118
|
+
mapping (parse error or non-dict root) so write paths can refuse to
|
|
119
|
+
overwrite and silently discard the user's native Hermes credentials.
|
|
120
|
+
"""
|
|
121
|
+
path = self._config_path()
|
|
122
|
+
if not path.is_file():
|
|
123
|
+
return {}
|
|
124
|
+
try:
|
|
125
|
+
data = load_yaml(path)
|
|
126
|
+
except (OSError, yaml.YAMLError) as exc:
|
|
127
|
+
raise _MalformedHermesConfig(str(path)) from exc
|
|
128
|
+
if data is None:
|
|
129
|
+
return {}
|
|
130
|
+
if not isinstance(data, dict):
|
|
131
|
+
raise _MalformedHermesConfig(str(path))
|
|
132
|
+
return data
|
|
133
|
+
|
|
134
|
+
def get_current_config(self):
|
|
135
|
+
"""Return ``{"mcp_servers": {...}}`` for the on-disk config."""
|
|
136
|
+
try:
|
|
137
|
+
data = self._load_document()
|
|
138
|
+
except _MalformedHermesConfig:
|
|
139
|
+
return {self.mcp_servers_key: {}}
|
|
140
|
+
servers = data.get(self.mcp_servers_key)
|
|
141
|
+
return {self.mcp_servers_key: dict(servers) if isinstance(servers, dict) else {}}
|
|
142
|
+
|
|
143
|
+
def update_config(self, config_updates, enabled=True):
|
|
144
|
+
"""Merge *config_updates* into the ``mcp_servers:`` block.
|
|
145
|
+
|
|
146
|
+
Entries are normalized to Hermes' shape. Per-server entries are
|
|
147
|
+
replaced on key conflict; unrelated servers and all other top-level
|
|
148
|
+
config keys are preserved. The file is written atomically with
|
|
149
|
+
``0o600`` permissions so the credential-bearing config is never left
|
|
150
|
+
group/world-readable. A malformed existing ``config.yaml`` is left
|
|
151
|
+
untouched (returns ``False``) rather than overwritten.
|
|
152
|
+
"""
|
|
153
|
+
path = self._config_path()
|
|
154
|
+
try:
|
|
155
|
+
data = self._load_document()
|
|
156
|
+
except _MalformedHermesConfig:
|
|
157
|
+
_rich_error(
|
|
158
|
+
f"{path} is malformed YAML; refusing to overwrite. "
|
|
159
|
+
"Fix or remove the file manually, then retry."
|
|
160
|
+
)
|
|
161
|
+
return False
|
|
162
|
+
try:
|
|
163
|
+
servers = data.get(self.mcp_servers_key)
|
|
164
|
+
if not isinstance(servers, dict):
|
|
165
|
+
servers = {}
|
|
166
|
+
for name, cfg in config_updates.items():
|
|
167
|
+
servers[name] = self._to_hermes_format(cfg, enabled=enabled)
|
|
168
|
+
data[self.mcp_servers_key] = servers
|
|
169
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
170
|
+
atomic_write_text(path, yaml_to_str(data), new_file_mode=_CONFIG_FILE_MODE)
|
|
171
|
+
# Tighten perms even when the file pre-existed with a looser mode
|
|
172
|
+
# (atomic_write_text only applies new_file_mode on first create).
|
|
173
|
+
with contextlib.suppress(OSError, NotImplementedError):
|
|
174
|
+
os.chmod(path, _CONFIG_FILE_MODE)
|
|
175
|
+
return True
|
|
176
|
+
except OSError:
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
def configure_mcp_server(
|
|
180
|
+
self,
|
|
181
|
+
server_url,
|
|
182
|
+
server_name=None,
|
|
183
|
+
enabled=True,
|
|
184
|
+
env_overrides=None,
|
|
185
|
+
server_info_cache=None,
|
|
186
|
+
runtime_vars=None,
|
|
187
|
+
):
|
|
188
|
+
if not server_url:
|
|
189
|
+
_rich_error("server_url cannot be empty")
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
server_info = self._fetch_server_info(server_url, server_info_cache)
|
|
194
|
+
if server_info is None:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
198
|
+
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
199
|
+
ok = self.update_config({config_key: server_config}, enabled=enabled)
|
|
200
|
+
if not ok:
|
|
201
|
+
_rich_error(f"Failed to write MCP config for '{config_key}' to Hermes")
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
_rich_success(f"Successfully configured MCP server '{config_key}' for Hermes")
|
|
205
|
+
return True
|
|
206
|
+
except Exception:
|
|
207
|
+
# Do not interpolate the exception message: registry URLs and
|
|
208
|
+
# other inputs may carry embedded credentials.
|
|
209
|
+
_rich_error("Error configuring MCP server")
|
|
210
|
+
return False
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Kiro IDE implementation of MCP client adapter.
|
|
2
|
+
|
|
3
|
+
Kiro reads MCP configuration from ``.kiro/settings/mcp.json`` at project
|
|
4
|
+
scope and ``~/.kiro/settings/mcp.json`` at user scope. The schema uses a
|
|
5
|
+
``mcpServers`` object whose server entries support stdio ``command`` /
|
|
6
|
+
``args`` / ``env`` and remote ``url`` / ``headers`` entries. Kiro resolves
|
|
7
|
+
``${VAR}`` environment placeholders at runtime, so this adapter preserves
|
|
8
|
+
placeholders instead of writing host secrets to disk.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from ...utils.atomic_io import atomic_write_text
|
|
20
|
+
from ...utils.console import _rich_error, _rich_success
|
|
21
|
+
from .copilot import CopilotClientAdapter
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class KiroClientAdapter(CopilotClientAdapter):
|
|
27
|
+
"""Kiro IDE MCP client adapter."""
|
|
28
|
+
|
|
29
|
+
supports_user_scope: bool = True
|
|
30
|
+
_client_label: str = "Kiro"
|
|
31
|
+
target_name: str = "kiro"
|
|
32
|
+
mcp_servers_key: str = "mcpServers"
|
|
33
|
+
_supports_runtime_env_substitution: bool = True
|
|
34
|
+
|
|
35
|
+
def _get_kiro_root(self) -> Path:
|
|
36
|
+
"""Return the ``.kiro`` directory for the active scope."""
|
|
37
|
+
if self.user_scope:
|
|
38
|
+
return Path.home() / ".kiro"
|
|
39
|
+
return self.project_root / ".kiro"
|
|
40
|
+
|
|
41
|
+
def get_config_path(self) -> str:
|
|
42
|
+
"""Return the Kiro MCP config path for the active scope."""
|
|
43
|
+
return str(self._get_kiro_root() / "settings" / "mcp.json")
|
|
44
|
+
|
|
45
|
+
def get_current_config(self) -> dict[str, Any]:
|
|
46
|
+
"""Read the current Kiro MCP config."""
|
|
47
|
+
config_path = Path(self.get_config_path())
|
|
48
|
+
if not config_path.exists():
|
|
49
|
+
return {}
|
|
50
|
+
try:
|
|
51
|
+
with open(config_path, encoding="utf-8") as f:
|
|
52
|
+
data = json.load(f)
|
|
53
|
+
return data if isinstance(data, dict) else {}
|
|
54
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
55
|
+
logger.warning("Could not read %s: %s", config_path, exc)
|
|
56
|
+
return {}
|
|
57
|
+
|
|
58
|
+
def update_config(self, config_updates: dict[str, dict[str, Any]]) -> bool | None:
|
|
59
|
+
"""Merge *config_updates* into Kiro's ``mcpServers`` object.
|
|
60
|
+
|
|
61
|
+
Project scope is opt-in: the workspace must already contain ``.kiro/``.
|
|
62
|
+
User scope creates ``~/.kiro/settings/`` on demand.
|
|
63
|
+
"""
|
|
64
|
+
kiro_root = self._get_kiro_root()
|
|
65
|
+
if not self.user_scope and not kiro_root.is_dir():
|
|
66
|
+
logger.debug("Skipping Kiro project-scope write -- %s does not exist", kiro_root)
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
config_path = Path(self.get_config_path())
|
|
70
|
+
current_config = self.get_current_config()
|
|
71
|
+
if not isinstance(current_config.get(self.mcp_servers_key), dict):
|
|
72
|
+
current_config[self.mcp_servers_key] = {}
|
|
73
|
+
current_config[self.mcp_servers_key].update(config_updates)
|
|
74
|
+
|
|
75
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
atomic_write_text(
|
|
77
|
+
config_path,
|
|
78
|
+
json.dumps(current_config, indent=2),
|
|
79
|
+
new_file_mode=0o600,
|
|
80
|
+
)
|
|
81
|
+
# Keep existing config files private after updates too.
|
|
82
|
+
os.chmod(config_path, 0o600)
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def _header_mapping(remote: dict[str, Any]) -> dict[str, str]:
|
|
87
|
+
"""Return registry remote headers as string key-value pairs."""
|
|
88
|
+
headers = remote.get("headers", {})
|
|
89
|
+
if isinstance(headers, list):
|
|
90
|
+
return {
|
|
91
|
+
str(h["name"]): str(h["value"])
|
|
92
|
+
for h in headers
|
|
93
|
+
if isinstance(h, dict) and "name" in h and "value" in h
|
|
94
|
+
}
|
|
95
|
+
if isinstance(headers, dict):
|
|
96
|
+
return {str(name): str(value) for name, value in headers.items()}
|
|
97
|
+
return {}
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _copy_kiro_extensions(config: dict[str, Any], server_info: dict[str, Any]) -> None:
|
|
101
|
+
"""Carry Kiro-specific MCP fields when the registry supplies them."""
|
|
102
|
+
for key in ("autoApprove", "disabledTools", "disabled"):
|
|
103
|
+
if key in server_info and server_info[key] is not None:
|
|
104
|
+
config[key] = server_info[key]
|
|
105
|
+
|
|
106
|
+
def _format_server_config(
|
|
107
|
+
self,
|
|
108
|
+
server_info: dict[str, Any],
|
|
109
|
+
env_overrides: dict[str, str] | None = None,
|
|
110
|
+
runtime_vars: dict[str, str] | None = None,
|
|
111
|
+
) -> dict[str, Any]:
|
|
112
|
+
"""Format registry or self-defined server info for Kiro."""
|
|
113
|
+
if runtime_vars is None:
|
|
114
|
+
runtime_vars = {}
|
|
115
|
+
|
|
116
|
+
raw = server_info.get("_raw_stdio")
|
|
117
|
+
if raw:
|
|
118
|
+
config: dict[str, Any] = {"command": raw["command"]}
|
|
119
|
+
resolved_env_for_args: dict[str, Any] = {}
|
|
120
|
+
if raw.get("env"):
|
|
121
|
+
resolved_env_for_args = self._resolve_environment_variables(
|
|
122
|
+
raw["env"], env_overrides=env_overrides
|
|
123
|
+
)
|
|
124
|
+
config["env"] = resolved_env_for_args
|
|
125
|
+
self._warn_input_variables(raw["env"], server_info.get("name", ""), "Kiro")
|
|
126
|
+
config["args"] = [
|
|
127
|
+
self._resolve_variable_placeholders(arg, resolved_env_for_args, runtime_vars)
|
|
128
|
+
if isinstance(arg, str)
|
|
129
|
+
else arg
|
|
130
|
+
for arg in raw.get("args") or []
|
|
131
|
+
]
|
|
132
|
+
self._copy_kiro_extensions(config, server_info)
|
|
133
|
+
return config
|
|
134
|
+
|
|
135
|
+
remotes = server_info.get("remotes", [])
|
|
136
|
+
if remotes:
|
|
137
|
+
remote = self._select_remote_with_url(remotes) or remotes[0]
|
|
138
|
+
transport = (remote.get("transport_type") or "").strip()
|
|
139
|
+
if not transport:
|
|
140
|
+
transport = "http"
|
|
141
|
+
elif transport not in ("sse", "http", "streamable-http"):
|
|
142
|
+
raise ValueError(
|
|
143
|
+
f"Unsupported remote transport '{transport}' for Kiro. "
|
|
144
|
+
f"Server: {server_info.get('name', 'unknown')}. "
|
|
145
|
+
"Supported transports: http, sse, streamable-http."
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
config = {"url": (remote.get("url") or "").strip()}
|
|
149
|
+
headers = {
|
|
150
|
+
name: self._resolve_env_variable(name, value, env_overrides)
|
|
151
|
+
if isinstance(value, str)
|
|
152
|
+
else value
|
|
153
|
+
for name, value in self._header_mapping(remote).items()
|
|
154
|
+
if name
|
|
155
|
+
}
|
|
156
|
+
if headers:
|
|
157
|
+
config["headers"] = headers
|
|
158
|
+
self._warn_input_variables(headers, server_info.get("name", ""), "Kiro")
|
|
159
|
+
self._copy_kiro_extensions(config, server_info)
|
|
160
|
+
return config
|
|
161
|
+
|
|
162
|
+
packages = server_info.get("packages", [])
|
|
163
|
+
if not packages:
|
|
164
|
+
raise ValueError(
|
|
165
|
+
"MCP server has incomplete configuration in registry - "
|
|
166
|
+
"no package information or remote endpoints available. "
|
|
167
|
+
f"Server: {server_info.get('name', 'unknown')}"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
config = {}
|
|
171
|
+
package = self._select_and_dispatch_best_package(
|
|
172
|
+
config,
|
|
173
|
+
packages,
|
|
174
|
+
env_overrides,
|
|
175
|
+
runtime_vars,
|
|
176
|
+
)
|
|
177
|
+
if not package:
|
|
178
|
+
raise ValueError(
|
|
179
|
+
f"No supported package type found for Kiro. "
|
|
180
|
+
f"Server: {server_info.get('name', 'unknown')}."
|
|
181
|
+
)
|
|
182
|
+
self._copy_kiro_extensions(config, server_info)
|
|
183
|
+
return config
|
|
184
|
+
|
|
185
|
+
def configure_mcp_server(
|
|
186
|
+
self,
|
|
187
|
+
server_url: str,
|
|
188
|
+
server_name: str | None = None,
|
|
189
|
+
enabled: bool = True,
|
|
190
|
+
env_overrides: dict[str, str] | None = None,
|
|
191
|
+
server_info_cache: dict[str, Any] | None = None,
|
|
192
|
+
runtime_vars: dict[str, str] | None = None,
|
|
193
|
+
) -> bool:
|
|
194
|
+
"""Configure an MCP server in Kiro's MCP config."""
|
|
195
|
+
if not server_url:
|
|
196
|
+
_rich_error("server_url cannot be empty", symbol="error")
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
if not self.user_scope and not self._get_kiro_root().is_dir():
|
|
200
|
+
logger.debug(
|
|
201
|
+
"Kiro opt-in gate: %s absent, skipping configure_mcp_server",
|
|
202
|
+
self._get_kiro_root(),
|
|
203
|
+
)
|
|
204
|
+
return True
|
|
205
|
+
|
|
206
|
+
config_key = self._determine_config_key(server_url, server_name)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
server_info = self._fetch_server_info(server_url, server_info_cache)
|
|
210
|
+
if server_info is None:
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
self._last_env_placeholder_keys = set()
|
|
214
|
+
self._last_legacy_angle_vars = set()
|
|
215
|
+
|
|
216
|
+
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
217
|
+
if not enabled:
|
|
218
|
+
server_config["disabled"] = True
|
|
219
|
+
self.update_config({config_key: server_config})
|
|
220
|
+
|
|
221
|
+
_rich_success(f"Configured MCP server '{config_key}' for Kiro", symbol="success")
|
|
222
|
+
return True
|
|
223
|
+
except Exception as exc:
|
|
224
|
+
logger.debug("Kiro MCP configuration failed: %s", exc)
|
|
225
|
+
_rich_error(f"Failed to configure MCP server '{config_key}' for Kiro", symbol="error")
|
|
226
|
+
return False
|