Skip to content

Commit 278eb12

Browse files
authored
fix(plugins): add missing plugin.json to .github directory (microsoft#2689)
## Description This PR fixed Copilot CLI installation from the HVE Core marketplace by adding the shared manifest resolved from the canonical `.github` Git source. Marketplace entries continued to own package-specific component membership, while repository validation, fixtures, tests, and documentation now use the same shared-source contract. * Added `.github/plugin.json` with the `hve-core` source identity and explicit empty component defaults. * Replaced optional generated `plugins/<name>/plugin.json` validation with a required shared-manifest check. * Updated fixture repositories and focused tests for accepted and absent shared manifests. * Documented complete source-tree copying, entry-selected component loading, unchanged channel refs, and the separate role of generated release ZIPs. ## Related Issue(s) None. ## Type of Change Select all that apply: **Code & Documentation:** * [x] Bug fix (non-breaking change fixing an issue) * [x] New feature (non-breaking change adding functionality) * [ ] Breaking change (fix or feature causing existing functionality to change) * [x] Documentation update **Infrastructure & Configuration:** * [ ] GitHub Actions workflow * [ ] Linting configuration (markdown, PowerShell, etc.) * [ ] Security configuration * [ ] DevContainer configuration * [ ] Dependency update **AI Artifacts:** * [ ] Reviewed contribution with `hve-builder` and addressed all actionable findings * [ ] Copilot instructions (`.github/instructions/*.instructions.md`) * [ ] Copilot prompt (`.github/prompts/*.prompt.md`) * [ ] Copilot agent (`.github/agents/*.agent.md`) * [ ] Copilot skill (`.github/skills/*/SKILL.md`) * [ ] Copilot hook (`.github/hooks/*/*.json`) * [ ] Eval spec added/updated for changed AI artifacts (`evals/`) > Note for AI Artifact Contributors: > > * Agents: Research, indexing/referencing other project (using standard VS Code GitHub Copilot/MCP tools), planning, and general implementation agents likely already exist. Review `.github/agents/` before creating new ones. > * Skills: Must include both bash and PowerShell scripts. See [Skills](../docs/contributing/skills.md). > * Model Versions: Contributions **MUST** target models listed in the model catalog (`scripts/linting/model-catalog.json`) whose provider appears in `providerAllowlist` and whose status is `ga` or `preview`. Run `npm run lint:models` to validate references. > * See [Agents Not Accepted](../docs/contributing/custom-agents.md#agents-not-accepted) and [Model Version Requirements](../docs/contributing/ai-artifacts-common.md#model-version-requirements). **Other:** * [x] Script/automation (`.ps1`, `.sh`, `.py`) * [ ] Other (please describe): ## Sample Prompts (for AI Artifact Contributions) <!-- If you checked any boxes under "AI Artifacts" above, provide a sample prompt showing how to use your contribution --> <!-- Delete this section if not applicable --> **User Request:** <!-- What natural language request would trigger this agent/prompt/instruction? --> **Execution Flow:** <!-- Step-by-step: what happens when invoked? Include tool usage, decision points --> **Output Artifacts:** <!-- What files/content are created? Show first 10-20 lines as preview --> **Success Indicators:** <!-- How does user know it worked correctly? What validation should they perform? --> For detailed contribution requirements, see: * Common Standards: [docs/contributing/ai-artifacts-common.md](../docs/contributing/ai-artifacts-common.md) - Shared standards for XML blocks, markdown quality, RFC 2119, validation, and testing * Agents: [docs/contributing/custom-agents.md](../docs/contributing/custom-agents.md) - Agent configurations with tools and behavior patterns * Prompts: [docs/contributing/prompts.md](../docs/contributing/prompts.md) - Workflow-specific guidance with template variables * Instructions: [docs/contributing/instructions.md](../docs/contributing/instructions.md) - Technology-specific standards with glob patterns * Skills: [docs/contributing/skills.md](../docs/contributing/skills.md) - Task execution utilities with cross-platform scripts ## Testing Fast validation was run against the changed surfaces after merging current `origin/main`: * `npm run test:ps -- -TestPath "scripts/tests/plugins/Validate-Marketplace.Tests.ps1"` - passed, 71 tests * `npm run lint:marketplace` - passed, 10 plugins * `npm run lint:plugin-output` - passed, no tracked plugin output or symbolic links * `npm exec -- markdownlint-cli2 "scripts/plugins/README.md" "docs/getting-started/methods/cli-plugins.md"` - passed, 0 issues * `git diff --check origin/main...HEAD` - passed Security analysis found no sensitive data, dependency changes, permission changes, or privilege expansion. Manual testing was not performed. ## Checklist ### Required Checks * [x] Documentation is updated (if applicable) * [x] Files follow existing naming conventions * [ ] Changes are backwards compatible (if applicable) * [x] Tests added for new functionality (if applicable) ### AI Artifact Contributions <!-- If contributing an agent, prompt, instruction, or skill, complete these checks --> * [ ] Used `hve-builder` review mode to review contribution * [ ] Addressed all actionable findings from the `hve-builder` review * [ ] Verified contribution follows common standards and type-specific requirements ### Required Local Checks The following local-safe validation commands must pass before merging: * [ ] Local validation aggregate: `npm run validate:local` (skipped by caller direction; changed-surface checks passed) * [ ] Documentation validation (if docs changed): `npm run validate:docs` (skipped by caller direction; targeted Markdown lint passed) * [ ] Spell checking: `npm run spell-check` (skipped by caller direction) * [ ] Link validation: `npm run lint:md-links` (skipped by caller direction) ## Security Considerations * [x] This PR does not contain any sensitive or NDA information * [ ] Any new dependencies have been reviewed for security issues (N/A - no dependency changes) * [ ] Security-related scripts follow the principle of least privilege (N/A - no security-related script changes) ## Additional Notes The branch was merged with current `origin/main` before PR generation. The broad template validation commands were intentionally not run; validation was limited to fast checks that own the changed files and behavior.
1 parent 14dafba commit 278eb12

8 files changed

Lines changed: 78 additions & 34 deletions

File tree

.github/plugin.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "hve-core",
3+
"agents": [],
4+
"commands": [],
5+
"skills": []
6+
}

docs/getting-started/methods/cli-plugins.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Copilot CLI Plugin
33
description: Register an HVE Core catalog ref and install the complete hve-core plugin
44
sidebar_position: 2
55
author: Microsoft
6-
ms.date: 2026-08-08
6+
ms.date: 2026-08-12
77
ms.topic: how-to
88
---
99

@@ -87,10 +87,17 @@ Each plugin includes:
8787
| Skills | Yes | Self-contained skill packages |
8888
| Instructions | No | Included for `#file:` references, not auto-applied |
8989

90-
Each plugin is a self-contained tree of regular files and real directories.
91-
Artifacts are copied from the source repository during generation, so a plugin
92-
installs the same way on every operating system and needs no symbolic link
93-
support.
90+
All marketplace entries install from the shared `.github` source tree, whose
91+
manifest is `.github/plugin.json`. During Git-source installation, the CLI
92+
copies that complete source tree for each installed entry. The declarations on
93+
the selected marketplace entry determine which recognized agents, commands,
94+
skills, and hooks the CLI loads for that package; they do not filter the copied
95+
files. Distinct packages can therefore share the same source tree and manifest
96+
while loading different component sets.
97+
98+
Generated per-package ZIPs are release assets. They are separate from the
99+
Git-source tree used by `copilot plugin install` and do not determine the
100+
contents copied during installation.
94101

95102
## Limitations
96103

scripts/plugins/README.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ selected temporary staging location. Ordinary validation uses
6767
## Marketplace Validation
6868

6969
`Validate-Marketplace.ps1` validates `.github/plugin/marketplace.json` against
70-
its JSON schema and checks version alignment with the root `package.json` plus
71-
the source locator of every entry.
70+
its JSON schema, requires the shared `.github/plugin.json` manifest, and checks
71+
version alignment with the root `package.json` plus the source locator of every
72+
entry.
7273

7374
```bash
7475
npm run lint:marketplace
@@ -97,6 +98,25 @@ Every entry uses the canonical `.github` source root:
9798
}
9899
```
99100

101+
Git-source installation resolves `.github/plugin.json` as the manifest for
102+
that shared source root:
103+
104+
```json
105+
{
106+
"name": "hve-core",
107+
"agents": [],
108+
"commands": [],
109+
"skills": []
110+
}
111+
```
112+
113+
The shared manifest provides the source identity and explicit empty component
114+
defaults. Each entry in `.github/plugin/marketplace.json` owns its package
115+
membership declarations. Those entry declarations select the recognized
116+
components that the CLI loads for that package; they do not filter which files
117+
the CLI copies from the source root. `Validate-Marketplace.ps1` owns validation
118+
of the shared manifest and the entry source contract.
119+
100120
The `repo` and `path` fields are required, and `path` must be `.github`.
101121
Main catalog entries omit `ref`. Prerelease catalog entries use the exact
102122
`prerelease-v<version>` ref, and release catalog entries use the exact
@@ -117,8 +137,11 @@ Component membership is relative to the `.github` source root:
117137
* `skills/*` directories
118138
* `hooks/*.json`
119139

120-
Generated ZIP paths are host-specific package layout, not catalog membership
121-
vocabulary.
140+
For Git-source installation, the CLI copies the complete shared `.github`
141+
source tree for each installed entry. Generated per-package ZIPs are separate
142+
release assets materialized in external staging; the CLI does not use them for
143+
Git-source installation. Their host-specific paths are package layout, not
144+
catalog membership vocabulary.
122145

123146
## Deterministic Release Evidence
124147

scripts/plugins/Validate-Marketplace.ps1

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,16 @@ function Test-MarketplaceRepositoryContract {
251251
$contractErrors += "package documentation root 'docs/plugins' is missing under $RepoRoot"
252252
}
253253

254+
# Every entry installs from the shared '.github' source root, so this single
255+
# manifest is what GitHub-object installation resolves for the whole catalog.
256+
$sharedManifestPath = Join-Path $RepoRoot '.github/plugin.json'
257+
$sharedManifest = if (Test-Path -LiteralPath $sharedManifestPath -PathType Leaf) {
258+
Get-Content -LiteralPath $sharedManifestPath -Raw -Encoding utf8 | ConvertFrom-Json -AsHashtable
259+
}
260+
if ($sharedManifest -isnot [System.Collections.IDictionary]) {
261+
$contractErrors += "shared plugin manifest '.github/plugin.json' must exist under $RepoRoot and contain a JSON object"
262+
}
263+
254264
$entries = @($Manifest['plugins'])
255265

256266
# The active package set is derived from the package documents on disk, so
@@ -321,17 +331,6 @@ function Test-MarketplaceRepositoryContract {
321331
if ($channelProjections['Stable'] -ne $channelProjections['PreRelease']) {
322332
$contractErrors += "package '$name' must resolve identical components and maturity on Stable and PreRelease"
323333
}
324-
325-
$pluginRoot = Join-Path $RepoRoot "plugins/$name/plugin.json"
326-
if (Test-Path -LiteralPath $pluginRoot -PathType Leaf) {
327-
$pluginManifest = Get-Content -LiteralPath $pluginRoot -Raw -Encoding utf8 | ConvertFrom-Json -AsHashtable
328-
if ([string]$pluginManifest['name'] -ne $name -or [string]$pluginManifest['version'] -ne [string]$entry['version']) {
329-
$contractErrors += "package '$name' root plugin.json identity does not mirror the catalog"
330-
}
331-
if ($pluginManifest.Contains('x-hve')) {
332-
$contractErrors += "package '$name' root plugin.json must not contain x-hve"
333-
}
334-
}
335334
}
336335
if ($tombstoneCount -eq 0) {
337336
$contractErrors += 'repository marketplace must declare at least one removed component tombstone'

scripts/tests/plugins/Assert-NoTrackedPluginOutput.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Describe 'Assert-NoTrackedPluginOutput' -Tag 'Unit' {
2121
}
2222

2323
It 'Inspects every staged entry' {
24-
$script:cleanResult.EntryCount | Should -Be 3
24+
$script:cleanResult.EntryCount | Should -Be 4
2525
}
2626

2727
It 'Reports no violation' {

scripts/tests/plugins/PluginHelpers.Materialization.Tests.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Describe 'Get-PluginTrackedPathIndex' -Tag 'Unit' {
6464
It 'Returns exactly the staged paths' {
6565
@($script:trackedIndex.Paths | Sort-Object) | Should -Be @(
6666
@(
67+
'.github/plugin.json',
6768
'.github/skills/rpi/rpi-plan/SKILL.md',
6869
'.github/skills/rpi/rpi-plan/references/checklist.md',
6970
'package.json'

scripts/tests/plugins/PluginTestFixtures.psm1

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ function New-PluginFixtureRepository {
4646
Creates an isolated git working tree that mimics the repository layout.
4747
4848
.DESCRIPTION
49-
Initializes a standalone git repository containing package.json and the
50-
canonical roots the plugin pipeline reads. Nothing is committed, because
51-
every production reader consults the index rather than history.
49+
Initializes a standalone git repository containing package.json, the shared
50+
.github/plugin.json manifest, and the canonical roots the plugin pipeline
51+
reads. Nothing is committed, because every production reader consults the
52+
index rather than history.
5253
5354
.PARAMETER Path
5455
Directory to initialize.
@@ -105,6 +106,14 @@ function New-PluginFixtureRepository {
105106
} | ConvertTo-Json -Depth 5
106107
Add-PluginFixtureFile -RepoRoot $Path -RelativePath 'package.json' -Content $packageJson | Out-Null
107108

109+
$sharedManifest = [ordered]@{
110+
name = 'contoso-hve'
111+
agents = @()
112+
commands = @()
113+
skills = @()
114+
} | ConvertTo-Json -Depth 5
115+
Add-PluginFixtureFile -RepoRoot $Path -RelativePath '.github/plugin.json' -Content $sharedManifest | Out-Null
116+
108117
if (-not $SkipAgentRoot) {
109118
New-Item -ItemType Directory -Path (Join-Path $Path '.github/agents') -Force | Out-Null
110119
}

scripts/tests/plugins/Validate-Marketplace.Tests.ps1

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -514,25 +514,24 @@ Describe 'Test-MarketplaceRepositoryContract' -Tag 'Unit' {
514514
}
515515
}
516516

517-
Context 'when a generated plugin manifest is present' {
518-
It 'Reports a manifest that does not mirror catalog identity' {
517+
Context 'when the shared source manifest defines the catalog root' {
518+
It 'Accepts a repository that owns .github/plugin.json' {
519519
New-ValidatorFixture -Root $script:contractRepo | Out-Null
520-
Add-PluginFixtureFile -RepoRoot $script:contractRepo -RelativePath 'plugins/rpi/plugin.json' `
521-
-Content '{"name":"rpi","version":"1.0.0"}' -Untracked | Out-Null
520+
Join-Path $script:contractRepo '.github/plugin.json' | Should -Exist
522521

523522
$run = Get-ValidationReport -Root $script:contractRepo
524-
(Get-ReportError -Report $run.Report) -join ' ' |
525-
Should -Match "repository contract: package 'rpi' root plugin\.json identity does not mirror the catalog"
523+
$run.Outcome.Success | Should -BeTrue
524+
$run.Outcome.ErrorCount | Should -Be 0
526525
}
527526

528-
It 'Reports a manifest that carries the catalog overlay' {
527+
It 'Reports an absent shared source manifest' {
529528
New-ValidatorFixture -Root $script:contractRepo | Out-Null
530-
Add-PluginFixtureFile -RepoRoot $script:contractRepo -RelativePath 'plugins/rpi/plugin.json' `
531-
-Content '{"name":"rpi","version":"9.9.9","x-hve":{"displayName":"Contoso - rpi"}}' -Untracked | Out-Null
529+
Remove-Item -LiteralPath (Join-Path $script:contractRepo '.github/plugin.json') -Force
532530

533531
$run = Get-ValidationReport -Root $script:contractRepo
532+
$run.Outcome.Success | Should -BeFalse
534533
(Get-ReportError -Report $run.Report) -join ' ' |
535-
Should -Match "repository contract: package 'rpi' root plugin\.json must not contain x-hve"
534+
Should -Match "repository contract: shared plugin manifest '\.github/plugin\.json' must exist"
536535
}
537536
}
538537

0 commit comments

Comments
 (0)