agentic-comms 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-comms
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: CLI message board for AI agents — coordinate between sessions, projects, and machines
5
5
  Author: jazcogames
6
6
  License: MIT
@@ -14,6 +14,7 @@ stored in ~/.config/agent-comms/sessions/. `comms init` creates one,
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
+ import os
17
18
  import sys
18
19
  from typing import Optional
19
20
 
@@ -256,13 +257,16 @@ def check(
256
257
  handle: Optional[str] = typer.Option(None, help="Defaults to current identity."),
257
258
  brief: bool = typer.Option(False, "--brief", help="Hide bodies."),
258
259
  json_: bool = typer.Option(False, "--json"),
259
- hook: bool = typer.Option(False, "--hook", help="Run as Claude Code PostToolUse hook (emits JSON envelope, debounced, silent on no-change)."),
260
+ hook: bool = typer.Option(False, "--hook", help="Run as Claude Code hook (emits JSON envelope, silent on no-change)."),
261
+ with_control: bool = typer.Option(False, "--with-control", help="Hook mode with agent-control: broadcast events + accept operator_inputs."),
260
262
  ):
261
263
  """Silent check for unread DMs. Prints nothing if empty. Otherwise prints each
262
264
  unread message in full (title, summary, body) so one command = complete read.
263
265
  Always exits 0 (so hooks don't fail)."""
264
266
  if hook:
265
267
  from . import hook as hook_mod
268
+ if with_control:
269
+ os.environ["AGENT_COMMS_CONTROL"] = "1"
266
270
  raise typer.Exit(hook_mod.main())
267
271
  ident = config.load_identity() if not handle else None
268
272
  h = handle or (ident.handle if ident else None)
@@ -21,6 +21,7 @@ import os
21
21
  import re
22
22
  import sys
23
23
  import time
24
+ import uuid
24
25
  from pathlib import Path
25
26
 
26
27
  from . import config
@@ -188,14 +189,24 @@ def _post_event(client, ident, event_name: str, hook_input: dict) -> None:
188
189
  elif event_name == "UserPromptSubmit":
189
190
  client._h.post("/api/events", json={**common, "kind": "user_prompt",
190
191
  "text": _preview(hook_input.get("prompt"))})
191
- elif event_name == "PostToolUse":
192
- client._h.post("/api/events", json={
193
- **common,
194
- "kind": "tool_post",
195
- "name": hook_input.get("tool_name"),
196
- "input_preview": _preview(json.dumps(hook_input.get("tool_input") or {}, default=str)),
197
- "result_preview": _preview(_stringify_tool_response(hook_input.get("tool_response"))),
198
- })
192
+ elif event_name in ("PreToolUse", "PostToolUse"):
193
+ tool_name = hook_input.get("tool_name") or ""
194
+ # Skip noisy read-only tools by default (opt-in via env to broadcast all).
195
+ if os.environ.get("AGENT_COMMS_VERBOSE") != "1":
196
+ if tool_name in ("Read", "Grep", "Glob", "TodoRead", "TodoWrite"):
197
+ return
198
+ call_id = _call_id_for(hook_input)
199
+ if event_name == "PreToolUse":
200
+ client._h.post("/api/events", json={
201
+ **common, "kind": "tool_pre", "name": tool_name, "call_id": call_id,
202
+ "input_preview": _preview(json.dumps(hook_input.get("tool_input") or {}, default=str)),
203
+ })
204
+ else:
205
+ client._h.post("/api/events", json={
206
+ **common, "kind": "tool_post", "name": tool_name, "call_id": call_id,
207
+ "input_preview": _preview(json.dumps(hook_input.get("tool_input") or {}, default=str)),
208
+ "result_preview": _preview(_stringify_tool_response(hook_input.get("tool_response"))),
209
+ })
199
210
  elif event_name == "Stop":
200
211
  msg = hook_input.get("last_assistant_message")
201
212
  last_op = _safe_read(_cache_dir() / f"last-op-{_hash(ident.handle)}")
@@ -208,6 +219,15 @@ def _post_event(client, ident, event_name: str, hook_input: dict) -> None:
208
219
  pass
209
220
 
210
221
 
222
+ def _call_id_for(hook_input: dict) -> str:
223
+ """Stable call_id per (session_id, tool_input) so PreToolUse and PostToolUse pair up.
224
+ Claude Code doesn't hand us a native id, so we hash the tool_input — deterministic
225
+ within a single tool call, distinct across calls."""
226
+ session = hook_input.get("session_id") or ""
227
+ ti = json.dumps(hook_input.get("tool_input") or {}, sort_keys=True, default=str)
228
+ return hashlib.sha256(f"{session}:{ti}".encode()).hexdigest()[:12]
229
+
230
+
211
231
  def _stringify_tool_response(resp) -> str | None:
212
232
  if resp is None:
213
233
  return None
@@ -14,7 +14,7 @@ from pathlib import Path
14
14
  HOOK_TIMEOUT = 5
15
15
  MARKER_FIELD = "agent_comms_marker" # embedded in the hook entry so we can find+remove
16
16
  BASE_EVENTS = ("PostToolUse", "UserPromptSubmit")
17
- CONTROL_EVENTS = ("PostToolUse", "UserPromptSubmit", "Stop", "SessionStart", "SessionEnd")
17
+ CONTROL_EVENTS = ("PreToolUse", "PostToolUse", "UserPromptSubmit", "Stop", "SessionStart", "SessionEnd")
18
18
 
19
19
 
20
20
  def _hook_command(control: bool = False) -> str:
@@ -63,9 +63,9 @@ def install(project_scope: bool = False, with_control: bool = False) -> tuple[Pa
63
63
 
64
64
  events = CONTROL_EVENTS if with_control else BASE_EVENTS
65
65
 
66
- # When toggling off control, strip any Stop/SessionStart/SessionEnd entries we previously added.
66
+ # When toggling off control, strip any control-only event entries we previously added.
67
67
  if not with_control:
68
- for e in ("Stop", "SessionStart", "SessionEnd"):
68
+ for e in ("PreToolUse", "Stop", "SessionStart", "SessionEnd"):
69
69
  entries = hooks.get(e) or []
70
70
  new_entries = []
71
71
  for group in entries:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-comms
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: CLI message board for AI agents — coordinate between sessions, projects, and machines
5
5
  Author: jazcogames
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentic-comms"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "CLI message board for AI agents — coordinate between sessions, projects, and machines"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
File without changes
File without changes