-
Notifications
You must be signed in to change notification settings - Fork 2
chore(plugin): lead both manifests with skill evaluation, add discovery metadata #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+215
−6
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b2981e1
chore(plugin): lead both manifests with skill evaluation, add discove…
uipreliga 4ef82e5
chore(plugin): pin both manifests to their published JSON schemas
uipreliga 1fa0a8f
chore(plugin): give the marketplace owner a contact address
uipreliga 5db2cd4
chore(plugin): give both author objects the same contact address
uipreliga d63d51b
chore(plugin): answer "skills only?", fold tags into keywords, add CE044
uipreliga File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,35 @@ | ||
| { | ||
| "$schema": "https://json.schemastore.org/claude-code-marketplace.json", | ||
| "name": "coder-eval", | ||
| "owner": { | ||
| "name": "UiPath", | ||
| "email": "coder-eval@uipath.com", | ||
| "url": "https://github.com/UiPath/coder_eval" | ||
| }, | ||
| "description": "Evaluate and benchmark AI coding agents and Claude Code skills.", | ||
| "description": "Test whether your Claude Code skills actually trigger, and benchmark AI coding agents against your own tasks.", | ||
| "plugins": [ | ||
| { | ||
| "name": "coder-eval", | ||
| "displayName": "Coder Eval", | ||
| "source": "./plugins/coder-eval", | ||
| "description": "Author, run, and analyze coder-eval suites — including whether your Claude Code skills actually trigger.", | ||
| "description": "Test whether your Claude Code skills actually trigger, and benchmark any coding agent — author, run, and analyze the eval suite that proves it, locally or as a CI gate.", | ||
| "author": { "name": "UiPath", "email": "coder-eval@uipath.com", "url": "https://github.com/UiPath/coder_eval" }, | ||
| "homepage": "https://coder-eval.com", | ||
| "repository": "https://github.com/UiPath/coder_eval", | ||
| "license": "Apache-2.0", | ||
| "category": "testing", | ||
| "keywords": ["evaluation", "testing", "skills", "benchmark", "ci"] | ||
| "keywords": [ | ||
| "claude-code-skills", | ||
| "skill-testing", | ||
| "skill-activation", | ||
| "evaluation", | ||
| "benchmark", | ||
| "agent-testing", | ||
| "github-actions", | ||
| "sandbox", | ||
| "llm-judge", | ||
| "ci" | ||
| ] | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,23 @@ | ||
| { | ||
| "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", | ||
| "name": "coder-eval", | ||
| "displayName": "Coder Eval", | ||
| "version": "0.11.4", | ||
| "description": "Author, run, and analyze coder-eval suites — including whether your Claude Code skills actually trigger.", | ||
| "author": { "name": "UiPath", "url": "https://github.com/UiPath/coder_eval" }, | ||
| "description": "Test whether your Claude Code skills actually trigger, and benchmark any coding agent — author, run, and analyze the eval suite that proves it, locally or as a CI gate.", | ||
| "author": { "name": "UiPath", "email": "coder-eval@uipath.com", "url": "https://github.com/UiPath/coder_eval" }, | ||
| "homepage": "https://coder-eval.com", | ||
| "repository": "https://github.com/UiPath/coder_eval", | ||
| "license": "Apache-2.0", | ||
| "keywords": ["evaluation", "testing", "claude-code-skills", "benchmark", "ci"] | ||
| "keywords": [ | ||
| "claude-code-skills", | ||
| "skill-testing", | ||
| "skill-activation", | ||
| "evaluation", | ||
| "benchmark", | ||
| "agent-testing", | ||
| "github-actions", | ||
| "sandbox", | ||
| "llm-judge", | ||
| "ci" | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| """CE044 — the marketplace entry and the plugin manifest are one metadata surface. | ||
|
|
||
| ``.claude-plugin/marketplace.json`` is what the ``/plugin`` browser and the | ||
| plugin directories show *before* install; ``plugins/coder-eval/.claude-plugin/ | ||
| plugin.json`` is what an installed user's copy carries *after*. Six fields are | ||
| byte-identical duplicates across the two files (``description``, ``keywords``, | ||
| ``author``, ``homepage``, ``repository``, ``license``, plus ``name`` and | ||
| ``displayName``), and nothing compared them — the only test that reads | ||
| ``plugin.json`` at all is ``tests/test_action_version_pin.py``, and only its | ||
| ``version``. A one-sided edit — retitling the plugin in the marketplace but not | ||
| the manifest — would ship silently and show two different one-liners in the wild. | ||
|
|
||
| The second half of the rule is the one that has already bitten: the marketplace | ||
| entry must not carry a *discovery* field the plugin manifest cannot mirror. The | ||
| marketplace schema allows both ``keywords`` ("Tags for plugin discovery and | ||
| categorization") and ``tags`` ("Tags for searchability and discovery"); the | ||
| plugin-manifest schema has no ``tags`` property at all. Splitting discovery | ||
| strings across the two therefore drops half of them from the installed copy, and | ||
| leaves a future editor with no rule for which list a new term belongs in. So an | ||
| extra key on the entry is a lint failure unless it is listed in | ||
| ``MARKETPLACE_ONLY`` with a written reason — the allowlist *is* the rule, kept in | ||
| code rather than in tribal knowledge. | ||
|
|
||
| Like CE026-CE031 and CE033 this reasons over whole files (JSON, plus resolving a | ||
| ``source`` path to a directory) rather than one ``.py`` AST, so it is not a | ||
| ``BaseRule`` in the runner; it is wired as a dedicated ``@pytest.mark.lint`` test | ||
| class in ``tests/test_custom_lint.py``. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
|
|
||
| # Fields both manifests can express, and which mean the same thing on both sides. | ||
| # `version` is deliberately absent: only `plugin.json` carries it (a derived pin of | ||
| # pyproject, guarded by tests/test_action_version_pin.py), and an entry-side copy | ||
| # would be a third pin no release step maintains. | ||
| SHARED_KEYS = ( | ||
| "name", | ||
| "displayName", | ||
| "description", | ||
| "author", | ||
| "homepage", | ||
| "repository", | ||
| "license", | ||
| "keywords", | ||
| ) | ||
|
|
||
| # Keys the marketplace entry may carry that the plugin manifest has no counterpart | ||
| # for. Each needs a reason: adding one here is the deliberate act of saying "this | ||
| # term does not belong in the shared surface". | ||
| MARKETPLACE_ONLY: dict[str, str] = { | ||
| "source": "the entry's pointer at the plugin directory; meaningless inside the manifest it points at", | ||
| "category": "a marketplace-browser facet with a fixed vocabulary, not a free discovery string", | ||
| } | ||
|
|
||
|
|
||
| def _entry_source_dir(repo_root: Path, entry: dict[str, Any]) -> Path: | ||
| source = entry.get("source") | ||
| if not isinstance(source, str): | ||
| raise TypeError(f"marketplace entry {entry.get('name')!r} has no string `source`") | ||
| return (repo_root / source).resolve() | ||
|
|
||
|
|
||
| def check(repo_root: Path) -> list[str]: | ||
| """Return one message per parity violation; empty means clean.""" | ||
| findings: list[str] = [] | ||
| marketplace_path = repo_root / ".claude-plugin" / "marketplace.json" | ||
| marketplace = json.loads(marketplace_path.read_text(encoding="utf-8")) | ||
|
|
||
| for entry in marketplace.get("plugins", []): | ||
| name = entry.get("name") | ||
| manifest_path = _entry_source_dir(repo_root, entry) / ".claude-plugin" / "plugin.json" | ||
| if not manifest_path.is_file(): | ||
| findings.append( | ||
| f"marketplace entry {name!r}: `source` does not resolve to a plugin manifest ({manifest_path})" | ||
| ) | ||
| continue | ||
| manifest = json.loads(manifest_path.read_text(encoding="utf-8")) | ||
|
|
||
| for key in SHARED_KEYS: | ||
| in_entry, in_manifest = entry.get(key), manifest.get(key) | ||
| if in_entry != in_manifest: | ||
| findings.append( | ||
| f"marketplace entry {name!r}: `{key}` differs from its plugin manifest\n" | ||
| f" marketplace: {in_entry!r}\n" | ||
| f" plugin.json: {in_manifest!r}" | ||
| ) | ||
|
|
||
| extras = set(entry) - set(SHARED_KEYS) - set(MARKETPLACE_ONLY) - {"$schema"} | ||
| for key in sorted(extras): | ||
| findings.append( | ||
| f"marketplace entry {name!r}: `{key}` has no counterpart in the plugin manifest, so its " | ||
| f"value is dropped from an installed user's copy. Fold it into `keywords`, or add it to " | ||
| f"MARKETPLACE_ONLY in tests/lint/plugin_manifest_parity.py with a reason." | ||
| ) | ||
|
|
||
| return findings |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.