Initial import of watchdog-discord-route skill
This commit is contained in:
143
scripts/owner_report_producer.py
Normal file
143
scripts/owner_report_producer.py
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Minimal owner-report producer for ClawTeam-style worker checkpoints.
|
||||
|
||||
Writes ~/.clawteam/owner-reports/pending/<report_id>.md using explicit checkpoint
|
||||
fields and a human-readable message suitable for direct Telegram delivery.
|
||||
|
||||
This intentionally stays tiny:
|
||||
- no daemon
|
||||
- no event bus
|
||||
- no parser for arbitrary logs
|
||||
- just explicit fields in -> pending markdown out
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from owner_report_consumer import OWNER_REPORT_ROOT
|
||||
|
||||
PENDING_DIR = OWNER_REPORT_ROOT / "pending"
|
||||
|
||||
|
||||
def _slug(value: str) -> str:
|
||||
slug = re.sub(r"[^a-zA-Z0-9._-]+", "-", value.strip()).strip("-._")
|
||||
return slug or "report"
|
||||
|
||||
|
||||
def _now_iso() -> str:
|
||||
return datetime.now().astimezone().isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def build_message(*, team: str, worker: str, task_id: str, progress: str, done: str, next_step: str, status: str, source: str | None, report_kind: str) -> str:
|
||||
headline = f"🔔 [{team}] {worker}"
|
||||
if report_kind == "leader-final":
|
||||
headline = f"✅ [{team}] final"
|
||||
|
||||
lines = [
|
||||
headline,
|
||||
done,
|
||||
]
|
||||
|
||||
if next_step.strip():
|
||||
lines.append(f"→ {next_step}")
|
||||
|
||||
tech = [
|
||||
f"task={task_id}",
|
||||
f"status={status}",
|
||||
f"progress={progress}",
|
||||
]
|
||||
if source:
|
||||
tech.append(f"source={source}")
|
||||
lines.append(" | ".join(tech))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_report_body(*, report_id: str, team: str, worker: str, task_id: str, progress: str, done: str, next_step: str, status: str, source: str | None, created_at: str, message: str, report_kind: str) -> str:
|
||||
fields: list[tuple[str, str | None]] = [
|
||||
("report_id", report_id),
|
||||
("team", team),
|
||||
("worker", worker),
|
||||
("task_id", task_id),
|
||||
("progress", progress),
|
||||
("done", done),
|
||||
("next", next_step),
|
||||
("status", status),
|
||||
("report_kind", report_kind),
|
||||
("source", source),
|
||||
("created_at", created_at),
|
||||
("message", json.dumps(message, ensure_ascii=False)),
|
||||
]
|
||||
return "\n".join(f"{k}: {v}" for k, v in fields if v is not None) + "\n"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Create one pending owner report from explicit checkpoint fields")
|
||||
ap.add_argument("--team", required=True)
|
||||
ap.add_argument("--worker", required=True)
|
||||
ap.add_argument("--task-id", required=True)
|
||||
ap.add_argument("--progress", required=True)
|
||||
ap.add_argument("--done", required=True)
|
||||
ap.add_argument("--next", dest="next_step", required=True)
|
||||
ap.add_argument("--status", required=True)
|
||||
ap.add_argument("--source")
|
||||
ap.add_argument("--report-kind", choices=["checkpoint", "leader-final"], default="checkpoint")
|
||||
ap.add_argument("--report-id", help="Optional explicit report_id / filename stem")
|
||||
ap.add_argument("--created-at", default=_now_iso())
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
args = ap.parse_args()
|
||||
|
||||
report_id = args.report_id or f"{_slug(args.team)}-{_slug(args.worker)}-{_slug(args.task_id)}-{_slug(args.report_kind)}"
|
||||
message = build_message(
|
||||
team=args.team,
|
||||
worker=args.worker,
|
||||
task_id=args.task_id,
|
||||
progress=args.progress,
|
||||
done=args.done,
|
||||
next_step=args.next_step,
|
||||
status=args.status,
|
||||
source=args.source,
|
||||
report_kind=args.report_kind,
|
||||
)
|
||||
body = build_report_body(
|
||||
report_id=report_id,
|
||||
team=args.team,
|
||||
worker=args.worker,
|
||||
task_id=args.task_id,
|
||||
progress=args.progress,
|
||||
done=args.done,
|
||||
next_step=args.next_step,
|
||||
status=args.status,
|
||||
source=args.source,
|
||||
created_at=args.created_at,
|
||||
message=message,
|
||||
report_kind=args.report_kind,
|
||||
)
|
||||
|
||||
path = PENDING_DIR / f"{report_id}.md"
|
||||
|
||||
result = {
|
||||
"ok": True,
|
||||
"report_id": report_id,
|
||||
"path": str(path),
|
||||
"message": message,
|
||||
"dry_run": args.dry_run,
|
||||
}
|
||||
|
||||
if args.dry_run:
|
||||
result["body"] = body
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
PENDING_DIR.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(body, encoding="utf-8")
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user