From 9b8b60a7d6d9195e11dfc92e4b229b1ad76e1d15 Mon Sep 17 00:00:00 2001 From: Katrien De Graeve Date: Wed, 19 Aug 2026 09:27:40 +0000 Subject: [PATCH 1/3] fix(build): source plugin metadata from repository root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - relocate plugin.json and emit repository-relative component paths - update VSIX, installer, release, CI, and documentation consumers - qualify Copilot CLI agent selection and preserve VSIX metadata 🔧 - Generated by Copilot --- .github/copilot-instructions.md | 8 +- .github/hooks/shared/telemetry.json | 44 +-- .../instructions/pull-request.instructions.md | 2 +- .github/plugin.json | 261 ------------------ .github/plugin/marketplace.json | 2 +- .../references/code-doc-mapping.md | 2 +- .../references/workflow-contract.md | 2 +- .../references/component-installation.md | 8 +- .../scripts/component-copy.ps1 | 9 +- .../scripts/component-copy.sh | 8 +- .../tests/component-copy.Tests.ps1 | 54 ++-- .github/workflows/README.md | 4 +- .../workflows/release-prerelease-prepare.yml | 2 +- .github/workflows/release-prerelease.yml | 2 +- .github/workflows/release-stable-publish.yml | 2 +- .github/workflows/release-stable.yml | 2 +- CONTRIBUTING.md | 18 +- README.md | 11 - TRANSPARENCY-NOTE.md | 4 +- docs/agents/code-review/language-skills.md | 4 +- docs/architecture/README.md | 6 +- docs/architecture/ai-artifacts.md | 10 +- docs/architecture/workflows.md | 4 +- docs/contributing/ai-artifacts-common.md | 6 +- docs/contributing/custom-agents.md | 6 +- docs/contributing/hooks.md | 6 +- docs/contributing/instructions.md | 6 +- docs/contributing/prompts.md | 6 +- docs/contributing/release-process.md | 13 +- docs/contributing/skills.md | 6 +- docs/customization/README.md | 4 +- docs/customization/build-system.md | 6 +- docs/customization/forking.md | 6 +- docs/customization/packages.md | 8 +- docs/customization/team-adoption.md | 4 +- .../__tests__/resolvePluginManifest.test.ts | 14 +- docs/getting-started/install.md | 28 +- docs/getting-started/methods/cli-plugins.md | 34 ++- docs/getting-started/package-migration.md | 4 +- docs/getting-started/packages.md | 8 +- docs/plugins/hve-core.md | 20 +- extension/PACKAGING.md | 16 +- plugin.json | 261 ++++++++++++++++++ release-please-config.json | 2 +- release-please-prerelease-config.json | 2 +- scripts/extension/Prepare-Extension.ps1 | 16 +- scripts/extension/README.md | 8 +- scripts/plugins/README.md | 10 +- scripts/plugins/Sync-PluginManifest.ps1 | 147 ++++++++-- scripts/release/Set-RepositoryVersion.ps1 | 4 +- .../extension/Prepare-Extension.Tests.ps1 | 24 +- .../Workflow-PackagingContracts.Tests.ps1 | 17 +- .../plugins/Sync-PluginManifest.Tests.ps1 | 109 ++++++-- .../release/Set-RepositoryVersion.Tests.ps1 | 12 +- 54 files changed, 754 insertions(+), 528 deletions(-) delete mode 100644 .github/plugin.json create mode 100644 plugin.json diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index efd63adfc..018283c3d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -57,7 +57,7 @@ The project is organized into these main areas: * Hooks (`.github/hooks/{package-id}/`) - Package-scoped Copilot hook manifests (JSON) that wire lifecycle event commands. * Extension (`extension/`) - VS Code extension source and packaging. * GitHub Configuration (`.github/`) - Workflows, instructions, prompts, agents, composite actions, and issue templates, typically organized into `{package-id}` subdirectories. -* Plugin manifest (`.github/plugin.json`) - Deterministic membership and metadata for the sole `hve-core` plugin. +* Plugin manifest (`plugin.json`) - Deterministic membership and metadata for the sole `hve-core` plugin. * Plugin locator (`.github/plugin/marketplace.json`) - One relative marketplace entry that points to `.github`. * Logs (`logs/`) - Output from validation and analysis scripts. @@ -141,9 +141,9 @@ Parent agents reference subagents using glob paths like `.github/agents/**/code- The plugin manifest owns plugin and VSIX composition: -* `.github/plugin.json` owns the complete `hve-core` component membership: package-scoped agents, prompts, instructions, and distributable skills discovered from tracked `.github` paths, plus the fixed telemetry hook. `.github/plugin/marketplace.json` contains one relative locator to `.github` and no component recipe. +* Root `plugin.json` owns the complete `hve-core` component membership: package-scoped agents, prompts, instructions, and distributable skills discovered from tracked `.github` paths, plus the fixed telemetry hook. `.github/plugin/marketplace.json` contains one relative locator to the repository root and no component recipe. * After adding, changing, moving, or removing a distributable artifact, run `npm run plugin:sync` to update the manifest. Run `npm run plugin:validate` to check manifest drift, marketplace parity and containment, component coverage, and hooks. -* The installable plugin root is `.github`; do not materialize a copied plugin tree or create a repository-root `plugins/` directory. +* The installable plugin root is the repository root. Artifact discovery remains limited to package-scoped `.github` paths; do not materialize a copied plugin tree or create a repository-root `plugins/` directory. * Run `npm run extension:prepare` or `npm run extension:prepare:prerelease` to refresh the single `extension/package.json` and `extension/README.md`. Stable and PreRelease contain the same component set. * After adding, changing, moving, or removing a documentable agent, prompt, instruction, or skill, run `npm run docs:generate` and commit the matching page under `docs/reference/`. The generator owns page frontmatter and the prefix through ``; edit only the preserved `When to use it`, applicable `How to use it`, and `Example usage` tail. Do not edit generated regions or catalog indexes by hand. * Run `npm run plugin:validate` to confirm the manifest, one-entry locator, component coverage, and hooks are correct. @@ -155,7 +155,7 @@ The plugin manifest owns plugin and VSIX composition: * Scripts follow instructions provided by the codebase for convention and standards. * Scripts used by the codebase have an `npm run` script for ease of use. * A root `plugins/` directory is forbidden as validation or package output. Do not create, edit, or stage one. -* The plugin uses `.github` directly. Synchronize `.github/plugin.json`; do not generate plugin output or ZIP packages. +* The plugin uses the repository root directly. Synchronize root `plugin.json`; do not generate plugin output or ZIP packages. * Artifacts at the root of `.github/agents/`, `.github/instructions/`, `.github/prompts/`, or `.github/skills/` (without a package subdirectory) are repo-specific and excluded from plugin membership and extension packaging. Validation enforces this rule. PowerShell scripts follow PSScriptAnalyzer rules from `scripts/linting/PSScriptAnalyzer.psd1` and include proper comment-based help. Validation runs via `npm run lint:ps` with results output to `logs/`. diff --git a/.github/hooks/shared/telemetry.json b/.github/hooks/shared/telemetry.json index cad55ab3f..daa42126b 100644 --- a/.github/hooks/shared/telemetry.json +++ b/.github/hooks/shared/telemetry.json @@ -5,88 +5,88 @@ "sessionStart": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "userPromptSubmitted": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "userPromptSubmit": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "preToolUse": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "postToolUse": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "subagentStart": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "subagentStop": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "sessionEnd": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "stop": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "agentStop": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ], "preCompact": [ { "type": "command", - "bash": "${CLAUDE_PLUGIN_ROOT:-.github}/hooks/shared/telemetry/telemetry-collector.sh", - "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", + "bash": "${CLAUDE_PLUGIN_ROOT:-.}/.github/hooks/shared/telemetry/telemetry-collector.sh", + "powershell": "& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.' : $env:CLAUDE_PLUGIN_ROOT) '.github/hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')", "timeoutSec": 10 } ] diff --git a/.github/instructions/pull-request.instructions.md b/.github/instructions/pull-request.instructions.md index fd551e53b..5f429eb75 100644 --- a/.github/instructions/pull-request.instructions.md +++ b/.github/instructions/pull-request.instructions.md @@ -117,7 +117,7 @@ Priority rules: Skip this section when no GHCP artifact files (`.instructions.md`, `.prompt.md`, `.agent.md`, `SKILL.md`) are included in the changes. -Determine distributable membership from `.github/plugin.json` and the sync policy: +Determine distributable membership from root `plugin.json` and the sync policy: 1. Agents, prompts, and instructions are distributable when they are tracked beneath a package subdirectory and match their canonical suffix. 2. Skills are distributable when a tracked `.github/skills///SKILL.md` exists and its top-level license has no noncommercial qualifier. diff --git a/.github/plugin.json b/.github/plugin.json deleted file mode 100644 index 7b7e54deb..000000000 --- a/.github/plugin.json +++ /dev/null @@ -1,261 +0,0 @@ -{ - "name": "hve-core", - "description": "Opinionated, rapidly evolving HVE Core agentic SDLC patterns and tools", - "version": "3.2.2", - "author": { - "name": "Microsoft", - "url": "https://www.microsoft.com" - }, - "homepage": "https://github.com/microsoft/hve-core", - "repository": "https://github.com/microsoft/hve-core", - "license": "MIT", - "keywords": [ - "hve", - "hve-core", - "agents", - "prompts", - "instructions", - "skills" - ], - "agents": [ - "agents/accessibility/accessibility-planner.agent.md", - "agents/accessibility/accessibility-reviewer.agent.md", - "agents/accessibility/subagents/accessibility-framework-assessor.agent.md", - "agents/accessibility/subagents/accessibility-surface-inventory.agent.md", - "agents/coding-standards/code-review.agent.md", - "agents/coding-standards/subagents/code-review-accessibility.agent.md", - "agents/coding-standards/subagents/code-review-explainer.agent.md", - "agents/coding-standards/subagents/code-review-functional.agent.md", - "agents/coding-standards/subagents/code-review-pr.agent.md", - "agents/coding-standards/subagents/code-review-readiness.agent.md", - "agents/coding-standards/subagents/code-review-security.agent.md", - "agents/coding-standards/subagents/code-review-standards.agent.md", - "agents/coding-standards/subagents/code-review-walkback.agent.md", - "agents/data-science/data-workstream-coach.agent.md", - "agents/design-thinking/dt-coach.agent.md", - "agents/design-thinking/dt-learning-tutor.agent.md", - "agents/experimental/experiment-designer.agent.md", - "agents/experimental/pptx.agent.md", - "agents/experimental/subagents/pptx-subagent.agent.md", - "agents/hve-core/documentation.agent.md", - "agents/hve-core/rpi-agent.agent.md", - "agents/hve-core/subagents/hve-artifact-tester.agent.md", - "agents/hve-core/subagents/rpi-planner.agent.md", - "agents/hve-core/subagents/rpi-researcher.agent.md", - "agents/hve-core/subagents/vally-test-author.agent.md", - "agents/privacy/privacy-planner.agent.md", - "agents/privacy/privacy-reviewer.agent.md", - "agents/project-planning/adr-creation.agent.md", - "agents/project-planning/backlog-manager.agent.md", - "agents/project-planning/brd-builder.agent.md", - "agents/project-planning/functional-planner.agent.md", - "agents/project-planning/meeting-analyst.agent.md", - "agents/project-planning/network-isa95-planner.agent.md", - "agents/project-planning/prd-builder.agent.md", - "agents/project-planning/subagents/ado-backlog-executor.agent.md", - "agents/project-planning/subagents/brd-quality-reviewer.agent.md", - "agents/project-planning/subagents/github-backlog-executor.agent.md", - "agents/project-planning/subagents/jira-backlog-executor.agent.md", - "agents/project-planning/subagents/prd-quality-reviewer.agent.md", - "agents/project-planning/system-architecture-reviewer.agent.md", - "agents/project-planning/ux-ui-designer.agent.md", - "agents/rai-planning/rai-planner.agent.md", - "agents/rai-planning/rai-reviewer.agent.md", - "agents/rai-planning/subagents/rai-skill-assessor.agent.md", - "agents/security/security-planner.agent.md", - "agents/security/security-reviewer.agent.md", - "agents/security/sssc-planner.agent.md", - "agents/security/sssc-reviewer.agent.md", - "agents/security/subagents/codebase-profiler.agent.md", - "agents/security/subagents/cve-analyzer.agent.md", - "agents/security/subagents/finding-deep-verifier.agent.md", - "agents/security/subagents/report-generator.agent.md", - "agents/security/subagents/skill-assessor.agent.md", - "agents/security/subagents/supply-chain-skill-assessor.agent.md", - "agents/security/supply-chain-reviewer.agent.md" - ], - "commands": [ - "prompts/accessibility/accessibility-coverage-matrix.prompt.md", - "prompts/data-science/synth-data-generate.prompt.md", - "prompts/design-thinking/dt-canonical-deck.prompt.md", - "prompts/design-thinking/dt-figma-export.prompt.md", - "prompts/design-thinking/dt-handoff-implementation-space.prompt.md", - "prompts/design-thinking/dt-handoff-problem-space.prompt.md", - "prompts/design-thinking/dt-handoff-solution-space.prompt.md", - "prompts/design-thinking/dt-method-04-convergence.prompt.md", - "prompts/design-thinking/dt-method-04-ideation.prompt.md", - "prompts/design-thinking/dt-method-05-concepts.prompt.md", - "prompts/design-thinking/dt-method-05-evaluation.prompt.md", - "prompts/design-thinking/dt-method-06-building.prompt.md", - "prompts/design-thinking/dt-method-06-planning.prompt.md", - "prompts/design-thinking/dt-method-06-testing.prompt.md", - "prompts/design-thinking/dt-method-next.prompt.md", - "prompts/design-thinking/dt-resume-coaching.prompt.md", - "prompts/design-thinking/dt-start-project.prompt.md", - "prompts/experimental/cspell-config.prompt.md", - "prompts/experimental/graph-research.prompt.md", - "prompts/hve-core/ado-create-pull-request.prompt.md", - "prompts/hve-core/ado-get-build-info.prompt.md", - "prompts/hve-core/evals-import.prompt.md", - "prompts/hve-core/git-commit-message.prompt.md", - "prompts/hve-core/git-commit.prompt.md", - "prompts/hve-core/git-merge.prompt.md", - "prompts/hve-core/git-setup.prompt.md", - "prompts/hve-core/pr-review.prompt.md", - "prompts/hve-core/pull-request.prompt.md", - "prompts/hve-core/rpi.prompt.md", - "prompts/hve-core/vally-test-write.prompt.md", - "prompts/rai-planning/rai-capture.prompt.md", - "prompts/rai-planning/rai-plan-from-prd.prompt.md", - "prompts/rai-planning/rai-plan-from-security-plan.prompt.md", - "prompts/security/incident-response.prompt.md", - "prompts/security/risk-register.prompt.md", - "prompts/security/security-capture.prompt.md", - "prompts/security/security-plan-from-prd.prompt.md", - "prompts/security/security-review-llm.prompt.md", - "prompts/security/security-review-sbd.prompt.md", - "prompts/security/security-review-web.prompt.md", - "prompts/security/security-review.prompt.md", - "prompts/security/sssc-capture.prompt.md", - "prompts/security/sssc-from-brd.prompt.md", - "prompts/security/sssc-from-prd.prompt.md", - "prompts/security/sssc-from-security-plan.prompt.md", - "prompts/security/vex-implement.prompt.md", - "prompts/security/vex-scan.prompt.md", - "prompts/security/vex-triage.prompt.md" - ], - "rules": [ - "instructions/accessibility/accessibility-identity.instructions.md", - "instructions/accessibility/accessibility-license-posture.instructions.md", - "instructions/coding-standards/bash/bash.instructions.md", - "instructions/coding-standards/bicep/bicep.instructions.md", - "instructions/coding-standards/code-review/diff-computation.instructions.md", - "instructions/coding-standards/code-review/review-artifacts.instructions.md", - "instructions/coding-standards/csharp/csharp-tests.instructions.md", - "instructions/coding-standards/csharp/csharp.instructions.md", - "instructions/coding-standards/powershell/pester.instructions.md", - "instructions/coding-standards/powershell/powershell.instructions.md", - "instructions/coding-standards/python-script.instructions.md", - "instructions/coding-standards/python-tests.instructions.md", - "instructions/coding-standards/rust/rust-tests.instructions.md", - "instructions/coding-standards/rust/rust.instructions.md", - "instructions/coding-standards/terraform/terraform.instructions.md", - "instructions/coding-standards/uv-projects.instructions.md", - "instructions/design-thinking/dt-coach-telemetry.instructions.md", - "instructions/experimental/experiment-designer.instructions.md", - "instructions/experimental/graphify.instructions.md", - "instructions/experimental/mural/mural-bootstrap.instructions.md", - "instructions/experimental/mural/mural-destinations.instructions.md", - "instructions/experimental/mural/mural-human-record.instructions.md", - "instructions/experimental/mural/mural-log-hygiene.instructions.md", - "instructions/experimental/mural/mural-seeding-patterns.instructions.md", - "instructions/experimental/mural/mural-writeback-hygiene.instructions.md", - "instructions/experimental/mural/mural-writing-style.instructions.md", - "instructions/experimental/pptx.instructions.md", - "instructions/hve-core/commit-message.instructions.md", - "instructions/hve-core/copilot-tracking.instructions.md", - "instructions/hve-core/git-merge.instructions.md", - "instructions/hve-core/hve-builder.instructions.md", - "instructions/hve-core/licensing-posture.instructions.md", - "instructions/hve-core/markdown.instructions.md", - "instructions/hve-core/pull-request.instructions.md", - "instructions/hve-core/writing-style.instructions.md", - "instructions/privacy/privacy-identity.instructions.md", - "instructions/project-planning/adr-byo-template.instructions.md", - "instructions/project-planning/adr-handoff.instructions.md", - "instructions/project-planning/adr-identity.instructions.md", - "instructions/project-planning/adr-standards.instructions.md", - "instructions/project-planning/backlog-guardrails.instructions.md", - "instructions/project-planning/community-interaction.instructions.md", - "instructions/rai-planning/rai-identity.instructions.md", - "instructions/rai-planning/rai-license-posture.instructions.md", - "instructions/security/identity.instructions.md", - "instructions/security/sssc-planner.instructions.md", - "instructions/security/standards-mapping.instructions.md", - "instructions/security/tm7-generation-workflow.instructions.md", - "instructions/security/vex-generation.instructions.md", - "instructions/security/vex-standards.instructions.md", - "instructions/shared/coaching-patterns.instructions.md", - "instructions/shared/content-policy-citation.instructions.md", - "instructions/shared/disclaimer-language.instructions.md", - "instructions/shared/hve-core-location.instructions.md", - "instructions/shared/planner-identity-base.instructions.md", - "instructions/shared/telemetry-overlay.instructions.md", - "instructions/shared/untrusted-content-boundary.instructions.md" - ], - "skills": [ - "skills/accessibility/accessibility", - "skills/coding-standards/code-review", - "skills/coding-standards/python-foundational", - "skills/data-science/data-workstream-foundation", - "skills/data-science/ds-analysis-authoring", - "skills/data-science/ds-catalog", - "skills/data-science/ds-dataops", - "skills/data-science/ds-evaluation-design", - "skills/data-science/ds-feasibility", - "skills/data-science/ml-experimentation", - "skills/design-thinking/dt-coaching-foundation", - "skills/design-thinking/dt-curriculum", - "skills/design-thinking/dt-methods", - "skills/design-thinking/dt-rpi-integration", - "skills/design-thinking/ux-artifacts", - "skills/design-thinking/ux-coaching", - "skills/experimental/caveman", - "skills/experimental/copilot-otel-metrics", - "skills/experimental/customer-card-render", - "skills/experimental/demo-video", - "skills/experimental/mural", - "skills/experimental/powerpoint", - "skills/experimental/tts-voiceover", - "skills/experimental/video-to-gif", - "skills/experimental/vscode-playwright", - "skills/hve-core/architecture-diagrams", - "skills/hve-core/c4-architecture", - "skills/hve-core/documentation", - "skills/hve-core/hve-builder", - "skills/hve-core/hve-builder-tester", - "skills/hve-core/prompt-analyze", - "skills/hve-core/prompt-builder", - "skills/hve-core/prompt-refactor", - "skills/hve-core/vally-tests", - "skills/installer/hve-core-installer", - "skills/project-planning/adr-author", - "skills/project-planning/backlog-execute", - "skills/project-planning/backlog-management", - "skills/project-planning/backlog-plan", - "skills/project-planning/experiment-design", - "skills/project-planning/functional-planner", - "skills/project-planning/gitlab", - "skills/project-planning/jira", - "skills/project-planning/performance-slo-planner", - "skills/project-planning/privacy-standards", - "skills/project-planning/rai-planner", - "skills/project-planning/requirements-author", - "skills/project-planning/security-planning", - "skills/rai/rai-standards", - "skills/rpi/rpi-challenger", - "skills/rpi/rpi-implement", - "skills/rpi/rpi-plan", - "skills/rpi/rpi-plan-critique", - "skills/rpi/rpi-quick", - "skills/rpi/rpi-research", - "skills/rpi/rpi-review", - "skills/rpi/rpi-walkthrough", - "skills/security/gh-code-scanning", - "skills/security/mcsb", - "skills/security/owasp-agentic", - "skills/security/owasp-cicd", - "skills/security/owasp-infrastructure", - "skills/security/owasp-llm", - "skills/security/owasp-mcp", - "skills/security/owasp-top-10", - "skills/security/secure-by-design", - "skills/security/security-reviewer-formats", - "skills/security/supply-chain-security", - "skills/security/vex", - "skills/shared/backlog-templates", - "skills/shared/pr-reference", - "skills/shared/telemetry-foundations" - ], - "hooks": "hooks/shared/telemetry.json" -} diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index 294fe9afe..22de782e3 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -10,7 +10,7 @@ "plugins": [ { "name": "hve-core", - "source": ".github", + "source": ".", "description": "Opinionated, rapidly evolving HVE Core agentic SDLC patterns and tools", "version": "3.2.2", "author": { diff --git a/.github/skills/hve-core/documentation/references/code-doc-mapping.md b/.github/skills/hve-core/documentation/references/code-doc-mapping.md index a4d27542b..448a15015 100644 --- a/.github/skills/hve-core/documentation/references/code-doc-mapping.md +++ b/.github/skills/hve-core/documentation/references/code-doc-mapping.md @@ -17,7 +17,7 @@ asset reference page before the broader conceptual and contributor documentation | `.github/skills///**` | `docs/reference/skills//.md` | `docs/contributing/skills.md`, `docs/customization/skills.md` | | `scripts/**` | Not applicable | `scripts/README.md`, `docs/architecture/workflows.md` | | `extension/**` | Not applicable | `extension/PACKAGING.md` | -| `.github/plugin.json` | Not applicable | `docs/customization/packages.md`, `docs/plugins/hve-core.md` | +| `plugin.json` | Not applicable | `docs/customization/packages.md`, `docs/plugins/hve-core.md` | | `.github/plugin/marketplace.json` | Not applicable | `docs/getting-started/packages.md`, `docs/plugins/hve-core.md` | | `.devcontainer/**` | Not applicable | `docs/getting-started/`, `docs/customization/environment.md` | | `.github/workflows/**` | Not applicable | `docs/architecture/workflows.md` | diff --git a/.github/skills/hve-core/hve-builder/references/workflow-contract.md b/.github/skills/hve-core/hve-builder/references/workflow-contract.md index ce39b1a31..a5398e80e 100644 --- a/.github/skills/hve-core/hve-builder/references/workflow-contract.md +++ b/.github/skills/hve-core/hve-builder/references/workflow-contract.md @@ -38,7 +38,7 @@ When evidence supports a non-tool capability-surface change, return to scope and 5. Review and close static findings. For mutating modes and `review`, dispatch a generic Medium-profile static-review subagent in fresh context against the complete candidate. Do not provide author reasoning or the author log; provide known target files, purpose, requirements, and canonical criteria. Its bounded reads are lifecycle-stage work, not exploration. When the verdict is Revise and all findings remain inside the assessed boundary, apply the complete finding set in one correction batch, then run one targeted closure check limited to the original finding IDs and their acceptance evidence. Do not turn targeted closure into another full static review. Return to scope and route when architecture, capability, safety, acceptance, or the evidence boundary changed. Skip this stage for `validate`. 6. Test behavior on the final correction state. Continue only after static findings are closed. For mutating modes, classify every changed target before testing. For minor and medium changes, record a satisfied-and-skipped behavior gate. For major changes only, dispatch the `hve-builder-tester` skill with the intended reasoning profile, fidelity, isolation set, together set, requirements, and any eligible prior behavior report for a correction run. In review mode, do not require a source delta. Ask whether the existing target can affect model action or output. Dispatch `hve-builder-tester` for a behavior-bearing review target. For a no-runtime review target, record a satisfied-and-skipped behavior gate with execution `Not run`, verdict `Not applicable`, fidelity `Not applicable`, and an evidence-backed reason. When required review behavior cannot execute, record behavior verdict `Not available` and overall `Deferred` with the exact rerun condition. Skip this stage for `validate`. 7. Validate the final correction state. For mutating modes and `validate`, dispatch a generic Low-profile validation subagent using `stage-dispatch.md` after source artifacts are at their real paths and the approved correction batch is complete. Classify caller-named or already-known applicable non-mutating checks as `local` or `CI`; generic validation executes local checks only. A specifically requested named CI lane may run directly, while its specialized setup remains separate. Record CI evidence that did not run truthfully and resolve required missing CI evidence as `Deferred`. In `review`, run validation only when requested. - * When distribution scope applies, a new or removed distributable artifact requires its wiring to be complete before validation passes: tracked path-and-license eligibility, synchronized `.github/plugin.json` membership including every declared subagent, the single prepared extension output, and plugin validation. Record wiring that is not applicable, with the reason, rather than omitting it silently. + * When distribution scope applies, a new or removed distributable artifact requires its wiring to be complete before validation passes: tracked path-and-license eligibility, synchronized root `plugin.json` membership including every declared subagent, the single prepared extension output, and plugin validation. Record wiring that is not applicable, with the reason, rather than omitting it silently. 8. Resolve. Apply the outcome resolver below. Re-enter authoring for an open original finding or failed final-state gate inside scope; return to routing for a changed assessed boundary; stop on Pass, Revise, Deferred, or Blocked. Stages may run in parallel only when neither consumes the other's output. An independent `rpi-research` handoff can run beside baseline review only when it cannot change the baseline target set. Authoring, candidate static review, correction-batch closure, final-state behavior testing, and final-state validation remain ordered because each consumes the preceding source state. diff --git a/.github/skills/installer/hve-core-installer/references/component-installation.md b/.github/skills/installer/hve-core-installer/references/component-installation.md index 2d1fd524c..73510809c 100644 --- a/.github/skills/installer/hve-core-installer/references/component-installation.md +++ b/.github/skills/installer/hve-core-installer/references/component-installation.md @@ -11,7 +11,7 @@ description: Phase 7 component installation and the Phase 7 upgrade mode for the After Phase 6 completes, offer users the option to copy HVE-Core components into their target repository. This phase ONLY applies to clone-based installation methods (1-6), NOT to extension installation. -A component is one agent, prompt, instruction, or complete skill declared by `.github/plugin.json`. Every Phase 7 operation validates its component paths against that manifest before writing. Component paths use installer form and map to canonical target paths without flattening: +A component is one agent, prompt, instruction, or complete skill declared by root `plugin.json`. Every Phase 7 operation validates its component paths against that manifest before writing. The manifest uses repository-relative `.github/...` paths; component selections use installer form and map to canonical target paths without flattening: | Component path | Target path | @@ -50,14 +50,14 @@ Your choice? (1/2/3) User input handling: -* "1", "all", "complete" → Select every agent, prompt, instruction, and skill declared by `.github/plugin.json`; set the selection name to `all` +* "1", "all", "complete" → Select every agent, prompt, instruction, and skill declared by root `plugin.json`; set the selection name to `all` * "2", "choose", "custom", "components" → Proceed to the Custom Selection sub-flow * "3", "skip", "none", "no" → Skip to the final success report * Unclear response → Ask for clarification ### Custom Selection Sub-Flow -When the user selects option 2, read `.github/plugin.json` from the HVE-Core source at `$hveCoreBasePath`. Present the declared components grouped by kind. Convert canonical manifest paths to installer paths before passing them to collision detection or component copy. +When the user selects option 2, read root `plugin.json` from the HVE-Core source at `$hveCoreBasePath`. Present the declared components grouped by kind. Convert canonical repository-relative manifest paths to installer paths before passing them to collision detection or component copy. ### Selection Resolution @@ -243,7 +243,7 @@ At Phase 7 start, check for an existing manifest. Output keys: `UPGRADE_MODE`, and when a manifest exists, `INSTALLED_VERSION`, `SOURCE_VERSION`, `VERSION_CHANGED`, `INSTALLED_PROFILE`, and `INSTALLED_COMPONENTS`. An unsupported `schemaVersion` fails with clean-reinstall guidance. -Replay `INSTALLED_COMPONENTS` after validating each recorded path against the current `.github/plugin.json` membership. Pass the recorded profile as `-SelectionName` or the Bash selection-name argument. If any recorded component is no longer declared, stop and ask the user to choose a current component set before writing. +Replay `INSTALLED_COMPONENTS` after validating each recorded path against the current root `plugin.json` membership. Pass the recorded profile as `-SelectionName` or the Bash selection-name argument. If any recorded component is no longer declared, stop and ask the user to choose a current component set before writing. ### Upgrade Prompt diff --git a/.github/skills/installer/hve-core-installer/scripts/component-copy.ps1 b/.github/skills/installer/hve-core-installer/scripts/component-copy.ps1 index b842d9e61..c28081afe 100644 --- a/.github/skills/installer/hve-core-installer/scripts/component-copy.ps1 +++ b/.github/skills/installer/hve-core-installer/scripts/component-copy.ps1 @@ -133,7 +133,7 @@ function ConvertTo-PackageComponentPath { input and tracking manifests use package form. Paths outside the four installable fields, such as hooks, pass through unchanged. .PARAMETER ManifestPath - Plugin-root-relative path declared by the canonical manifest. + Repository-root-relative path declared by the canonical manifest. .OUTPUTS [string] Installer package path, or the original path when no mapping applies. #> @@ -145,7 +145,10 @@ function ConvertTo-PackageComponentPath { [string]$ManifestPath ) - $segments = $ManifestPath -split '/', 2 + if (-not $ManifestPath.StartsWith('.github/', [System.StringComparison]::Ordinal)) { + throw "Plugin manifest path '$ManifestPath' must start with '.github/'." + } + $segments = $ManifestPath.Substring('.github/'.Length) -split '/', 2 if ($segments.Count -lt 2) { return $ManifestPath } $manifestRoot = $segments[0] $relative = $segments[1] @@ -165,7 +168,7 @@ $sourceRoot = (Resolve-Path -LiteralPath $HveCoreBasePath).Path $targetBase = (Resolve-Path -LiteralPath $TargetRoot).Path $manifestPath = Join-Path $targetBase '.hve-tracking.json' -$pluginManifestPath = Join-Path $sourceRoot '.github/plugin.json' +$pluginManifestPath = Join-Path $sourceRoot 'plugin.json' if (-not (Test-Path -LiteralPath $pluginManifestPath -PathType Leaf)) { throw "Plugin manifest not found: $pluginManifestPath" } diff --git a/.github/skills/installer/hve-core-installer/scripts/component-copy.sh b/.github/skills/installer/hve-core-installer/scripts/component-copy.sh index e6f6104eb..adc30447e 100644 --- a/.github/skills/installer/hve-core-installer/scripts/component-copy.sh +++ b/.github/skills/installer/hve-core-installer/scripts/component-copy.sh @@ -122,11 +122,15 @@ manifest_root_descriptor() { esac } -# The plugin manifest stores canonical source identities while installer input and +# The plugin manifest stores repository-root-relative source identities while installer input and # manifests use package form. A path whose root is outside the four installable # fields, such as hooks/, carries through unprojected so manifest load never fails. to_package_component_path() { local manifest_path="$1" + [[ "$manifest_path" == .github/* ]] || { + fail "Plugin manifest path '$manifest_path' must start with '.github/'." + } + manifest_path="${manifest_path#.github/}" [[ "$manifest_path" == */* ]] || { echo "$manifest_path" return 0 @@ -201,7 +205,7 @@ main() { target_base=$(cd "$target_root_arg" && pwd) local manifest_path="$target_base/.hve-tracking.json" - local plugin_manifest_path="$source_root/.github/plugin.json" + local plugin_manifest_path="$source_root/plugin.json" [[ -f "$plugin_manifest_path" ]] || fail "Plugin manifest not found: $plugin_manifest_path" local -A membership=() diff --git a/.github/skills/installer/hve-core-installer/tests/component-copy.Tests.ps1 b/.github/skills/installer/hve-core-installer/tests/component-copy.Tests.ps1 index 9838b3f20..f7c74c31c 100644 --- a/.github/skills/installer/hve-core-installer/tests/component-copy.Tests.ps1 +++ b/.github/skills/installer/hve-core-installer/tests/component-copy.Tests.ps1 @@ -64,17 +64,16 @@ BeforeAll { name = 'hve-core' version = $Version agents = @( - 'agents/hve-core/rpi-agent.agent.md' - 'agents/hve-core/subagents/rpi-planner.agent.md' - 'agents/experimental/pptx.agent.md' + '.github/agents/hve-core/rpi-agent.agent.md' + '.github/agents/hve-core/subagents/rpi-planner.agent.md' + '.github/agents/experimental/pptx.agent.md' ) - commands = @('prompts/hve-core/rpi.prompt.md') - rules = @('instructions/hve-core/copilot-tracking.instructions.md') - skills = @('skills/rpi/rpi-plan') - hooks = 'hooks/shared/telemetry.json' + commands = @('.github/prompts/hve-core/rpi.prompt.md') + rules = @('.github/instructions/hve-core/copilot-tracking.instructions.md') + skills = @('.github/skills/rpi/rpi-plan') + hooks = '.github/hooks/shared/telemetry.json' } - $pluginManifestPath = Join-Path $source '.github/plugin.json' - New-Item -ItemType Directory -Path (Split-Path $pluginManifestPath -Parent) -Force | Out-Null + $pluginManifestPath = Join-Path $source 'plugin.json' $manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $pluginManifestPath -NoNewline Set-Content -LiteralPath (Join-Path $source 'package.json') -Value "{ `"version`": `"$Version`" }" -NoNewline @@ -165,14 +164,16 @@ Describe 'component-copy parameter contract' -Tag 'Unit' { } It 'Resolves membership from the plugin manifest in the PowerShell implementation' { - $script:powerShellSource | Should -Match '\.github/plugin\.json' + $script:powerShellSource | Should -Match "Join-Path \`$sourceRoot 'plugin\.json'" + $script:powerShellSource | Should -Not -Match '\.github/plugin\.json' $script:powerShellSource | Should -Not -Match '(?i)marketplace' $script:powerShellSource | Should -Match '\$SelectionName' $script:powerShellSource | Should -Match "schemaVersion = \`$schemaVersion" } It 'Resolves membership from the plugin manifest in the Bash implementation' { - $script:bashSource | Should -Match '\.github/plugin\.json' + $script:bashSource | Should -Match '\$source_root/plugin\.json' + $script:bashSource | Should -Not -Match '\.github/plugin\.json' $script:bashSource | Should -Not -Match '(?i)marketplace' $script:bashSource | Should -Match 'selection_name' $script:bashSource | Should -Match 'schemaVersion: \$schema' @@ -400,19 +401,36 @@ Describe 'component-copy preflight rejection' -Tag 'Unit' { } It 'Rejects a source without a plugin manifest' { - Remove-Item -LiteralPath (Join-Path $script:fixture.Source '.github/plugin.json') -Force + Remove-Item -LiteralPath (Join-Path $script:fixture.Source 'plugin.json') -Force { Invoke-ComponentCopy -Fixture $script:fixture -Component @('agents/hve-core/rpi-agent.md') } | Should -Throw -ExpectedMessage '*Plugin manifest not found*' } It 'Rejects a plugin manifest that declares no installable component' { - '{ "name": "hve-core", "hooks": "hooks/shared/telemetry.json" }' | - Set-Content -LiteralPath (Join-Path $script:fixture.Source '.github/plugin.json') -NoNewline + '{ "name": "hve-core", "hooks": ".github/hooks/shared/telemetry.json" }' | + Set-Content -LiteralPath (Join-Path $script:fixture.Source 'plugin.json') -NoNewline { Invoke-ComponentCopy -Fixture $script:fixture -Component @('agents/hve-core/rpi-agent.md') } | Should -Throw -ExpectedMessage '*declares no installable components*' } + + It 'Rejects installable manifest entries outside .github in both implementations' { + $powerShellFixture = New-ComponentCopyFixture + $bashFixture = New-ComponentCopyFixture + foreach ($fixture in @($powerShellFixture, $bashFixture)) { + $manifestPath = Join-Path $fixture.Source 'plugin.json' + $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json + $manifest.agents = @('agents/hve-core/rpi-agent.agent.md') + $manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $manifestPath -NoNewline + } + + { Invoke-ComponentCopy -Fixture $powerShellFixture -Component @('agents/hve-core/rpi-agent.md') } | + Should -Throw -ExpectedMessage "*must start with '.github/'*" + $bashOutput = Invoke-BashComponentCopy -Fixture $bashFixture -Component @('agents/hve-core/rpi-agent.md') + $LASTEXITCODE | Should -Not -Be 0 + $bashOutput | Should -Match "must start with '\.github/'" + } } Describe 'component-copy manifest schema gate' -Tag 'Unit' { @@ -540,7 +558,7 @@ Describe 'component-copy report-only preflight' -Tag 'Unit' { Describe 'component-copy production manifest selection' -Tag 'Unit' { BeforeAll { - # A real subset of .github/plugin.json membership, so the production + # A real subset of root plugin.json membership, so the production # manifest itself gates the selection rather than a test-local recipe. $script:ProductionSelection = @( 'agents/hve-core/rpi-agent.md' @@ -660,11 +678,11 @@ Describe 'component-copy PowerShell and Bash parity' -Tag 'Unit' -Skip:(-not $sc It 'Accepts adjacent dots inside a valid filename in both implementations' { $sourceRelative = '.github/agents/hve-core/foo..bar.agent.md' - $manifestRelative = 'agents/hve-core/foo..bar.agent.md' + $manifestRelative = '.github/agents/hve-core/foo..bar.agent.md' $component = 'agents/hve-core/foo..bar.md' foreach ($fixture in @($script:powerShellFixture, $script:bashFixture)) { Set-Content -LiteralPath (Join-Path $fixture.Source $sourceRelative) -Value '# Adjacent dots' -NoNewline - $manifestPath = Join-Path $fixture.Source '.github/plugin.json' + $manifestPath = Join-Path $fixture.Source 'plugin.json' $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json $manifest.agents = @($manifest.agents) + $manifestRelative $manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $manifestPath -NoNewline @@ -749,7 +767,7 @@ Describe 'component-copy PowerShell and Bash parity' -Tag 'Unit' -Skip:(-not $sc It 'Exits non-zero in both implementations when the plugin manifest is absent' { foreach ($fixture in @($script:powerShellFixture, $script:bashFixture)) { - Remove-Item -LiteralPath (Join-Path $fixture.Source '.github/plugin.json') -Force + Remove-Item -LiteralPath (Join-Path $fixture.Source 'plugin.json') -Force } { Invoke-ComponentCopy -Fixture $script:powerShellFixture -Component @('agents/hve-core/rpi-agent.md') } | diff --git a/.github/workflows/README.md b/.github/workflows/README.md index cc88bdd38..43288af9c 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -2,7 +2,7 @@ title: GitHub Actions Workflows description: Modular CI/CD workflow architecture for validation, security scanning, and automated maintenance author: HVE Core Team -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: reference keywords: - github actions @@ -119,7 +119,7 @@ release-state decision. Odd/even minor parity remains repository policy aligned with VS Code Marketplace guidance and behavior, rather than a requirement of `MAJOR.MINOR.PATCH` syntax. -Release branches and exact tags retain the relative `.github` plugin root. Their reviewed, release-gated VSIX assets remain SBOM-covered, attested, and immutable. The ref-less main catalog instead sources current canonical `.github` content and has no published-release assurance. +Release branches and exact tags retain the repository-root plugin source from their selected snapshots. Their reviewed, release-gated VSIX assets remain SBOM-covered, attested, and immutable. The ref-less main catalog instead sources current root `plugin.json` and canonical `.github` artifacts from `main` and has no published-release assurance. Final publication mints a release GitHub App token and atomically runs `gh release edit --prerelease --draft=false`; the resulting published event diff --git a/.github/workflows/release-prerelease-prepare.yml b/.github/workflows/release-prerelease-prepare.yml index 017ad26a3..57e9c4627 100644 --- a/.github/workflows/release-prerelease-prepare.yml +++ b/.github/workflows/release-prerelease-prepare.yml @@ -272,7 +272,7 @@ jobs: 'package.json' \ 'package-lock.json' \ 'extension/templates/package.template.json' \ - '.github/plugin.json' \ + 'plugin.json' \ '.github/plugin/marketplace.json') merge_ref() { diff --git a/.github/workflows/release-prerelease.yml b/.github/workflows/release-prerelease.yml index 66eeb4054..ef852fdcc 100644 --- a/.github/workflows/release-prerelease.yml +++ b/.github/workflows/release-prerelease.yml @@ -591,7 +591,7 @@ jobs: RELEASE_VERSION: ${{ needs.release-please.outputs.version }} run: | set -euo pipefail - for entry in "package.json:.version" "package-lock.json:.version" ".release-please-prerelease-manifest.json:.\".\"" "extension/templates/package.template.json:.version" ".github/plugin.json:.version" ".github/plugin/marketplace.json:.metadata.version" ".github/plugin/marketplace.json:.plugins[0].version"; do + for entry in "package.json:.version" "package-lock.json:.version" ".release-please-prerelease-manifest.json:.\".\"" "extension/templates/package.template.json:.version" "plugin.json:.version" ".github/plugin/marketplace.json:.metadata.version" ".github/plugin/marketplace.json:.plugins[0].version"; do file="${entry%%:*}" path="${entry#*:}" actual=$(jq -r "$path" "$file") diff --git a/.github/workflows/release-stable-publish.yml b/.github/workflows/release-stable-publish.yml index 5366e4a5d..e9aa43230 100644 --- a/.github/workflows/release-stable-publish.yml +++ b/.github/workflows/release-stable-publish.yml @@ -594,7 +594,7 @@ jobs: exit 1 fi - for entry in "package.json:.version" "package-lock.json:.version" ".release-please-manifest.json:.\".\"" "extension/templates/package.template.json:.version" ".github/plugin.json:.version" ".github/plugin/marketplace.json:.metadata.version" ".github/plugin/marketplace.json:.plugins[0].version"; do + for entry in "package.json:.version" "package-lock.json:.version" ".release-please-manifest.json:.\".\"" "extension/templates/package.template.json:.version" "plugin.json:.version" ".github/plugin/marketplace.json:.metadata.version" ".github/plugin/marketplace.json:.plugins[0].version"; do file="${entry%%:*}" path="${entry#*:}" actual=$(jq -r "$path" "$file") diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml index ecf047292..8c7aa6f5d 100644 --- a/.github/workflows/release-stable.yml +++ b/.github/workflows/release-stable.yml @@ -320,7 +320,7 @@ jobs: 'package.json' \ 'package-lock.json' \ 'extension/templates/package.template.json' \ - '.github/plugin.json' \ + 'plugin.json' \ '.github/plugin/marketplace.json' ) ALLOWLIST=$(printf '%s\n' "${RELEASE_OWNED_FILES[@]}") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a70a8669e..e3395ff53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ title: Contributing description: Guidelines for contributing code, documentation, and improvements to the HVE Core project author: HVE Core Team -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: guide keywords: - contributing @@ -25,11 +25,12 @@ All types of contributions are encouraged and valued. See the [Table of Contents ## Table of Contents +* [Table of Contents](#table-of-contents) * [Build and Validation Requirements](#build-and-validation-requirements) * [Required Tools](#required-tools) * [Validation Commands](#validation-commands) * [Development Environment](#development-environment) -* [Table of Contents](#table-of-contents) +* [Label Management](#label-management) * [Code of Conduct](#code-of-conduct) * [I Have a Question](#i-have-a-question) * [I Want To Contribute](#i-want-to-contribute) @@ -117,6 +118,19 @@ For additional validation commands specific to AI artifacts (agents, prompts, in We strongly recommend using the provided DevContainer, which comes pre-configured with all required tools. See the [DevContainer README](./.devcontainer/README.md) for setup instructions. +## Label Management + +Repository labels are declared in [`.github/labels.yml`](.github/labels.yml) +and synchronized by the [Label Sync](.github/workflows/label-sync.yml) workflow +on pushes to `main` or through `workflow_dispatch`. + +| Task | How | +|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Add a label | Add `name`, `color` (bare hex without `#`), and `description` to `.github/labels.yml`, then push to `main` | +| Update a label | Edit the existing entry's `color` or `description` | +| Rename a label | Add the old name to the new canonical entry's `aliases` array; synchronization migrates existing assignments | +| Delete a label | Remove it in the [GitHub Labels UI](https://github.com/microsoft/hve-core/labels); removing the declaration does not delete it because synchronization is additive | + ## Code of Conduct This project and everyone participating in it is governed by the diff --git a/README.md b/README.md index 5ec03e46a..1a759d5fd 100644 --- a/README.md +++ b/README.md @@ -106,17 +106,6 @@ Full documentation is available at ****. | [AI Artifacts Architecture](docs/architecture/ai-artifacts.md) | Prompt engineering framework and artifact types | | [Validation Standards](docs/contributing/ai-artifacts-common.md) | CI/CD validation pipeline and quality gates | -## Label Management - -Repository labels are declared in [`.github/labels.yml`](.github/labels.yml) and synced automatically by the [Label Sync](.github/workflows/label-sync.yml) workflow on push to `main` or via manual `workflow_dispatch`. - -| Task | How | -|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Add a label** | Add an entry with `name`, `color` (bare hex, no `#`), and `description` to `.github/labels.yml`, then push to `main` | -| **Update a label** | Edit the existing entry's `color` or `description` | -| **Rename a label** | Add an `aliases` array under the new canonical name listing the old name; the sync migrates existing assignments automatically | -| **Delete a label** | Remove it manually in the [GitHub Labels UI](https://github.com/microsoft/hve-core/labels). Deleting an entry from the file does **not** delete it from GitHub (the workflow runs in additive mode) | - ## Contributing We appreciate contributions! Whether you're fixing typos or adding new components: diff --git a/TRANSPARENCY-NOTE.md b/TRANSPARENCY-NOTE.md index 050c01c36..69afad476 100644 --- a/TRANSPARENCY-NOTE.md +++ b/TRANSPARENCY-NOTE.md @@ -2,7 +2,7 @@ title: "Transparency Note: HVE Core" description: "Public Transparency Note for HVE Core, a prompt-engineering and agentic-customization framework distributed by microsoft/hve-core." author: HVE Core Maintainers -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: overview keywords: - responsible-ai @@ -135,7 +135,7 @@ For a set of files, "performance" is not a model-accuracy score. It is how well Quality rests on a few things: * **CI checks on every pull request.** Markdown linting, frontmatter validation, model-reference checks, link checking, PowerShell and Python linting, YAML validation, collection-metadata and marketplace validation, dependency-pinning and action-version checks, copyright-header checks, and skill-structure validation all run on each pull request and block merge on failure. -* **Plugin-manifest gate.** The complete `.github/plugin.json` membership is derived from tracked distributable source paths; drift, invalid locator metadata, missing component paths, or invalid hooks block merge. +* **Plugin-manifest gate.** The complete root `plugin.json` membership is derived from tracked distributable `.github` source paths; missing or untracked root metadata, drift, invalid locator metadata, escaping or absent component paths, or invalid hooks block merge. * **Human review.** Every file change needs human review. Supply-chain and dependency checks surface to reviewers. * **Manifest parity and release review.** Stable and PreRelease ship the same complete plugin manifest. Stable release review happens through promotion of a reviewed PreRelease tree into `release/stable`; channel selection changes release cadence and assurance rather than component membership. * **Feedback channel.** GitHub issues on `microsoft/hve-core` are the main place for bugs, requests, and concerns. diff --git a/docs/agents/code-review/language-skills.md b/docs/agents/code-review/language-skills.md index 4843c5468..eb5d4a36c 100644 --- a/docs/agents/code-review/language-skills.md +++ b/docs/agents/code-review/language-skills.md @@ -16,7 +16,7 @@ tags: - skills - coding-standards author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to estimated_reading_time: 8 --- @@ -226,7 +226,7 @@ A frontend team authors `.github/skills/coding-standards/northwind/react-standar | Engineering fundamentals | `docs/templates/engineering-fundamentals.md` | | Skill authoring guide | [Authoring Custom Skills](../../customization/skills.md) | | Contributing skills | [Contributing: Skills](../../contributing/skills.md) | -| HVE Core plugin manifest | `.github/plugin.json` | +| HVE Core plugin manifest | `plugin.json` | *🤖 Crafted with precision by ✨Copilot following brilliant human instruction, diff --git a/docs/architecture/README.md b/docs/architecture/README.md index aa1f186cd..7864b72cb 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -3,7 +3,7 @@ title: Architecture Overview description: HVE Core system design and component relationships sidebar_position: 1 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: concept keywords: - architecture @@ -43,7 +43,7 @@ graph TD | GitHub Workflows | `.github/workflows/` | CI/CD pipelines for validation, security, and release automation | | Access Control | `.github/CODEOWNERS` | Path-based review requirements and ownership | | MCP Configuration | `.vscode/mcp.json` | Model Context Protocol server definitions | -| Plugin Manifest | `.github/plugin.json` | Deterministic membership for the plugin and extension | +| Plugin Manifest | `plugin.json` | Deterministic membership for the plugin and extension | | Test Infrastructure | `scripts/tests/` | Pester test suites with fixtures and mocks | ## Core Subsystems @@ -63,7 +63,7 @@ Automation scripts handle quality assurance and development workflows. The scrip ### Plugins -`.github/plugin.json` defines the complete `hve-core` membership from package-scoped canonical agents, prompts, instructions, and distributable skills. `.github/plugin/marketplace.json` contains one relative locator to the `.github` plugin root. The Copilot CLI installs from that root directly, and extension preparation uses the same manifest for one VSIX. +Root `plugin.json` defines the complete `hve-core` membership from package-scoped canonical agents, prompts, instructions, and distributable skills discovered under `.github`. `.github/plugin/marketplace.json` contains one relative locator to the repository root. Agent Plugins and the Copilot CLI install from that root and resolve its README and LICENSE, while extension preparation uses the same membership for one VSIX with extension-owned metadata. See [scripts/plugins/README.md](https://github.com/microsoft/hve-core/blob/main/scripts/plugins/README.md) for synchronization and validation commands. diff --git a/docs/architecture/ai-artifacts.md b/docs/architecture/ai-artifacts.md index 28f7c112d..d5f56cbd6 100644 --- a/docs/architecture/ai-artifacts.md +++ b/docs/architecture/ai-artifacts.md @@ -3,7 +3,7 @@ title: AI Artifacts Architecture description: Prompt, agent, and instruction delegation model for Copilot customizations sidebar_position: 2 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: concept keywords: - ai artifacts @@ -215,7 +215,7 @@ Copilot discovers skills automatically when their description matches the curren ## Plugin Identity -`.github/plugin.json` is the sole component-membership authority for the `hve-core` plugin and VSIX. `.github/plugin/marketplace.json` contains one relative locator to the `.github` plugin root and does not repeat membership. +Root `plugin.json` is the sole component-membership authority for the `hve-core` plugin and VSIX. `.github/plugin/marketplace.json` contains one relative locator to the repository root and does not repeat membership. The plugin details surface resolves root `README.md` and `LICENSE`; the VSIX keeps `extension/README.md` and `extension/LICENSE` as its separate metadata surface. The one product identity includes every distributable agent, prompt, instruction, and skill plus the fixed telemetry hook. Stable and PreRelease use the same manifest membership. @@ -249,19 +249,19 @@ The extension scans these directories at startup: * `.github/instructions/{package-id}/` for technology standards * `.github/skills/{package-id}/` for utility packages -These paths reflect the conventional directory structure. Artifact inclusion is controlled by `.github/plugin.json`. Root-level artifacts (files directly under `.github/{type}/` with no subdirectory) are repo-specific, excluded from discovery, and never packaged into extension builds. +These paths reflect the conventional directory structure. Artifact inclusion is controlled by root `plugin.json`, whose declarations are repository-relative `.github/...` paths. Artifacts directly under `.github/{type}/` with no package subdirectory are repo-specific, excluded from discovery, and never packaged into extension builds. Stable and PreRelease differ in source ownership, cadence, and version, not component membership. ### Extension Identities -HVE Core has one Copilot plugin root at `.github` and one VSIX identity, `ise-hve-essentials.hve-core`. +HVE Core has one Copilot plugin root at the repository root and one VSIX identity, `ise-hve-essentials.hve-core`. Artifact discovery remains bounded to eligible package-scoped paths under `.github`. The VS Code extension is prepared with `Prepare-Extension.ps1` and packaged with `Package-Extension.ps1`. Both Stable and PreRelease preparation write the same component set to the single extension manifest and README. No Copilot package assembly step exists. The plugin includes the telemetry hook. VS Code has no declarative hook contribution point, so extension users configure its location manually. -For a repository-owned selection, the installer validates chosen component paths against `.github/plugin.json`. It copies agents, prompts, instructions, and complete distributable skill directories while preserving repository-relative paths; hooks are not copied. +For a repository-owned selection, the installer validates chosen component paths against root `plugin.json`. It converts repository-relative manifest declarations to installer form, then copies agents, prompts, instructions, and complete distributable skill directories while preserving canonical `.github` target paths; hooks are not copied. ### Activation Context diff --git a/docs/architecture/workflows.md b/docs/architecture/workflows.md index dba1c4563..d07381346 100644 --- a/docs/architecture/workflows.md +++ b/docs/architecture/workflows.md @@ -3,7 +3,7 @@ title: Build Workflows description: GitHub Actions CI/CD pipeline architecture for validation, security, and release automation sidebar_position: 3 author: WilliamBerryiii -ms.date: 2026-08-16 +ms.date: 2026-08-19 ms.topic: overview keywords: - github actions @@ -326,7 +326,7 @@ OIDC and `vsce`. Both channel workflows validate the one-entry catalog and call the generic publisher for `hve-core`. The publisher validates inputs, downloads `hve-core-.vsix`, verifies its lane-specific attestation, prepares the locked publisher toolchain from protected `main`, and publishes through Azure OIDC and `vsce`. -Stable and PreRelease package the same `.github/plugin.json` membership into the same extension identity. The channel controls version, release source, and the VS Code Marketplace pre-release flag, not component inclusion. +Stable and PreRelease package the same root `plugin.json` membership into the same extension identity. Each selected branch or exact-tag snapshot carries its own root manifest, README, and LICENSE. The channel controls version, release source, and the VS Code Marketplace pre-release flag, not component inclusion. ### Version Channels diff --git a/docs/contributing/ai-artifacts-common.md b/docs/contributing/ai-artifacts-common.md index b40cc614a..de6403535 100644 --- a/docs/contributing/ai-artifacts-common.md +++ b/docs/contributing/ai-artifacts-common.md @@ -3,7 +3,7 @@ title: 'AI Artifacts Common Standards' description: 'Common standards and quality gates for all AI artifact contributions to hve-core' sidebar_position: 2 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: reference keywords: - contributing @@ -156,7 +156,7 @@ npm run lint:models:refresh ## Plugin Membership -`.github/plugin.json` is the distribution authority for the single `hve-core` plugin and VSIX. `npm run plugin:sync` derives its membership from tracked package-scoped artifacts: +Root `plugin.json` is the distribution authority for the single `hve-core` plugin and VSIX. `npm run plugin:sync` derives repository-relative membership from tracked package-scoped artifacts under `.github`: * Agents under `.github/agents//**/*.agent.md` * Prompts under `.github/prompts//**/*.prompt.md` @@ -176,7 +176,7 @@ Stable and PreRelease contain the same manifest membership. Their differences ar When you add or change an artifact: 1. Author the artifact under a package subdirectory of `.github/`. -2. Run `npm run plugin:sync` to update `.github/plugin.json`. +2. Run `npm run plugin:sync` to update root `plugin.json`. 3. Update `docs/plugins/hve-core.md` when user-visible capabilities or identity guidance changed. 4. Run `npm run plugin:validate`. 5. Run `npm run docs:generate:check` and the focused tests for the changed artifact kind. diff --git a/docs/contributing/custom-agents.md b/docs/contributing/custom-agents.md index 5be95182c..3ee1bb2ff 100644 --- a/docs/contributing/custom-agents.md +++ b/docs/contributing/custom-agents.md @@ -3,7 +3,7 @@ title: 'Contributing Agents to HVE Core' description: 'Requirements and standards for contributing GitHub Copilot agent files to hve-core' sidebar_position: 5 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - contributing @@ -145,7 +145,7 @@ Agent files are typically organized in a package subdirectory by convention: ``` > [!NOTE] -> Tracked agents beneath a package subdirectory are included automatically when `npm run plugin:sync` derives `.github/plugin.json`. +> Tracked agents beneath a `.github/agents//` subdirectory are included automatically when `npm run plugin:sync` derives root `plugin.json`. ### Naming Convention @@ -330,7 +330,7 @@ named dependencies that the agent must dispatch by name. ## Plugin Manifest Registration -Distributable agents must use the canonical path `.github/agents///.agent.md`. `npm run plugin:sync` adds the `.github`-root-relative path to the `agents` array in `.github/plugin.json`. +Distributable agents must use the canonical path `.github/agents///.agent.md`. `npm run plugin:sync` adds that repository-relative path to the `agents` array in root `plugin.json`. Ensure every declared subagent is also eligible for manifest inclusion. Update `docs/plugins/hve-core.md` when the user-visible agent surface changes, then run `npm run plugin:sync`, `npm run plugin:validate`, and `npm run docs:generate:check`. diff --git a/docs/contributing/hooks.md b/docs/contributing/hooks.md index 377b37a03..220af72ab 100644 --- a/docs/contributing/hooks.md +++ b/docs/contributing/hooks.md @@ -3,7 +3,7 @@ title: Contributing Hooks description: How to implement, register, and validate hook artifacts in hve-core sidebar_position: 7 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - hooks @@ -32,7 +32,7 @@ Hooks use package-oriented source folders. Use this structure for hook contribut | `.github/hooks//.json` | Hook manifest that maps lifecycle events to executable commands | | `.github/hooks///` | Hook implementation scripts and support files | | `scripts/linting/schemas/hook-manifest.schema.json` | JSON Schema (draft-07) that defines the manifest contract | -| `.github/plugin.json` | Canonical hook declaration for the plugin | +| `plugin.json` | Canonical repository-relative hook declaration for the plugin | | `docs/plugins/hve-core.md` | Durable plugin documentation | Manifests live one package level down (`.github/hooks//`). A flat `.github/hooks/.json` is treated as a repo-specific artifact and is excluded from distribution. @@ -46,7 +46,7 @@ The telemetry hook is the current reference implementation: 1. Add a manifest at `.github/hooks//.json`. 2. Add executable scripts under `.github/hooks///`. -3. Register the manifest through the `hooks` field in `.github/plugin.json`. +3. Register the manifest through the `hooks` field in root `plugin.json` using its repository-relative `.github/...` path. 4. Document the hook in `docs/plugins/hve-core.md`. 5. Add or update docs under `docs/` for setup and usage. diff --git a/docs/contributing/instructions.md b/docs/contributing/instructions.md index e757bb72f..72e20aec4 100644 --- a/docs/contributing/instructions.md +++ b/docs/contributing/instructions.md @@ -3,7 +3,7 @@ title: 'Contributing Instructions to HVE Core' description: 'Requirements and standards for contributing GitHub Copilot instruction files to hve-core' sidebar_position: 3 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - contributing @@ -56,7 +56,7 @@ Instruction files are typically organized in a package subdirectory by conventio > [!NOTE] -> Tracked instructions beneath a package subdirectory are included automatically when `npm run plugin:sync` derives `.github/plugin.json`. +> Tracked instructions beneath a `.github/instructions//` subdirectory are included automatically when `npm run plugin:sync` derives root `plugin.json`. #### Examples @@ -142,7 +142,7 @@ lastUpdated: '2025-11-19' Distributable instructions must use the canonical path `.github/instructions///.instructions.md`. Root-level instructions remain repository-specific and outside plugin membership. -Run `npm run plugin:sync` to add the `.github`-root-relative path to the `rules` array in `.github/plugin.json`. Update `docs/plugins/hve-core.md` when the user-visible instruction surface changes, then run `npm run plugin:validate` and `npm run docs:generate:check`. +Run `npm run plugin:sync` to add the repository-relative `.github/...` path to the `rules` array in root `plugin.json`. Update `docs/plugins/hve-core.md` when the user-visible instruction surface changes, then run `npm run plugin:validate` and `npm run docs:generate:check`. ## Content Structure Standards diff --git a/docs/contributing/prompts.md b/docs/contributing/prompts.md index eddd408a1..a64cf87e5 100644 --- a/docs/contributing/prompts.md +++ b/docs/contributing/prompts.md @@ -3,7 +3,7 @@ title: 'Contributing Prompts to HVE Core' description: 'Requirements and standards for contributing GitHub Copilot prompt files to hve-core' sidebar_position: 4 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - contributing @@ -41,7 +41,7 @@ Prompt files are typically organized in a package subdirectory by convention: ``` > [!NOTE] -> Tracked prompts beneath a package subdirectory are included automatically when `npm run plugin:sync` derives `.github/plugin.json`. +> Tracked prompts beneath a `.github/prompts//` subdirectory are included automatically when `npm run plugin:sync` derives root `plugin.json`. ### Naming Convention @@ -208,7 +208,7 @@ Prompts that delegate to a custom agent via `agent:` typically omit the activati ## Plugin Manifest Registration -Distributable prompts must use the canonical path `.github/prompts///.prompt.md`. `npm run plugin:sync` adds the `.github`-root-relative path to the `commands` array in `.github/plugin.json`. +Distributable prompts must use the canonical path `.github/prompts///.prompt.md`. `npm run plugin:sync` adds that repository-relative path to the `commands` array in root `plugin.json`. Update `docs/plugins/hve-core.md` when the user-visible prompt surface changes, then run `npm run plugin:validate` and `npm run docs:generate:check`. diff --git a/docs/contributing/release-process.md b/docs/contributing/release-process.md index 22a1fb504..27b634d26 100644 --- a/docs/contributing/release-process.md +++ b/docs/contributing/release-process.md @@ -2,7 +2,7 @@ title: Release Process description: Release HVE Core through reviewed PreRelease metadata and Stable promotion workflows sidebar_position: 9 -ms.date: 2026-08-16 +ms.date: 2026-08-19 ms.topic: how-to author: WilliamBerryiii keywords: @@ -133,7 +133,7 @@ prepares channel version metadata and changelog changes on its release branch: * Updated `package.json` and `package-lock.json` versions * Updated `extension/templates/package.template.json` version -* Updated `.github/plugin.json` version +* Updated root `plugin.json` version * Updated `.github/plugin/marketplace.json` metadata and sole entry version * Updated channel manifest * Updated `CHANGELOG.md` @@ -347,7 +347,14 @@ The VS Code extension is published to two same-content channels with different c ### Membership Policy -`.github/plugin.json` is identical in membership across Stable and PreRelease. `npm run plugin:sync` derives it from tracked package-scoped agents, prompts, instructions, and distributable skills; the fixed telemetry hook is included on both channels. +Root `plugin.json` is identical in membership across Stable and PreRelease. +`npm run plugin:sync` derives it from tracked package-scoped agents, prompts, +instructions, and distributable skills under `.github`; the fixed telemetry +hook is included on both channels. Promotion and release validation version +root `plugin.json`, and each moving branch or exact tag resolves root README +and LICENSE from its selected snapshot. The VSIX continues to package +`extension/README.md` and `extension/LICENSE` from that immutable release +source. Channel selection changes version, source ownership, release assurance, and the VS Code Marketplace pre-release flag. It never filters components. diff --git a/docs/contributing/skills.md b/docs/contributing/skills.md index 22335ffae..5e3925118 100644 --- a/docs/contributing/skills.md +++ b/docs/contributing/skills.md @@ -3,7 +3,7 @@ title: Contributing Skills to HVE Core description: Requirements and standards for contributing skill packages to hve-core sidebar_position: 6 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - skills @@ -76,7 +76,7 @@ Skill files are typically organized in a package subdirectory by convention: ``` > [!NOTE] -> Tracked skills beneath a package subdirectory are included automatically when `npm run plugin:sync` derives `.github/plugin.json`, unless the skill's top-level license has a noncommercial qualifier. +> Tracked skills beneath a `.github/skills//` subdirectory are included automatically when `npm run plugin:sync` derives root `plugin.json`, unless the skill's top-level license has a noncommercial qualifier. The `scripts/` directory is **optional**. When present, it **MUST** contain at least one PowerShell script for PowerShell or cross-platform skills, and it **SHOULD** contain at least one `.sh` file when a bash implementation is also provided. Python skills may instead package executable modules under `scripts//__init__.py` and still satisfy the scripts requirement. Skills without scripts are valid and function as documentation-driven knowledge packages. @@ -240,7 +240,7 @@ This example demonstrates a skill incorporating third-party content with provena Distributable skills must use the canonical path `.github/skills///SKILL.md`, and the directory name must equal the skill `name`. Skills whose top-level license contains a noncommercial qualifier are not distributed. -Run `npm run plugin:sync` to add the skill directory to the `skills` array in `.github/plugin.json`. Update `docs/plugins/hve-core.md` when the user-visible skill surface changes, then run `npm run plugin:validate`, `npm run validate:skills`, and `npm run docs:generate:check`. +Run `npm run plugin:sync` to add the repository-relative `.github/...` skill directory to the `skills` array in root `plugin.json`. Update `docs/plugins/hve-core.md` when the user-visible skill surface changes, then run `npm run plugin:validate`, `npm run validate:skills`, and `npm run docs:generate:check`. ## SKILL.md Content Structure diff --git a/docs/customization/README.md b/docs/customization/README.md index b53f8c852..ec04dd0ca 100644 --- a/docs/customization/README.md +++ b/docs/customization/README.md @@ -2,7 +2,7 @@ title: Customizing HVE Core description: Overview of customization approaches from lightweight settings to full fork-and-extend, with role-based entry points author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: overview sidebar_position: 1 keywords: @@ -72,7 +72,7 @@ graph LR | Create a reusable workflow | Prompt | `.github/prompts/{package-id}/name.prompt.md` | Low | | Build a specialized Copilot assistant | Agent | `.github/agents/{package-id}/name.agent.md` | Medium | | Package domain expertise | Skill | `.github/skills/{package-id}/{skill}/SKILL.md` | Medium | -| Change managed distribution membership | Plugin Manifest | `.github/plugin.json`, `docs/plugins/hve-core.md` | Medium | +| Change managed distribution membership | Plugin Manifest | `plugin.json`, `docs/plugins/hve-core.md` | Medium | | Add custom validation or packaging | Build System | `scripts/`, `package.json` | High | | Diverge from upstream entirely | Fork and Extend | Full repository | High | diff --git a/docs/customization/build-system.md b/docs/customization/build-system.md index 88c64dc88..ece875bec 100644 --- a/docs/customization/build-system.md +++ b/docs/customization/build-system.md @@ -2,7 +2,7 @@ title: Build System and Validation description: Understand plugin manifest synchronization, schema validation, npm scripts, and CI checks for customizing HVE Core author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - build system @@ -15,7 +15,7 @@ estimated_reading_time: 8 ## Plugin Manifest Synchronization -`.github/plugin.json` is the deterministic distribution manifest for the one `hve-core` plugin and VSIX. +Root `plugin.json` is the deterministic distribution manifest for the one `hve-core` plugin and VSIX. Its component paths are repository-relative, while discovery remains scoped to eligible package directories under `.github`. `npm run plugin:sync` runs `Sync-PluginManifest.ps1`, which derives agents, prompts, instructions, and distributable skills from git-tracked package-scoped paths. It preserves plugin metadata, synchronizes the repository version, and retains the fixed telemetry hook. @@ -29,7 +29,7 @@ npm run plugin:validate ``` > [!IMPORTANT] -> The Copilot plugin root is `.github`. Do not create a copied plugin tree or plugin ZIP. +> The Copilot plugin root is the repository root. Do not create a copied plugin tree or plugin ZIP; keep distributable artifacts in their canonical `.github` package directories. ## Schema Validation System diff --git a/docs/customization/forking.md b/docs/customization/forking.md index 72f22e36b..2878d7a5e 100644 --- a/docs/customization/forking.md +++ b/docs/customization/forking.md @@ -2,7 +2,7 @@ title: Forking and Extending HVE Core description: Fork HVE Core to create a fully customized prompt engineering framework with upstream sync and Copilot-assisted adaptation author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: tutorial keywords: - forking @@ -99,7 +99,7 @@ README, and marketplace presentation. See the Agent and prompt files live under `.github/agents/` and `.github/prompts/`. Restructure these directories to match your organization's team topology or domain boundaries. Run -`npm run plugin:sync` to reflect eligible paths in `.github/plugin.json`. +`npm run plugin:sync` to reflect eligible `.github` paths in root `plugin.json`. ### 3. MCP Servers @@ -155,7 +155,7 @@ Conflicts typically occur in files you have customized. Common conflict points: * `package.json` (script modifications) * `.markdownlint.json` (rule adjustments) -* `.github/plugin.json` membership after artifacts are added or removed +* Root `plugin.json` membership after artifacts are added or removed * Workflow files (permission or job changes) For each conflict, evaluate whether to keep your change, accept the upstream change, or diff --git a/docs/customization/packages.md b/docs/customization/packages.md index f76bd1b6c..7ac9024a3 100644 --- a/docs/customization/packages.md +++ b/docs/customization/packages.md @@ -2,7 +2,7 @@ title: Managing the HVE Core Plugin Manifest description: Maintain the single HVE Core plugin and VSIX membership through the canonical manifest author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - marketplace @@ -14,14 +14,14 @@ estimated_reading_time: 6 ## Manifest Authority -`.github/plugin.json` is the operational distribution definition for the one `hve-core` plugin and VSIX. Its `agents`, `commands`, `rules`, and `skills` arrays are deterministic outputs of tracked path and license classification. The fixed `hooks` value includes the telemetry hook. +Root `plugin.json` is the operational distribution definition for the one `hve-core` plugin and VSIX. Its `agents`, `commands`, `rules`, and `skills` arrays are deterministic repository-relative outputs of tracked path and license classification under `.github`. The fixed `hooks` value includes the telemetry hook. `.github/plugin/marketplace.json` contains one `hve-core` entry with the relative source `.github`. It owns locator metadata only and must not repeat component arrays or package policy. ## Add Or Change A Component 1. Add canonical artifacts under `.github///`. -2. Run `npm run plugin:sync` to derive `.github/plugin.json` from tracked paths. +2. Run `npm run plugin:sync` to derive root `plugin.json` from tracked `.github` paths. 3. Update `docs/plugins/hve-core.md` when user-visible capabilities or identity guidance changed. 4. Run the local-safe checks in [Validation and Package Staging](#validation-and-package-staging). @@ -54,7 +54,7 @@ npm run test:ps -- -TestPath scripts/tests/extension/ ## Selective Adoption -The installer resolves all components from `.github/plugin.json`. Users can copy the complete manifest or a custom selection. +The installer resolves all components from root `plugin.json`. Users can copy the complete manifest or a custom selection; the installer converts repository-relative `.github/...` declarations to its selection form without changing canonical target paths. Schema version 2 records `selection.profile` and `selection.components`. File records track selected components without package identity. The installer copies agents, prompts, instructions, and complete distributable skill directories while preserving source-relative paths. diff --git a/docs/customization/team-adoption.md b/docs/customization/team-adoption.md index 41447b0b4..9d4ffdb7a 100644 --- a/docs/customization/team-adoption.md +++ b/docs/customization/team-adoption.md @@ -2,7 +2,7 @@ title: Team Adoption and Governance description: Establish governance practices, naming conventions, onboarding patterns, and change management for team-wide HVE Core adoption author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - governance @@ -40,7 +40,7 @@ outputs). ### Phase 3: Skills and Shared Distribution -Package domain knowledge into skills for complex, multi-step workflows. Place related artifacts under package-scoped `.github` paths and synchronize `.github/plugin.json` for managed distribution, or use selective cloning for a repository-owned subset. +Package domain knowledge into skills for complex, multi-step workflows. Place related artifacts under package-scoped `.github` paths and synchronize root `plugin.json` for managed distribution, or use selective cloning for a repository-owned subset. ### Measuring Adoption Progress diff --git a/docs/docusaurus/src/data/__tests__/resolvePluginManifest.test.ts b/docs/docusaurus/src/data/__tests__/resolvePluginManifest.test.ts index 86d2318b2..49adf89aa 100644 --- a/docs/docusaurus/src/data/__tests__/resolvePluginManifest.test.ts +++ b/docs/docusaurus/src/data/__tests__/resolvePluginManifest.test.ts @@ -21,7 +21,7 @@ type JsonObject = Record; function locator(overrides: JsonObject = {}): JsonObject { return { name: 'hve-core', - source: '.github', + source: '.', version: '1.2.3', ...overrides, }; @@ -32,11 +32,11 @@ function manifest(overrides: JsonObject = {}): JsonObject { name: 'hve-core', description: 'HVE Core description', version: '1.2.3', - agents: ['agents/one.agent.md', 'agents/two.agent.md'], - commands: ['commands/one.prompt.md'], + agents: ['.github/agents/one.agent.md', '.github/agents/two.agent.md'], + commands: ['.github/commands/one.prompt.md'], rules: [], - skills: ['skills/one'], - hooks: 'hooks/shared/telemetry.json', + skills: ['.github/skills/one'], + hooks: '.github/hooks/shared/telemetry.json', ...overrides, }; } @@ -52,7 +52,7 @@ function writeFixture( fs.writeFileSync(pluginLocatorPath, JSON.stringify(catalog), 'utf-8'); if (pluginManifest !== null) { fs.writeFileSync( - path.join(root, '.github', 'plugin.json'), + path.join(root, 'plugin.json'), JSON.stringify(pluginManifest), 'utf-8', ); @@ -136,4 +136,4 @@ describe('loadPackageCards failures', () => { 'manifest.description must be a non-empty string', ); }); -}); \ No newline at end of file +}); diff --git a/docs/getting-started/install.md b/docs/getting-started/install.md index b54718ebc..9c32308c9 100644 --- a/docs/getting-started/install.md +++ b/docs/getting-started/install.md @@ -3,7 +3,7 @@ title: Installing HVE Core description: Install the HVE Core extension or plugin, or adopt selected components from a clone sidebar_position: 2 author: Microsoft -ms.date: 2026-08-16 +ms.date: 2026-08-19 ms.topic: how-to keywords: [installation, setup, github copilot, marketplace, selective clone] estimated_reading_time: 4 @@ -22,7 +22,7 @@ Stable and PreRelease contain the same complete component set. They differ in so Teams that need a repository-owned subset can use `hve-core-installer`. 1. Clone or pin the HVE Core version to adopt. -2. Choose every component declared by `.github/plugin.json`, or select a subset. +2. Choose every component declared by root `plugin.json`, or select a subset. 3. Review component kinds and collisions before writes. 4. Choose automatic source updates or a controlled pinned version. @@ -46,7 +46,7 @@ The installer can copy agents, prompts, instructions, and complete skill directo ⭐ **VS Code Extension** is the recommended method for most users who don't need customization. > [!NOTE] -> HVE Core uses one identity across the plugin and extension. `.github/plugin.json` owns membership, and `.github/plugin/marketplace.json` contains one relative locator to the `.github` plugin root. +> HVE Core uses one identity across the plugin and extension. Root `plugin.json` owns membership, and `.github/plugin/marketplace.json` contains one relative locator to the repository root. Plugin clients resolve root README and LICENSE; the VSIX keeps its extension-owned metadata. ### Distribution Relationships @@ -54,9 +54,9 @@ The installer can copy agents, prompts, instructions, and complete skill directo graph LR accTitle: HVE Core distribution relationships accDescr: The repository contains the marketplace locator and plugin manifest. The locator resolves the plugin root, while the manifest supplies membership to both the Copilot plugin and VS Code extension. - REPO["microsoft/hve-core
(canonical source)"] --> MANIFEST[".github/plugin.json
(complete membership)"] + REPO["microsoft/hve-core
(plugin root)"] --> MANIFEST["plugin.json
(complete membership)"] REPO --> CATALOG[".github/plugin/marketplace.json
(one relative locator)"] - CATALOG --> ROOT[".github
(plugin root)"] + CATALOG --> ROOT["repository root
(README and LICENSE)"] MANIFEST --> PLUGIN["hve-core plugin"] MANIFEST --> EXT["hve-core VSIX"] ROOT --> PLUGIN @@ -75,15 +75,15 @@ release branches that advance through `main` to `release/prerelease` to `release/stable`. An exact channel tag freezes one release catalog and its source payloads. -| Use case | Marketplace registration | Source resolution | -|----------------------|--------------------------------------------|--------------------------------------| -| Development tip | `microsoft/hve-core` | Current `main` `.github` plugin root | -| Moving PreRelease | `microsoft/hve-core#release/prerelease` | Current reviewed PreRelease branch | -| Moving Stable | `microsoft/hve-core#release/stable` | Current reviewed Stable branch | -| Immutable PreRelease | `microsoft/hve-core#prerelease-v` | One exact PreRelease tag | -| Immutable Stable | `microsoft/hve-core#v` | One exact Stable tag | +| Use case | Marketplace registration | Source resolution | +|----------------------|--------------------------------------------|------------------------------------| +| Development tip | `microsoft/hve-core` | Current `main` repository root | +| Moving PreRelease | `microsoft/hve-core#release/prerelease` | Current reviewed PreRelease branch | +| Moving Stable | `microsoft/hve-core#release/stable` | Current reviewed Stable branch | +| Immutable PreRelease | `microsoft/hve-core#prerelease-v` | One exact PreRelease tag | +| Immutable Stable | `microsoft/hve-core#v` | One exact Stable tag | -A moving release registration selects the catalog and relative `.github` source currently committed to its reviewed branch. The branch can advance, while an exact-tag registration remains fixed. +A moving release registration selects the catalog and repository-root source currently committed to its reviewed branch. The branch can advance, while an exact-tag registration remains fixed. A published channel release is the assurance boundary for its immutable tag. The release workflow applies review and release gates, produces one VSIX and its @@ -140,7 +140,7 @@ registrations; confirm the behavior supported by your Copilot CLI version. ### Clone Methods -The installer validates each selected component against `.github/plugin.json`. Schema version 2 stores `selection.profile` and `selection.components` without package identity. File records identify component ownership, and hooks remain plugin-only. +The installer validates each selected component against root `plugin.json`. Schema version 2 stores `selection.profile` and `selection.components` without package identity. File records identify component ownership, and hooks remain plugin-only. ## Developer Setup diff --git a/docs/getting-started/methods/cli-plugins.md b/docs/getting-started/methods/cli-plugins.md index c24b42e20..164dfb7e5 100644 --- a/docs/getting-started/methods/cli-plugins.md +++ b/docs/getting-started/methods/cli-plugins.md @@ -3,7 +3,7 @@ title: Copilot CLI Plugin description: Register an HVE Core catalog ref and install the complete hve-core plugin sidebar_position: 2 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: how-to keywords: - copilot cli @@ -41,7 +41,7 @@ copilot plugin marketplace add microsoft/hve-core#prerelease-v copilot plugin marketplace add microsoft/hve-core#v ``` -`main` is the development tip. `release/prerelease` and `release/stable` are moving registrations that resolve the current reviewed branch catalog and relative `.github` plugin root. Exact-tag registrations freeze the catalog, manifest, and plugin source together. +`main` is the development tip. `release/prerelease` and `release/stable` are moving registrations that resolve the current reviewed branch catalog and repository-root plugin package. Exact-tag registrations freeze the catalog, root manifest, README, LICENSE, and plugin source together. A published channel release provides release assurance for its exact tag, including release gates, SBOMs, attestations, provenance verification, and the @@ -90,7 +90,7 @@ Each plugin includes: | Skills | Yes | Self-contained skill packages | | Instructions | No | Included for `#file:` references, not auto-applied | -The one marketplace entry resolves the `.github` plugin root. `.github/plugin.json` declares the complete agents, commands, rules, skills, and hook membership that the client installs. No generated plugin tree or plugin ZIP participates in Git-source installation. +The one marketplace entry resolves the repository root. Root `plugin.json` declares the complete agents, commands, rules, skills, and hook membership as repository-relative `.github/...` paths. The client resolves the root README and LICENSE; no generated plugin tree or plugin ZIP participates in Git-source installation. ## Limitations @@ -128,21 +128,21 @@ After installing a plugin, agents and named commands are available in your CLI s CLI plugins provide two distinct interaction patterns: -| Mode | Command | Behavior | -|---------------|--------------------|--------------------------------------------------------------| -| Named Command | `/git-commit` | Executes a predefined workflow, then returns to default mode | -| Skill | `/rpi-research` | Activates one reusable RPI phase capability | -| Agent Mode | `/agent RPI Agent` | Switches to the coordinated RPI lifecycle | +| Mode | Command | Behavior | +|---------------|-----------------------------|--------------------------------------------------------------| +| Named Command | `/git-commit` | Executes a predefined workflow, then returns to default mode | +| Skill | `/rpi-research` | Activates one reusable RPI phase capability | +| Agent Mode | `/agent hve-core:rpi-agent` | Switches to the coordinated RPI lifecycle | Named commands (prompts) run a specific workflow and produce structured output. Agent mode enables freeform conversation with a specialized agent until you exit. > [!IMPORTANT] > The CLI does not switch to a custom agent on behalf of an agent-bound -> prompt. Select `RPI Agent` when you want lifecycle coordination, or invoke a +> prompt. Select `hve-core:rpi-agent` when you want lifecycle coordination, or invoke a > direct phase skill such as `/rpi-research`: > > ```text -> /agent RPI Agent +> /agent hve-core:rpi-agent > Research API authentication patterns before deciding whether planning is ready. > ``` > @@ -169,10 +169,16 @@ Continue with follow-up questions in the same session: ### Available Agents -After installing the hve-core plugin, these agents are available via `/agent `: +After installing the hve-core plugin, these agents are available via `/agent `: -* RPI Agent - coordinates Research, Plan, Implement, Review, and Follow-up -* Documentation - audits, authors, and validates documentation +* `hve-core:rpi-agent` coordinates Research, Plan, Implement, Review, and Follow-up +* `hve-core:documentation` audits, authors, and validates documentation + +Start an interactive scripted invocation with the same qualified identifier: + +```bash +copilot --agent hve-core:rpi-agent +``` For the complete list, run `/help` in a CLI session to see all available commands and agents. @@ -180,7 +186,7 @@ For the complete list, run `/help` in a CLI session to see all available command * Use **named commands** (`/git-commit-message`, `/git-merge`) directly from default mode for workflows that do not require a custom agent. * Use direct skills (`/rpi-research`, `/rpi-plan`, `/rpi-implement`, `/rpi-review`) for one bounded RPI responsibility. -* Use **agent mode** with `/agent RPI Agent` for lifecycle coordination. +* Use **agent mode** with `/agent hve-core:rpi-agent` for lifecycle coordination. * Stay in **agent mode** for exploratory conversations, follow-up questions, or tasks that don't fit a predefined prompt. --- diff --git a/docs/getting-started/package-migration.md b/docs/getting-started/package-migration.md index 5675c4619..2179d3bd1 100644 --- a/docs/getting-started/package-migration.md +++ b/docs/getting-started/package-migration.md @@ -3,7 +3,7 @@ title: Migrate to the HVE Core Identity description: Move retired package installations to the single HVE Core plugin or extension sidebar_position: 4 author: Microsoft -ms.date: 2026-08-16 +ms.date: 2026-08-19 ms.topic: how-to keywords: - migration @@ -15,7 +15,7 @@ keywords: estimated_reading_time: 8 --- -HVE Core now publishes one `hve-core` plugin and one `ise-hve-essentials.hve-core` extension. `.github/plugin.json` owns the complete distributable membership, and `.github/plugin/marketplace.json` contains one relative locator to `.github`. +HVE Core now publishes one `hve-core` plugin and one `ise-hve-essentials.hve-core` extension. Root `plugin.json` owns the complete distributable membership, and `.github/plugin/marketplace.json` contains one relative locator to the repository root. Choose the migration path for your host. Neither GitHub Copilot nor VS Code provides a universal automatic migration between different published identities. diff --git a/docs/getting-started/packages.md b/docs/getting-started/packages.md index 91cddd6f8..4a2095cf4 100644 --- a/docs/getting-started/packages.md +++ b/docs/getting-started/packages.md @@ -3,7 +3,7 @@ title: HVE Core Identity and Channels description: Understand the single HVE Core identity and its development, PreRelease, and Stable channels sidebar_position: 3 author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: overview keywords: - packages @@ -15,7 +15,7 @@ keywords: HVE Core publishes one plugin named `hve-core` and one VS Code extension named `ise-hve-essentials.hve-core`. -`.github/plugin.json` is the deterministic membership authority for both products. `.github/plugin/marketplace.json` contains one `hve-core` locator whose relative source is `.github`; it does not repeat component membership. +Root `plugin.json` is the deterministic membership authority for both products. `.github/plugin/marketplace.json` contains one `hve-core` locator whose relative source is the repository root; it does not repeat component membership. Plugin clients resolve root README and LICENSE, while the VSIX packages `extension/README.md` and `extension/LICENSE`. ## Stable and PreRelease @@ -37,9 +37,9 @@ Source moves in one direction through reviewed target-based promotion PRs: no tag. Release-please opens a separate managed PR on the target branch, and merging that PR creates the channel's exact tag and draft release. -`main` is not a release-please target. It is a ref-less development-tip channel, so a marketplace refresh followed by a plugin update resolves current `main` content from `.github`. Release branches, tags, and published releases own release state and history; PreRelease publication does not synchronize versions or `CHANGELOG.md` state back into `main`. +`main` is not a release-please target. It is a ref-less development-tip channel, so a marketplace refresh followed by a plugin update resolves current `main` content from the repository root. Release branches, tags, and published releases own release state and history; PreRelease publication does not synchronize versions or `CHANGELOG.md` state back into `main`. -The plugin root remains `.github` on every branch and exact tag. The extension identity remains `ise-hve-essentials.hve-core` on both Marketplace channels. +The plugin root remains the repository root on every branch and exact tag, with artifact discovery bounded to package-scoped `.github` paths. The extension identity remains `ise-hve-essentials.hve-core` on both Marketplace channels. Release branches are reviewed moving channels. Registering a branch resolves its current committed manifest and source, while an exact tag fixes both catalog selection and source content. diff --git a/docs/plugins/hve-core.md b/docs/plugins/hve-core.md index 289b015fb..7f4c2670a 100644 --- a/docs/plugins/hve-core.md +++ b/docs/plugins/hve-core.md @@ -3,7 +3,7 @@ title: HVE Core description: Complete HVE Core plugin identity, distribution channels, membership policy, and capability inventory sidebar_position: 1 author: Microsoft -ms.date: 2026-08-16 +ms.date: 2026-08-19 ms.topic: reference keywords: - package @@ -16,7 +16,7 @@ HVE Core is the single plugin and extension identity for all distributable HVE C > [!CAUTION] > HVE Core evolves quickly. Evaluate these assets as adaptable engineering patterns, review changes before adoption, and pin an exact release tag when reproducible source is required. -`.github/plugin.json` owns complete membership. `.github/plugin/marketplace.json` contains one `hve-core` entry whose relative source is `.github`; it does not repeat component membership. +Root `plugin.json` owns complete membership. `.github/plugin/marketplace.json` contains one `hve-core` entry whose relative source is the repository root; it does not repeat component membership. The plugin details view resolves root `README.md` and `LICENSE`, while the VSIX retains its own generated README and license. Stable and PreRelease contain the same complete agents, prompts, instructions, skills, and telemetry hook. Channel selection changes source ownership, cadence, version, release assurance, and VS Code Marketplace behavior, not membership. @@ -34,17 +34,17 @@ copilot plugin install hve-core@hve-core Install the extension as `ise-hve-essentials.hve-core`. For a repository-owned subset, use `hve-core-installer` to choose all manifest components or a custom selection. The installer records `selection.profile` and `selection.components` in `.hve-tracking.json` and does not copy hooks. -The full path inventory remains machine-readable in `.github/plugin.json`. Agent, prompt, instruction, and skill reference pages are available under `docs/reference/`. +The full repository-relative path inventory remains machine-readable in root `plugin.json`. Agent, prompt, instruction, and skill reference pages are available under `docs/reference/`. ## Component Inventory -| Component kind | Manifest field | Source convention | -|----------------|----------------|-----------------------------------------------| -| Agents | `agents` | `agents//**/*.agent.md` | -| Prompts | `commands` | `prompts//**/*.prompt.md` | -| Instructions | `rules` | `instructions//**/*.instructions.md` | -| Skills | `skills` | `skills///SKILL.md` | -| Hooks | `hooks` | `hooks/shared/telemetry.json` | +| Component kind | Manifest field | Source convention | +|----------------|----------------|-------------------------------------------------------| +| Agents | `agents` | `.github/agents//**/*.agent.md` | +| Prompts | `commands` | `.github/prompts//**/*.prompt.md` | +| Instructions | `rules` | `.github/instructions//**/*.instructions.md` | +| Skills | `skills` | `.github/skills///SKILL.md` | +| Hooks | `hooks` | `.github/hooks/shared/telemetry.json` | ### Capability Areas diff --git a/extension/PACKAGING.md b/extension/PACKAGING.md index 74ef21254..6fc9c9030 100644 --- a/extension/PACKAGING.md +++ b/extension/PACKAGING.md @@ -2,7 +2,7 @@ title: Extension Packaging Guide description: Developer guide for packaging and publishing the HVE Core VS Code extension author: Microsoft -ms.date: 2026-08-13 +ms.date: 2026-08-19 ms.topic: reference --- @@ -92,7 +92,7 @@ packages one VSIX from the release tag, attaches the same VSIX assurance plus Stable OpenVEX, and publishes the release with an App token. The resulting event triggers Stable Marketplace publication. -Release branches and exact tags retain the relative `.github` plugin root. Their reviewed, immutable VSIX assets remain release-gated, SBOM-covered, and attested. The ref-less main catalog represents `main`, receives no post-release synchronization, and requires an explicit marketplace refresh and plugin update; its bytes have no release gate, SBOM, or attestation. +Release branches and exact tags retain the repository-root plugin source from their selected snapshots. Their reviewed, immutable VSIX assets remain release-gated, SBOM-covered, and attested. The ref-less main catalog represents `main`, receives no post-release synchronization, and requires an explicit marketplace refresh and plugin update; its bytes have no release gate, SBOM, or attestation. The moving registrations are `microsoft/hve-core#release/prerelease` and `microsoft/hve-core#release/stable`; immutable registrations use @@ -109,7 +109,7 @@ patch or hotfix needs a separate explicit manifest and release-state decision. ## Packaging Pipeline Overview -Extension packaging is a two-step process: **Prepare** maps `.github/plugin.json` +Extension packaging is a two-step process: **Prepare** maps root `plugin.json` into VS Code contributions, then **Package** stages its tracked files, runs the pinned `vsce`, and cleans up. @@ -132,11 +132,11 @@ flowchart LR ### Manifest Projection -The prepare step reads `.github/plugin.json` and maps each declared component to its canonical `.github` source and VS Code contribution type. Hooks are omitted because VS Code has no declarative hook contribution point. +The prepare step reads root `plugin.json` and maps each repository-relative `.github/...` component to its VS Code contribution type. Hooks are omitted because VS Code has no declarative hook contribution point. Root `README.md` and `LICENSE` are plugin metadata, not extension contributions; the VSIX retains `extension/README.md` and `extension/LICENSE`. ```mermaid flowchart TB - MANIFEST["Plugin Manifest
.github/plugin.json"] --> AG[Agents] + MANIFEST["Plugin Manifest
plugin.json"] --> AG[Agents] MANIFEST --> PR[Commands to Prompts] MANIFEST --> IN[Rules to Instructions] MANIFEST --> SK[Skills] @@ -171,7 +171,7 @@ npm run extension:prepare:prerelease The preparation script automatically: -* Reads identity, description, and membership from `.github/plugin.json` +* Reads identity, description, and membership from root `plugin.json` * Maps canonical source paths to VS Code contribution kinds * Refreshes the one HVE Core extension manifest and README * Writes HVE Core's VS Code contribution paths @@ -300,7 +300,7 @@ patch or hotfix remains a separate explicit manifest and release-state decision. Each managed PR synchronizes `package.json`, `package-lock.json`, -`extension/templates/package.template.json`, `.github/plugin.json`, +`extension/templates/package.template.json`, root `plugin.json`, `.github/plugin/marketplace.json`, the channel manifest, and `CHANGELOG.md` on its release branch. `Prepare-Extension.ps1` generates `extension/package.json` from the template @@ -399,7 +399,7 @@ See [Plugin Membership](../docs/contributing/ai-artifacts-common.md#plugin-membe ## Marketplace Build -`.github/plugin.json` defines the complete extension membership. `.github/plugin/marketplace.json` provides one relative `hve-core` locator for Copilot CLI registration. +Root `plugin.json` defines the complete extension membership. `.github/plugin/marketplace.json` provides one relative `hve-core` locator to the repository root for Copilot CLI registration. Extension preparation consumes only manifest-declared `.github` components and does not add root plugin metadata to contribution membership. Prepare and package directly: diff --git a/plugin.json b/plugin.json new file mode 100644 index 000000000..8df8a0041 --- /dev/null +++ b/plugin.json @@ -0,0 +1,261 @@ +{ + "name": "hve-core", + "description": "Opinionated, rapidly evolving HVE Core agentic SDLC patterns and tools", + "version": "3.2.2", + "author": { + "name": "Microsoft", + "url": "https://www.microsoft.com" + }, + "homepage": "https://github.com/microsoft/hve-core", + "repository": "https://github.com/microsoft/hve-core", + "license": "MIT", + "keywords": [ + "hve", + "hve-core", + "agents", + "prompts", + "instructions", + "skills" + ], + "agents": [ + ".github/agents/accessibility/accessibility-planner.agent.md", + ".github/agents/accessibility/accessibility-reviewer.agent.md", + ".github/agents/accessibility/subagents/accessibility-framework-assessor.agent.md", + ".github/agents/accessibility/subagents/accessibility-surface-inventory.agent.md", + ".github/agents/coding-standards/code-review.agent.md", + ".github/agents/coding-standards/subagents/code-review-accessibility.agent.md", + ".github/agents/coding-standards/subagents/code-review-explainer.agent.md", + ".github/agents/coding-standards/subagents/code-review-functional.agent.md", + ".github/agents/coding-standards/subagents/code-review-pr.agent.md", + ".github/agents/coding-standards/subagents/code-review-readiness.agent.md", + ".github/agents/coding-standards/subagents/code-review-security.agent.md", + ".github/agents/coding-standards/subagents/code-review-standards.agent.md", + ".github/agents/coding-standards/subagents/code-review-walkback.agent.md", + ".github/agents/data-science/data-workstream-coach.agent.md", + ".github/agents/design-thinking/dt-coach.agent.md", + ".github/agents/design-thinking/dt-learning-tutor.agent.md", + ".github/agents/experimental/experiment-designer.agent.md", + ".github/agents/experimental/pptx.agent.md", + ".github/agents/experimental/subagents/pptx-subagent.agent.md", + ".github/agents/hve-core/documentation.agent.md", + ".github/agents/hve-core/rpi-agent.agent.md", + ".github/agents/hve-core/subagents/hve-artifact-tester.agent.md", + ".github/agents/hve-core/subagents/rpi-planner.agent.md", + ".github/agents/hve-core/subagents/rpi-researcher.agent.md", + ".github/agents/hve-core/subagents/vally-test-author.agent.md", + ".github/agents/privacy/privacy-planner.agent.md", + ".github/agents/privacy/privacy-reviewer.agent.md", + ".github/agents/project-planning/adr-creation.agent.md", + ".github/agents/project-planning/backlog-manager.agent.md", + ".github/agents/project-planning/brd-builder.agent.md", + ".github/agents/project-planning/functional-planner.agent.md", + ".github/agents/project-planning/meeting-analyst.agent.md", + ".github/agents/project-planning/network-isa95-planner.agent.md", + ".github/agents/project-planning/prd-builder.agent.md", + ".github/agents/project-planning/subagents/ado-backlog-executor.agent.md", + ".github/agents/project-planning/subagents/brd-quality-reviewer.agent.md", + ".github/agents/project-planning/subagents/github-backlog-executor.agent.md", + ".github/agents/project-planning/subagents/jira-backlog-executor.agent.md", + ".github/agents/project-planning/subagents/prd-quality-reviewer.agent.md", + ".github/agents/project-planning/system-architecture-reviewer.agent.md", + ".github/agents/project-planning/ux-ui-designer.agent.md", + ".github/agents/rai-planning/rai-planner.agent.md", + ".github/agents/rai-planning/rai-reviewer.agent.md", + ".github/agents/rai-planning/subagents/rai-skill-assessor.agent.md", + ".github/agents/security/security-planner.agent.md", + ".github/agents/security/security-reviewer.agent.md", + ".github/agents/security/sssc-planner.agent.md", + ".github/agents/security/sssc-reviewer.agent.md", + ".github/agents/security/subagents/codebase-profiler.agent.md", + ".github/agents/security/subagents/cve-analyzer.agent.md", + ".github/agents/security/subagents/finding-deep-verifier.agent.md", + ".github/agents/security/subagents/report-generator.agent.md", + ".github/agents/security/subagents/skill-assessor.agent.md", + ".github/agents/security/subagents/supply-chain-skill-assessor.agent.md", + ".github/agents/security/supply-chain-reviewer.agent.md" + ], + "commands": [ + ".github/prompts/accessibility/accessibility-coverage-matrix.prompt.md", + ".github/prompts/data-science/synth-data-generate.prompt.md", + ".github/prompts/design-thinking/dt-canonical-deck.prompt.md", + ".github/prompts/design-thinking/dt-figma-export.prompt.md", + ".github/prompts/design-thinking/dt-handoff-implementation-space.prompt.md", + ".github/prompts/design-thinking/dt-handoff-problem-space.prompt.md", + ".github/prompts/design-thinking/dt-handoff-solution-space.prompt.md", + ".github/prompts/design-thinking/dt-method-04-convergence.prompt.md", + ".github/prompts/design-thinking/dt-method-04-ideation.prompt.md", + ".github/prompts/design-thinking/dt-method-05-concepts.prompt.md", + ".github/prompts/design-thinking/dt-method-05-evaluation.prompt.md", + ".github/prompts/design-thinking/dt-method-06-building.prompt.md", + ".github/prompts/design-thinking/dt-method-06-planning.prompt.md", + ".github/prompts/design-thinking/dt-method-06-testing.prompt.md", + ".github/prompts/design-thinking/dt-method-next.prompt.md", + ".github/prompts/design-thinking/dt-resume-coaching.prompt.md", + ".github/prompts/design-thinking/dt-start-project.prompt.md", + ".github/prompts/experimental/cspell-config.prompt.md", + ".github/prompts/experimental/graph-research.prompt.md", + ".github/prompts/hve-core/ado-create-pull-request.prompt.md", + ".github/prompts/hve-core/ado-get-build-info.prompt.md", + ".github/prompts/hve-core/evals-import.prompt.md", + ".github/prompts/hve-core/git-commit-message.prompt.md", + ".github/prompts/hve-core/git-commit.prompt.md", + ".github/prompts/hve-core/git-merge.prompt.md", + ".github/prompts/hve-core/git-setup.prompt.md", + ".github/prompts/hve-core/pr-review.prompt.md", + ".github/prompts/hve-core/pull-request.prompt.md", + ".github/prompts/hve-core/rpi.prompt.md", + ".github/prompts/hve-core/vally-test-write.prompt.md", + ".github/prompts/rai-planning/rai-capture.prompt.md", + ".github/prompts/rai-planning/rai-plan-from-prd.prompt.md", + ".github/prompts/rai-planning/rai-plan-from-security-plan.prompt.md", + ".github/prompts/security/incident-response.prompt.md", + ".github/prompts/security/risk-register.prompt.md", + ".github/prompts/security/security-capture.prompt.md", + ".github/prompts/security/security-plan-from-prd.prompt.md", + ".github/prompts/security/security-review-llm.prompt.md", + ".github/prompts/security/security-review-sbd.prompt.md", + ".github/prompts/security/security-review-web.prompt.md", + ".github/prompts/security/security-review.prompt.md", + ".github/prompts/security/sssc-capture.prompt.md", + ".github/prompts/security/sssc-from-brd.prompt.md", + ".github/prompts/security/sssc-from-prd.prompt.md", + ".github/prompts/security/sssc-from-security-plan.prompt.md", + ".github/prompts/security/vex-implement.prompt.md", + ".github/prompts/security/vex-scan.prompt.md", + ".github/prompts/security/vex-triage.prompt.md" + ], + "rules": [ + ".github/instructions/accessibility/accessibility-identity.instructions.md", + ".github/instructions/accessibility/accessibility-license-posture.instructions.md", + ".github/instructions/coding-standards/bash/bash.instructions.md", + ".github/instructions/coding-standards/bicep/bicep.instructions.md", + ".github/instructions/coding-standards/code-review/diff-computation.instructions.md", + ".github/instructions/coding-standards/code-review/review-artifacts.instructions.md", + ".github/instructions/coding-standards/csharp/csharp-tests.instructions.md", + ".github/instructions/coding-standards/csharp/csharp.instructions.md", + ".github/instructions/coding-standards/powershell/pester.instructions.md", + ".github/instructions/coding-standards/powershell/powershell.instructions.md", + ".github/instructions/coding-standards/python-script.instructions.md", + ".github/instructions/coding-standards/python-tests.instructions.md", + ".github/instructions/coding-standards/rust/rust-tests.instructions.md", + ".github/instructions/coding-standards/rust/rust.instructions.md", + ".github/instructions/coding-standards/terraform/terraform.instructions.md", + ".github/instructions/coding-standards/uv-projects.instructions.md", + ".github/instructions/design-thinking/dt-coach-telemetry.instructions.md", + ".github/instructions/experimental/experiment-designer.instructions.md", + ".github/instructions/experimental/graphify.instructions.md", + ".github/instructions/experimental/mural/mural-bootstrap.instructions.md", + ".github/instructions/experimental/mural/mural-destinations.instructions.md", + ".github/instructions/experimental/mural/mural-human-record.instructions.md", + ".github/instructions/experimental/mural/mural-log-hygiene.instructions.md", + ".github/instructions/experimental/mural/mural-seeding-patterns.instructions.md", + ".github/instructions/experimental/mural/mural-writeback-hygiene.instructions.md", + ".github/instructions/experimental/mural/mural-writing-style.instructions.md", + ".github/instructions/experimental/pptx.instructions.md", + ".github/instructions/hve-core/commit-message.instructions.md", + ".github/instructions/hve-core/copilot-tracking.instructions.md", + ".github/instructions/hve-core/git-merge.instructions.md", + ".github/instructions/hve-core/hve-builder.instructions.md", + ".github/instructions/hve-core/licensing-posture.instructions.md", + ".github/instructions/hve-core/markdown.instructions.md", + ".github/instructions/hve-core/pull-request.instructions.md", + ".github/instructions/hve-core/writing-style.instructions.md", + ".github/instructions/privacy/privacy-identity.instructions.md", + ".github/instructions/project-planning/adr-byo-template.instructions.md", + ".github/instructions/project-planning/adr-handoff.instructions.md", + ".github/instructions/project-planning/adr-identity.instructions.md", + ".github/instructions/project-planning/adr-standards.instructions.md", + ".github/instructions/project-planning/backlog-guardrails.instructions.md", + ".github/instructions/project-planning/community-interaction.instructions.md", + ".github/instructions/rai-planning/rai-identity.instructions.md", + ".github/instructions/rai-planning/rai-license-posture.instructions.md", + ".github/instructions/security/identity.instructions.md", + ".github/instructions/security/sssc-planner.instructions.md", + ".github/instructions/security/standards-mapping.instructions.md", + ".github/instructions/security/tm7-generation-workflow.instructions.md", + ".github/instructions/security/vex-generation.instructions.md", + ".github/instructions/security/vex-standards.instructions.md", + ".github/instructions/shared/coaching-patterns.instructions.md", + ".github/instructions/shared/content-policy-citation.instructions.md", + ".github/instructions/shared/disclaimer-language.instructions.md", + ".github/instructions/shared/hve-core-location.instructions.md", + ".github/instructions/shared/planner-identity-base.instructions.md", + ".github/instructions/shared/telemetry-overlay.instructions.md", + ".github/instructions/shared/untrusted-content-boundary.instructions.md" + ], + "skills": [ + ".github/skills/accessibility/accessibility", + ".github/skills/coding-standards/code-review", + ".github/skills/coding-standards/python-foundational", + ".github/skills/data-science/data-workstream-foundation", + ".github/skills/data-science/ds-analysis-authoring", + ".github/skills/data-science/ds-catalog", + ".github/skills/data-science/ds-dataops", + ".github/skills/data-science/ds-evaluation-design", + ".github/skills/data-science/ds-feasibility", + ".github/skills/data-science/ml-experimentation", + ".github/skills/design-thinking/dt-coaching-foundation", + ".github/skills/design-thinking/dt-curriculum", + ".github/skills/design-thinking/dt-methods", + ".github/skills/design-thinking/dt-rpi-integration", + ".github/skills/design-thinking/ux-artifacts", + ".github/skills/design-thinking/ux-coaching", + ".github/skills/experimental/caveman", + ".github/skills/experimental/copilot-otel-metrics", + ".github/skills/experimental/customer-card-render", + ".github/skills/experimental/demo-video", + ".github/skills/experimental/mural", + ".github/skills/experimental/powerpoint", + ".github/skills/experimental/tts-voiceover", + ".github/skills/experimental/video-to-gif", + ".github/skills/experimental/vscode-playwright", + ".github/skills/hve-core/architecture-diagrams", + ".github/skills/hve-core/c4-architecture", + ".github/skills/hve-core/documentation", + ".github/skills/hve-core/hve-builder", + ".github/skills/hve-core/hve-builder-tester", + ".github/skills/hve-core/prompt-analyze", + ".github/skills/hve-core/prompt-builder", + ".github/skills/hve-core/prompt-refactor", + ".github/skills/hve-core/vally-tests", + ".github/skills/installer/hve-core-installer", + ".github/skills/project-planning/adr-author", + ".github/skills/project-planning/backlog-execute", + ".github/skills/project-planning/backlog-management", + ".github/skills/project-planning/backlog-plan", + ".github/skills/project-planning/experiment-design", + ".github/skills/project-planning/functional-planner", + ".github/skills/project-planning/gitlab", + ".github/skills/project-planning/jira", + ".github/skills/project-planning/performance-slo-planner", + ".github/skills/project-planning/privacy-standards", + ".github/skills/project-planning/rai-planner", + ".github/skills/project-planning/requirements-author", + ".github/skills/project-planning/security-planning", + ".github/skills/rai/rai-standards", + ".github/skills/rpi/rpi-challenger", + ".github/skills/rpi/rpi-implement", + ".github/skills/rpi/rpi-plan", + ".github/skills/rpi/rpi-plan-critique", + ".github/skills/rpi/rpi-quick", + ".github/skills/rpi/rpi-research", + ".github/skills/rpi/rpi-review", + ".github/skills/rpi/rpi-walkthrough", + ".github/skills/security/gh-code-scanning", + ".github/skills/security/mcsb", + ".github/skills/security/owasp-agentic", + ".github/skills/security/owasp-cicd", + ".github/skills/security/owasp-infrastructure", + ".github/skills/security/owasp-llm", + ".github/skills/security/owasp-mcp", + ".github/skills/security/owasp-top-10", + ".github/skills/security/secure-by-design", + ".github/skills/security/security-reviewer-formats", + ".github/skills/security/supply-chain-security", + ".github/skills/security/vex", + ".github/skills/shared/backlog-templates", + ".github/skills/shared/pr-reference", + ".github/skills/shared/telemetry-foundations" + ], + "hooks": ".github/hooks/shared/telemetry.json" +} diff --git a/release-please-config.json b/release-please-config.json index 9b93d78aa..ff6106d91 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -29,7 +29,7 @@ }, { "type": "json", - "path": ".github/plugin.json", + "path": "plugin.json", "jsonpath": "$.version" }, { diff --git a/release-please-prerelease-config.json b/release-please-prerelease-config.json index c2a37014d..5f164b5b2 100644 --- a/release-please-prerelease-config.json +++ b/release-please-prerelease-config.json @@ -50,7 +50,7 @@ }, { "type": "json", - "path": ".github/plugin.json", + "path": "plugin.json", "jsonpath": "$.version" }, { diff --git a/scripts/extension/Prepare-Extension.ps1 b/scripts/extension/Prepare-Extension.ps1 index f4cf624f5..3647914af 100644 --- a/scripts/extension/Prepare-Extension.ps1 +++ b/scripts/extension/Prepare-Extension.ps1 @@ -7,7 +7,7 @@ .SYNOPSIS Prepares the HVE Core VS Code extension from the plugin manifest. .DESCRIPTION - Reads .github/plugin.json as the sole component authority and writes the + Reads root plugin.json as the sole component authority and writes the single hve-core extension manifest and README. Component membership is identical on every release channel. .PARAMETER DryRun @@ -56,7 +56,7 @@ function Test-PluginComponentPath { .SYNOPSIS Tests whether a plugin component path is contained and well shaped. .PARAMETER Path - Manifest-declared path relative to the plugin root. + Manifest-declared path relative to the repository root. .PARAMETER Shape Anchored expression describing the expected artifact shape. .OUTPUTS @@ -97,10 +97,10 @@ function Get-PluginComponent { ) $fieldKinds = [ordered]@{ - agents = @{ Kind = 'agent'; Shape = '^agents/[^/]+/.+\.agent\.md$' } - commands = @{ Kind = 'prompt'; Shape = '^prompts/[^/]+/.+\.prompt\.md$' } - rules = @{ Kind = 'instruction'; Shape = '^instructions/[^/]+/.+\.instructions\.md$' } - skills = @{ Kind = 'skill'; Shape = '^skills/[^/]+/[^/]+$' } + agents = @{ Kind = 'agent'; Shape = '^\.github/agents/[^/]+/.+\.agent\.md$' } + commands = @{ Kind = 'prompt'; Shape = '^\.github/prompts/[^/]+/.+\.prompt\.md$' } + rules = @{ Kind = 'instruction'; Shape = '^\.github/instructions/[^/]+/.+\.instructions\.md$' } + skills = @{ Kind = 'skill'; Shape = '^\.github/skills/[^/]+/[^/]+$' } } $items = @() foreach ($field in $fieldKinds.Keys) { @@ -110,7 +110,7 @@ function Get-PluginComponent { if (-not (Test-PluginComponentPath -Path $declared -Shape $fieldKinds[$field].Shape)) { throw "Plugin manifest $field entry '$declared' is not a contained artifact path." } - $items += @{ Kind = $fieldKinds[$field].Kind; SourcePath = ".github/$declared" } + $items += @{ Kind = $fieldKinds[$field].Kind; SourcePath = $declared } } } return [hashtable[]]$items @@ -428,7 +428,7 @@ function Invoke-PrepareExtension { ) try { - $manifest = Get-PluginManifest -Path (Join-Path $RepoRoot '.github/plugin.json') + $manifest = Get-PluginManifest -Path (Join-Path $RepoRoot 'plugin.json') $items = @(Get-PluginComponent -Manifest $manifest) if ($items.Count -eq 0) { return New-PrepareResult -Success $false -ErrorMessage 'Plugin manifest declares no extension components.' diff --git a/scripts/extension/README.md b/scripts/extension/README.md index 657f23582..7c73f5be7 100644 --- a/scripts/extension/README.md +++ b/scripts/extension/README.md @@ -2,7 +2,7 @@ title: Extension Scripts description: PowerShell scripts for manifest-driven VS Code extension preparation and packaging author: HVE Core Team -ms.date: 2026-08-16 +ms.date: 2026-08-19 ms.topic: reference keywords: - powershell @@ -20,18 +20,18 @@ publishing the HVE Core VS Code extension. The extension packaging pipeline follows the one plugin manifest: -1. `Prepare-Extension.ps1` maps `.github/plugin.json` to one extension manifest and README +1. `Prepare-Extension.ps1` maps root `plugin.json` to one extension manifest and README 2. `Package-Extension.ps1` stages tracked contribution files and creates one `.vsix` 3. `Resolve-VsixFile.ps1` requires exactly one VSIX for provenance workflows 4. `Export-AttestationBundle.ps1` writes Sigstore and in-toto sidecars -Membership comes from `.github/plugin.json`. Stable and PreRelease preparation use the same component set. +Membership comes from root `plugin.json`. Stable and PreRelease preparation use the same component set. Repository-relative `.github/...` declarations become extension contribution paths without adding plugin-root README or license files. ## Scripts ### `Prepare-Extension.ps1` -Prepares extension contents from `.github/plugin.json`. +Prepares extension contents from root `plugin.json`. Purpose: Gather and filter artifacts for inclusion in the extension package. diff --git a/scripts/plugins/README.md b/scripts/plugins/README.md index f09c231a5..d5e26d84d 100644 --- a/scripts/plugins/README.md +++ b/scripts/plugins/README.md @@ -3,7 +3,7 @@ title: Plugin Manifest Scripts description: PowerShell tooling for synchronizing and validating the HVE Core plugin manifest --- -PowerShell tooling for synchronizing `.github/plugin.json` with the complete distributable HVE Core component set and validating the one-entry marketplace locator. +PowerShell tooling for synchronizing root `plugin.json` with the complete distributable HVE Core component set and validating the one-entry marketplace locator. Discovery remains limited to tracked package-scoped artifacts under `.github`. ## Scripts @@ -21,20 +21,20 @@ PowerShell tooling for synchronizing `.github/plugin.json` with the complete dis ## Source to Manifest Pipeline 1. Author artifacts in `.github/` (agents, prompts, instructions, skills, hooks) -2. Run `npm run plugin:sync` to derive `.github/plugin.json` +2. Run `npm run plugin:sync` to derive root `plugin.json` 3. Run `npm run plugin:validate` for non-mutating manifest and hook validation 4. Prepare or package the single extension separately when VSIX output is in scope ## Manifest Output -The script discovers these git-tracked, `.github`-relative component paths: +The script discovers these git-tracked paths beneath `.github` and emits repository-relative `.github/...` component paths: * `agents//**/*.agent.md` * `prompts//**/*.prompt.md` * `instructions//**/*.instructions.md` * `skills///SKILL.md`, unless the top-level license has a noncommercial qualifier -Repository-root artifacts without a package segment are excluded. Paths are unique and ordinal-sorted. Metadata is preserved, the version follows root `package.json`, and `hooks/shared/telemetry.json` remains fixed. +Artifacts without a package segment are excluded from discovery. Paths are unique and ordinal-sorted. Metadata is preserved, the version follows root `package.json`, and `.github/hooks/shared/telemetry.json` remains fixed. Validation also requires tracked, regular, non-empty root `plugin.json`, `README.md`, and `LICENSE` files. ## Synchronizing After Artifact Changes @@ -60,7 +60,7 @@ The moving registrations `microsoft/hve-core#release/prerelease` and `microsoft/ ## Release Boundary -Plugin validation produces no package archive or release evidence document. Release workflows package, attest, and publish one VSIX separately. The Copilot CLI installs the plugin directly from the selected repository ref and relative `.github` source. +Plugin validation produces no package archive or release evidence document. Release workflows package, attest, and publish one VSIX separately. The Copilot CLI installs the plugin directly from the repository root at the selected ref. --- diff --git a/scripts/plugins/Sync-PluginManifest.ps1 b/scripts/plugins/Sync-PluginManifest.ps1 index 30d5e9d60..01b1d1af2 100644 --- a/scripts/plugins/Sync-PluginManifest.ps1 +++ b/scripts/plugins/Sync-PluginManifest.ps1 @@ -5,11 +5,12 @@ <# .SYNOPSIS - Synchronizes .github/plugin.json with the distributable tracked component set. + Synchronizes root plugin.json with the distributable tracked component set. .DESCRIPTION - Derives the sole plugin manifest from git-tracked files under the .github - plugin root. Inclusion is a closed path-and-license classification: + Derives the sole root plugin manifest from git-tracked files under the + .github artifact discovery root. Inclusion is a closed path-and-license + classification: - agents//**/*.agent.md - prompts//**/*.prompt.md @@ -54,10 +55,11 @@ param( $ErrorActionPreference = 'Stop' -$script:PluginRoot = '.github' -$script:PluginManifestFile = '.github/plugin.json' +$script:ArtifactRoot = '.github' +$script:PluginManifestFile = 'plugin.json' $script:MarketplaceCatalogFile = '.github/plugin/marketplace.json' -$script:FixedHookPath = 'hooks/shared/telemetry.json' +$script:FixedHookPath = '.github/hooks/shared/telemetry.json' +$script:RequiredPluginMetadata = @('plugin.json', 'README.md', 'LICENSE') $script:ManifestMetadataKey = @('author', 'homepage', 'repository', 'license', 'keywords') $script:LocatorMetadataKey = @('name', 'description', 'version', 'author', 'homepage', 'repository', 'license', 'keywords') $script:RecipeField = @('agents', 'commands', 'rules', 'skills', 'hooks', 'x-hve') @@ -84,12 +86,12 @@ function Get-TrackedPluginIndex { [string]$RepoRoot ) - $output = & git -C $RepoRoot ls-files -s -z --full-name -- $script:PluginRoot + $output = & git -C $RepoRoot ls-files -s -z --full-name -- $script:ArtifactRoot if ($LASTEXITCODE -ne 0) { throw "git ls-files failed with exit code $LASTEXITCODE in $RepoRoot" } - $prefix = "$script:PluginRoot/" + $prefix = "$script:ArtifactRoot/" $paths = @() $violations = @() foreach ($entry in (($output -join '') -split "`0" | Where-Object { $_ })) { @@ -119,13 +121,13 @@ function Get-TrackedPluginIndex { function Get-TrackedPluginFile { <# .SYNOPSIS - Lists git-tracked files under the plugin root. + Lists git-tracked files under the artifact discovery root. .PARAMETER RepoRoot Root directory of the repository. .OUTPUTS - [string[]] Plugin-root-relative forward-slash paths. + [string[]] Artifact-root-relative forward-slash paths. #> [CmdletBinding()] [OutputType([string[]])] @@ -221,7 +223,7 @@ function Get-PluginComponentSet { Root directory of the repository. .PARAMETER TrackedPath - Prevalidated plugin-root-relative tracked paths. + Prevalidated artifact-root-relative tracked paths. .OUTPUTS [System.Collections.Specialized.OrderedDictionary] agents, commands, @@ -254,9 +256,9 @@ function Get-PluginComponentSet { '^prompts/[^/]+/.+\.prompt\.md$' { [void]$commands.Add($path); continue } '^instructions/[^/]+/.+\.instructions\.md$' { [void]$rules.Add($path); continue } '^skills/[^/]+/[^/]+/SKILL\.md$' { - $license = Get-SkillLicense -Path (Join-Path $RepoRoot $script:PluginRoot $path) + $license = Get-SkillLicense -Path (Join-Path $RepoRoot $script:ArtifactRoot $path) if (-not (Test-NoncommercialLicense -License $license)) { - [void]$skills.Add(($path -replace '/SKILL\.md$', '')) + [void]$skills.Add("$script:ArtifactRoot/$($path -replace '/SKILL\.md$', '')") } continue } @@ -264,9 +266,9 @@ function Get-PluginComponentSet { } return [ordered]@{ - agents = @($agents) - commands = @($commands) - rules = @($rules) + agents = @($agents | ForEach-Object { "$script:ArtifactRoot/$_" }) + commands = @($commands | ForEach-Object { "$script:ArtifactRoot/$_" }) + rules = @($rules | ForEach-Object { "$script:ArtifactRoot/$_" }) skills = @($skills) } } @@ -451,8 +453,92 @@ function Compare-PluginManifest { #endregion Manifest +#region Package metadata + +function Get-PluginMetadataViolations { + <# + .SYNOPSIS + Validates required files at the plugin root. + + .PARAMETER RepoRoot + Root directory of the repository. + + .OUTPUTS + [string[]] Violation messages. + #> + [CmdletBinding()] + [OutputType([string[]])] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$RepoRoot + ) + + $violations = @() + foreach ($relativePath in $script:RequiredPluginMetadata) { + $path = Join-Path $RepoRoot $relativePath + if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { + $violations += "Required plugin root file is missing or not a regular file: $relativePath" + continue + } + + $indexOutput = & git -C $RepoRoot ls-files -s --full-name -- $relativePath + if ($LASTEXITCODE -ne 0) { + throw "git ls-files failed with exit code $LASTEXITCODE in $RepoRoot" + } + $entry = @($indexOutput | Where-Object { $_ }) + if ($entry.Count -ne 1 -or $entry[0] -notmatch '^(?\d{6}) [0-9a-f]+ \d+\t(?.+)$' -or $Matches['Path'] -cne $relativePath) { + $violations += "Required plugin root file is not tracked: $relativePath" + continue + } + if ($Matches['Mode'] -notin @('100644', '100755')) { + $violations += "Required plugin root file is not a tracked regular file: $relativePath (mode $($Matches['Mode']))" + continue + } + if ((Get-Item -LiteralPath $path).Length -eq 0) { + $violations += "Required plugin root file is empty: $relativePath" + } + } + return $violations +} + +#endregion Package metadata + #region Catalog +function Resolve-PluginPath { + <# + .SYNOPSIS + Resolves a path through symbolic links in every existing segment. + + .PARAMETER Path + Existing file or directory path to resolve. + + .OUTPUTS + [string] Canonical absolute path. + #> + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$Path + ) + + $fullPath = [System.IO.Path]::GetFullPath($Path) + $pathRoot = [System.IO.Path]::GetPathRoot($fullPath) + $current = $pathRoot + $relative = $fullPath.Substring($pathRoot.Length) + foreach ($segment in $relative.Split([System.IO.Path]::DirectorySeparatorChar, [System.StringSplitOptions]::RemoveEmptyEntries)) { + $current = Join-Path $current $segment + $item = Get-Item -LiteralPath $current -Force + if ($item.LinkType) { + $current = $item.ResolveLinkTarget($true).FullName + } + } + return [System.IO.Path]::GetFullPath($current) +} + function Get-PluginComponentCoverageViolations { <# .SYNOPSIS @@ -486,7 +572,8 @@ function Get-PluginComponentCoverageViolations { ) $violations = @() - $root = Join-Path $RepoRoot $SourcePath + $root = Resolve-PluginPath -Path (Join-Path $RepoRoot $SourcePath) + $rootPrefix = $root.TrimEnd([System.IO.Path]::DirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar foreach ($kind in @('agents', 'commands', 'rules', 'skills')) { $type = if ($kind -eq 'skills') { 'Container' } else { 'Leaf' } @@ -495,15 +582,28 @@ function Get-PluginComponentCoverageViolations { $violations += "$kind path escapes the plugin root: $path" continue } - if (-not (Test-Path -LiteralPath (Join-Path $root $path) -PathType $type)) { + $candidate = Join-Path $root $path + if (-not (Test-Path -LiteralPath $candidate -PathType $type)) { $violations += "$kind path does not exist under $SourcePath : $path" + continue + } + $resolved = Resolve-PluginPath -Path $candidate + if ($resolved -cne $root -and -not $resolved.StartsWith($rootPrefix, [System.StringComparison]::Ordinal)) { + $violations += "$kind path resolves outside the plugin root: $path" } } } - if (-not (Test-Path -LiteralPath (Join-Path $root $Manifest['hooks']) -PathType Leaf)) { + $hookPath = Join-Path $root $Manifest['hooks'] + if (-not (Test-Path -LiteralPath $hookPath -PathType Leaf)) { $violations += "hooks path does not exist under $SourcePath : $($Manifest['hooks'])" } + else { + $resolvedHook = Resolve-PluginPath -Path $hookPath + if (-not $resolvedHook.StartsWith($rootPrefix, [System.StringComparison]::Ordinal)) { + $violations += "hooks path resolves outside the plugin root: $($Manifest['hooks'])" + } + } return $violations } @@ -620,6 +720,15 @@ function Invoke-PluginManifestSync { [switch]$Check ) + $metadataViolations = @(Get-PluginMetadataViolations -RepoRoot $RepoRoot) + if ($metadataViolations.Count -gt 0) { + return [ordered]@{ + Changed = $false + Violations = $metadataViolations + Manifest = $null + } + } + $trackedIndex = Get-TrackedPluginIndex -RepoRoot $RepoRoot $components = Get-PluginComponentSet -RepoRoot $RepoRoot -TrackedPath $trackedIndex.Paths $version = Get-RepositoryVersion -RepoRoot $RepoRoot diff --git a/scripts/release/Set-RepositoryVersion.ps1 b/scripts/release/Set-RepositoryVersion.ps1 index 4dc0796e8..b19828fce 100644 --- a/scripts/release/Set-RepositoryVersion.ps1 +++ b/scripts/release/Set-RepositoryVersion.ps1 @@ -13,7 +13,7 @@ - package.json $.version - package-lock.json $.version and $.packages[""].version - extension/templates/package.template.json $.version - - .github/plugin.json $.version + - plugin.json $.version - .github/plugin/marketplace.json $.metadata.version and the sole $.plugins[0].version @@ -70,7 +70,7 @@ $script:VersionTarget = @( Assert = @('$.version') } [ordered]@{ - Path = '.github/plugin.json' + Path = 'plugin.json' Location = @(, @('version')) Assert = @('$.version') } diff --git a/scripts/tests/extension/Prepare-Extension.Tests.ps1 b/scripts/tests/extension/Prepare-Extension.Tests.ps1 index ea3d91d6f..52fadc279 100644 --- a/scripts/tests/extension/Prepare-Extension.Tests.ps1 +++ b/scripts/tests/extension/Prepare-Extension.Tests.ps1 @@ -34,7 +34,7 @@ BeforeAll { # makes any surviving catalog read fail loudly instead of passing silently. Remove-Item -LiteralPath $fixture.CatalogPath -Force if ($null -ne $Manifest) { - Set-FixtureFile -Path (Join-Path $fixture.RepoRoot '.github/plugin.json') ` + Set-FixtureFile -Path (Join-Path $fixture.RepoRoot 'plugin.json') ` -Value (($Manifest | ConvertTo-Json -Depth 8) + "`n") } return $fixture @@ -55,11 +55,11 @@ BeforeAll { name = 'hve-core' description = 'Manifest fixture description' version = '9.9.9' - agents = @('agents/labs/gamma.agent.md', 'agents/core/alpha.agent.md') - commands = @('prompts/core/build.prompt.md') - rules = @('instructions/core/style.instructions.md') - skills = @('skills/labs/probe', 'skills/core/toolkit') - hooks = 'hooks/core/session.json' + agents = @('.github/agents/labs/gamma.agent.md', '.github/agents/core/alpha.agent.md') + commands = @('.github/prompts/core/build.prompt.md') + rules = @('.github/instructions/core/style.instructions.md') + skills = @('.github/skills/labs/probe', '.github/skills/core/toolkit') + hooks = '.github/hooks/core/session.json' } } @@ -69,7 +69,7 @@ BeforeAll { Describe 'Prepare-Extension plugin manifest reading' -Tag 'Unit' { BeforeAll { $script:ManifestFixture = New-PluginFixtureRepo -Path (Join-Path $TestDrive 'manifest-repo') -Manifest (Get-PluginManifestFixture) - $script:ManifestPath = Join-Path $script:ManifestFixture.RepoRoot '.github/plugin.json' + $script:ManifestPath = Join-Path $script:ManifestFixture.RepoRoot 'plugin.json' } It 'Reads the manifest as the component authority' { @@ -113,7 +113,7 @@ Describe 'Prepare-Extension component projection' -Tag 'Unit' { } It 'Skips empty manifest fields' { - $manifest = [pscustomobject]@{ agents = @('agents/core/alpha.agent.md'); commands = @(); rules = $null; skills = @('') } + $manifest = [pscustomobject]@{ agents = @('.github/agents/core/alpha.agent.md'); commands = @(); rules = $null; skills = @('') } @(Get-PluginComponent -Manifest $manifest).SourcePath | Should -Be @('.github/agents/core/alpha.agent.md') } @@ -293,7 +293,7 @@ Describe 'Prepare-Extension failure and dry-run behavior' -Tag 'Unit' { } It 'Fails when the plugin manifest is absent' { - Remove-Item -LiteralPath (Join-Path $script:FailureFixture.RepoRoot '.github/plugin.json') -Force + Remove-Item -LiteralPath (Join-Path $script:FailureFixture.RepoRoot 'plugin.json') -Force $result = Invoke-PrepareExtension -ExtensionDirectory $script:FailureFixture.ExtensionDirectory -RepoRoot $script:FailureFixture.RepoRoot $result.Success | Should -BeFalse @@ -301,8 +301,8 @@ Describe 'Prepare-Extension failure and dry-run behavior' -Tag 'Unit' { } It 'Fails when the plugin manifest declares no components' { - Set-FixtureFile -Path (Join-Path $script:FailureFixture.RepoRoot '.github/plugin.json') ` - -Value (([ordered]@{ name = 'hve-core'; description = 'Empty'; hooks = 'hooks/core/session.json' } | ConvertTo-Json -Depth 4) + "`n") + Set-FixtureFile -Path (Join-Path $script:FailureFixture.RepoRoot 'plugin.json') ` + -Value (([ordered]@{ name = 'hve-core'; description = 'Empty'; hooks = '.github/hooks/core/session.json' } | ConvertTo-Json -Depth 4) + "`n") $result = Invoke-PrepareExtension -ExtensionDirectory $script:FailureFixture.ExtensionDirectory -RepoRoot $script:FailureFixture.RepoRoot $result.Success | Should -BeFalse @@ -310,7 +310,7 @@ Describe 'Prepare-Extension failure and dry-run behavior' -Tag 'Unit' { } It 'Fails before writing when the plugin manifest declares an escaping component' { - $manifestPath = Join-Path $script:FailureFixture.RepoRoot '.github/plugin.json' + $manifestPath = Join-Path $script:FailureFixture.RepoRoot 'plugin.json' $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json $manifest.agents = @('../package.json') Set-FixtureFile -Path $manifestPath -Value (($manifest | ConvertTo-Json -Depth 8) + "`n") diff --git a/scripts/tests/extension/Workflow-PackagingContracts.Tests.ps1 b/scripts/tests/extension/Workflow-PackagingContracts.Tests.ps1 index d52cd1755..bd245db78 100644 --- a/scripts/tests/extension/Workflow-PackagingContracts.Tests.ps1 +++ b/scripts/tests/extension/Workflow-PackagingContracts.Tests.ps1 @@ -580,7 +580,7 @@ Describe 'Retained release reconciliation and OpenVEX' -Tag 'Unit' { $verify = @($steps | Where-Object { $_ -match 'release-please reported \$RELEASE_VERSION' }) $verify | Should -HaveCount 1 foreach ($path in @('package\.json', 'package-lock\.json', [regex]::Escape($Manifest), - 'extension/templates/package\.template\.json', '\.github/plugin\.json', + 'extension/templates/package\.template\.json', 'plugin\.json', '\.github/plugin/marketplace\.json:\.metadata\.version', '\.github/plugin/marketplace\.json:\.plugins\[0\]\.version')) { $verify[0] | Should -Match $path @@ -619,6 +619,19 @@ Describe 'Retained release reconciliation and OpenVEX' -Tag 'Unit' { } Describe 'Release-please ownership and promotion transforms' -Tag 'Unit' { + It 'Versions root plugin.json in ' -ForEach @( + @{ Config = 'release-please-config.json' } + @{ Config = 'release-please-prerelease-config.json' } + ) { + $path = Join-Path $script:RepositoryRoot $Config + $config = Get-Content -LiteralPath $path -Raw -Encoding utf8 | ConvertFrom-Json + $extraFiles = @($config.packages.'.'.'extra-files') + @($extraFiles | Where-Object { $_.path -eq 'plugin.json' -and $_.jsonpath -eq '$.version' }) | + Should -HaveCount 1 + @($extraFiles | Where-Object { $_.path -eq '.github/plugin.json' }) | + Should -HaveCount 0 + } + It 'Creates no tag or GitHub release outside release-please in ' -ForEach @( @{ Workflow = 'release-prerelease.yml' } @{ Workflow = 'release-stable-publish.yml' } @@ -689,7 +702,7 @@ Describe 'Release-please ownership and promotion transforms' -Tag 'Unit' { $run = [string](Get-NamedJobStep -Document (Get-WorkflowDocument -Name $Workflow) ` -JobName 'prepare-promotion' -StepName 'Refresh the promotion head')['run'] $run | Should -Match "restore_from_base required CHANGELOG\.md \.github/plugin/marketplace\.json" - $run | Should -Match "'\.github/plugin\.json'" + $run | Should -Match "'plugin\.json'" $run | Should -Not -Match 'release-candidate\.json' } diff --git a/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 b/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 index 46c8a7e33..68c16449c 100644 --- a/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 +++ b/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 @@ -62,6 +62,8 @@ BeforeAll { Set-Content -LiteralPath (Join-Path $Root 'package.json') ` -Value ("{`n `"name`": `"fixture`",`n `"version`": `"$Version`"`n}`n") -Encoding UTF8 + Set-Content -LiteralPath (Join-Path $Root 'README.md') -Value "# Fixture`n" -Encoding UTF8 -NoNewline + Set-Content -LiteralPath (Join-Path $Root 'LICENSE') -Value "MIT`n" -Encoding UTF8 -NoNewline $manifest = [ordered]@{ name = 'hve-core' @@ -76,9 +78,9 @@ BeforeAll { commands = @() rules = @() skills = @() - hooks = 'hooks/shared/telemetry.json' + hooks = '.github/hooks/shared/telemetry.json' } - Set-Content -LiteralPath (Join-Path $github 'plugin.json') ` + Set-Content -LiteralPath (Join-Path $Root 'plugin.json') ` -Value (ConvertTo-PluginManifestJson -Manifest $manifest) -Encoding UTF8 -NoNewline $catalogPath = Join-Path $github 'plugin/marketplace.json' @@ -90,7 +92,7 @@ BeforeAll { plugins = @( [ordered]@{ name = 'hve-core' - source = '.github' + source = '.' description = 'Fixture plugin' version = $Version author = [ordered]@{ name = 'Microsoft'; url = 'https://www.microsoft.com' } @@ -118,7 +120,7 @@ BeforeAll { [scriptblock]$Transform ) - $path = Join-Path $Root '.github/plugin.json' + $path = Join-Path $Root 'plugin.json' $manifest = Get-Content -LiteralPath $path -Raw | ConvertFrom-Json -AsHashtable $manifest = & $Transform $manifest Set-Content -LiteralPath $path -Value (ConvertTo-PluginManifestJson -Manifest $manifest) -Encoding UTF8 -NoNewline @@ -207,23 +209,23 @@ Describe 'Get-PluginComponentSet' -Tag 'Unit' { It 'Includes package-scoped agents from nested directories' { $script:Components.agents | Should -Be @( - 'agents/alpha/one.agent.md', - 'agents/alpha/subagents/two.agent.md' + '.github/agents/alpha/one.agent.md', + '.github/agents/alpha/subagents/two.agent.md' ) } It 'Includes package-scoped prompts as commands' { - $script:Components.commands | Should -Be @('prompts/alpha/one.prompt.md') + $script:Components.commands | Should -Be @('.github/prompts/alpha/one.prompt.md') } It 'Includes package-scoped instructions as rules' { - $script:Components.rules | Should -Be @('instructions/beta/one.instructions.md') + $script:Components.rules | Should -Be @('.github/instructions/beta/one.instructions.md') } It 'Includes skill roots whose license permits distribution' { $script:Components.skills | Should -Be @( - 'skills/alpha/open-skill', - 'skills/alpha/unlicensed-skill' + '.github/skills/alpha/open-skill', + '.github/skills/alpha/unlicensed-skill' ) } @@ -278,7 +280,7 @@ Describe 'New-PluginManifest' -Tag 'Unit' { } It 'Declares the fixed hook manifest' { - $script:Manifest.hooks | Should -BeExactly 'hooks/shared/telemetry.json' + $script:Manifest.hooks | Should -BeExactly '.github/hooks/shared/telemetry.json' } It 'Orders manifest keys deterministically' { @@ -296,11 +298,58 @@ Describe 'New-PluginManifest' -Tag 'Unit' { } } +Describe 'Get-PluginMetadataViolations' -Tag 'Unit' { + It 'Accepts tracked non-empty regular root metadata' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'metadata-valid') + Get-PluginMetadataViolations -RepoRoot $root | Should -HaveCount 0 + } + + It 'Rejects missing root metadata' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'metadata-missing') + Remove-Item -LiteralPath (Join-Path $root 'README.md') -Force + (Get-PluginMetadataViolations -RepoRoot $root) -join "`n" | + Should -Match 'missing or not a regular file: README\.md' + } + + It 'Rejects untracked root metadata' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'metadata-untracked') + Set-Content -LiteralPath (Join-Path $root 'NOTICE') -Value 'notice' -Encoding UTF8 + $original = $script:RequiredPluginMetadata + try { + $script:RequiredPluginMetadata = @('NOTICE') + (Get-PluginMetadataViolations -RepoRoot $root) -join "`n" | + Should -Match 'not tracked: NOTICE' + } + finally { + $script:RequiredPluginMetadata = $original + } + } + + It 'Rejects empty root metadata' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'metadata-empty') + Set-Content -LiteralPath (Join-Path $root 'LICENSE') -Value '' -Encoding UTF8 -NoNewline + (Get-PluginMetadataViolations -RepoRoot $root) -join "`n" | + Should -Match 'empty: LICENSE' + } + + It 'Rejects symbolic-link root metadata by git mode' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'metadata-symlink') + $targetPath = Join-Path $root 'license-target.txt' + Set-Content -LiteralPath $targetPath -Value 'MIT' -Encoding UTF8 -NoNewline + Remove-Item -LiteralPath (Join-Path $root 'LICENSE') -Force + New-Item -ItemType SymbolicLink -Path (Join-Path $root 'LICENSE') -Target $targetPath | Out-Null + & git -C $root add --all + + (Get-PluginMetadataViolations -RepoRoot $root) -join "`n" | + Should -Match 'not a tracked regular file: LICENSE \(mode 120000\)' + } +} + Describe 'Invoke-PluginManifestSync write mode' -Tag 'Unit' { BeforeAll { $script:WriteRoot = New-PluginFixture -Root (Join-Path $TestDrive 'write') $script:WriteResult = Invoke-PluginManifestSync -RepoRoot $script:WriteRoot - $script:WrittenManifest = Get-Content -LiteralPath (Join-Path $script:WriteRoot '.github/plugin.json') -Raw | ConvertFrom-Json -AsHashtable + $script:WrittenManifest = Get-Content -LiteralPath (Join-Path $script:WriteRoot 'plugin.json') -Raw | ConvertFrom-Json -AsHashtable } It 'Reports the manifest as changed' { @@ -317,10 +366,10 @@ Describe 'Invoke-PluginManifestSync write mode' -Tag 'Unit' { } It 'Makes no change on a second synchronization' { - $before = (Get-FileHash (Join-Path $script:WriteRoot '.github/plugin.json') -Algorithm SHA256).Hash + $before = (Get-FileHash (Join-Path $script:WriteRoot 'plugin.json') -Algorithm SHA256).Hash $second = Invoke-PluginManifestSync -RepoRoot $script:WriteRoot $second.Changed | Should -BeFalse - (Get-FileHash (Join-Path $script:WriteRoot '.github/plugin.json') -Algorithm SHA256).Hash | Should -BeExactly $before + (Get-FileHash (Join-Path $script:WriteRoot 'plugin.json') -Algorithm SHA256).Hash | Should -BeExactly $before } } @@ -330,7 +379,7 @@ Describe 'Invoke-PluginManifestSync failure atomicity' -Tag 'Unit' { Set-FixtureCatalog -Root $root -Transform { param($c) @($c['plugins'])[0]['description'] = 'Drifted description'; $c } - $manifestPath = Join-Path $root '.github/plugin.json' + $manifestPath = Join-Path $root 'plugin.json' $before = (Get-FileHash $manifestPath -Algorithm SHA256).Hash $result = Invoke-PluginManifestSync -RepoRoot $root @@ -370,17 +419,17 @@ Describe 'Invoke-PluginManifestSync check mode' -Tag 'Unit' { Set-FixtureManifest -Root $script:CheckRoot -Transform { param($m) $m['agents'] = @('agents/alpha/one.agent.md'); $m } - $before = (Get-FileHash (Join-Path $script:CheckRoot '.github/plugin.json') -Algorithm SHA256).Hash + $before = (Get-FileHash (Join-Path $script:CheckRoot 'plugin.json') -Algorithm SHA256).Hash Invoke-PluginManifestSync -RepoRoot $script:CheckRoot -Check | Out-Null - (Get-FileHash (Join-Path $script:CheckRoot '.github/plugin.json') -Algorithm SHA256).Hash | Should -BeExactly $before + (Get-FileHash (Join-Path $script:CheckRoot 'plugin.json') -Algorithm SHA256).Hash | Should -BeExactly $before } It 'Names components missing from the committed manifest' { Set-FixtureManifest -Root $script:CheckRoot -Transform { - param($m) $m['agents'] = @('agents/alpha/one.agent.md'); $m + param($m) $m['agents'] = @('.github/agents/alpha/one.agent.md'); $m } $violations = (Invoke-PluginManifestSync -RepoRoot $script:CheckRoot -Check).Violations - $violations -join "`n" | Should -Match 'agents missing 1: agents/alpha/subagents/two\.agent\.md' + $violations -join "`n" | Should -Match 'agents missing 1: \.github/agents/alpha/subagents/two\.agent\.md' } It 'Names components the committed manifest adds' { @@ -393,7 +442,7 @@ Describe 'Invoke-PluginManifestSync check mode' -Tag 'Unit' { It 'Detects stale drift' -ForEach @( @{ Field = 'version'; Value = '9.9.9'; Pattern = "version differs: committed '9\.9\.9'; expected '1\.2\.3'" } - @{ Field = 'hooks'; Value = 'hooks/other.json'; Pattern = "hooks differs: committed 'hooks/other\.json'; expected 'hooks/shared/telemetry\.json'" } + @{ Field = 'hooks'; Value = '.github/hooks/other.json'; Pattern = "hooks differs: committed '\.github/hooks/other\.json'; expected '\.github/hooks/shared/telemetry\.json'" } ) { $field, $value = $Field, $Value Set-FixtureManifest -Root $script:CheckRoot -Transform { @@ -425,7 +474,7 @@ Describe 'Get-PluginCatalogViolations' -Tag 'Unit' { $c['plugins'] = @( [ordered]@{ name = 'hve-core' - source = '.github' + source = '.' description = 'Fixture plugin' version = $c['metadata']['version'] author = [ordered]@{ name = 'Microsoft'; url = 'https://www.microsoft.com' } @@ -540,7 +589,7 @@ Describe 'Get-PluginCatalogViolations' -Tag 'Unit' { It 'Reports a declared component that does not exist' { $manifest = [ordered]@{} foreach ($key in $script:CatalogManifest.Keys) { $manifest[$key] = $script:CatalogManifest[$key] } - $manifest['agents'] = @('agents/alpha/absent.agent.md') + $manifest['agents'] = @('.github/agents/alpha/absent.agent.md') (Get-PluginCatalogViolations -RepoRoot $script:CatalogRoot -Manifest $manifest) -join "`n" | Should -Match 'agents path does not exist' @@ -554,6 +603,20 @@ Describe 'Get-PluginCatalogViolations' -Tag 'Unit' { (Get-PluginCatalogViolations -RepoRoot $script:CatalogRoot -Manifest $manifest) -join "`n" | Should -Match 'skills path escapes the plugin root' } + + It 'Reports a declared component that resolves outside the plugin root' { + $outsidePath = Join-Path $TestDrive 'outside.agent.md' + Set-Content -LiteralPath $outsidePath -Value 'outside' -Encoding UTF8 + $linkPath = Join-Path $script:CatalogRoot '.github/agents/alpha/outside.agent.md' + New-Item -ItemType SymbolicLink -Path $linkPath -Target $outsidePath | Out-Null + + $manifest = [ordered]@{} + foreach ($key in $script:CatalogManifest.Keys) { $manifest[$key] = $script:CatalogManifest[$key] } + $manifest['agents'] = @('.github/agents/alpha/outside.agent.md') + + (Get-PluginCatalogViolations -RepoRoot $script:CatalogRoot -Manifest $manifest) -join "`n" | + Should -Match 'agents path resolves outside the plugin root' + } } Describe 'Committed repository manifest' -Tag 'Unit' { @@ -564,6 +627,6 @@ Describe 'Committed repository manifest' -Tag 'Unit' { It 'Excludes the tracked noncommercial skill' { (Get-PluginComponentSet -RepoRoot $script:RepositoryRoot).skills | - Should -Not -Contain 'skills/security/owasp-docker' + Should -Not -Contain '.github/skills/security/owasp-docker' } } diff --git a/scripts/tests/release/Set-RepositoryVersion.Tests.ps1 b/scripts/tests/release/Set-RepositoryVersion.Tests.ps1 index 46705ef70..b5756ce1c 100644 --- a/scripts/tests/release/Set-RepositoryVersion.Tests.ps1 +++ b/scripts/tests/release/Set-RepositoryVersion.Tests.ps1 @@ -60,7 +60,7 @@ BeforeAll { "publisher": "ise-hve-essentials" } "@ - '.github/plugin.json' = @" + 'plugin.json' = @" { "name": "hve-core", "description": "Fixture plugin", @@ -85,7 +85,7 @@ BeforeAll { "plugins": [ { "name": "hve-core", - "source": ".github", + "source": ".", "description": "Fixture plugin", "version": "$Version" } @@ -138,7 +138,7 @@ Describe 'Set-RepositoryVersion' -Tag 'Unit' { @{ Path = 'package-lock.json'; Member = '$.version'; Expected = '3.3.0' } @{ Path = 'package-lock.json'; Member = '$.packages[""].version'; Expected = '3.3.0' } @{ Path = 'extension/templates/package.template.json'; Member = '$.version'; Expected = '3.3.0' } - @{ Path = '.github/plugin.json'; Member = '$.version'; Expected = '3.3.0' } + @{ Path = 'plugin.json'; Member = '$.version'; Expected = '3.3.0' } @{ Path = '.github/plugin/marketplace.json'; Member = '$.metadata.version'; Expected = '3.3.0' } @{ Path = '.github/plugin/marketplace.json'; Member = '$.plugins[0].version'; Expected = '3.3.0' } ) { @@ -173,7 +173,7 @@ Describe 'Set-RepositoryVersion' -Tag 'Unit' { It 'Adds no source ref to the catalog entry' { $catalog = Get-Content -LiteralPath (Join-Path $script:Root '.github/plugin/marketplace.json') -Raw | ConvertFrom-Json -AsHashtable - @($catalog['plugins'])[0]['source'] | Should -BeExactly '.github' + @($catalog['plugins'])[0]['source'] | Should -BeExactly '.' } It 'Keeps the catalog at one entry' { @@ -203,10 +203,10 @@ Describe 'Set-RepositoryVersion' -Tag 'Unit' { Context 'when the repository shape is unexpected' { It 'Throws when a version-tracked file is missing' { $root = New-VersionFixture -Root (Join-Path $TestDrive 'missing') - Remove-Item -LiteralPath (Join-Path $root '.github/plugin.json') -Force + Remove-Item -LiteralPath (Join-Path $root 'plugin.json') -Force { Set-RepositoryVersion -RepoRoot $root -Version '3.3.0' } | - Should -Throw '*Version-tracked file not found: .github/plugin.json*' + Should -Throw '*Version-tracked file not found: plugin.json*' } It 'Rejects a version that is not three numeric segments' { From 0bedca11946785c00ad66338ec361067807ae416 Mon Sep 17 00:00:00 2001 From: Katrien De Graeve Date: Wed, 19 Aug 2026 10:36:17 +0000 Subject: [PATCH 2/3] fix(docs): correct relative source reference in marketplace.json description --- docs/customization/packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/customization/packages.md b/docs/customization/packages.md index 7ac9024a3..bec0c3399 100644 --- a/docs/customization/packages.md +++ b/docs/customization/packages.md @@ -16,7 +16,7 @@ estimated_reading_time: 6 Root `plugin.json` is the operational distribution definition for the one `hve-core` plugin and VSIX. Its `agents`, `commands`, `rules`, and `skills` arrays are deterministic repository-relative outputs of tracked path and license classification under `.github`. The fixed `hooks` value includes the telemetry hook. -`.github/plugin/marketplace.json` contains one `hve-core` entry with the relative source `.github`. It owns locator metadata only and must not repeat component arrays or package policy. +`.github/plugin/marketplace.json` contains one `hve-core` entry with the relative source `.`. It owns locator metadata only and must not repeat component arrays or package policy. ## Add Or Change A Component From e3e7900d79da105837ce7e1165e0f7fa66cc0430 Mon Sep 17 00:00:00 2001 From: Katrien De Graeve Date: Wed, 19 Aug 2026 15:14:23 +0000 Subject: [PATCH 3/3] test(plugins): add tests for invalid root metadata and missing hooks in plugin manifest sync --- .../plugins/Sync-PluginManifest.Tests.ps1 | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 b/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 index 68c16449c..52317664b 100644 --- a/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 +++ b/scripts/tests/plugins/Sync-PluginManifest.Tests.ps1 @@ -374,6 +374,17 @@ Describe 'Invoke-PluginManifestSync write mode' -Tag 'Unit' { } Describe 'Invoke-PluginManifestSync failure atomicity' -Tag 'Unit' { + It 'Stops before manifest derivation when required root metadata is invalid' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'invalid-metadata') + Remove-Item -LiteralPath (Join-Path $root 'README.md') -Force + + $result = Invoke-PluginManifestSync -RepoRoot $root + + $result.Changed | Should -BeFalse + $result.Violations -join "`n" | Should -Match 'missing or not a regular file: README\.md' + $result.Manifest | Should -BeNullOrEmpty + } + It 'Leaves a drifted manifest unchanged when catalog validation fails' { $root = New-PluginFixture -Root (Join-Path $TestDrive 'atomicity') Set-FixtureCatalog -Root $root -Transform { @@ -617,6 +628,28 @@ Describe 'Get-PluginCatalogViolations' -Tag 'Unit' { (Get-PluginCatalogViolations -RepoRoot $script:CatalogRoot -Manifest $manifest) -join "`n" | Should -Match 'agents path resolves outside the plugin root' } + + It 'Reports a declared hook that does not exist' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'missing-hook') + $manifest = (Invoke-PluginManifestSync -RepoRoot $root).Manifest + Remove-Item -LiteralPath (Join-Path $root '.github/hooks/shared/telemetry.json') -Force + + (Get-PluginCatalogViolations -RepoRoot $root -Manifest $manifest) -join "`n" | + Should -Match 'hooks path does not exist' + } + + It 'Reports a declared hook that resolves outside the plugin root' { + $root = New-PluginFixture -Root (Join-Path $TestDrive 'outside-hook') + $manifest = (Invoke-PluginManifestSync -RepoRoot $root).Manifest + $outsidePath = Join-Path $TestDrive 'outside-hook.json' + Set-Content -LiteralPath $outsidePath -Value '{}' -Encoding UTF8 + $hookPath = Join-Path $root '.github/hooks/shared/telemetry.json' + Remove-Item -LiteralPath $hookPath -Force + New-Item -ItemType SymbolicLink -Path $hookPath -Target $outsidePath | Out-Null + + (Get-PluginCatalogViolations -RepoRoot $root -Manifest $manifest) -join "`n" | + Should -Match 'hooks path resolves outside the plugin root' + } } Describe 'Committed repository manifest' -Tag 'Unit' {