Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save alexey-pelykh/566a4e5160b305db703d543312a1e686 to your computer and use it in GitHub Desktop.

Select an option

Save alexey-pelykh/566a4e5160b305db703d543312a1e686 to your computer and use it in GitHub Desktop.

Investigative Engineering Report: Claude Code extraKnownMarketplaces in Headless Mode

Date: December 4, 2025 Claude Code Version: 2.0.58


1. Introduction

1.1 The Mystery

A development team attempted to standardize plugin distribution across their organization using Claude Code's extraKnownMarketplaces feature. The configuration was placed in the repository's .claude/settings.json file, following the official documentation which promised:

"When team members trust the repository folder, Claude Code automatically installs these marketplaces and any plugins specified in the enabledPlugins field."

The problem: In their CI/CD pipeline using GitHub Actions, the feature simply didn't work. The claude plugin marketplace list command returned "No marketplaces configured" despite the project settings being correctly configured.

1.2 Investigation Objectives

  1. Understand how extraKnownMarketplaces is supposed to work
  2. Determine why it fails in CI/headless mode
  3. Find workarounds for automated workflows
  4. Document findings for the community

2. The Investigation

2.1 Initial Hypothesis

The first hypothesis was that the --setting-sources CLI flag might control which settings are loaded. The -p (print/headless) mode documentation states:

"The workspace trust dialog is skipped when Claude is run with the -p mode."

Perhaps project settings simply weren't being loaded in print mode?

2.2 Test Environment Setup

A controlled test environment was created to isolate variables:

/tmp/test-setting-sources/
├── .claude/
│   └── settings.json          # extraKnownMarketplaces config
├── .claude-plugin/
│   └── marketplace.json       # Valid marketplace definition
└── test-plugin/
    └── plugin.json            # Minimal plugin definition

Project settings (.claude/settings.json):

{
  "extraKnownMarketplaces": {
    "test-marketplace": {
      "source": { "source": "directory", "path": "./" }
    }
  }
}

Marketplace definition (.claude-plugin/marketplace.json):

{
  "name": "test-marketplace",
  "owner": { "name": "Test Owner" },
  "plugins": [
    { "name": "test-plugin", "source": "./test-plugin" }
  ]
}

2.3 Discovery: Two Separate Storage Systems

The first breakthrough came from examining the Claude Code configuration directory:

$ find ~/.claude -name "*marketplace*"
/Users/user/.claude/plugins/known_marketplaces.json

Inspection revealed that claude plugin marketplace add writes to this file:

{
  "some-marketplace": {
    "source": { "source": "directory", "path": "/path/to/project" },
    "installLocation": "/path/to/project",
    "lastUpdated": "2025-12-04T14:27:54.862Z"
  }
}

Key insight: There are TWO separate systems:

  1. extraKnownMarketplaces in settings files (supposed to define available sources)
  2. ~/.claude/plugins/known_marketplaces.json (actually added marketplaces)

The plugin commands only read from #2.

2.4 The Critical Test

To prove project settings were being ignored, the user-level marketplace storage was temporarily removed:

$ mv ~/.claude/plugins/known_marketplaces.json ~/.claude/plugins/known_marketplaces.json.bak
$ cd /tmp/test-setting-sources
$ claude plugin marketplace list

Result:

No marketplaces configured

Evidence: With no user-level storage, the project's extraKnownMarketplaces was completely invisible to the plugin system.

2.5 Testing the --setting-sources Flag

The hypothesis that --setting-sources might enable project settings was tested:

$ cd /tmp/test-setting-sources
$ claude plugin --setting-sources user,project,local marketplace list

Result: Still only showed user-level marketplaces. The flag had no effect on plugin subcommands.

2.6 The Trust Mechanism Investigation

The documentation mentions "trust" as the trigger. Investigation of ~/.claude.json revealed trust state tracking:

{
  "projects": {
    "/path/to/some/project": {
      "hasTrustDialogAccepted": true,
      "allowedTools": [],
      "projectOnboardingSeenCount": 4
    }
  }
}

Hypothesis: Perhaps setting hasTrustDialogAccepted: true would trigger auto-installation?

Test:

$ jq '.projects["/tmp/test-setting-sources"] = {"hasTrustDialogAccepted": true}' ~/.claude.json > /tmp/new.json
$ mv /tmp/new.json ~/.claude.json
$ cd /tmp/test-setting-sources
$ claude plugin marketplace list

Result: Still no test-marketplace. The trust flag alone doesn't trigger installation.

2.7 Final Understanding

The auto-installation logic is executed during the interactive trust dialog event handler, not:

  • At session start
  • When trust flag is already set
  • In print/headless mode
  • By plugin subcommands

The flow in interactive mode:

  1. User runs claude in a new directory
  2. Trust dialog appears: "Do you trust the files in this folder?"
  3. User clicks "Yes, proceed"
  4. At this exact moment: extraKnownMarketplaces is processed
  5. Marketplaces are copied to ~/.claude/plugins/known_marketplaces.json
  6. enabledPlugins is processed
  7. Plugins are installed

In print mode (-p), step 2-3 are skipped, so steps 4-7 never occur.


3. Findings Summary

3.1 Architecture Diagram

┌─────────────────────────────────────────────────────────────────┐
│                     INTERACTIVE MODE                            │
├─────────────────────────────────────────────────────────────────┤
│  1. User runs `claude` in project directory                     │
│  2. Trust dialog: "Do you trust the files in this folder?"      │
│  3. User clicks "Yes, proceed"                                  │
│  4. ─────► extraKnownMarketplaces processed ◄─────              │
│  5. Marketplaces auto-installed to ~/.claude/plugins/           │
│  6. enabledPlugins processed                                    │
│  7. Plugins auto-installed                                      │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                      PRINT MODE (-p)                            │
├─────────────────────────────────────────────────────────────────┤
│  1. User runs `claude -p "prompt"`                              │
│  2. Trust dialog SKIPPED (documented behavior)                  │
│  3. ─────► extraKnownMarketplaces NEVER processed ◄─────        │
│  4. Only ~/.claude/plugins/known_marketplaces.json consulted    │
│  5. Project extraKnownMarketplaces completely ignored           │
└─────────────────────────────────────────────────────────────────┘

3.2 Evidence Table

Test Command Expected Actual Conclusion
Baseline claude plugin marketplace list Show project marketplace Only user marketplaces Project ignored
No user storage Same, after removing user storage Show project marketplace "No marketplaces configured" Project completely ignored
With --setting-sources claude plugin --setting-sources user,project,local marketplace list Show project marketplace Only user marketplaces Flag has no effect
Install from project claude plugin install test-plugin@test-marketplace Install plugin "Plugin not found" Project marketplace not recognized
Manual add claude plugin marketplace add ./ Add marketplace Success Writes to user storage
Trust flag set marketplace list after setting hasTrustDialogAccepted Show project marketplace Only user marketplaces Trust flag alone insufficient

3.3 Key Files Reference

File Purpose Read by plugin commands?
~/.claude/settings.json User-level settings Unknown
~/.claude/plugins/known_marketplaces.json Added marketplaces YES
~/.claude/plugins/installed_plugins.json Installed plugins YES
~/.claude.json Trust state, project configs For trust only
.claude/settings.json Project settings (extraKnownMarketplaces) NO (by plugin cmds)
.claude/settings.local.json Local project settings Unknown

4. Workarounds for CI/CD

4.1 Use Action's plugin_marketplaces Input

- uses: anthropics/claude-code-action@v1
  with:
    plugin_marketplaces: |
      https://github.com/your-org/your-marketplace.git
    plugins: |
      your-plugin@your-marketplace

4.2 Manual Marketplace Add

- name: Add local marketplace
  run: claude plugin marketplace add ./

- name: Install plugins
  run: claude plugin install my-plugin@my-marketplace

4.3 Pre-populate User Storage

- name: Configure marketplace
  run: |
    mkdir -p ~/.claude/plugins
    cat > ~/.claude/plugins/known_marketplaces.json << 'EOF'
    {
      "my-marketplace": {
        "source": { "source": "directory", "path": "${{ github.workspace }}" },
        "installLocation": "${{ github.workspace }}",
        "lastUpdated": "2025-01-01T00:00:00.000Z"
      }
    }
    EOF

5. Recommendations

5.1 For Users

  1. Don't rely on extraKnownMarketplaces in CI/headless workflows - it will not work
  2. Use explicit claude plugin marketplace add commands in your workflow
  3. Consider the plugin_marketplaces action input if using the official GitHub Action

5.2 For Anthropic (Suggested Improvements)

  1. Add a CLI flag to process project settings in headless mode

    • Example: claude -p --trust-project-settings "prompt"
    • This would process extraKnownMarketplaces and enabledPlugins without interactive dialog
  2. Make plugin subcommands respect project settings

    • claude plugin marketplace list should show extraKnownMarketplaces
    • claude plugin install should recognize project-defined marketplaces
  3. Improve documentation

    • Clarify that extraKnownMarketplaces requires interactive trust dialog
    • Document the limitation for CI/headless use cases
    • Add a "CI/CD" section with recommended approaches
  4. Consider a --setting-sources flag for plugin subcommands

    • Allow claude plugin --setting-sources project marketplace list

6. Conclusion

The extraKnownMarketplaces feature is designed for interactive team onboarding, not automated CI/CD pipelines. The auto-installation is intentionally tied to the trust dialog as a security measure - ensuring users explicitly consent before loading project-defined plugin sources.

In headless mode, where there's no human to consent, this trust-gated flow is bypassed entirely. This is arguably correct from a security standpoint (untrusted PR code shouldn't auto-install plugins), but it means the feature cannot be used for CI automation.

Until Anthropic provides an explicit opt-in mechanism for headless mode, teams must use workarounds: manual marketplace adds, action inputs, or pre-populated configuration files.


References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment