473 lines
12 KiB
JSON
473 lines
12 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://hugin.intern/schema/hugin-bundle-1.json",
|
|
"title": "hugin-bundle.json",
|
|
"description": "Installationsanleitung in einem Hugin-Bundle (ZIP). Beschreibt den vollständigen Soll-Zustand der angefragten Pakete im Repo. Clients wenden die Operationen der Reihe nach an.",
|
|
"type": "object",
|
|
"required": [
|
|
"format",
|
|
"generated",
|
|
"tools",
|
|
"packages",
|
|
"operations",
|
|
"hints"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"format": {
|
|
"const": 1
|
|
},
|
|
"generated": {
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
"tools": {
|
|
"description": "Für welche Werkzeuge übersetzt wurde, mit Hauptversion.",
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": {
|
|
"enum": [
|
|
"claude",
|
|
"opencode@1",
|
|
"opencode@2"
|
|
]
|
|
},
|
|
"uniqueItems": true
|
|
},
|
|
"packages": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/$defs/package"
|
|
}
|
|
},
|
|
"operations": {
|
|
"type": "array",
|
|
"items": {
|
|
"oneOf": [
|
|
{
|
|
"$ref": "#/$defs/op_file"
|
|
},
|
|
{
|
|
"$ref": "#/$defs/op_block"
|
|
},
|
|
{
|
|
"$ref": "#/$defs/op_json"
|
|
},
|
|
{
|
|
"$ref": "#/$defs/op_remove"
|
|
},
|
|
{
|
|
"$ref": "#/$defs/op_json_remove"
|
|
},
|
|
{
|
|
"$ref": "#/$defs/op_install"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"hints": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/$defs/hint"
|
|
}
|
|
}
|
|
},
|
|
"$defs": {
|
|
"package_id": {
|
|
"type": "string",
|
|
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
|
|
"minLength": 1,
|
|
"maxLength": 64
|
|
},
|
|
"checksum": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
},
|
|
"repo_path": {
|
|
"description": "Pfad relativ zur Repo-Wurzel, mit /, ohne führenden /. Segmente '.' und '..' sind verboten; Clients prüfen das zusätzlich selbst (das Muster kommt ohne Lookahead aus, damit es auch mit RE2 funktioniert).",
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"pattern": "^[^/\\\\:*?\"<>|][^\\\\:*?\"<>|]*$"
|
|
},
|
|
"zip_path": {
|
|
"description": "Pfad einer Datei im ZIP, immer unter files/. Segmente '..' sind verboten.",
|
|
"type": "string",
|
|
"pattern": "^files/[^\\\\]+$"
|
|
},
|
|
"json_path": {
|
|
"description": "Schlüsselpfad in einer JSON-Datei, als Liste von Schlüsseln (Punkte in Namen sind erlaubt).",
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"package": {
|
|
"type": "object",
|
|
"required": [
|
|
"id",
|
|
"version",
|
|
"checksum",
|
|
"kind",
|
|
"risk",
|
|
"compatibility",
|
|
"mandatory"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"id": {
|
|
"$ref": "#/$defs/package_id"
|
|
},
|
|
"version": {
|
|
"type": "string",
|
|
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
},
|
|
"checksum": {
|
|
"$ref": "#/$defs/checksum"
|
|
},
|
|
"kind": {
|
|
"enum": [
|
|
"skill",
|
|
"rules",
|
|
"command",
|
|
"subagent",
|
|
"mcp",
|
|
"plugin",
|
|
"hook"
|
|
]
|
|
},
|
|
"risk": {
|
|
"enum": [
|
|
"text",
|
|
"tools",
|
|
"scripts",
|
|
"permissions"
|
|
]
|
|
},
|
|
"compatibility": {
|
|
"enum": [
|
|
"portable",
|
|
"translated",
|
|
"claude-only",
|
|
"opencode-only"
|
|
]
|
|
},
|
|
"mandatory": {
|
|
"type": "boolean"
|
|
}
|
|
}
|
|
},
|
|
"op_file": {
|
|
"description": "Datei anlegen oder ersetzen. Inhalt liegt im ZIP unter quelle.",
|
|
"type": "object",
|
|
"required": [
|
|
"op",
|
|
"package",
|
|
"path",
|
|
"source",
|
|
"sha256"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"op": {
|
|
"const": "file"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
},
|
|
"path": {
|
|
"$ref": "#/$defs/repo_path"
|
|
},
|
|
"source": {
|
|
"$ref": "#/$defs/zip_path"
|
|
},
|
|
"sha256": {
|
|
"$ref": "#/$defs/checksum"
|
|
}
|
|
}
|
|
},
|
|
"op_block": {
|
|
"description": "Verwalteten Block in einer Textdatei setzen. Der Block steht zwischen den Zeilen '<!-- {block}:anfang' … und '<!-- {block}:ende -->'. Leerer inhalt entfernt den Block. Fehlt die Datei, wird sie aus anlegen erzeugt (falls gesetzt), sonst nur mit dem Block.",
|
|
"type": "object",
|
|
"required": [
|
|
"op",
|
|
"file",
|
|
"block",
|
|
"content"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"op": {
|
|
"const": "block"
|
|
},
|
|
"file": {
|
|
"$ref": "#/$defs/repo_path"
|
|
},
|
|
"block": {
|
|
"type": "string",
|
|
"pattern": "^[a-z][a-z0-9-]*$"
|
|
},
|
|
"content": {
|
|
"type": "string"
|
|
},
|
|
"create": {
|
|
"$ref": "#/$defs/zip_path"
|
|
}
|
|
}
|
|
},
|
|
"op_json": {
|
|
"description": "Schlüssel in einer JSON- oder JSONC-Datei setzen. modus 'setzen' ersetzt den Wert; 'listeneintrag' stellt sicher, dass wert in der Liste unter schluessel vorkommt. Andere Inhalte, Reihenfolge und Kommentare bleiben erhalten. Fehlt die Datei, wird sie aus anlegen erzeugt (falls gesetzt), sonst als leeres Objekt.",
|
|
"type": "object",
|
|
"required": [
|
|
"op",
|
|
"file",
|
|
"key",
|
|
"value"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"op": {
|
|
"const": "json"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id",
|
|
"description": "Fehlt bei gemeinsamen Einträgen, die Hugin für mehrere Pakete pflegt (z. B. instructions in opencode.json)."
|
|
},
|
|
"file": {
|
|
"$ref": "#/$defs/repo_path"
|
|
},
|
|
"key": {
|
|
"$ref": "#/$defs/json_path"
|
|
},
|
|
"mode": {
|
|
"enum": [
|
|
"set",
|
|
"list_item"
|
|
],
|
|
"default": "setzen"
|
|
},
|
|
"value": {},
|
|
"create": {
|
|
"$ref": "#/$defs/zip_path"
|
|
}
|
|
}
|
|
},
|
|
"op_remove": {
|
|
"description": "Datei oder Ordner entfernen, den eine frühere Version angelegt hat. Fehlt das Ziel, ist nichts zu tun.",
|
|
"type": "object",
|
|
"required": [
|
|
"op",
|
|
"package",
|
|
"path"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"op": {
|
|
"const": "remove"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
},
|
|
"path": {
|
|
"$ref": "#/$defs/repo_path"
|
|
}
|
|
}
|
|
},
|
|
"op_json_remove": {
|
|
"description": "Schlüssel entfernen ('setzen') bzw. Eintrag aus der Liste entfernen ('listeneintrag').",
|
|
"type": "object",
|
|
"required": [
|
|
"op",
|
|
"file",
|
|
"key"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"op": {
|
|
"const": "json_remove"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id",
|
|
"description": "Fehlt bei gemeinsamen Einträgen, die Hugin für mehrere Pakete pflegt (z. B. instructions in opencode.json)."
|
|
},
|
|
"file": {
|
|
"$ref": "#/$defs/repo_path"
|
|
},
|
|
"key": {
|
|
"$ref": "#/$defs/json_path"
|
|
},
|
|
"mode": {
|
|
"enum": [
|
|
"set",
|
|
"list_item"
|
|
],
|
|
"default": "setzen"
|
|
},
|
|
"value": {}
|
|
}
|
|
},
|
|
"hint": {
|
|
"type": "object",
|
|
"required": [
|
|
"kind"
|
|
],
|
|
"oneOf": [
|
|
{
|
|
"properties": {
|
|
"kind": {
|
|
"const": "env_variable"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"pattern": "^[A-Z_][A-Z0-9_]*$"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
}
|
|
},
|
|
"required": [
|
|
"kind",
|
|
"name",
|
|
"package"
|
|
],
|
|
"additionalProperties": false
|
|
},
|
|
{
|
|
"properties": {
|
|
"kind": {
|
|
"const": "min_version"
|
|
},
|
|
"tool": {
|
|
"enum": [
|
|
"claude",
|
|
"opencode"
|
|
]
|
|
},
|
|
"version": {
|
|
"type": "string"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
}
|
|
},
|
|
"required": [
|
|
"kind",
|
|
"tool",
|
|
"version"
|
|
],
|
|
"additionalProperties": false
|
|
},
|
|
{
|
|
"properties": {
|
|
"kind": {
|
|
"const": "deprecated"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
},
|
|
"successor": {
|
|
"$ref": "#/$defs/package_id"
|
|
}
|
|
},
|
|
"required": [
|
|
"kind",
|
|
"package"
|
|
],
|
|
"additionalProperties": false
|
|
},
|
|
{
|
|
"properties": {
|
|
"kind": {
|
|
"const": "major_available"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
},
|
|
"version": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"kind",
|
|
"package",
|
|
"version"
|
|
],
|
|
"additionalProperties": false
|
|
}
|
|
]
|
|
},
|
|
"local_path": {
|
|
"description": "Pfad einer Datei im ZIP für lokale Server, immer unter local/<package>/<version>/. Segmente '..' sind verboten.",
|
|
"type": "string",
|
|
"pattern": "^local/[^\\\\]+$"
|
|
},
|
|
"op_install": {
|
|
"description": "Lokalen MCP-Server außerhalb des Repos installieren (→ KONZEPT.md 20, Stufe 5b). Ablage: <HUGIN_MCP_HOME oder ~/.local/share/mcp>/<package>/<version>/. Gestartet wird mit hugin mcp run <package>; Clients ohne diesen Befehl überspringen die Operation mit einer Warnung.",
|
|
"type": "object",
|
|
"required": [
|
|
"op",
|
|
"package",
|
|
"version",
|
|
"checksum",
|
|
"runtime",
|
|
"entry",
|
|
"files"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"op": {
|
|
"const": "install"
|
|
},
|
|
"package": {
|
|
"$ref": "#/$defs/package_id"
|
|
},
|
|
"version": {
|
|
"type": "string",
|
|
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
},
|
|
"checksum": {
|
|
"$ref": "#/$defs/checksum"
|
|
},
|
|
"runtime": {
|
|
"enum": [
|
|
"python"
|
|
]
|
|
},
|
|
"entry": {
|
|
"description": "python: Modul (python -m) oder Skript im Paket (.py)",
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"files": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": [
|
|
"path",
|
|
"source",
|
|
"sha256"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"path": {
|
|
"$ref": "#/$defs/repo_path"
|
|
},
|
|
"source": {
|
|
"$ref": "#/$defs/local_path"
|
|
},
|
|
"sha256": {
|
|
"$ref": "#/$defs/checksum"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"python": {
|
|
"description": "kleinste nötige Python-Version, z. B. 3.10",
|
|
"type": "string",
|
|
"pattern": "^3\\.\\d{1,2}$"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|