ring-cli 0.3.0__tar.gz → 0.4.0__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.
- {ring_cli-0.3.0 → ring_cli-0.4.0}/PKG-INFO +8 -5
- {ring_cli-0.3.0 → ring_cli-0.4.0}/README.en.md +7 -4
- {ring_cli-0.3.0 → ring_cli-0.4.0}/pyproject.toml +1 -1
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/cli.py +2 -20
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/hook.py +41 -4
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/registry.py +1 -1
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/tui.py +2 -7
- {ring_cli-0.3.0 → ring_cli-0.4.0}/LICENSE +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/__init__.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/__main__.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/commands/__init__.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/commands/_args.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/commands/doctor.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/commands/focus.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/commands/gc.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/commands/hook.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/config.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/focus/__init__.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/focus/applescript.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/focus/base.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/focus/iterm2.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/focus/terminal.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/focus/tmux.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/gc.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/hook_protocol.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/i18n.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/ipc.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/labels.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/locale/en/LC_MESSAGES/ring.mo +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/locale/en/LC_MESSAGES/ring.po +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/locale/ring.pot +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/notify/__init__.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/notify/base.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/notify/command.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/notify/notify_send.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/notify/osascript_notifier.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/notify/terminal_notifier.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/osascript.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/sources/__init__.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/sources/base.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/sources/claude_code.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/sources/codex.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/sources/hook_registry.py +0 -0
- {ring_cli-0.3.0 → ring_cli-0.4.0}/src/ring/watcher.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ring-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: RiNG — Realtime Instance Notification Grid for active agent CLI sessions.
|
|
5
5
|
Keywords: claude-code,codex,tui,dashboard,session,monitor,rich
|
|
6
6
|
Author: Wei Lee
|
|
@@ -58,7 +58,7 @@ RiNG puts them on one board, with sessions waiting for you sorted first.
|
|
|
58
58
|
- **One board for every session**: Claude Code / Codex are built in; other tools can feed `ring hook`.
|
|
59
59
|
- **Waiting first**: sessions that need your response are highlighted and sorted above the rest.
|
|
60
60
|
- **Jump back to the terminal**: in the TUI, select a session and press `Enter` / `Space` to focus tmux, iTerm2, or Terminal.app.
|
|
61
|
-
- **
|
|
61
|
+
- **Notifies you without the board open**: with hooks installed, the moment a session turns 🔴 waiting it beeps and fires a system notification — even with no RiNG board running. With `terminal-notifier`, clicking the notification jumps back.
|
|
62
62
|
- **Name your sessions**: press `n` in the TUI to add a local label such as `maigo · auth refactor`.
|
|
63
63
|
- **Local and extensible**: RiNG only reads local Claude Code / Codex data and writes `~/.config/ring/`; session sources, focusers, and notifiers are pluggable.
|
|
64
64
|
|
|
@@ -139,8 +139,10 @@ one live Codex session per cwd can jump correctly; multiple live Codex sessions
|
|
|
139
139
|
|
|
140
140
|
### Notifications
|
|
141
141
|
|
|
142
|
-
Default behavior: with hooks installed,
|
|
143
|
-
a system notification
|
|
142
|
+
Default behavior: with hooks installed, the moment a session changes to 🔴 waiting, the hook beeps
|
|
143
|
+
and sends a system notification right then — **no RiNG board needs to be open**, so it rings you even
|
|
144
|
+
after you close the terminal (notifications are event-driven, not polled). While a TUI is open, it
|
|
145
|
+
also nudges you again in-app at 30s / 120s / 300s if the session keeps waiting.
|
|
144
146
|
|
|
145
147
|
For clickable notifications on macOS, install `terminal-notifier`:
|
|
146
148
|
|
|
@@ -266,7 +268,8 @@ send `requires_action = true/false` or `waiting_for = "permission" | "options" |
|
|
|
266
268
|
|
|
267
269
|
### agent-hooks
|
|
268
270
|
|
|
269
|
-
Notification details:
|
|
271
|
+
Notification details: system notifications are sent by `ring hook` at the event (both `--watch` and
|
|
272
|
+
the TUI only render the board; they no longer send system notifications). The default backend is `auto`:
|
|
270
273
|
RiNG prefers clickable `terminal-notifier`, then falls back to `osascript` on macOS or `notify-send`
|
|
271
274
|
on Linux. If none are available, RiNG keeps the board running and skips notifications.
|
|
272
275
|
|
|
@@ -33,7 +33,7 @@ RiNG puts them on one board, with sessions waiting for you sorted first.
|
|
|
33
33
|
- **One board for every session**: Claude Code / Codex are built in; other tools can feed `ring hook`.
|
|
34
34
|
- **Waiting first**: sessions that need your response are highlighted and sorted above the rest.
|
|
35
35
|
- **Jump back to the terminal**: in the TUI, select a session and press `Enter` / `Space` to focus tmux, iTerm2, or Terminal.app.
|
|
36
|
-
- **
|
|
36
|
+
- **Notifies you without the board open**: with hooks installed, the moment a session turns 🔴 waiting it beeps and fires a system notification — even with no RiNG board running. With `terminal-notifier`, clicking the notification jumps back.
|
|
37
37
|
- **Name your sessions**: press `n` in the TUI to add a local label such as `maigo · auth refactor`.
|
|
38
38
|
- **Local and extensible**: RiNG only reads local Claude Code / Codex data and writes `~/.config/ring/`; session sources, focusers, and notifiers are pluggable.
|
|
39
39
|
|
|
@@ -114,8 +114,10 @@ one live Codex session per cwd can jump correctly; multiple live Codex sessions
|
|
|
114
114
|
|
|
115
115
|
### Notifications
|
|
116
116
|
|
|
117
|
-
Default behavior: with hooks installed,
|
|
118
|
-
a system notification
|
|
117
|
+
Default behavior: with hooks installed, the moment a session changes to 🔴 waiting, the hook beeps
|
|
118
|
+
and sends a system notification right then — **no RiNG board needs to be open**, so it rings you even
|
|
119
|
+
after you close the terminal (notifications are event-driven, not polled). While a TUI is open, it
|
|
120
|
+
also nudges you again in-app at 30s / 120s / 300s if the session keeps waiting.
|
|
119
121
|
|
|
120
122
|
For clickable notifications on macOS, install `terminal-notifier`:
|
|
121
123
|
|
|
@@ -241,7 +243,8 @@ send `requires_action = true/false` or `waiting_for = "permission" | "options" |
|
|
|
241
243
|
|
|
242
244
|
### agent-hooks
|
|
243
245
|
|
|
244
|
-
Notification details:
|
|
246
|
+
Notification details: system notifications are sent by `ring hook` at the event (both `--watch` and
|
|
247
|
+
the TUI only render the board; they no longer send system notifications). The default backend is `auto`:
|
|
245
248
|
RiNG prefers clickable `terminal-notifier`, then falls back to `osascript` on macOS or `notify-send`
|
|
246
249
|
on Linux. If none are available, RiNG keeps the board running and skips notifications.
|
|
247
250
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# import 的 module(見 [tool.uv.build-backend] module-name)與 CLI 指令仍是 `ring`。
|
|
3
3
|
[project]
|
|
4
4
|
name = "ring-cli"
|
|
5
|
-
version = "0.
|
|
5
|
+
version = "0.4.0"
|
|
6
6
|
description = "RiNG — Realtime Instance Notification Grid for active agent CLI sessions."
|
|
7
7
|
readme = "README.en.md"
|
|
8
8
|
requires-python = ">=3.13"
|
|
@@ -331,25 +331,15 @@ def run_config(args: list[str]) -> int:
|
|
|
331
331
|
|
|
332
332
|
|
|
333
333
|
def watch(interval: float, count: int, show_all: bool, show_legend: bool) -> int:
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
cfg = get_config()
|
|
334
|
+
# 系統通知由 ``ring hook`` 在 session 轉 🔴 等你的當下就地發出(見 hook._ring_waiting_now);
|
|
335
|
+
# watch 只負責顯示看板,不再輪詢發通知——這樣關掉看板也照樣 ring 你。
|
|
338
336
|
frames = 0
|
|
339
337
|
footer_text = _("每 {interval}s 刷新 · Ctrl-C 離場", interval=int(interval))
|
|
340
338
|
if not HAVE_RICH:
|
|
341
|
-
scheduler = WaitingAlertScheduler(cfg.notify_repeat_seconds, cfg.notify_repeat_max)
|
|
342
339
|
try:
|
|
343
340
|
while True:
|
|
344
341
|
sys.stdout.write("\033[2J\033[H")
|
|
345
342
|
sessions = board(show_all)
|
|
346
|
-
alerts = scheduler.feed(sessions)
|
|
347
|
-
try:
|
|
348
|
-
hint = notify_waiting(alerts)
|
|
349
|
-
if hint:
|
|
350
|
-
print(hint)
|
|
351
|
-
except Exception:
|
|
352
|
-
pass
|
|
353
343
|
print(_render_plain(sessions, show_legend, show_tool_column(sessions)))
|
|
354
344
|
print(f"\n{footer_text}")
|
|
355
345
|
sys.stdout.flush()
|
|
@@ -361,18 +351,10 @@ def watch(interval: float, count: int, show_all: bool, show_legend: bool) -> int
|
|
|
361
351
|
return 0
|
|
362
352
|
|
|
363
353
|
console = Console()
|
|
364
|
-
scheduler = WaitingAlertScheduler(cfg.notify_repeat_seconds, cfg.notify_repeat_max)
|
|
365
354
|
try:
|
|
366
355
|
with Live(console=console, screen=True, auto_refresh=False) as live:
|
|
367
356
|
while True:
|
|
368
357
|
sessions = board(show_all)
|
|
369
|
-
alerts = scheduler.feed(sessions)
|
|
370
|
-
try:
|
|
371
|
-
hint = notify_waiting(alerts)
|
|
372
|
-
if hint:
|
|
373
|
-
print(hint)
|
|
374
|
-
except Exception:
|
|
375
|
-
pass
|
|
376
358
|
body = _rich_renderable(sessions, show_legend, show_tool_column(sessions))
|
|
377
359
|
live.update(Group(body, Text(f"\n{footer_text}", style=_MUTED)), refresh=True)
|
|
378
360
|
frames += 1
|
|
@@ -33,6 +33,7 @@ from ring.i18n import gettext as _
|
|
|
33
33
|
from ring.i18n import set_lang
|
|
34
34
|
from ring.registry import (
|
|
35
35
|
RING_REGISTRY,
|
|
36
|
+
Session,
|
|
36
37
|
Status,
|
|
37
38
|
_extract_todo,
|
|
38
39
|
_latest_action,
|
|
@@ -127,11 +128,13 @@ def _session_tty(process_names: tuple[str, ...]) -> str:
|
|
|
127
128
|
|
|
128
129
|
|
|
129
130
|
def run_hook(provider: str = "claude-code") -> int:
|
|
130
|
-
"""讀一次 stdin → 寫 RiNG registry
|
|
131
|
+
"""讀一次 stdin → 寫 RiNG registry 狀態(看板)→ 轉等你時就地發通知 →(可選)委派 agent-hooks。
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
轉 🔴 等你的事件,會在當下直接發系統通知(見 ``_ring_waiting_now``)——不必開著看板,
|
|
134
|
+
關掉終端也照樣 ring 你。委派只在 ``notify_backend == "agent-hooks"`` 且 PATH 上有
|
|
135
|
+
``agent-hooks`` 時發生:把原始 payload 透傳給 ``agent-hooks callback``,由它同步出 modal、
|
|
136
|
+
收按鈕、把決策寫到 stdout 回給 Claude(這條路下 ``_ring_waiting_now`` 自動短路、不重複發)。
|
|
137
|
+
其餘情況 RiNG 記狀態 + 發通知後 exit 0(你在終端自己回答)。
|
|
135
138
|
"""
|
|
136
139
|
try:
|
|
137
140
|
raw = sys.stdin.read()
|
|
@@ -191,6 +194,40 @@ def _record_session_state(data: dict[str, Any], selected_provider: str) -> None:
|
|
|
191
194
|
tmp.write_text(json.dumps(payload))
|
|
192
195
|
tmp.replace(path) # atomic
|
|
193
196
|
|
|
197
|
+
if event.status is Status.WAITING:
|
|
198
|
+
_ring_waiting_now(event, payload, last_action)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _ring_waiting_now(event: Any, payload: dict[str, Any], last_action: str) -> None:
|
|
202
|
+
"""session 轉 🔴 等你的當下,就地由 hook 發系統通知——不必等 RiNG 看板輪詢。
|
|
203
|
+
|
|
204
|
+
WAITING 只會從 hook 來(scan 模式永不標 WAITING),所以 hook 是第一手知道「等你」
|
|
205
|
+
的地方;在事件當下通知最即時,也不依賴看板有沒有開著(看板沒開時舊版根本不會 ring
|
|
206
|
+
你)。backend=none / agent-hooks 由 notify_waiting → _select_notifier 自動短路
|
|
207
|
+
(agent-hooks 改走 _delegate_to_agent_hooks 的 modal 委派),不會重複發。
|
|
208
|
+
失敗安靜吞掉,絕不擋住 session。
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
from ring.notify import notify_waiting
|
|
212
|
+
|
|
213
|
+
notify_waiting(
|
|
214
|
+
[
|
|
215
|
+
Session(
|
|
216
|
+
session_id=event.session_id,
|
|
217
|
+
cwd=event.cwd,
|
|
218
|
+
status=Status.WAITING,
|
|
219
|
+
last_active=float(payload.get("last_active", time.time())),
|
|
220
|
+
last_action=last_action,
|
|
221
|
+
source="hook",
|
|
222
|
+
tty=payload.get("tty"),
|
|
223
|
+
provider=event.provider,
|
|
224
|
+
origin_cwd=event.cwd,
|
|
225
|
+
)
|
|
226
|
+
]
|
|
227
|
+
)
|
|
228
|
+
except Exception:
|
|
229
|
+
pass
|
|
230
|
+
|
|
194
231
|
|
|
195
232
|
def _delegate_to_agent_hooks(raw: str, selected_provider: str) -> int:
|
|
196
233
|
"""notify_backend=="agent-hooks" 且 agent-hooks 在 PATH → 透傳 payload 給它出決策。
|
|
@@ -590,7 +590,7 @@ def _codex_threads(procs: list[tuple[str, str]]) -> list[Session]:
|
|
|
590
590
|
continue
|
|
591
591
|
updated_ms = int(row["updated_at_ms"] or 0)
|
|
592
592
|
last_active = updated_ms / 1000 if updated_ms else float(row["updated_at"] or 0)
|
|
593
|
-
if now - last_active > ACTIVE_WINDOW_SECONDS and counts.get(cwd, 0) == 0:
|
|
593
|
+
if now - last_active > ACTIVE_WINDOW_SECONDS and counts.get(_real(cwd), 0) == 0:
|
|
594
594
|
continue
|
|
595
595
|
rollout_path = Path(str(row["rollout_path"] or ""))
|
|
596
596
|
records = _tail_records(rollout_path) if rollout_path else []
|
|
@@ -36,7 +36,6 @@ from ring.i18n import gettext as _
|
|
|
36
36
|
from ring.i18n import set_lang
|
|
37
37
|
from ring.ipc import clear_tui_presence, read_focus_request, write_tui_presence
|
|
38
38
|
from ring.labels import get_label, load_labels, set_label
|
|
39
|
-
from ring.notify import notify_waiting
|
|
40
39
|
from ring.registry import Session, Status, running_agent_pids
|
|
41
40
|
from ring.watcher import WaitingAlertScheduler
|
|
42
41
|
|
|
@@ -280,14 +279,10 @@ class RingApp(App[None]):
|
|
|
280
279
|
cur = next((s for s in self._sessions if s.session_id == self._focused_sid), None)
|
|
281
280
|
if cur is None or cur.status is not Status.WAITING:
|
|
282
281
|
self._focused_sid = None
|
|
282
|
+
# 系統通知(toast)改由 ``ring hook`` 在事件當下發出(見 hook._ring_waiting_now);
|
|
283
|
+
# 這裡只留 TUI 自己的 in-app 響鈴 / 訊息列與醒目標記,不重複發系統通知。
|
|
283
284
|
alerts = self._alerts.feed(self._sessions)
|
|
284
285
|
self._ring_on_waiting_alerts(alerts)
|
|
285
|
-
try:
|
|
286
|
-
hint = notify_waiting(alerts)
|
|
287
|
-
if hint:
|
|
288
|
-
self.notify(hint, timeout=10)
|
|
289
|
-
except Exception:
|
|
290
|
-
pass
|
|
291
286
|
self.sub_title = _header(len(self._sessions), len(running_agent_pids()))
|
|
292
287
|
labels = load_labels()
|
|
293
288
|
table.clear()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|