Files
Playa/extensions/Volcanic.AgenticCore.Extensions.ClaudeCode/BuiltinPlugins.cs
EugeneTes 094d518827 fix(claude-code): disable built-in plugins and learn new ones from init
Claude Code 2.1.287 loads cc-plugin-* built-ins regardless of --setting-sources,
which tripped the fail-closed init check. Disable every known built-in in
--settings, and when init lists only built-ins that slipped through, disable them
process-wide and replay the request once. Other plugins still fail closed.
2026-10-02 10:08:23 +00:00

52 lines
2.2 KiB
C#

namespace Volcanic.AgenticCore.Extensions.ClaudeCode;
/// <summary>
/// The built-in Claude Code plugins every request disables through <c>enabledPlugins</c>. Built-ins load whatever the
/// setting sources say, and which ones load depends on the CLI version and the account's feature flags, so the set
/// starts with the known ones and learns new ones from <c>system/init</c> (the request is then replayed once).
/// </summary>
public sealed class BuiltinPlugins
{
public const string Suffix = "@builtin";
/// <summary>The built-ins known to the CLI versions this provider was tested with.</summary>
public static readonly IReadOnlyList<string> Known =
[
"agents-md@builtin", "telemetry@builtin",
"cc-plugin-agents-md@builtin", "cc-plugin-claude-test@builtin", "cc-plugin-diff@builtin", "cc-plugin-mermaid@builtin",
"cc-plugin-mods-guide@builtin", "cc-plugin-plugin-authoring@builtin", "cc-plugin-responsive-mode@builtin",
"cc-plugin-sec-default@builtin", "cc-plugin-telemetry@builtin", "cc-plugin-tips@builtin", "cc-plugin-you-should-know@builtin",
];
/// <summary>Process-wide: a built-in learned in one session is disabled for every later request.</summary>
public static BuiltinPlugins Shared { get; } = new();
private readonly Lock _gate = new();
private readonly SortedSet<string> _disabled;
public BuiltinPlugins(IEnumerable<string>? initial = null) =>
_disabled = new SortedSet<string>(initial ?? Known, StringComparer.Ordinal);
/// <summary>A sorted snapshot of the plugins to disable.</summary>
public IReadOnlyList<string> Disabled
{
get { lock (_gate) return [.. _disabled]; }
}
/// <summary>Adds built-in plugin ids; true when at least one was new.</summary>
public bool Disable(IEnumerable<string> plugins)
{
ArgumentNullException.ThrowIfNull(plugins);
var added = false;
lock (_gate)
{
foreach (var plugin in plugins)
if (IsBuiltin(plugin)) added |= _disabled.Add(plugin);
}
return added;
}
public static bool IsBuiltin(string? source) =>
source is { Length: > 9 } s && s.EndsWith(Suffix, StringComparison.Ordinal) && !s.Contains('/') && !s.Contains('\\');
}