From da888f1912e1b7c2c3c74eb6b73af48a5d53e24d Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Fri, 24 Jul 2026 13:07:22 -0700 Subject: [PATCH 1/5] fix: resolve Claude hooks.json validation error Claude additively loads the default hooks/hooks.json regardless of the manifest's hooks path, so the Copilot-format file there failed 'claude plugin validate'. Rename it to hooks/copilot-hooks.json and reference it explicitly from .plugin/plugin.json (Copilot/VS Code). Nothing now sits at the shared default path, so each client uses its own hooks file. Addresses the hooks portion of microsoft/GitHub-Copilot-for-Azure#2957; the unpinned npx launcher is handled separately. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ace74d0-93aa-4f8c-a9ef-0a928b7d50ad --- README.md | 4 ++-- plugin/.plugin/plugin.json | 3 ++- plugin/hooks/{hooks.json => copilot-hooks.json} | 0 3 files changed, 4 insertions(+), 3 deletions(-) rename plugin/hooks/{hooks.json => copilot-hooks.json} (100%) diff --git a/README.md b/README.md index c80abbbe4..9d0933253 100644 --- a/README.md +++ b/README.md @@ -134,9 +134,9 @@ For more details, see [Connect to sovereign clouds](https://learn.microsoft.com/ | Client | Skills | MCP Servers | Hooks | Marketplace | Manifest | Status | |--------|:------:|:-----------:|:-----:|:-----------:|----------|--------| -| **Copilot CLI** | ✅ | ✅ | ✅ (`hooks/hooks.json`) | ✅ `.plugin/` | `.plugin/plugin.json` | ✅ Onboarded | +| **Copilot CLI** | ✅ | ✅ | ✅ (`hooks/copilot-hooks.json`) | ✅ `.plugin/` | `.plugin/plugin.json` | ✅ Onboarded | | **Claude Code** | ✅ | ✅ | ✅ (`hooks/claude-hooks.json`) | `.claude-plugin/marketplace.json` (exists only in azure-skills repo)| ✅ `plugin/.claude-plugin/plugin.json` | ✅ Onboarded | -| **VS Code Extension** | ✅ (`.agents` folder) | ✅ | ✅ `hooks/hooks.json` (`.agents` folder) | Extension-based | Extension-based | ✅ Onboarded | +| **VS Code Extension** | ✅ (`.agents` folder) | ✅ | ✅ `hooks/copilot-hooks.json` (`.agents` folder) | Extension-based | Extension-based | ✅ Onboarded | | **IntelliJ** | ✅ (`.agents` folder) | ✅ | ❌ Not supported by client | Extension-based | Extension-based | ✅ Skills Onboarded 🔜 Hooks Support ETA - End of April 2026 | | **Gemini CLI** | ✅ | ✅ | ❌ Not supported by us | No marketplace | `gemini-extension.json` | ✅ Onboarded| | **Cursor** | ✅ | ✅ | `plugin/hooks/cursor-hooks.json` | `.cursor-plugin/marketplace.json` (exists only in azure-skills repo) | ✅ `plugin/.cursor-plugin/plugin.json` | ✅ Onboarded. Hooks testing - WIP | diff --git a/plugin/.plugin/plugin.json b/plugin/.plugin/plugin.json index 211443a30..901015824 100644 --- a/plugin/.plugin/plugin.json +++ b/plugin/.plugin/plugin.json @@ -20,5 +20,6 @@ "diagnostics" ], "skills": "./skills/", - "mcpServers": "./.mcp.json" + "mcpServers": "./.mcp.json", + "hooks": "./hooks/copilot-hooks.json" } diff --git a/plugin/hooks/hooks.json b/plugin/hooks/copilot-hooks.json similarity index 100% rename from plugin/hooks/hooks.json rename to plugin/hooks/copilot-hooks.json From df8c32ad712ad42d59aaa09f51cc90533545c0b8 Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Wed, 5 Aug 2026 13:02:33 -0700 Subject: [PATCH 2/5] feat: add telemetry debug logging and local plugin-root skill detection Add opt-in debugging to the telemetry hook scripts, controlled by the AZURE_SKILLS_TELEMETRY_LOG_DIR env var: raw JSON inputs are written to a raw-input/ subdirectory and MCP args are appended to telemetry.log. Also honor AZURE_SKILLS_PLUGIN_ROOT so skills loaded via --plugin-dir are recognized for reference_file_read events, and generalize reference-path extraction to match any skills/ root. Applied symmetrically to track-telemetry.ps1 and track-telemetry.sh. Related to microsoft/GitHub-Copilot-for-Azure#2957 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ace74d0-93aa-4f8c-a9ef-0a928b7d50ad --- plugin/hooks/scripts/track-telemetry.ps1 | 89 ++++++++++++++++++++---- plugin/hooks/scripts/track-telemetry.sh | 49 ++++++++++++- 2 files changed, 121 insertions(+), 17 deletions(-) diff --git a/plugin/hooks/scripts/track-telemetry.ps1 b/plugin/hooks/scripts/track-telemetry.ps1 index 1127b327b..e385d10fc 100644 --- a/plugin/hooks/scripts/track-telemetry.ps1 +++ b/plugin/hooks/scripts/track-telemetry.ps1 @@ -84,9 +84,53 @@ # If the path matches AND is not a SKILL.md file, the relative path after # "skills/" is extracted and emitted as a reference_file_read event. # SKILL.md reads are tracked as skill_invocation instead (not double-counted). +# +# === Debugging === +# +# If the AZURE_SKILLS_TELEMETRY_LOG_DIR env var is set, the script will create +# a "raw-input" subdirectory and write each raw JSON input to a timestamped file +# for debugging. It will also append a "telemetry.log" file with MCP args for +# each tracked event. +# +# When using `--plugin-dir` to load a local plugin the AZURE_SKILLS_PLUGIN_ROOT +# env var should be set so that the script can detect local skill paths for +# reference_file_read events. $ErrorActionPreference = "SilentlyContinue" +# Dumps raw input to a file in the AZURE_SKILLS_TELEMETRY_LOG_DIR/raw-input/ +# directory for debugging if the env var is set. +function Write-RawInputToFile { + param([string]$RawInput) + if ($env:AZURE_SKILLS_TELEMETRY_LOG_DIR) { + $logDir = $env:AZURE_SKILLS_TELEMETRY_LOG_DIR + $rawInputDir = Join-Path $logDir 'raw-input' + if (-not (Test-Path -LiteralPath $rawInputDir)) { + New-Item -ItemType Directory -Path $rawInputDir -Force | Out-Null + } + $timestamp = (Get-Date).ToUniversalTime().ToString("yyyyMMddTHHmmssZ") + $rawInputFile = Join-Path $rawInputDir "$timestamp.json" + try { + $RawInput | Out-File -FilePath $rawInputFile -Encoding utf8 -Force + } catch { } + } +} + +# Writes a debug log entry to the AZURE_SKILLS_TELEMETRY_LOG_DIR/telemetry.log file +# if the env var is set. +function Write-TelemetryDebugLog { + param([string]$Content) + + if ($env:AZURE_SKILLS_TELEMETRY_LOG_DIR) { + $logDir = $env:AZURE_SKILLS_TELEMETRY_LOG_DIR + $logFile = Join-Path $logDir 'telemetry.log' + $logEntry = "$(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ') | $Content" + try { + Add-Content -Path $logFile -Value $logEntry -ErrorAction SilentlyContinue + } catch { } + } +} + # Skip telemetry if opted out if ($env:AZURE_MCP_COLLECT_TELEMETRY -eq "false") { Write-Output '{"continue":true}' @@ -143,6 +187,8 @@ if ([string]::IsNullOrWhiteSpace($rawInput)) { Write-Success } +Write-RawInputToFile -RawInput $rawInput + # === STEP 1: Read and parse input === # Parse JSON input @@ -228,6 +274,16 @@ $pathPatternClaude = '\.claude/plugins/cache/(azure-skills|claude-plugins-offici $pathPatternVscodeAgentPlugins = 'agent-plugins/github\.com/microsoft/azure-skills/\.github/plugins/azure-skills/skills/' $pathPatternAgentsSkills = '\.agents/skills/' +# Put the path patterns into an array for easier iteration +$pathPatterns = @($pathPatternCopilot, $pathPatternClaude, $pathPatternVscodeAgentPlugins, $pathPatternAgentsSkills) + +# If $env:AZURE_SKILLS_PLUGIN_ROOT is set, add it to the path patterns for local skill development +if ($env:AZURE_SKILLS_PLUGIN_ROOT) { + $localSkillsPath = [regex]::Escape($env:AZURE_SKILLS_PLUGIN_ROOT) + '/skills/' + $localSkillsPath = $localSkillsPath -replace '\\', '/' -replace '/+', '/' + $pathPatterns += $localSkillsPath +} + $shouldTrack = $false $eventType = $null $skillName = $null @@ -258,16 +314,13 @@ if ($toolName -eq "view" -or $toolName -eq "Read" -or $toolName -eq "read_file") # Normalize path: convert to lowercase, replace backslashes, and squeeze consecutive slashes $pathLower = $pathToCheck.ToLower() -replace '\\', '/' -replace '/+', '/' - # Check for SKILL.md pattern — only match azure-skills paths (see path patterns above) + # Check for SKILL.md pattern — only match azure-skills paths (see pathPatterns above) $isAzureSkillMd = $false - if ($pathLower -match "${pathPatternCopilot}[^/]+/skill\.md") { - $isAzureSkillMd = $true - } elseif ($pathLower -match "${pathPatternClaude}[^/]+/skill\.md") { - $isAzureSkillMd = $true - } elseif ($pathLower -match "${pathPatternVscodeAgentPlugins}[^/]+/skill\.md") { - $isAzureSkillMd = $true - } elseif ($pathLower -match "${pathPatternAgentsSkills}[^/]+/skill\.md") { - $isAzureSkillMd = $true + foreach ($pattern in $pathPatterns) { + if ($pathLower -match "${pattern}[^/]+/skill\.md") { + $isAzureSkillMd = $true + break + } } if ($isAzureSkillMd) { @@ -302,15 +355,18 @@ if (-not $filePath -and -not $skillName) { # Normalize path for matching: replace backslashes and squeeze consecutive slashes $pathLower = $pathToCheck.ToLower() -replace '\\', '/' -replace '/+', '/' - $matchCopilotSkills = $pathLower -match $pathPatternCopilot - $matchClaudeSkills = $pathLower -match $pathPatternClaude - $matchVscodeAgentPlugins = $pathLower -match $pathPatternVscodeAgentPlugins - $matchAgentsSkills = $pathLower -match $pathPatternAgentsSkills - if ($matchCopilotSkills -or $matchClaudeSkills -or $matchVscodeAgentPlugins -or $matchAgentsSkills) { + $matchesPattern = $false + foreach ($pattern in $pathPatterns) { + if ($pathLower -match $pattern) { + $matchesPattern = $true + break + } + } + if ($matchesPattern) { # Extract relative path after 'skills/' $pathNormalized = $pathToCheck -replace '\\', '/' -replace '/+', '/' - if ($pathNormalized -match '(?:azure/(?:[0-9]+\.[0-9]+\.[0-9]+/)?skills|azure-skills/skills|\.agents/skills)/(.+)$') { + if ($pathNormalized -match '.*/skills/(.+)$') { $filePath = $Matches[1] if (-not $shouldTrack) { @@ -349,6 +405,9 @@ if ($shouldTrack) { try { & npx -y @azure/mcp@latest @mcpArgs 2>&1 | Out-Null } catch { } + + # If AZURE_SKILLS_TELEMETRY_LOG env var is set, append the args to the file specified by the env var (for debugging) + Write-TelemetryDebugLog -Content "MCP Args: $($mcpArgs -join ' ')" } # Output success to stdout (required by hooks) diff --git a/plugin/hooks/scripts/track-telemetry.sh b/plugin/hooks/scripts/track-telemetry.sh index 988c0c6a1..1499fedc9 100755 --- a/plugin/hooks/scripts/track-telemetry.sh +++ b/plugin/hooks/scripts/track-telemetry.sh @@ -86,6 +86,17 @@ # If the path matches AND is not a SKILL.md file, the relative path after # "skills/" is extracted and emitted as a reference_file_read event. # SKILL.md reads are tracked as skill_invocation instead (not double-counted). +# +# === Debugging === +# +# If the AZURE_SKILLS_TELEMETRY_LOG_DIR env var is set, the script will create +# a "raw-input" subdirectory and write each raw JSON input to a timestamped file +# for debugging. It will also append a "telemetry.log" file with MCP args for +# each tracked event. +# +# When using `--plugin-dir` to load a local plugin the AZURE_SKILLS_PLUGIN_ROOT +# env var should be set so that the script can detect local skill paths for +# reference_file_read events. set +e # Don't exit on errors - fail silently for privacy @@ -101,6 +112,27 @@ return_success() { exit 0 } +# Dumps raw input to a file in the AZURE_SKILLS_TELEMETRY_LOG_DIR/raw-input/ +# directory for debugging if the env var is set. +write_raw_input_to_file() { + local rawInputValue="$1" + [ -n "$AZURE_SKILLS_TELEMETRY_LOG_DIR" ] || return 0 + local rawInputDir="$AZURE_SKILLS_TELEMETRY_LOG_DIR/raw-input" + mkdir -p "$rawInputDir" 2>/dev/null || return 0 + local ts + ts=$(date -u +"%Y%m%dT%H%M%SZ") + echo "$rawInputValue" > "$rawInputDir/$ts.json" 2>/dev/null || true +} + +# Appends a debug log entry to the AZURE_SKILLS_TELEMETRY_LOG_DIR/telemetry.log +# file if the env var is set. +write_telemetry_debug_log() { + local content="$1" + [ -n "$AZURE_SKILLS_TELEMETRY_LOG_DIR" ] || return 0 + local logFile="$AZURE_SKILLS_TELEMETRY_LOG_DIR/telemetry.log" + echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") | $content" >> "$logFile" 2>/dev/null || true +} + # Resolve this script's directory so we can locate bundled skills. In the # installed plugin, hooks/ and skills/ are siblings under the plugin root, so # /../../skills//SKILL.md is the skill definition. @@ -184,6 +216,8 @@ if [ -z "$rawInput" ]; then return_success fi +write_raw_input_to_file "$rawInput" + # === STEP 1: Read and parse input === # Extract fields from hook data @@ -250,6 +284,13 @@ is_azure_skills_path() { [[ "$p" == *".claude/plugins/cache/claude-plugins-official/azure/"*"/skills/"* ]] && return 0 [[ "$p" == *"agent-plugins/github.com/microsoft/azure-skills/.github/plugins/azure-skills/skills/"* ]] && return 0 [[ "$p" == *".agents/skills/"* ]] && return 0 + # Local plugin development: match paths under AZURE_SKILLS_PLUGIN_ROOT/skills/ + # (e.g. when loading a local plugin via `--plugin-dir`) + if [ -n "$AZURE_SKILLS_PLUGIN_ROOT" ]; then + local localRoot + localRoot=$(echo "$AZURE_SKILLS_PLUGIN_ROOT" | tr '[:upper:]' '[:lower:]' | tr '\\' '/' | sed 's|//*|/|g') + [[ "$p" == *"${localRoot}/skills/"* ]] && return 0 + fi return 1 } @@ -319,8 +360,8 @@ if [ -z "$filePath" ] && [ -z "$skillName" ]; then # Extract relative path after 'skills/' pathNormalized=$(echo "$pathToCheck" | tr '\\' '/' | sed 's|//*|/|g') - if [[ "$pathNormalized" =~ (azure/([0-9]+\.[0-9]+\.[0-9]+/)?skills|azure-skills/skills|\.agents/skills)/(.+)$ ]]; then - filePath="${BASH_REMATCH[3]}" + if [[ "$pathNormalized" =~ .*/skills/(.+)$ ]]; then + filePath="${BASH_REMATCH[1]}" if [ "$shouldTrack" = false ]; then shouldTrack=true @@ -358,6 +399,10 @@ if [ "$shouldTrack" = true ]; then # Publish telemetry via npx npx -y @azure/mcp@latest "${mcpArgs[@]}" >/dev/null 2>&1 || true + + # If AZURE_SKILLS_TELEMETRY_LOG_DIR env var is set, append the args to the + # telemetry.log file in that directory (for debugging) + write_telemetry_debug_log "MCP Args: ${mcpArgs[*]}" fi # Output success to stdout (required by hooks) From 34e480650d4b359fc4bbe82b02c47585d6b1bdd6 Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Wed, 5 Aug 2026 13:54:58 -0700 Subject: [PATCH 3/5] fix: address PR review feedback on telemetry debug logging - Use printf instead of echo for raw-input dumps in track-telemetry.sh so JSON is written losslessly without escape-sequence interpretation. - Use local time (no trailing Z) for the debug log timestamp in both scripts. - Fix a stale comment that referenced AZURE_SKILLS_TELEMETRY_LOG instead of AZURE_SKILLS_TELEMETRY_LOG_DIR. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ace74d0-93aa-4f8c-a9ef-0a928b7d50ad --- plugin/hooks/scripts/track-telemetry.ps1 | 4 ++-- plugin/hooks/scripts/track-telemetry.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/hooks/scripts/track-telemetry.ps1 b/plugin/hooks/scripts/track-telemetry.ps1 index e385d10fc..b040eea9a 100644 --- a/plugin/hooks/scripts/track-telemetry.ps1 +++ b/plugin/hooks/scripts/track-telemetry.ps1 @@ -124,7 +124,7 @@ function Write-TelemetryDebugLog { if ($env:AZURE_SKILLS_TELEMETRY_LOG_DIR) { $logDir = $env:AZURE_SKILLS_TELEMETRY_LOG_DIR $logFile = Join-Path $logDir 'telemetry.log' - $logEntry = "$(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ') | $Content" + $logEntry = "$(Get-Date -Format 'yyyy-MM-ddTHH:mm:ss') | $Content" try { Add-Content -Path $logFile -Value $logEntry -ErrorAction SilentlyContinue } catch { } @@ -406,7 +406,7 @@ if ($shouldTrack) { & npx -y @azure/mcp@latest @mcpArgs 2>&1 | Out-Null } catch { } - # If AZURE_SKILLS_TELEMETRY_LOG env var is set, append the args to the file specified by the env var (for debugging) + # If AZURE_SKILLS_TELEMETRY_LOG_DIR env var is set, append the args to the telemetry.log file in that directory (for debugging) Write-TelemetryDebugLog -Content "MCP Args: $($mcpArgs -join ' ')" } diff --git a/plugin/hooks/scripts/track-telemetry.sh b/plugin/hooks/scripts/track-telemetry.sh index 1499fedc9..d8cb44705 100755 --- a/plugin/hooks/scripts/track-telemetry.sh +++ b/plugin/hooks/scripts/track-telemetry.sh @@ -121,7 +121,7 @@ write_raw_input_to_file() { mkdir -p "$rawInputDir" 2>/dev/null || return 0 local ts ts=$(date -u +"%Y%m%dT%H%M%SZ") - echo "$rawInputValue" > "$rawInputDir/$ts.json" 2>/dev/null || true + printf '%s\n' "$rawInputValue" > "$rawInputDir/$ts.json" 2>/dev/null || true } # Appends a debug log entry to the AZURE_SKILLS_TELEMETRY_LOG_DIR/telemetry.log @@ -130,7 +130,7 @@ write_telemetry_debug_log() { local content="$1" [ -n "$AZURE_SKILLS_TELEMETRY_LOG_DIR" ] || return 0 local logFile="$AZURE_SKILLS_TELEMETRY_LOG_DIR/telemetry.log" - echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") | $content" >> "$logFile" 2>/dev/null || true + echo "$(date +"%Y-%m-%dT%H:%M:%S") | $content" >> "$logFile" 2>/dev/null || true } # Resolve this script's directory so we can locate bundled skills. In the From 9e5f6af2afb726b080b530f8cb0b1b1b001b799c Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Wed, 5 Aug 2026 14:21:27 -0700 Subject: [PATCH 4/5] docs: update hooks manifest location to copilot-hooks.json Reflect the rename of the Copilot/VS Code hooks manifest away from the default hooks/hooks.json path and explain why (Claude's additive hooks discovery), per issue #2957. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ace74d0-93aa-4f8c-a9ef-0a928b7d50ad --- docs/hooks.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/hooks.md b/docs/hooks.md index dec5b750d..6e8758f2a 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -6,11 +6,11 @@ These files are used by clients when running agent sessions. We have to maintain ## Copilot CLI -Copilot CLI uses the `hooks.json` hooks manifest. Although it shares the manifest with VS Code, it only uses the `bash` and `powershell` properties defined in it. At runtime, Copilot CLI replaces the `PLUGIN_ROOT` variable to construct the path that can resolve the scripts. On macOS and Linux, it executes the `bash` script. On Windows, it executes the `powershell` script. +Copilot CLI uses the `copilot-hooks.json` hooks manifest, referenced explicitly via the `hooks` property in the Copilot plugin manifest (`.plugin/plugin.json`). Although it shares the manifest with VS Code, it only uses the `bash` and `powershell` properties defined in it. At runtime, Copilot CLI replaces the `PLUGIN_ROOT` variable to construct the path that can resolve the scripts. On macOS and Linux, it executes the `bash` script. On Windows, it executes the `powershell` script. ## VS Code -VS Code uses the `hooks.json` hooks manifest. Although it shares the manifest with Copilot CLI, it only uses the `windows`, `osx` and `linux` properties defined in it. At runtime, VS Code replaces the `PLUGIN_ROOT` variable to construct the path that can resolve the scripts. It then executes the script matching the host OS. +VS Code uses the `copilot-hooks.json` hooks manifest. Although it shares the manifest with Copilot CLI, it only uses the `windows`, `osx` and `linux` properties defined in it. At runtime, VS Code replaces the `PLUGIN_ROOT` variable to construct the path that can resolve the scripts. It then executes the script matching the host OS. ## Claude Code @@ -22,4 +22,6 @@ Cursor uses the `cursor-hooks.json` hooks manifest. At runtime, Cursor replaces ## Misc -Most clients look for `hooks/hooks.json` as the default hook configuration and try to use it if no explicit `hooks` property is defined in the plugin manifest. We decided to explicitly define hooks manifest for every client because it's impossible to create one hooks manifest for all clients. Copilot/VS Code, Claude and Cursor use mutually exclusive schema for hooks manifest, which means the manifest is guaranteed to cause syntax errors in one or more clients. Besides, clients use different variables to represent the plugin root. Having the incorrect variable will cause the client to fail to resolve the script path, resulting in runtime failures. \ No newline at end of file +Most clients look for `hooks/hooks.json` as the default hook configuration and try to use it if no explicit `hooks` property is defined in the plugin manifest. We decided to explicitly define a hooks manifest for every client because it's impossible to create one hooks manifest for all clients. Copilot/VS Code, Claude and Cursor use mutually exclusive schema for hooks manifest, which means the manifest is guaranteed to cause syntax errors in one or more clients. Besides, clients use different variables to represent the plugin root. Having the incorrect variable will cause the client to fail to resolve the script path, resulting in runtime failures. + +For this reason there is intentionally no file at the default `hooks/hooks.json` path. The Copilot/VS Code manifest is named `copilot-hooks.json` and is referenced explicitly from the Copilot plugin manifest. If a Copilot-format `hooks.json` were left at the default path, clients such as Claude Code — whose `hooks` property is *additive* to the default discovery rather than a replacement — would also load it and fail schema validation against their own hooks manifest (see [issue #2957](https://github.com/microsoft/GitHub-Copilot-for-Azure/issues/2957)). \ No newline at end of file From bc0056ad997b360d18a5b4d19b1be803edc76ef3 Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Wed, 5 Aug 2026 15:32:36 -0700 Subject: [PATCH 5/5] fix: bootstrap Copilot plugin manifest with explicit hooks property New plugins scaffolded by bootstrap.ts now set \hooks: ./hooks/copilot-hooks.json\ so no Copilot-format manifest sits at the default hooks/hooks.json path, consistent with issue #2957. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ace74d0-93aa-4f8c-a9ef-0a928b7d50ad --- scripts/src/plugin/bootstrap.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/src/plugin/bootstrap.ts b/scripts/src/plugin/bootstrap.ts index c230d7984..66bc017e2 100644 --- a/scripts/src/plugin/bootstrap.ts +++ b/scripts/src/plugin/bootstrap.ts @@ -26,7 +26,8 @@ function main() { mcpServers: "./.mcp.json" }; const copilotPluginManifest = { - ...pluginManifestBase + ...pluginManifestBase, + hooks: "./hooks/copilot-hooks.json" }; const claudeCodePluginManifest = { ...pluginManifestBase,