From a7f0a4cf466ac36090d15e1002aba1600cba9bd4 Mon Sep 17 00:00:00 2001 From: Robin Mordasiewicz Date: Sat, 14 Mar 2026 04:40:27 +0000 Subject: [PATCH] fix(plugin-loader): support Claude Code v3 flat array format for installed_plugins.json --- .../claude-code-plugin-loader/discovery.ts | 20 ++++++++++++- .../claude-code-plugin-loader/types.ts | 29 +++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/features/claude-code-plugin-loader/discovery.ts b/src/features/claude-code-plugin-loader/discovery.ts index c7c46a803..767c974f9 100644 --- a/src/features/claude-code-plugin-loader/discovery.ts +++ b/src/features/claude-code-plugin-loader/discovery.ts @@ -4,6 +4,7 @@ import { join } from "path" import { log } from "../../shared/logger" import type { InstalledPluginsDatabase, + InstalledPluginEntryV3, PluginInstallation, PluginManifest, LoadedPlugin, @@ -96,9 +97,26 @@ function isPluginEnabled( return true } +function v3EntryToInstallation(entry: InstalledPluginEntryV3): PluginInstallation { + return { + scope: entry.scope, + installPath: entry.installPath, + version: entry.version, + installedAt: entry.lastUpdated, + lastUpdated: entry.lastUpdated, + gitCommitSha: entry.gitCommitSha, + } +} + function extractPluginEntries( db: InstalledPluginsDatabase, ): Array<[string, PluginInstallation | undefined]> { + if (Array.isArray(db)) { + return db.map((entry) => [ + `${entry.name}@${entry.marketplace}`, + v3EntryToInstallation(entry), + ]) + } if (db.version === 1) { return Object.entries(db.plugins).map(([key, installation]) => [key, installation]) } @@ -111,7 +129,7 @@ export function discoverInstalledPlugins(options?: PluginLoaderOptions): PluginL const plugins: LoadedPlugin[] = [] const errors: PluginLoadError[] = [] - if (!db || !db.plugins) { + if (!db || (!Array.isArray(db) && !db.plugins)) { return { plugins, errors } } diff --git a/src/features/claude-code-plugin-loader/types.ts b/src/features/claude-code-plugin-loader/types.ts index f384f4ef6..9a6d5ee1c 100644 --- a/src/features/claude-code-plugin-loader/types.ts +++ b/src/features/claude-code-plugin-loader/types.ts @@ -30,19 +30,42 @@ export interface InstalledPluginsDatabaseV1 { } /** - * Installed plugins database v2 (current) - * plugins stored as arrays + * Installed plugins database v2 + * plugins stored as arrays keyed by plugin identifier */ export interface InstalledPluginsDatabaseV2 { version: 2 plugins: Record } +/** + * Installed plugins database v3 entry (current Claude Code format) + * A flat array of plugin entries, each containing name and marketplace fields + * used to construct the plugin key as "name@marketplace". + */ +export interface InstalledPluginEntryV3 { + name: string + marketplace: string + scope: PluginScope + version: string + installPath: string + lastUpdated: string + gitCommitSha?: string +} + /** * Installed plugins database structure * Located at ~/.claude/plugins/installed_plugins.json + * + * Supports three formats: + * - v1: { version: 1, plugins: Record } + * - v2: { version: 2, plugins: Record } + * - v3: InstalledPluginEntryV3[] (flat array, current Claude Code format) */ -export type InstalledPluginsDatabase = InstalledPluginsDatabaseV1 | InstalledPluginsDatabaseV2 +export type InstalledPluginsDatabase = + | InstalledPluginsDatabaseV1 + | InstalledPluginsDatabaseV2 + | InstalledPluginEntryV3[] /** * Plugin author information