Skip to content

Support git symlinks in plugin install on Windows #2286

@katriendg

Description

@katriendg

Describe the feature or problem you'd like to solve

Plugin install should resolve git symlink text stubs on Windows (core.symlinks=false)

Proposed solution

When copilot plugin install clones a marketplace repository on Windows, Git for Windows (which defaults to core.symlinks=false) materializes symlinks as plain-text files containing the target path instead of creating actual symlinks. The CLI reads these text stubs as literal file content, causing agents, skills, commands, and instructions to fail with "missing or malformed YAML frontmatter" errors. On Linux and macOS this works correctly because git creates real symlinks.

We'd love to see the CLI handle this transparently so plugin authors don't need to restructure their repositories. A few ideas, roughly in order of impact:

1. Post-clone symlink resolution (most robust)

After git clone, detect and resolve text stubs:

  • Use git ls-files -s to identify mode 120000 entries (symlinks)
  • Check if the working-tree file is a small text file containing a relative path (text stub) rather than actual content
  • Resolve each stub's path against the full cloned repository tree and copy the real file content in place
  • Warn the user if resolution fails

This works regardless of the user's OS configuration and requires no changes from plugin authors.

2. Clone with -c core.symlinks=true (low effort, partial fix)

Passing -c core.symlinks=true to git clone helps Windows machines where Developer Mode is enabled. Note: without Developer Mode, CreateSymbolicLinkW silently fails and git writes text stubs anyway with exit code 0 — so this alone is not sufficient but is a nice baseline improvement.

3. Detect and warn on broken installations

If the CLI detects text stubs (small files starting with ../ in agent/skill/command directories), emit a clear diagnostic explaining the symlink issue and suggesting remediation.

4. Future consideration: additional source types

Looking ahead, we wonder whether the source field in marketplace.json could eventually support distribution mechanisms beyond git clones — for example, npm packages or archive URLs. Package managers like npm resolve symlinks during npm pack, producing tarballs with real file content. This would eliminate the problem entirely for plugin authors who can publish to a package registry. We understand this may be a longer-term consideration and wanted to note it as a potential direction.

Example prompts or workflows

Broken workflow on Windows (current behavior):

# 1. Register the marketplace
copilot plugin marketplace add microsoft/hve-core

# 2. Install a plugin
copilot plugin install hve-core@hve-core

# 3. Start interactive session
copilot

# 4. Try to use an agent
> /agent
# Result: agents are listed but produce "missing or malformed YAML frontmatter" errors
# Every agent file contains a path string like "../../../../.github/agents/hve-core/task-planner.agent.md"
# instead of the actual agent definition with YAML frontmatter

Working workflow on Linux/macOS/WSL (same steps, no errors):

The exact same commands produce a fully functional installation because git creates real symlinks that resolve correctly within the cloned repository tree.

Expected behavior after fix:

copilot plugin marketplace add microsoft/hve-core
copilot plugin install hve-core@hve-core
copilot
> /agent
# All agents load correctly with proper YAML frontmatter — identical to Linux/macOS behavior

Additional context

Why plugin authors use symlinks

Symlinks are a natural fit for plugin marketplaces that offer multiple collections assembled from shared source artifacts. Rather than duplicating hundreds of files across collections, the repository maintains one copy of each artifact and uses symlinks in plugin directories. This is a standard git workflow that works on Linux/macOS and in CI environments.

Affected repositories

microsoft/hve-core (issue #785) — 14 plugin collections, 546 git symlinks (mode 120000). Every one becomes a broken text stub on Windows. All 18 agent files produce frontmatter errors.

github/awesome-copilot — The official awesome-copilot marketplace hit the identical problem:

  • Issue #736: "broken file references after Copilot CLI installation" (Windows 11, CLI v0.0.410)
  • Issue #751: Maintainer confirmed "the Copilot CLI clones the repo behind the scenes and does not handle symlinks properly" and "Clearly, the use of symlinks was going to be a problem going forward."
  • PR #750: Eliminated all 185 symlinks and introduced a publish pipeline that materializes files — a workaround for this CLI limitation

Current workarounds available to plugin authors

  1. Inline copies — Replace symlinks with real file copies. Awesome-copilot adopted this. Works but introduces duplication and requires sync pipelines.
  2. Separate distribution repo — Use the object form of source in marketplace.json pointing to a repo with real files. Adds cross-repo maintenance overhead.
  3. Manual user fix — Ask Windows users to enable Developer Mode + git config --global core.symlinks true + clear cache + reinstall. Fragile, enterprise-hostile, doesn't scale.

None address the root cause. We'd love to avoid requiring every plugin marketplace author to restructure their repositories for a platform-specific git behavior.

Environment details

  • Copilot CLI: Tested with v0.0.410+ through current
  • Git for Windows: Default core.symlinks=false (all install methods: winget, Chocolatey, official installer — checkbox unchecked by default)
  • Windows: 10/11, Developer Mode disabled (enterprise default — frequently locked by Group Policy)
  • Working environments: Linux, macOS, WSL (all default to core.symlinks=true)
  • Industry precedent: Every major package ecosystem (npm, Cargo, Go modules, pip) resolves symlinks at publish/install time rather than relying on consumer OS support

Thank you for considering this request. We're happy to provide additional details, test proposed changes, or help validate a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions