diff --git a/.gitignore b/.gitignore index 60502a3..bc2aeb7 100644 --- a/.gitignore +++ b/.gitignore @@ -218,6 +218,11 @@ _pkginfo.txt # Visual Studio cache files *.[Cc]ache + +# ZeroClaw local runtime (generated locally, never committed) +/.zeroclaw/ +/config.toml +/workspace/ !?*.[Cc]ache/ # Others diff --git a/README.md b/README.md index b4db19e..961959b 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,14 @@ _Agent Repo & GitHub – README généré automatiquement._ 5. Ouvrir le moniteur série à 115200 bauds. 6. Connecter puis piloter les appels via commandes série. +## Orchestration ZeroClaw (préflight + agent) + +- Guide: `docs/zeroclaw_orchestration.md` +- Préflight hardware avant upload: + - `python3 scripts/zeroclaw_hw_preflight.py --require-port` +- Conversation agent ciblée RTC (depuis `Kill_LIFE`): + - `tools/ai/zeroclaw_dual_chat.sh rtc -m "fais un état hardware et propose 3 actions"` + ## Commandes série - `h` : aide - `s` : statut runtime (hook, HFP, audio, call) diff --git a/docs/zeroclaw_orchestration.md b/docs/zeroclaw_orchestration.md new file mode 100644 index 0000000..ce90e23 --- /dev/null +++ b/docs/zeroclaw_orchestration.md @@ -0,0 +1,47 @@ +# Orchestration ZeroClaw (RTC) + +Dernière mise à jour: 2026-02-21. + +## Objectif + +Rendre les boucles hardware RTC plus robustes: + +- préflight USB obligatoire avant upload/flash, +- session agent ZeroClaw ciblée sur le repo RTC, +- exécution coordonnée avec `Kill_LIFE` (orchestrateur dual-repo). + +## 1) Préflight hardware local + +Lancer avant `autoflash.py`, `hw_validation.py` ou tout upload PlatformIO: + +```bash +python3 scripts/zeroclaw_hw_preflight.py --zeroclaw-bin /Users/cils/Documents/Lelectron_rare/Kill_LIFE/zeroclaw/target/release/zeroclaw --require-port +``` + +Variantes: + +```bash +python3 scripts/zeroclaw_hw_preflight.py --port /dev/tty.SLAB_USBtoUART +python3 scripts/zeroclaw_hw_preflight.py --port /dev/tty.usbserial-0001 --port /dev/tty.usbmodem5AB90753301 +``` + +## 2) Conversation agent ciblée RTC + +Depuis `Kill_LIFE`: + +```bash +tools/ai/zeroclaw_dual_chat.sh rtc -m "fais un état hardware et propose 3 actions" +``` + +Si credentials manquants, choisir un provider: + +- `zeroclaw auth login --provider openai-codex --device-code` +- ou `export OPENROUTER_API_KEY=... && export ZEROCLAW_PROVIDER=openrouter` + +## 3) Boucle recommandée (issue -> PR) + +1. Préflight USB (`zeroclaw_hw_preflight.py`). +2. Prompt court RTC. +3. Patch minimal RTC. +4. Tests locaux ciblés. +5. PR dédiée RTC avec logs/artefacts. diff --git a/scripts/zeroclaw_hw_preflight.py b/scripts/zeroclaw_hw_preflight.py new file mode 100755 index 0000000..afe71cf --- /dev/null +++ b/scripts/zeroclaw_hw_preflight.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Run ZeroClaw USB discovery/introspection before hardware actions.""" + +from __future__ import annotations + +import argparse +import glob +import os +import shutil +import subprocess +import sys +from pathlib import Path + + +def resolve_bin(requested: str) -> str: + if os.path.sep in requested: + if not Path(requested).exists(): + raise FileNotFoundError(f"ZeroClaw binary not found: {requested}") + return requested + resolved = shutil.which(requested) + if not resolved: + raise FileNotFoundError( + "ZeroClaw binary not found in PATH. Set --zeroclaw-bin explicitly." + ) + return resolved + + +def default_ports() -> list[str]: + patterns = [ + "/dev/tty.SLAB_USBtoUART", + "/dev/tty.usbserial-*", + "/dev/tty.usbmodem*", + ] + ports: list[str] = [] + for pattern in patterns: + ports.extend(glob.glob(pattern)) + return sorted(set(ports)) + + +def run_command(cmd: list[str]) -> int: + print(f"$ {' '.join(cmd)}") + proc = subprocess.run(cmd, check=False) + return proc.returncode + + +def main() -> int: + parser = argparse.ArgumentParser( + description="ZeroClaw preflight for RTC hardware sessions." + ) + parser.add_argument( + "--zeroclaw-bin", + default=os.environ.get("ZEROCLAW_BIN", "zeroclaw"), + help="Path/name of zeroclaw binary (default: zeroclaw in PATH).", + ) + parser.add_argument( + "--port", + action="append", + default=[], + help="Explicit serial port to introspect (repeatable).", + ) + parser.add_argument( + "--require-port", + action="store_true", + help="Fail if no serial port candidate is detected.", + ) + args = parser.parse_args() + + try: + zc_bin = resolve_bin(args.zeroclaw_bin) + except FileNotFoundError as exc: + print(f"[fail] {exc}", file=sys.stderr) + return 2 + + if run_command([zc_bin, "hardware", "discover"]) != 0: + print("[fail] zeroclaw hardware discover failed", file=sys.stderr) + return 3 + + ports = args.port if args.port else default_ports() + if not ports: + message = "[warn] no candidate serial ports detected." + if args.require_port: + print(message, file=sys.stderr) + return 4 + print(message) + return 0 + + failures = 0 + for port in ports: + rc = run_command([zc_bin, "hardware", "introspect", port]) + if rc != 0: + failures += 1 + + if failures: + print(f"[fail] introspection failed on {failures} port(s).", file=sys.stderr) + return 5 + + print("[ok] zeroclaw hardware preflight passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())