Apply git dependencies
Apply git dependencies declared in apm.yml.
ht install (and ht apply with no plugin selector) reads apm.yml and can pull a git entry from dependencies.apm. HarnessTap resolves the ref to an exact commit, fetches that SHA, and records the identity in apm.lock.yaml. It does not invoke Microsoft's apm CLI.
ht install
ht apply --updateDeclare a git dependency
String form (owner/repo, a git URL, optional #ref):
dependencies:
apm:
- acme/ship-kit#v1.2.3
- https://github.com/acme/ship-kit.git#main
- git@github.com:acme/ship-kit.gitObject form for a virtual package path or a semver range against tags:
dependencies:
apm:
- git: acme/ship-kit
ref: "^1.0.0"
path: packages/shipref (or #version) may be a branch, tag, commit SHA, or a semver range matched against git tags. path selects a subdirectory or a virtual file (.prompt.md, .instructions.md, .agent.md). Paths that escape the repo fail closed.
Lock and replay
The first apply writes apm.lock.yaml with the clone identity (repo_url, resolved_commit, the declared ref or constraint, path when set) plus SHA-256 hashes of the files that were deployed.
A later ht install or ht apply without --update reuses the locked commit. --update re-resolves the ref (or HEAD when none was declared) and refreshes the lock.
An unpinned git dep (no ref) locks HEAD; later apply without --update keeps that SHA even if the remote moved.
Apply deploys only lockfile-attested files. Path traversal, symlinks, and SHA-256 drift fail closed. Refresh hashes with ht apply --update.
Transitive dependencies.apm
After ht install / apply-from-manifest fetches a git, catalog, or path APM package, HarnessTap reads that package’s apm.yml and keeps walking runtime dependencies.apm. Nested devDependencies are not installed.
Nested packages attach to the parent plugin and deploy through the existing overlay and harness writers (CLI 151–157). Diamond graphs (A→C, B→C) install C once.
Root catalog, marketplace, and named-local entries still use the existing apply selector path. Nested named, catalog, or marketplace packages must already exist or install fails closed — they are not fetched if missing.
Every walked git package is pinned in apm.lock.yaml (repo_url, resolved_commit, the declared ref / constraint, path, declared_license). A later apply without --update replays the locked SHA. Same apm-policy.yml, Unicode scan, lock SHA-256, and executable-trust gates as root deps. Transitive plugins are depth > 0, so MCP-transitive policy and trust parking apply. See Audit and Executable trust.
The root manifest wins on plugin name collisions: a transitive with the same name is skipped, not swapped in. Resource-level conflicts still use nearest-to-root (root overlay / root package resources beat deeper transitives).
Fail closed:
- Cycles in
dependencies.apmidentity (canonical git URL + path, or resolved filesystem path) - Missing nested git, path, or catalog packages
- Hash mismatch / extra / missing lockfile-attested files
- Unsafe path or symlink
- Unresolvable git ref
Root dependencies.mcp (including registry ids) stay on the CLI #159 path. Nested MCP Registry identities are not part of this walk.
A typical APM sample that lists only a wrapper package still installs that wrapper’s review-and-refactor (or other) transitive.
apm-policy.yml can restrict git hosts (and catalog / local sources) before apply writes. Re-check in CI with ht audit --ci.
Export the recorded inventory with ht lock export.