ring-cli 0.16.0__tar.gz → 0.17.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.
Files changed (68) hide show
  1. {ring_cli-0.16.0 → ring_cli-0.17.0}/PKG-INFO +1 -1
  2. {ring_cli-0.16.0 → ring_cli-0.17.0}/pyproject.toml +2 -2
  3. {ring_cli-0.16.0 → ring_cli-0.17.0}/pyproject.toml.orig +3 -2
  4. ring_cli-0.17.0/src/ring/agent_notify.py +140 -0
  5. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/doctor.py +42 -0
  6. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/locale/en/LC_MESSAGES/ring.mo +0 -0
  7. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/locale/en/LC_MESSAGES/ring.po +43 -0
  8. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/locale/ring.pot +163 -102
  9. {ring_cli-0.16.0 → ring_cli-0.17.0}/LICENSE +0 -0
  10. {ring_cli-0.16.0 → ring_cli-0.17.0}/README.en.md +0 -0
  11. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/__init__.py +0 -0
  12. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/__main__.py +0 -0
  13. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/cli.py +0 -0
  14. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/__init__.py +0 -0
  15. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/_args.py +0 -0
  16. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/completion.py +0 -0
  17. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/digest.py +0 -0
  18. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/focus.py +0 -0
  19. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/gc.py +0 -0
  20. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/hook.py +0 -0
  21. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/quiet.py +0 -0
  22. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/commands/stats.py +0 -0
  23. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/config.py +0 -0
  24. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/__init__.py +0 -0
  25. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/applescript.py +0 -0
  26. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/base.py +0 -0
  27. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/iterm2.py +0 -0
  28. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/kitty.py +0 -0
  29. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/linux_wm.py +0 -0
  30. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/neovim.py +0 -0
  31. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/terminal.py +0 -0
  32. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/tmux.py +0 -0
  33. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/focus/trace.py +0 -0
  34. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/gc.py +0 -0
  35. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/hook.py +0 -0
  36. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/hook_protocol.py +0 -0
  37. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/i18n.py +0 -0
  38. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/ipc.py +0 -0
  39. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/labels.py +0 -0
  40. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/__init__.py +0 -0
  41. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/base.py +0 -0
  42. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/command.py +0 -0
  43. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/notify_send.py +0 -0
  44. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/ntfy.py +0 -0
  45. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/osascript_notifier.py +0 -0
  46. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/terminal_notifier.py +0 -0
  47. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify/webhook.py +0 -0
  48. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/notify_queue.py +0 -0
  49. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/osascript.py +0 -0
  50. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/pathutil.py +0 -0
  51. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/payload_log.py +0 -0
  52. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/permission.py +0 -0
  53. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/plugins.py +0 -0
  54. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/ps_parse.py +0 -0
  55. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/question_detect.py +0 -0
  56. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/registry.py +0 -0
  57. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/sources/__init__.py +0 -0
  58. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/sources/base.py +0 -0
  59. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/sources/claude_code.py +0 -0
  60. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/sources/codex.py +0 -0
  61. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/sources/hook_registry.py +0 -0
  62. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/sources/local_llm.py +0 -0
  63. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/stats.py +0 -0
  64. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/subproc.py +0 -0
  65. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/tmux_scan.py +0 -0
  66. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/transcript.py +0 -0
  67. {ring_cli-0.16.0 → ring_cli-0.17.0}/src/ring/tui.py +0 -0
  68. {ring_cli-0.16.0 → ring_cli-0.17.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.16.0
3
+ Version: 0.17.0
4
4
  Summary: RiNG — Realtime Instance Notification Grid for active agent CLI sessions.
5
5
  Keywords: claude-code,codex,ollama,llama-cpp,tui,dashboard,session,monitor,rich
6
6
  Author: Wei Lee
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ring-cli"
3
- version = "0.16.0"
3
+ version = "0.17.0"
4
4
  description = "RiNG — Realtime Instance Notification Grid for active agent CLI sessions."
5
5
  readme = "README.en.md"
6
6
  requires-python = ">=3.13"
@@ -126,7 +126,7 @@ ignore = [
126
126
  [tool.codespell]
127
127
  skip = ".git*,*.lock"
128
128
  check-hidden = true
129
- ignore-words-list = "otification"
129
+ ignore-words-list = "otification,iterm"
130
130
 
131
131
  [tool.poe.tasks.format]
132
132
  help = "Format the code"
@@ -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.16.0"
5
+ version = "0.17.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"
@@ -113,7 +113,8 @@ ignore = [
113
113
  skip = '.git*,*.lock'
114
114
  check-hidden = true
115
115
  # README 縮寫 **N**otification 被 markdown 粗體拆成 `otification`,codespell 會誤判為 notification 拼錯
116
- ignore-words-list = 'otification'
116
+ # iterm:TERM_PROGRAM 的值是 `iTerm.app`(沒有結尾的 2),codespell 會把它當 term 拼錯
117
+ ignore-words-list = 'otification,iterm'
117
118
 
118
119
  [tool.poe.tasks]
119
120
  format.help = "Format the code"
@@ -0,0 +1,140 @@
1
+ """agent CLI「自己」的桌面通知通道診斷(目前只有 Claude Code 有這種東西)。
2
+
3
+ 為什麼需要這個模組:``ring install-hooks`` 只會寫 ``~/.claude/settings.json`` 的
4
+ ``hooks`` 這一個 key(見 ``ring.hook._hook_targets``),從來不碰 Claude Code 自己的
5
+ ``preferredNotifChannel``。這是兩條平行的路——hook 決定「RiNG 知不知道發生了什麼」,
6
+ ``preferredNotifChannel`` 決定「Claude Code 自己要不要跳桌面通知」。裝了 hook 不會關掉
7
+ 後者,所以同一個權限請求會有兩則通知;而且 Claude Code 那條還會在 Stop 之後補一則
8
+ ``idle_prompt``(「閒著、換你了」),那是 RiNG 刻意判 🟡 不發的類別——使用者跳過去只會
9
+ 看到一個空 prompt,沒有東西可回。
10
+
11
+ 這個模組只負責把這個重複來源攤開給 ``ring doctor`` 看,**全程唯讀**:讀 settings 檔、看
12
+ 環境變數,不改任何設定、不寫任何檔。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import os
19
+ from collections.abc import Mapping
20
+ from dataclasses import dataclass
21
+ from pathlib import Path
22
+
23
+ CLAUDE_SETTINGS_PATH = Path.home() / ".claude" / "settings.json"
24
+
25
+ # Claude Code 的通知通道設定 key,以及沒設定時的值。
26
+ _CHANNEL_KEY = "preferredNotifChannel"
27
+ DEFAULT_CHANNEL = "auto"
28
+
29
+ # 通道值 → 會不會跳桌面通知。取自 claude binary 裡的通道 enum:
30
+ # auto | iterm2 | terminal_bell | iterm2_with_bell | kitty | ghostty | notifications_disabled
31
+ # 這份對應只涵蓋「明確指定」的值;``auto`` 要看終端,另外由 _auto_kind() 判。
32
+ _EXPLICIT_KINDS = {
33
+ "notifications_disabled": "off",
34
+ "terminal_bell": "bell",
35
+ "iterm2": "banner",
36
+ "iterm2_with_bell": "banner",
37
+ "kitty": "banner",
38
+ "ghostty": "banner",
39
+ }
40
+
41
+ # ``auto`` 在這些終端底下有對應的專屬通道可挑 → 會跳桌面通知。
42
+ # 依據是上面那份 enum 裡有 iterm2 / kitty / ghostty 三個終端專屬值;auto 實際怎麼判
43
+ # 沒有公開文件,所以其他終端一律回 "unknown",不假裝知道。
44
+ _AUTO_BANNER_TERMINALS = {"iTerm2", "kitty", "ghostty"}
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class NativeNotifyStatus:
49
+ """Claude Code 自帶通知通道的現況(唯讀快照)。
50
+
51
+ :param path: 讀的 settings 檔路徑。
52
+ :param exists: 該檔存不存在(不存在 → Claude Code 用內建預設)。
53
+ :param channel: 生效中的通道值;沒設定就是 ``DEFAULT_CHANNEL``。
54
+ :param explicit: 通道是不是使用者明確設定的(False = 沒設、吃預設)。
55
+ :param terminal: 偵測到的終端名;認不出來是空字串。
56
+ :param kind: ``"off"`` / ``"bell"`` / ``"banner"`` / ``"unknown"``。
57
+ """
58
+
59
+ path: Path
60
+ exists: bool
61
+ channel: str
62
+ explicit: bool
63
+ terminal: str
64
+ kind: str
65
+
66
+ @property
67
+ def duplicates_ring(self) -> bool:
68
+ """這個設定會不會跟 RiNG 的通知重複(只有明確關掉才不會)。"""
69
+ return self.kind != "off"
70
+
71
+
72
+ def detect_terminal(env: Mapping[str, str] | None = None) -> str:
73
+ """從環境變數認出目前的終端;認不出回空字串。
74
+
75
+ kitty 的標記(``KITTY_WINDOW_ID`` / ``TERM=xterm-kitty``)先看,因為 kitty 底下
76
+ ``TERM_PROGRAM`` 不一定有值。認不出來的 ``TERM_PROGRAM`` 原樣回傳,讓 doctor 至少
77
+ 印得出「我看到的是什麼」。
78
+
79
+ 注意:tmux / ssh 裡 ``TERM_PROGRAM`` 可能不見,或被繼承成外層終端的值——所以這個
80
+ 結果只能當提示,不能當判定依據(呼叫端請照這個前提呈現)。
81
+ """
82
+ env = os.environ if env is None else env
83
+ if env.get("KITTY_WINDOW_ID") or env.get("TERM", "") == "xterm-kitty":
84
+ return "kitty"
85
+ known = {
86
+ "iTerm.app": "iTerm2",
87
+ "Apple_Terminal": "Terminal",
88
+ "ghostty": "ghostty",
89
+ "WezTerm": "WezTerm",
90
+ "vscode": "VS Code",
91
+ }
92
+ term_program = env.get("TERM_PROGRAM", "").strip()
93
+ if not term_program:
94
+ return ""
95
+ return known.get(term_program, term_program)
96
+
97
+
98
+ def _read_channel(path: Path) -> tuple[bool, str, bool]:
99
+ """讀 settings 檔的通道設定 → ``(檔案存在, 通道值, 是否明確設定)``。
100
+
101
+ 讀不到 / 不是合法 JSON / 值不是字串 → 一律當「沒設定」(吃預設),不拋例外:
102
+ doctor 是唯讀診斷,任何一節壞掉都不該讓整份報告掛掉。
103
+ """
104
+ if not path.exists():
105
+ return False, DEFAULT_CHANNEL, False
106
+ try:
107
+ data = json.loads(path.read_text() or "{}")
108
+ except Exception:
109
+ return True, DEFAULT_CHANNEL, False
110
+ if not isinstance(data, dict):
111
+ return True, DEFAULT_CHANNEL, False
112
+ value = data.get(_CHANNEL_KEY)
113
+ if not isinstance(value, str) or not value.strip():
114
+ return True, DEFAULT_CHANNEL, False
115
+ return True, value.strip(), True
116
+
117
+
118
+ def _auto_kind(terminal: str) -> str:
119
+ """``auto`` 在這個終端底下會不會跳桌面通知。認不出的終端回 ``"unknown"``。"""
120
+ return "banner" if terminal in _AUTO_BANNER_TERMINALS else "unknown"
121
+
122
+
123
+ def native_notify_status(path: Path | None = None, env: Mapping[str, str] | None = None) -> NativeNotifyStatus:
124
+ """組出 Claude Code 自帶通知通道的現況快照。唯讀,不改任何設定。"""
125
+ target = CLAUDE_SETTINGS_PATH if path is None else path
126
+ exists, channel, explicit = _read_channel(target)
127
+ terminal = detect_terminal(env)
128
+ # 明確寫 "auto" 跟完全沒設定是同一件事,都要看終端才知道會不會跳通知。
129
+ kind = _auto_kind(terminal) if channel == DEFAULT_CHANNEL else _EXPLICIT_KINDS.get(channel, "unknown")
130
+ return NativeNotifyStatus(
131
+ path=target,
132
+ exists=exists,
133
+ channel=channel,
134
+ explicit=explicit,
135
+ terminal=terminal,
136
+ kind=kind,
137
+ )
138
+
139
+
140
+ __all__ = ["CLAUDE_SETTINGS_PATH", "DEFAULT_CHANNEL", "NativeNotifyStatus", "detect_terminal", "native_notify_status"]
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import shutil
6
6
  import sys
7
7
 
8
+ from ring.agent_notify import native_notify_status
8
9
  from ring.commands._args import strip_lang
9
10
  from ring.config import CONFIG_PATH, get_config
10
11
  from ring.focus.kitty import sockets as kitty_sockets
@@ -14,6 +15,43 @@ from ring.i18n import gettext as _
14
15
  from ring.sources import discover_sessions, sources
15
16
 
16
17
 
18
+ def _print_native_notify() -> None:
19
+ """印「Claude Code 自帶通知」這一節。
20
+
21
+ 這節回答的是「除了 RiNG,還有誰會跳通知」——``ring install-hooks`` 不會關掉
22
+ Claude Code 自己的 ``preferredNotifChannel``(見 ``ring.agent_notify`` 模組說明),
23
+ 兩邊會對同一個權限請求各發一則,而且 Claude Code 那條還會多發 Stop 之後的
24
+ idle_prompt。整節唯讀,任何一步失敗就整節略過,不影響其餘報告。
25
+ """
26
+ try:
27
+ status = native_notify_status()
28
+ except Exception:
29
+ print(f" {_('狀態')}:{_('偵測失敗')}")
30
+ return
31
+
32
+ # 用 "Claude 設定檔" 而不是既有的 "設定檔" msgid:後者是最後一節 RiNG 自己 config
33
+ # 的標題(en 翻成 "Config File"),共用會把這行講成 RiNG 的設定檔,指錯地方。
34
+ print(f" {_('Claude 設定檔')}:{status.path}")
35
+ if not status.exists:
36
+ print(f" {_('狀態')}:{_('settings 檔不存在 → 吃 Claude Code 內建預設')}")
37
+ channel_str = status.channel if status.explicit else _("未設定(={channel})", channel=status.channel)
38
+ print(f" preferredNotifChannel:{channel_str}")
39
+ print(f" {_('目前終端')}:{status.terminal or _('認不出(tmux/ssh 裡可能測不到,僅供參考)')}")
40
+
41
+ if status.kind == "off":
42
+ print(f" {_('狀態')}:{_('已關閉——只有 RiNG 會通知你')}")
43
+ return
44
+ if status.kind == "bell":
45
+ print(f" {_('狀態')}:{_('只響鈴、不跳通知框;RiNG 的通知不受影響')}")
46
+ return
47
+ if status.kind == "banner":
48
+ print(f" {_('狀態')}:{_('Claude Code 也會自己跳通知——權限提示會跟 RiNG 重複')}")
49
+ else:
50
+ print(f" {_('狀態')}:{_('認不出這個通道會不會跳通知;沒明確關掉就有重複的可能')}")
51
+ print(f" {_('另外')}:{_('它還會在 Stop 後約 60 秒發 idle_prompt(閒著、換你),這類 RiNG 刻意不發')}")
52
+ print(f" {_('只想留 RiNG')}:{_('把 preferredNotifChannel 設成 notifications_disabled')}")
53
+
54
+
17
55
  def run_doctor(args: list[str]) -> int:
18
56
  """唯讀環境診斷,印出各節報告,固定回 0。args 非空回 2。"""
19
57
  args = strip_lang(args)
@@ -100,6 +138,10 @@ def run_doctor(args: list[str]) -> int:
100
138
  print(f" {_('auto 實際選中')}:{_('不發通知')}({reason})")
101
139
  print()
102
140
 
141
+ print(_("Claude Code 自帶通知"))
142
+ _print_native_notify()
143
+ print()
144
+
103
145
  print(_("聚焦終端(focuser)"))
104
146
  focuser_list = focusers()
105
147
  width_f = max(len(f.name) for f in focuser_list) if focuser_list else 10
@@ -802,3 +802,46 @@ msgstr "invalid duration: {value} (e.g. 30m, 1h)"
802
802
 
803
803
  msgid "🔇 quiet 已開啟,{duration} 後自動解除"
804
804
  msgstr "🔇 quiet turned on, auto-clears after {duration}"
805
+
806
+ # ── doctor:Claude Code 自帶通知 ──
807
+ msgid "Claude Code 自帶通知"
808
+ msgstr "Claude Code's Own Notifications"
809
+
810
+ msgid "Claude 設定檔"
811
+ msgstr "Claude settings"
812
+
813
+ msgid "settings 檔不存在 → 吃 Claude Code 內建預設"
814
+ msgstr "settings file missing -> Claude Code falls back to its built-in default"
815
+
816
+ msgid "未設定(={channel})"
817
+ msgstr "unset (= {channel})"
818
+
819
+ msgid "目前終端"
820
+ msgstr "current terminal"
821
+
822
+ msgid "認不出(tmux/ssh 裡可能測不到,僅供參考)"
823
+ msgstr "unrecognized (may be undetectable under tmux/ssh; treat as a hint only)"
824
+
825
+ msgid "已關閉——只有 RiNG 會通知你"
826
+ msgstr "disabled - RiNG is your only notifier"
827
+
828
+ msgid "只響鈴、不跳通知框;RiNG 的通知不受影響"
829
+ msgstr "bell only, no banner; RiNG's notifications are unaffected"
830
+
831
+ msgid "Claude Code 也會自己跳通知——權限提示會跟 RiNG 重複"
832
+ msgstr "Claude Code also posts its own banners - permission prompts will double up with RiNG"
833
+
834
+ msgid "認不出這個通道會不會跳通知;沒明確關掉就有重複的可能"
835
+ msgstr "unknown whether this channel posts banners; without an explicit opt-out, duplicates are possible"
836
+
837
+ msgid "另外"
838
+ msgstr "also"
839
+
840
+ msgid "它還會在 Stop 後約 60 秒發 idle_prompt(閒著、換你),這類 RiNG 刻意不發"
841
+ msgstr "it also fires idle_prompt ~60s after Stop (idle, your turn), a class RiNG deliberately stays silent on"
842
+
843
+ msgid "只想留 RiNG"
844
+ msgstr "to keep only RiNG"
845
+
846
+ msgid "把 preferredNotifChannel 設成 notifications_disabled"
847
+ msgstr "set preferredNotifChannel to notifications_disabled"
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PROJECT VERSION\n"
10
10
  "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
11
- "POT-Creation-Date: 2026-08-12 23:28+0800\n"
11
+ "POT-Creation-Date: 2026-08-16 15:25+0800\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -52,7 +52,7 @@ msgstr[1] ""
52
52
  msgid "🎤 RiNG — {sess} · {proc}"
53
53
  msgstr ""
54
54
 
55
- #: src/ring/cli.py:166 src/ring/cli.py:219 src/ring/tui.py:288
55
+ #: src/ring/cli.py:166 src/ring/cli.py:219 src/ring/tui.py:305
56
56
  msgid "圖例"
57
57
  msgstr ""
58
58
 
@@ -60,35 +60,38 @@ msgstr ""
60
60
  msgid "(場館目前沒人上台)"
61
61
  msgstr ""
62
62
 
63
- #: src/ring/cli.py:187 src/ring/cli.py:341 src/ring/commands/doctor.py:32
64
- #: src/ring/commands/doctor.py:74 src/ring/commands/doctor.py:76
65
- #: src/ring/commands/doctor.py:78 src/ring/commands/doctor.py:154
66
- #: src/ring/tui.py:243
63
+ #: src/ring/cli.py:187 src/ring/cli.py:341 src/ring/commands/doctor.py:29
64
+ #: src/ring/commands/doctor.py:36 src/ring/commands/doctor.py:42
65
+ #: src/ring/commands/doctor.py:45 src/ring/commands/doctor.py:48
66
+ #: src/ring/commands/doctor.py:50 src/ring/commands/doctor.py:70
67
+ #: src/ring/commands/doctor.py:112 src/ring/commands/doctor.py:114
68
+ #: src/ring/commands/doctor.py:116 src/ring/commands/doctor.py:196
69
+ #: src/ring/tui.py:260
67
70
  msgid "狀態"
68
71
  msgstr ""
69
72
 
70
- #: src/ring/cli.py:189 src/ring/cli.py:238 src/ring/tui.py:245
73
+ #: src/ring/cli.py:189 src/ring/cli.py:238 src/ring/tui.py:262
71
74
  msgid "工具"
72
75
  msgstr ""
73
76
 
74
77
  #: src/ring/cli.py:190 src/ring/cli.py:238 src/ring/commands/stats.py:60
75
- #: src/ring/tui.py:246
78
+ #: src/ring/tui.py:263
76
79
  msgid "專案"
77
80
  msgstr ""
78
81
 
79
- #: src/ring/cli.py:191 src/ring/cli.py:238 src/ring/tui.py:246
82
+ #: src/ring/cli.py:191 src/ring/cli.py:238 src/ring/tui.py:263
80
83
  msgid "進度"
81
84
  msgstr ""
82
85
 
83
- #: src/ring/cli.py:192 src/ring/cli.py:238 src/ring/tui.py:246
86
+ #: src/ring/cli.py:192 src/ring/cli.py:238 src/ring/tui.py:263
84
87
  msgid "閒置"
85
88
  msgstr ""
86
89
 
87
- #: src/ring/cli.py:193 src/ring/cli.py:238 src/ring/tui.py:246
90
+ #: src/ring/cli.py:193 src/ring/cli.py:238 src/ring/tui.py:263
88
91
  msgid "去哪"
89
92
  msgstr ""
90
93
 
91
- #: src/ring/cli.py:195 src/ring/cli.py:238 src/ring/tui.py:246
94
+ #: src/ring/cli.py:195 src/ring/cli.py:238 src/ring/tui.py:263
92
95
  msgid "動作"
93
96
  msgstr ""
94
97
 
@@ -100,15 +103,15 @@ msgstr ""
100
103
  msgid "RiNG 設定檔"
101
104
  msgstr ""
102
105
 
103
- #: src/ring/cli.py:340 src/ring/commands/doctor.py:153
106
+ #: src/ring/cli.py:340 src/ring/commands/doctor.py:195
104
107
  msgid "路徑"
105
108
  msgstr ""
106
109
 
107
- #: src/ring/cli.py:341 src/ring/commands/doctor.py:154
110
+ #: src/ring/cli.py:341 src/ring/commands/doctor.py:196
108
111
  msgid "已存在"
109
112
  msgstr ""
110
113
 
111
- #: src/ring/cli.py:341 src/ring/commands/doctor.py:154
114
+ #: src/ring/cli.py:341 src/ring/commands/doctor.py:196
112
115
  msgid "不存在(全部用內建預設)"
113
116
  msgstr ""
114
117
 
@@ -399,182 +402,182 @@ msgstr ""
399
402
  msgid "這個 session 在做什麼…"
400
403
  msgstr ""
401
404
 
402
- #: src/ring/tui.py:145
405
+ #: src/ring/tui.py:162
403
406
  #, python-brace-format
404
407
  msgid "回覆 {project} 的權限請求"
405
408
  msgstr ""
406
409
 
407
- #: src/ring/tui.py:150
410
+ #: src/ring/tui.py:167
408
411
  msgid "Enter 或數字鍵送出、Esc 取消"
409
412
  msgstr ""
410
413
 
411
- #: src/ring/tui.py:173
414
+ #: src/ring/tui.py:190
412
415
  msgid "離場"
413
416
  msgstr ""
414
417
 
415
- #: src/ring/tui.py:174
418
+ #: src/ring/tui.py:191
416
419
  msgid "刷新"
417
420
  msgstr ""
418
421
 
419
- #: src/ring/tui.py:175
422
+ #: src/ring/tui.py:192
420
423
  msgid "含已離場"
421
424
  msgstr ""
422
425
 
423
- #: src/ring/tui.py:176
426
+ #: src/ring/tui.py:193
424
427
  msgid "命名"
425
428
  msgstr ""
426
429
 
427
- #: src/ring/tui.py:177
430
+ #: src/ring/tui.py:194
428
431
  msgid "隱藏"
429
432
  msgstr ""
430
433
 
431
- #: src/ring/tui.py:178
434
+ #: src/ring/tui.py:195
432
435
  msgid "最久等待"
433
436
  msgstr ""
434
437
 
435
- #: src/ring/tui.py:179
438
+ #: src/ring/tui.py:196
436
439
  msgid "回覆權限"
437
440
  msgstr ""
438
441
 
439
- #: src/ring/tui.py:180 src/ring/tui.py:181
442
+ #: src/ring/tui.py:197 src/ring/tui.py:198
440
443
  msgid "跳過去"
441
444
  msgstr ""
442
445
 
443
- #: src/ring/tui.py:264
446
+ #: src/ring/tui.py:281
444
447
  msgid "💡 同專案開了多個 session,裝 hook 跳轉才精準:ring install-hooks"
445
448
  msgstr ""
446
449
 
447
- #: src/ring/tui.py:266
450
+ #: src/ring/tui.py:283
448
451
  msgid "↑/↓ 選一列,Enter 或 Space 跳到那個 session 的終端"
449
452
  msgstr ""
450
453
 
451
- #: src/ring/tui.py:333
454
+ #: src/ring/tui.py:350
452
455
  #, python-brace-format
453
456
  msgid "🔔 {names} 在等你回話"
454
457
  msgstr ""
455
458
 
456
- #: src/ring/tui.py:372
459
+ #: src/ring/tui.py:389
457
460
  #, python-brace-format
458
461
  msgid "→ 已跳到 {project}(來自通知)"
459
462
  msgstr ""
460
463
 
461
- #: src/ring/tui.py:377
464
+ #: src/ring/tui.py:394
462
465
  msgid "那個 session 已不在場"
463
466
  msgstr ""
464
467
 
465
- #: src/ring/tui.py:388
468
+ #: src/ring/tui.py:405
466
469
  msgid "🔇 QUIET"
467
470
  msgstr ""
468
471
 
469
- #: src/ring/tui.py:390
472
+ #: src/ring/tui.py:407
470
473
  #, python-brace-format
471
474
  msgid "🔇 QUIET · 剩 {remaining}"
472
475
  msgstr ""
473
476
 
474
- #: src/ring/tui.py:393
477
+ #: src/ring/tui.py:410
475
478
  #, python-brace-format
476
479
  msgid "queue: {n}"
477
480
  msgstr ""
478
481
 
479
- #: src/ring/tui.py:485
482
+ #: src/ring/tui.py:502
480
483
  msgid " ⚠ hook 可能失效:來源檔有更新但沒有 heartbeat"
481
484
  msgstr ""
482
485
 
483
- #: src/ring/tui.py:514 src/ring/tui.py:538 src/ring/tui.py:578
484
- #: src/ring/tui.py:682
486
+ #: src/ring/tui.py:531 src/ring/tui.py:555 src/ring/tui.py:595
487
+ #: src/ring/tui.py:699
485
488
  msgid "(沒有選到 session)"
486
489
  msgstr ""
487
490
 
488
- #: src/ring/tui.py:545 src/ring/tui.py:553 src/ring/tui.py:584
491
+ #: src/ring/tui.py:562 src/ring/tui.py:570 src/ring/tui.py:601
489
492
  #, python-brace-format
490
493
  msgid "{project}:{msg}"
491
494
  msgstr ""
492
495
 
493
- #: src/ring/tui.py:551
496
+ #: src/ring/tui.py:568
494
497
  #, python-brace-format
495
498
  msgid "→ {project}({where})"
496
499
  msgstr ""
497
500
 
498
- #: src/ring/tui.py:561
501
+ #: src/ring/tui.py:578
499
502
  msgid "(沒有正在等待的 session)"
500
503
  msgstr ""
501
504
 
502
- #: src/ring/tui.py:588
505
+ #: src/ring/tui.py:605
503
506
  #, python-brace-format
504
507
  msgid "{project}:抓不到這個 session 的畫面(不在 tmux,也不是可控的 iTerm2/kitty 視窗),無法就地回覆"
505
508
  msgstr ""
506
509
 
507
- #: src/ring/tui.py:596
510
+ #: src/ring/tui.py:613
508
511
  #, python-brace-format
509
512
  msgid "{project}:讀不到 {backend} 畫面"
510
513
  msgstr ""
511
514
 
512
- #: src/ring/tui.py:600
515
+ #: src/ring/tui.py:617
513
516
  #, python-brace-format
514
517
  msgid "{project}:畫面上沒有可辨識的權限對話框,未送出任何按鍵"
515
518
  msgstr ""
516
519
 
517
- #: src/ring/tui.py:611
520
+ #: src/ring/tui.py:628
518
521
  #, python-brace-format
519
522
  msgid "{project}:選項 {number} 是兩位數編號,RiNG 不代按(送出的第一個數字會先選中別的選項);請按 Enter 跳過去自己回"
520
523
  msgstr ""
521
524
 
522
- #: src/ring/tui.py:621
525
+ #: src/ring/tui.py:638
523
526
  #, python-brace-format
524
527
  msgid "… {project}:正在回覆權限"
525
528
  msgstr ""
526
529
 
527
- #: src/ring/tui.py:664
530
+ #: src/ring/tui.py:681
528
531
  #, python-brace-format
529
532
  msgid "→ {project}:已回覆權限({option})"
530
533
  msgstr ""
531
534
 
532
- #: src/ring/tui.py:668
535
+ #: src/ring/tui.py:685
533
536
  #, python-brace-format
534
537
  msgid "{project}:權限對話框已不在,未送出任何按鍵"
535
538
  msgstr ""
536
539
 
537
- #: src/ring/tui.py:669
540
+ #: src/ring/tui.py:686
538
541
  #, python-brace-format
539
542
  msgid "{project}:對話框內容已變化,未送出;請再按一次 p"
540
543
  msgstr ""
541
544
 
542
- #: src/ring/tui.py:670
545
+ #: src/ring/tui.py:687
543
546
  #, python-brace-format
544
547
  msgid "{project}:tmux send-keys 失敗,沒能回覆"
545
548
  msgstr ""
546
549
 
547
- #: src/ring/tui.py:671
550
+ #: src/ring/tui.py:688
548
551
  #, python-brace-format
549
552
  msgid "{project}:已送出但對話框還在,請跳過去確認"
550
553
  msgstr ""
551
554
 
552
- #: src/ring/tui.py:673
555
+ #: src/ring/tui.py:690
553
556
  #, python-brace-format
554
557
  msgid "{project}:對話框已不在,數字落進輸入框,已補 Backspace 清掉"
555
558
  msgstr ""
556
559
 
557
- #: src/ring/tui.py:675
560
+ #: src/ring/tui.py:692
558
561
  #, python-brace-format
559
562
  msgid "{project}:已送出但拿不到畫面驗證,請跳過去確認"
560
563
  msgstr ""
561
564
 
562
- #: src/ring/tui.py:690
565
+ #: src/ring/tui.py:707
563
566
  #, python-brace-format
564
567
  msgid "再按一次 d 隱藏 {project}(有新活動會自動重新出現)"
565
568
  msgstr ""
566
569
 
567
- #: src/ring/tui.py:695
570
+ #: src/ring/tui.py:712
568
571
  #, python-brace-format
569
572
  msgid "{project} 是仍在執行的 process,無法隱藏;請跳過去結束它"
570
573
  msgstr ""
571
574
 
572
- #: src/ring/tui.py:703
575
+ #: src/ring/tui.py:720
573
576
  #, python-brace-format
574
577
  msgid "已隱藏 {project},並清掉 RiNG 狀態;有新活動會自動重新出現"
575
578
  msgstr ""
576
579
 
577
- #: src/ring/tui.py:705
580
+ #: src/ring/tui.py:722
578
581
  #, python-brace-format
579
582
  msgid "已隱藏 {project}(沒有 RiNG registry 可清;有新活動會自動重新出現)"
580
583
  msgstr ""
@@ -639,172 +642,230 @@ msgstr ""
639
642
  msgid "無效的 --since:{value}"
640
643
  msgstr ""
641
644
 
642
- #: src/ring/commands/doctor.py:21
645
+ #: src/ring/commands/doctor.py:29 src/ring/commands/doctor.py:82
646
+ #: src/ring/commands/doctor.py:89 src/ring/commands/doctor.py:116
647
+ #: src/ring/commands/doctor.py:190
648
+ msgid "偵測失敗"
649
+ msgstr ""
650
+
651
+ #: src/ring/commands/doctor.py:34
652
+ msgid "Claude 設定檔"
653
+ msgstr ""
654
+
655
+ #: src/ring/commands/doctor.py:36
656
+ msgid "settings 檔不存在 → 吃 Claude Code 內建預設"
657
+ msgstr ""
658
+
659
+ #: src/ring/commands/doctor.py:37
660
+ #, python-brace-format
661
+ msgid "未設定(={channel})"
662
+ msgstr ""
663
+
664
+ #: src/ring/commands/doctor.py:39
665
+ msgid "目前終端"
666
+ msgstr ""
667
+
668
+ #: src/ring/commands/doctor.py:39
669
+ msgid "認不出(tmux/ssh 裡可能測不到,僅供參考)"
670
+ msgstr ""
671
+
672
+ #: src/ring/commands/doctor.py:42
673
+ msgid "已關閉——只有 RiNG 會通知你"
674
+ msgstr ""
675
+
676
+ #: src/ring/commands/doctor.py:45
677
+ msgid "只響鈴、不跳通知框;RiNG 的通知不受影響"
678
+ msgstr ""
679
+
680
+ #: src/ring/commands/doctor.py:48
681
+ msgid "Claude Code 也會自己跳通知——權限提示會跟 RiNG 重複"
682
+ msgstr ""
683
+
684
+ #: src/ring/commands/doctor.py:50
685
+ msgid "認不出這個通道會不會跳通知;沒明確關掉就有重複的可能"
686
+ msgstr ""
687
+
688
+ #: src/ring/commands/doctor.py:51
689
+ msgid "另外"
690
+ msgstr ""
691
+
692
+ #: src/ring/commands/doctor.py:51
693
+ msgid "它還會在 Stop 後約 60 秒發 idle_prompt(閒著、換你),這類 RiNG 刻意不發"
694
+ msgstr ""
695
+
696
+ #: src/ring/commands/doctor.py:52
697
+ msgid "只想留 RiNG"
698
+ msgstr ""
699
+
700
+ #: src/ring/commands/doctor.py:52
701
+ msgid "把 preferredNotifChannel 設成 notifications_disabled"
702
+ msgstr ""
703
+
704
+ #: src/ring/commands/doctor.py:59
643
705
  msgid "用法:ring doctor"
644
706
  msgstr ""
645
707
 
646
- #: src/ring/commands/doctor.py:31
708
+ #: src/ring/commands/doctor.py:69
647
709
  msgid "RiNG 環境診斷"
648
710
  msgstr ""
649
711
 
650
- #: src/ring/commands/doctor.py:32
712
+ #: src/ring/commands/doctor.py:70
651
713
  msgid "唯讀檢查,不會改動任何設定"
652
714
  msgstr ""
653
715
 
654
- #: src/ring/commands/doctor.py:35
716
+ #: src/ring/commands/doctor.py:73
655
717
  msgid "Session 來源"
656
718
  msgstr ""
657
719
 
658
- #: src/ring/commands/doctor.py:44 src/ring/commands/doctor.py:51
659
- #: src/ring/commands/doctor.py:78 src/ring/commands/doctor.py:148
660
- msgid "偵測失敗"
661
- msgstr ""
662
-
663
- #: src/ring/commands/doctor.py:47
720
+ #: src/ring/commands/doctor.py:85
664
721
  msgid "活著"
665
722
  msgstr ""
666
723
 
667
- #: src/ring/commands/doctor.py:48
724
+ #: src/ring/commands/doctor.py:86
668
725
  #, python-brace-format
669
726
  msgid "偵測到 {n} 個 session"
670
727
  msgstr ""
671
728
 
672
- #: src/ring/commands/doctor.py:54
729
+ #: src/ring/commands/doctor.py:92
673
730
  msgid "Hook 安裝"
674
731
  msgstr ""
675
732
 
676
- #: src/ring/commands/doctor.py:61
733
+ #: src/ring/commands/doctor.py:99
677
734
  msgid "未使用 Codex(zero-config)"
678
735
  msgstr ""
679
736
 
680
- #: src/ring/commands/doctor.py:63
737
+ #: src/ring/commands/doctor.py:101
681
738
  msgid "已安裝"
682
739
  msgstr ""
683
740
 
684
- #: src/ring/commands/doctor.py:65
741
+ #: src/ring/commands/doctor.py:103
685
742
  msgid "未安裝(執行 ring install-hooks)"
686
743
  msgstr ""
687
744
 
688
- #: src/ring/commands/doctor.py:69
745
+ #: src/ring/commands/doctor.py:107
689
746
  msgid "Hook 心跳偵測"
690
747
  msgstr ""
691
748
 
692
- #: src/ring/commands/doctor.py:74
749
+ #: src/ring/commands/doctor.py:112
693
750
  msgid "可能失效"
694
751
  msgstr ""
695
752
 
696
- #: src/ring/commands/doctor.py:74
753
+ #: src/ring/commands/doctor.py:112
697
754
  #, python-brace-format
698
755
  msgid "{n} 個 session"
699
756
  msgstr ""
700
757
 
701
- #: src/ring/commands/doctor.py:76
758
+ #: src/ring/commands/doctor.py:114
702
759
  msgid "正常"
703
760
  msgstr ""
704
761
 
705
- #: src/ring/commands/doctor.py:81
762
+ #: src/ring/commands/doctor.py:119
706
763
  msgid "通知後端"
707
764
  msgstr ""
708
765
 
709
- #: src/ring/commands/doctor.py:82
766
+ #: src/ring/commands/doctor.py:120
710
767
  msgid "目前設定"
711
768
  msgstr ""
712
769
 
713
- #: src/ring/commands/doctor.py:86 src/ring/commands/doctor.py:110
714
- #: src/ring/commands/doctor.py:113 src/ring/commands/doctor.py:120
715
- #: src/ring/commands/doctor.py:127 src/ring/commands/doctor.py:134
770
+ #: src/ring/commands/doctor.py:124 src/ring/commands/doctor.py:152
771
+ #: src/ring/commands/doctor.py:155 src/ring/commands/doctor.py:162
772
+ #: src/ring/commands/doctor.py:169 src/ring/commands/doctor.py:176
716
773
  msgid "可用"
717
774
  msgstr ""
718
775
 
719
- #: src/ring/commands/doctor.py:86
776
+ #: src/ring/commands/doctor.py:124
720
777
  msgid "不可用"
721
778
  msgstr ""
722
779
 
723
- #: src/ring/commands/doctor.py:90 src/ring/commands/doctor.py:100
780
+ #: src/ring/commands/doctor.py:128 src/ring/commands/doctor.py:138
724
781
  msgid "auto 實際選中"
725
782
  msgstr ""
726
783
 
727
- #: src/ring/commands/doctor.py:92
784
+ #: src/ring/commands/doctor.py:130
728
785
  msgid "macOS 提醒:若只聽到聲音但沒有通知框,請到系統設定的通知項目啟用 Banner/Alert。"
729
786
  msgstr ""
730
787
 
731
- #: src/ring/commands/doctor.py:95
788
+ #: src/ring/commands/doctor.py:133
732
789
  msgid "backend=none"
733
790
  msgstr ""
734
791
 
735
- #: src/ring/commands/doctor.py:97
792
+ #: src/ring/commands/doctor.py:135
736
793
  msgid "agent-hooks 已接手"
737
794
  msgstr ""
738
795
 
739
- #: src/ring/commands/doctor.py:99
796
+ #: src/ring/commands/doctor.py:137
740
797
  msgid "全部不可用"
741
798
  msgstr ""
742
799
 
743
- #: src/ring/commands/doctor.py:100
800
+ #: src/ring/commands/doctor.py:138
744
801
  msgid "不發通知"
745
802
  msgstr ""
746
803
 
747
- #: src/ring/commands/doctor.py:103
804
+ #: src/ring/commands/doctor.py:141
805
+ msgid "Claude Code 自帶通知"
806
+ msgstr ""
807
+
808
+ #: src/ring/commands/doctor.py:145
748
809
  msgid "聚焦終端(focuser)"
749
810
  msgstr ""
750
811
 
751
- #: src/ring/commands/doctor.py:110
812
+ #: src/ring/commands/doctor.py:152
752
813
  msgid "不可用(nvim 不在 PATH)"
753
814
  msgstr ""
754
815
 
755
- #: src/ring/commands/doctor.py:113
816
+ #: src/ring/commands/doctor.py:155
756
817
  msgid "不可用(tmux 不在 PATH)"
757
818
  msgstr ""
758
819
 
759
- #: src/ring/commands/doctor.py:116
820
+ #: src/ring/commands/doctor.py:158
760
821
  msgid "不可用(kitty 不在 PATH)"
761
822
  msgstr ""
762
823
 
763
- #: src/ring/commands/doctor.py:118
824
+ #: src/ring/commands/doctor.py:160
764
825
  msgid ""
765
826
  "不可用(kitty remote control 沒開:kitty.conf 要設 allow_remote_control + "
766
827
  "listen_on)"
767
828
  msgstr ""
768
829
 
769
- #: src/ring/commands/doctor.py:123
830
+ #: src/ring/commands/doctor.py:165
770
831
  msgid "不可用(非 Linux)"
771
832
  msgstr ""
772
833
 
773
- #: src/ring/commands/doctor.py:125
834
+ #: src/ring/commands/doctor.py:167
774
835
  msgid "不可用(wmctrl 不在 PATH)"
775
836
  msgstr ""
776
837
 
777
- #: src/ring/commands/doctor.py:129
838
+ #: src/ring/commands/doctor.py:171
778
839
  msgid "不可用(osascript 不在 PATH)"
779
840
  msgstr ""
780
841
 
781
- #: src/ring/commands/doctor.py:134 src/ring/commands/doctor.py:136
842
+ #: src/ring/commands/doctor.py:176 src/ring/commands/doctor.py:178
782
843
  msgid "不可用(app 沒在跑)"
783
844
  msgstr ""
784
845
 
785
- #: src/ring/commands/doctor.py:140
846
+ #: src/ring/commands/doctor.py:182
786
847
  msgid "維護"
787
848
  msgstr ""
788
849
 
789
- #: src/ring/commands/doctor.py:144 src/ring/commands/doctor.py:146
790
- #: src/ring/commands/doctor.py:148
850
+ #: src/ring/commands/doctor.py:186 src/ring/commands/doctor.py:188
851
+ #: src/ring/commands/doctor.py:190
791
852
  msgid "可清理"
792
853
  msgstr ""
793
854
 
794
- #: src/ring/commands/doctor.py:144
855
+ #: src/ring/commands/doctor.py:186
795
856
  #, python-brace-format
796
857
  msgid "{n} 個 RiNG stale 狀態檔(執行 ring gc --dry-run 預覽)"
797
858
  msgstr ""
798
859
 
799
- #: src/ring/commands/doctor.py:146
860
+ #: src/ring/commands/doctor.py:188
800
861
  msgid "沒有 RiNG stale 狀態檔"
801
862
  msgstr ""
802
863
 
803
- #: src/ring/commands/doctor.py:151
864
+ #: src/ring/commands/doctor.py:193
804
865
  msgid "設定檔"
805
866
  msgstr ""
806
867
 
807
- #: src/ring/commands/doctor.py:155
868
+ #: src/ring/commands/doctor.py:197
808
869
  msgid "完整生效值請看 `ring config`。"
809
870
  msgstr ""
810
871
 
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