package com.claudecode.plugin;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Map;
/**
* 插件清单 —— 对应 claude-code 中 manifest.json 的 Java 映射。
*
* 每个可安装的插件必须包含一个 manifest.json,声明其元数据和能力。
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public record PluginManifest(
/** 插件唯一 ID (kebab-case) */
String id,
/** 显示名称 */
String name,
/** 版本号 (semver) */
String version,
/** 功能描述 */
String description,
/** 作者 */
String author,
/** 主页 URL */
String homepage,
/** 许可证 */
String license,
/** 最低 claude-code-java 版本 */
String minAppVersion,
/** 入口类(JAR 中实现 Plugin 接口的类) */
String mainClass,
/** 声明的工具 */
List tools,
/** 声明的命令 */
List commands,
/** 声明的 Hook */
List hooks,
/** 安装作用域支持 */
List scopes,
/** 依赖的其他插件 ID */
List dependencies,
/** 额外元数据 */
Map metadata
) {
private static final ObjectMapper MAPPER = new ObjectMapper();
/** 从 JSON 字符串解析 */
public static PluginManifest fromJson(String json) throws Exception {
return MAPPER.readValue(json, PluginManifest.class);
}
/** 从 JSON bytes 解析 */
public static PluginManifest fromJson(byte[] json) throws Exception {
return MAPPER.readValue(json, PluginManifest.class);
}
/** 序列化为 JSON */
public String toJson() throws Exception {
return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(this);
}
/** 校验必填字段 */
public List validate() {
var errors = new java.util.ArrayList();
if (id == null || id.isBlank()) errors.add("id is required");
if (name == null || name.isBlank()) errors.add("name is required");
if (version == null || version.isBlank()) errors.add("version is required");
if (mainClass == null || mainClass.isBlank()) errors.add("mainClass is required");
if (id != null && !id.matches("^[a-z][a-z0-9-]*$")) {
errors.add("id must be kebab-case: " + id);
}
if (version != null && !version.matches("^\\d+\\.\\d+\\.\\d+.*$")) {
errors.add("version must be semver: " + version);
}
return errors;
}
/** 是否支持指定作用域 */
public boolean supportsScope(String scope) {
return scopes == null || scopes.isEmpty() || scopes.contains(scope);
}
// ---- 子记录 ----
@JsonIgnoreProperties(ignoreUnknown = true)
public record DeclaredTool(String name, String description, boolean readOnly) {}
@JsonIgnoreProperties(ignoreUnknown = true)
public record DeclaredCommand(String name, String description) {}
@JsonIgnoreProperties(ignoreUnknown = true)
public record DeclaredHook(String event, String handler) {}
/**
* 市场目录条目 —— 市场 API 返回的精简信息。
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public record MarketplaceEntry(
String id,
String name,
String version,
String description,
String author,
String downloadUrl,
long downloads,
double rating,
String updatedAt,
List tags
) {
public static List fromJsonArray(String json) throws Exception {
return MAPPER.readValue(json, new TypeReference<>() {});
}
}
}