apm-cli 0.7.8__tar.gz → 0.7.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. {apm_cli-0.7.8/src/apm_cli.egg-info → apm_cli-0.7.9}/PKG-INFO +24 -2
  2. {apm_cli-0.7.8 → apm_cli-0.7.9}/README.md +22 -0
  3. {apm_cli-0.7.8 → apm_cli-0.7.9}/pyproject.toml +2 -2
  4. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/codex.py +3 -3
  5. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/copilot.py +2 -2
  6. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/vscode.py +1 -1
  7. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/lockfile_enrichment.py +1 -1
  8. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/packer.py +26 -13
  9. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/unpacker.py +15 -10
  10. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/cli.py +46 -1
  11. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/_helpers.py +4 -4
  12. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/compile.py +42 -42
  13. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/deps.py +38 -38
  14. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/init.py +5 -5
  15. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/install.py +339 -11
  16. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/list_cmd.py +2 -2
  17. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/mcp.py +19 -19
  18. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/pack.py +4 -4
  19. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/prune.py +7 -8
  20. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/run.py +6 -6
  21. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/runtime.py +5 -5
  22. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/uninstall.py +18 -18
  23. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/update.py +50 -19
  24. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/agents_compiler.py +7 -7
  25. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/context_optimizer.py +14 -8
  26. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/distributed_compiler.py +7 -7
  27. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/link_resolver.py +5 -4
  28. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/safe_installer.py +6 -6
  29. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/script_runner.py +29 -15
  30. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/target_detection.py +4 -4
  31. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/token_manager.py +1 -1
  32. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/apm_resolver.py +1 -1
  33. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/github_downloader.py +18 -10
  34. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/lockfile.py +59 -5
  35. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/plugin_parser.py +22 -22
  36. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/agent_integrator.py +1 -1
  37. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/base_integrator.py +6 -6
  38. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/hook_integrator.py +14 -12
  39. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/mcp_integrator.py +35 -35
  40. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/prompt_integrator.py +1 -1
  41. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/skill_integrator.py +95 -29
  42. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/skill_transformer.py +2 -2
  43. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/dependency.py +105 -23
  44. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/validation.py +5 -5
  45. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/formatters.py +85 -85
  46. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/script_formatters.py +21 -21
  47. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/models.py +2 -2
  48. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/operations.py +2 -2
  49. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/manager.py +78 -44
  50. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/console.py +25 -25
  51. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/diagnostics.py +5 -4
  52. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/github_host.py +4 -4
  53. {apm_cli-0.7.8 → apm_cli-0.7.9/src/apm_cli.egg-info}/PKG-INFO +24 -2
  54. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/requires.txt +3 -1
  55. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_apm_package_models.py +9 -4
  56. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_apm_resolver.py +1 -1
  57. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_enhanced_discovery.py +3 -3
  58. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_github_downloader.py +23 -2
  59. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_lockfile.py +42 -3
  60. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_runnable_prompts.py +2 -2
  61. {apm_cli-0.7.8 → apm_cli-0.7.9}/AUTHORS +0 -0
  62. {apm_cli-0.7.8 → apm_cli-0.7.9}/LICENSE +0 -0
  63. {apm_cli-0.7.8 → apm_cli-0.7.9}/setup.cfg +0 -0
  64. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/__init__.py +0 -0
  65. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/__init__.py +0 -0
  66. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/__init__.py +0 -0
  67. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/base.py +0 -0
  68. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  69. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/base.py +0 -0
  70. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  71. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/__init__.py +0 -0
  72. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/__init__.py +0 -0
  73. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/config.py +0 -0
  74. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/__init__.py +0 -0
  75. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/claude_formatter.py +0 -0
  76. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/constants.py +0 -0
  77. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/constitution.py +0 -0
  78. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/constitution_block.py +0 -0
  79. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/injector.py +0 -0
  80. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/template_builder.py +0 -0
  81. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/config.py +0 -0
  82. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/__init__.py +0 -0
  83. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/conflict_detector.py +0 -0
  84. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/docker_args.py +0 -0
  85. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/operations.py +0 -0
  86. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/__init__.py +0 -0
  87. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/aggregator.py +0 -0
  88. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/collection_parser.py +0 -0
  89. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/dependency_graph.py +0 -0
  90. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/package_validator.py +0 -0
  91. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/verifier.py +0 -0
  92. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/drift.py +0 -0
  93. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/factory.py +0 -0
  94. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/__init__.py +0 -0
  95. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/command_integrator.py +0 -0
  96. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/instruction_integrator.py +0 -0
  97. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/utils.py +0 -0
  98. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/__init__.py +0 -0
  99. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/apm_package.py +0 -0
  100. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/plugin.py +0 -0
  101. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/__init__.py +0 -0
  102. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/models.py +0 -0
  103. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/__init__.py +0 -0
  104. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/discovery.py +0 -0
  105. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/parser.py +0 -0
  106. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/__init__.py +0 -0
  107. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/client.py +0 -0
  108. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/integration.py +0 -0
  109. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/__init__.py +0 -0
  110. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/base.py +0 -0
  111. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/codex_runtime.py +0 -0
  112. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/copilot_runtime.py +0 -0
  113. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/factory.py +0 -0
  114. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/llm_runtime.py +0 -0
  115. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/__init__.py +0 -0
  116. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/helpers.py +0 -0
  117. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/version_checker.py +0 -0
  118. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/version.py +0 -0
  119. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/__init__.py +0 -0
  120. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/discovery.py +0 -0
  121. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/parser.py +0 -0
  122. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/runner.py +0 -0
  123. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/SOURCES.txt +0 -0
  124. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  125. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/entry_points.txt +0 -0
  126. {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/top_level.txt +0 -0
  127. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_codex_docker_args_fix.py +0 -0
  128. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_codex_empty_string_and_defaults.py +0 -0
  129. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_collision_integration.py +0 -0
  130. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_console.py +0 -0
  131. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_distributed_compilation.py +0 -0
  132. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_empty_string_and_defaults.py +0 -0
  133. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_github_downloader_token_precedence.py +0 -0
  134. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_runtime_manager_token_precedence.py +0 -0
  135. {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_virtual_package_multi_install.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apm-cli
3
- Version: 0.7.8
3
+ Version: 0.7.9
4
4
  Summary: MCP configuration tool
5
5
  Author-email: Daniel Meppiel <user@example.com>
6
6
  License: MIT License
@@ -52,7 +52,7 @@ Requires-Dist: GitPython>=3.1.0
52
52
  Provides-Extra: dev
53
53
  Requires-Dist: pytest>=7.0.0; extra == "dev"
54
54
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
55
- Requires-Dist: black>=23.0.0; extra == "dev"
55
+ Requires-Dist: black>=26.3.1; python_version >= "3.10" and extra == "dev"
56
56
  Requires-Dist: isort>=5.0.0; extra == "dev"
57
57
  Requires-Dist: mypy>=1.0.0; extra == "dev"
58
58
  Provides-Extra: build
@@ -107,13 +107,25 @@ apm install # every agent is configured
107
107
 
108
108
  ## Get Started
109
109
 
110
+ #### Linux / macOS
111
+
110
112
  ```bash
111
113
  curl -sSL https://raw.githubusercontent.com/microsoft/apm/main/install.sh | sh
112
114
  ```
113
115
 
116
+ #### Windows
117
+
118
+ ```powershell
119
+ irm https://raw.githubusercontent.com/microsoft/apm/main/install.ps1 | iex
120
+ ```
121
+
122
+ Native release binaries are published for macOS, Linux, and Windows x86_64. `apm update` reuses the matching platform installer.
123
+
114
124
  <details>
115
125
  <summary>Other install methods</summary>
116
126
 
127
+ #### Linux / macOS
128
+
117
129
  ```bash
118
130
  # Homebrew
119
131
  brew install microsoft/apm/apm
@@ -121,6 +133,16 @@ brew install microsoft/apm/apm
121
133
  pip install apm-cli
122
134
  ```
123
135
 
136
+ #### Windows
137
+
138
+ ```powershell
139
+ # Scoop
140
+ scoop bucket add apm https://github.com/microsoft/scoop-apm
141
+ scoop install apm
142
+ # pip
143
+ pip install apm-cli
144
+ ```
145
+
124
146
  </details>
125
147
 
126
148
  Then start adding packages:
@@ -46,13 +46,25 @@ apm install # every agent is configured
46
46
 
47
47
  ## Get Started
48
48
 
49
+ #### Linux / macOS
50
+
49
51
  ```bash
50
52
  curl -sSL https://raw.githubusercontent.com/microsoft/apm/main/install.sh | sh
51
53
  ```
52
54
 
55
+ #### Windows
56
+
57
+ ```powershell
58
+ irm https://raw.githubusercontent.com/microsoft/apm/main/install.ps1 | iex
59
+ ```
60
+
61
+ Native release binaries are published for macOS, Linux, and Windows x86_64. `apm update` reuses the matching platform installer.
62
+
53
63
  <details>
54
64
  <summary>Other install methods</summary>
55
65
 
66
+ #### Linux / macOS
67
+
56
68
  ```bash
57
69
  # Homebrew
58
70
  brew install microsoft/apm/apm
@@ -60,6 +72,16 @@ brew install microsoft/apm/apm
60
72
  pip install apm-cli
61
73
  ```
62
74
 
75
+ #### Windows
76
+
77
+ ```powershell
78
+ # Scoop
79
+ scoop bucket add apm https://github.com/microsoft/scoop-apm
80
+ scoop install apm
81
+ # pip
82
+ pip install apm-cli
83
+ ```
84
+
63
85
  </details>
64
86
 
65
87
  Then start adding packages:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apm-cli"
7
- version = "0.7.8"
7
+ version = "0.7.9"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -41,7 +41,7 @@ dependencies = [
41
41
  dev = [
42
42
  "pytest>=7.0.0",
43
43
  "pytest-cov>=4.0.0",
44
- "black>=23.0.0",
44
+ "black>=26.3.1; python_version>='3.10'",
45
45
  "isort>=5.0.0",
46
46
  "mypy>=1.0.0",
47
47
  ]
@@ -122,7 +122,7 @@ class CodexClientAdapter(MCPClientAdapter):
122
122
 
123
123
  # If server has only remote endpoints and no packages, it's a remote-only server
124
124
  if remotes and not packages:
125
- print(f"⚠️ Warning: MCP server '{server_url}' is a remote server (SSE type)")
125
+ print(f"[!] Warning: MCP server '{server_url}' is a remote server (SSE type)")
126
126
  print(" Codex CLI only supports local servers with command/args configuration")
127
127
  print(" Remote servers are not supported by Codex CLI")
128
128
  print(" Skipping installation for Codex CLI")
@@ -174,7 +174,7 @@ class CodexClientAdapter(MCPClientAdapter):
174
174
  "id": server_info.get("id", "") # Add registry UUID for conflict detection
175
175
  }
176
176
 
177
- # Self-defined stdio deps carry raw command/args use directly
177
+ # Self-defined stdio deps carry raw command/args -- use directly
178
178
  raw = server_info.get("_raw_stdio")
179
179
  if raw:
180
180
  config["command"] = raw["command"]
@@ -328,7 +328,7 @@ class CodexClientAdapter(MCPClientAdapter):
328
328
  # Check for CI/automated environment via APM_E2E_TESTS flag (more reliable than TTY detection)
329
329
  if os.getenv('APM_E2E_TESTS') == '1':
330
330
  skip_prompting = True
331
- print(f"💡 APM_E2E_TESTS detected, will skip environment variable prompts")
331
+ print(f" APM_E2E_TESTS detected, will skip environment variable prompts")
332
332
 
333
333
  # Also skip prompting if we're in a non-interactive environment (fallback)
334
334
  is_interactive = sys.stdin.isatty() and sys.stdout.isatty()
@@ -166,7 +166,7 @@ class CopilotClientAdapter(MCPClientAdapter):
166
166
  "id": server_info.get("id", "") # Add registry UUID for conflict detection
167
167
  }
168
168
 
169
- # Self-defined stdio deps carry raw command/args use directly
169
+ # Self-defined stdio deps carry raw command/args -- use directly
170
170
  raw = server_info.get("_raw_stdio")
171
171
  if raw:
172
172
  config["command"] = raw["command"]
@@ -331,7 +331,7 @@ class CopilotClientAdapter(MCPClientAdapter):
331
331
  # Check for CI/automated environment via APM_E2E_TESTS flag (more reliable than TTY detection)
332
332
  if os.getenv('APM_E2E_TESTS') == '1':
333
333
  skip_prompting = True
334
- print(f"💡 APM_E2E_TESTS detected, will skip environment variable prompts")
334
+ print(f" APM_E2E_TESTS detected, will skip environment variable prompts")
335
335
 
336
336
  # Also skip prompting if we're in a non-interactive environment (fallback)
337
337
  is_interactive = sys.stdin.isatty() and sys.stdout.isatty()
@@ -187,7 +187,7 @@ class VSCodeClientAdapter(MCPClientAdapter):
187
187
  server_config = {}
188
188
  input_vars = []
189
189
 
190
- # Self-defined stdio deps carry raw command/args use directly
190
+ # Self-defined stdio deps carry raw command/args -- use directly
191
191
  raw = server_info.get("_raw_stdio")
192
192
  if raw:
193
193
  server_config = {
@@ -12,7 +12,7 @@ def enrich_lockfile_for_pack(
12
12
  ) -> str:
13
13
  """Create an enriched copy of the lockfile YAML with a ``pack:`` section.
14
14
 
15
- Does NOT mutate the original *lockfile* object serialises a copy and
15
+ Does NOT mutate the original *lockfile* object -- serialises a copy and
16
16
  prepends the pack metadata.
17
17
 
18
18
  Args:
@@ -1,4 +1,4 @@
1
- """Bundle packer creates self-contained APM bundles from the resolved dependency tree."""
1
+ """Bundle packer -- creates self-contained APM bundles from the resolved dependency tree."""
2
2
 
3
3
  import shutil
4
4
  import tarfile
@@ -6,7 +6,7 @@ from dataclasses import dataclass, field
6
6
  from pathlib import Path
7
7
  from typing import List, Optional
8
8
 
9
- from ..deps.lockfile import LockFile
9
+ from ..deps.lockfile import LockFile, get_lockfile_path, migrate_lockfile_if_needed
10
10
  from ..models.apm_package import APMPackage
11
11
  from ..core.target_detection import detect_target
12
12
  from .lockfile_enrichment import enrich_lockfile_for_pack
@@ -47,10 +47,10 @@ def pack_bundle(
47
47
  """Create a self-contained bundle from installed APM dependencies.
48
48
 
49
49
  Args:
50
- project_root: Root of the project containing ``apm.lock`` and ``apm.yml``.
50
+ project_root: Root of the project containing ``apm.lock.yaml`` and ``apm.yml``.
51
51
  output_dir: Directory where the bundle will be created.
52
- fmt: Bundle format ``"apm"`` (default) or ``"plugin"``.
53
- target: Target filter ``"vscode"``, ``"claude"``, ``"all"``, or *None*
52
+ fmt: Bundle format -- ``"apm"`` (default) or ``"plugin"``.
53
+ target: Target filter -- ``"vscode"``, ``"claude"``, ``"all"``, or *None*
54
54
  (auto-detect from apm.yml / project structure).
55
55
  archive: If *True*, produce a ``.tar.gz`` and remove the directory.
56
56
  dry_run: If *True*, resolve the file list but write nothing to disk.
@@ -59,15 +59,16 @@ def pack_bundle(
59
59
  :class:`PackResult` describing what was (or would be) produced.
60
60
 
61
61
  Raises:
62
- FileNotFoundError: If ``apm.lock`` is missing.
62
+ FileNotFoundError: If ``apm.lock.yaml`` is missing.
63
63
  ValueError: If deployed files referenced in the lockfile are missing on disk.
64
64
  """
65
- # 1. Read lockfile
66
- lockfile_path = project_root / "apm.lock"
65
+ # 1. Read lockfile (migrate legacy apm.lock → apm.lock.yaml if needed)
66
+ migrate_lockfile_if_needed(project_root)
67
+ lockfile_path = get_lockfile_path(project_root)
67
68
  lockfile = LockFile.read(lockfile_path)
68
69
  if lockfile is None:
69
70
  raise FileNotFoundError(
70
- "apm.lock not found run 'apm install' first to resolve dependencies."
71
+ "apm.lock.yaml not found -- run 'apm install' first to resolve dependencies."
71
72
  )
72
73
 
73
74
  # 2. Read apm.yml for name / version / config target
@@ -77,7 +78,19 @@ def pack_bundle(
77
78
  pkg_name = package.name
78
79
  pkg_version = package.version or "0.0.0"
79
80
  config_target = package.target
80
- except (FileNotFoundError, ValueError):
81
+
82
+ # Guard: reject local-path dependencies (non-portable)
83
+ for dep_ref in package.get_apm_dependencies():
84
+ if dep_ref.is_local:
85
+ raise ValueError(
86
+ f"Cannot pack — apm.yml contains local path dependency: "
87
+ f"{dep_ref.local_path}\n"
88
+ f"Local dependencies are for development only. Replace them with "
89
+ f"remote references (e.g., 'owner/repo') before packing."
90
+ )
91
+ except ValueError:
92
+ raise
93
+ except FileNotFoundError:
81
94
  pkg_name = project_root.resolve().name
82
95
  pkg_version = "0.0.0"
83
96
  config_target = None
@@ -88,7 +101,7 @@ def pack_bundle(
88
101
  explicit_target=target,
89
102
  config_target=config_target,
90
103
  )
91
- # For packing purposes, "minimal" means nothing to pack treat as "all"
104
+ # For packing purposes, "minimal" means nothing to pack -- treat as "all"
92
105
  if effective_target == "minimal":
93
106
  effective_target = "all"
94
107
 
@@ -126,7 +139,7 @@ def pack_bundle(
126
139
  missing.append(rel_path)
127
140
  if missing:
128
141
  raise ValueError(
129
- f"The following deployed files are missing on disk "
142
+ f"The following deployed files are missing on disk -- "
130
143
  f"run 'apm install' to restore them:\n"
131
144
  + "\n".join(f" - {m}" for m in missing)
132
145
  )
@@ -156,7 +169,7 @@ def pack_bundle(
156
169
 
157
170
  # 8. Enrich lockfile copy and write to bundle
158
171
  enriched_yaml = enrich_lockfile_for_pack(lockfile, fmt, effective_target)
159
- (bundle_dir / "apm.lock").write_text(enriched_yaml, encoding="utf-8")
172
+ (bundle_dir / "apm.lock.yaml").write_text(enriched_yaml, encoding="utf-8")
160
173
 
161
174
  result = PackResult(
162
175
  bundle_path=bundle_dir,
@@ -1,4 +1,4 @@
1
- """Bundle unpacker extracts and verifies APM bundles."""
1
+ """Bundle unpacker -- extracts and verifies APM bundles."""
2
2
 
3
3
  import shutil
4
4
  import sys
@@ -8,7 +8,7 @@ from dataclasses import dataclass, field
8
8
  from pathlib import Path
9
9
  from typing import Dict, List
10
10
 
11
- from ..deps.lockfile import LockFile
11
+ from ..deps.lockfile import LockFile, LOCKFILE_NAME, LEGACY_LOCKFILE_NAME
12
12
 
13
13
 
14
14
  @dataclass
@@ -44,7 +44,7 @@ def unpack_bundle(
44
44
  :class:`UnpackResult` describing what was (or would be) extracted.
45
45
 
46
46
  Raises:
47
- FileNotFoundError: If the bundle's ``apm.lock`` is missing.
47
+ FileNotFoundError: If the bundle's ``apm.lock.yaml`` is missing.
48
48
  ValueError: If verification finds files listed in the lockfile but
49
49
  absent from the bundle.
50
50
  """
@@ -65,7 +65,7 @@ def unpack_bundle(
65
65
  if sys.version_info >= (3, 12):
66
66
  tar.extractall(temp_dir, filter="data")
67
67
  else:
68
- tar.extractall(temp_dir) # noqa: S202 manual checks above
68
+ tar.extractall(temp_dir) # noqa: S202 -- manual checks above
69
69
  except Exception:
70
70
  shutil.rmtree(temp_dir, ignore_errors=True)
71
71
  raise
@@ -83,16 +83,21 @@ def unpack_bundle(
83
83
  raise FileNotFoundError(f"Bundle not found or unsupported format: {bundle_path}")
84
84
 
85
85
  try:
86
- # 2. Read apm.lock from bundle
87
- lockfile_path = source_dir / "apm.lock"
86
+ # 2. Read apm.lock.yaml (or legacy apm.lock) from bundle
87
+ lockfile_path = source_dir / LOCKFILE_NAME
88
+ if not lockfile_path.exists():
89
+ # Backward compat: older bundles used "apm.lock"
90
+ legacy_lockfile_path = source_dir / LEGACY_LOCKFILE_NAME
91
+ if legacy_lockfile_path.exists():
92
+ lockfile_path = legacy_lockfile_path
88
93
  lockfile = LockFile.read(lockfile_path)
89
94
  if lockfile is None:
90
95
  if not lockfile_path.exists():
91
96
  raise FileNotFoundError(
92
- "apm.lock not found in the bundle the bundle may be incomplete."
97
+ f"{lockfile_path.name} not found in the bundle -- the bundle may be incomplete."
93
98
  )
94
99
  raise FileNotFoundError(
95
- "apm.lock in the bundle could not be parsed the bundle may be corrupt."
100
+ f"{lockfile_path.name} in the bundle could not be parsed -- the bundle may be corrupt."
96
101
  )
97
102
 
98
103
  # Collect deployed_files per dependency and deduplicated global list
@@ -118,7 +123,7 @@ def unpack_bundle(
118
123
  ]
119
124
  if missing:
120
125
  raise ValueError(
121
- "Bundle verification failed the following deployed files "
126
+ "Bundle verification failed -- the following deployed files "
122
127
  "are missing from the bundle:\n"
123
128
  + "\n".join(f" - {m}" for m in missing)
124
129
  )
@@ -142,7 +147,7 @@ def unpack_bundle(
142
147
  for rel_path in unique_files:
143
148
  # Guard against absolute paths or path-traversal entries in deployed_files
144
149
  p = Path(rel_path)
145
- if p.is_absolute() or ".." in p.parts:
150
+ if p.is_absolute() or rel_path.startswith("/") or ".." in p.parts:
146
151
  raise ValueError(
147
152
  f"Refusing to unpack unsafe path from bundle lockfile: {rel_path!r}"
148
153
  )
@@ -1,8 +1,10 @@
1
1
  """Command-line interface for Agent Package Manager (APM).
2
2
 
3
- Thin wiring layer all command logic lives in ``apm_cli.commands.*`` modules.
3
+ Thin wiring layer -- all command logic lives in ``apm_cli.commands.*`` modules.
4
4
  """
5
5
 
6
+ import ctypes
7
+ import os
6
8
  import sys
7
9
 
8
10
  import click
@@ -67,8 +69,51 @@ cli.add_command(runtime)
67
69
  cli.add_command(mcp)
68
70
 
69
71
 
72
+ def _configure_encoding() -> None:
73
+ """Configure stdout/stderr for full Unicode on Windows.
74
+
75
+ The default Windows console encoding (cp1252) cannot represent many Unicode
76
+ characters used in APM output (box-drawing, check marks, arrows, etc.).
77
+
78
+ This function:
79
+ 1. Sets ``PYTHONIOENCODING`` so child processes and redirected pipes
80
+ default to UTF-8.
81
+ 2. Switches the console codepage to 65001 (UTF-8) via the Win32 API so
82
+ the terminal itself renders UTF-8 byte sequences correctly.
83
+ 3. Reconfigures the Python text-mode streams to UTF-8.
84
+
85
+ On non-Windows platforms this is a no-op.
86
+ """
87
+ if sys.platform != "win32":
88
+ return
89
+
90
+ # 1. Help child processes / pipes default to UTF-8
91
+ os.environ.setdefault("PYTHONIOENCODING", "utf-8")
92
+
93
+ # 2. Switch the console codepage to UTF-8
94
+ try:
95
+ kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
96
+ kernel32.SetConsoleOutputCP(65001)
97
+ kernel32.SetConsoleCP(65001)
98
+ except (OSError, AttributeError):
99
+ pass # not a real console or ctypes unavailable
100
+
101
+ # 3. Reconfigure Python streams to UTF-8
102
+ for name in ("stdout", "stderr"):
103
+ stream = getattr(sys, name, None)
104
+ if stream is not None and hasattr(stream, "reconfigure"):
105
+ try:
106
+ stream.reconfigure(encoding="utf-8")
107
+ except Exception:
108
+ try:
109
+ stream.reconfigure(encoding="utf-8", errors="backslashreplace")
110
+ except Exception:
111
+ pass
112
+
113
+
70
114
  def main():
71
115
  """Main entry point for the CLI."""
116
+ _configure_encoding()
72
117
  try:
73
118
  cli(obj={})
74
119
  except Exception as e:
@@ -120,7 +120,7 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
120
120
  install_path = dep.get_install_path(apm_modules_dir)
121
121
  try:
122
122
  relative_path = install_path.relative_to(apm_modules_dir)
123
- expected.add(str(relative_path))
123
+ expected.add(relative_path.as_posix())
124
124
  except ValueError:
125
125
  expected.add(str(install_path))
126
126
 
@@ -136,7 +136,7 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
136
136
  install_path = dep_ref.get_install_path(apm_modules_dir)
137
137
  try:
138
138
  relative_path = install_path.relative_to(apm_modules_dir)
139
- expected.add(str(relative_path))
139
+ expected.add(relative_path.as_posix())
140
140
  except ValueError:
141
141
  pass
142
142
  return expected
@@ -185,11 +185,11 @@ def _check_orphaned_packages():
185
185
 
186
186
  try:
187
187
  from ..models.apm_package import APMPackage
188
- from ..deps.lockfile import LockFile
188
+ from ..deps.lockfile import LockFile, get_lockfile_path
189
189
 
190
190
  apm_package = APMPackage.from_apm_yml(Path("apm.yml"))
191
191
  declared_deps = apm_package.get_apm_dependencies()
192
- lockfile = LockFile.read(Path.cwd() / "apm.lock")
192
+ lockfile = LockFile.read(get_lockfile_path(Path.cwd()))
193
193
  expected = _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir)
194
194
  except Exception:
195
195
  return []