mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-12 12:06:29 +03:00
* feat: add portable ESP32 development skill Add source-backed workflows, safe templates, native CLI routing, and a read-only preflight for ESP32 hardware and firmware work.\n\nAI assistance: research, drafting, implementation, and review used OpenAI Codex and delegated DeepSeek agents under human direction. * docs: harden ESP32 family and security guidance Add source-backed family traps, brownout, calibration, USB recovery, and security-mode boundaries found during independent review.\n\nAI assistance: independent audits and drafting used delegated DeepSeek agents and OpenAI Codex under human direction.
111 lines
3.1 KiB
Python
Executable File
111 lines
3.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Report ESP32-related host tools and candidate serial ports without opening them."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import glob
|
|
import json
|
|
import platform
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
TOOLS = (
|
|
"idf.py",
|
|
"esptool",
|
|
"esptool.py",
|
|
"arduino-cli",
|
|
"pio",
|
|
"mpremote",
|
|
"circup",
|
|
"esphome",
|
|
"west",
|
|
"espflash",
|
|
"cargo-espflash",
|
|
)
|
|
PORT_PATTERNS = {
|
|
"Darwin": ("/dev/cu.usb*", "/dev/cu.SLAB*", "/dev/cu.wchusb*"),
|
|
"Linux": ("/dev/ttyUSB*", "/dev/ttyACM*", "/dev/serial/by-id/*"),
|
|
}
|
|
|
|
|
|
def candidate_ports(system: str) -> list[str]:
|
|
ports = {
|
|
str(Path(path))
|
|
for pattern in PORT_PATTERNS.get(system, ())
|
|
for path in glob.glob(pattern)
|
|
}
|
|
return sorted(ports)
|
|
|
|
|
|
def report(system: str | None = None) -> dict[str, object]:
|
|
system = system or platform.system()
|
|
port_enumeration = (
|
|
"filesystem patterns"
|
|
if system in PORT_PATTERNS
|
|
else "not available without a native device-list tool on this platform"
|
|
)
|
|
return {
|
|
"host": {"system": system, "machine": platform.machine()},
|
|
"tools": {name: shutil.which(name) for name in TOOLS},
|
|
"candidate_ports": candidate_ports(system),
|
|
"port_enumeration": port_enumeration,
|
|
"opened_ports": False,
|
|
"next": "Identify the exact board before opening or flashing a port.",
|
|
}
|
|
|
|
|
|
def self_test() -> None:
|
|
assert "/dev/cu.Bluetooth-Incoming-Port" not in candidate_ports("Darwin")
|
|
assert candidate_ports("Windows") == []
|
|
windows = report("Windows")
|
|
assert str(windows["port_enumeration"]).startswith("not available")
|
|
data = report("TestOS")
|
|
assert data["opened_ports"] is False
|
|
tools = data["tools"]
|
|
assert isinstance(tools, dict)
|
|
assert set(tools) == set(TOOLS)
|
|
print("self-test: PASS")
|
|
|
|
|
|
def print_text(data: dict[str, object]) -> None:
|
|
host = data["host"]
|
|
assert isinstance(host, dict)
|
|
print(f"host: {host['system']} {host['machine']}")
|
|
print("tools:")
|
|
tools = data["tools"]
|
|
assert isinstance(tools, dict)
|
|
for name, path in tools.items():
|
|
print(f" {name}: {path or 'missing'}")
|
|
print(f"port enumeration: {data['port_enumeration']}")
|
|
print("candidate ports:")
|
|
ports = data["candidate_ports"]
|
|
assert isinstance(ports, list)
|
|
if ports:
|
|
for port in ports:
|
|
print(f" {port}")
|
|
else:
|
|
print(" none found (use the framework's native device-list command)")
|
|
print(f"next: {data['next']}")
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(
|
|
description="List ESP32-related tools and candidate ports without opening serial devices."
|
|
)
|
|
parser.add_argument("--json", action="store_true", help="emit JSON")
|
|
parser.add_argument("--self-test", action="store_true", help="run deterministic checks")
|
|
args = parser.parse_args()
|
|
if args.self_test:
|
|
self_test()
|
|
return
|
|
data = report()
|
|
if args.json:
|
|
print(json.dumps(data, indent=2, sort_keys=True))
|
|
else:
|
|
print_text(data)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|