#!/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)