fix: script.py guillemets, cron bad minute, system_prompt scripts bash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-23 19:11:23 +00:00
parent d3ca3eee68
commit ba59e3b4c5
4 changed files with 54 additions and 15 deletions
+20 -7
View File
@@ -22,17 +22,30 @@ Tu reçois des instructions via MQTT (depuis Nexus) ou XMPP (directement).
1. Utilise toujours le skill le plus spécifique disponible
2. Préfère plusieurs appels de skills atomiques plutôt qu'une commande shell complexe
3. Après chaque action importante (install, restart, delete), vérifie le résultat
4. Si une tâche génère un script, utilise SKILL:script pour le créer et l'exécuter,
et le résultat sera automatiquement renvoyé via MQTT
4. **OBLIGATOIRE : tout script bash doit être créé via `SKILL:script ARGS:save <nom> | <contenu>` et jamais via `filesystem write`.** N'utilise JAMAIS filesystem pour écrire un fichier .sh.
5. En cas d'erreur, diagnostique avant de réessayer
6. Réponds toujours en français
7. Sois concis dans tes réponses — l'essentiel, pas tout le stdout brut
## Communication MQTT
## Écriture de scripts bash — règles strictes
### ❌ Interdit dans les scripts bash
- `muc_send`, `mqtt_send`, `shell` et tous les noms de skills — ce ne sont PAS des commandes bash
- Les guillemets échappés : écris `"texte"` et non `\"texte\"`
### ✅ Pour envoyer un message depuis un script
Variables injectées automatiquement : `$MQTT_BROKER`, `$MQTT_REPLY_TOPIC`, `$AGENT_ID`
```bash
mosquitto_pub -h "$MQTT_BROKER" -t "$MQTT_REPLY_TOPIC" -m "mon résultat"
```
### ✅ Bonnes pratiques
- Commence toujours par `#!/bin/bash` et `set -euo pipefail`
- Guillemets doubles autour des variables : `"$VAR"`
- Gère les cas d'erreur avec des messages explicites
## Communication MQTT (depuis le LLM, pas depuis un script)
Tu peux envoyer des messages à d'autres agents :
SKILL:mqtt_send ARGS:agents/nexus/inbox | {"type":"result","payload":"mon résultat"}
Pour les scripts qui doivent retourner un résultat :
Les variables $MQTT_BROKER et $MQTT_REPLY_TOPIC sont disponibles dans l'environnement.
mosquitto_pub -h $MQTT_BROKER -t $MQTT_REPLY_TOPIC -m "résultat"
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Récupération des mises à jour en filtrant l'entête \"Listing...\"
updates=$(apt list --upgradable 2>/dev/null | grep -v \"^Listing\")
if [ -z \"$updates\" ]; then
payload='{\"type\":\"updates\",\"payload\":\"Aucune mise à jour disponible\"}'
else
payload=$(jq -nc --arg p \"$updates\" '{\"type\":\"updates\",\"payload\":$p}')
fi
mosquitto_pub -h \"${MQTT_BROKER}\" -t \"${MQTT_REPLY_TOPIC}\" -m \"${payload}\"
+21 -7
View File
@@ -35,6 +35,16 @@ def _run(cmd: str, timeout: int = 10) -> str:
return str(e)
def _get_current_crontab() -> str:
"""Retourne le crontab actuel, ou chaîne vide si inexistant."""
result = subprocess.run(
"crontab -l", shell=True, text=True, capture_output=True
)
if result.returncode != 0:
return ""
return result.stdout.strip()
def run(args: str, context) -> str:
parts = args.strip().split(None, 1)
action = parts[0].lower() if parts else "list"
@@ -54,26 +64,28 @@ def run(args: str, context) -> str:
command = " ".join(words[5:])
entry = f"{cron_expr} {command}"
current = _run("crontab -l 2>/dev/null")
current = _get_current_crontab()
if entry in current:
return f"Cette entrée existe déjà : {entry}"
def _do_add():
with tempfile.NamedTemporaryFile(mode="w", suffix=".cron", delete=False) as f:
if current and "no crontab" not in current.lower():
if current:
f.write(current + "\n")
f.write(entry + "\n")
tmpfile = f.name
out = _run(f"crontab {tmpfile}")
result = subprocess.run(f"crontab {tmpfile}", shell=True, text=True, capture_output=True)
os.unlink(tmpfile)
return f"Entrée ajoutée : {entry}\n{out}"
if result.returncode != 0:
return f"❌ Erreur crontab : {(result.stdout + result.stderr).strip()}"
return f"✅ Entrée ajoutée : {entry}"
return _confirm_or_execute(context, f"Ajouter cron : {entry}", _do_add)
if action == "remove":
if not rest:
return "Précise le pattern à supprimer."
current = _run("crontab -l 2>/dev/null")
current = _get_current_crontab()
lines = [l for l in current.splitlines() if rest not in l]
removed_count = len(current.splitlines()) - len(lines)
if removed_count == 0:
@@ -84,9 +96,11 @@ def run(args: str, context) -> str:
with tempfile.NamedTemporaryFile(mode="w", suffix=".cron", delete=False) as f:
f.write(new_cron + "\n")
tmpfile = f.name
out = _run(f"crontab {tmpfile}")
result = subprocess.run(f"crontab {tmpfile}", shell=True, text=True, capture_output=True)
os.unlink(tmpfile)
return f"{removed_count} entrée(s) supprimée(s) contenant '{rest}'.\n{out}"
if result.returncode != 0:
return f"❌ Erreur crontab : {(result.stdout + result.stderr).strip()}"
return f"{removed_count} entrée(s) supprimée(s) contenant '{rest}'."
return _confirm_or_execute(context, f"Supprimer {removed_count} cron contenant '{rest}'", _do_remove)
+1 -1
View File
@@ -147,7 +147,7 @@ def run(args: str, context) -> str:
return "Format : save <nom> | <contenu du script>"
name_raw, content = rest.split("|", 1)
name = _safe_name(name_raw)
content = content.strip().replace("\\n", "\n")
content = content.strip().replace("\\n", "\n").replace('\\"', '"').replace("\\'", "'")
if not name:
return "Nom de script invalide."