Files
holocron/plugins/git/.apm/skills/git-remotes/SKILL.md
Defame1297 5e232503c4 feat(kyberforge): execute plugin-to-apm marketplace conversion
Why:
ADR-0015 established that Microsoft APM (apm.yml + .apm/) should replace
this repo's hand-authored plugin.json/marketplace.json model, with those
files becoming compiled output of `apm pack` instead of files edited by
hand via the (now-retired) plugin-author/marketplace-author skills.
Issue #90 was the deferred execution of that decision, gated on #88
(apm tooling) and #89 (apm-native agent-author/skill-author routing).

Implementation notes:
- All six plugins (bin, core, git, gitea, kyberforge, lint) now carry
  apm.yml + .apm/{skills,agents,hooks} as their authoring source. Skills
  moved with a plain git mv (content-identical across targets). Agents
  were re-authored, not moved: per ADR-0016, .apm/agents/*.agent.md
  compiles verbatim to both Claude and Copilot, so plugin-scope agents
  now carry only name/description/model/source_keys -- no tools: field,
  no Claude-only knobs (isolation, maxTurns, effort, memory,
  permissionMode).
- Root apm.yml registers all 7 marketplace packages (6 local plus
  mattpocock-skills as a remote entry) under versioning: per_package,
  matching this repo's existing independent-plugin-versioning practice.
- .claude-plugin/marketplace.json and every plugin's plugin.json are now
  apm-pack-compiled output, verified against the prior hand-maintained
  content: same names/descriptions/versions/licenses/authors, only
  cosmetic serialization differences (JSON key order, owner email vs.
  url, Unicode escaping).
- plugin-author and marketplace-author are retired now that apm-based
  authoring fully replaces their job; kyberforge bumped 1.3.1 -> 1.4.0
  for that removal, and the root marketplace catalog bumped
  0.3.1 -> 0.3.2 to match, per the version-bump convention now
  documented in apm-workflow's reference docs instead of a dedicated
  script (apm has no native version-bump automation).
- Fixed hardcoded pre-.apm/ path assumptions across
  .pre-commit-config.yaml, .pre-commit-hooks.yaml,
  scripts/check-scope-walkup-sync.sh, scripts/sync-vale-styles.sh,
  scripts/check-vale-style-sync.sh, six plugins' root plugin.json
  (stale skills/hooks/agents pointer fields that check-manifests.sh
  validates), and several tests/*.bats and tests/*.sh fixtures --
  including a bats REPO_ROOT relative-path depth bug (10 files, one
  extra .apm/ directory level to walk up) and a vale probe-path
  isolation regression introduced mid-fix.
- Corrected empirically-wrong assumptions surfaced this session in
  apm-workflow/apm-install's own reference docs: `apm marketplace
  package add` does not accept local paths (only owner/repo remote
  shorthand -- local packages are registered by editing apm.yml's
  marketplace.packages[] directly); `apm compile` is a consumer-side
  AGENTS.md/CLAUDE.md generator, not the plugin.json producer, and
  hard-fails on skill/agent-only packages without --clean; `apm plugin
  init <name>` nests a stray subdirectory when run with a positional
  name arg from inside a same-named directory; no native Copilot
  marketplace output profile exists; .mcp.json is merged into the
  compiled plugin.json content-aware and target-scoped, with no
  dependencies.mcp entry needed for simple passthrough; pipx is the
  correct pip fallback on externally-managed Python environments.
- Renamed agent-author's copilot.agent.md template asset to
  copilot.agent.md.template so apm compile's recursive *.agent.md glob
  stops misparsing the placeholder template as a real agent primitive.

Impact:
plugin.json and marketplace.json are compiled artifacts from here on --
editing them by hand is no longer the workflow; edit apm.yml/.apm/ and
run apm pack. CONTEXT.md's Plugin/Plugin marketplace glossary entries
reflect this. ADR-0001 is marked superseded, ADR-0006 moot, and
ADR-0010 updated for the new .apm/agents/ path (project/user scope
unaffected, per ADR-0016). Full local verification: claude plugin
validate --strict on all 6 plugins, apm audit --ci, apm marketplace
check, check-manifests.sh, and the full test suite (165/165 bats,
13/13 shell scripts) all pass clean.

Fixes: #90
Refs: #88, #89
ADR: 0015
ADR: 0016

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub96PyaSRD9BHPktotj1pC
2026-08-12 18:21:24 +00:00

9.2 KiB

name, description, metadata
name description metadata
git-remotes Manage git remote repositories — add/remove/configure remotes, push/pull with safety checks, handle fetch patterns and tracking branch updates, support multi-remote workflows. Use when automating remote operations, pushing with force-push safety, fetching with pruning, pulling with divergence resolution, or managing multi-remote tracking. Include indirect triggers: any git operation that touches a remote, even if the user doesn't explicitly name the remote. Do not use when working with local git history, commits, branches, or staging — use git-history or git-branches instead.
category source_keys
git-workflow
git-scm-remote-docs
git-scm-fetch-docs
git-scm-push-docs
git-scm-pull-docs
context7-git-htmldocs

Gotchas

  • Never force-push main or master, under any circumstances — this is a hard refusal, not a confirm: true gate. If a force-push targets one of these branches, decline and explain why, regardless of how the request is confirmed.
  • Force-push to any other branch requires explicit confirmation — never execute git push --force or git push -f without user/agent approval. Always ask or require confirm: true flag first.
  • --force-with-lease alone is not safe — background processes (IDE plugins, cron jobs) that run git fetch silently defeat the protection. Always combine with --force-if-includes or use explicit SHA form --force-with-lease=<ref>:<sha>.
  • Prune doesn't touch tags by default — git fetch --prune leaves orphaned tags. Use git fetch --prune --prune-tags or configure fetch.pruneTags true globally.
  • Pull with rebase rewrites history — only safe for unpublished work. Rebasing already-pushed commits breaks everyone downstream. Check what's been pushed before rebasing.
  • git remote show requires network access — use -n flag for cached data if working offline. git remote -v lists URLs without network queries.
  • Pull behavior defaults shift between Git versions — older versions default to merge, newer versions to --ff-only. Always set pull.ff only explicitly for deterministic behavior.

Operations

Remote Management

Use these to configure which remotes you push to and pull from:

  • Add a remote: git remote add <name> <url> or git remote add -f <name> <url> to fetch immediately
  • Remove a remote: git remote remove <name> (deletes remote + all tracking refs + config)
  • Rename a remote: git remote rename <old> <new>
  • Inspect remotes: git remote -v (show URLs) or git remote show <name> (live tracking status, requires network)
  • Set-url separately for fetch vs. push: git remote set-url --push <name> <url> changes only where pushes go — but fetch and push URLs must still reference the same repository. For genuine fetch-from-A / push-to-B workflows, use two separate named remotes instead; --push cannot do this. Full set-url variants (regex-targeted replace, --add, --delete): references/remotes.md.
  • Remove a stale URL: git remote set-url --delete <name> <regex>
  • Inspect effective URLs: git remote get-url <name> (shows URL after insteadOf rewrites) or git remote get-url --push --all <name> (all push URLs)
  • Track only one branch: git remote add -t <branch> <name> <url> (repeatable), or suppress tag import entirely with git remote add --no-tags <name> <url>
  • Mirror a remote: git remote add --mirror=fetch <name> <url> mirrors all refs locally (bare repos only); --mirror=push makes every push behave like --mirror
  • Prune stale tracking refs without fetching: git remote prune <name> (add --dry-run to preview first)
  • Set the remote's default branch pointer: git remote set-head <name> -a (auto-detect, requires a prior fetch), git remote set-head <name> <branch> (explicit), or git remote set-head <name> -d (delete refs/remotes/<name>/HEAD)

Fetch Operations

Use these to update your tracking branches without touching your local branches:

  • Fetch from one remote: git fetch <remote> — fetches all branches
  • Fetch one branch only: git fetch <remote> <branch> — stores the result in FETCH_HEAD, not a tracking ref
  • Fetch from all remotes: git fetch --all with optional --prune to clean up stale tracking refs
  • Prune properly: Use git fetch --all --prune --prune-tags to clean both branches and tags
  • Configure auto-prune: Set git config --global fetch.prune true to auto-prune on every fetch across all remotes (or remote.<name>.prune to scope it to one remote)
  • Shallow clones: --depth=<n> to deepen or create a shallow clone, --unshallow to convert to full history, --update-shallow to allow the shallow boundary to move. Details and the default fetch refspec: references/remotes.md.

Fetch never modifies your local branches — it only updates remote-tracking branches (refs/remotes/origin/*).

Push Operations

Use these to send your commits upstream. Default: safe push to same-named branch on the remote.

  • Basic push: git push <remote> <branch> — pushes to same-named remote branch
  • Set upstream: git push -u <remote> <branch> — push and configure this branch to track the remote
  • Multi-remote push: git push origin develop and git push staging develop sequentially, or use git remote set-url --add <name> <url> to push to multiple remotes with one command
  • Force-push safety: Always use git push --force-with-lease --force-if-includes <remote> <branch> over bare --force. Require explicit confirmation first — and never for main/master (see Gotchas). --force-if-includes is a no-op without --force-with-lease. If background tools (IDE, cron) auto-fetch and could poison the lease check, use a dedicated push-only remote instead — see references/remotes.md.
  • Server-side enforcement: receive.denyDeletes, receive.denyDeleteCurrent, and receive.denyNonFastForwards are enforced on the remote regardless of local flags — a hardened server rejects the push even with --force.
  • Delete remote branch: git push <remote> --delete <branch> (not :<branch> syntax; clearer and cleaner)
  • Push everything: git push --all (all local branches) or git push --tags (all tags)
  • Push a single tag: git push origin <tag>
  • Delete remote branches with no local counterpart: git push --prune origin 'refs/heads/*:refs/heads/*'
  • Force only part of a multi-ref push: prefix the one refspec that needs it with +, e.g. git push origin +main develop forces main while safe-pushing develop

Refspec syntax is [+]<src>[:<dst>]:

Pattern Meaning
<branch> Push to same-named remote branch
<src>:<dst> Push <src> local ref to <dst> remote ref
+<src>:<dst> Force this refspec (non-fast-forward allowed)
:<branch> Delete remote <branch>
refs/heads/*:refs/heads/* Glob: push all matching branches
^refs/heads/dev-* Negative: exclude matching refs
tag <name> Sugar for refs/tags/<name>:refs/tags/<name>

Pull Operations

Use these to fetch and integrate remote changes. Default strategy: --ff-only (fail if diverged, forcing a conscious choice).

  • Pull with fast-forward only: git pull --ff-only (recommended default — fails if you've diverged, forcing a rebase/merge decision)
  • Pull with rebase: git pull --rebase (replays your unpublished commits on top; linear history, but rewrites SHAs — only safe for unpublished work)
  • Pull with merge: git pull --no-rebase (three-way merge commit; preserves original commits, non-linear)
  • Pull with rebase, preserving merges: git pull --rebase=merges (like --rebase, but keeps intentional local merge commits during replay)
  • Pull without integrating: git pull --squash collapses incoming commits into staged changes without committing — you write the commit message
  • Set pull strategy globally: git config pull.ff only (or pull.rebase true; respects branch-specific overrides via branch.<name>.rebase). Full precedence order (CLI flag > pull.rebase > branch.<name>.rebase > branch.autoSetupRebase): references/remotes.md.
  • Check before rebasing: Always verify your commits haven't been pushed before using --rebase. Rebasing published commits breaks everyone downstream.
  • Merge strategy default: Git 2.34+ defaults to the ort merge strategy (recursive is now just an alias for it). Strategy options like -X ours, -X theirs, -X ignore-space-change still pass through unchanged.
  • Submodules on pull: --recurse-submodules only fetches submodules that are already checked out — newly added submodules are not initialized automatically. Use the git-submodules skill to initialize new ones.

If pull diverges and you haven't set a strategy, the operation fails — this is good, forces a conscious choice. Never auto-merge diverged branches without asking.

Return Format (for agents)

Return structured output:

{
  "success": true,
  "operation": "push",
  "remote": "origin",
  "branch": "main",
  "output": "...",
  "warnings": ["force-with-lease not confirmed"],
  "recommendations": ["set pull.ff=only globally"]
}

On failure, include error field with root cause and recovery suggestion.