From f16e020e12ce1865501e49ddd6bc7e689a23761d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20L=C3=B3pez=20S=C3=A1nchez?= Date: Thu, 26 Mar 2026 01:07:23 +0100 Subject: [PATCH] chore: sort all imports within the repo with `uv run isort .` According to the [docs](https://github.com/microsoft/apm/blob/main/CONTRIBUTING.md#coding-style), the project uses [isort[(https://pycqa.github.io/isort/) for import sorting. There were missing sorts causing unrelated changes due to the automatic formatting, and this commit fixes all of them. --- scripts/benchmark_manifest_ops.py | 4 +- src/apm_cli/adapters/client/codex.py | 7 ++- src/apm_cli/adapters/client/copilot.py | 11 +++-- src/apm_cli/adapters/client/vscode.py | 3 +- .../package_manager/default_manager.py | 4 +- src/apm_cli/bundle/__init__.py | 4 +- src/apm_cli/bundle/lockfile_enrichment.py | 1 - src/apm_cli/bundle/packer.py | 4 +- src/apm_cli/bundle/plugin_exporter.py | 5 +- src/apm_cli/bundle/unpacker.py | 2 +- src/apm_cli/commands/_helpers.py | 4 +- src/apm_cli/commands/audit.py | 6 +-- src/apm_cli/commands/compile/__init__.py | 2 +- src/apm_cli/commands/compile/cli.py | 3 +- src/apm_cli/commands/compile/watcher.py | 2 +- src/apm_cli/commands/config.py | 2 +- src/apm_cli/commands/deps/__init__.py | 10 ++-- src/apm_cli/commands/deps/_utils.py | 2 +- src/apm_cli/commands/deps/cli.py | 29 +++++------ src/apm_cli/commands/install.py | 49 ++++++++++++------- src/apm_cli/commands/prune.py | 4 +- src/apm_cli/commands/uninstall/__init__.py | 8 +-- src/apm_cli/commands/uninstall/cli.py | 12 ++--- src/apm_cli/commands/uninstall/engine.py | 15 +++--- src/apm_cli/compilation/__init__.py | 16 +++--- src/apm_cli/compilation/agents_compiler.py | 19 ++++--- src/apm_cli/compilation/claude_formatter.py | 4 +- src/apm_cli/compilation/constitution_block.py | 1 - src/apm_cli/compilation/context_optimizer.py | 16 +++--- .../compilation/distributed_compiler.py | 10 ++-- src/apm_cli/compilation/injector.py | 6 +-- src/apm_cli/compilation/link_resolver.py | 2 +- src/apm_cli/compilation/template_builder.py | 5 +- src/apm_cli/constants.py | 1 - src/apm_cli/core/conflict_detector.py | 3 +- src/apm_cli/core/docker_args.py | 2 +- src/apm_cli/core/safe_installer.py | 5 +- src/apm_cli/core/script_runner.py | 9 ++-- src/apm_cli/deps/__init__.py | 14 ++++-- src/apm_cli/deps/aggregator.py | 5 +- src/apm_cli/deps/apm_resolver.py | 13 +++-- src/apm_cli/deps/collection_parser.py | 5 +- src/apm_cli/deps/dependency_graph.py | 3 +- src/apm_cli/deps/github_downloader.py | 41 ++++++++-------- src/apm_cli/deps/package_validator.py | 9 ++-- src/apm_cli/deps/plugin_parser.py | 3 +- src/apm_cli/deps/verifier.py | 4 +- src/apm_cli/drift.py | 2 +- src/apm_cli/factory.py | 2 +- src/apm_cli/integration/__init__.py | 22 ++++----- src/apm_cli/integration/agent_integrator.py | 2 +- src/apm_cli/integration/base_integrator.py | 3 +- src/apm_cli/integration/command_integrator.py | 3 +- src/apm_cli/integration/hook_integrator.py | 4 +- src/apm_cli/integration/prompt_integrator.py | 2 +- src/apm_cli/integration/skill_integrator.py | 12 ++--- src/apm_cli/models/__init__.py | 2 +- src/apm_cli/models/apm_package.py | 5 +- src/apm_cli/models/dependency/__init__.py | 7 ++- src/apm_cli/models/plugin.py | 4 +- src/apm_cli/models/validation.py | 5 +- src/apm_cli/output/__init__.py | 7 ++- src/apm_cli/output/formatters.py | 9 ++-- src/apm_cli/output/models.py | 2 +- src/apm_cli/output/script_formatters.py | 6 +-- src/apm_cli/policy/__init__.py | 12 ++--- src/apm_cli/policy/ci_checks.py | 3 +- src/apm_cli/policy/inheritance.py | 1 - src/apm_cli/policy/models.py | 1 - src/apm_cli/policy/policy_checks.py | 3 +- src/apm_cli/primitives/__init__.py | 15 +++++- src/apm_cli/primitives/discovery.py | 9 ++-- src/apm_cli/primitives/models.py | 2 +- src/apm_cli/primitives/parser.py | 5 +- src/apm_cli/registry/client.py | 3 +- src/apm_cli/registry/integration.py | 4 +- src/apm_cli/registry/operations.py | 2 +- src/apm_cli/runtime/__init__.py | 2 +- src/apm_cli/runtime/base.py | 2 +- src/apm_cli/runtime/codex_runtime.py | 7 +-- src/apm_cli/runtime/copilot_runtime.py | 7 +-- src/apm_cli/runtime/factory.py | 5 +- src/apm_cli/runtime/llm_runtime.py | 5 +- src/apm_cli/runtime/manager.py | 5 +- src/apm_cli/security/gate.py | 3 +- src/apm_cli/utils/__init__.py | 22 ++++----- src/apm_cli/utils/console.py | 7 +-- src/apm_cli/utils/helpers.py | 2 +- src/apm_cli/utils/version_checker.py | 2 +- src/apm_cli/version.py | 4 +- src/apm_cli/workflow/discovery.py | 3 +- src/apm_cli/workflow/parser.py | 1 + src/apm_cli/workflow/runner.py | 6 ++- tests/acceptance/test_logging_acceptance.py | 1 - tests/basic_workflow_test.py | 8 +-- tests/benchmarks/run_baseline.py | 9 ++-- tests/benchmarks/test_perf_benchmarks.py | 15 +++--- tests/integration/test_apm_dependencies.py | 22 +++++---- tests/integration/test_auto_install_e2e.py | 4 +- tests/integration/test_auto_integration.py | 14 ++++-- tests/integration/test_collection_install.py | 18 ++++--- .../test_compile_constitution_injection.py | 5 +- .../test_compile_permission_denied.py | 7 ++- tests/integration/test_deployed_files_e2e.py | 3 +- .../test_diff_aware_install_e2e.py | 3 +- tests/integration/test_golden_scenario_e2e.py | 8 +-- .../integration/test_guardrailing_hero_e2e.py | 2 +- tests/integration/test_install_with_links.py | 14 ++++-- tests/integration/test_integration.py | 15 +++--- .../test_llm_runtime_integration.py | 5 +- tests/integration/test_local_install.py | 4 +- .../test_marketplace_plugin_integration.py | 3 +- tests/integration/test_mcp_registry_e2e.py | 9 ++-- tests/integration/test_mixed_deps.py | 2 +- .../test_multi_runtime_integration.py | 7 +-- tests/integration/test_pack_unpack_e2e.py | 3 +- tests/integration/test_plugin_e2e.py | 1 - tests/integration/test_registry.py | 14 +++--- .../test_registry_client_integration.py | 4 +- .../test_runnable_prompts_integration.py | 25 ++++++---- tests/integration/test_runtime_smoke.py | 12 +++-- .../integration/test_selective_install_mcp.py | 1 - tests/integration/test_skill_install.py | 2 +- tests/integration/test_skill_integration.py | 2 +- .../integration/test_version_notification.py | 3 +- .../test_virtual_package_orphan_detection.py | 2 + tests/manual_workflow_script.py | 6 +-- tests/noninteractive_workflow_test.py | 3 +- tests/test_apm_package_models.py | 24 ++++----- tests/test_apm_resolver.py | 8 ++- tests/test_codex_docker_args_fix.py | 7 +-- tests/test_codex_empty_string_and_defaults.py | 7 +-- tests/test_collision_integration.py | 5 +- tests/test_console.py | 3 +- tests/test_distributed_compilation.py | 14 ++++-- tests/test_empty_string_and_defaults.py | 7 +-- tests/test_enhanced_discovery.py | 29 ++++++----- tests/test_github_downloader.py | 19 +++---- ...test_github_downloader_token_precedence.py | 3 +- tests/test_lockfile.py | 10 +++- tests/test_runnable_prompts.py | 11 +++-- .../test_runtime_manager_token_precedence.py | 3 +- tests/test_token_manager.py | 2 +- tests/test_virtual_package_multi_install.py | 10 ++-- .../unit/compilation/test_claude_formatter.py | 8 +-- tests/unit/compilation/test_compilation.py | 36 ++++++++------ .../compilation/test_compile_target_flag.py | 4 +- .../compilation/test_constitution_injector.py | 10 ++-- .../compilation/test_context_optimizer.py | 9 ++-- .../compilation/test_coverage_guarantees.py | 3 +- tests/unit/compilation/test_link_resolver.py | 15 ++---- .../test_mathematical_guarantees.py | 3 +- .../test_mathematical_optimization.py | 5 +- .../test_sibling_directory_coverage.py | 3 +- tests/unit/core/test_target_detection.py | 8 +-- .../unit/integration/test_agent_integrator.py | 9 +++- .../integration/test_command_integrator.py | 6 +-- .../test_deployed_files_manifest.py | 11 +++-- .../unit/integration/test_hook_integrator.py | 4 +- .../test_instruction_integrator.py | 16 ++++-- .../integration/test_prompt_integrator.py | 14 ++++-- .../unit/integration/test_skill_integrator.py | 25 ++++++++-- .../integration/test_skill_transformer.py | 2 +- ...test_sync_integration_url_normalization.py | 2 +- tests/unit/policy/test_ci_checks.py | 21 ++++---- tests/unit/policy/test_inheritance.py | 16 +++--- tests/unit/policy/test_policy_checks.py | 5 +- tests/unit/test_ado_path_structure.py | 20 +++++--- tests/unit/test_audit_ci_command.py | 1 - tests/unit/test_audit_command.py | 8 ++- tests/unit/test_audit_policy_command.py | 1 - tests/unit/test_auth.py | 3 +- tests/unit/test_auth_scoping.py | 5 +- tests/unit/test_build_sha.py | 2 +- tests/unit/test_canonicalization.py | 6 +-- tests/unit/test_cli_encoding.py | 2 +- tests/unit/test_codex_runtime.py | 4 +- tests/unit/test_command_logger.py | 1 + tests/unit/test_compile_rich_output.py | 5 +- tests/unit/test_conflict_detection.py | 3 +- tests/unit/test_content_hash.py | 3 +- tests/unit/test_copilot_runtime.py | 4 +- tests/unit/test_dev_dependencies.py | 1 - tests/unit/test_docker_args.py | 1 + tests/unit/test_docker_args_and_installer.py | 5 +- tests/unit/test_github_host.py | 2 +- tests/unit/test_init_command.py | 9 ++-- tests/unit/test_init_plugin.py | 1 - tests/unit/test_install_command.py | 12 ++--- tests/unit/test_install_output.py | 5 +- tests/unit/test_install_scanning.py | 10 +++- tests/unit/test_install_update.py | 7 ++- tests/unit/test_list_command.py | 3 +- tests/unit/test_llm_runtime.py | 4 +- tests/unit/test_local_deps.py | 8 +-- tests/unit/test_lockfile_enrichment.py | 2 +- tests/unit/test_mcp_client_factory.py | 10 ++-- tests/unit/test_mcp_lifecycle_e2e.py | 3 +- tests/unit/test_mcp_overlays.py | 5 +- tests/unit/test_package_identity.py | 3 +- tests/unit/test_package_manager.py | 3 +- tests/unit/test_packer.py | 4 +- tests/unit/test_path_security.py | 3 +- tests/unit/test_plugin_exporter.py | 3 +- tests/unit/test_python_paths.py | 1 + tests/unit/test_registry_client.py | 4 +- tests/unit/test_registry_integration.py | 2 + tests/unit/test_runtime_args.py | 1 + tests/unit/test_runtime_detection.py | 2 +- tests/unit/test_runtime_factory.py | 4 +- tests/unit/test_runtime_windows.py | 6 ++- tests/unit/test_safe_installer.py | 3 +- tests/unit/test_script_runner.py | 11 +++-- tests/unit/test_security_gate.py | 1 - tests/unit/test_ssl_cert_hook.py | 3 +- tests/unit/test_transitive_deps.py | 2 +- tests/unit/test_uninstall_reintegration.py | 15 +++--- .../unit/test_uninstall_transitive_cleanup.py | 4 +- tests/unit/test_unpack_security.py | 4 +- tests/unit/test_unpacker.py | 18 +++++-- tests/unit/test_version_checker.py | 12 ++--- tests/utils/constitution_fixtures.py | 4 +- 222 files changed, 893 insertions(+), 660 deletions(-) diff --git a/scripts/benchmark_manifest_ops.py b/scripts/benchmark_manifest_ops.py index a7556be3..95b21cf4 100644 --- a/scripts/benchmark_manifest_ops.py +++ b/scripts/benchmark_manifest_ops.py @@ -12,10 +12,10 @@ """ import os +import shutil +import sys import tempfile import time -import sys -import shutil from pathlib import Path # --------------------------------------------------------------------------- diff --git a/src/apm_cli/adapters/client/codex.py b/src/apm_cli/adapters/client/codex.py index eda11493..3d47520c 100644 --- a/src/apm_cli/adapters/client/codex.py +++ b/src/apm_cli/adapters/client/codex.py @@ -6,11 +6,13 @@ """ import os -import toml from pathlib import Path -from .base import MCPClientAdapter + +import toml + from ...registry.client import SimpleRegistryClient from ...registry.integration import RegistryIntegration +from .base import MCPClientAdapter class CodexClientAdapter(MCPClientAdapter): @@ -331,6 +333,7 @@ def _process_environment_variables(self, env_vars, env_overrides=None): """ import os import sys + from rich.prompt import Prompt resolved = {} diff --git a/src/apm_cli/adapters/client/copilot.py b/src/apm_cli/adapters/client/copilot.py index 734ace6c..44916af1 100644 --- a/src/apm_cli/adapters/client/copilot.py +++ b/src/apm_cli/adapters/client/copilot.py @@ -8,12 +8,13 @@ import json import os from pathlib import Path -from .base import MCPClientAdapter -from ...registry.client import SimpleRegistryClient -from ...registry.integration import RegistryIntegration + from ...core.docker_args import DockerArgsProcessor from ...core.token_manager import GitHubTokenManager +from ...registry.client import SimpleRegistryClient +from ...registry.integration import RegistryIntegration from ...utils.github_host import is_github_hostname +from .base import MCPClientAdapter class CopilotClientAdapter(MCPClientAdapter): @@ -327,6 +328,7 @@ def _resolve_environment_variables(self, env_vars, env_overrides=None): """ import os import sys + from rich.prompt import Prompt resolved = {} @@ -406,6 +408,7 @@ def _resolve_env_variable(self, name, value, env_overrides=None): import os import re import sys + from rich.prompt import Prompt env_overrides = env_overrides or {} @@ -684,7 +687,7 @@ def _is_github_server(self, server_name, url): bool: True if this is a legitimate GitHub MCP server, False otherwise. """ from urllib.parse import urlparse - + # Check server name against an allowlist of known GitHub MCP servers github_server_names = [ "github-mcp-server", diff --git a/src/apm_cli/adapters/client/vscode.py b/src/apm_cli/adapters/client/vscode.py index 4dd17042..33063a9d 100644 --- a/src/apm_cli/adapters/client/vscode.py +++ b/src/apm_cli/adapters/client/vscode.py @@ -8,9 +8,10 @@ import json import os from pathlib import Path -from .base import MCPClientAdapter, _INPUT_VAR_RE + from ...registry.client import SimpleRegistryClient from ...registry.integration import RegistryIntegration +from .base import _INPUT_VAR_RE, MCPClientAdapter class VSCodeClientAdapter(MCPClientAdapter): diff --git a/src/apm_cli/adapters/package_manager/default_manager.py b/src/apm_cli/adapters/package_manager/default_manager.py index 192da5dd..3da7fd6c 100644 --- a/src/apm_cli/adapters/package_manager/default_manager.py +++ b/src/apm_cli/adapters/package_manager/default_manager.py @@ -1,8 +1,8 @@ """Implementation of the default MCP package manager.""" -from .base import MCPPackageManagerAdapter from ...config import get_default_client from ...registry.integration import RegistryIntegration +from .base import MCPPackageManagerAdapter class DefaultMCPPackageManager(MCPPackageManagerAdapter): @@ -81,7 +81,7 @@ def list_installed(self): try: # Import here to avoid circular import from ...factory import ClientFactory - + # Get client type from configuration (default is vscode) client_type = get_default_client() diff --git a/src/apm_cli/bundle/__init__.py b/src/apm_cli/bundle/__init__.py index 8cba3e1f..df3cc6b1 100644 --- a/src/apm_cli/bundle/__init__.py +++ b/src/apm_cli/bundle/__init__.py @@ -1,8 +1,8 @@ """Bundle creation and consumption for APM packages.""" -from .packer import pack_bundle, PackResult +from .packer import PackResult, pack_bundle from .plugin_exporter import export_plugin_bundle -from .unpacker import unpack_bundle, UnpackResult +from .unpacker import UnpackResult, unpack_bundle __all__ = [ "pack_bundle", diff --git a/src/apm_cli/bundle/lockfile_enrichment.py b/src/apm_cli/bundle/lockfile_enrichment.py index 34febfe4..fc3f691e 100644 --- a/src/apm_cli/bundle/lockfile_enrichment.py +++ b/src/apm_cli/bundle/lockfile_enrichment.py @@ -5,7 +5,6 @@ from ..deps.lockfile import LockFile - # Authoritative mapping of target names to deployed-file path prefixes. _TARGET_PREFIXES = { "copilot": [".github/"], diff --git a/src/apm_cli/bundle/packer.py b/src/apm_cli/bundle/packer.py index 5fc9c650..d10d964e 100644 --- a/src/apm_cli/bundle/packer.py +++ b/src/apm_cli/bundle/packer.py @@ -7,10 +7,10 @@ from pathlib import Path from typing import Dict, List, Optional +from ..core.target_detection import detect_target from ..deps.lockfile import LockFile, get_lockfile_path, migrate_lockfile_if_needed from ..models.apm_package import APMPackage -from ..core.target_detection import detect_target -from .lockfile_enrichment import enrich_lockfile_for_pack, _filter_files_by_target +from .lockfile_enrichment import _filter_files_by_target, enrich_lockfile_for_pack @dataclass diff --git a/src/apm_cli/bundle/plugin_exporter.py b/src/apm_cli/bundle/plugin_exporter.py index 096840ff..1f6ffb38 100644 --- a/src/apm_cli/bundle/plugin_exporter.py +++ b/src/apm_cli/bundle/plugin_exporter.py @@ -7,6 +7,7 @@ import json import os +import re import shutil import tarfile from pathlib import Path @@ -14,11 +15,9 @@ import yaml -import re - from ..deps.lockfile import ( - LockFile, LockedDependency, + LockFile, get_lockfile_path, migrate_lockfile_if_needed, ) diff --git a/src/apm_cli/bundle/unpacker.py b/src/apm_cli/bundle/unpacker.py index 908f4ddd..49be9e9d 100644 --- a/src/apm_cli/bundle/unpacker.py +++ b/src/apm_cli/bundle/unpacker.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import Dict, List -from ..deps.lockfile import LockFile, LOCKFILE_NAME, LEGACY_LOCKFILE_NAME +from ..deps.lockfile import LEGACY_LOCKFILE_NAME, LOCKFILE_NAME, LockFile @dataclass diff --git a/src/apm_cli/commands/_helpers.py b/src/apm_cli/commands/_helpers.py index 284256e7..4da09081 100644 --- a/src/apm_cli/commands/_helpers.py +++ b/src/apm_cli/commands/_helpers.py @@ -21,8 +21,8 @@ GITIGNORE_FILENAME, ) from ..utils.console import _rich_echo, _rich_info, _rich_warning -from ..version import get_build_sha, get_version from ..utils.version_checker import check_for_updates +from ..version import get_build_sha, get_version # CRITICAL: Shadow Click commands at module level to prevent namespace collision # When Click commands like 'config set' are defined, calling set() can invoke the command @@ -192,8 +192,8 @@ def _check_orphaned_packages(): return [] try: - from ..models.apm_package import APMPackage from ..deps.lockfile import LockFile, get_lockfile_path + from ..models.apm_package import APMPackage apm_package = APMPackage.from_apm_yml(Path(APM_YML_FILENAME)) declared_deps = apm_package.get_apm_dependencies() diff --git a/src/apm_cli/commands/audit.py b/src/apm_cli/commands/audit.py index 4554bfa8..6672cbfa 100644 --- a/src/apm_cli/commands/audit.py +++ b/src/apm_cli/commands/audit.py @@ -17,20 +17,19 @@ import click +from ..core.command_logger import CommandLogger from ..deps.lockfile import LockFile, get_lockfile_path from ..security.content_scanner import ContentScanner, ScanFinding from ..security.file_scanner import scan_lockfile_packages -from ..core.command_logger import CommandLogger from ..utils.console import ( + STATUS_SYMBOLS, _get_console, _rich_echo, _rich_error, _rich_success, _rich_warning, - STATUS_SYMBOLS, ) - # -- Helpers -------------------------------------------------------- @@ -89,6 +88,7 @@ def _render_findings_table( if console: try: from rich.table import Table + from ..security.audit_report import relative_path_for_report table = Table( diff --git a/src/apm_cli/commands/compile/__init__.py b/src/apm_cli/commands/compile/__init__.py index 2e92dc72..93c9b956 100644 --- a/src/apm_cli/commands/compile/__init__.py +++ b/src/apm_cli/commands/compile/__init__.py @@ -1,6 +1,6 @@ """APM compile command.""" -from .cli import compile, _display_validation_errors, _get_validation_suggestion +from .cli import _display_validation_errors, _get_validation_suggestion, compile from .watcher import _watch_mode __all__ = [ diff --git a/src/apm_cli/commands/compile/cli.py b/src/apm_cli/commands/compile/cli.py index e29f5a17..ab3b937d 100644 --- a/src/apm_cli/commands/compile/cli.py +++ b/src/apm_cli/commands/compile/cli.py @@ -5,8 +5,8 @@ import click -from ...constants import AGENTS_MD_FILENAME, APM_DIR, APM_MODULES_DIR, APM_YML_FILENAME from ...compilation import AgentsCompiler, CompilationConfig +from ...constants import AGENTS_MD_FILENAME, APM_DIR, APM_MODULES_DIR, APM_YML_FILENAME from ...core.command_logger import CommandLogger from ...primitives.discovery import discover_primitives from ...utils.console import ( @@ -35,6 +35,7 @@ def _display_single_file_summary(stats, c_status, c_hash, output_path, dry_run): return import os + from rich.table import Table table = Table( diff --git a/src/apm_cli/commands/compile/watcher.py b/src/apm_cli/commands/compile/watcher.py index 3f5ef3ea..3e02a387 100644 --- a/src/apm_cli/commands/compile/watcher.py +++ b/src/apm_cli/commands/compile/watcher.py @@ -2,8 +2,8 @@ import time -from ...constants import AGENTS_MD_FILENAME, APM_DIR, APM_YML_FILENAME from ...compilation import AgentsCompiler, CompilationConfig +from ...constants import AGENTS_MD_FILENAME, APM_DIR, APM_YML_FILENAME from ...core.command_logger import CommandLogger diff --git a/src/apm_cli/commands/config.py b/src/apm_cli/commands/config.py index 7794ce20..678d2950 100644 --- a/src/apm_cli/commands/config.py +++ b/src/apm_cli/commands/config.py @@ -147,7 +147,7 @@ def get(key): apm config get auto-integrate apm config get """ - from ..config import get_config, get_auto_integrate + from ..config import get_auto_integrate, get_config logger = CommandLogger("config get") if key: diff --git a/src/apm_cli/commands/deps/__init__.py b/src/apm_cli/commands/deps/__init__.py index e341d327..df84063a 100644 --- a/src/apm_cli/commands/deps/__init__.py +++ b/src/apm_cli/commands/deps/__init__.py @@ -1,17 +1,17 @@ """APM dependency management commands.""" -from .cli import deps, list_packages, tree, clean, update, info from ._utils import ( - _is_nested_under_package, - _count_primitives, _count_package_files, + _count_primitives, _count_workflows, _get_detailed_context_counts, - _get_package_display_info, _get_detailed_package_info, - _update_single_package, + _get_package_display_info, + _is_nested_under_package, _update_all_packages, + _update_single_package, ) +from .cli import clean, deps, info, list_packages, tree, update __all__ = [ # CLI commands diff --git a/src/apm_cli/commands/deps/_utils.py b/src/apm_cli/commands/deps/_utils.py index 16383cec..7e6f96f5 100644 --- a/src/apm_cli/commands/deps/_utils.py +++ b/src/apm_cli/commands/deps/_utils.py @@ -4,8 +4,8 @@ from typing import Any, Dict, List, Optional from ...constants import APM_DIR, APM_MODULES_DIR, APM_YML_FILENAME, SKILL_MD_FILENAME -from ...models.apm_package import APMPackage from ...deps.github_downloader import GitHubPackageDownloader +from ...models.apm_package import APMPackage def _scan_installed_packages(apm_modules_dir: Path) -> list: diff --git a/src/apm_cli/commands/deps/cli.py b/src/apm_cli/commands/deps/cli.py index ac115ba4..611da150 100644 --- a/src/apm_cli/commands/deps/cli.py +++ b/src/apm_cli/commands/deps/cli.py @@ -1,30 +1,30 @@ """APM dependency management CLI commands.""" -import sys import shutil -import click +import sys from pathlib import Path -from typing import List, Optional, Dict, Any +from typing import Any, Dict, List, Optional + +import click # Import existing APM components from ...constants import APM_DIR, APM_MODULES_DIR, APM_YML_FILENAME, SKILL_MD_FILENAME -from ...models.apm_package import APMPackage, ValidationResult, validate_apm_package from ...core.command_logger import CommandLogger +from ...deps.apm_resolver import APMDependencyResolver # Import APM dependency system components (with fallback) from ...deps.github_downloader import GitHubPackageDownloader -from ...deps.apm_resolver import APMDependencyResolver - +from ...models.apm_package import APMPackage, ValidationResult, validate_apm_package from ._utils import ( - _is_nested_under_package, - _count_primitives, _count_package_files, + _count_primitives, _count_workflows, _get_detailed_context_counts, - _get_package_display_info, _get_detailed_package_info, - _update_single_package, + _get_package_display_info, + _is_nested_under_package, _update_all_packages, + _update_single_package, ) @@ -41,9 +41,10 @@ def list_packages(): try: # Import Rich components with fallback - from rich.table import Table - from rich.console import Console import shutil + + from rich.console import Console + from rich.table import Table term_width = shutil.get_terminal_size((120, 24)).columns console = Console(width=max(120, term_width)) has_rich = True @@ -241,8 +242,8 @@ def tree(): try: # Import Rich components with fallback - from rich.tree import Tree from rich.console import Console + from rich.tree import Tree console = Console() has_rich = True except ImportError: @@ -533,8 +534,8 @@ def info(package: str): # Display with Rich panel if available try: - from rich.panel import Panel from rich.console import Console + from rich.panel import Panel from rich.text import Text console = Console() diff --git a/src/apm_cli/commands/install.py b/src/apm_cli/commands/install.py index c145a4b3..b49a4513 100644 --- a/src/apm_cli/commands/install.py +++ b/src/apm_cli/commands/install.py @@ -11,14 +11,14 @@ APM_LOCK_FILENAME, APM_MODULES_DIR, APM_YML_FILENAME, - GITHUB_DIR, CLAUDE_DIR, + GITHUB_DIR, SKILL_MD_FILENAME, InstallMode, ) +from ..core.command_logger import InstallLogger, _ValidationOutcome from ..drift import build_download_ref, detect_orphans, detect_ref_change from ..models.results import InstallResult -from ..core.command_logger import InstallLogger, _ValidationOutcome from ..utils.console import _rich_echo, _rich_error, _rich_info, _rich_success from ..utils.diagnostics import DiagnosticCollector from ..utils.github_host import default_host, is_valid_fqdn @@ -263,6 +263,7 @@ def _validate_package_exists(package, verbose=False): import os import subprocess import tempfile + from apm_cli.core.auth import AuthResolver verbose_log = (lambda msg: _rich_echo(f" {msg}", color="dim")) if verbose else None @@ -270,8 +271,8 @@ def _validate_package_exists(package, verbose=False): try: # Parse the package to check if it's a virtual package or ADO - from apm_cli.models.apm_package import DependencyReference from apm_cli.deps.github_downloader import GitHubPackageDownloader + from apm_cli.models.apm_package import DependencyReference dep_ref = DependencyReference.parse(package) @@ -314,7 +315,10 @@ def _validate_package_exists(package, verbose=False): # For Azure DevOps or GitHub Enterprise (non-github.com hosts), # use the downloader which handles authentication properly if dep_ref.is_azure_devops() or (dep_ref.host and dep_ref.host != "github.com"): - from apm_cli.utils.github_host import is_github_hostname, is_azure_devops_hostname + from apm_cli.utils.github_host import ( + is_azure_devops_hostname, + is_github_hostname, + ) downloader = GitHubPackageDownloader() # Set the host @@ -374,8 +378,8 @@ def _validate_package_exists(package, verbose=False): def _check_repo(token, git_env): """Check repo accessibility via GitHub API (or git ls-remote for non-GitHub).""" - import urllib.request import urllib.error + import urllib.request api_base = host_info.api_base api_url = f"{api_base}/repos/{dep_ref.repo_url}" @@ -428,8 +432,8 @@ def _check_repo(token, git_env): repo_path = package # owner/repo format def _check_repo_fallback(token, git_env): - import urllib.request import urllib.error + import urllib.request host_info = auth_resolver.classify_host(host) api_url = f"{host_info.api_base}/repos/{repo_path}" @@ -1301,10 +1305,10 @@ def _collect_descendants(node, visited=None): # Auto-detect target for integration (same logic as compile) from apm_cli.core.target_detection import ( detect_target, - should_integrate_vscode, + get_target_description, should_integrate_claude, should_integrate_opencode, - get_target_description, + should_integrate_vscode, ) # Get config target from apm.yml if available @@ -1340,10 +1344,13 @@ def _collect_descendants(node, visited=None): # Initialize integrators prompt_integrator = PromptIntegrator() agent_integrator = AgentIntegrator() - from apm_cli.integration.skill_integrator import SkillIntegrator, should_install_skill from apm_cli.integration.command_integrator import CommandIntegrator from apm_cli.integration.hook_integrator import HookIntegrator from apm_cli.integration.instruction_integrator import InstructionIntegrator + from apm_cli.integration.skill_integrator import ( + SkillIntegrator, + should_install_skill, + ) skill_integrator = SkillIntegrator() command_integrator = CommandIntegrator() @@ -1365,7 +1372,8 @@ def _collect_descendants(node, visited=None): total_links_resolved = 0 # Collect installed packages for lockfile generation - from apm_cli.deps.lockfile import LockFile, LockedDependency, get_lockfile_path + from apm_cli.deps.lockfile import LockedDependency, LockFile, get_lockfile_path + from ..utils.content_hash import compute_package_hash as _compute_hash installed_packages: List[tuple] = [] # List of (dep_ref, resolved_commit, depth, resolved_by, is_dev) package_deployed_files: builtins.dict = {} # dep_key → list of relative deployed paths @@ -1392,11 +1400,11 @@ def _collect_descendants(node, visited=None): # Install each dependency with Rich progress display from rich.progress import ( + BarColumn, Progress, SpinnerColumn, - TextColumn, - BarColumn, TaskProgressColumn, + TextColumn, ) # downloader already created above for transitive resolution @@ -1406,7 +1414,8 @@ def _collect_descendants(node, visited=None): # Phase 4 (#171): Parallel package downloads using ThreadPoolExecutor # Pre-download all non-cached packages in parallel for wall-clock speedup. # Results are stored and consumed by the sequential integration loop below. - from concurrent.futures import ThreadPoolExecutor, as_completed as _futures_completed + from concurrent.futures import ThreadPoolExecutor + from concurrent.futures import as_completed as _futures_completed _pre_download_results = {} # dep_key -> PackageInfo _need_download = [] @@ -1525,14 +1534,15 @@ def _collect_descendants(node, visited=None): logger.download_complete(dep_ref.local_path, ref_suffix="local") # Build minimal PackageInfo for integration + from datetime import datetime + from apm_cli.models.apm_package import ( APMPackage, + GitReferenceType, PackageInfo, PackageType, ResolvedReference, - GitReferenceType, ) - from datetime import datetime local_apm_yml = install_path / "apm.yml" if local_apm_yml.exists(): @@ -1568,7 +1578,9 @@ def _collect_descendants(node, visited=None): if pkg_type == PackageType.MARKETPLACE_PLUGIN: # Normalize: synthesize .apm/ from plugin.json so # integration can discover and deploy primitives - from apm_cli.deps.plugin_parser import normalize_plugin_directory + from apm_cli.deps.plugin_parser import ( + normalize_plugin_directory, + ) normalize_plugin_directory(install_path, plugin_json_path) # Record for lockfile @@ -1726,14 +1738,15 @@ def _collect_descendants(node, visited=None): # Integrate prompts for cached packages (zero-config behavior) try: # Create PackageInfo from cached package + from datetime import datetime + from apm_cli.models.apm_package import ( APMPackage, + GitReferenceType, PackageInfo, PackageType, ResolvedReference, - GitReferenceType, ) - from datetime import datetime # Load package from apm.yml in install path apm_yml_path = install_path / APM_YML_FILENAME diff --git a/src/apm_cli/commands/prune.py b/src/apm_cli/commands/prune.py index d5203282..ec9f71eb 100644 --- a/src/apm_cli/commands/prune.py +++ b/src/apm_cli/commands/prune.py @@ -8,12 +8,12 @@ from ..constants import APM_LOCK_FILENAME, APM_MODULES_DIR, APM_YML_FILENAME from ..core.command_logger import CommandLogger -from ..utils.path_security import PathTraversalError, safe_rmtree -from ._helpers import _build_expected_install_paths, _scan_installed_packages # APM Dependencies from ..deps.lockfile import LockFile, get_lockfile_path from ..models.apm_package import APMPackage +from ..utils.path_security import PathTraversalError, safe_rmtree +from ._helpers import _build_expected_install_paths, _scan_installed_packages @click.command(help="Remove APM packages not listed in apm.yml") diff --git a/src/apm_cli/commands/uninstall/__init__.py b/src/apm_cli/commands/uninstall/__init__.py index a4a8b370..a53ac59c 100644 --- a/src/apm_cli/commands/uninstall/__init__.py +++ b/src/apm_cli/commands/uninstall/__init__.py @@ -2,13 +2,13 @@ from .cli import uninstall from .engine import ( - _parse_dependency_entry, - _validate_uninstall_packages, + _cleanup_stale_mcp, + _cleanup_transitive_orphans, _dry_run_uninstall, + _parse_dependency_entry, _remove_packages_from_disk, - _cleanup_transitive_orphans, _sync_integrations_after_uninstall, - _cleanup_stale_mcp, + _validate_uninstall_packages, ) __all__ = [ diff --git a/src/apm_cli/commands/uninstall/cli.py b/src/apm_cli/commands/uninstall/cli.py index b85b5d01..cde60750 100644 --- a/src/apm_cli/commands/uninstall/cli.py +++ b/src/apm_cli/commands/uninstall/cli.py @@ -8,17 +8,15 @@ from ...constants import APM_MODULES_DIR, APM_YML_FILENAME from ...core.command_logger import CommandLogger - from ...models.apm_package import APMPackage - from .engine import ( - _parse_dependency_entry, - _validate_uninstall_packages, + _cleanup_stale_mcp, + _cleanup_transitive_orphans, _dry_run_uninstall, + _parse_dependency_entry, _remove_packages_from_disk, - _cleanup_transitive_orphans, _sync_integrations_after_uninstall, - _cleanup_stale_mcp, + _validate_uninstall_packages, ) @@ -54,7 +52,7 @@ def uninstall(ctx, packages, dry_run, verbose): logger.start(f"Uninstalling {len(packages)} package(s)...") # Read current apm.yml - from ...utils.yaml_io import load_yaml, dump_yaml + from ...utils.yaml_io import dump_yaml, load_yaml apm_yml_path = Path(APM_YML_FILENAME) try: diff --git a/src/apm_cli/commands/uninstall/engine.py b/src/apm_cli/commands/uninstall/engine.py index ed20129b..7472090f 100644 --- a/src/apm_cli/commands/uninstall/engine.py +++ b/src/apm_cli/commands/uninstall/engine.py @@ -5,12 +5,11 @@ from ...constants import APM_MODULES_DIR, APM_YML_FILENAME from ...core.command_logger import CommandLogger -from ...utils.path_security import PathTraversalError, safe_rmtree -from ...utils.paths import portable_relpath - from ...deps.lockfile import LockFile -from ...models.apm_package import APMPackage, DependencyReference from ...integration.mcp_integrator import MCPIntegrator +from ...models.apm_package import APMPackage, DependencyReference +from ...utils.path_security import PathTraversalError, safe_rmtree +from ...utils.paths import portable_relpath def _parse_dependency_entry(dep_entry): @@ -226,14 +225,14 @@ def _cleanup_transitive_orphans(lockfile, packages_to_remove, apm_modules_dir, a def _sync_integrations_after_uninstall(apm_package, project_root, all_deployed_files, logger): """Remove deployed files and re-integrate from remaining packages.""" - from ...integration.base_integrator import BaseIntegrator - from ...models.apm_package import PackageInfo, validate_apm_package - from ...integration.prompt_integrator import PromptIntegrator from ...integration.agent_integrator import AgentIntegrator - from ...integration.skill_integrator import SkillIntegrator + from ...integration.base_integrator import BaseIntegrator from ...integration.command_integrator import CommandIntegrator from ...integration.hook_integrator import HookIntegrator from ...integration.instruction_integrator import InstructionIntegrator + from ...integration.prompt_integrator import PromptIntegrator + from ...integration.skill_integrator import SkillIntegrator + from ...models.apm_package import PackageInfo, validate_apm_package sync_managed = all_deployed_files if all_deployed_files else None if sync_managed is not None: diff --git a/src/apm_cli/compilation/__init__.py b/src/apm_cli/compilation/__init__.py index f2f256c5..6c07edf6 100644 --- a/src/apm_cli/compilation/__init__.py +++ b/src/apm_cli/compilation/__init__.py @@ -1,14 +1,16 @@ """APM compilation module for generating AGENTS.md files.""" -from .agents_compiler import AgentsCompiler, compile_agents_md, CompilationConfig, CompilationResult +from .agents_compiler import ( + AgentsCompiler, + CompilationConfig, + CompilationResult, + compile_agents_md, +) +from .link_resolver import resolve_markdown_links, validate_link_targets from .template_builder import ( - build_conditional_sections, TemplateData, - find_chatmode_by_name -) -from .link_resolver import ( - resolve_markdown_links, - validate_link_targets + build_conditional_sections, + find_chatmode_by_name, ) __all__ = [ diff --git a/src/apm_cli/compilation/agents_compiler.py b/src/apm_cli/compilation/agents_compiler.py index a1619648..4083c8f8 100644 --- a/src/apm_cli/compilation/agents_compiler.py +++ b/src/apm_cli/compilation/agents_compiler.py @@ -7,19 +7,20 @@ from dataclasses import dataclass from pathlib import Path -from typing import List, Optional, Dict, Any -from ..primitives.models import PrimitiveCollection +from typing import Any, Dict, List, Optional + from ..primitives.discovery import discover_primitives +from ..primitives.models import PrimitiveCollection +from ..utils.paths import portable_relpath from ..version import get_version from .claude_formatter import ClaudeFormatter +from .link_resolver import resolve_markdown_links, validate_link_targets from .template_builder import ( + TemplateData, build_conditional_sections, + find_chatmode_by_name, generate_agents_md_template, - TemplateData, - find_chatmode_by_name ) -from .link_resolver import resolve_markdown_links, validate_link_targets -from ..utils.paths import portable_relpath @dataclass @@ -190,7 +191,9 @@ def compile(self, config: CompilationConfig, primitives: Optional[PrimitiveColle primitives = discover_primitives(str(self.base_dir)) else: # Use enhanced discovery with dependencies (Task 4 integration) - from ..primitives.discovery import discover_primitives_with_dependencies + from ..primitives.discovery import ( + discover_primitives_with_dependencies, + ) primitives = discover_primitives_with_dependencies(str(self.base_dir)) # Route to targets based on config.target @@ -246,7 +249,7 @@ def _compile_distributed(self, config: CompilationConfig, primitives: PrimitiveC CompilationResult: Result of distributed compilation. """ from .distributed_compiler import DistributedAgentsCompiler - + # Create distributed compiler with exclude patterns distributed_compiler = DistributedAgentsCompiler( str(self.base_dir), diff --git a/src/apm_cli/compilation/claude_formatter.py b/src/apm_cli/compilation/claude_formatter.py index f06e5ef3..ce3ee871 100644 --- a/src/apm_cli/compilation/claude_formatter.py +++ b/src/apm_cli/compilation/claude_formatter.py @@ -16,9 +16,9 @@ import frontmatter -from ..primitives.models import Instruction, PrimitiveCollection, Chatmode -from ..version import get_version +from ..primitives.models import Chatmode, Instruction, PrimitiveCollection from ..utils.paths import portable_relpath +from ..version import get_version from .constants import BUILD_ID_PLACEHOLDER from .constitution import read_constitution diff --git a/src/apm_cli/compilation/constitution_block.py b/src/apm_cli/compilation/constitution_block.py index 93885141..676e70ff 100644 --- a/src/apm_cli/compilation/constitution_block.py +++ b/src/apm_cli/compilation/constitution_block.py @@ -12,7 +12,6 @@ CONSTITUTION_RELATIVE_PATH, ) - HASH_PREFIX = "hash:" diff --git a/src/apm_cli/compilation/context_optimizer.py b/src/apm_cli/compilation/context_optimizer.py index 4fc4181d..d338b044 100644 --- a/src/apm_cli/compilation/context_optimizer.py +++ b/src/apm_cli/compilation/context_optimizer.py @@ -7,20 +7,24 @@ import builtins import fnmatch +import glob import os import time from collections import defaultdict from dataclasses import dataclass, field +from functools import lru_cache from pathlib import Path from typing import Any, Dict, List, Optional, Set, Tuple -from functools import lru_cache -import glob -from ..primitives.models import Instruction from ..output.models import ( - CompilationResults, ProjectAnalysis, OptimizationDecision, OptimizationStats, - PlacementStrategy, PlacementSummary + CompilationResults, + OptimizationDecision, + OptimizationStats, + PlacementStrategy, + PlacementSummary, + ProjectAnalysis, ) +from ..primitives.models import Instruction from ..utils.paths import portable_relpath # CRITICAL: Shadow Click commands to prevent namespace collision @@ -763,7 +767,7 @@ def _expand_glob_pattern(self, pattern: str) -> List[str]: or ['**/*.test.ts', '**/*.test.js', '**/*.spec.ts', '**/*.spec.js'] """ import re - + # Handle brace expansion like {css,scss} brace_match = re.search(r'\{([^}]+)\}', pattern) if brace_match: diff --git a/src/apm_cli/compilation/distributed_compiler.py b/src/apm_cli/compilation/distributed_compiler.py index 48c3214b..9a40d7a2 100644 --- a/src/apm_cli/compilation/distributed_compiler.py +++ b/src/apm_cli/compilation/distributed_compiler.py @@ -7,20 +7,20 @@ import builtins import os +from collections import defaultdict from dataclasses import dataclass, field from pathlib import Path from typing import Dict, List, Optional, Set, Tuple -from collections import defaultdict +from ..output.formatters import CompilationFormatter +from ..output.models import CompilationResults from ..primitives.models import Instruction, PrimitiveCollection +from ..utils.paths import portable_relpath from ..version import get_version -from .template_builder import TemplateData, find_chatmode_by_name from .constants import BUILD_ID_PLACEHOLDER from .context_optimizer import ContextOptimizer from .link_resolver import UnifiedLinkResolver -from ..output.formatters import CompilationFormatter -from ..output.models import CompilationResults -from ..utils.paths import portable_relpath +from .template_builder import TemplateData, find_chatmode_by_name # CRITICAL: Shadow Click commands to prevent namespace collision set = builtins.set diff --git a/src/apm_cli/compilation/injector.py b/src/apm_cli/compilation/injector.py index 58debb26..2802aa5c 100644 --- a/src/apm_cli/compilation/injector.py +++ b/src/apm_cli/compilation/injector.py @@ -2,11 +2,11 @@ from __future__ import annotations from pathlib import Path -from typing import Optional, Literal +from typing import Literal, Optional -from .constitution import read_constitution -from .constitution_block import render_block, find_existing_block from .constants import CONSTITUTION_MARKER_BEGIN, CONSTITUTION_MARKER_END +from .constitution import read_constitution +from .constitution_block import find_existing_block, render_block InjectionStatus = Literal["CREATED", "UPDATED", "UNCHANGED", "SKIPPED", "MISSING"] diff --git a/src/apm_cli/compilation/link_resolver.py b/src/apm_cli/compilation/link_resolver.py index 30f58b8d..d6d3a80c 100644 --- a/src/apm_cli/compilation/link_resolver.py +++ b/src/apm_cli/compilation/link_resolver.py @@ -13,7 +13,7 @@ import re from dataclasses import dataclass from pathlib import Path -from typing import List, Dict, Optional, Set +from typing import Dict, List, Optional, Set from urllib.parse import urlparse # CRITICAL: Shadow Click commands to prevent namespace collision diff --git a/src/apm_cli/compilation/template_builder.py b/src/apm_cli/compilation/template_builder.py index e9b945d2..d25ec316 100644 --- a/src/apm_cli/compilation/template_builder.py +++ b/src/apm_cli/compilation/template_builder.py @@ -3,8 +3,9 @@ import re from dataclasses import dataclass from pathlib import Path -from typing import List, Dict, Optional, Tuple -from ..primitives.models import Instruction, Chatmode +from typing import Dict, List, Optional, Tuple + +from ..primitives.models import Chatmode, Instruction from ..utils.paths import portable_relpath diff --git a/src/apm_cli/constants.py b/src/apm_cli/constants.py index 12d3372a..5187b318 100644 --- a/src/apm_cli/constants.py +++ b/src/apm_cli/constants.py @@ -2,7 +2,6 @@ from enum import Enum - # --------------------------------------------------------------------------- # Enums # --------------------------------------------------------------------------- diff --git a/src/apm_cli/core/conflict_detector.py b/src/apm_cli/core/conflict_detector.py index d0865fd5..b4e44062 100644 --- a/src/apm_cli/core/conflict_detector.py +++ b/src/apm_cli/core/conflict_detector.py @@ -1,6 +1,7 @@ """MCP server conflict detection and resolution.""" -from typing import Dict, Any +from typing import Any, Dict + from ..adapters.client.base import MCPClientAdapter diff --git a/src/apm_cli/core/docker_args.py b/src/apm_cli/core/docker_args.py index ab7ccd91..98f9369a 100644 --- a/src/apm_cli/core/docker_args.py +++ b/src/apm_cli/core/docker_args.py @@ -1,6 +1,6 @@ """Docker arguments processing utilities for MCP configuration.""" -from typing import List, Dict, Tuple +from typing import Dict, List, Tuple class DockerArgsProcessor: diff --git a/src/apm_cli/core/safe_installer.py b/src/apm_cli/core/safe_installer.py index 6ce93af4..9a5846e4 100644 --- a/src/apm_cli/core/safe_installer.py +++ b/src/apm_cli/core/safe_installer.py @@ -1,10 +1,11 @@ """Safe MCP server installation with conflict detection.""" -from typing import List, Dict, Any, Optional from dataclasses import dataclass +from typing import Any, Dict, List, Optional + from ..factory import ClientFactory +from ..utils.console import _rich_error, _rich_success, _rich_warning from .conflict_detector import MCPConflictDetector -from ..utils.console import _rich_warning, _rich_success, _rich_error @dataclass diff --git a/src/apm_cli/core/script_runner.py b/src/apm_cli/core/script_runner.py index 6c79ae2b..e6ee0587 100644 --- a/src/apm_cli/core/script_runner.py +++ b/src/apm_cli/core/script_runner.py @@ -6,12 +6,13 @@ import subprocess import sys import time -import yaml from pathlib import Path from typing import Dict, Optional -from .token_manager import setup_runtime_environment +import yaml + from ..output.script_formatters import ScriptExecutionFormatter +from .token_manager import setup_runtime_environment class ScriptRunner: @@ -742,8 +743,8 @@ def _auto_install_virtual_package(self, package_ref: str) -> bool: True if installation succeeded, False otherwise """ try: - from ..models.apm_package import DependencyReference from ..deps.github_downloader import GitHubPackageDownloader + from ..models.apm_package import DependencyReference # Parse the reference as-is -- no extension guessing dep_ref = DependencyReference.parse(package_ref) @@ -808,7 +809,7 @@ def _add_dependency_to_config(self, package_ref: str) -> None: return # Load current config - from ..utils.yaml_io import load_yaml, dump_yaml + from ..utils.yaml_io import dump_yaml, load_yaml config = load_yaml(config_path) or {} # Ensure dependencies.apm section exists diff --git a/src/apm_cli/deps/__init__.py b/src/apm_cli/deps/__init__.py index d22856e7..29123789 100644 --- a/src/apm_cli/deps/__init__.py +++ b/src/apm_cli/deps/__init__.py @@ -1,15 +1,19 @@ """Dependencies management package for APM.""" +from .aggregator import scan_workflows_for_dependencies, sync_workflow_dependencies from .apm_resolver import APMDependencyResolver from .dependency_graph import ( - DependencyGraph, DependencyTree, DependencyNode, FlatDependencyMap, - CircularRef, ConflictInfo + CircularRef, + ConflictInfo, + DependencyGraph, + DependencyNode, + DependencyTree, + FlatDependencyMap, ) -from .aggregator import sync_workflow_dependencies, scan_workflows_for_dependencies -from .verifier import verify_dependencies, install_missing_dependencies, load_apm_config from .github_downloader import GitHubPackageDownloader +from .lockfile import LockedDependency, LockFile, get_lockfile_path from .package_validator import PackageValidator -from .lockfile import LockFile, LockedDependency, get_lockfile_path +from .verifier import install_missing_dependencies, load_apm_config, verify_dependencies __all__ = [ 'sync_workflow_dependencies', diff --git a/src/apm_cli/deps/aggregator.py b/src/apm_cli/deps/aggregator.py index a6a6976d..631c8f21 100644 --- a/src/apm_cli/deps/aggregator.py +++ b/src/apm_cli/deps/aggregator.py @@ -1,10 +1,11 @@ """Workflow dependency aggregator for APM.""" -import os import glob +import os from pathlib import Path -import yaml + import frontmatter +import yaml def scan_workflows_for_dependencies(): diff --git a/src/apm_cli/deps/apm_resolver.py b/src/apm_cli/deps/apm_resolver.py index 69aa48fa..56855b08 100644 --- a/src/apm_cli/deps/apm_resolver.py +++ b/src/apm_cli/deps/apm_resolver.py @@ -1,15 +1,20 @@ """APM dependency resolution engine with recursive resolution and conflict detection.""" -from pathlib import Path -from typing import List, Set, Optional, Protocol, Tuple, runtime_checkable from collections import deque +from pathlib import Path +from typing import List, Optional, Protocol, Set, Tuple, runtime_checkable from ..models.apm_package import APMPackage, DependencyReference from .dependency_graph import ( - DependencyGraph, DependencyTree, DependencyNode, FlatDependencyMap, - CircularRef, ConflictInfo + CircularRef, + ConflictInfo, + DependencyGraph, + DependencyNode, + DependencyTree, + FlatDependencyMap, ) + # Type alias for the download callback. # Takes (dep_ref, apm_modules_dir, parent_chain) and returns the install path # if successful. ``parent_chain`` is a human-readable breadcrumb string like diff --git a/src/apm_cli/deps/collection_parser.py b/src/apm_cli/deps/collection_parser.py index 22080163..0ee4fccb 100644 --- a/src/apm_cli/deps/collection_parser.py +++ b/src/apm_cli/deps/collection_parser.py @@ -1,8 +1,9 @@ """Parser for APM collection manifest files (.collection.yml).""" -import yaml from dataclasses import dataclass -from typing import List, Optional, Dict, Any +from typing import Any, Dict, List, Optional + +import yaml @dataclass diff --git a/src/apm_cli/deps/dependency_graph.py b/src/apm_cli/deps/dependency_graph.py index cd5cfa7d..5eadce18 100644 --- a/src/apm_cli/deps/dependency_graph.py +++ b/src/apm_cli/deps/dependency_graph.py @@ -3,7 +3,8 @@ from collections import defaultdict from dataclasses import dataclass, field from pathlib import Path -from typing import Dict, List, Optional, Set, Tuple, Any +from typing import Any, Dict, List, Optional, Set, Tuple + from ..models.apm_package import APMPackage, DependencyReference diff --git a/src/apm_cli/deps/github_downloader.py b/src/apm_cli/deps/github_downloader.py index b69eaa11..0216c777 100644 --- a/src/apm_cli/deps/github_downloader.py +++ b/src/apm_cli/deps/github_downloader.py @@ -1,6 +1,8 @@ """GitHub package downloader for APM dependencies.""" import os +import random +import re import shutil import stat import sys @@ -8,38 +10,35 @@ import time from datetime import datetime from pathlib import Path -from typing import Optional, Dict, Any, Callable -import random -import re -from typing import Union -import requests +from typing import Any, Callable, Dict, Optional, Union import git -from git import Repo, RemoteProgress +import requests +from git import RemoteProgress, Repo from git.exc import GitCommandError, InvalidGitRepositoryError from ..core.auth import AuthResolver from ..models.apm_package import ( - DependencyReference, - PackageInfo, - ResolvedReference, + APMPackage, + DependencyReference, GitReferenceType, + PackageInfo, PackageType, + ResolvedReference, validate_apm_package, - APMPackage ) from ..utils.github_host import ( - build_https_clone_url, - build_ssh_url, + build_ado_api_url, build_ado_https_clone_url, build_ado_ssh_url, - build_ado_api_url, - build_raw_content_url, build_artifactory_archive_url, - sanitize_token_url_in_message, + build_https_clone_url, + build_raw_content_url, + build_ssh_url, default_host, is_azure_devops_hostname, - is_github_hostname + is_github_hostname, + sanitize_token_url_in_message, ) @@ -150,7 +149,7 @@ def update(self, op_code, cur_count, max_count=None, message=''): def _get_op_name(self, op_code): """Convert git operation code to human-readable name.""" from git import RemoteProgress - + # Extract operation type from op_code if op_code & RemoteProgress.COUNTING: return "Counting objects" @@ -469,7 +468,7 @@ def _sanitize_git_error(self, error_message: str) -> str: str: Sanitized error message with sensitive data removed """ import re - + # Remove any tokens that might appear in URLs for github hosts (format: https://token@host) # Sanitize for default host and common enterprise hosts via helper sanitized = sanitize_token_url_in_message(error_message, host=default_host()) @@ -861,7 +860,7 @@ def _download_ado_file(self, dep_ref: DependencyReference, file_path: str, ref: bytes: File content """ import base64 - + # Validate required ADO fields before proceeding if not all([dep_ref.ado_organization, dep_ref.ado_project, dep_ref.ado_repo]): raise ValueError( @@ -1678,7 +1677,7 @@ def download_subdirectory_package(self, dep_ref: DependencyReference, target_pat package.version = short_sha apm_yml_path = target_path / "apm.yml" if apm_yml_path.exists(): - from ..utils.yaml_io import load_yaml, dump_yaml + from ..utils.yaml_io import dump_yaml, load_yaml _data = load_yaml(apm_yml_path) or {} _data["version"] = short_sha dump_yaml(_data, apm_yml_path) @@ -1984,7 +1983,7 @@ def download_package( # Keep the synthesized apm.yml in sync apm_yml_path = target_path / "apm.yml" if apm_yml_path.exists(): - from ..utils.yaml_io import load_yaml, dump_yaml + from ..utils.yaml_io import dump_yaml, load_yaml _data = load_yaml(apm_yml_path) or {} _data["version"] = short_sha dump_yaml(_data, apm_yml_path) diff --git a/src/apm_cli/deps/package_validator.py b/src/apm_cli/deps/package_validator.py index b06b7fae..81747a31 100644 --- a/src/apm_cli/deps/package_validator.py +++ b/src/apm_cli/deps/package_validator.py @@ -1,14 +1,11 @@ """APM package structure validation.""" +import os from pathlib import Path from typing import List, Optional -import os -from ..models.apm_package import ( - ValidationResult, - APMPackage, - validate_apm_package as base_validate_apm_package -) +from ..models.apm_package import APMPackage, ValidationResult +from ..models.apm_package import validate_apm_package as base_validate_apm_package class PackageValidator: diff --git a/src/apm_cli/deps/plugin_parser.py b/src/apm_cli/deps/plugin_parser.py index b0e70bd8..e11ba19e 100644 --- a/src/apm_cli/deps/plugin_parser.py +++ b/src/apm_cli/deps/plugin_parser.py @@ -15,7 +15,8 @@ import logging import shutil from pathlib import Path -from typing import Dict, Any, List, Optional +from typing import Any, Dict, List, Optional + import yaml diff --git a/src/apm_cli/deps/verifier.py b/src/apm_cli/deps/verifier.py index c11e1866..e44596b8 100644 --- a/src/apm_cli/deps/verifier.py +++ b/src/apm_cli/deps/verifier.py @@ -2,8 +2,10 @@ import os from pathlib import Path + import yaml -from ..factory import PackageManagerFactory, ClientFactory + +from ..factory import ClientFactory, PackageManagerFactory def load_apm_config(config_file="apm.yml"): diff --git a/src/apm_cli/drift.py b/src/apm_cli/drift.py index 1e3a1574..6d39b6ec 100644 --- a/src/apm_cli/drift.py +++ b/src/apm_cli/drift.py @@ -49,7 +49,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set if TYPE_CHECKING: - from apm_cli.deps.lockfile import LockFile, LockedDependency + from apm_cli.deps.lockfile import LockedDependency, LockFile from apm_cli.models.apm_package import DependencyReference diff --git a/src/apm_cli/factory.py b/src/apm_cli/factory.py index 4f6295a8..e2a603bf 100644 --- a/src/apm_cli/factory.py +++ b/src/apm_cli/factory.py @@ -1,10 +1,10 @@ """Factory classes for creating adapters.""" -from .adapters.client.vscode import VSCodeClientAdapter from .adapters.client.codex import CodexClientAdapter from .adapters.client.copilot import CopilotClientAdapter from .adapters.client.cursor import CursorClientAdapter from .adapters.client.opencode import OpenCodeClientAdapter +from .adapters.client.vscode import VSCodeClientAdapter from .adapters.package_manager.default_manager import DefaultMCPPackageManager diff --git a/src/apm_cli/integration/__init__.py b/src/apm_cli/integration/__init__.py index 356f871d..6d9ab4d5 100644 --- a/src/apm_cli/integration/__init__.py +++ b/src/apm_cli/integration/__init__.py @@ -1,28 +1,28 @@ """APM package integration utilities.""" -from .base_integrator import BaseIntegrator, IntegrationResult -from .prompt_integrator import PromptIntegrator from .agent_integrator import AgentIntegrator +from .base_integrator import BaseIntegrator, IntegrationResult from .hook_integrator import HookIntegrator from .instruction_integrator import InstructionIntegrator +from .mcp_integrator import MCPIntegrator +from .prompt_integrator import PromptIntegrator from .skill_integrator import ( SkillIntegrator, - validate_skill_name, - normalize_skill_name, - to_hyphen_case, copy_skill_to_target, - should_install_skill, - should_compile_instructions, get_effective_type, + normalize_skill_name, + should_compile_instructions, + should_install_skill, + to_hyphen_case, + validate_skill_name, ) from .skill_transformer import SkillTransformer -from .mcp_integrator import MCPIntegrator from .targets import ( - TargetProfile, - PrimitiveMapping, KNOWN_TARGETS, - get_integration_prefixes, + PrimitiveMapping, + TargetProfile, active_targets, + get_integration_prefixes, ) __all__ = [ diff --git a/src/apm_cli/integration/agent_integrator.py b/src/apm_cli/integration/agent_integrator.py index fa6c86da..7d10cdc4 100644 --- a/src/apm_cli/integration/agent_integrator.py +++ b/src/apm_cli/integration/agent_integrator.py @@ -6,7 +6,7 @@ """ from pathlib import Path -from typing import List, Dict +from typing import Dict, List from apm_cli.integration.base_integrator import BaseIntegrator, IntegrationResult from apm_cli.utils.paths import portable_relpath diff --git a/src/apm_cli/integration/base_integrator.py b/src/apm_cli/integration/base_integrator.py index 47405060..6490bd09 100644 --- a/src/apm_cli/integration/base_integrator.py +++ b/src/apm_cli/integration/base_integrator.py @@ -1,11 +1,10 @@ """Base integrator with shared collision detection and sync logic.""" import re +from dataclasses import dataclass, field from pathlib import Path from typing import Dict, List, Optional, Set -from dataclasses import dataclass, field - from apm_cli.compilation.link_resolver import UnifiedLinkResolver from apm_cli.primitives.discovery import discover_primitives from apm_cli.utils.console import _rich_warning diff --git a/src/apm_cli/integration/command_integrator.py b/src/apm_cli/integration/command_integrator.py index 883f7ef5..7fde4e5b 100644 --- a/src/apm_cli/integration/command_integrator.py +++ b/src/apm_cli/integration/command_integrator.py @@ -5,7 +5,8 @@ """ from pathlib import Path -from typing import List, Dict +from typing import Dict, List + import frontmatter from apm_cli.integration.base_integrator import BaseIntegrator, IntegrationResult diff --git a/src/apm_cli/integration/hook_integrator.py b/src/apm_cli/integration/hook_integrator.py index 2d122c27..70102003 100644 --- a/src/apm_cli/integration/hook_integrator.py +++ b/src/apm_cli/integration/hook_integrator.py @@ -45,9 +45,9 @@ import json import re import shutil -from pathlib import Path -from typing import List, Dict, Tuple, Optional from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, List, Optional, Tuple from apm_cli.integration.base_integrator import BaseIntegrator from apm_cli.utils.paths import portable_relpath diff --git a/src/apm_cli/integration/prompt_integrator.py b/src/apm_cli/integration/prompt_integrator.py index 444831df..7b555114 100644 --- a/src/apm_cli/integration/prompt_integrator.py +++ b/src/apm_cli/integration/prompt_integrator.py @@ -1,7 +1,7 @@ """Prompt integration functionality for APM packages.""" from pathlib import Path -from typing import List, Dict +from typing import Dict, List from apm_cli.integration.base_integrator import BaseIntegrator, IntegrationResult from apm_cli.utils.paths import portable_relpath diff --git a/src/apm_cli/integration/skill_integrator.py b/src/apm_cli/integration/skill_integrator.py index f4cbb8d7..8ca6d106 100644 --- a/src/apm_cli/integration/skill_integrator.py +++ b/src/apm_cli/integration/skill_integrator.py @@ -1,13 +1,13 @@ """Skill integration functionality for APM packages (Claude Code & Cursor support).""" -from pathlib import Path -from typing import List, Dict -from dataclasses import dataclass -from datetime import datetime import filecmp import hashlib -import shutil import re +import shutil +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Dict, List import frontmatter @@ -171,7 +171,7 @@ def get_effective_type(package_info) -> "PackageContentType": PackageContentType: The effective type """ from apm_cli.models.apm_package import PackageContentType, PackageType - + # Check if package has SKILL.md (via package_type field) # PackageType.CLAUDE_SKILL = has SKILL.md only # PackageType.HYBRID = has both apm.yml AND SKILL.md diff --git a/src/apm_cli/models/__init__.py b/src/apm_cli/models/__init__.py index 05d9a2c0..d1e36afa 100644 --- a/src/apm_cli/models/__init__.py +++ b/src/apm_cli/models/__init__.py @@ -8,6 +8,7 @@ ResolvedReference, parse_git_reference, ) +from .results import InstallResult, PrimitiveCounts from .validation import ( InvalidVirtualPackageExtensionError, PackageContentType, @@ -17,7 +18,6 @@ detect_package_type, validate_apm_package, ) -from .results import InstallResult, PrimitiveCounts __all__ = [ # Core diff --git a/src/apm_cli/models/apm_package.py b/src/apm_cli/models/apm_package.py index 367177eb..d014f6f0 100644 --- a/src/apm_cli/models/apm_package.py +++ b/src/apm_cli/models/apm_package.py @@ -6,10 +6,11 @@ compatibility. """ -import yaml from dataclasses import dataclass from pathlib import Path -from typing import Optional, List, Dict, Union +from typing import Dict, List, Optional, Union + +import yaml from .dependency import ( DependencyReference, diff --git a/src/apm_cli/models/dependency/__init__.py b/src/apm_cli/models/dependency/__init__.py index edab860c..238d9364 100644 --- a/src/apm_cli/models/dependency/__init__.py +++ b/src/apm_cli/models/dependency/__init__.py @@ -2,7 +2,12 @@ from .mcp import MCPDependency from .reference import DependencyReference -from .types import GitReferenceType, ResolvedReference, VirtualPackageType, parse_git_reference +from .types import ( + GitReferenceType, + ResolvedReference, + VirtualPackageType, + parse_git_reference, +) __all__ = [ "DependencyReference", diff --git a/src/apm_cli/models/plugin.py b/src/apm_cli/models/plugin.py index aa486d37..19c16f31 100644 --- a/src/apm_cli/models/plugin.py +++ b/src/apm_cli/models/plugin.py @@ -1,9 +1,9 @@ """Plugin management data models.""" +import json from dataclasses import dataclass, field from pathlib import Path -from typing import List, Optional, Dict, Any -import json +from typing import Any, Dict, List, Optional @dataclass diff --git a/src/apm_cli/models/validation.py b/src/apm_cli/models/validation.py index 8cccdbd6..b542d883 100644 --- a/src/apm_cli/models/validation.py +++ b/src/apm_cli/models/validation.py @@ -271,8 +271,9 @@ def _validate_claude_skill(package_path: Path, skill_md_path: Path, result: Vali Returns: ValidationResult: Updated validation result """ - from .apm_package import APMPackage import frontmatter + + from .apm_package import APMPackage try: # Parse SKILL.md to extract metadata @@ -316,8 +317,8 @@ def _validate_marketplace_plugin(package_path: Path, plugin_json_path: Optional[ Returns: ValidationResult: Updated validation result with MARKETPLACE_PLUGIN type """ - from .apm_package import APMPackage from ..deps.plugin_parser import normalize_plugin_directory + from .apm_package import APMPackage try: # Normalize the plugin directory; plugin.json is optional metadata diff --git a/src/apm_cli/output/__init__.py b/src/apm_cli/output/__init__.py index a86a446f..466ab161 100644 --- a/src/apm_cli/output/__init__.py +++ b/src/apm_cli/output/__init__.py @@ -1,7 +1,12 @@ """Output formatting and presentation layer for APM CLI.""" from .formatters import CompilationFormatter -from .models import CompilationResults, ProjectAnalysis, OptimizationDecision, OptimizationStats +from .models import ( + CompilationResults, + OptimizationDecision, + OptimizationStats, + ProjectAnalysis, +) __all__ = [ 'CompilationFormatter', diff --git a/src/apm_cli/output/formatters.py b/src/apm_cli/output/formatters.py index 556b5f7f..c474688b 100644 --- a/src/apm_cli/output/formatters.py +++ b/src/apm_cli/output/formatters.py @@ -5,13 +5,14 @@ from typing import List, Optional try: + from io import StringIO + + from rich import box from rich.console import Console + from rich.panel import Panel from rich.table import Table - from rich.tree import Tree from rich.text import Text - from rich.panel import Panel - from rich import box - from io import StringIO + from rich.tree import Tree RICH_AVAILABLE = True except ImportError: RICH_AVAILABLE = False diff --git a/src/apm_cli/output/models.py b/src/apm_cli/output/models.py index 8afc1331..2c79f598 100644 --- a/src/apm_cli/output/models.py +++ b/src/apm_cli/output/models.py @@ -1,9 +1,9 @@ """Data models for compilation output and results.""" from dataclasses import dataclass, field +from enum import Enum from pathlib import Path from typing import Dict, List, Optional, Set -from enum import Enum from ..primitives.models import Instruction diff --git a/src/apm_cli/output/script_formatters.py b/src/apm_cli/output/script_formatters.py index d73de659..96577329 100644 --- a/src/apm_cli/output/script_formatters.py +++ b/src/apm_cli/output/script_formatters.py @@ -1,14 +1,14 @@ """Professional CLI output formatters for APM script execution.""" -from typing import Dict, List, Optional from pathlib import Path +from typing import Dict, List, Optional try: + from rich import box from rich.console import Console - from rich.text import Text from rich.panel import Panel + from rich.text import Text from rich.tree import Tree - from rich import box RICH_AVAILABLE = True except ImportError: RICH_AVAILABLE = False diff --git a/src/apm_cli/policy/__init__.py b/src/apm_cli/policy/__init__.py index 19a77876..16fb52b3 100644 --- a/src/apm_cli/policy/__init__.py +++ b/src/apm_cli/policy/__init__.py @@ -1,5 +1,11 @@ """APM Policy schema, parser, matching, inheritance, and discovery utilities.""" +from .discovery import PolicyFetchResult, discover_policy +from .inheritance import PolicyInheritanceError, merge_policies, resolve_policy_chain +from .matcher import check_dependency_allowed, check_mcp_allowed, matches_pattern +from .models import CheckResult, CIAuditResult +from .parser import PolicyValidationError, load_policy, validate_policy +from .policy_checks import run_policy_checks from .schema import ( ApmPolicy, CompilationPolicy, @@ -12,12 +18,6 @@ PolicyCache, UnmanagedFilesPolicy, ) -from .parser import PolicyValidationError, load_policy, validate_policy -from .matcher import check_dependency_allowed, check_mcp_allowed, matches_pattern -from .inheritance import merge_policies, resolve_policy_chain, PolicyInheritanceError -from .discovery import PolicyFetchResult, discover_policy -from .models import CIAuditResult, CheckResult -from .policy_checks import run_policy_checks __all__ = [ "ApmPolicy", diff --git a/src/apm_cli/policy/ci_checks.py b/src/apm_cli/policy/ci_checks.py index 0b1e94d1..f05970c6 100644 --- a/src/apm_cli/policy/ci_checks.py +++ b/src/apm_cli/policy/ci_checks.py @@ -15,8 +15,7 @@ from pathlib import Path from typing import List -from .models import CIAuditResult, CheckResult - +from .models import CheckResult, CIAuditResult # -- Individual checks --------------------------------------------- diff --git a/src/apm_cli/policy/inheritance.py b/src/apm_cli/policy/inheritance.py index b5e55286..5fe8385b 100644 --- a/src/apm_cli/policy/inheritance.py +++ b/src/apm_cli/policy/inheritance.py @@ -26,7 +26,6 @@ UnmanagedFilesPolicy, ) - MAX_CHAIN_DEPTH = 5 # Escalation ladders -- index = severity, higher is stricter. diff --git a/src/apm_cli/policy/models.py b/src/apm_cli/policy/models.py index b9fbf914..2998e28e 100644 --- a/src/apm_cli/policy/models.py +++ b/src/apm_cli/policy/models.py @@ -9,7 +9,6 @@ from dataclasses import dataclass, field from typing import Dict, List - # Check name -> most relevant artifact for SARIF locations. _CHECK_ARTIFACT_MAP: Dict[str, str] = { "lockfile-exists": "apm.lock.yaml", diff --git a/src/apm_cli/policy/policy_checks.py b/src/apm_cli/policy/policy_checks.py index 5605a46c..774c0691 100644 --- a/src/apm_cli/policy/policy_checks.py +++ b/src/apm_cli/policy/policy_checks.py @@ -10,8 +10,7 @@ from pathlib import Path from typing import List, Optional -from .models import CIAuditResult, CheckResult - +from .models import CheckResult, CIAuditResult # -- Helpers ------------------------------------------------------- diff --git a/src/apm_cli/primitives/__init__.py b/src/apm_cli/primitives/__init__.py index 2d2145f6..ac3d09d5 100644 --- a/src/apm_cli/primitives/__init__.py +++ b/src/apm_cli/primitives/__init__.py @@ -1,7 +1,18 @@ """Primitives package for APM CLI - discovery and parsing of APM context.""" -from .models import Chatmode, Instruction, Context, Skill, PrimitiveCollection, PrimitiveConflict -from .discovery import discover_primitives, find_primitive_files, discover_primitives_with_dependencies +from .discovery import ( + discover_primitives, + discover_primitives_with_dependencies, + find_primitive_files, +) +from .models import ( + Chatmode, + Context, + Instruction, + PrimitiveCollection, + PrimitiveConflict, + Skill, +) from .parser import parse_primitive_file, parse_skill_file, validate_primitive __all__ = [ diff --git a/src/apm_cli/primitives/discovery.py b/src/apm_cli/primitives/discovery.py index bc9d97be..e6fc3f3b 100644 --- a/src/apm_cli/primitives/discovery.py +++ b/src/apm_cli/primitives/discovery.py @@ -1,15 +1,14 @@ """Discovery functionality for primitive files.""" -import os import glob +import os from pathlib import Path -from typing import List, Dict +from typing import Dict, List +from ..deps.lockfile import LockFile +from ..models.apm_package import APMPackage from .models import PrimitiveCollection from .parser import parse_primitive_file, parse_skill_file -from ..models.apm_package import APMPackage -from ..deps.lockfile import LockFile - # Common primitive patterns for local discovery (with recursive search) LOCAL_PRIMITIVE_PATTERNS: Dict[str, List[str]] = { diff --git a/src/apm_cli/primitives/models.py b/src/apm_cli/primitives/models.py index 857d2ae1..02952e1f 100644 --- a/src/apm_cli/primitives/models.py +++ b/src/apm_cli/primitives/models.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from pathlib import Path -from typing import Optional, List, Union, Dict +from typing import Dict, List, Optional, Union @dataclass diff --git a/src/apm_cli/primitives/parser.py b/src/apm_cli/primitives/parser.py index 9770d592..0322d27e 100644 --- a/src/apm_cli/primitives/parser.py +++ b/src/apm_cli/primitives/parser.py @@ -2,10 +2,11 @@ import os from pathlib import Path -from typing import Union, List +from typing import List, Union + import frontmatter -from .models import Chatmode, Instruction, Context, Skill, Primitive +from .models import Chatmode, Context, Instruction, Primitive, Skill def parse_skill_file(file_path: Union[str, Path], source: str = None) -> Skill: diff --git a/src/apm_cli/registry/client.py b/src/apm_cli/registry/client.py index 8f174183..7371bf1b 100644 --- a/src/apm_cli/registry/client.py +++ b/src/apm_cli/registry/client.py @@ -1,8 +1,9 @@ """Simple MCP Registry client for server discovery.""" import os +from typing import Any, Dict, List, Optional, Tuple + import requests -from typing import Dict, List, Optional, Any, Tuple class SimpleRegistryClient: diff --git a/src/apm_cli/registry/integration.py b/src/apm_cli/registry/integration.py index c9f1cba5..38efca27 100644 --- a/src/apm_cli/registry/integration.py +++ b/src/apm_cli/registry/integration.py @@ -1,7 +1,9 @@ """Integration module for connecting registry client with package manager.""" +from typing import Any, Dict, List, Optional + import requests -from typing import Dict, List, Any, Optional + from .client import SimpleRegistryClient diff --git a/src/apm_cli/registry/operations.py b/src/apm_cli/registry/operations.py index 0ced867c..2e4bc9b2 100644 --- a/src/apm_cli/registry/operations.py +++ b/src/apm_cli/registry/operations.py @@ -2,8 +2,8 @@ import logging import os -from typing import List, Dict, Set, Optional, Tuple from pathlib import Path +from typing import Dict, List, Optional, Set, Tuple import requests diff --git a/src/apm_cli/runtime/__init__.py b/src/apm_cli/runtime/__init__.py index c032ef9a..f2ffa488 100644 --- a/src/apm_cli/runtime/__init__.py +++ b/src/apm_cli/runtime/__init__.py @@ -1,10 +1,10 @@ """Runtime adapters for executing prompts and workflows.""" from .base import RuntimeAdapter -from .llm_runtime import LLMRuntime from .codex_runtime import CodexRuntime from .copilot_runtime import CopilotRuntime from .factory import RuntimeFactory +from .llm_runtime import LLMRuntime from .manager import RuntimeManager __all__ = ["RuntimeAdapter", "LLMRuntime", "CodexRuntime", "CopilotRuntime", "RuntimeFactory", "RuntimeManager"] diff --git a/src/apm_cli/runtime/base.py b/src/apm_cli/runtime/base.py index f4dc7eb9..081b23b7 100644 --- a/src/apm_cli/runtime/base.py +++ b/src/apm_cli/runtime/base.py @@ -1,7 +1,7 @@ """Base runtime adapter interface for APM.""" from abc import ABC, abstractmethod -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional class RuntimeAdapter(ABC): diff --git a/src/apm_cli/runtime/codex_runtime.py b/src/apm_cli/runtime/codex_runtime.py index 002531b9..dd66f42c 100644 --- a/src/apm_cli/runtime/codex_runtime.py +++ b/src/apm_cli/runtime/codex_runtime.py @@ -1,8 +1,9 @@ """Codex runtime adapter for APM.""" -import subprocess import shutil -from typing import Dict, Any, Optional +import subprocess +from typing import Any, Dict, Optional + from .base import RuntimeAdapter @@ -30,8 +31,8 @@ def execute_prompt(self, prompt_content: str, **kwargs) -> str: Returns: str: The response text from Codex """ - import sys import os + import sys try: # Use codex exec to execute the prompt with real-time streaming diff --git a/src/apm_cli/runtime/copilot_runtime.py b/src/apm_cli/runtime/copilot_runtime.py index 46dd49b9..7b30ad75 100644 --- a/src/apm_cli/runtime/copilot_runtime.py +++ b/src/apm_cli/runtime/copilot_runtime.py @@ -1,11 +1,12 @@ """GitHub Copilot CLI runtime adapter for APM.""" -import subprocess -import shutil import json import os +import shutil +import subprocess from pathlib import Path -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + from .base import RuntimeAdapter diff --git a/src/apm_cli/runtime/factory.py b/src/apm_cli/runtime/factory.py index 0688df2a..0a121c37 100644 --- a/src/apm_cli/runtime/factory.py +++ b/src/apm_cli/runtime/factory.py @@ -1,10 +1,11 @@ """Runtime factory for automatic runtime detection and instantiation.""" -from typing import List, Dict, Any, Optional, Type +from typing import Any, Dict, List, Optional, Type + from .base import RuntimeAdapter -from .llm_runtime import LLMRuntime from .codex_runtime import CodexRuntime from .copilot_runtime import CopilotRuntime +from .llm_runtime import LLMRuntime class RuntimeFactory: diff --git a/src/apm_cli/runtime/llm_runtime.py b/src/apm_cli/runtime/llm_runtime.py index df24869f..5ab1aa32 100644 --- a/src/apm_cli/runtime/llm_runtime.py +++ b/src/apm_cli/runtime/llm_runtime.py @@ -1,9 +1,10 @@ """LLM runtime adapter for APM.""" +import os import subprocess import tempfile -import os -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + from .base import RuntimeAdapter diff --git a/src/apm_cli/runtime/manager.py b/src/apm_cli/runtime/manager.py index c3ebc394..840876bc 100644 --- a/src/apm_cli/runtime/manager.py +++ b/src/apm_cli/runtime/manager.py @@ -4,12 +4,13 @@ """ import os -import sys +import shutil import subprocess +import sys import tempfile -import shutil from pathlib import Path from typing import Dict, List, Optional + import click from colorama import Fore, Style diff --git a/src/apm_cli/security/gate.py b/src/apm_cli/security/gate.py index e8ac9f96..8435cd67 100644 --- a/src/apm_cli/security/gate.py +++ b/src/apm_cli/security/gate.py @@ -12,9 +12,8 @@ from pathlib import Path from typing import Dict, List, Literal, Optional -from .content_scanner import ContentScanner, ScanFinding from ..utils.paths import portable_relpath - +from .content_scanner import ContentScanner, ScanFinding # --------------------------------------------------------------------------- # Policy & Verdict diff --git a/src/apm_cli/utils/__init__.py b/src/apm_cli/utils/__init__.py index d397b8d1..1a4fdd5e 100644 --- a/src/apm_cli/utils/__init__.py +++ b/src/apm_cli/utils/__init__.py @@ -1,26 +1,24 @@ """Utility modules for APM CLI.""" from .console import ( - _rich_success, - _rich_error, - _rich_warning, - _rich_info, - _rich_echo, - _rich_panel, + STATUS_SYMBOLS, _create_files_table, _get_console, - STATUS_SYMBOLS + _rich_echo, + _rich_error, + _rich_info, + _rich_panel, + _rich_success, + _rich_warning, ) - from .diagnostics import ( - Diagnostic, - DiagnosticCollector, CATEGORY_COLLISION, + CATEGORY_ERROR, CATEGORY_OVERWRITE, CATEGORY_WARNING, - CATEGORY_ERROR, + Diagnostic, + DiagnosticCollector, ) - from .paths import portable_relpath __all__ = [ diff --git a/src/apm_cli/utils/console.py b/src/apm_cli/utils/console.py index 5b2db0f9..d152dbaf 100644 --- a/src/apm_cli/utils/console.py +++ b/src/apm_cli/utils/console.py @@ -1,16 +1,17 @@ """Console utility functions for formatting and output.""" -import click import sys -from typing import Optional, Any from contextlib import contextmanager +from typing import Any, Optional + +import click # Rich library imports with fallbacks try: + from rich import print as rich_print from rich.console import Console from rich.panel import Panel from rich.table import Table - from rich import print as rich_print RICH_AVAILABLE = True except ImportError: RICH_AVAILABLE = False diff --git a/src/apm_cli/utils/helpers.py b/src/apm_cli/utils/helpers.py index 02b502db..ad5336a0 100644 --- a/src/apm_cli/utils/helpers.py +++ b/src/apm_cli/utils/helpers.py @@ -2,8 +2,8 @@ import os import platform -import subprocess import shutil +import subprocess import sys from pathlib import Path from typing import Optional diff --git a/src/apm_cli/utils/version_checker.py b/src/apm_cli/utils/version_checker.py index 72d69971..9340c498 100644 --- a/src/apm_cli/utils/version_checker.py +++ b/src/apm_cli/utils/version_checker.py @@ -2,8 +2,8 @@ import re import sys -from typing import Optional, Tuple from pathlib import Path +from typing import Optional, Tuple def get_latest_version_from_github( diff --git a/src/apm_cli/version.py b/src/apm_cli/version.py index cd68a814..a105bd95 100644 --- a/src/apm_cli/version.py +++ b/src/apm_cli/version.py @@ -29,9 +29,9 @@ def get_version() -> str: try: # Python 3.8+ has importlib.metadata if sys.version_info >= (3, 8): - from importlib.metadata import version, PackageNotFoundError + from importlib.metadata import PackageNotFoundError, version else: - from importlib_metadata import version, PackageNotFoundError + from importlib_metadata import PackageNotFoundError, version return version("apm-cli") except (ImportError, PackageNotFoundError): diff --git a/src/apm_cli/workflow/discovery.py b/src/apm_cli/workflow/discovery.py index 1c03d9e9..da2b3f7f 100644 --- a/src/apm_cli/workflow/discovery.py +++ b/src/apm_cli/workflow/discovery.py @@ -1,7 +1,8 @@ """Discovery functionality for workflow files.""" -import os import glob +import os + from .parser import parse_workflow_file diff --git a/src/apm_cli/workflow/parser.py b/src/apm_cli/workflow/parser.py index 1b1f0a1f..bbf60343 100644 --- a/src/apm_cli/workflow/parser.py +++ b/src/apm_cli/workflow/parser.py @@ -1,6 +1,7 @@ """Parser for workflow definition files.""" import os + import frontmatter diff --git a/src/apm_cli/workflow/runner.py b/src/apm_cli/workflow/runner.py index d6ce3281..0f289f06 100644 --- a/src/apm_cli/workflow/runner.py +++ b/src/apm_cli/workflow/runner.py @@ -2,10 +2,12 @@ import os import re + from colorama import Fore, Style -from .parser import WorkflowDefinition -from .discovery import discover_workflows + from ..runtime.factory import RuntimeFactory +from .discovery import discover_workflows +from .parser import WorkflowDefinition # Color constants (matching cli.py) WARNING = f"{Fore.YELLOW}" diff --git a/tests/acceptance/test_logging_acceptance.py b/tests/acceptance/test_logging_acceptance.py index 7beb521d..37e56259 100644 --- a/tests/acceptance/test_logging_acceptance.py +++ b/tests/acceptance/test_logging_acceptance.py @@ -22,7 +22,6 @@ from apm_cli.models.results import InstallResult from apm_cli.utils.console import STATUS_SYMBOLS - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/tests/basic_workflow_test.py b/tests/basic_workflow_test.py index 5eaa60e7..e3f6cac1 100644 --- a/tests/basic_workflow_test.py +++ b/tests/basic_workflow_test.py @@ -1,19 +1,19 @@ """Basic tests for workflow functionality.""" +import gc import os +import shutil import sys import tempfile -import unittest import time -import shutil -import gc +import unittest # Add the src directory to the path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))) +from apm_cli.workflow.discovery import create_workflow_template from apm_cli.workflow.parser import WorkflowDefinition, parse_workflow_file from apm_cli.workflow.runner import substitute_parameters -from apm_cli.workflow.discovery import create_workflow_template def safe_rmdir(path): diff --git a/tests/benchmarks/run_baseline.py b/tests/benchmarks/run_baseline.py index 76ffdc86..25412c2f 100644 --- a/tests/benchmarks/run_baseline.py +++ b/tests/benchmarks/run_baseline.py @@ -8,19 +8,18 @@ """ import importlib +import statistics import sys import time -import statistics from pathlib import Path # Ensure the project is importable sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src")) -from apm_cli.primitives.models import PrimitiveCollection, Instruction -from apm_cli.deps.dependency_graph import DependencyTree, DependencyNode from apm_cli.deps.apm_resolver import APMDependencyResolver +from apm_cli.deps.dependency_graph import DependencyNode, DependencyTree from apm_cli.models.apm_package import APMPackage, DependencyReference - +from apm_cli.primitives.models import Instruction, PrimitiveCollection # --------------------------------------------------------------------------- # Helpers @@ -199,8 +198,8 @@ def main(): print(f" {key:45s} median={med:8.2f}ms min={lo:8.2f}ms max={hi:8.2f}ms") # 6. Phase 4: Parallel execution overhead (ThreadPoolExecutor vs sequential) - from concurrent.futures import ThreadPoolExecutor, as_completed import time as _time + from concurrent.futures import ThreadPoolExecutor, as_completed def _simulated_work(ms: float): """Simulate I/O-bound work by sleeping.""" diff --git a/tests/benchmarks/test_perf_benchmarks.py b/tests/benchmarks/test_perf_benchmarks.py index da6bab81..86249e9c 100644 --- a/tests/benchmarks/test_perf_benchmarks.py +++ b/tests/benchmarks/test_perf_benchmarks.py @@ -15,15 +15,18 @@ import pytest +from apm_cli.compilation.constitution import clear_constitution_cache, read_constitution +from apm_cli.deps.apm_resolver import APMDependencyResolver +from apm_cli.deps.dependency_graph import DependencyNode, DependencyTree +from apm_cli.models.apm_package import ( + APMPackage, + DependencyReference, + clear_apm_yml_cache, +) from apm_cli.primitives.models import ( - PrimitiveCollection, Instruction, + PrimitiveCollection, ) -from apm_cli.deps.dependency_graph import DependencyTree, DependencyNode -from apm_cli.deps.apm_resolver import APMDependencyResolver -from apm_cli.models.apm_package import APMPackage, DependencyReference, clear_apm_yml_cache -from apm_cli.compilation.constitution import read_constitution, clear_constitution_cache - # --------------------------------------------------------------------------- # Helpers to build synthetic data diff --git a/tests/integration/test_apm_dependencies.py b/tests/integration/test_apm_dependencies.py index d39c37de..4fa7c37c 100644 --- a/tests/integration/test_apm_dependencies.py +++ b/tests/integration/test_apm_dependencies.py @@ -17,17 +17,18 @@ """ import os -import pytest -import tempfile import shutil import subprocess -import yaml +import tempfile from pathlib import Path -from unittest.mock import patch, Mock -from typing import Dict, Any, List +from typing import Any, Dict, List +from unittest.mock import Mock, patch + +import pytest +import yaml -from apm_cli.deps.github_downloader import GitHubPackageDownloader from apm_cli.deps.apm_resolver import APMDependencyResolver +from apm_cli.deps.github_downloader import GitHubPackageDownloader from apm_cli.models.apm_package import APMPackage, DependencyReference @@ -316,8 +317,11 @@ def test_cli_deps_commands_with_real_dependencies(self): downloader.download_package(str(dep_ref), package_dir) # Import and test CLI commands - from apm_cli.commands.deps import _count_package_files, _get_package_display_info - + from apm_cli.commands.deps import ( + _count_package_files, + _get_package_display_info, + ) + # Test file counting context_count, workflow_count = _count_package_files(package_dir) assert context_count >= 0 # May have context files in .apm structure @@ -423,7 +427,7 @@ def test_cross_platform_dependency_download(self): def test_authentication_token_handling(self): """Test GitHub authentication token handling according to our token management architecture.""" from apm_cli.deps.github_downloader import GitHubPackageDownloader - + # Test with environment tokens (new token architecture) github_apm_pat = os.getenv('GITHUB_APM_PAT') github_token = os.getenv('GITHUB_TOKEN') diff --git a/tests/integration/test_auto_install_e2e.py b/tests/integration/test_auto_install_e2e.py index b3ba24b7..94c43adb 100644 --- a/tests/integration/test_auto_install_e2e.py +++ b/tests/integration/test_auto_install_e2e.py @@ -12,13 +12,13 @@ import os import platform -import pytest +import shutil import subprocess import tempfile -import shutil import time from pathlib import Path +import pytest # Skip all tests in this module if not in E2E mode E2E_MODE = os.environ.get('APM_E2E_TESTS', '').lower() in ('1', 'true', 'yes') diff --git a/tests/integration/test_auto_integration.py b/tests/integration/test_auto_integration.py index f3d7f3d9..b0239236 100644 --- a/tests/integration/test_auto_integration.py +++ b/tests/integration/test_auto_integration.py @@ -1,14 +1,20 @@ """Integration tests for auto-integration feature.""" -import pytest -import tempfile import os +import tempfile +from datetime import datetime from pathlib import Path from unittest.mock import patch +import pytest + from apm_cli.integration import PromptIntegrator -from apm_cli.models.apm_package import PackageInfo, APMPackage, ResolvedReference, GitReferenceType -from datetime import datetime +from apm_cli.models.apm_package import ( + APMPackage, + GitReferenceType, + PackageInfo, + ResolvedReference, +) @pytest.mark.integration diff --git a/tests/integration/test_collection_install.py b/tests/integration/test_collection_install.py index 8000ac7b..329f0d1e 100644 --- a/tests/integration/test_collection_install.py +++ b/tests/integration/test_collection_install.py @@ -1,11 +1,15 @@ """Integration tests for collection virtual package installation.""" -import pytest -from pathlib import Path -import tempfile import shutil +import tempfile +from pathlib import Path + +import pytest -from apm_cli.deps.github_downloader import GitHubPackageDownloader, normalize_collection_path +from apm_cli.deps.github_downloader import ( + GitHubPackageDownloader, + normalize_collection_path, +) from apm_cli.models.apm_package import DependencyReference @@ -121,7 +125,7 @@ def test_collection_manifest_parsing(self): def test_collection_manifest_validation_missing_fields(self): """Test that collection manifest validation catches missing fields.""" from apm_cli.deps.collection_parser import parse_collection_yml - + # Missing required field 'description' invalid_yaml = b""" id: test @@ -137,7 +141,7 @@ def test_collection_manifest_validation_missing_fields(self): def test_collection_manifest_validation_empty_items(self): """Test that collection manifest validation catches empty items.""" from apm_cli.deps.collection_parser import parse_collection_yml - + # Empty items array invalid_yaml = b""" id: test @@ -152,7 +156,7 @@ def test_collection_manifest_validation_empty_items(self): def test_collection_manifest_validation_invalid_item(self): """Test that collection manifest validation catches invalid items.""" from apm_cli.deps.collection_parser import parse_collection_yml - + # Item missing 'kind' field invalid_yaml = b""" id: test diff --git a/tests/integration/test_compile_constitution_injection.py b/tests/integration/test_compile_constitution_injection.py index 6b2506a3..eea1ba4a 100644 --- a/tests/integration/test_compile_constitution_injection.py +++ b/tests/integration/test_compile_constitution_injection.py @@ -4,7 +4,10 @@ import sys from pathlib import Path -from ..utils.constitution_fixtures import temp_project_with_constitution, DEFAULT_CONSTITUTION +from ..utils.constitution_fixtures import ( + DEFAULT_CONSTITUTION, + temp_project_with_constitution, +) CLI = [sys.executable, "-m", "apm_cli.cli", "compile", "--single-agents"] diff --git a/tests/integration/test_compile_permission_denied.py b/tests/integration/test_compile_permission_denied.py index 7bd6e31f..dfc6ecd5 100644 --- a/tests/integration/test_compile_permission_denied.py +++ b/tests/integration/test_compile_permission_denied.py @@ -5,10 +5,13 @@ import sys from pathlib import Path -from ..utils.constitution_fixtures import temp_project_with_constitution, DEFAULT_CONSTITUTION - import pytest +from ..utils.constitution_fixtures import ( + DEFAULT_CONSTITUTION, + temp_project_with_constitution, +) + CLI = [sys.executable, "-m", "apm_cli.cli", "compile", "--single-agents"] diff --git a/tests/integration/test_deployed_files_e2e.py b/tests/integration/test_deployed_files_e2e.py index ae9e9620..5d107710 100644 --- a/tests/integration/test_deployed_files_e2e.py +++ b/tests/integration/test_deployed_files_e2e.py @@ -16,11 +16,10 @@ import os import shutil import subprocess +from pathlib import Path import pytest import yaml -from pathlib import Path - # Skip all tests if no GitHub token is available pytestmark = pytest.mark.skipif( diff --git a/tests/integration/test_diff_aware_install_e2e.py b/tests/integration/test_diff_aware_install_e2e.py index fb36c9cf..21139c28 100644 --- a/tests/integration/test_diff_aware_install_e2e.py +++ b/tests/integration/test_diff_aware_install_e2e.py @@ -14,11 +14,10 @@ import os import shutil import subprocess +from pathlib import Path import pytest import yaml -from pathlib import Path - # Skip all tests if no GitHub token is available pytestmark = pytest.mark.skipif( diff --git a/tests/integration/test_golden_scenario_e2e.py b/tests/integration/test_golden_scenario_e2e.py index da45960f..b6844adf 100644 --- a/tests/integration/test_golden_scenario_e2e.py +++ b/tests/integration/test_golden_scenario_e2e.py @@ -24,16 +24,16 @@ - Network access to download runtimes and make API calls """ +import json import os +import shutil import subprocess import tempfile -import shutil -import pytest -import json from pathlib import Path from unittest import mock -import toml +import pytest +import toml # Skip all tests in this module if not in E2E mode E2E_MODE = os.environ.get('APM_E2E_TESTS', '').lower() in ('1', 'true', 'yes') diff --git a/tests/integration/test_guardrailing_hero_e2e.py b/tests/integration/test_guardrailing_hero_e2e.py index bbc10739..98df8a25 100644 --- a/tests/integration/test_guardrailing_hero_e2e.py +++ b/tests/integration/test_guardrailing_hero_e2e.py @@ -17,9 +17,9 @@ import os import subprocess import tempfile -import pytest from pathlib import Path +import pytest # Skip all tests in this module if not in E2E mode E2E_MODE = os.environ.get('APM_E2E_TESTS', '').lower() in ('1', 'true', 'yes') diff --git a/tests/integration/test_install_with_links.py b/tests/integration/test_install_with_links.py index 03035b4f..1eef0399 100644 --- a/tests/integration/test_install_with_links.py +++ b/tests/integration/test_install_with_links.py @@ -1,12 +1,18 @@ """Integration tests for link resolution during installation.""" -import pytest +from datetime import datetime from textwrap import dedent -from apm_cli.integration.prompt_integrator import PromptIntegrator +import pytest + from apm_cli.integration.agent_integrator import AgentIntegrator -from apm_cli.models.apm_package import APMPackage, PackageInfo, ResolvedReference, GitReferenceType -from datetime import datetime +from apm_cli.integration.prompt_integrator import PromptIntegrator +from apm_cli.models.apm_package import ( + APMPackage, + GitReferenceType, + PackageInfo, + ResolvedReference, +) @pytest.fixture diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 307b55eb..582390e1 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -1,16 +1,17 @@ """Integration tests for APM.""" -import os +import gc import json -import tempfile -import unittest -import time +import os import shutil -import gc import sys -from unittest.mock import patch, MagicMock -from apm_cli.factory import ClientFactory, PackageManagerFactory +import tempfile +import time +import unittest +from unittest.mock import MagicMock, patch + from apm_cli.core.operations import install_package +from apm_cli.factory import ClientFactory, PackageManagerFactory def safe_rmdir(path): diff --git a/tests/integration/test_llm_runtime_integration.py b/tests/integration/test_llm_runtime_integration.py index 22d2d963..3090ed2a 100644 --- a/tests/integration/test_llm_runtime_integration.py +++ b/tests/integration/test_llm_runtime_integration.py @@ -1,8 +1,9 @@ """Integration test for LLM runtime with APM workflows.""" -import tempfile import os -from unittest.mock import patch, Mock +import tempfile +from unittest.mock import Mock, patch + from apm_cli.workflow.runner import run_workflow diff --git a/tests/integration/test_local_install.py b/tests/integration/test_local_install.py index 31d54db8..72c939ca 100644 --- a/tests/integration/test_local_install.py +++ b/tests/integration/test_local_install.py @@ -14,7 +14,6 @@ import pytest import yaml - # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @@ -358,7 +357,8 @@ def test_pack_rejects_with_local_deps(self, temp_workspace, apm_command): })) # Create a valid lockfile via the LockFile API - from apm_cli.deps.lockfile import LockFile as _LF, LockedDependency as _LD + from apm_cli.deps.lockfile import LockedDependency as _LD + from apm_cli.deps.lockfile import LockFile as _LF _lock = _LF() _lock.add_dependency(_LD( repo_url="_local/local-skills", diff --git a/tests/integration/test_marketplace_plugin_integration.py b/tests/integration/test_marketplace_plugin_integration.py index dc17c465..3b31474f 100644 --- a/tests/integration/test_marketplace_plugin_integration.py +++ b/tests/integration/test_marketplace_plugin_integration.py @@ -9,9 +9,10 @@ import json import shutil +from datetime import datetime from pathlib import Path + import pytest -from datetime import datetime from apm_cli.integration.agent_integrator import AgentIntegrator from apm_cli.integration.command_integrator import CommandIntegrator diff --git a/tests/integration/test_mcp_registry_e2e.py b/tests/integration/test_mcp_registry_e2e.py index ff4df1eb..6db3cae5 100644 --- a/tests/integration/test_mcp_registry_e2e.py +++ b/tests/integration/test_mcp_registry_e2e.py @@ -12,16 +12,17 @@ the MCP registry functionality we've implemented. """ +import json import os +import shutil import subprocess import tempfile -import shutil -import pytest -import json -import toml from pathlib import Path from unittest import mock +import pytest +import toml + def _is_registry_healthy() -> bool: """Check if GitHub MCP server has proper package configuration. diff --git a/tests/integration/test_mixed_deps.py b/tests/integration/test_mixed_deps.py index 7fd6bdc2..8cf218b8 100644 --- a/tests/integration/test_mixed_deps.py +++ b/tests/integration/test_mixed_deps.py @@ -9,9 +9,9 @@ import os import shutil import subprocess -import pytest from pathlib import Path +import pytest # Skip all tests if GITHUB_APM_PAT is not set pytestmark = pytest.mark.skipif( diff --git a/tests/integration/test_multi_runtime_integration.py b/tests/integration/test_multi_runtime_integration.py index 66ab2399..cff92221 100644 --- a/tests/integration/test_multi_runtime_integration.py +++ b/tests/integration/test_multi_runtime_integration.py @@ -1,10 +1,11 @@ """Integration test for multi-runtime architecture.""" -import tempfile import os -from unittest.mock import patch, Mock -from apm_cli.workflow.runner import run_workflow +import tempfile +from unittest.mock import Mock, patch + from apm_cli.runtime.factory import RuntimeFactory +from apm_cli.workflow.runner import run_workflow def test_runtime_type_selection(): diff --git a/tests/integration/test_pack_unpack_e2e.py b/tests/integration/test_pack_unpack_e2e.py index 0fdad215..8650af04 100644 --- a/tests/integration/test_pack_unpack_e2e.py +++ b/tests/integration/test_pack_unpack_e2e.py @@ -8,10 +8,9 @@ import os import shutil import subprocess - -import pytest from pathlib import Path +import pytest pytestmark = pytest.mark.skipif( not os.environ.get("GITHUB_APM_PAT") and not os.environ.get("GITHUB_TOKEN"), diff --git a/tests/integration/test_plugin_e2e.py b/tests/integration/test_plugin_e2e.py index 5025ab26..265bb3ff 100644 --- a/tests/integration/test_plugin_e2e.py +++ b/tests/integration/test_plugin_e2e.py @@ -29,7 +29,6 @@ ) from apm_cli.utils.helpers import find_plugin_json - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/tests/integration/test_registry.py b/tests/integration/test_registry.py index b630abb7..71e9790f 100644 --- a/tests/integration/test_registry.py +++ b/tests/integration/test_registry.py @@ -1,16 +1,18 @@ """Integration tests for MCP registry client.""" -import os +import gc import json -import pytest -import tempfile -import time +import os import shutil -import gc import sys +import tempfile +import time from pathlib import Path -from apm_cli.registry.client import SimpleRegistryClient + +import pytest + from apm_cli.adapters.client.vscode import VSCodeClientAdapter +from apm_cli.registry.client import SimpleRegistryClient def safe_rmdir(path): diff --git a/tests/integration/test_registry_client_integration.py b/tests/integration/test_registry_client_integration.py index 1f3a55e4..4e1b3b24 100644 --- a/tests/integration/test_registry_client_integration.py +++ b/tests/integration/test_registry_client_integration.py @@ -1,8 +1,10 @@ """Integration tests for the MCP registry client with GitHub MCP Registry.""" -import unittest import os +import unittest + import requests + from apm_cli.registry.client import SimpleRegistryClient diff --git a/tests/integration/test_runnable_prompts_integration.py b/tests/integration/test_runnable_prompts_integration.py index 228bcb3e..f76895e6 100644 --- a/tests/integration/test_runnable_prompts_integration.py +++ b/tests/integration/test_runnable_prompts_integration.py @@ -1,10 +1,11 @@ """Integration tests for runnable prompts feature.""" -import pytest -from pathlib import Path +import os import subprocess import tempfile -import os +from pathlib import Path + +import pytest @pytest.fixture(autouse=True) @@ -53,8 +54,9 @@ def test_local_prompt_immediate_run(self, tmp_path): os.chdir(tmp_path) # Import here to ensure we're in the right directory - from apm_cli.core.script_runner import ScriptRunner from unittest.mock import patch + + from apm_cli.core.script_runner import ScriptRunner runner = ScriptRunner() @@ -97,8 +99,9 @@ def test_dependency_prompt_discovery(self, tmp_path): os.chdir(tmp_path) - from apm_cli.core.script_runner import ScriptRunner from unittest.mock import patch + + from apm_cli.core.script_runner import ScriptRunner runner = ScriptRunner() @@ -156,8 +159,9 @@ def test_explicit_script_precedence_over_discovery(self, tmp_path): os.chdir(tmp_path) - from apm_cli.core.script_runner import ScriptRunner from unittest.mock import patch + + from apm_cli.core.script_runner import ScriptRunner runner = ScriptRunner() @@ -181,8 +185,9 @@ def test_copilot_command_defaults(self, tmp_path): os.chdir(tmp_path) - from apm_cli.core.script_runner import ScriptRunner from unittest.mock import patch + + from apm_cli.core.script_runner import ScriptRunner runner = ScriptRunner() @@ -232,8 +237,9 @@ def test_no_runtime_error_message(self, tmp_path): os.chdir(tmp_path) - from apm_cli.core.script_runner import ScriptRunner from unittest.mock import patch + + from apm_cli.core.script_runner import ScriptRunner runner = ScriptRunner() @@ -272,8 +278,9 @@ def test_virtual_package_prompt_workflow(self, tmp_path): os.chdir(tmp_path) - from apm_cli.core.script_runner import ScriptRunner from unittest.mock import patch + + from apm_cli.core.script_runner import ScriptRunner runner = ScriptRunner() diff --git a/tests/integration/test_runtime_smoke.py b/tests/integration/test_runtime_smoke.py index 144eb9cc..41eb9dad 100644 --- a/tests/integration/test_runtime_smoke.py +++ b/tests/integration/test_runtime_smoke.py @@ -6,13 +6,15 @@ """ import os +import shutil import subprocess import sys import tempfile -import shutil -import pytest from pathlib import Path +import pytest + + # Test fixtures and utilities @pytest.fixture(scope="module") def temp_apm_home(): @@ -166,7 +168,7 @@ def test_apm_runtime_detection(self, temp_apm_home): """Test that APM can detect installed runtimes.""" # Import APM modules from apm_cli.runtime.factory import RuntimeFactory - + # Update PATH to include our test runtime directory runtime_dir = Path(temp_apm_home) / ".apm" / "runtimes" if runtime_dir.exists(): @@ -188,7 +190,7 @@ def test_apm_runtime_detection(self, temp_apm_home): def test_apm_workflow_compilation(self, temp_apm_home): """Test that APM can compile workflows without executing them.""" from apm_cli.workflow.runner import preview_workflow - + # Create a test workflow with tempfile.TemporaryDirectory() as temp_dir: workflow_content = """--- @@ -266,7 +268,7 @@ def test_apm_init_workflow_dry_run(self, temp_apm_home): # Manually copy template files (simulating what init does) import shutil - + # Copy template files for template_file in ["apm.yml", "hello-world.prompt.md", "README.md"]: src = template_dir / template_file diff --git a/tests/integration/test_selective_install_mcp.py b/tests/integration/test_selective_install_mcp.py index 1db66038..7cd920e7 100644 --- a/tests/integration/test_selective_install_mcp.py +++ b/tests/integration/test_selective_install_mcp.py @@ -20,7 +20,6 @@ from apm_cli.deps.lockfile import LockedDependency, LockFile - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/tests/integration/test_skill_install.py b/tests/integration/test_skill_install.py index 99b7c042..ddf921ac 100644 --- a/tests/integration/test_skill_install.py +++ b/tests/integration/test_skill_install.py @@ -9,9 +9,9 @@ import os import shutil import subprocess -import pytest from pathlib import Path +import pytest # Skip all tests if GITHUB_APM_PAT is not set pytestmark = pytest.mark.skipif( diff --git a/tests/integration/test_skill_integration.py b/tests/integration/test_skill_integration.py index 8e5dfefa..1a347a76 100644 --- a/tests/integration/test_skill_integration.py +++ b/tests/integration/test_skill_integration.py @@ -10,9 +10,9 @@ import os import shutil import subprocess -import pytest from pathlib import Path +import pytest # Skip all tests if GITHUB_APM_PAT is not set pytestmark = pytest.mark.skipif( diff --git a/tests/integration/test_version_notification.py b/tests/integration/test_version_notification.py index a6807138..197e5268 100644 --- a/tests/integration/test_version_notification.py +++ b/tests/integration/test_version_notification.py @@ -1,8 +1,9 @@ """Integration tests for version update notification in CLI.""" -import unittest import os +import unittest from unittest.mock import patch + from click.testing import CliRunner diff --git a/tests/integration/test_virtual_package_orphan_detection.py b/tests/integration/test_virtual_package_orphan_detection.py index 59d70a4b..c1e55ebd 100644 --- a/tests/integration/test_virtual_package_orphan_detection.py +++ b/tests/integration/test_virtual_package_orphan_detection.py @@ -10,8 +10,10 @@ import tempfile from pathlib import Path + import pytest import yaml + from apm_cli.models.apm_package import APMPackage from apm_cli.primitives.discovery import get_dependency_declaration_order diff --git a/tests/manual_workflow_script.py b/tests/manual_workflow_script.py index 632592d2..6888cc28 100644 --- a/tests/manual_workflow_script.py +++ b/tests/manual_workflow_script.py @@ -8,17 +8,17 @@ """ import os +import subprocess import sys import tempfile -import subprocess # Add the src directory to the path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))) -from apm_cli.workflow.discovery import create_workflow_template -from apm_cli.workflow.discovery import discover_workflows +from apm_cli.workflow.discovery import create_workflow_template, discover_workflows from apm_cli.workflow.runner import run_workflow + def manual_test_workflow_commands(): """Test the workflow commands.""" # Create a temporary directory for testing diff --git a/tests/noninteractive_workflow_test.py b/tests/noninteractive_workflow_test.py index c4ec6975..dbd52b8b 100644 --- a/tests/noninteractive_workflow_test.py +++ b/tests/noninteractive_workflow_test.py @@ -11,8 +11,9 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))) from apm_cli.workflow.discovery import create_workflow_template, discover_workflows -from apm_cli.workflow.runner import substitute_parameters from apm_cli.workflow.parser import parse_workflow_file +from apm_cli.workflow.runner import substitute_parameters + def test_workflow_features(): """Test the core workflow features without interactive prompts.""" diff --git a/tests/test_apm_package_models.py b/tests/test_apm_package_models.py index a5f13aea..00a99896 100644 --- a/tests/test_apm_package_models.py +++ b/tests/test_apm_package_models.py @@ -1,26 +1,27 @@ """Unit tests for APM package data models and validation.""" import json -import pytest import tempfile -import yaml from pathlib import Path -from unittest.mock import patch, mock_open +from unittest.mock import mock_open, patch + +import pytest +import yaml +from apm_cli.utils import github_host from src.apm_cli.models.apm_package import ( APMPackage, - DependencyReference, - ValidationResult, - ValidationError, - ResolvedReference, - PackageInfo, + DependencyReference, GitReferenceType, PackageContentType, + PackageInfo, PackageType, - validate_apm_package, + ResolvedReference, + ValidationError, + ValidationResult, parse_git_reference, + validate_apm_package, ) -from apm_cli.utils import github_host class TestDependencyReference: @@ -1426,9 +1427,10 @@ def test_github_round_trip_works(self): def test_build_download_ref_preserves_virtual_path(self): """build_download_ref returns a DependencyReference that preserves virtual_path for generic hosts (not a lossy flat string).""" - from apm_cli.drift import build_download_ref from unittest.mock import Mock + from apm_cli.drift import build_download_ref + dep = DependencyReference( repo_url="org/my-skills", host="git.example.com", diff --git a/tests/test_apm_resolver.py b/tests/test_apm_resolver.py index 214deaee..c8595096 100644 --- a/tests/test_apm_resolver.py +++ b/tests/test_apm_resolver.py @@ -7,8 +7,12 @@ from src.apm_cli.deps.apm_resolver import APMDependencyResolver from src.apm_cli.deps.dependency_graph import ( - DependencyGraph, DependencyTree, DependencyNode, FlatDependencyMap, - CircularRef, ConflictInfo + CircularRef, + ConflictInfo, + DependencyGraph, + DependencyNode, + DependencyTree, + FlatDependencyMap, ) from src.apm_cli.models.apm_package import APMPackage, DependencyReference diff --git a/tests/test_codex_docker_args_fix.py b/tests/test_codex_docker_args_fix.py index e429c307..aba44a16 100644 --- a/tests/test_codex_docker_args_fix.py +++ b/tests/test_codex_docker_args_fix.py @@ -7,10 +7,11 @@ 3. Handles both Docker and npm packages correctly """ -import pytest -from unittest.mock import Mock, patch -import sys import os +import sys +from unittest.mock import Mock, patch + +import pytest # Add the source directory to the path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) diff --git a/tests/test_codex_empty_string_and_defaults.py b/tests/test_codex_empty_string_and_defaults.py index 5ba62cca..1a2988e8 100644 --- a/tests/test_codex_empty_string_and_defaults.py +++ b/tests/test_codex_empty_string_and_defaults.py @@ -8,10 +8,11 @@ 4. Maintains consistent behavior for environment variable handling """ -import pytest -from unittest.mock import Mock, patch -import sys import os +import sys +from unittest.mock import Mock, patch + +import pytest # Add the source directory to the path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) diff --git a/tests/test_collision_integration.py b/tests/test_collision_integration.py index bf0b2e49..bbc2398e 100644 --- a/tests/test_collision_integration.py +++ b/tests/test_collision_integration.py @@ -1,8 +1,9 @@ """Integration tests for prompt collision handling.""" -import pytest -from pathlib import Path import os +from pathlib import Path + +import pytest from apm_cli.core.script_runner import ScriptRunner diff --git a/tests/test_console.py b/tests/test_console.py index 82363d9f..0f3b2a6e 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -1,8 +1,8 @@ """Test module for console commands.""" import os -import tempfile import shutil +import tempfile from unittest import TestCase import pytest @@ -10,7 +10,6 @@ from apm_cli.cli import cli as app - runner = CliRunner() diff --git a/tests/test_distributed_compilation.py b/tests/test_distributed_compilation.py index c9c9a7d0..e76efe1c 100644 --- a/tests/test_distributed_compilation.py +++ b/tests/test_distributed_compilation.py @@ -1,13 +1,17 @@ """Tests for distributed compilation system (Task 7).""" -import pytest -import tempfile import shutil +import tempfile from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + +import pytest -from apm_cli.compilation.distributed_compiler import DistributedAgentsCompiler, DirectoryMap -from apm_cli.compilation.agents_compiler import CompilationConfig, AgentsCompiler +from apm_cli.compilation.agents_compiler import AgentsCompiler, CompilationConfig +from apm_cli.compilation.distributed_compiler import ( + DirectoryMap, + DistributedAgentsCompiler, +) from apm_cli.primitives.models import Instruction, PrimitiveCollection diff --git a/tests/test_empty_string_and_defaults.py b/tests/test_empty_string_and_defaults.py index 35693833..c628be54 100644 --- a/tests/test_empty_string_and_defaults.py +++ b/tests/test_empty_string_and_defaults.py @@ -8,10 +8,11 @@ 4. Maintains consistent behavior for environment variable handling """ -import pytest -from unittest.mock import Mock, patch -import sys import os +import sys +from unittest.mock import Mock, patch + +import pytest # Add the source directory to the path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) diff --git a/tests/test_enhanced_discovery.py b/tests/test_enhanced_discovery.py index 271b320f..8c7e102f 100644 --- a/tests/test_enhanced_discovery.py +++ b/tests/test_enhanced_discovery.py @@ -1,27 +1,34 @@ """Comprehensive tests for enhanced primitive discovery with dependencies.""" import os + +# Test imports - using absolute imports since we may not have proper package setup +import sys import tempfile import unittest from pathlib import Path -# Test imports - using absolute imports since we may not have proper package setup -import sys import yaml # Add src to path for testing sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) try: - from apm_cli.primitives.models import Chatmode, Instruction, Context, PrimitiveCollection, PrimitiveConflict + from apm_cli.models.apm_package import APMPackage, DependencyReference from apm_cli.primitives.discovery import ( - discover_primitives_with_dependencies, - scan_local_primitives, - scan_dependency_primitives, + discover_primitives_with_dependencies, get_dependency_declaration_order, - scan_directory_with_source + scan_dependency_primitives, + scan_directory_with_source, + scan_local_primitives, + ) + from apm_cli.primitives.models import ( + Chatmode, + Context, + Instruction, + PrimitiveCollection, + PrimitiveConflict, ) - from apm_cli.models.apm_package import APMPackage, DependencyReference except ImportError as e: print(f"Import error: {e}") print("Skipping enhanced discovery tests due to missing dependencies") @@ -414,7 +421,7 @@ def test_collection_methods(self): def test_dependency_order_includes_transitive_from_lockfile(self): """Test that transitive dependencies from apm.lock are included in declaration order.""" - from apm_cli.deps.lockfile import LockFile, LockedDependency + from apm_cli.deps.lockfile import LockedDependency, LockFile # Create apm.yml with only one direct dependency dependencies = {"apm": ["rieraj/team-cot-agent-instructions"]} @@ -459,7 +466,7 @@ def test_dependency_order_no_lockfile(self): def test_dependency_order_lockfile_no_duplicates(self): """Test that direct deps already in apm.yml are not duplicated from lockfile.""" - from apm_cli.deps.lockfile import LockFile, LockedDependency + from apm_cli.deps.lockfile import LockedDependency, LockFile # Create apm.yml with all deps listed directly dependencies = {"apm": [ @@ -483,7 +490,7 @@ def test_dependency_order_lockfile_no_duplicates(self): def test_scan_dependency_primitives_with_transitive(self): """Test that scan_dependency_primitives finds transitive dep primitives.""" - from apm_cli.deps.lockfile import LockFile, LockedDependency + from apm_cli.deps.lockfile import LockedDependency, LockFile # Create apm.yml with only one direct dependency dependencies = {"apm": ["owner/direct-dep"]} diff --git a/tests/test_github_downloader.py b/tests/test_github_downloader.py index ec941ddf..0fd84178 100644 --- a/tests/test_github_downloader.py +++ b/tests/test_github_downloader.py @@ -1,25 +1,25 @@ """Tests for GitHub package downloader.""" import os -import pytest +import shutil import stat import tempfile -import shutil from pathlib import Path -from unittest.mock import Mock, patch, MagicMock +from unittest.mock import MagicMock, Mock, patch from urllib.parse import urlparse +import pytest import requests as requests_lib + from apm_cli.deps.github_downloader import GitHubPackageDownloader from apm_cli.models.apm_package import ( - DependencyReference, - ResolvedReference, + APMPackage, + DependencyReference, GitReferenceType, + ResolvedReference, ValidationResult, - APMPackage ) - _CRED_FILL_PATCH = patch( 'apm_cli.core.token_manager.GitHubTokenManager.resolve_credential_from_git', return_value=None, @@ -102,7 +102,7 @@ def test_resolve_git_reference_commit(self, mock_mkdtemp, mock_repo_class): mock_mkdtemp.return_value = mock_temp_dir from git.exc import GitCommandError - + # First call (shallow clone) fails, second call (full clone) succeeds mock_repo = Mock() mock_commit = Mock() @@ -718,9 +718,9 @@ def test_build_repo_url_for_ado_ssh(self): def test_build_ado_urls_with_spaces_in_project(self): """Test that URL builders properly encode spaces in ADO project names.""" from apm_cli.utils.github_host import ( + build_ado_api_url, build_ado_https_clone_url, build_ado_ssh_url, - build_ado_api_url, ) # HTTPS clone URL with token @@ -1149,6 +1149,7 @@ def test_rmtree_handles_readonly_files(self): def test_close_repo_none_is_safe(self): from apm_cli.deps.github_downloader import _close_repo + # Must not raise when passed None _close_repo(None) diff --git a/tests/test_github_downloader_token_precedence.py b/tests/test_github_downloader_token_precedence.py index dba9c79d..6f9673a6 100644 --- a/tests/test_github_downloader_token_precedence.py +++ b/tests/test_github_downloader_token_precedence.py @@ -2,10 +2,11 @@ import os from unittest.mock import patch + import pytest -from apm_cli.deps.github_downloader import GitHubPackageDownloader from apm_cli.core.token_manager import GitHubTokenManager +from apm_cli.deps.github_downloader import GitHubPackageDownloader from apm_cli.utils import github_host diff --git a/tests/test_lockfile.py b/tests/test_lockfile.py index 701ca6a5..e19dc7f5 100644 --- a/tests/test_lockfile.py +++ b/tests/test_lockfile.py @@ -1,11 +1,17 @@ """Tests for the APM lock file module.""" -import pytest from pathlib import Path from unittest.mock import Mock + +import pytest import yaml -from apm_cli.deps.lockfile import LockedDependency, LockFile, get_lockfile_path, migrate_lockfile_if_needed +from apm_cli.deps.lockfile import ( + LockedDependency, + LockFile, + get_lockfile_path, + migrate_lockfile_if_needed, +) from apm_cli.models.apm_package import DependencyReference diff --git a/tests/test_runnable_prompts.py b/tests/test_runnable_prompts.py index 693029c3..63d0a908 100644 --- a/tests/test_runnable_prompts.py +++ b/tests/test_runnable_prompts.py @@ -1,11 +1,12 @@ """Unit tests for runnable prompts feature.""" -import pytest -from pathlib import Path -from unittest.mock import Mock, patch, MagicMock -import tempfile -import shutil import os +import shutil +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, Mock, patch + +import pytest from apm_cli.core.script_runner import ScriptRunner diff --git a/tests/test_runtime_manager_token_precedence.py b/tests/test_runtime_manager_token_precedence.py index 1a118216..de26a2d5 100644 --- a/tests/test_runtime_manager_token_precedence.py +++ b/tests/test_runtime_manager_token_precedence.py @@ -2,7 +2,8 @@ import os import tempfile -from unittest.mock import patch, Mock +from unittest.mock import Mock, patch + import pytest from src.apm_cli.runtime.manager import RuntimeManager diff --git a/tests/test_token_manager.py b/tests/test_token_manager.py index 1ddb02cb..8b541435 100644 --- a/tests/test_token_manager.py +++ b/tests/test_token_manager.py @@ -2,7 +2,7 @@ import os import subprocess -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch import pytest diff --git a/tests/test_virtual_package_multi_install.py b/tests/test_virtual_package_multi_install.py index 4ef22750..88d3695e 100644 --- a/tests/test_virtual_package_multi_install.py +++ b/tests/test_virtual_package_multi_install.py @@ -1,7 +1,9 @@ """Tests for installing multiple virtual packages from the same repository.""" -import pytest from pathlib import Path + +import pytest + from src.apm_cli.deps.apm_resolver import APMDependencyResolver from src.apm_cli.models.apm_package import DependencyReference @@ -116,7 +118,7 @@ def test_dependency_tree_node_unique_ids(self): """Test that dependency tree nodes use unique keys as IDs.""" from src.apm_cli.deps.dependency_graph import DependencyNode from src.apm_cli.models.apm_package import APMPackage - + # Create two virtual packages from same repo dep_ref1 = DependencyReference.parse("owner/test-repo/prompts/file1.prompt.md") dep_ref2 = DependencyReference.parse("owner/test-repo/prompts/file2.prompt.md") @@ -137,7 +139,7 @@ def test_dependency_tree_node_unique_ids(self): def test_flat_dependency_map_uses_unique_keys(self): """Test that FlatDependencyMap properly uses unique keys for storage.""" from src.apm_cli.deps.dependency_graph import FlatDependencyMap - + # Create multiple virtual packages from same repo dep_ref1 = DependencyReference.parse("owner/test-repo/prompts/file1.prompt.md") dep_ref2 = DependencyReference.parse("owner/test-repo/prompts/file2.prompt.md") @@ -179,7 +181,7 @@ def test_no_false_conflicts_for_virtual_packages(self): def test_actual_conflict_detection_still_works(self): """Ensure real conflicts (same unique key) are still detected.""" from src.apm_cli.deps.dependency_graph import FlatDependencyMap - + # Same package, different references (this is a real conflict) dep_ref1 = DependencyReference.parse("github/design-guidelines#main") dep_ref2 = DependencyReference.parse("github/design-guidelines#v1.0.0") diff --git a/tests/unit/compilation/test_claude_formatter.py b/tests/unit/compilation/test_claude_formatter.py index 2884cf6f..8a15fc83 100644 --- a/tests/unit/compilation/test_claude_formatter.py +++ b/tests/unit/compilation/test_claude_formatter.py @@ -1,22 +1,22 @@ """Unit tests for ClaudeFormatter - CLAUDE.md generation and commands.""" -import tempfile import shutil +import tempfile from pathlib import Path import pytest from apm_cli.compilation.claude_formatter import ( + CLAUDE_HEADER, + ClaudeCompilationResult, ClaudeFormatter, ClaudePlacement, - ClaudeCompilationResult, CommandGenerationResult, format_claude_md, generate_claude_commands, - CLAUDE_HEADER, ) from apm_cli.compilation.constants import BUILD_ID_PLACEHOLDER -from apm_cli.primitives.models import Instruction, Chatmode, PrimitiveCollection +from apm_cli.primitives.models import Chatmode, Instruction, PrimitiveCollection from apm_cli.version import get_version diff --git a/tests/unit/compilation/test_compilation.py b/tests/unit/compilation/test_compilation.py index 2d18fab7..1a3e93aa 100644 --- a/tests/unit/compilation/test_compilation.py +++ b/tests/unit/compilation/test_compilation.py @@ -3,22 +3,23 @@ import os import tempfile import unittest -import yaml from pathlib import Path from unittest.mock import patch -from apm_cli.compilation.template_builder import ( - build_conditional_sections, +import yaml + +from apm_cli.compilation.agents_compiler import ( + AgentsCompiler, + CompilationConfig, + compile_agents_md, ) from apm_cli.compilation.link_resolver import ( validate_link_targets, ) -from apm_cli.compilation.agents_compiler import ( - AgentsCompiler, - CompilationConfig, - compile_agents_md +from apm_cli.compilation.template_builder import ( + build_conditional_sections, ) -from apm_cli.primitives.models import Instruction, Chatmode, PrimitiveCollection +from apm_cli.primitives.models import Chatmode, Instruction, PrimitiveCollection class TestTemplateBuilder(unittest.TestCase): @@ -313,8 +314,11 @@ def tearDown(self): def test_validate_mode_with_valid_primitives(self): """Test validation mode with valid primitives.""" - from apm_cli.commands.compile import _display_validation_errors, _get_validation_suggestion - + from apm_cli.commands.compile import ( + _display_validation_errors, + _get_validation_suggestion, + ) + # Test validation suggestion function suggestion = _get_validation_suggestion("Missing 'description' in frontmatter") self.assertIn("Add 'description:", suggestion) @@ -327,8 +331,11 @@ def test_validate_mode_with_valid_primitives(self): def test_validation_error_display(self): """Test validation error display functionality.""" - from apm_cli.commands.compile import _display_validation_errors, _get_validation_suggestion - + from apm_cli.commands.compile import ( + _display_validation_errors, + _get_validation_suggestion, + ) + # Test with mock errors errors = [ "test.md: Missing 'description' in frontmatter", @@ -343,9 +350,10 @@ def test_validation_error_display(self): def test_compilation_config_from_apm_yml(self): """Test CompilationConfig loading from apm.yml.""" - from apm_cli.compilation.agents_compiler import CompilationConfig import yaml - + + from apm_cli.compilation.agents_compiler import CompilationConfig + # Create test apm.yml test_config = { 'compilation': { diff --git a/tests/unit/compilation/test_compile_target_flag.py b/tests/unit/compilation/test_compile_target_flag.py index 7178d124..4f3432d9 100644 --- a/tests/unit/compilation/test_compile_target_flag.py +++ b/tests/unit/compilation/test_compile_target_flag.py @@ -10,9 +10,9 @@ - _merge_results() correctly combines results """ -import tempfile -import shutil import os +import shutil +import tempfile from pathlib import Path import pytest diff --git a/tests/unit/compilation/test_constitution_injector.py b/tests/unit/compilation/test_constitution_injector.py index a8fc0137..bf212b08 100644 --- a/tests/unit/compilation/test_constitution_injector.py +++ b/tests/unit/compilation/test_constitution_injector.py @@ -7,6 +7,11 @@ import pytest +from apm_cli.compilation.constants import ( + CONSTITUTION_MARKER_BEGIN, + CONSTITUTION_MARKER_END, + CONSTITUTION_RELATIVE_PATH, +) from apm_cli.compilation.constitution import clear_constitution_cache from apm_cli.compilation.constitution_block import ( ExistingBlock, @@ -15,11 +20,6 @@ inject_or_update, render_block, ) -from apm_cli.compilation.constants import ( - CONSTITUTION_MARKER_BEGIN, - CONSTITUTION_MARKER_END, - CONSTITUTION_RELATIVE_PATH, -) from apm_cli.compilation.injector import ConstitutionInjector _BEGIN = CONSTITUTION_MARKER_BEGIN diff --git a/tests/unit/compilation/test_context_optimizer.py b/tests/unit/compilation/test_context_optimizer.py index e7c637b9..bef779f1 100644 --- a/tests/unit/compilation/test_context_optimizer.py +++ b/tests/unit/compilation/test_context_optimizer.py @@ -4,18 +4,19 @@ loaded by agents working in specific directories. """ +import fnmatch import os import tempfile -import pytest -import fnmatch from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + +import pytest from apm_cli.compilation.context_optimizer import ( ContextOptimizer, DirectoryAnalysis, InheritanceAnalysis, - PlacementCandidate + PlacementCandidate, ) from apm_cli.primitives.models import Instruction diff --git a/tests/unit/compilation/test_coverage_guarantees.py b/tests/unit/compilation/test_coverage_guarantees.py index 9b96fd45..830944b9 100644 --- a/tests/unit/compilation/test_coverage_guarantees.py +++ b/tests/unit/compilation/test_coverage_guarantees.py @@ -9,10 +9,11 @@ import os import tempfile -import pytest from pathlib import Path from unittest.mock import patch +import pytest + from apm_cli.compilation.context_optimizer import ContextOptimizer from apm_cli.primitives.models import Instruction diff --git a/tests/unit/compilation/test_link_resolver.py b/tests/unit/compilation/test_link_resolver.py index bc1118e7..19fa9d8e 100644 --- a/tests/unit/compilation/test_link_resolver.py +++ b/tests/unit/compilation/test_link_resolver.py @@ -3,20 +3,15 @@ Following TDD approach - tests written before implementation. """ -import pytest +import re from pathlib import Path from textwrap import dedent -import re from urllib.parse import urlparse -from apm_cli.compilation.link_resolver import ( - UnifiedLinkResolver, - LinkResolutionContext -) -from apm_cli.primitives.models import ( - PrimitiveCollection, - Context -) +import pytest + +from apm_cli.compilation.link_resolver import LinkResolutionContext, UnifiedLinkResolver +from apm_cli.primitives.models import Context, PrimitiveCollection @pytest.fixture diff --git a/tests/unit/compilation/test_mathematical_guarantees.py b/tests/unit/compilation/test_mathematical_guarantees.py index 8b57e299..fc7a9795 100644 --- a/tests/unit/compilation/test_mathematical_guarantees.py +++ b/tests/unit/compilation/test_mathematical_guarantees.py @@ -1,10 +1,11 @@ """Tests for mathematical guarantees and coverage constraints.""" -import pytest import tempfile from pathlib import Path from unittest.mock import MagicMock +import pytest + from apm_cli.compilation.context_optimizer import ContextOptimizer from apm_cli.primitives.models import Instruction diff --git a/tests/unit/compilation/test_mathematical_optimization.py b/tests/unit/compilation/test_mathematical_optimization.py index 2ae4b23e..888084c3 100644 --- a/tests/unit/compilation/test_mathematical_optimization.py +++ b/tests/unit/compilation/test_mathematical_optimization.py @@ -6,14 +6,15 @@ import os import tempfile -import pytest from pathlib import Path from unittest.mock import patch +import pytest + from apm_cli.compilation.context_optimizer import ( ContextOptimizer, DirectoryAnalysis, - PlacementCandidate + PlacementCandidate, ) from apm_cli.primitives.models import Instruction diff --git a/tests/unit/compilation/test_sibling_directory_coverage.py b/tests/unit/compilation/test_sibling_directory_coverage.py index 9fd11b10..4045adf5 100644 --- a/tests/unit/compilation/test_sibling_directory_coverage.py +++ b/tests/unit/compilation/test_sibling_directory_coverage.py @@ -12,9 +12,10 @@ """ import tempfile -import pytest from pathlib import Path +import pytest + from apm_cli.compilation.context_optimizer import ContextOptimizer from apm_cli.primitives.models import Instruction diff --git a/tests/unit/core/test_target_detection.py b/tests/unit/core/test_target_detection.py index 90487a75..14d6e230 100644 --- a/tests/unit/core/test_target_detection.py +++ b/tests/unit/core/test_target_detection.py @@ -2,13 +2,13 @@ from apm_cli.core.target_detection import ( detect_target, - should_integrate_vscode, + get_target_description, + should_compile_agents_md, + should_compile_claude_md, should_integrate_claude, should_integrate_cursor, should_integrate_opencode, - should_compile_agents_md, - should_compile_claude_md, - get_target_description, + should_integrate_vscode, ) diff --git a/tests/unit/integration/test_agent_integrator.py b/tests/unit/integration/test_agent_integrator.py index 635446a5..f7d8f873 100644 --- a/tests/unit/integration/test_agent_integrator.py +++ b/tests/unit/integration/test_agent_integrator.py @@ -1,12 +1,17 @@ """Tests for agent integration functionality.""" import tempfile +from datetime import datetime from pathlib import Path from unittest.mock import Mock -from datetime import datetime from apm_cli.integration import AgentIntegrator -from apm_cli.models.apm_package import PackageInfo, APMPackage, ResolvedReference, GitReferenceType +from apm_cli.models.apm_package import ( + APMPackage, + GitReferenceType, + PackageInfo, + ResolvedReference, +) class TestAgentIntegrator: diff --git a/tests/unit/integration/test_command_integrator.py b/tests/unit/integration/test_command_integrator.py index 1c58b9f1..b35b7165 100644 --- a/tests/unit/integration/test_command_integrator.py +++ b/tests/unit/integration/test_command_integrator.py @@ -7,14 +7,14 @@ - Removal of all APM command files """ -import tempfile import shutil +import tempfile +from dataclasses import dataclass from pathlib import Path from unittest.mock import MagicMock -from dataclasses import dataclass -import pytest import frontmatter +import pytest from apm_cli.integration.command_integrator import CommandIntegrator diff --git a/tests/unit/integration/test_deployed_files_manifest.py b/tests/unit/integration/test_deployed_files_manifest.py index 9cd9ba1f..57c792f2 100644 --- a/tests/unit/integration/test_deployed_files_manifest.py +++ b/tests/unit/integration/test_deployed_files_manifest.py @@ -13,24 +13,25 @@ """ import json -import pytest from datetime import datetime from pathlib import Path +import pytest + from apm_cli.deps.lockfile import LockedDependency, LockFile -from apm_cli.integration.base_integrator import BaseIntegrator -from apm_cli.integration.prompt_integrator import PromptIntegrator from apm_cli.integration.agent_integrator import AgentIntegrator +from apm_cli.integration.base_integrator import BaseIntegrator from apm_cli.integration.command_integrator import CommandIntegrator from apm_cli.integration.hook_integrator import HookIntegrator +from apm_cli.integration.prompt_integrator import PromptIntegrator from apm_cli.integration.skill_integrator import SkillIntegrator from apm_cli.models.apm_package import ( APMPackage, + GitReferenceType, PackageInfo, ResolvedReference, - GitReferenceType, ) -from apm_cli.utils.diagnostics import DiagnosticCollector, CATEGORY_COLLISION +from apm_cli.utils.diagnostics import CATEGORY_COLLISION, DiagnosticCollector def _make_package_info(tmp_path: Path, name: str = "test-pkg", diff --git a/tests/unit/integration/test_hook_integrator.py b/tests/unit/integration/test_hook_integrator.py index 45d110de..77d104da 100644 --- a/tests/unit/integration/test_hook_integrator.py +++ b/tests/unit/integration/test_hook_integrator.py @@ -10,14 +10,14 @@ """ import json -import tempfile import shutil +import tempfile from pathlib import Path from unittest.mock import MagicMock import pytest -from apm_cli.integration.hook_integrator import HookIntegrator, HookIntegrationResult +from apm_cli.integration.hook_integrator import HookIntegrationResult, HookIntegrator from apm_cli.models.apm_package import APMPackage, PackageInfo diff --git a/tests/unit/integration/test_instruction_integrator.py b/tests/unit/integration/test_instruction_integrator.py index 862d172a..caf71834 100644 --- a/tests/unit/integration/test_instruction_integrator.py +++ b/tests/unit/integration/test_instruction_integrator.py @@ -1,15 +1,21 @@ """Tests for instruction integration functionality.""" -import pytest -import tempfile import shutil +import tempfile +from datetime import datetime from pathlib import Path from unittest.mock import Mock -from datetime import datetime -from apm_cli.integration.instruction_integrator import InstructionIntegrator +import pytest + from apm_cli.integration.base_integrator import IntegrationResult -from apm_cli.models.apm_package import PackageInfo, APMPackage, ResolvedReference, GitReferenceType +from apm_cli.integration.instruction_integrator import InstructionIntegrator +from apm_cli.models.apm_package import ( + APMPackage, + GitReferenceType, + PackageInfo, + ResolvedReference, +) def _make_package_info(package_dir, name="test-pkg"): diff --git a/tests/unit/integration/test_prompt_integrator.py b/tests/unit/integration/test_prompt_integrator.py index cf74071b..8d7cd536 100644 --- a/tests/unit/integration/test_prompt_integrator.py +++ b/tests/unit/integration/test_prompt_integrator.py @@ -1,14 +1,20 @@ """Tests for prompt integration functionality.""" -import pytest -import tempfile import os +import tempfile +from datetime import datetime from pathlib import Path from unittest.mock import Mock, patch -from datetime import datetime + +import pytest from apm_cli.integration import PromptIntegrator -from apm_cli.models.apm_package import PackageInfo, APMPackage, ResolvedReference, GitReferenceType +from apm_cli.models.apm_package import ( + APMPackage, + GitReferenceType, + PackageInfo, + ResolvedReference, +) class TestPromptIntegrator: diff --git a/tests/unit/integration/test_skill_integrator.py b/tests/unit/integration/test_skill_integrator.py index 0956b2fe..eb094cfe 100644 --- a/tests/unit/integration/test_skill_integrator.py +++ b/tests/unit/integration/test_skill_integrator.py @@ -1,13 +1,28 @@ """Tests for skill integration functionality (Claude Code SKILL.md support).""" -import tempfile import shutil +import tempfile +from datetime import datetime from pathlib import Path from unittest.mock import Mock, patch -from datetime import datetime -from apm_cli.integration.skill_integrator import SkillIntegrator, SkillIntegrationResult, to_hyphen_case, validate_skill_name, normalize_skill_name, copy_skill_to_target -from apm_cli.models.apm_package import PackageInfo, APMPackage, ResolvedReference, GitReferenceType, DependencyReference, PackageType, PackageContentType +from apm_cli.integration.skill_integrator import ( + SkillIntegrationResult, + SkillIntegrator, + copy_skill_to_target, + normalize_skill_name, + to_hyphen_case, + validate_skill_name, +) +from apm_cli.models.apm_package import ( + APMPackage, + DependencyReference, + GitReferenceType, + PackageContentType, + PackageInfo, + PackageType, + ResolvedReference, +) class TestToHyphenCase: @@ -2544,7 +2559,7 @@ def test_cross_package_overwrite_records_diagnostic(self): # Managed files includes this skill → it's APM-managed managed_files = {".github/skills/shared-skill"} - from apm_cli.utils.diagnostics import DiagnosticCollector, CATEGORY_OVERWRITE + from apm_cli.utils.diagnostics import CATEGORY_OVERWRITE, DiagnosticCollector diag = DiagnosticCollector() with patch("apm_cli.utils.console._rich_warning") as mock_warning: diff --git a/tests/unit/integration/test_skill_transformer.py b/tests/unit/integration/test_skill_transformer.py index fdd226e0..725411d9 100644 --- a/tests/unit/integration/test_skill_transformer.py +++ b/tests/unit/integration/test_skill_transformer.py @@ -6,8 +6,8 @@ See skill-strategy.md for architectural rationale (T5). """ -import tempfile import shutil +import tempfile from pathlib import Path from apm_cli.integration.skill_transformer import SkillTransformer, to_hyphen_case diff --git a/tests/unit/integration/test_sync_integration_url_normalization.py b/tests/unit/integration/test_sync_integration_url_normalization.py index 068f66f6..9d44d6f3 100644 --- a/tests/unit/integration/test_sync_integration_url_normalization.py +++ b/tests/unit/integration/test_sync_integration_url_normalization.py @@ -8,7 +8,7 @@ from pathlib import Path from unittest.mock import Mock -from apm_cli.integration import PromptIntegrator, AgentIntegrator +from apm_cli.integration import AgentIntegrator, PromptIntegrator class TestSyncIntegrationURLNormalization: diff --git a/tests/unit/policy/test_ci_checks.py b/tests/unit/policy/test_ci_checks.py index c3175611..5624a8cb 100644 --- a/tests/unit/policy/test_ci_checks.py +++ b/tests/unit/policy/test_ci_checks.py @@ -7,6 +7,7 @@ import pytest +from apm_cli.models.apm_package import clear_apm_yml_cache from apm_cli.policy.ci_checks import ( _check_config_consistency, _check_content_integrity, @@ -16,9 +17,7 @@ _check_ref_consistency, run_baseline_checks, ) -from apm_cli.policy.models import CIAuditResult, CheckResult -from apm_cli.models.apm_package import clear_apm_yml_cache - +from apm_cli.policy.models import CheckResult, CIAuditResult # -- Helpers -------------------------------------------------------- @@ -125,8 +124,8 @@ def test_pass_refs_match(self, tmp_path): deployed_files: [] """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -146,8 +145,8 @@ def test_fail_ref_mismatch(self, tmp_path): deployed_files: [] """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -165,8 +164,8 @@ def test_fail_dep_not_in_lockfile(self, tmp_path): dependencies: [] """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -234,8 +233,8 @@ def test_pass_no_orphans(self, tmp_path): deployed_files: [] """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -256,8 +255,8 @@ def test_fail_orphan_in_lockfile(self, tmp_path): deployed_files: [] """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -282,8 +281,8 @@ def test_pass_no_mcp(self, tmp_path): deployed_files: [] """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -303,8 +302,8 @@ def test_pass_mcp_configs_match(self, tmp_path): name: my-server """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) @@ -328,8 +327,8 @@ def test_fail_mcp_config_drift(self, tmp_path): name: my-server """), ) - from apm_cli.models.apm_package import APMPackage from apm_cli.deps.lockfile import LockFile, get_lockfile_path + from apm_cli.models.apm_package import APMPackage manifest = APMPackage.from_apm_yml(tmp_path / "apm.yml") lock = LockFile.read(get_lockfile_path(tmp_path)) diff --git a/tests/unit/policy/test_inheritance.py b/tests/unit/policy/test_inheritance.py index cf81a2ff..54a751ef 100644 --- a/tests/unit/policy/test_inheritance.py +++ b/tests/unit/policy/test_inheritance.py @@ -4,6 +4,14 @@ import unittest +from apm_cli.policy.inheritance import ( + MAX_CHAIN_DEPTH, + PolicyInheritanceError, + detect_cycle, + merge_policies, + resolve_policy_chain, + validate_chain_depth, +) from apm_cli.policy.schema import ( ApmPolicy, CompilationPolicy, @@ -16,14 +24,6 @@ PolicyCache, UnmanagedFilesPolicy, ) -from apm_cli.policy.inheritance import ( - MAX_CHAIN_DEPTH, - PolicyInheritanceError, - detect_cycle, - merge_policies, - resolve_policy_chain, - validate_chain_depth, -) class TestEnforcementEscalation(unittest.TestCase): diff --git a/tests/unit/policy/test_policy_checks.py b/tests/unit/policy/test_policy_checks.py index 9f7b096e..c153f56e 100644 --- a/tests/unit/policy/test_policy_checks.py +++ b/tests/unit/policy/test_policy_checks.py @@ -9,7 +9,7 @@ import yaml from apm_cli.models.apm_package import clear_apm_yml_cache -from apm_cli.policy.models import CIAuditResult, CheckResult +from apm_cli.policy.models import CheckResult, CIAuditResult from apm_cli.policy.policy_checks import ( _check_compilation_strategy, _check_compilation_target, @@ -41,7 +41,6 @@ UnmanagedFilesPolicy, ) - # ── Helpers ──────────────────────────────────────────────────────── @@ -81,7 +80,7 @@ def _make_mcp_deps(mcp_list: list): def _make_lockfile(deps_data: list[dict]): """Create a LockFile from a list of dependency dicts.""" - from apm_cli.deps.lockfile import LockFile, LockedDependency + from apm_cli.deps.lockfile import LockedDependency, LockFile lock = LockFile() for d in deps_data: diff --git a/tests/unit/test_ado_path_structure.py b/tests/unit/test_ado_path_structure.py index 8636719d..3631bc8f 100644 --- a/tests/unit/test_ado_path_structure.py +++ b/tests/unit/test_ado_path_structure.py @@ -7,18 +7,18 @@ 4. Compilation finds and processes ADO dependency primitives """ -import pytest import tempfile from pathlib import Path +import pytest + from apm_cli.models.apm_package import DependencyReference from apm_cli.primitives.discovery import ( discover_primitives_with_dependencies, - get_dependency_declaration_order + get_dependency_declaration_order, ) - class TestADOPathStructure: """Test ADO vs GitHub path structure handling.""" @@ -246,8 +246,11 @@ def _create_instruction_file(self, file_path: Path, apply_to: str, content: str) def test_compile_generates_agents_md_from_ado_deps(self, temp_project): """Test that compile generates AGENTS.md with content from ADO dependencies.""" - from apm_cli.compilation.agents_compiler import AgentsCompiler, CompilationConfig - + from apm_cli.compilation.agents_compiler import ( + AgentsCompiler, + CompilationConfig, + ) + # Create apm.yml with ADO dependency self._create_apm_yml(temp_project, ["dev.azure.com/myorg/myproj/_git/compliance"]) @@ -275,8 +278,11 @@ def test_compile_generates_agents_md_from_ado_deps(self, temp_project): def test_compile_with_both_github_and_ado_deps(self, temp_project): """Test compilation with both GitHub and ADO dependencies.""" - from apm_cli.compilation.agents_compiler import AgentsCompiler, CompilationConfig - + from apm_cli.compilation.agents_compiler import ( + AgentsCompiler, + CompilationConfig, + ) + # Create apm.yml with both self._create_apm_yml(temp_project, [ "owner/github-pkg", diff --git a/tests/unit/test_audit_ci_command.py b/tests/unit/test_audit_ci_command.py index f2caffe0..0492d4ae 100644 --- a/tests/unit/test_audit_ci_command.py +++ b/tests/unit/test_audit_ci_command.py @@ -13,7 +13,6 @@ from apm_cli.commands.audit import audit from apm_cli.models.apm_package import clear_apm_yml_cache - # -- Fixtures ------------------------------------------------------- diff --git a/tests/unit/test_audit_command.py b/tests/unit/test_audit_command.py index 810a8aa7..6f881991 100644 --- a/tests/unit/test_audit_command.py +++ b/tests/unit/test_audit_command.py @@ -6,11 +6,15 @@ import pytest from click.testing import CliRunner -from apm_cli.commands.audit import audit, _scan_single_file, _apply_strip, _preview_strip +from apm_cli.commands.audit import ( + _apply_strip, + _preview_strip, + _scan_single_file, + audit, +) from apm_cli.core.command_logger import CommandLogger from apm_cli.security.content_scanner import ContentScanner - # ── Fixtures ──────────────────────────────────────────────────────── _logger = CommandLogger("audit", verbose=False) diff --git a/tests/unit/test_audit_policy_command.py b/tests/unit/test_audit_policy_command.py index 9ef86a9e..60aa57bb 100644 --- a/tests/unit/test_audit_policy_command.py +++ b/tests/unit/test_audit_policy_command.py @@ -15,7 +15,6 @@ from apm_cli.policy.discovery import PolicyFetchResult from apm_cli.policy.schema import ApmPolicy - # -- Fixtures ------------------------------------------------------- diff --git a/tests/unit/test_auth.py b/tests/unit/test_auth.py index 9daa10d0..cab4df8d 100644 --- a/tests/unit/test_auth.py +++ b/tests/unit/test_auth.py @@ -5,10 +5,9 @@ import pytest -from apm_cli.core.auth import AuthResolver, HostInfo, AuthContext +from apm_cli.core.auth import AuthContext, AuthResolver, HostInfo from apm_cli.core.token_manager import GitHubTokenManager - # --------------------------------------------------------------------------- # TestClassifyHost # --------------------------------------------------------------------------- diff --git a/tests/unit/test_auth_scoping.py b/tests/unit/test_auth_scoping.py index dc065642..b62852cc 100644 --- a/tests/unit/test_auth_scoping.py +++ b/tests/unit/test_auth_scoping.py @@ -10,15 +10,14 @@ import sys import tempfile from pathlib import Path -from unittest.mock import Mock, patch, MagicMock +from unittest.mock import MagicMock, Mock, patch from urllib.parse import urlparse import pytest from git.exc import GitCommandError from apm_cli.deps.github_downloader import GitHubPackageDownloader -from apm_cli.models.apm_package import DependencyReference, APMPackage - +from apm_cli.models.apm_package import APMPackage, DependencyReference # --------------------------------------------------------------------------- # Helpers diff --git a/tests/unit/test_build_sha.py b/tests/unit/test_build_sha.py index 71c0a374..bed46e69 100644 --- a/tests/unit/test_build_sha.py +++ b/tests/unit/test_build_sha.py @@ -2,7 +2,7 @@ import subprocess import unittest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from apm_cli.version import get_build_sha diff --git a/tests/unit/test_canonicalization.py b/tests/unit/test_canonicalization.py index 75a8b81e..77e768d1 100644 --- a/tests/unit/test_canonicalization.py +++ b/tests/unit/test_canonicalization.py @@ -9,12 +9,12 @@ - only_packages filter in _install_apm_dependencies """ -import pytest -from unittest.mock import patch, MagicMock from pathlib import Path +from unittest.mock import MagicMock, patch -from apm_cli.models.apm_package import DependencyReference +import pytest +from apm_cli.models.apm_package import DependencyReference # ── to_canonical() ────────────────────────────────────────────────────────── diff --git a/tests/unit/test_cli_encoding.py b/tests/unit/test_cli_encoding.py index 420cd1fa..e15f31f5 100644 --- a/tests/unit/test_cli_encoding.py +++ b/tests/unit/test_cli_encoding.py @@ -1,6 +1,6 @@ """Tests for CLI entry point encoding configuration.""" -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from apm_cli.cli import _configure_encoding diff --git a/tests/unit/test_codex_runtime.py b/tests/unit/test_codex_runtime.py index 3517fc55..fb66d3db 100644 --- a/tests/unit/test_codex_runtime.py +++ b/tests/unit/test_codex_runtime.py @@ -1,7 +1,9 @@ """Test Codex runtime adapter.""" +from unittest.mock import MagicMock, Mock, patch + import pytest -from unittest.mock import Mock, patch, MagicMock + from apm_cli.runtime.codex_runtime import CodexRuntime diff --git a/tests/unit/test_command_logger.py b/tests/unit/test_command_logger.py index 3939f9c3..fecb7b8c 100644 --- a/tests/unit/test_command_logger.py +++ b/tests/unit/test_command_logger.py @@ -452,6 +452,7 @@ class TestVerboseFlagAcceptance: def test_uninstall_accepts_verbose_flag(self): from click.testing import CliRunner + from apm_cli.commands.uninstall.cli import uninstall runner = CliRunner() diff --git a/tests/unit/test_compile_rich_output.py b/tests/unit/test_compile_rich_output.py index d4e67a6e..5cdefd94 100644 --- a/tests/unit/test_compile_rich_output.py +++ b/tests/unit/test_compile_rich_output.py @@ -4,7 +4,10 @@ import sys from pathlib import Path -from ..utils.constitution_fixtures import temp_project_with_constitution, DEFAULT_CONSTITUTION +from ..utils.constitution_fixtures import ( + DEFAULT_CONSTITUTION, + temp_project_with_constitution, +) CLI = [sys.executable, "-m", "apm_cli.cli", "compile", "--single-agents"] diff --git a/tests/unit/test_conflict_detection.py b/tests/unit/test_conflict_detection.py index 180f9d2f..2eaf9a8d 100644 --- a/tests/unit/test_conflict_detection.py +++ b/tests/unit/test_conflict_detection.py @@ -2,8 +2,9 @@ import unittest from unittest.mock import Mock, patch -from apm_cli.core.conflict_detector import MCPConflictDetector + from apm_cli.adapters.client.base import MCPClientAdapter +from apm_cli.core.conflict_detector import MCPConflictDetector class TestMCPConflictDetection(unittest.TestCase): diff --git a/tests/unit/test_content_hash.py b/tests/unit/test_content_hash.py index c5433f63..c2d82403 100644 --- a/tests/unit/test_content_hash.py +++ b/tests/unit/test_content_hash.py @@ -7,7 +7,6 @@ from apm_cli.utils.content_hash import compute_package_hash, verify_package_hash - # --------------------------------------------------------------------------- # compute_package_hash # --------------------------------------------------------------------------- @@ -218,7 +217,7 @@ def test_content_hash_none_not_emitted(self): def test_content_hash_roundtrip_yaml(self, tmp_path): """content_hash survives a full write/read YAML cycle.""" - from apm_cli.deps.lockfile import LockFile, LockedDependency + from apm_cli.deps.lockfile import LockedDependency, LockFile lockfile = LockFile(apm_version="test") dep = LockedDependency( repo_url="owner/repo", diff --git a/tests/unit/test_copilot_runtime.py b/tests/unit/test_copilot_runtime.py index 3f80268c..012925f5 100644 --- a/tests/unit/test_copilot_runtime.py +++ b/tests/unit/test_copilot_runtime.py @@ -1,7 +1,9 @@ """Test Copilot Runtime.""" -import pytest from unittest.mock import Mock, patch + +import pytest + from apm_cli.runtime.copilot_runtime import CopilotRuntime diff --git a/tests/unit/test_dev_dependencies.py b/tests/unit/test_dev_dependencies.py index 4e4de703..baf12abb 100644 --- a/tests/unit/test_dev_dependencies.py +++ b/tests/unit/test_dev_dependencies.py @@ -15,7 +15,6 @@ from apm_cli.models.apm_package import APMPackage, DependencyReference from apm_cli.models.results import InstallResult - # --------------------------------------------------------------------------- # Part 3d: LockedDependency.is_dev field # --------------------------------------------------------------------------- diff --git a/tests/unit/test_docker_args.py b/tests/unit/test_docker_args.py index 605a0d82..eebaa3cb 100644 --- a/tests/unit/test_docker_args.py +++ b/tests/unit/test_docker_args.py @@ -1,6 +1,7 @@ """Tests for Docker arguments deduplication.""" import unittest + from apm_cli.core.docker_args import DockerArgsProcessor diff --git a/tests/unit/test_docker_args_and_installer.py b/tests/unit/test_docker_args_and_installer.py index af07eb25..77d75a88 100644 --- a/tests/unit/test_docker_args_and_installer.py +++ b/tests/unit/test_docker_args_and_installer.py @@ -1,9 +1,10 @@ """Tests for Docker arguments processing and safe installation.""" import unittest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + from apm_cli.core.docker_args import DockerArgsProcessor -from apm_cli.core.safe_installer import SafeMCPInstaller, InstallationSummary +from apm_cli.core.safe_installer import InstallationSummary, SafeMCPInstaller class TestDockerArgsProcessor(unittest.TestCase): diff --git a/tests/unit/test_github_host.py b/tests/unit/test_github_host.py index 90bc8a71..4606a02b 100644 --- a/tests/unit/test_github_host.py +++ b/tests/unit/test_github_host.py @@ -1,6 +1,6 @@ import pytest -from apm_cli.utils.github_host import is_valid_fqdn, build_raw_content_url +from apm_cli.utils.github_host import build_raw_content_url, is_valid_fqdn def test_build_raw_content_url(): diff --git a/tests/unit/test_init_command.py b/tests/unit/test_init_command.py index af21b9e7..2c17571d 100644 --- a/tests/unit/test_init_command.py +++ b/tests/unit/test_init_command.py @@ -1,14 +1,15 @@ """Tests for the apm init command.""" import json -import pytest -import tempfile import os -import yaml +import tempfile from pathlib import Path -from click.testing import CliRunner from unittest.mock import patch +import pytest +import yaml +from click.testing import CliRunner + from apm_cli.cli import cli diff --git a/tests/unit/test_init_plugin.py b/tests/unit/test_init_plugin.py index 1b27237e..e125e456 100644 --- a/tests/unit/test_init_plugin.py +++ b/tests/unit/test_init_plugin.py @@ -16,7 +16,6 @@ from apm_cli.cli import cli from apm_cli.commands._helpers import _validate_plugin_name - # --------------------------------------------------------------------------- # CLI integration tests # --------------------------------------------------------------------------- diff --git a/tests/unit/test_install_command.py b/tests/unit/test_install_command.py index 52ab9d55..d0768f9b 100644 --- a/tests/unit/test_install_command.py +++ b/tests/unit/test_install_command.py @@ -1,17 +1,17 @@ """Tests for the apm install command auto-bootstrap feature.""" import contextlib -import pytest -import tempfile import os -import yaml +import tempfile from pathlib import Path -from click.testing import CliRunner -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch -from apm_cli.models.results import InstallResult +import pytest +import yaml +from click.testing import CliRunner from apm_cli.cli import cli +from apm_cli.models.results import InstallResult class TestInstallCommandAutoBootstrap: diff --git a/tests/unit/test_install_output.py b/tests/unit/test_install_output.py index 8fa9bf05..e65f50e4 100644 --- a/tests/unit/test_install_output.py +++ b/tests/unit/test_install_output.py @@ -1,12 +1,13 @@ """Tests for install command output formatting: resolved refs and pinning hints.""" -import pytest from unittest.mock import MagicMock +import pytest + from apm_cli.models.dependency import ( DependencyReference, - ResolvedReference, GitReferenceType, + ResolvedReference, ) diff --git a/tests/unit/test_install_scanning.py b/tests/unit/test_install_scanning.py index 6e672b25..400e4e11 100644 --- a/tests/unit/test_install_scanning.py +++ b/tests/unit/test_install_scanning.py @@ -258,7 +258,10 @@ def test_compilation_result_propagates_critical(self): assert r.has_critical_security is True def test_merge_results_propagates_critical(self): - from apm_cli.compilation.agents_compiler import AgentsCompiler, CompilationResult + from apm_cli.compilation.agents_compiler import ( + AgentsCompiler, + CompilationResult, + ) clean = CompilationResult( success=True, output_path="a.md", content="clean", warnings=[], errors=[], stats={}, @@ -273,7 +276,10 @@ def test_merge_results_propagates_critical(self): assert merged.has_critical_security is True def test_merge_results_clean_stays_clean(self): - from apm_cli.compilation.agents_compiler import AgentsCompiler, CompilationResult + from apm_cli.compilation.agents_compiler import ( + AgentsCompiler, + CompilationResult, + ) r1 = CompilationResult( success=True, output_path="a.md", content="ok", warnings=[], errors=[], stats={}, diff --git a/tests/unit/test_install_update.py b/tests/unit/test_install_update.py index d260b391..bede9dce 100644 --- a/tests/unit/test_install_update.py +++ b/tests/unit/test_install_update.py @@ -11,7 +11,12 @@ from unittest.mock import Mock -from apm_cli.drift import build_download_ref, detect_config_drift, detect_orphans, detect_ref_change +from apm_cli.drift import ( + build_download_ref, + detect_config_drift, + detect_orphans, + detect_ref_change, +) from apm_cli.models.apm_package import DependencyReference diff --git a/tests/unit/test_list_command.py b/tests/unit/test_list_command.py index 657f77c4..f7b7b1f9 100644 --- a/tests/unit/test_list_command.py +++ b/tests/unit/test_list_command.py @@ -1,8 +1,9 @@ """Tests for the apm list command.""" -from click.testing import CliRunner from unittest.mock import patch +from click.testing import CliRunner + from apm_cli.commands.list_cmd import list as list_command diff --git a/tests/unit/test_llm_runtime.py b/tests/unit/test_llm_runtime.py index 46c0209c..d2f80ffa 100644 --- a/tests/unit/test_llm_runtime.py +++ b/tests/unit/test_llm_runtime.py @@ -1,7 +1,9 @@ """Test LLM runtime integration.""" -import pytest from unittest.mock import Mock, patch + +import pytest + from apm_cli.runtime.llm_runtime import LLMRuntime diff --git a/tests/unit/test_local_deps.py b/tests/unit/test_local_deps.py index 323d2300..c6b52c8b 100644 --- a/tests/unit/test_local_deps.py +++ b/tests/unit/test_local_deps.py @@ -1,13 +1,13 @@ """Unit tests for local filesystem path dependency support.""" -import pytest -import yaml from pathlib import Path from unittest.mock import Mock -from apm_cli.models.apm_package import DependencyReference, APMPackage -from apm_cli.deps.lockfile import LockedDependency, LockFile +import pytest +import yaml +from apm_cli.deps.lockfile import LockedDependency, LockFile +from apm_cli.models.apm_package import APMPackage, DependencyReference # =========================================================================== # DependencyReference.is_local_path() diff --git a/tests/unit/test_lockfile_enrichment.py b/tests/unit/test_lockfile_enrichment.py index 8066663c..df6c164d 100644 --- a/tests/unit/test_lockfile_enrichment.py +++ b/tests/unit/test_lockfile_enrichment.py @@ -3,7 +3,7 @@ import yaml from apm_cli.bundle.lockfile_enrichment import enrich_lockfile_for_pack -from apm_cli.deps.lockfile import LockFile, LockedDependency +from apm_cli.deps.lockfile import LockedDependency, LockFile def _make_lockfile() -> LockFile: diff --git a/tests/unit/test_mcp_client_factory.py b/tests/unit/test_mcp_client_factory.py index f1522e1c..98db7a91 100644 --- a/tests/unit/test_mcp_client_factory.py +++ b/tests/unit/test_mcp_client_factory.py @@ -1,15 +1,15 @@ """Unit tests for MCP client factory and adapters.""" -import unittest -import tempfile import json import os +import tempfile +import unittest from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch -from apm_cli.factory import ClientFactory -from apm_cli.adapters.client.vscode import VSCodeClientAdapter from apm_cli.adapters.client.codex import CodexClientAdapter +from apm_cli.adapters.client.vscode import VSCodeClientAdapter +from apm_cli.factory import ClientFactory class TestMCPClientFactory(unittest.TestCase): diff --git a/tests/unit/test_mcp_lifecycle_e2e.py b/tests/unit/test_mcp_lifecycle_e2e.py index 62a09cf7..881a08bb 100644 --- a/tests/unit/test_mcp_lifecycle_e2e.py +++ b/tests/unit/test_mcp_lifecycle_e2e.py @@ -13,9 +13,8 @@ import pytest import yaml -from apm_cli.integration.mcp_integrator import MCPIntegrator from apm_cli.deps.lockfile import LockedDependency, LockFile - +from apm_cli.integration.mcp_integrator import MCPIntegrator # --------------------------------------------------------------------------- # Helpers — mirror the per-file convention used across the test suite. diff --git a/tests/unit/test_mcp_overlays.py b/tests/unit/test_mcp_overlays.py index a459797d..de51a6f1 100644 --- a/tests/unit/test_mcp_overlays.py +++ b/tests/unit/test_mcp_overlays.py @@ -1,11 +1,12 @@ """Tests for MCP overlay functionality: MCPDependency model, self-defined server info building, overlay application, and install flow integration.""" +from unittest.mock import MagicMock, patch + import pytest -from unittest.mock import patch, MagicMock -from apm_cli.models.apm_package import MCPDependency from apm_cli.integration.mcp_integrator import MCPIntegrator +from apm_cli.models.apm_package import MCPDependency # --------------------------------------------------------------------------- diff --git a/tests/unit/test_package_identity.py b/tests/unit/test_package_identity.py index c1bb18ae..1a54e275 100644 --- a/tests/unit/test_package_identity.py +++ b/tests/unit/test_package_identity.py @@ -7,10 +7,11 @@ 3. Agent/prompt metadata for orphan detection """ -import pytest from pathlib import Path from urllib.parse import urlparse +import pytest + from src.apm_cli.models.apm_package import DependencyReference diff --git a/tests/unit/test_package_manager.py b/tests/unit/test_package_manager.py index ac02cf2f..033a6da6 100644 --- a/tests/unit/test_package_manager.py +++ b/tests/unit/test_package_manager.py @@ -1,7 +1,8 @@ """Unit tests for the default MCP package manager.""" import unittest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + from apm_cli.adapters.package_manager.default_manager import DefaultMCPPackageManager diff --git a/tests/unit/test_packer.py b/tests/unit/test_packer.py index a5eb6e85..677234e9 100644 --- a/tests/unit/test_packer.py +++ b/tests/unit/test_packer.py @@ -8,8 +8,8 @@ import pytest import yaml -from apm_cli.bundle.packer import pack_bundle, PackResult, _filter_files_by_target -from apm_cli.deps.lockfile import LockFile, LockedDependency +from apm_cli.bundle.packer import PackResult, _filter_files_by_target, pack_bundle +from apm_cli.deps.lockfile import LockedDependency, LockFile def _setup_project(tmp_path: Path, deployed_files: list[str], *, target: str | None = None) -> Path: diff --git a/tests/unit/test_path_security.py b/tests/unit/test_path_security.py index f93f9503..8a611611 100644 --- a/tests/unit/test_path_security.py +++ b/tests/unit/test_path_security.py @@ -11,13 +11,12 @@ import pytest +from apm_cli.models.dependency import DependencyReference from apm_cli.utils.path_security import ( PathTraversalError, ensure_path_within, safe_rmtree, ) -from apm_cli.models.dependency import DependencyReference - # --------------------------------------------------------------------------- # ensure_path_within diff --git a/tests/unit/test_plugin_exporter.py b/tests/unit/test_plugin_exporter.py index 0d2372d1..a8e3bedb 100644 --- a/tests/unit/test_plugin_exporter.py +++ b/tests/unit/test_plugin_exporter.py @@ -25,10 +25,9 @@ _validate_output_rel, export_plugin_bundle, ) -from apm_cli.deps.lockfile import LockFile, LockedDependency +from apm_cli.deps.lockfile import LockedDependency, LockFile from apm_cli.deps.plugin_parser import synthesize_plugin_json_from_apm_yml - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/tests/unit/test_python_paths.py b/tests/unit/test_python_paths.py index a34cdc15..f3835616 100644 --- a/tests/unit/test_python_paths.py +++ b/tests/unit/test_python_paths.py @@ -2,6 +2,7 @@ import unittest from unittest import mock + from apm_cli.adapters.client.vscode import VSCodeClientAdapter diff --git a/tests/unit/test_registry_client.py b/tests/unit/test_registry_client.py index 83a6a9da..b8d52087 100644 --- a/tests/unit/test_registry_client.py +++ b/tests/unit/test_registry_client.py @@ -1,9 +1,11 @@ """Unit tests for the MCP registry client.""" -import unittest import os +import unittest from unittest import mock + import requests + from apm_cli.registry.client import SimpleRegistryClient from apm_cli.utils import github_host diff --git a/tests/unit/test_registry_integration.py b/tests/unit/test_registry_integration.py index 06e03ddd..44c5d4a5 100644 --- a/tests/unit/test_registry_integration.py +++ b/tests/unit/test_registry_integration.py @@ -2,7 +2,9 @@ import unittest from unittest import mock + import requests + from apm_cli.registry.integration import RegistryIntegration from apm_cli.utils import github_host diff --git a/tests/unit/test_runtime_args.py b/tests/unit/test_runtime_args.py index 4ca7d3e0..6b584339 100644 --- a/tests/unit/test_runtime_args.py +++ b/tests/unit/test_runtime_args.py @@ -2,6 +2,7 @@ import unittest from unittest import mock + from apm_cli.adapters.client.vscode import VSCodeClientAdapter diff --git a/tests/unit/test_runtime_detection.py b/tests/unit/test_runtime_detection.py index 386becbe..e38e1b48 100644 --- a/tests/unit/test_runtime_detection.py +++ b/tests/unit/test_runtime_detection.py @@ -3,7 +3,7 @@ import shutil import unittest from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from apm_cli.integration.mcp_integrator import MCPIntegrator diff --git a/tests/unit/test_runtime_factory.py b/tests/unit/test_runtime_factory.py index 244f0d75..92c9052d 100644 --- a/tests/unit/test_runtime_factory.py +++ b/tests/unit/test_runtime_factory.py @@ -1,7 +1,9 @@ """Test Runtime Factory.""" -import pytest from unittest.mock import Mock, patch + +import pytest + from apm_cli.runtime.factory import RuntimeFactory diff --git a/tests/unit/test_runtime_windows.py b/tests/unit/test_runtime_windows.py index 3c4feff9..fcd203f0 100644 --- a/tests/unit/test_runtime_windows.py +++ b/tests/unit/test_runtime_windows.py @@ -1,13 +1,15 @@ """Tests for Windows platform support in RuntimeManager and ScriptRunner.""" import sys -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + import pytest +from apm_cli.core.script_runner import ScriptRunner + # Import modules at module level BEFORE any sys.platform patching, # to avoid triggering Windows-only import paths (msvcrt, CREATE_NO_WINDOW) on Unix. from apm_cli.runtime.manager import RuntimeManager -from apm_cli.core.script_runner import ScriptRunner def _make_manager(platform: str) -> RuntimeManager: diff --git a/tests/unit/test_safe_installer.py b/tests/unit/test_safe_installer.py index d5cc3f6d..56a1b938 100644 --- a/tests/unit/test_safe_installer.py +++ b/tests/unit/test_safe_installer.py @@ -2,7 +2,8 @@ import unittest from unittest.mock import Mock, patch -from apm_cli.core.safe_installer import SafeMCPInstaller, InstallationSummary + +from apm_cli.core.safe_installer import InstallationSummary, SafeMCPInstaller class TestSafeMCPInstaller(unittest.TestCase): diff --git a/tests/unit/test_script_runner.py b/tests/unit/test_script_runner.py index 64913bd1..0ea10498 100644 --- a/tests/unit/test_script_runner.py +++ b/tests/unit/test_script_runner.py @@ -1,13 +1,14 @@ """Unit tests for script runner functionality.""" -import pytest -from pathlib import Path -from unittest.mock import patch, mock_open, MagicMock import os -import tempfile import shutil +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, mock_open, patch + +import pytest -from apm_cli.core.script_runner import ScriptRunner, PromptCompiler +from apm_cli.core.script_runner import PromptCompiler, ScriptRunner class TestScriptRunner: diff --git a/tests/unit/test_security_gate.py b/tests/unit/test_security_gate.py index c4b1fa0b..13bcbf83 100644 --- a/tests/unit/test_security_gate.py +++ b/tests/unit/test_security_gate.py @@ -16,7 +16,6 @@ SecurityGate, ) - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/tests/unit/test_ssl_cert_hook.py b/tests/unit/test_ssl_cert_hook.py index 10b3a9d0..5fab8bbe 100644 --- a/tests/unit/test_ssl_cert_hook.py +++ b/tests/unit/test_ssl_cert_hook.py @@ -9,11 +9,10 @@ import os import sys from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch import pytest - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/tests/unit/test_transitive_deps.py b/tests/unit/test_transitive_deps.py index 75dae2dc..d57a8cde 100644 --- a/tests/unit/test_transitive_deps.py +++ b/tests/unit/test_transitive_deps.py @@ -11,8 +11,8 @@ import yaml from apm_cli.deps.lockfile import ( - LockFile, LockedDependency, + LockFile, ) from apm_cli.primitives.discovery import get_dependency_declaration_order diff --git a/tests/unit/test_uninstall_reintegration.py b/tests/unit/test_uninstall_reintegration.py index ba44c1bf..bb338b81 100644 --- a/tests/unit/test_uninstall_reintegration.py +++ b/tests/unit/test_uninstall_reintegration.py @@ -4,20 +4,21 @@ then remaining packages are re-integrated from apm_modules/. """ -import pytest -from pathlib import Path from datetime import datetime +from pathlib import Path -from apm_cli.integration import PromptIntegrator, AgentIntegrator -from apm_cli.integration.skill_integrator import SkillIntegrator +import pytest + +from apm_cli.integration import AgentIntegrator, PromptIntegrator from apm_cli.integration.command_integrator import CommandIntegrator +from apm_cli.integration.skill_integrator import SkillIntegrator from apm_cli.models.apm_package import ( - PackageInfo, APMPackage, - ResolvedReference, GitReferenceType, - PackageType, PackageContentType, + PackageInfo, + PackageType, + ResolvedReference, ) diff --git a/tests/unit/test_uninstall_transitive_cleanup.py b/tests/unit/test_uninstall_transitive_cleanup.py index 23273303..79d2ab9f 100644 --- a/tests/unit/test_uninstall_transitive_cleanup.py +++ b/tests/unit/test_uninstall_transitive_cleanup.py @@ -7,16 +7,16 @@ import os import tempfile +from pathlib import Path from types import SimpleNamespace from unittest.mock import patch import pytest import yaml from click.testing import CliRunner -from pathlib import Path from apm_cli.cli import cli -from apm_cli.deps.lockfile import LockFile, LockedDependency +from apm_cli.deps.lockfile import LockedDependency, LockFile from apm_cli.models.apm_package import APMPackage from apm_cli.models.dependency import DependencyReference diff --git a/tests/unit/test_unpack_security.py b/tests/unit/test_unpack_security.py index 3df60ec3..122743ce 100644 --- a/tests/unit/test_unpack_security.py +++ b/tests/unit/test_unpack_security.py @@ -6,8 +6,8 @@ import pytest -from apm_cli.bundle.unpacker import unpack_bundle, UnpackResult -from apm_cli.deps.lockfile import LockFile, LockedDependency +from apm_cli.bundle.unpacker import UnpackResult, unpack_bundle +from apm_cli.deps.lockfile import LockedDependency, LockFile def _build_bundle_dir(tmp_path: Path, deployed_files: dict[str, Union[str, bytes]]) -> Path: diff --git a/tests/unit/test_unpacker.py b/tests/unit/test_unpacker.py index 86a4ab85..6d4886c4 100644 --- a/tests/unit/test_unpacker.py +++ b/tests/unit/test_unpacker.py @@ -6,7 +6,7 @@ import pytest from apm_cli.bundle.unpacker import unpack_bundle -from apm_cli.deps.lockfile import LockFile, LockedDependency +from apm_cli.deps.lockfile import LockedDependency, LockFile def _build_bundle_dir(tmp_path: Path, deployed_files: list[str]) -> Path: @@ -403,9 +403,11 @@ class TestUnpackCmdLogging: def test_unpack_cmd_logs_file_list(self, tmp_path): """unpack command outputs each file under its dependency name.""" + import os + from click.testing import CliRunner + from apm_cli.commands.pack import unpack_cmd - import os deployed = [".github/agents/a.md", ".github/prompts/b.md"] bundle = _build_bundle_dir(tmp_path, deployed) @@ -430,9 +432,11 @@ def test_unpack_cmd_logs_file_list(self, tmp_path): def test_unpack_cmd_dry_run_logs_files(self, tmp_path): """Dry-run output includes per-dependency file listing.""" + import os + from click.testing import CliRunner + from apm_cli.commands.pack import unpack_cmd - import os deployed = [".github/agents/a.md"] bundle = _build_bundle_dir(tmp_path, deployed) @@ -457,9 +461,11 @@ def test_unpack_cmd_dry_run_logs_files(self, tmp_path): def test_unpack_cmd_logs_skipped_files(self, tmp_path): """Skipped files warning appears when skip_verify allows missing files.""" + import os + from click.testing import CliRunner + from apm_cli.commands.pack import unpack_cmd - import os deployed = [".github/agents/a.md", ".github/agents/missing.md"] bundle_dir = tmp_path / "bundle" / "test-pkg" @@ -493,9 +499,11 @@ def test_unpack_cmd_logs_skipped_files(self, tmp_path): def test_unpack_cmd_multi_dep_logging(self, tmp_path): """Multiple dependencies are each logged with their file lists.""" + import os + from click.testing import CliRunner + from apm_cli.commands.pack import unpack_cmd - import os bundle_dir = tmp_path / "bundle" / "multi-pkg" bundle_dir.mkdir(parents=True) diff --git a/tests/unit/test_version_checker.py b/tests/unit/test_version_checker.py index 39769510..9025415b 100644 --- a/tests/unit/test_version_checker.py +++ b/tests/unit/test_version_checker.py @@ -1,18 +1,18 @@ """Tests for version checker utility.""" -import unittest -from unittest.mock import patch, Mock -from pathlib import Path import tempfile import time +import unittest +from pathlib import Path +from unittest.mock import Mock, patch from apm_cli.utils.version_checker import ( + check_for_updates, get_latest_version_from_github, - parse_version, is_newer_version, - should_check_for_updates, + parse_version, save_version_check_timestamp, - check_for_updates, + should_check_for_updates, ) diff --git a/tests/utils/constitution_fixtures.py b/tests/utils/constitution_fixtures.py index 2d9d9fe9..576e60a4 100644 --- a/tests/utils/constitution_fixtures.py +++ b/tests/utils/constitution_fixtures.py @@ -1,10 +1,10 @@ from __future__ import annotations +import contextlib import shutil +import tempfile from pathlib import Path from typing import Callable -import contextlib -import tempfile @contextlib.contextmanager