Plugin marketplace updater runs git reset --hard against user's project repo every 10 minutes

Resolved 💬 3 comments Opened Mar 29, 2026 by johnmathews Closed Mar 29, 2026

Summary

Claude Code's plugin marketplace refresh mechanism runs git fetch origin + git reset --hard origin/main every 10 minutes. When the marketplace clone at ~/.claude/plugins/marketplaces/claude-plugins-official/ is broken (missing .git directory), these git commands execute against the user's project repo instead, silently destroying all uncommitted changes to tracked files.

Environment

  • Claude Code version: 2.1.87
  • OS: macOS 15.4 (Darwin 25.3.0)
  • Shell: zsh

Root Cause

The marketplace clone directory exists but has no .git:

$ ls -la ~/.claude/plugins/marketplaces/claude-plugins-official/.git
No such file or directory

$ ls ~/.claude/plugins/marketplaces/claude-plugins-official/
external_plugins/  plugins/  README.md

The marketplace config confirms periodic refresh is active:

{
  "claude-plugins-official": {
    "source": { "source": "github", "repo": "anthropics/claude-plugins-official" },
    "installLocation": "/Users/john/.claude/plugins/marketplaces/claude-plugins-official",
    "lastUpdated": "2026-03-29T21:25:55.704Z"
  }
}

Debug logs confirm the mechanism does git pull:

[DEBUG] git pull: cwd=/Users/john/.claude/plugins/marketplaces/claude-plugins-official ref=default
[DEBUG] Successfully refreshed marketplace: claude-plugins-official

When the marketplace clone's .git is missing, the fallback code path runs git operations without an explicit cwd parameter, so they execute against the user's current working directory (their project repo). Analysis of the compiled binary shows the marketplace pull function does git fetch origin + git reset --hard origin/main, and the fetch function (hg1()) calls git without specifying a cwd.

Evidence

Git reflog: 95+ entries at exact 10-minute intervals

e8ea2c9 HEAD@{2026-03-29 22:19:09 +0200}: reset: moving to origin/main
e8ea2c9 HEAD@{2026-03-29 22:09:09 +0200}: reset: moving to origin/main
e8ea2c9 HEAD@{2026-03-29 21:59:09 +0200}: reset: moving to origin/main
e8ea2c9 HEAD@{2026-03-29 21:49:09 +0200}: reset: moving to origin/main
e8ea2c9 HEAD@{2026-03-29 21:39:09 +0200}: reset: moving to origin/main
...
8792b6c HEAD@{2026-03-29 16:55:41 +0200}: reset: moving to origin/main
8792b6c HEAD@{2026-03-29 16:45:41 +0200}: reset: moving to origin/main
...
32aa7c7 HEAD@{2026-03-28 15:47:36 +0100}: reset: moving to origin/main
32aa7c7 HEAD@{2026-03-28 15:37:36 +0100}: reset: moving to origin/main

The second offset is consistent within each session but varies between sessions (:08, :36, :41, :09), confirming a setInterval(fn, 600000) tied to session start time. 95+ entries observed across 4 sessions over ~36 hours.

Live reproduction

  1. Modified src/lib/api.ts (tracked file) and created .canary-test.txt (untracked file)
  2. Monitored every 15 seconds
  3. At the next 10-minute mark, api.ts silently reverted — the modification was gone
  4. .canary-test.txt (untracked) survived
  5. Reproduced consistently across 4 consecutive 10-minute cycles

No external git binary spawned

Process monitoring at 0.1-second intervals found zero git processes around reset times. fswatch on .git/ detected lock file operations (.git/refs/remotes/origin/HEAD.lock, .git/refs/heads/main.lock) at exact reset times, consistent with programmatic git operations (libgit2 or similar) within the Claude Code Node.js process.

Worktrees are immune

The worktree reflog shows zero reset: moving to origin entries. The marketplace updater's git commands execute against the main working tree only.

What was ruled out

A thorough investigation eliminated all other causes:

  • Git hooks — all inactive (.sample only), no husky/lint-staged
  • Claude Code user hooks — only peon-ping audio notifications, none reference git
  • macOS mechanisms — no cloud sync, cron, LaunchAgents, or file watchers covering the project
  • Vite/SvelteKit dev server — traced all file-writing code paths; writes only to output directories
  • IDE/editors — no format-on-save, no auto-restore configured
  • File history/checkpoint system — uses writeFile for restores, not git operations; not the cause

Impact

Any uncommitted changes to tracked files are silently destroyed every 10 minutes. This caused significant rework during a 2-hour session where changes had to be re-applied 3+ times. The bug is invisible when all changes are committed (the reset is a no-op), making it appear intermittent.

Suggested fix

The marketplace refresh function should:

  1. Verify the target directory is a valid git repo (has .git) before running git commands
  2. Always pass an explicit cwd or git -C <path> to git operations
  3. Never fall back to the session's working directory for marketplace git operations
  4. If the clone is broken, re-clone it rather than running git commands against an unknown repo

Workarounds

  1. Use git worktrees — confirmed immune
  2. Commit frequently — committed changes survive the reset
  3. Possibly: delete the broken marketplace clone (rm -rf ~/.claude/plugins/marketplaces/claude-plugins-official/) to force a fresh clone with a proper .git (untested)

Related issues

  • #32793 — "claude install corrupts project git remote URL to anthropics/claude-plugins-official on macOS" (same root cause: marketplace git commands in wrong directory)
  • #8072 — "Critical Bug: Code Revisions Being Repeatedly Reverted"
  • #7232 — "CRITICAL: Claude executed git reset --hard without authorization causing data destruction"

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗