fix(skills): skill_get-Tool nachgereicht — kein Blind-Rewrite mehr
Skandal-Ursache: die skill_update-Anleitung sagt "erst skill_get lesen, dann updaten" — aber skill_get existierte GAR NICHT als Tool. ARIA/Claude konnte einen Skill also nie LESEN vor dem Aendern → Blind-Rewrite aus Beschreibung + Gedaechtnis. (Claude sagte korrekt "skill_get steht mir nicht zur Verfuegung" und hat den Spotify-Skill trotzdem komplett neu geschrieben.) Neu: skill_get(name) liefert Manifest (args/fast_patterns/speak/converse) + kompletten entry_code (echter Python) + README. skills.read_skill_source(). So sieht ARIA den Ist-Code und aendert gezielt statt blind zu ueberschreiben. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -284,6 +284,27 @@ META_TOOLS = [
|
|||||||
"parameters": {"type": "object", "properties": {}},
|
"parameters": {"type": "object", "properties": {}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "skill_get",
|
||||||
|
"description": (
|
||||||
|
"Liest einen EXISTIERENDEN Skill VOLLSTAENDIG: Manifest (inkl. "
|
||||||
|
"args, fast_patterns, speak/converse) + kompletter entry_code "
|
||||||
|
"(der echte Python-Code) + README. IMMER vor `skill_update` "
|
||||||
|
"aufrufen, damit du den bestehenden Code SIEHST und ihn gezielt "
|
||||||
|
"aenderst statt blind zu ueberschreiben (Blind-Rewrite killt "
|
||||||
|
"leicht funktionierende Teile!)."
|
||||||
|
),
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string", "description": "Skill-Name."},
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
@@ -1832,6 +1853,29 @@ class Agent:
|
|||||||
f"- {s['name']} ({s['execution']}) {'aktiv' if s.get('active', True) else 'DEAKTIVIERT'}: {s.get('description', '')}"
|
f"- {s['name']} ({s['execution']}) {'aktiv' if s.get('active', True) else 'DEAKTIVIERT'}: {s.get('description', '')}"
|
||||||
for s in items
|
for s in items
|
||||||
)
|
)
|
||||||
|
if name == "skill_get":
|
||||||
|
sk_name = (arguments.get("name") or "").strip()
|
||||||
|
if not sk_name:
|
||||||
|
return "FEHLER: name ist Pflicht."
|
||||||
|
src = skills_mod.read_skill_source(sk_name)
|
||||||
|
if src is None:
|
||||||
|
return f"FEHLER: Skill '{sk_name}' nicht gefunden."
|
||||||
|
m = src["manifest"]
|
||||||
|
# Manifest kompakt + kompletter Code, damit ARIA gezielt aendern kann.
|
||||||
|
meta = {
|
||||||
|
"name": m.get("name"), "description": m.get("description"),
|
||||||
|
"execution": m.get("execution"), "entry": m.get("entry"),
|
||||||
|
"active": m.get("active"), "args": m.get("args"),
|
||||||
|
"requires": m.get("requires"),
|
||||||
|
"config_schema": m.get("config_schema"),
|
||||||
|
"fast_patterns": m.get("fast_patterns"),
|
||||||
|
"speak": m.get("speak"), "converse": m.get("converse"),
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
f"MANIFEST:\n{json.dumps(meta, ensure_ascii=False, indent=2)}\n\n"
|
||||||
|
f"ENTRY-CODE ({src['entry']}):\n{src['entry_code']}\n\n"
|
||||||
|
f"README:\n{src.get('readme') or '(leer)'}"
|
||||||
|
)
|
||||||
if name == "skill_update":
|
if name == "skill_update":
|
||||||
skill_name = (arguments.get("name") or "").strip()
|
skill_name = (arguments.get("name") or "").strip()
|
||||||
if not skill_name:
|
if not skill_name:
|
||||||
|
|||||||
@@ -139,6 +139,26 @@ def read_manifest(name: str) -> Optional[dict]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def read_skill_source(name: str) -> Optional[dict]:
|
||||||
|
"""Manifest + kompletter entry_code + README eines Skills. Damit ARIA einen
|
||||||
|
Skill LESEN kann bevor sie ihn per skill_update aendert (sonst Blind-Rewrite,
|
||||||
|
der bestehende Funktionen killt)."""
|
||||||
|
m = read_manifest(name)
|
||||||
|
if m is None:
|
||||||
|
return None
|
||||||
|
d = _skill_dir(name)
|
||||||
|
entry = m.get("entry", "run.sh")
|
||||||
|
try:
|
||||||
|
code = (d / entry).read_text(encoding="utf-8")
|
||||||
|
except Exception as exc:
|
||||||
|
code = f"(entry-Datei '{entry}' nicht lesbar: {exc})"
|
||||||
|
try:
|
||||||
|
readme = (d / "README.md").read_text(encoding="utf-8")
|
||||||
|
except Exception:
|
||||||
|
readme = ""
|
||||||
|
return {"manifest": m, "entry": entry, "entry_code": code, "readme": readme}
|
||||||
|
|
||||||
|
|
||||||
def write_manifest(name: str, manifest: dict) -> None:
|
def write_manifest(name: str, manifest: dict) -> None:
|
||||||
d = _skill_dir(name)
|
d = _skill_dir(name)
|
||||||
d.mkdir(parents=True, exist_ok=True)
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user