apm-cli 0.7.7__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 (140) hide show
  1. apm_cli-0.7.9/PKG-INFO +170 -0
  2. apm_cli-0.7.9/README.md +109 -0
  3. {apm_cli-0.7.7 → apm_cli-0.7.9}/pyproject.toml +3 -3
  4. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/client/codex.py +3 -3
  5. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/client/copilot.py +2 -2
  6. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/client/vscode.py +1 -1
  7. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/bundle/lockfile_enrichment.py +1 -1
  8. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/bundle/packer.py +26 -13
  9. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/bundle/unpacker.py +35 -21
  10. apm_cli-0.7.9/src/apm_cli/cli.py +125 -0
  11. apm_cli-0.7.9/src/apm_cli/commands/_helpers.py +405 -0
  12. apm_cli-0.7.9/src/apm_cli/commands/compile.py +739 -0
  13. apm_cli-0.7.9/src/apm_cli/commands/config.py +169 -0
  14. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/commands/deps.py +38 -38
  15. apm_cli-0.7.9/src/apm_cli/commands/init.py +192 -0
  16. apm_cli-0.7.9/src/apm_cli/commands/install.py +1893 -0
  17. apm_cli-0.7.9/src/apm_cli/commands/list_cmd.py +102 -0
  18. apm_cli-0.7.9/src/apm_cli/commands/mcp.py +373 -0
  19. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/commands/pack.py +26 -7
  20. apm_cli-0.7.9/src/apm_cli/commands/prune.py +142 -0
  21. apm_cli-0.7.9/src/apm_cli/commands/run.py +218 -0
  22. apm_cli-0.7.9/src/apm_cli/commands/runtime.py +188 -0
  23. apm_cli-0.7.9/src/apm_cli/commands/uninstall.py +559 -0
  24. apm_cli-0.7.9/src/apm_cli/commands/update.py +167 -0
  25. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/agents_compiler.py +7 -7
  26. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/context_optimizer.py +14 -8
  27. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/distributed_compiler.py +7 -7
  28. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/link_resolver.py +5 -4
  29. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/safe_installer.py +6 -6
  30. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/script_runner.py +29 -15
  31. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/target_detection.py +4 -4
  32. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/token_manager.py +1 -1
  33. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/apm_resolver.py +1 -1
  34. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/github_downloader.py +103 -38
  35. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/lockfile.py +63 -5
  36. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/plugin_parser.py +22 -22
  37. apm_cli-0.7.9/src/apm_cli/drift.py +199 -0
  38. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/agent_integrator.py +8 -6
  39. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/base_integrator.py +17 -13
  40. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/command_integrator.py +3 -2
  41. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/hook_integrator.py +21 -17
  42. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/instruction_integrator.py +2 -1
  43. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/mcp_integrator.py +168 -38
  44. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/prompt_integrator.py +4 -3
  45. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/skill_integrator.py +120 -40
  46. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/skill_transformer.py +2 -2
  47. apm_cli-0.7.9/src/apm_cli/models/__init__.py +38 -0
  48. apm_cli-0.7.9/src/apm_cli/models/apm_package.py +240 -0
  49. apm_cli-0.7.7/src/apm_cli/models/apm_package.py → apm_cli-0.7.9/src/apm_cli/models/dependency.py +116 -588
  50. apm_cli-0.7.9/src/apm_cli/models/validation.py +388 -0
  51. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/output/formatters.py +85 -85
  52. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/output/script_formatters.py +21 -21
  53. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/primitives/models.py +2 -2
  54. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/primitives/parser.py +4 -2
  55. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/registry/operations.py +2 -2
  56. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/manager.py +78 -44
  57. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/utils/__init__.py +16 -1
  58. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/utils/console.py +25 -25
  59. apm_cli-0.7.9/src/apm_cli/utils/diagnostics.py +238 -0
  60. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/utils/github_host.py +4 -4
  61. apm_cli-0.7.9/src/apm_cli.egg-info/PKG-INFO +170 -0
  62. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli.egg-info/SOURCES.txt +16 -0
  63. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli.egg-info/requires.txt +3 -1
  64. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_apm_package_models.py +9 -4
  65. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_apm_resolver.py +1 -1
  66. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_enhanced_discovery.py +3 -3
  67. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_github_downloader.py +151 -2
  68. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_lockfile.py +101 -3
  69. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_runnable_prompts.py +2 -2
  70. apm_cli-0.7.7/PKG-INFO +0 -276
  71. apm_cli-0.7.7/README.md +0 -215
  72. apm_cli-0.7.7/src/apm_cli/cli.py +0 -4511
  73. apm_cli-0.7.7/src/apm_cli/models/__init__.py +0 -23
  74. apm_cli-0.7.7/src/apm_cli.egg-info/PKG-INFO +0 -276
  75. {apm_cli-0.7.7 → apm_cli-0.7.9}/AUTHORS +0 -0
  76. {apm_cli-0.7.7 → apm_cli-0.7.9}/LICENSE +0 -0
  77. {apm_cli-0.7.7 → apm_cli-0.7.9}/setup.cfg +0 -0
  78. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/__init__.py +0 -0
  79. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/__init__.py +0 -0
  80. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/client/__init__.py +0 -0
  81. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/client/base.py +0 -0
  82. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
  83. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/base.py +0 -0
  84. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
  85. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/bundle/__init__.py +0 -0
  86. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/commands/__init__.py +0 -0
  87. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/__init__.py +0 -0
  88. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/claude_formatter.py +0 -0
  89. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/constants.py +0 -0
  90. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/constitution.py +0 -0
  91. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/constitution_block.py +0 -0
  92. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/injector.py +0 -0
  93. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/compilation/template_builder.py +0 -0
  94. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/config.py +0 -0
  95. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/__init__.py +0 -0
  96. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/conflict_detector.py +0 -0
  97. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/docker_args.py +0 -0
  98. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/core/operations.py +0 -0
  99. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/__init__.py +0 -0
  100. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/aggregator.py +0 -0
  101. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/collection_parser.py +0 -0
  102. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/dependency_graph.py +0 -0
  103. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/package_validator.py +0 -0
  104. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/deps/verifier.py +0 -0
  105. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/factory.py +0 -0
  106. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/__init__.py +0 -0
  107. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/integration/utils.py +0 -0
  108. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/models/plugin.py +0 -0
  109. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/output/__init__.py +0 -0
  110. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/output/models.py +0 -0
  111. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/primitives/__init__.py +0 -0
  112. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/primitives/discovery.py +0 -0
  113. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/registry/__init__.py +0 -0
  114. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/registry/client.py +0 -0
  115. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/registry/integration.py +0 -0
  116. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/__init__.py +0 -0
  117. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/base.py +0 -0
  118. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/codex_runtime.py +0 -0
  119. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/copilot_runtime.py +0 -0
  120. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/factory.py +0 -0
  121. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/runtime/llm_runtime.py +0 -0
  122. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/utils/helpers.py +0 -0
  123. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/utils/version_checker.py +0 -0
  124. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/version.py +0 -0
  125. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/workflow/__init__.py +0 -0
  126. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/workflow/discovery.py +0 -0
  127. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/workflow/parser.py +0 -0
  128. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli/workflow/runner.py +0 -0
  129. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli.egg-info/dependency_links.txt +0 -0
  130. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli.egg-info/entry_points.txt +0 -0
  131. {apm_cli-0.7.7 → apm_cli-0.7.9}/src/apm_cli.egg-info/top_level.txt +0 -0
  132. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_codex_docker_args_fix.py +0 -0
  133. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_codex_empty_string_and_defaults.py +0 -0
  134. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_collision_integration.py +0 -0
  135. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_console.py +0 -0
  136. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_distributed_compilation.py +0 -0
  137. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_empty_string_and_defaults.py +0 -0
  138. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_github_downloader_token_precedence.py +0 -0
  139. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_runtime_manager_token_precedence.py +0 -0
  140. {apm_cli-0.7.7 → apm_cli-0.7.9}/tests/test_virtual_package_multi_install.py +0 -0
apm_cli-0.7.9/PKG-INFO ADDED
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: apm-cli
3
+ Version: 0.7.9
4
+ Summary: MCP configuration tool
5
+ Author-email: Daniel Meppiel <user@example.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) Microsoft Corporation.
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Classifier: Programming Language :: Python :: 3
29
+ Classifier: Programming Language :: Python :: 3.9
30
+ Classifier: Programming Language :: Python :: 3.10
31
+ Classifier: Programming Language :: Python :: 3.11
32
+ Classifier: Programming Language :: Python :: 3.12
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Requires-Python: >=3.10
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ License-File: AUTHORS
39
+ Requires-Dist: click>=8.0.0
40
+ Requires-Dist: colorama>=0.4.6
41
+ Requires-Dist: pyyaml>=6.0.0
42
+ Requires-Dist: requests>=2.28.0
43
+ Requires-Dist: python-frontmatter>=1.0.0
44
+ Requires-Dist: llm>=0.17.0
45
+ Requires-Dist: llm-github-models>=0.1.0
46
+ Requires-Dist: tomli>=1.2.0; python_version < "3.11"
47
+ Requires-Dist: toml>=0.10.2
48
+ Requires-Dist: rich>=13.0.0
49
+ Requires-Dist: rich-click>=1.7.0
50
+ Requires-Dist: watchdog>=3.0.0
51
+ Requires-Dist: GitPython>=3.1.0
52
+ Provides-Extra: dev
53
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
54
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
55
+ Requires-Dist: black>=26.3.1; python_version >= "3.10" and extra == "dev"
56
+ Requires-Dist: isort>=5.0.0; extra == "dev"
57
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
58
+ Provides-Extra: build
59
+ Requires-Dist: pyinstaller>=6.0.0; extra == "build"
60
+ Dynamic: license-file
61
+
62
+ # APM – Agent Package Manager
63
+
64
+ **An open-source, community-driven dependency manager for AI agents.**
65
+
66
+ Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
67
+
68
+ GitHub Copilot · Claude Code
69
+
70
+ **[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)**
71
+
72
+ ## Why APM
73
+
74
+ AI coding agents need context to be useful — standards, prompts, skills, plugins — but today every developer sets this up manually. Nothing is portable nor reproducible. There's no manifest for it.
75
+
76
+ **APM fixes this.** Declare your project's agentic dependencies once in `apm.yml`, and every developer who clones your repo gets a fully configured agent setup in seconds — with transitive dependency resolution, just like npm or pip.
77
+
78
+ ```yaml
79
+ # apm.yml — ships with your project
80
+ name: your-project
81
+ version: 1.0.0
82
+ dependencies:
83
+ apm:
84
+ # Skills from any repository
85
+ - anthropics/skills/skills/frontend-design
86
+ # Plugins
87
+ - github/awesome-copilot/plugins/context-engineering
88
+ # Specific agent primitives from any repository
89
+ - github/awesome-copilot/agents/api-architect.agent.md
90
+ # A full APM package with instructions, skills, prompts, hooks...
91
+ - microsoft/apm-sample-package
92
+ ```
93
+
94
+ ```bash
95
+ git clone <org/repo> && cd <repo>
96
+ apm install # every agent is configured
97
+ ```
98
+
99
+ ## Highlights
100
+
101
+ - **One manifest for everything** — instructions, skills, prompts, agents, hooks, plugins, MCP servers
102
+ - **Install from anywhere** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
103
+ - **Transitive dependencies** — packages can depend on packages; APM resolves the full tree
104
+ - **Compile to standards** — `apm compile` produces `AGENTS.md` (GitHub Copilot) and `CLAUDE.md` (Claude Code)
105
+ - **Create & share** — `apm pack` bundles your current configuration as a zipped package
106
+ - **CI/CD ready** — [GitHub Action](https://github.com/microsoft/apm-action) for automated workflows
107
+
108
+ ## Get Started
109
+
110
+ #### Linux / macOS
111
+
112
+ ```bash
113
+ curl -sSL https://raw.githubusercontent.com/microsoft/apm/main/install.sh | sh
114
+ ```
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
+
124
+ <details>
125
+ <summary>Other install methods</summary>
126
+
127
+ #### Linux / macOS
128
+
129
+ ```bash
130
+ # Homebrew
131
+ brew install microsoft/apm/apm
132
+ # pip
133
+ pip install apm-cli
134
+ ```
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
+
146
+ </details>
147
+
148
+ Then start adding packages:
149
+
150
+ ```bash
151
+ apm install microsoft/apm-sample-package
152
+ ```
153
+
154
+ See the **[Getting Started guide](https://microsoft.github.io/apm/getting-started/quick-start/)** for the full walkthrough.
155
+
156
+ ## Community
157
+
158
+ Created and maintained by [@danielmeppiel](https://github.com/danielmeppiel).
159
+
160
+ - [Roadmap & Discussions](https://github.com/microsoft/apm/discussions/116)
161
+ - [Contributing](CONTRIBUTING.md)
162
+ - [AI Native Development guide](https://danielmeppiel.github.io/awesome-ai-native) — a practical learning path for AI-native development
163
+
164
+ ---
165
+
166
+ **Built on open standards:** [AGENTS.md](https://agents.md) · [Agent Skills](https://agentskills.io) · [MCP](https://modelcontextprotocol.io)
167
+
168
+ ## Trademarks
169
+
170
+ This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
@@ -0,0 +1,109 @@
1
+ # APM – Agent Package Manager
2
+
3
+ **An open-source, community-driven dependency manager for AI agents.**
4
+
5
+ Think `package.json`, `requirements.txt`, or `Cargo.toml` — but for AI agent configuration.
6
+
7
+ GitHub Copilot · Claude Code
8
+
9
+ **[Documentation](https://microsoft.github.io/apm/)** · **[Quick Start](https://microsoft.github.io/apm/getting-started/quick-start/)** · **[CLI Reference](https://microsoft.github.io/apm/reference/cli-commands/)**
10
+
11
+ ## Why APM
12
+
13
+ AI coding agents need context to be useful — standards, prompts, skills, plugins — but today every developer sets this up manually. Nothing is portable nor reproducible. There's no manifest for it.
14
+
15
+ **APM fixes this.** Declare your project's agentic dependencies once in `apm.yml`, and every developer who clones your repo gets a fully configured agent setup in seconds — with transitive dependency resolution, just like npm or pip.
16
+
17
+ ```yaml
18
+ # apm.yml — ships with your project
19
+ name: your-project
20
+ version: 1.0.0
21
+ dependencies:
22
+ apm:
23
+ # Skills from any repository
24
+ - anthropics/skills/skills/frontend-design
25
+ # Plugins
26
+ - github/awesome-copilot/plugins/context-engineering
27
+ # Specific agent primitives from any repository
28
+ - github/awesome-copilot/agents/api-architect.agent.md
29
+ # A full APM package with instructions, skills, prompts, hooks...
30
+ - microsoft/apm-sample-package
31
+ ```
32
+
33
+ ```bash
34
+ git clone <org/repo> && cd <repo>
35
+ apm install # every agent is configured
36
+ ```
37
+
38
+ ## Highlights
39
+
40
+ - **One manifest for everything** — instructions, skills, prompts, agents, hooks, plugins, MCP servers
41
+ - **Install from anywhere** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
42
+ - **Transitive dependencies** — packages can depend on packages; APM resolves the full tree
43
+ - **Compile to standards** — `apm compile` produces `AGENTS.md` (GitHub Copilot) and `CLAUDE.md` (Claude Code)
44
+ - **Create & share** — `apm pack` bundles your current configuration as a zipped package
45
+ - **CI/CD ready** — [GitHub Action](https://github.com/microsoft/apm-action) for automated workflows
46
+
47
+ ## Get Started
48
+
49
+ #### Linux / macOS
50
+
51
+ ```bash
52
+ curl -sSL https://raw.githubusercontent.com/microsoft/apm/main/install.sh | sh
53
+ ```
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
+
63
+ <details>
64
+ <summary>Other install methods</summary>
65
+
66
+ #### Linux / macOS
67
+
68
+ ```bash
69
+ # Homebrew
70
+ brew install microsoft/apm/apm
71
+ # pip
72
+ pip install apm-cli
73
+ ```
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
+
85
+ </details>
86
+
87
+ Then start adding packages:
88
+
89
+ ```bash
90
+ apm install microsoft/apm-sample-package
91
+ ```
92
+
93
+ See the **[Getting Started guide](https://microsoft.github.io/apm/getting-started/quick-start/)** for the full walkthrough.
94
+
95
+ ## Community
96
+
97
+ Created and maintained by [@danielmeppiel](https://github.com/danielmeppiel).
98
+
99
+ - [Roadmap & Discussions](https://github.com/microsoft/apm/discussions/116)
100
+ - [Contributing](CONTRIBUTING.md)
101
+ - [AI Native Development guide](https://danielmeppiel.github.io/awesome-ai-native) — a practical learning path for AI-native development
102
+
103
+ ---
104
+
105
+ **Built on open standards:** [AGENTS.md](https://agents.md) · [Agent Skills](https://agentskills.io) · [MCP](https://modelcontextprotocol.io)
106
+
107
+ ## Trademarks
108
+
109
+ This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apm-cli"
7
- version = "0.7.7"
7
+ version = "0.7.9"
8
8
  description = "MCP configuration tool"
9
9
  readme = "README.md"
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.10"
11
11
  license = {file = "LICENSE"}
12
12
  authors = [
13
13
  {name = "Daniel Meppiel", email = "user@example.com"}
@@ -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
@@ -6,9 +6,9 @@ import tarfile
6
6
  import tempfile
7
7
  from dataclasses import dataclass, field
8
8
  from pathlib import Path
9
- from typing import List
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
@@ -18,6 +18,8 @@ class UnpackResult:
18
18
  extracted_dir: Path
19
19
  files: List[str] = field(default_factory=list)
20
20
  verified: bool = False
21
+ dependency_files: Dict[str, List[str]] = field(default_factory=dict)
22
+ skipped_count: int = 0
21
23
 
22
24
 
23
25
  def unpack_bundle(
@@ -42,7 +44,7 @@ def unpack_bundle(
42
44
  :class:`UnpackResult` describing what was (or would be) extracted.
43
45
 
44
46
  Raises:
45
- FileNotFoundError: If the bundle's ``apm.lock`` is missing.
47
+ FileNotFoundError: If the bundle's ``apm.lock.yaml`` is missing.
46
48
  ValueError: If verification finds files listed in the lockfile but
47
49
  absent from the bundle.
48
50
  """
@@ -63,7 +65,7 @@ def unpack_bundle(
63
65
  if sys.version_info >= (3, 12):
64
66
  tar.extractall(temp_dir, filter="data")
65
67
  else:
66
- tar.extractall(temp_dir) # noqa: S202 manual checks above
68
+ tar.extractall(temp_dir) # noqa: S202 -- manual checks above
67
69
  except Exception:
68
70
  shutil.rmtree(temp_dir, ignore_errors=True)
69
71
  raise
@@ -81,30 +83,37 @@ def unpack_bundle(
81
83
  raise FileNotFoundError(f"Bundle not found or unsupported format: {bundle_path}")
82
84
 
83
85
  try:
84
- # 2. Read apm.lock from bundle
85
- 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
86
93
  lockfile = LockFile.read(lockfile_path)
87
94
  if lockfile is None:
88
95
  if not lockfile_path.exists():
89
96
  raise FileNotFoundError(
90
- "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."
91
98
  )
92
99
  raise FileNotFoundError(
93
- "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."
94
101
  )
95
102
 
96
- # Collect all deployed_files from lockfile
97
- all_deployed: list[str] = []
98
- for dep in lockfile.get_all_dependencies():
99
- all_deployed.extend(dep.deployed_files)
100
-
101
- # Deduplicate
103
+ # Collect deployed_files per dependency and deduplicated global list
104
+ dep_file_map: Dict[str, List[str]] = {}
102
105
  seen: set[str] = set()
103
106
  unique_files: list[str] = []
104
- for f in all_deployed:
105
- if f not in seen:
106
- seen.add(f)
107
- unique_files.append(f)
107
+ for dep in lockfile.get_all_dependencies():
108
+ dep_key = dep.get_unique_key()
109
+ dep_files: list[str] = []
110
+ for f in dep.deployed_files:
111
+ dep_files.append(f)
112
+ if f not in seen:
113
+ seen.add(f)
114
+ unique_files.append(f)
115
+ if dep_files:
116
+ dep_file_map[dep_key] = dep_files
108
117
 
109
118
  # 3. Verify completeness
110
119
  verified = True
@@ -114,7 +123,7 @@ def unpack_bundle(
114
123
  ]
115
124
  if missing:
116
125
  raise ValueError(
117
- "Bundle verification failed the following deployed files "
126
+ "Bundle verification failed -- the following deployed files "
118
127
  "are missing from the bundle:\n"
119
128
  + "\n".join(f" - {m}" for m in missing)
120
129
  )
@@ -128,15 +137,17 @@ def unpack_bundle(
128
137
  extracted_dir=bundle_path,
129
138
  files=unique_files,
130
139
  verified=verified,
140
+ dependency_files=dep_file_map,
131
141
  )
132
142
 
133
143
  # 4. Copy target files to output_dir (additive, no deletes)
134
144
  output_dir = Path(output_dir)
135
145
  output_dir_resolved = output_dir.resolve()
146
+ skipped = 0
136
147
  for rel_path in unique_files:
137
148
  # Guard against absolute paths or path-traversal entries in deployed_files
138
149
  p = Path(rel_path)
139
- if p.is_absolute() or ".." in p.parts:
150
+ if p.is_absolute() or rel_path.startswith("/") or ".." in p.parts:
140
151
  raise ValueError(
141
152
  f"Refusing to unpack unsafe path from bundle lockfile: {rel_path!r}"
142
153
  )
@@ -147,6 +158,7 @@ def unpack_bundle(
147
158
  )
148
159
  src = source_dir / rel_path
149
160
  if not src.exists():
161
+ skipped += 1
150
162
  continue # skip_verify may allow missing files
151
163
  if src.is_dir():
152
164
  shutil.copytree(src, dest, dirs_exist_ok=True)
@@ -158,6 +170,8 @@ def unpack_bundle(
158
170
  extracted_dir=bundle_path,
159
171
  files=unique_files,
160
172
  verified=verified,
173
+ dependency_files=dep_file_map,
174
+ skipped_count=skipped,
161
175
  )
162
176
  finally:
163
177
  # Clean up temp dir if we created one