feat: add interactive recipient composer
Some checks failed
tamq-ci / test (push) Failing after 6s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a03397-4d51-7fd1-8ff2-946eb22ea2bc
This commit is contained in:
tegwick 2026-08-24 23:24:18 +02:00
parent d7d92502b4
commit 8186550c9a
10 changed files with 379 additions and 33 deletions

View file

@ -24,6 +24,7 @@ from .tmux import TmuxError, TmuxManager
from .broker import BrokerIdentity, InputBroker
from .ptytap import PtyTap
from .control import ControlModeClient
from .composer import ComposerError, compose_message, recipient_order
from .diagnostics import configure
from .policy import load_profile
from .registry import RegistryError, validate_targets
@ -162,7 +163,8 @@ def build_parser() -> argparse.ArgumentParser:
manual messaging from a managed shell:
@TARGET: MESSAGE...
@ MESSAGE... reply to the latest sender for this window
@ compose with a visible, Tab-selectable recipient
@ MESSAGE... fast reply (ordinary shell quoting applies)
tamq inbox [--filter COMMAND]
""",
)
@ -208,7 +210,7 @@ manual messaging from a managed shell:
send.add_argument("--endpoint-id")
send.add_argument("--from", dest="sender_repo", help="sender repository (default: TAMQ_REPO or local)")
reply = subparsers.add_parser("reply", help="reply to the latest sender for the current repository")
reply.add_argument("body", nargs="+", help="message body")
reply.add_argument("body", nargs="*", help="message body; omit to open the interactive composer")
export = subparsers.add_parser("export", help="export history as JSONL")
export.add_argument("--output", required=True)
export.add_argument("--repo", dest="target_repo")
@ -383,11 +385,33 @@ def main(argv: list[str] | None = None) -> int:
if not sender:
print("tamq reply requires a managed window with TAMQ_REPO", file=sys.stderr)
return 2
body = " ".join(args.body).strip()
target = store.latest_counterparty(sender)
if target is None:
print(f"tamq: no counterparty has sent a message to {sender}", file=sys.stderr)
return 2
latest = store.latest_counterparty(sender)
if args.body:
body = " ".join(args.body).strip()
target = latest
if target is None:
print(f"tamq: no counterparty has sent a message to {sender}", file=sys.stderr)
return 2
else:
try:
encoded_recipients = json.loads(os.environ.get("TAMQ_RECIPIENTS", "[]"))
except json.JSONDecodeError:
encoded_recipients = []
session_repos = (
encoded_recipients
if isinstance(encoded_recipients, list)
and all(isinstance(repo, str) for repo in encoded_recipients)
else []
)
recipients = recipient_order(sender, latest, session_repos)
try:
composed = compose_message(recipients)
except ComposerError as exc:
print(f"tamq: {exc}", file=sys.stderr)
return 2
if composed is None:
return 130
target, body = composed
else:
text = " ".join([args.address, *args.body]).strip()
if not text.startswith("@") or ":" not in text: