@@ -51,6 +51,23 @@ elif not isinstance(author, dict):
5151elif not isinstance(author.get("name"), str) or not author["name"].strip():
5252 errors.append("'author.name' must be a non-empty string")
5353
54+ skills = data.get("skills")
55+ if isinstance(skills, list):
56+ for i, skill in enumerate(skills):
57+ if not isinstance(skill, dict):
58+ errors.append(f"skills[{i}]: must be an object")
59+ continue
60+ if not isinstance(skill.get("name"), str) or not skill["name"].strip():
61+ errors.append(f"skills[{i}]: missing 'name'")
62+ if not isinstance(skill.get("description"), str) or not skill["description"].strip():
63+ errors.append(f"skills[{i}]: missing 'description'")
64+ path_value = skill.get("path")
65+ if path_value is not None:
66+ if not isinstance(path_value, str):
67+ errors.append(f"skills[{i}]: 'path' must be a string")
68+ elif not os.path.isfile(os.path.join(plugin_dir, path_value)):
69+ errors.append(f"skills[{i}]: path '{path_value}' does not exist")
70+
5471if mode == "codex":
5572 for field in ("skills", "hooks", "mcpServers", "apps"):
5673 value = data.get(field)
@@ -152,23 +169,34 @@ plugin_dirs = sorted(
152169plugin_set = set(plugin_dirs)
153170
154171def load_json(relpath):
155- with open(os.path.join(repo_root, relpath)) as f:
156- return json.load(f)
172+ try:
173+ with open(os.path.join(repo_root, relpath)) as f:
174+ return json.load(f)
175+ except FileNotFoundError:
176+ errors.append(f"missing {relpath}")
177+ except json.JSONDecodeError as exc:
178+ errors.append(f"{relpath}: invalid JSON: {exc}")
179+ return None
157180
158181def require(condition, message):
159182 if not condition:
160183 errors.append(message)
161184
162185root_marketplace = load_json("marketplace.json")
186+ if root_marketplace is None:
187+ root_marketplace = {"plugins": []}
163188root_names = [plugin.get("name") for plugin in root_marketplace.get("plugins", [])]
164189require(set(root_names) == plugin_set, "marketplace.json plugin names must match plugin directories")
165190for plugin in root_marketplace.get("plugins", []):
166191 name = plugin.get("name")
167192 for field in ("name", "id", "description", "version", "type", "category"):
168- require(isinstance(plugin.get(field), str) and plugin[field].strip(), f"marketplace.json {name}: missing {field}")
193+ value = plugin.get(field)
194+ require(isinstance(value, str) and value.strip(), f"marketplace.json {name}: missing {field}")
169195 require(plugin.get("id") == f"{name}@{root_marketplace.get('name')}", f"marketplace.json {name}: id does not match marketplace name")
170196
171197claude_marketplace = load_json(".claude-plugin/marketplace.json")
198+ if claude_marketplace is None:
199+ claude_marketplace = {"plugins": []}
172200claude_names = [plugin.get("name") for plugin in claude_marketplace.get("plugins", [])]
173201require(set(claude_names).issubset(plugin_set), ".claude-plugin/marketplace.json references unknown plugin")
174202for plugin in claude_marketplace.get("plugins", []):
@@ -179,6 +207,8 @@ for plugin in claude_marketplace.get("plugins", []):
179207 require(os.path.isdir(os.path.join(repo_root, source)), f".claude-plugin/marketplace.json {name}: source path does not exist")
180208
181209codex_marketplace = load_json(".agents/plugins/marketplace.json")
210+ if codex_marketplace is None:
211+ codex_marketplace = {"plugins": []}
182212codex_names = [plugin.get("name") for plugin in codex_marketplace.get("plugins", [])]
183213require(set(codex_names) == plugin_set, ".agents/plugins/marketplace.json plugin names must match plugin directories")
184214require(isinstance(codex_marketplace.get("interface", {}).get("displayName"), str), ".agents/plugins/marketplace.json missing interface.displayName")
@@ -197,7 +227,8 @@ for plugin in codex_marketplace.get("plugins", []):
197227 if isinstance(policy, dict):
198228 require(policy.get("installation") in {"NOT_AVAILABLE", "AVAILABLE", "INSTALLED_BY_DEFAULT"}, f".agents/plugins/marketplace.json {name}: invalid policy.installation")
199229 require(policy.get("authentication") in {"ON_INSTALL", "ON_USE"}, f".agents/plugins/marketplace.json {name}: invalid policy.authentication")
200- require(isinstance(plugin.get("category"), str) and plugin["category"].strip(), f".agents/plugins/marketplace.json {name}: missing category")
230+ category = plugin.get("category")
231+ require(isinstance(category, str) and category.strip(), f".agents/plugins/marketplace.json {name}: missing category")
201232
202233if errors:
203234 for error in errors:
0 commit comments