Describe the bug
Repository-level enabledPlugins in .github/copilot/settings.json is not applied in non-interactive prompt mode (copilot -p). The same override is applied correctly in interactive mode and by copilot plugins list, so the surfaces disagree.
Concretely: a marketplace plugin set to false at user level and true in a trusted repo's .github/copilot/settings.json is reported as enabled by copilot plugins list for that directory, loads in an interactive session started in that directory, but is silently absent from a copilot -p run in the same directory. There is no warning.
This is the same class of bug as #3345 (.github/hooks/*.json not loaded in -p) and #3088 (copilot plugin marketplace list ignoring repo-level overrides), both of which were fixed.
Affected version
1.0.81-0 (Windows, x86_64)
Steps to reproduce the behavior
Given two marketplace-backed plugins — example-plugin@example-marketplace and other-plugin@other-marketplace — and a trusted folder C:\repos\myrepo (present in trustedFolders in ~/.copilot/config.json):
-
~/.copilot/settings.json:
{
"enabledPlugins": {
"other-plugin@other-marketplace": true,
"example-plugin@example-marketplace": false
}
}
-
C:\repos\myrepo\.github\copilot\settings.json:
{
"enabledPlugins": {
"example-plugin@example-marketplace": true
}
}
-
From C:\repos\myrepo, run copilot plugins list:
example-plugin@example-marketplace is shown as enabled (✓) for this directory, and disabled for other directories. Correct.
-
From C:\repos\myrepo, start an interactive session:
Startup banner reads Loading: X instructions, 2 plugins, Y skills. The plugin is loaded. Correct.
-
From C:\repos\myrepo, run copilot -p "Reply with exactly: PONG" with --log-level all:
Plugin activation [skills]: fingerprint=legacy, plugins=1, loaded=Y
Only other-plugin is loaded. example-plugin does not appear anywhere in the log, and its skills are absent from <available_skills>. This is the bug.
Note that the same -p run does log:
Loading repo hooks in prompt mode (folder is trusted or opt-in set)
Loading workspace MCP sources in prompt mode (folder is trusted or opt-in set)
so repo hooks and workspace MCP sources correctly honour folder trust in prompt mode — only enabledPlugins does not.
Expected behavior
In a trusted folder, copilot -p should apply repository-scoped enabledPlugins from .github/copilot/settings.json, consistent with interactive mode and with copilot plugins list.
At minimum, the two surfaces should agree: copilot plugins list should not report a plugin as enabled for a directory in which copilot -p will silently decline to load it.
Additional context
Root cause (from the 1.0.81-0 bundle). The two modes resolve the plugin set through different code paths:
-
Interactive builds a repository overlay in the UI layer and resolves with repositoryOverlay: { mode: "provided", enabledPlugins, trusted }.
-
Prompt mode computes its plugin list as, in effect:
const installed = [...getEnabledInstalledPlugins(config.installedPlugins || [], settings.enabledPlugins), ...explicitPlugins];
which calls the native configLoaderGetEnabledInstalledPlugins(installedPlugins, enabledPlugins, pluginDirOnly). That function receives no working directory, no repository overlay, and no trust parameter, so repository-scoped settings cannot reach it.
The session manager's own resolveSessionPluginActivation does use repositoryOverlay: { mode: "load", trust: { mode: "persisted" } }, but in prompt mode the plugin list has already been filtered before it is consulted.
Confirmation. Driving the native resolver (configResolveEffectivePlugins) directly with the two overlay modes, with everything else held constant:
repositoryOverlay |
resolved plugins in the repo directory |
{ mode: "load", trust: { mode: "persisted" } } |
other-plugin@other-marketplace, example-plugin@example-marketplace |
{ mode: "none" } |
other-plugin@other-marketplace |
The observed -p behaviour (plugins=1) matches mode: "none" exactly.
Opt-in env vars. Prompt mode has GITHUB_COPILOT_PROMPT_MODE_REPO_HOOKS, GITHUB_COPILOT_PROMPT_MODE_WORKSPACE_MCP and GITHUB_COPILOT_PROMPT_MODE_EXTENSIONS, but there is no equivalent for plugins/settings, and COPILOT_ALLOW_ALL=true does not cover this path either. So there is no supported way to opt in.
Workaround. Passing the plugin explicitly bypasses the filter, since explicit plugins are appended after it:
copilot -p "..." --plugin-dir "C:\repos\myrepo\skills\example-plugin"
This raises the log line to plugins=2, loaded=Y. It requires hard-coding an absolute path per invocation, so it does not substitute for repo-scoped configuration.
Environment. Windows 11, x86_64, Windows Terminal, PowerShell 7. Reproduced with --log-level all. Not tested on macOS or Linux.
Related: #3345 (fixed, same pattern for repo hooks), #3088 / #3087 (fixed, same interactive-vs-CLI inconsistency), #3742 (repo enabledPlugins override not honoured from nested cwd), #3637 (per-session enabledPlugins overlay).
Describe the bug
Repository-level
enabledPluginsin.github/copilot/settings.jsonis not applied in non-interactive prompt mode (copilot -p). The same override is applied correctly in interactive mode and bycopilot plugins list, so the surfaces disagree.Concretely: a marketplace plugin set to
falseat user level andtruein a trusted repo's.github/copilot/settings.jsonis reported as enabled bycopilot plugins listfor that directory, loads in an interactive session started in that directory, but is silently absent from acopilot -prun in the same directory. There is no warning.This is the same class of bug as #3345 (
.github/hooks/*.jsonnot loaded in-p) and #3088 (copilot plugin marketplace listignoring repo-level overrides), both of which were fixed.Affected version
1.0.81-0 (Windows, x86_64)
Steps to reproduce the behavior
Given two marketplace-backed plugins —
example-plugin@example-marketplaceandother-plugin@other-marketplace— and a trusted folderC:\repos\myrepo(present intrustedFoldersin~/.copilot/config.json):~/.copilot/settings.json:{ "enabledPlugins": { "other-plugin@other-marketplace": true, "example-plugin@example-marketplace": false } }C:\repos\myrepo\.github\copilot\settings.json:{ "enabledPlugins": { "example-plugin@example-marketplace": true } }From
C:\repos\myrepo, runcopilot plugins list:From
C:\repos\myrepo, start an interactive session:From
C:\repos\myrepo, runcopilot -p "Reply with exactly: PONG"with--log-level all:Note that the same
-prun does log:so repo hooks and workspace MCP sources correctly honour folder trust in prompt mode — only
enabledPluginsdoes not.Expected behavior
In a trusted folder,
copilot -pshould apply repository-scopedenabledPluginsfrom.github/copilot/settings.json, consistent with interactive mode and withcopilot plugins list.At minimum, the two surfaces should agree:
copilot plugins listshould not report a plugin as enabled for a directory in whichcopilot -pwill silently decline to load it.Additional context
Root cause (from the 1.0.81-0 bundle). The two modes resolve the plugin set through different code paths:
Interactive builds a repository overlay in the UI layer and resolves with
repositoryOverlay: { mode: "provided", enabledPlugins, trusted }.Prompt mode computes its plugin list as, in effect:
which calls the native
configLoaderGetEnabledInstalledPlugins(installedPlugins, enabledPlugins, pluginDirOnly). That function receives no working directory, no repository overlay, and no trust parameter, so repository-scoped settings cannot reach it.The session manager's own
resolveSessionPluginActivationdoes userepositoryOverlay: { mode: "load", trust: { mode: "persisted" } }, but in prompt mode the plugin list has already been filtered before it is consulted.Confirmation. Driving the native resolver (
configResolveEffectivePlugins) directly with the two overlay modes, with everything else held constant:repositoryOverlay{ mode: "load", trust: { mode: "persisted" } }other-plugin@other-marketplace,example-plugin@example-marketplace{ mode: "none" }other-plugin@other-marketplaceThe observed
-pbehaviour (plugins=1) matchesmode: "none"exactly.Opt-in env vars. Prompt mode has
GITHUB_COPILOT_PROMPT_MODE_REPO_HOOKS,GITHUB_COPILOT_PROMPT_MODE_WORKSPACE_MCPandGITHUB_COPILOT_PROMPT_MODE_EXTENSIONS, but there is no equivalent for plugins/settings, andCOPILOT_ALLOW_ALL=truedoes not cover this path either. So there is no supported way to opt in.Workaround. Passing the plugin explicitly bypasses the filter, since explicit plugins are appended after it:
This raises the log line to
plugins=2, loaded=Y. It requires hard-coding an absolute path per invocation, so it does not substitute for repo-scoped configuration.Environment. Windows 11, x86_64, Windows Terminal, PowerShell 7. Reproduced with
--log-level all. Not tested on macOS or Linux.Related: #3345 (fixed, same pattern for repo hooks), #3088 / #3087 (fixed, same interactive-vs-CLI inconsistency), #3742 (repo
enabledPluginsoverride not honoured from nested cwd), #3637 (per-sessionenabledPluginsoverlay).