Skip to content

fix(plugin-loader): support Claude Code v3 flat array format for installed_plugins.json#2563

Merged
acamq merged 5 commits intocode-yeongyu:devfrom
robinmordasiewicz:fix/claude-code-plugin-v3-array-format
Mar 16, 2026
Merged

fix(plugin-loader): support Claude Code v3 flat array format for installed_plugins.json#2563
acamq merged 5 commits intocode-yeongyu:devfrom
robinmordasiewicz:fix/claude-code-plugin-v3-array-format

Conversation

@robinmordasiewicz
Copy link
Copy Markdown
Contributor

@robinmordasiewicz robinmordasiewicz commented Mar 14, 2026

Summary

Two bugs prevent Claude Code Marketplace plugins from loading in oh-my-opencode:

  1. Plugin discovery failsinstalled_plugins.json uses a flat array (v3), but the loader only handles v1/v2 object formats
  2. Agent loader crashes opencodemapClaudeModelToOpenCode() returns {providerID, modelID} objects, but opencode's parseModel() expects strings

Bug 1: Plugin Discovery

~/.claude/plugins/installed_plugins.json current format (flat array):

[
  {
    "name": "superpowers",
    "marketplace": "claude-plugins-official",
    "scope": "user",
    "version": "0.0.0",
    "installPath": "~/.claude/plugins/cache/claude-plugins-official/superpowers/0.0.0",
    "lastUpdated": "2026-03-14T01:38:34.000Z"
  }
]

The guard clause if (!db || !db.plugins) exits early because arrays have no .plugins property.

Fix (types.ts + discovery.ts)

  • Added InstalledPluginEntryV3 interface and included InstalledPluginEntryV3[] in the InstalledPluginsDatabase union type
  • Added v3EntryToInstallation() to convert flat array entries to PluginInstallation
  • Updated extractPluginEntries() to handle Array.isArray(db) — constructs plugin keys as name@marketplace
  • Fixed guard clause to (!Array.isArray(db) && !db.plugins) so arrays pass through

Bug 2: Agent Model Format

Once plugins load, agents from plugins (e.g., code-simplifier, feature-dev, pr-review-toolkit) crash opencode with:

TypeError: model.split is not a function

Both agent-loader.ts files spread mapClaudeModelToOpenCode() return value directly into agent config as { model: {providerID, modelID} }, but opencode expects { model: "provider/model" }.

Fix (agent-loader.ts + loader.ts)

Convert the object to a string before assigning:

const modelString = mappedModelOverride
  ? `${mappedModelOverride.providerID}/${mappedModelOverride.modelID}`
  : undefined

Backward Compatibility

v1 and v2 installed_plugins.json formats are unchanged. The array check runs first and only triggers for the new format.

Testing & Verification

Build & Typecheck

  • bun run build — passes
  • bun run typecheck — zero errors

Local Validation (real environment, 13 Claude Code plugins)

Before fix — every startup:

Loaded 0 plugins with 0 commands, 0 skills, 0 agents, 0 MCP servers

After fix — clean startup, no crashes:

[05:18:07.180Z] Loaded 13 plugins with 14 commands, 5 skills, 11 agents, 0 MCP servers
[05:18:07.190Z] agents loaded {"agentKeys":["Sisyphus (Ultraworker)","Hephaestus (Deep Agent)",
  "Prometheus (Plan Builder)","Atlas (Plan Executor)","Sisyphus-Junior","oracle","librarian",
  "explore","multimodal-looker","Metis (Plan Consultant)","Momus (Plan Critic)",
  "code-simplifier:code-simplifier","feature-dev:code-architect","feature-dev:code-explorer",
  "feature-dev:code-reviewer","pr-review-toolkit:code-reviewer","pr-review-toolkit:code-simplifier",
  "pr-review-toolkit:comment-analyzer","pr-review-toolkit:pr-test-analyzer",
  "pr-review-toolkit:silent-failure-hunter","pr-review-toolkit:type-design-analyzer",
  "hookify:conversation-analyzer","build","plan"]}
[05:18:07.193Z] config handler applied {"agentCount":24,"commandCount":27}

Results

Metric Before After
Startup crashes model.split is not a function None
Plugins discovered 0 13
Commands loaded 0 14
Skills loaded 0 5
Agents loaded 0 11
Total agents (builtin + plugin) 13 24
Total commands (builtin + plugin) 13 27

Files Changed

File Change
src/features/claude-code-plugin-loader/types.ts Added InstalledPluginEntryV3 interface, updated union type
src/features/claude-code-plugin-loader/discovery.ts v3 array handling in extractPluginEntries() and guard clause
src/features/claude-code-plugin-loader/agent-loader.ts Convert model object to "provider/model" string
src/features/claude-code-agent-loader/loader.ts Convert model object to "provider/model" string

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 14, 2026

All contributors have signed the CLA. Thank you! ✅
Posted by the CLA Assistant Lite bot.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: The PR correctly implements support for the new V3 flat array format while maintaining backward compatibility for V1 and V2, verified by local testing and type checks.


Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

@robinmordasiewicz
Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Mar 14, 2026
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1013 files

Confidence score: 3/5

  • There is a concrete user-impacting risk: src/features/claude-code-plugin-loader/discovery.ts can dereference properties on null V3 array entries, which may cause a fatal crash when input data is malformed.
  • Given the 7/10 severity and high confidence (8/10), this is more than a minor edge-case cleanup and introduces meaningful regression risk if malformed plugin metadata is encountered.
  • Pay close attention to src/features/claude-code-plugin-loader/discovery.ts - null-safe handling is needed to prevent runtime crashes during discovery.

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/features/claude-code-plugin-loader/discovery.ts">

<violation number="1" location="src/features/claude-code-plugin-loader/discovery.ts:116">
P1: Unsafe property access on potentially null V3 array entries causes fatal crash on malformed data</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

…ibility

mapClaudeModelToOpenCode() returns {providerID, modelID} but opencode
expects model as a string. Both agent loaders now convert to
'providerID/modelID' string format before assigning to config.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 3 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

Filter out null, undefined, or malformed entries in installed_plugins.json
before accessing properties. Prevents fatal crash on corrupted data.

Addresses cubic-dev-ai review feedback.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Requires human review: PR contains 150k+ lines and 1000+ files (including dist folder changes) despite claiming a small fix. Cannot be 100% sure of no regressions with such high volume of changes.

@acamq
Copy link
Copy Markdown
Collaborator

acamq commented Mar 14, 2026

It seems like you committed the built files in bce8ff3. Please remove these from the PR.

Reverts the dist/ directory added in bce8ff3 ("chore: include pre-built
dist for github install"). Built artifacts should not be tracked in git.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: Fixes a crash and adds support for Claude Code v3 plugins using safe, backward-compatible logic with no regressions.

@acamq
Copy link
Copy Markdown
Collaborator

acamq commented Mar 16, 2026

LGTM. Thank you!

@acamq acamq merged commit 73685da into code-yeongyu:dev Mar 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants