Skip to content

[FEATURE] Authoring-repo mode: publish local .apm/ content via apm pack without self-deploying it during apm install #1474

@wwalendz-relativity

Description

@wwalendz-relativity

Is your feature request related to a problem? Please describe.

In a package's authoring repository — i.e., the repo whose .apm/ tree IS the package source — apm install always deploys that local content into the same project's runtime target directories (.agents/skills/, .claude/skills/, etc.). There is no way to keep the publish-side behavior (apm pack bundles .apm/ content for external consumers) while suppressing the install-side self-deployment.

For an authoring repo this produces three concrete pain points:

  1. Working-tree noise. Every apm install writes N copies of each local primitive into .agents/<type>/<name>/ and per-client mirrors (.claude/skills/<name>/, etc.). Authors of a 20+ skill package see 40+ deployed paths show up next to the source they just edited. The lockfile's local_deployed_files: block grows proportionally.

  2. Source vs. deployed-copy drift during iteration. While editing .apm/skills/foo/SKILL.md, the file Claude Code (or Cursor, Copilot, etc.) actually loads from .claude/skills/foo/SKILL.md is a snapshot from the last apm install. The author either has to re-run apm install after every edit, or symlink manually, or accept that their own client is running stale skills. None of these are good answers for a tight authoring loop.

  3. No clean way to say "ship this, don't run it here." Sometimes the author specifically does not want their in-progress skills loaded into their own client — early prototypes, breaking changes mid-refactor, skills with side effects (network calls, file writes) that shouldn't fire in the authoring session. Today the only escape hatch is includes: [], which also prevents apm pack from including the content, defeating the purpose.

Per the docs (https://microsoft.github.io/apm/reference/cli/install/): "After dependencies are integrated, primitives in the project's own .apm/ directory are deployed to the same targets." This behavior is documented as unconditional.

Describe the solution you'd like

Add a knob that splits "publish in apm pack" from "deploy on local apm install." Three shapes worth considering:

  1. Manifest field (preferred for declarative repos):

    # apm.yml
    self_install: false      # default true; when false, apm install skips local .apm/ deployment
    includes: auto           # publish path unaffected — apm pack still bundles .apm/

    The field is checked only by apm install (and apm compile) when walking root .apm/. apm pack ignores it. External consumers installing this package as a dependency are unaffected because their copy lives under apm_modules/<this-package>/.apm/, not at their project root.

  2. CLI flag for ad-hoc use:

    apm install --no-self-install
    

    Useful in CI (apm install --no-self-install to validate external deps resolve without polluting the workspace) and as an opt-out when the manifest field defaults to true.

  3. Per-includes-entry granularity if the team wants finer control:

    includes:
      - path: skills/
        deploy_local: false   # ship in apm pack; do not deploy on install
      - templates/            # publish + deploy as today

For audit honesty, when self-install is skipped, apm.lock.yaml's local_deployed_files: should be empty (not stale entries from a prior install) and apm audit --ci should still hash-verify the source .apm/ paths the way it would the deployed ones — closing the same gap that #887 closed for the deploy-on path.

Describe alternatives you've considered

  • includes: [] — kills publishing too, so apm pack produces an empty bundle. Not viable.
  • Move skills outside .apm/ (e.g., src/skills/) — apm pack's file scanner walks .apm/, so out-of-tree content isn't picked up. Could declare each as a path: devDep, but that flips the problem: dev-installed but not shipped.
  • Post-install cleanup script — works (Remove-Item .agents/skills/foo-*, .claude/skills/foo-* after apm install) but every consumer of the authoring repo has to remember it, and apm audit --ci will then complain about missing deployed files relative to local_deployed_files: in the lockfile.
  • .gitignore the deployed paths (adjacent to [FEATURE] Extend .gitignore coverage from apm_modules/ to deployed files #1342) — files still get written on every install and still get loaded by the local AI client; this only hides them from git, which addresses pain point (1) partially and doesn't help (2) or (3).
  • Narrow targets: in apm.yml — also affects what external consumers receive; the targets list is part of the package contract, not a local-only switch.
  • Symlinks from .claude/skills/foo.apm/skills/foo — would solve the drift problem (pain point 2), but APM owns those paths and overwrites symlinks on next install; also not portable to Windows authors without dev-mode.

Additional context

Concrete repro of pain point (1), redacted:

# apm.yml in an authoring repo with 21 local skills
name: example-skills-pack
version: 1.0.0
targets: [agent-skills, claude]
dependencies:
  apm: []
includes: auto
devDependencies:
  apm:
    - some-external/skill-bundle#main
# apm.lock.yaml after `apm install --dev`
dependencies:
- repo_url: some-external/skill-bundle
  ...
  deployed_files:
  - .agents/skills/skill-creator
  - .claude/skills/skill-creator
local_deployed_files:    # <-- 42 entries the author neither needs nor wants
- .agents/skills/foo-skill-1
- .agents/skills/foo-skill-2
  ...
- .claude/skills/foo-skill-1
- .claude/skills/foo-skill-2
  ...

Related prior art in this repo:

Naming bikeshed welcome — self_install, self_deploy, local_deploy, author_mode are all in the ballpark.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/cliCLI command surface, flags, help text (cross-cutting).area/package-authoringapm pack/unpack, plugin authoring, vendoring guidance, bundle format.status/needs-designDirection approved, design discussion required before code.status/triagedInitial agentic triage complete; pending maintainer ratification (silence = approval).theme/portabilityOne manifest, every target. Multi-target deploy, marketplace, packaging, install.type/featureNew capability, new flag, new primitive.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions