Skip to content

Pack & Distribute

Bundle your resolved APM dependencies into a portable artifact that can be distributed, cached, and consumed without APM, Python, or network access.

Every CI job that runs apm install pays the same tax: install APM, authenticate against GitHub, clone N repositories, compile prompts. Multiply that across a matrix of jobs, nightly builds, and staging environments and the cost adds up fast.

A bundle removes all of that. You resolve once, pack the output, and distribute the artifact. Consumers extract it and get the exact files that apm install would have produced — no toolchain required.

Common motivations:

  • CI cost reduction — resolve once, fan out to many jobs
  • Air-gapped environments — no network access at deploy time
  • Reproducibility — the bundle is a snapshot of exactly what was resolved
  • Faster onboarding — new contributors get pre-built context without running install
  • Audit trail — attach the bundle to a release for traceability

The pack/distribute workflow fits between install and consumption:

apm install -> apm pack -> upload artifact -> download -> apm unpack (or tar xzf)

The left side (install, pack) runs where APM is available. The right side (download, unpack) runs anywhere — a CI job, a dev container, a colleague’s laptop. The bundle is the boundary.

Creates a self-contained bundle from installed dependencies. Reads the deployed_files manifest in apm.lock as the source of truth — it does not scan the disk.

Terminal window
# Default: apm format, target auto-detected from apm.yml
apm pack
# Filter by target
apm pack --target vscode # only .github/ files
apm pack --target claude # only .claude/ files
apm pack --target all # both targets
# Bundle format
apm pack --format plugin # valid plugin directory structure
# Produce a .tar.gz archive
apm pack --archive
# Custom output directory (default: ./build)
apm pack -o ./dist/
# Preview without writing
apm pack --dry-run
FlagDefaultDescription
--formatapmBundle format (apm or plugin)
-t, --targetauto-detectFile filter: vscode, claude, all
--archiveoffProduce .tar.gz instead of directory
-o, --output./buildOutput directory
--dry-runoffList files without writing

The target flag controls which deployed files are included based on path prefix:

TargetIncludes
vscodePaths starting with .github/
claudePaths starting with .claude/
allBoth .github/ and .claude/

When no target is specified, APM auto-detects from the target field in apm.yml, falling back to all.

The bundle mirrors the directory structure that apm install produces. It is not an intermediate format — extract it at the project root and the files land exactly where they belong.

Output is written to ./build/<name>-<version>/ by default, where name and version come from apm.yml.

build/my-project-1.0.0/
.github/
prompts/
design-review.prompt.md
code-quality.prompt.md
agents/
architect.md
skills/
security-scan/
skill.md
apm.lock # enriched copy (see below)
build/my-project-1.0.0/
.claude/
commands/
review.md
debug.md
skills/
code-analysis/
skill.md
apm.lock
build/my-project-1.0.0/
.github/
prompts/
...
agents/
...
.claude/
commands/
...
apm.lock

The bundle is self-describing: its apm.lock lists every file it contains and the dependency graph that produced them.

The bundle includes a copy of apm.lock enriched with a pack: section. The project’s own apm.lock is never modified.

pack:
format: apm
target: vscode
packed_at: '2025-07-14T09:30:00+00:00'
lockfile_version: '1'
generated_at: '2025-07-14T09:28:00+00:00'
apm_version: '0.5.0'
dependencies:
- repo_url: microsoft/apm-sample-package
host: github.com
resolved_commit: a1b2c3d4
resolved_ref: main
version: 1.0.0
depth: 1
package_type: apm
deployed_files:
- .github/prompts/design-review.prompt.md
- .github/agents/architect.md

The pack: section records:

  • format — the bundle format used (apm or plugin)
  • target — the effective target filter applied
  • packed_at — UTC timestamp of when the bundle was created

This metadata lets consumers verify what they received and trace it back to a build.

Extracts an APM bundle into a project directory. Accepts both .tar.gz archives and unpacked bundle directories.

Terminal window
# Extract and verify
apm unpack ./build/my-project-1.0.0.tar.gz
# Extract to a specific directory
apm unpack ./build/my-project-1.0.0.tar.gz -o ./
# Skip integrity check
apm unpack --skip-verify ./build/my-project-1.0.0.tar.gz
# Preview without writing
apm unpack ./build/my-project-1.0.0.tar.gz --dry-run
FlagDefaultDescription
-o, --output. (current dir)Target project directory
--skip-verifyoffSkip completeness check against lockfile
--dry-runoffList files without writing
  • Additive-only: unpack writes files listed in the bundle’s lockfile. It never deletes existing files in the target directory.
  • Overwrite on conflict: if a file already exists at the target path, the bundle file wins.
  • Verification: by default, unpack checks that every path in the bundle’s deployed_files manifest exists in the bundle before extracting. Pass --skip-verify to skip this check for partial bundles.
  • Lockfile not copied: the bundle’s enriched apm.lock is metadata for verification only — it is not written to the output directory.

Resolve once in a setup job, fan out to N consumer jobs. No APM installation in downstream jobs.

.github/workflows/ci.yml
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: microsoft/apm-action@v1
- run: apm pack --archive
- uses: actions/upload-artifact@v4
with:
name: apm-bundle
path: build/*.tar.gz
test:
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: apm-bundle
path: ./bundle
- run: tar xzf ./bundle/*.tar.gz -C .
# Prompts and agents are now in place — no APM needed

GitHub’s agentic workflow runners operate in sandboxed environments with no network access. Pre-pack the bundle and include it as a workflow artifact so the agent has full context from the start.

Attach the bundle as a release artifact. Anyone auditing the release can inspect exactly which prompts, agents, and skills shipped with that version.

Terminal window
apm pack --archive -o ./release-artifacts/
gh release upload v1.2.0 ./release-artifacts/*.tar.gz

Include a pre-built bundle in the dev container image or restore it during onCreateCommand. New contributors get working AI context without running apm install.

{
"onCreateCommand": "tar xzf .devcontainer/apm-bundle.tar.gz -C ."
}

A central platform team maintains the canonical prompt library. Monthly, they run apm install && apm pack --archive, publish the bundle to an internal artifact registry, and downstream repos pull it during CI or onboarding.

The official apm-action supports pack and restore as first-class modes.

Generate a bundle as part of a GitHub Actions workflow:

- uses: microsoft/apm-action@v1
with:
pack: true

Consume a bundle without installing APM. The action extracts the archive directly:

- uses: microsoft/apm-action@v1
with:
bundle: ./path/to/bundle.tar.gz

No APM binary, no Python runtime, no network calls. The action handles extraction and verification internally.

apm pack requires two things:

  1. apm.lock — the resolved lockfile produced by apm install. Pack reads the deployed_files manifest from this file to know what to include.
  2. Installed files on disk — the actual files referenced in deployed_files must exist at their expected paths. Pack verifies this and fails with a clear error if files are missing.

The typical sequence is:

Terminal window
apm install # resolve dependencies and deploy files
apm pack # bundle the deployed files

Pack reads from the lockfile, not from a disk scan. If a file exists on disk but is not listed in apm.lock, it will not be included. If a file is listed in apm.lock but missing from disk, pack will fail and prompt you to re-run apm install.

Pack requires a lockfile. Run apm install first to resolve dependencies and generate apm.lock.

The lockfile references files that do not exist. This usually means dependencies were installed but the files were deleted. Run apm install to restore them.

During unpack, verification found files listed in the bundle’s lockfile that are missing from the bundle itself. The bundle may have been created from a partial install or corrupted during transfer. Re-pack from a clean install, or pass --skip-verify if you know the bundle is intentionally partial.

If apm pack produces zero files, check that your dependencies have deployed_files entries in apm.lock. This can happen if apm install completed but no integration files were deployed (e.g., the package has no prompts or agents for the active target).