48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
#!/usr/bin/env python3
|
|
"""echo_output.py — M4: machine-readable output + dry-run plumbing. [v1.0 SCAFFOLD — implemented]
|
|
|
|
The high-level ops (capture/recall/resolve/scope) print prose, forcing the agent to
|
|
re-parse free text to act on a result. This provides a single JSON envelope and a
|
|
small dry-run helper so every op can emit a structured result and preview writes.
|
|
|
|
INTEGRATION (merge step): thread a global `--json` / `--dry-run` flag through echo.py's
|
|
parser and have each cmd_* return its data dict; emit() it. In --dry-run, the write
|
|
verbs print the envelope with action="dry-run" and perform no network mutation.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
|
|
|
|
def envelope(action: str, data: dict | None = None, ok: bool = True,
|
|
error: str | None = None) -> dict:
|
|
"""Canonical result shape for every high-level op."""
|
|
env = {"ok": ok, "action": action}
|
|
if data:
|
|
env.update(data)
|
|
if error:
|
|
env["error"] = error
|
|
return env
|
|
|
|
|
|
def emit(env: dict, as_json: bool) -> None:
|
|
"""Print either the JSON envelope (machine mode) or a one-line human summary."""
|
|
if as_json:
|
|
print(json.dumps(env, ensure_ascii=False))
|
|
return
|
|
if not env.get("ok"):
|
|
print(f"error: {env.get('error', 'failed')}", file=sys.stderr)
|
|
return
|
|
bits = [env.get("action", "ok")]
|
|
if env.get("path"):
|
|
bits.append(str(env["path"]))
|
|
if env.get("links_added"):
|
|
bits.append(f"+{env['links_added']} links")
|
|
print("ok: " + " ".join(bits))
|
|
|
|
|
|
def dry_run_envelope(action: str, data: dict | None = None) -> dict:
|
|
"""Result for a previewed-but-not-executed write."""
|
|
return envelope(f"dry-run:{action}", data, ok=True)
|