Skip to content

feat(claude-code): emit installationPreference in marketplace.json entries - #41430

Open
devin-ai-integration[bot] wants to merge 11 commits into
mainfrom
litellm_marketplace_installation_preference
Open

devin-ai-integration[bot] wants to merge 11 commits into
mainfrom
litellm_marketplace_installation_preference

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • marketplace.json entries never carry installationPreference
  • Claude Desktop treats every same-origin plugin as manual install only

How it solves it:

  • PluginSpec gains an optional installation_preference field
  • Only available, auto_install or required are accepted, anything else is a 422
  • marketplace.json emits it as installationPreference per plugin

User Flow

Before: a proxy admin registers an internal plugin and Claude Desktop will not auto-install it

  1. They send POST https://litellm-domain/claude-code/plugins with {"name": "my-plugin", "source": {"source": "archive", "url": "https://files.example.com/my-plugin.zip", "sha256": "..."}} and get back 200 with "action": "created"
  2. They open https://litellm-domain/claude-code/marketplace.json and the entry has name, source, version but nothing about installation
  3. Claude Desktop, pointed at the same origin, lists the plugin as manual install only

After: the same admin marks the plugin for auto install and Claude Desktop picks it up

  1. They send POST https://litellm-domain/claude-code/plugins with the same body plus "installation_preference": "auto_install" and get back 200 with "action": "created"
  2. They open https://litellm-domain/claude-code/marketplace.json and the entry now carries "installationPreference": "auto_install"
  3. Claude Desktop, pointed at the same origin, auto-installs the plugin

A value outside available, auto_install or required is rejected with 422 rather than silently dropped. The same field is accepted by PUT https://litellm-domain/claude-code/plugins/{name} and echoed back as installation_preference by GET https://litellm-domain/claude-code/plugins and GET https://litellm-domain/claude-code/plugins/{name}. Entries without it are unchanged

Relevant issues

Feature request from a customer (Pylon #8704)

Linear ticket

Resolves LIT-7868

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Last updated: 1ec6486. QA and /live-pr-risk ran on 19a6c00, the last commit with a product diff

Two proxies booted from this rig, each with --num_workers 2 on a random port and its own fresh Postgres database: the before leg at the merge base 118ce3c on port 31361 and the after leg at this PR's tip 19a6c00 on port 50017. The proxy admin's calls are the curl commands below, run as typed with the leg's master key in $LITELLM_MASTER_KEY. The end-user client is Claude Code v2.1.286 driven interactively under tmux against each proxy's marketplace.json. Claude Desktop is the client that acts on installationPreference, and only under managed configuration, so it was not driven here; what it reads is the marketplace.json shown in each leg. Docs: BerriAI/litellm-docs#1497

Before (118ce3c)

Register one plugin with installation_preference and one without, read every surface that serves them back, then send a value outside the allowed set

$ curl -s -X POST http://127.0.0.1:31361/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"name":"internal-auto-plugin","version":"1.0.0","description":"Auto install demo","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"'$(printf 'a%.0s' {1..64})'"},"installation_preference":"auto_install"}'
{"status":"success","action":"created","plugin":{"id":"0c6549d4-87b2-4e9d-903a-1c0432929b11","name":"internal-auto-plugin","version":"1.0.0","description":"Auto install demo","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"enabled":true}}
$ curl -s -X POST http://127.0.0.1:31361/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"name":"internal-manual-plugin","version":"1.0.0","description":"Manual install demo","source":{"source":"archive","url":"https://files.example.com/internal-manual-plugin.zip","sha256":"'$(printf 'b%.0s' {1..64})'"}}' | jq -c '{status, action, name: .plugin.name}'
{"status":"success","action":"created","name":"internal-manual-plugin"}
$ curl -s http://127.0.0.1:31361/claude-code/marketplace.json | jq -c '.plugins[]'
{"name":"internal-auto-plugin","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"version":"1.0.0","description":"Auto install demo"}
{"name":"internal-manual-plugin","source":{"source":"archive","url":"https://files.example.com/internal-manual-plugin.zip","sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},"version":"1.0.0","description":"Manual install demo"}
$ curl -s http://127.0.0.1:31361/claude-code/plugins/internal-auto-plugin -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq -c 'with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-auto-plugin"}
$ curl -s http://127.0.0.1:31361/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq -c '.plugins[] | with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-manual-plugin"}
{"name":"internal-auto-plugin"}
$ curl -s http://127.0.0.1:31361/public/skill_hub | jq -c '.plugins[] | with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-auto-plugin"}
{"name":"internal-manual-plugin"}
$ curl -s -w '\nHTTP %{http_code}\n' -X POST http://127.0.0.1:31361/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"name":"bad-plugin","version":"1.0.0","description":"Bad value demo","source":{"source":"archive","url":"https://files.example.com/bad-plugin.zip","sha256":"'$(printf 'c%.0s' {1..64})'"},"installation_preference":"sometimes"}'
{"status":"success","action":"created","plugin":{"id":"514ad4fd-12c3-4dfd-a6f7-358800e33492","name":"bad-plugin","version":"1.0.0","description":"Bad value demo","source":{"source":"archive","url":"https://files.example.com/bad-plugin.zip","sha256":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"},"enabled":true}}
HTTP 200
$ curl -s http://127.0.0.1:31361/claude-code/marketplace.json | jq -c '[.plugins[].name]'
["internal-auto-plugin","internal-manual-plugin","bad-plugin"]

Claude Code v2.1.286 under tmux: /plugin marketplace add http://127.0.0.1:31361/claude-code/marketplace.json, then /plugins, internal typed into the Discover search, Enter on internal-auto-plugin

pr41430-19a6c00ed8-before-cli-plugins.png

pr41430-19a6c00ed8-before-cli-plugin-detail.png

Replace the plugin with a body that carries only source

$ curl -s -X PUT http://127.0.0.1:31361/claude-code/plugins/internal-auto-plugin -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"'$(printf 'a%.0s' {1..64})'"}}' | jq -c '{status, action, name: .plugin.name}'
{"status":"success","action":"updated","name":"internal-auto-plugin"}
$ curl -s http://127.0.0.1:31361/claude-code/marketplace.json | jq -c '.plugins[] | select(.name == "internal-auto-plugin")'
{"name":"internal-auto-plugin","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}
$ curl -s http://127.0.0.1:31361/claude-code/plugins/internal-auto-plugin -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq -c 'with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-auto-plugin"}

After (19a6c00)

Register one plugin with installation_preference and one without, read every surface that serves them back, then send a value outside the allowed set

$ curl -s -X POST http://127.0.0.1:50017/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"name":"internal-auto-plugin","version":"1.0.0","description":"Auto install demo","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"'$(printf 'a%.0s' {1..64})'"},"installation_preference":"auto_install"}'
{"status":"success","action":"created","plugin":{"id":"6825ec14-0ac9-4284-859f-51add37e299c","name":"internal-auto-plugin","version":"1.0.0","description":"Auto install demo","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"enabled":true}}
$ curl -s -X POST http://127.0.0.1:50017/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"name":"internal-manual-plugin","version":"1.0.0","description":"Manual install demo","source":{"source":"archive","url":"https://files.example.com/internal-manual-plugin.zip","sha256":"'$(printf 'b%.0s' {1..64})'"}}' | jq -c '{status, action, name: .plugin.name}'
{"status":"success","action":"created","name":"internal-manual-plugin"}
$ curl -s http://127.0.0.1:50017/claude-code/marketplace.json | jq -c '.plugins[]'
{"name":"internal-auto-plugin","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"installationPreference":"auto_install","version":"1.0.0","description":"Auto install demo"}
{"name":"internal-manual-plugin","source":{"source":"archive","url":"https://files.example.com/internal-manual-plugin.zip","sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},"version":"1.0.0","description":"Manual install demo"}
$ curl -s http://127.0.0.1:50017/claude-code/plugins/internal-auto-plugin -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq -c 'with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-auto-plugin","installation_preference":"auto_install"}
$ curl -s http://127.0.0.1:50017/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq -c '.plugins[] | with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-manual-plugin","installation_preference":null}
{"name":"internal-auto-plugin","installation_preference":"auto_install"}
$ curl -s http://127.0.0.1:50017/public/skill_hub | jq -c '.plugins[] | with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-auto-plugin","installation_preference":"auto_install"}
{"name":"internal-manual-plugin","installation_preference":null}
$ curl -s -w '\nHTTP %{http_code}\n' -X POST http://127.0.0.1:50017/claude-code/plugins -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"name":"bad-plugin","version":"1.0.0","description":"Bad value demo","source":{"source":"archive","url":"https://files.example.com/bad-plugin.zip","sha256":"'$(printf 'c%.0s' {1..64})'"},"installation_preference":"sometimes"}'
{"detail":[{"type":"literal_error","loc":["body","installation_preference"],"msg":"Input should be 'available', 'auto_install' or 'required'"}]}
HTTP 422
$ curl -s http://127.0.0.1:50017/claude-code/marketplace.json | jq -c '[.plugins[].name]'
["internal-auto-plugin","internal-manual-plugin"]

Claude Code v2.1.286 under tmux: /plugin marketplace add http://127.0.0.1:50017/claude-code/marketplace.json, then /plugins, internal typed into the Discover search, Enter on internal-auto-plugin

pr41430-19a6c00ed8-after-cli-plugins.png

pr41430-19a6c00ed8-after-cli-plugin-detail.png

Replace the plugin with a body that carries only source

$ curl -s -X PUT http://127.0.0.1:50017/claude-code/plugins/internal-auto-plugin -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d '{"source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"'$(printf 'a%.0s' {1..64})'"}}' | jq -c '{status, action, name: .plugin.name}'
{"status":"success","action":"updated","name":"internal-auto-plugin"}
$ curl -s http://127.0.0.1:50017/claude-code/marketplace.json | jq -c '.plugins[] | select(.name == "internal-auto-plugin")'
{"name":"internal-auto-plugin","source":{"source":"archive","url":"https://files.example.com/internal-auto-plugin.zip","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}
$ curl -s http://127.0.0.1:50017/claude-code/plugins/internal-auto-plugin -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq -c 'with_entries(select(.key == "name" or .key == "installation_preference"))'
{"name":"internal-auto-plugin","installation_preference":null}

Dependents driven live (/live-pr-risk)

The bundled Admin UI, logged in as the admin on each leg, at /ui/skills and the internal-auto-plugin detail page. Both legs render the same rows and fields; the page reads name, version, description, source and id, never the preference. The before leg lists bad-plugin because that proxy accepted the invalid value

pr41430-19a6c00ed8-before-ui-skills.png

pr41430-19a6c00ed8-before-ui-plugin-detail.png

pr41430-19a6c00ed8-after-ui-skills.png

pr41430-19a6c00ed8-after-ui-plugin-detail.png

Merge-ref check: this PR merged onto origin/main 6fd9334 (a throwaway merge commit, tree e1eabc8962) booted the same way on port 49052, and the whole command sequence above produced the same output as the after leg, the generated plugin id aside

Observations from the legs

  • CLI shows no preference label on either leg: leaves alone
  • POST and PUT responses omit installation_preference: leaves alone
  • PUT with only source drops version and description: leaves alone
  • Marketplace plugin order flips after PUT, both legs: leaves alone
  • Claude Code auto-runs /reload-plugins after marketplace add: leaves alone
  • Admin UI cannot set or show the preference: leaves alone

/audit: integration cells, base 118ce3c vs head 1ec6486

One checked-in test per inventory row in tests/integration/management/test_claude_code_plugin_installation_preference.py (group management), run against a real proxy rig: owned Redis, a fresh Postgres database per leg, the scripted upstream from tests/integration/_support/upstream.py, and per leg two proxy processes sharing that database (primary and peer) booted from the leg's commit, no mocks inside the proxy. The chaos rows boot their own two-worker proxies through owned_proxy_process. Command per run: python tests/integration/run.py management --seed 4106601 --order-seed 0 <file>

Run Commit Result
head run 1 1ec6486 36 passed, 0 skipped, exit 0
head run 2 1ec6486 35 passed; row 21's own two-worker proxy missed the 70s readiness budget (box load)
head run 3 1ec6486 35 passed; same readiness miss on row 21
head run 4 1ec6486 35 passed; same readiness miss on row 21
base run 1 118ce3c 36 failed, 0 skipped
head, whole management group 1ec6486 418 passed, 13 failed, 7 skipped in 49:48; every failure an owned proxy missing the suite's 70s readiness budget under box load, none a product assertion

Every cell asserts the new field, so every cell is red on the base by construction; the base column names where the base first diverged

# Scenario Node id Base 118ce3c Head 1ec6486
1 each value served on marketplace.json test_registered_preference_is_served_on_the_marketplace[available|auto_install|required] no installationPreference PASS x4
2 list, detail, skill hub echo test_preference_echoes_on_list_detail_and_skill_hub no field PASS x4
3 written on primary, read on peer test_peer_process_serves_the_stored_preference no field PASS x4
4 POST without the field test_omitted_preference_is_unset key absent (head: null) PASS x4
5 POST with null test_explicit_null_preference_is_unset key absent (head: null) PASS x4
6 POST with six invalid shapes test_invalid_preference_is_refused_on_register[6 ids] 200, value dropped PASS x4, 422
7 PUT with six invalid shapes test_invalid_preference_is_refused_on_update[6 ids] 200, value dropped PASS x4, 422
8 raw JSON key twice test_duplicate_json_key_keeps_the_last_value no field PASS x4
9 PUT twice, same value test_repeated_identical_update_is_idempotent no field PASS x4
10 duplicate name test_duplicate_name_is_refused_and_keeps_the_first_preference 409 held, then no field PASS x4
11 PUT changes then clears test_update_changes_and_clears_the_preference no field PASS x4
12 PUT whole-object diff test_update_replaces_the_whole_manifest no field PASS x4
13 unauthenticated writes test_unauthenticated_requests_are_refused 401s held, then no field PASS x4
14 non-admin writes test_non_admin_key_cannot_modify_the_catalog 403s held, then no field PASS x4
15 disabled plugin, granted key test_disabled_plugin_keeps_its_preference_for_granted_keys no field PASS x4
16 disabled plugin, non-granted key test_non_granted_key_cannot_read_a_disabled_plugin no field at the first read PASS x4
17 hand-edited unknown value test_hand_edited_unknown_preference_is_not_served key absent (head: null) PASS x4
18 openapi.json enum test_openapi_declares_the_preference_enum[3 schemas] property missing PASS x4
19 13 concurrent PUTs under 40 reads test_burst_reads_across_processes_while_the_preference_flips every read None PASS x4
20 repeated identical reads test_repeated_identical_reads_are_stable no field PASS x4
21 worker killed mid-burst test_registration_burst_survives_a_worker_kill no field, all POSTs landed PASS in run 1; runs 2 to 4 missed the 70s readiness budget before the first request (box load); green 4x at the two earlier tips
22 proxy restart after a burst test_registrations_survive_a_proxy_restart no field, all rows survived PASS x4
23 Claude Desktop auto-install none not run not run

Audit verdict: blocked on row 23, and row 21 is short of its second green at this tip. Its own two-worker proxy took 76s to 97s to boot on a box at 21 to 27 GB of swap, past the suite's 70s readiness budget, in three of four file runs and in the group run, where the same class took 12 other cells across four suite files; the cell is green here in run 1 and four times at the two earlier tips on the same product code, and the file gets rerun on a calm box before any PASS. Claude Desktop honors installationPreference only for a marketplace served from a managed deployment's inferenceGatewayBaseUrl origin, so that row needs a managed Claude Desktop deployment against an HTTPS LiteLLM origin, which this rig cannot provide; rows 1-22 are verified. Pre-existing on both legs: a non-admin key gets 401 on /claude-code/plugins/{name}/enable and /disable but 403 on POST, PUT, and DELETE, and concurrent PUTs on one plugin are last-commit-wins

Type

🆕 New Feature

Caveats (if any)

Medium

  • Claude Desktop's auto-install itself was not driven, only the contract it reads
    • This is the /audit blocker (row 23 above). Both legs stop at marketplace.json. The field name, its three values, the archive plus sha256 rule, and the same-origin rule were checked against Anthropic's Claude Desktop extensions doc on 2026-09-30. Closing it needs a managed Claude Desktop deployment whose inferenceGatewayBaseUrl is an HTTPS LiteLLM origin that also serves the plugin archive
  • auto_install and required are accepted on any source type, while Claude Desktop honors them only on an archive source with a sha256 served from its gateway origin
    • A github or git-subdir plugin marked auto_install is served as marked and silently stays manual install in Claude Desktop. The alternative is a 422 on the mark for a non-archive source. Not done here because Anthropic's doc pins per-plugin marks to archives by example only, so it is left open for the reviewer

Low

  • The audit's worker-kill cell boots a two-worker proxy that took 76s to 97s on this box tonight against the suite's 70s readiness budget
    • Green once at 1ec6486 and four times at the two earlier tips on the same product code, so the field's behavior is not in question; a calm-box rerun of the file is owed before /audit can pass
  • A stored installation_preference outside the three values is not served
    • Only reachable by editing manifest_json by hand, since POST and PUT reject it with a 422. marketplace.json, the list, get, and skill hub responses treat it as unset instead of failing the request
  • proxy-endpoints / Run tests is red on main too, not from this PR
    • batches_endpoints/test_endpoints.py::test_create__non_object_metadata_is_400 (both params) fails the same way on main run 36785087678
  • misc / Run tests is red on main too, not from this PR
    • the same four tests/unit/interactions/test_openapi_compliance.py cases fail on main run 36785087678; the test reads Google's live Interactions spec
  • budget-ratchet is non-gating and red from main, not from this PR
  • osv-scan is red on two lockfile pins main has already bumped, not from this PR
    • it flags tornado 6.5.8 and gitpython 3.1.61 in uv.lock, which this PR does not touch; main carries 6.5.10 and 3.1.62 since c168199e33, so merging main into the branch clears it, and main's own latest scan run 36762246733 is red on its newer pins the same way
  • proxy-behavior is red on its coverage upload step, not on a test, and main fails the same step
    • all 929 tests pass at 1ec6486 and the job then fails in Upload Lens database coverage, the same step that fails on main run 36805100205

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Link to Devin session: https://app.devin.ai/sessions/ee4e29a1f283440e865ceea49877f0fb
Open in Devin Desktop: https://app.devin.ai/desktop/session/ee4e29a1f283440e865ceea49877f0fb?variant=devin


Note

Low Risk
Additive marketplace metadata on admin plugin CRUD and public skill hub listing; behavior is unchanged when the field is not set.

Overview
Adds optional installation_preference (available, auto_install, required) on Claude Code marketplace plugin register/update APIs and related types. Invalid values are rejected at validation time (422) instead of being ignored.

When set, marketplace.json includes installationPreference on each plugin entry so Claude Desktop can honor auto-install for same-origin archive plugins. The field is omitted when unset. GET plugin, list, and /public/skill_hub echo installation_preference; a full PUT without the field clears it per existing replace semantics.

OpenAPI snapshot and dashboard schema.d.ts are updated; unit tests cover marketplace emission, validation, propagation, and clearing.

Reviewed by Cursor Bugbot for commit 4caf394. Bugbot is set up for automated code reviews on this repo. Configure here.

…tries

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
devin-ai-integration Bot requested a review from a team September 16, 2026 15:42
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@CLAassistant

CLAassistant commented Sep 16, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ mateo-berri
❌ devin-ai-integration[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@codspeed

codspeed Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_marketplace_installation_preference (1ec6486) with main (6997223)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Adds installation preference field to plugin marketplace.

The PR appears safe to merge based on the reviewed changes.

Summary

Adds an optional installation preference to Claude Code plugin registration and updates, emits it in marketplace entries, and exposes it through plugin read APIs. Validation, API schemas, and tests are updated. The prior worker-selection finding was manually resolved.

Reviews (7) · Last reviewed commit: "test(integration): give the marketplace ..."

@codecov

codecov Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

devin-ai-integration Bot and others added 5 commits September 16, 2026 16:10
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…values

Accept only available, auto_install, or required so a typo returns 422 instead of being silently dropped, and cover the PUT full-replace clearing behavior with a test
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor

cursor Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 1ec6486. Configure here.

This branch has not been deployed

No deployments
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.

3 participants