fix(extension): inline agent content instead of path stubs in packaged plugins#907
Conversation
When plugins are packaged in non-symlink environments (e.g., Windows without Developer Mode), agent files were being written as single-line relative path references instead of actual YAML frontmatter content. This caused Copilot CLI to fail with 'frontmatter is malformed' errors on all agent files. The fix changes New-PluginLink to copy actual file content when symlinks are not supported, ensuring agent files have valid YAML frontmatter in installed plugins. Fixes microsoft#785 Co-Authored-By: Stable Genius <noreply@anthropic.com>
|
@ Maintainers - please hold on this until we can fully confirm the backing issue is as described and we're sure of the approach. I'm on a windows box and can test this, but won't have time for a bit. @peterbryntesson .... if you have access to windows bare metal ... this would be a cool one to run to ground to make sure this fix is correct and we can generate these files correctly (e.g. the fake symlinks) without having to run the regenerate plugins in Admin via powershell. |
|
@stablegenius49 - please sign the LCA |
|
@stablegenius49 - I'm going to take over this branch and complete the work here so that you contribution marker remains. I really appreciate you taking a run at this one and your solution is close, but needs some additions. Should have this completed and tested today, but not sure I'll be able to cut a release until next week due to the other changes we've already merged down into main. |
|
We've updated the PR description to align with the current pull request template. All original content has been preserved and relocated into the appropriate template sections. No action needed on your end — though you're welcome to review the updated description and fill in any remaining sections (testing details, checklist confirmations, etc.) at your convenience. |
|
@microsoft-github-policy-service agree |
|
PR Compliance Update — Added the missing
The PR body and |
|
Per the linked issue, I'm going to revert this back to a draft until we have some resolution from the CLI team on how to make all this work correctly. |
chaosdinosaur
left a comment
There was a problem hiding this comment.
Review of PR #907 — inline agent content instead of path stubs in packaged plugins.
The core fix is sound: replacing text-stub path references with actual file content ensures Copilot CLI can parse agent YAML frontmatter in non-symlink environments. However, the change has several downstream impacts that need attention before merging:
High priority:
Repair-PluginSymlinkIndexbecomes a silent no-op since its text-stub detection heuristic will never match full file contentCopy-Itemwithout-Recursewon't work correctly for the shared directory sources (docs/templates,scripts/lib) passed at line 657
Medium priority:
- The existing test
'Writes text stub when SymlinkCapable is false'will fail and needs updating
Low priority:
- Dead
$relativePathcomputation in the non-symlink branch - Stale comments in
Generate-Plugins.ps1referencing text stubs
5 inline comments with details and suggestions.
| # This ensures agent files have valid YAML frontmatter in installed plugins | ||
| Copy-Item -Path $SourcePath -Destination $DestinationPath -Force | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Repair-PluginSymlinkIndex is now a silent no-op
Repair-PluginSymlinkIndex detects text stubs by checking for files < 500 bytes whose content matches ^\.\./ (a relative path starting with ../). With New-PluginLink now using Copy-Item, the non-symlink fallback produces full file content that will never match this heuristic — making the entire function a silent no-op.
The caller in Generate-Plugins.ps1 (line 299) still invokes it and logs "entries fixed", but nothing will ever be fixed.
Since copied files should be committed as regular files (not mode 120000 symlinks), this function likely needs to be removed entirely, along with its call site and the comment-based help that still references "text stubs containing relative paths." If git index mode repair is still needed for some other reason, the detection logic needs reworking.
| # This ensures agent files have valid YAML frontmatter in installed plugins | ||
| Copy-Item -Path $SourcePath -Destination $DestinationPath -Force | ||
| } |
There was a problem hiding this comment.
Copy-Item on directory sources lacks -Recurse
New-PluginLink is also called for shared directories (docs/templates, scripts/lib) at line 657 in Write-PluginDirectory. Without -Recurse, Copy-Item on a directory source copies only the top-level directory, not its contents. The old WriteAllText created a single file acting as a text-based symlink — Copy-Item on a directory is fundamentally different.
Suggestion: Add directory detection:
if (Test-Path -Path $SourcePath -PathType Container) {
Copy-Item -Path $SourcePath -Destination $DestinationPath -Recurse -Force
}
else {
Copy-Item -Path $SourcePath -Destination $DestinationPath -Force
}Or keep directory sources on the symlink-only path and limit Copy-Item to file sources.
| # Copy the actual file content instead of writing a path reference | ||
| # This ensures agent files have valid YAML frontmatter in installed plugins | ||
| Copy-Item -Path $SourcePath -Destination $DestinationPath -Force | ||
| } |
There was a problem hiding this comment.
🔍 Functional Correctness — Medium: Existing test will fail
The test 'Writes text stub when SymlinkCapable is false' in scripts/tests/plugins/PluginHelpers.Tests.ps1 (line 233) asserts the destination file content equals the computed relative path. After this change, the destination contains the source file's actual content, not the relative path. This test will break.
Suggestion: Update the test to validate the new copy behavior:
It 'Copies file content when SymlinkCapable is false' {
$src = Join-Path $script:linkRoot 'src-stub.txt'
Set-Content -Path $src -Value 'content' -NoNewline
$dest = Join-Path $script:linkRoot 'dest-stub.txt'
New-PluginLink -SourcePath $src -DestinationPath $dest
Test-Path $dest | Should -BeTrue
$destContent = [System.IO.File]::ReadAllText($dest)
$destContent | Should -Be 'content'
}| else { | ||
| [System.IO.File]::WriteAllText($DestinationPath, $relativePath) | ||
| # Copy the actual file content instead of writing a path reference | ||
| # This ensures agent files have valid YAML frontmatter in installed plugins |
There was a problem hiding this comment.
💡 Code Quality — Low: Dead code — $relativePath unused in non-symlink branch
$relativePath is computed unconditionally (line 499) but only the symlink branch uses it now. Consider moving it inside the if ($SymlinkCapable) block to avoid dead code.
There was a problem hiding this comment.
💡 Documentation — Low: Generate-Plugins.ps1 comments reference obsolete text-stub behavior
The comment at line 296 in Generate-Plugins.ps1 reads "Fix git index modes for text stubs on non-symlink systems" and the conditional block invokes Repair-PluginSymlinkIndex. Both reference the old text-stub pattern that this PR eliminates. Should be updated or removed consistent with the resolution for Repair-PluginSymlinkIndex.
Pull Request
Description
When plugins are packaged in non-symlink environments (e.g., Windows without Developer Mode, or when Copilot CLI installs plugins), agent files were being written as single-line relative path references instead of actual YAML frontmatter content. This caused Copilot CLI to fail with 'frontmatter is malformed' errors on all agent files.
Root Cause
The
New-PluginLinkfunction was writing text stubs containing relative paths when symlinks were not supported. The Copilot CLI expects agent.mdfiles to begin with valid YAML frontmatter (---delimited block with at least adescriptionfield), but received single-line path strings instead.Fix
Changed
New-PluginLinkto copy the actual file content when symlinks are not supported, ensuring agent files have valid YAML frontmatter in installed plugins.Before:
After:
Related Issue(s)
Fixes #785
Type of Change
Select all that apply:
Code & Documentation:
Infrastructure & Configuration:
AI Artifacts:
prompt-builderagent and addressed all feedback.github/instructions/*.instructions.md).github/prompts/*.prompt.md).github/agents/*.agent.md).github/skills/*/SKILL.md)Other:
.ps1,.sh,.py)Testing
This fix ensures:
copilot plugin installValidation
Checklist
Required Checks
Required Automated Checks
The following validation commands must pass before merging:
npm run lint:mdnpm run spell-checknpm run lint:frontmatternpm run validate:skillsnpm run lint:md-linksnpm run lint:psnpm run plugin:generateSecurity Considerations
Additional Notes