yee88 0.4.0__py3-none-any.whl → 0.6.1__py3-none-any.whl

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.
@@ -40,6 +40,10 @@ class _ResumeLineProxy:
40
40
  def engine(self) -> str:
41
41
  return self.runner.engine
42
42
 
43
+ @property
44
+ def model(self) -> str | None:
45
+ return self.runner.model
46
+
43
47
  def is_resume_line(self, line: str) -> bool:
44
48
  return self.runner.is_resume_line(line)
45
49
 
@@ -126,7 +130,7 @@ async def _send_runner_unavailable(
126
130
  ) -> None:
127
131
  tracker = ProgressTracker(engine=runner.engine)
128
132
  tracker.set_resume(resume_token)
129
- state = tracker.snapshot(resume_formatter=runner.format_resume)
133
+ state = tracker.snapshot(resume_formatter=runner.format_resume, model=runner.model)
130
134
  message = exec_cfg.presenter.render_final(
131
135
  state,
132
136
  elapsed_s=0.0,
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
4
+ import re
3
5
  from typing import TYPE_CHECKING
4
6
 
5
7
  from ...context import RunContext
@@ -8,7 +10,7 @@ from ..engine_overrides import EngineOverrides, resolve_override_value
8
10
  from ..files import split_command_args
9
11
  from ..topic_state import TopicStateStore
10
12
  from ..topics import _topic_key
11
- from ..types import TelegramIncomingMessage
13
+ from ..types import TelegramCallbackQuery, TelegramIncomingMessage
12
14
  from .overrides import (
13
15
  ENGINE_SOURCE_LABELS,
14
16
  OVERRIDE_SOURCE_LABELS,
@@ -22,12 +24,107 @@ from .reply import make_reply
22
24
  if TYPE_CHECKING:
23
25
  from ..bridge import TelegramBridgeConfig
24
26
 
27
+ # Callback data prefix for model selection
28
+ MODEL_SELECT_CALLBACK_PREFIX = "yee88:model_select:"
29
+
25
30
  MODEL_USAGE = (
26
- "usage: `/model`, `/model set <model>`, "
31
+ "usage: `/model`, `/model status`, `/model set <model>`, "
27
32
  "`/model set <engine> <model>`, or `/model clear [engine]`"
28
33
  )
29
34
 
30
35
 
36
+ async def _get_opencode_models() -> list[str]:
37
+ """Fetch available models from opencode CLI."""
38
+ try:
39
+ proc = await asyncio.create_subprocess_exec(
40
+ "opencode",
41
+ "models",
42
+ stdout=asyncio.subprocess.PIPE,
43
+ stderr=asyncio.subprocess.PIPE,
44
+ )
45
+ stdout, _ = await proc.communicate()
46
+ if proc.returncode != 0:
47
+ return []
48
+ models = stdout.decode().strip().split("\n")
49
+ return [m.strip() for m in models if m.strip()]
50
+ except Exception:
51
+ return []
52
+
53
+
54
+ async def _send_model_selector(
55
+ cfg: TelegramBridgeConfig,
56
+ msg: TelegramIncomingMessage,
57
+ engine: str,
58
+ models: list[str],
59
+ ) -> None:
60
+ """Send a copyable model list for selection."""
61
+ if not models:
62
+ await cfg.bot.send_message(
63
+ chat_id=msg.chat_id,
64
+ text="No models available.",
65
+ reply_to_message_id=msg.message_id,
66
+ message_thread_id=msg.thread_id,
67
+ )
68
+ return
69
+
70
+ header = f"Models for {engine}:\n\n"
71
+ max_len = 4000
72
+ chunks: list[str] = []
73
+ current = header
74
+
75
+ for model in models:
76
+ line = f"<code>/model set {model}</code>\n"
77
+ if len(current) + len(line) > max_len:
78
+ chunks.append(current.rstrip())
79
+ current = line
80
+ else:
81
+ current += line
82
+
83
+ if current.strip():
84
+ chunks.append(current.rstrip())
85
+
86
+ for i, chunk in enumerate(chunks):
87
+ await cfg.bot.send_message(
88
+ chat_id=msg.chat_id,
89
+ text=chunk,
90
+ reply_to_message_id=msg.message_id if i == 0 else None,
91
+ message_thread_id=msg.thread_id,
92
+ parse_mode="HTML",
93
+ )
94
+
95
+
96
+ async def handle_model_select_callback(
97
+ cfg: TelegramBridgeConfig,
98
+ query: TelegramCallbackQuery,
99
+ ) -> None:
100
+ """Handle model selection from inline keyboard."""
101
+ if not query.data:
102
+ return
103
+
104
+ # Parse callback data: yee88:model_select:<engine>:<model>
105
+ prefix = MODEL_SELECT_CALLBACK_PREFIX
106
+ if not query.data.startswith(prefix):
107
+ return
108
+
109
+ data = query.data[len(prefix):]
110
+ if ":" not in data:
111
+ return
112
+
113
+ engine, model = data.split(":", 1)
114
+
115
+ # Answer the callback query
116
+ await cfg.bot.answer_callback_query(
117
+ callback_query_id=query.callback_query_id,
118
+ text=f"Setting model to {model}...",
119
+ )
120
+
121
+ # Send the model set command as a new message
122
+ await cfg.bot.send_message(
123
+ chat_id=query.chat_id,
124
+ text=f"/model set {engine} {model}",
125
+ )
126
+
127
+
31
128
  async def _handle_model_command(
32
129
  cfg: TelegramBridgeConfig,
33
130
  msg: TelegramIncomingMessage,
@@ -46,10 +143,39 @@ async def _handle_model_command(
46
143
  else None
47
144
  )
48
145
  tokens = split_command_args(args_text)
49
- action = tokens[0].lower() if tokens else "show"
146
+ action = tokens[0].lower() if tokens else ""
50
147
  engine_ids = {engine.lower() for engine in cfg.runtime.engine_ids}
51
148
 
52
- if action in {"show", ""}:
149
+ if action == "":
150
+ selection = await resolve_engine_selection(
151
+ cfg,
152
+ msg,
153
+ ambient_context=ambient_context,
154
+ topic_store=topic_store,
155
+ chat_prefs=chat_prefs,
156
+ topic_key=tkey,
157
+ )
158
+ if selection is None:
159
+ return
160
+ engine, _ = selection
161
+
162
+ if engine == "opencode":
163
+ models = await _get_opencode_models()
164
+ opencode_cfg = cfg.runtime.engine_config("opencode")
165
+ model_filter = opencode_cfg.get("model_filter")
166
+ if model_filter and isinstance(model_filter, str):
167
+ try:
168
+ pattern = re.compile(model_filter, re.IGNORECASE)
169
+ models = [m for m in models if pattern.search(m)]
170
+ except re.error:
171
+ pass
172
+ if models:
173
+ await _send_model_selector(cfg, msg, engine, models)
174
+ return
175
+
176
+ action = "status"
177
+
178
+ if action == "status":
53
179
  selection = await resolve_engine_selection(
54
180
  cfg,
55
181
  msg,
@@ -61,6 +187,7 @@ async def _handle_model_command(
61
187
  if selection is None:
62
188
  return
63
189
  engine, engine_source = selection
190
+
64
191
  topic_override = None
65
192
  if tkey is not None and topic_store is not None:
66
193
  topic_override = await topic_store.get_engine_override(
@@ -69,6 +69,14 @@ async def _handle_ctx_command(
69
69
  chat_id=msg.chat_id,
70
70
  ambient_context=ambient,
71
71
  )
72
+ engine = None
73
+ model = None
74
+ if snapshot is not None:
75
+ engine = snapshot.default_engine
76
+ if engine is not None:
77
+ override = await store.get_engine_override(tkey[0], tkey[1], engine)
78
+ if override is not None and override.model is not None:
79
+ model = override.model
72
80
  text = _format_ctx_status(
73
81
  cfg=cfg,
74
82
  runtime=cfg.runtime,
@@ -77,6 +85,8 @@ async def _handle_ctx_command(
77
85
  context_source=resolved.context_source,
78
86
  snapshot=snapshot,
79
87
  chat_project=chat_project,
88
+ engine=engine,
89
+ model=model,
80
90
  )
81
91
  await reply(text=text)
82
92
  return
yee88/telegram/context.py CHANGED
@@ -107,6 +107,8 @@ def _format_ctx_status(
107
107
  context_source: str,
108
108
  snapshot: TopicThreadSnapshot | None,
109
109
  chat_project: str | None,
110
+ engine: str | None = None,
111
+ model: str | None = None,
110
112
  ) -> str:
111
113
  lines = [
112
114
  f"topics: enabled (scope={_topics_scope_label(cfg)})",
@@ -125,6 +127,10 @@ def _format_ctx_status(
125
127
  if snapshot is not None and snapshot.sessions:
126
128
  sessions = ", ".join(sorted(snapshot.sessions))
127
129
  lines.append(f"sessions: {sessions or 'none'}")
130
+ if engine is not None:
131
+ lines.append(f"engine: {engine}")
132
+ if model is not None:
133
+ lines.append(f"model: {model}")
128
134
  return "\n".join(lines)
129
135
 
130
136
 
yee88/telegram/loop.py CHANGED
@@ -6,11 +6,17 @@ from functools import partial
6
6
  from pathlib import Path
7
7
  from typing import TYPE_CHECKING, cast
8
8
 
9
+ import signal
10
+
9
11
  import anyio
10
12
  from anyio.abc import TaskGroup
11
13
 
12
- from ..config import ConfigError
14
+ from ..cli.reload import request_reload
15
+ from ..config import ConfigError, HOME_CONFIG_PATH
13
16
  from ..config_watch import ConfigReload, watch_config as watch_config_changes
17
+ from ..cron.manager import CronManager
18
+ from ..cron.scheduler import CronScheduler
19
+ from ..cron.models import CronJob
14
20
  from ..commands import list_command_ids
15
21
  from ..directives import DirectiveError
16
22
  from ..logging import get_logger
@@ -25,6 +31,10 @@ from ..context import RunContext
25
31
  from ..ids import RESERVED_CHAT_COMMANDS
26
32
  from .bridge import CANCEL_CALLBACK_DATA, TelegramBridgeConfig, send_plain
27
33
  from .commands.cancel import handle_callback_cancel, handle_cancel
34
+ from .commands.model import (
35
+ MODEL_SELECT_CALLBACK_PREFIX,
36
+ handle_model_select_callback,
37
+ )
28
38
  from .commands.file_transfer import FILE_PUT_USAGE
29
39
  from .commands.handlers import (
30
40
  dispatch_command,
@@ -1066,6 +1076,48 @@ async def run_main_loop(
1066
1076
 
1067
1077
  tg.start_soon(run_config_watch)
1068
1078
 
1079
+ if config_path is not None:
1080
+ cron_manager = CronManager(config_path.parent)
1081
+
1082
+ async def _execute_cron_job(job: CronJob) -> None:
1083
+ try:
1084
+ context = RunContext(project=job.project) if job.project else None
1085
+ await run_job(
1086
+ chat_id=cfg.chat_id,
1087
+ user_msg_id=0,
1088
+ text=job.message,
1089
+ resume_token=None,
1090
+ context=context,
1091
+ thread_id=None,
1092
+ force_hide_resume_line=True,
1093
+ )
1094
+ except Exception as exc:
1095
+ logger.error(
1096
+ "cron.job_failed",
1097
+ job_id=job.id,
1098
+ error=str(exc),
1099
+ )
1100
+
1101
+ cron_scheduler = CronScheduler(cron_manager, _execute_cron_job, tg)
1102
+
1103
+ async def run_cron_scheduler() -> None:
1104
+ await cron_scheduler.start()
1105
+
1106
+ tg.start_soon(run_cron_scheduler)
1107
+
1108
+ async def run_signal_watcher() -> None:
1109
+ if not hasattr(signal, "SIGHUP"):
1110
+ return
1111
+ with anyio.open_signal_receiver(signal.SIGHUP) as signals:
1112
+ async for signum in signals:
1113
+ if signum == signal.SIGHUP:
1114
+ logger.info("reload.signal_received", signal="SIGHUP")
1115
+ request_reload()
1116
+ tg.cancel_scope.cancel()
1117
+ return
1118
+
1119
+ tg.start_soon(run_signal_watcher)
1120
+
1069
1121
  def wrap_on_thread_known(
1070
1122
  base_cb: Callable[[ResumeToken, anyio.Event], Awaitable[None]] | None,
1071
1123
  topic_key: tuple[int, int] | None,
@@ -1104,6 +1156,7 @@ async def run_main_loop(
1104
1156
  | None = None,
1105
1157
  engine_override: EngineId | None = None,
1106
1158
  progress_ref: MessageRef | None = None,
1159
+ force_hide_resume_line: bool = False,
1107
1160
  ) -> None:
1108
1161
  topic_key = (
1109
1162
  (chat_id, thread_id)
@@ -1115,7 +1168,7 @@ async def run_main_loop(
1115
1168
  else None
1116
1169
  )
1117
1170
  stateful_mode = topic_key is not None or chat_session_key is not None
1118
- show_resume_line = should_show_resume_line(
1171
+ show_resume_line = False if force_hide_resume_line else should_show_resume_line(
1119
1172
  show_resume_line=cfg.show_resume_line,
1120
1173
  stateful_mode=stateful_mode,
1121
1174
  context=context,
@@ -1808,6 +1861,14 @@ async def run_main_loop(
1808
1861
  state.running_tasks,
1809
1862
  scheduler,
1810
1863
  )
1864
+ elif update.data and update.data.startswith(
1865
+ MODEL_SELECT_CALLBACK_PREFIX
1866
+ ):
1867
+ tg.start_soon(
1868
+ handle_model_select_callback,
1869
+ cfg,
1870
+ update,
1871
+ )
1811
1872
  else:
1812
1873
  tg.start_soon(
1813
1874
  cfg.bot.answer_callback_query,
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import shutil
4
+ import subprocess
4
5
  from contextlib import contextmanager
5
6
  from collections.abc import Awaitable, Callable
6
7
  from dataclasses import dataclass
@@ -44,6 +45,8 @@ from .api_models import User
44
45
  from .client import TelegramClient, TelegramRetryAfter
45
46
  from .topics import _validate_topics_setup_for
46
47
 
48
+ import yee88
49
+
47
50
  __all__ = [
48
51
  "ChatInfo",
49
52
  "check_setup",
@@ -929,6 +932,42 @@ async def step_save_config(ui: UI, svc: Services, state: OnboardingState) -> Non
929
932
  ui.print(Text("✓ setup complete. starting yee88...", style="green"))
930
933
 
931
934
 
935
+ async def step_install_skill(ui: UI, _svc: Services, _state: OnboardingState) -> None:
936
+ install = await ui.confirm(
937
+ "install yee88 skill for AI agents (opencode, claude-code, etc.)?",
938
+ default=True,
939
+ )
940
+ if not install:
941
+ ui.print(" skipped skill installation")
942
+ return
943
+
944
+ skills_path = Path(yee88.__file__).parent / "skills"
945
+ if not skills_path.exists():
946
+ ui.print(f" warning: skills not found at {skills_path}")
947
+ return
948
+
949
+ ui.print(" installing yee88 skill...")
950
+ try:
951
+ result = subprocess.run(
952
+ ["npx", "skills", "add", str(skills_path), "-g", "--all", "-y"],
953
+ capture_output=True,
954
+ text=True,
955
+ timeout=60,
956
+ )
957
+ if result.returncode == 0:
958
+ ui.print(" ✓ skill installed successfully")
959
+ else:
960
+ ui.print(f" warning: skill installation failed (exit code {result.returncode})")
961
+ if result.stderr:
962
+ ui.print(f" error: {result.stderr.strip()}")
963
+ except subprocess.TimeoutExpired:
964
+ ui.print(" warning: skill installation timed out")
965
+ except FileNotFoundError:
966
+ ui.print(" warning: npx not found. install node.js to use skills.")
967
+ except Exception as exc:
968
+ ui.print(f" warning: skill installation failed: {exc}")
969
+
970
+
932
971
  def always_true(_state: OnboardingState) -> bool:
933
972
  return True
934
973
 
@@ -947,6 +986,7 @@ STEPS: list[OnboardingStep] = [
947
986
  OnboardingStep("connect chat", 3, step_capture_chat),
948
987
  OnboardingStep("default engine", 4, step_default_engine),
949
988
  OnboardingStep("save config", 5, step_save_config),
989
+ OnboardingStep("install skill", 6, step_install_skill),
950
990
  ]
951
991
 
952
992
 
@@ -52,6 +52,7 @@ class TransportRuntime:
52
52
  "_allowlist",
53
53
  "_config_path",
54
54
  "_plugin_configs",
55
+ "_engine_configs",
55
56
  "_watch_config",
56
57
  )
57
58
 
@@ -63,6 +64,7 @@ class TransportRuntime:
63
64
  allowlist: Iterable[str] | None = None,
64
65
  config_path: Path | None = None,
65
66
  plugin_configs: Mapping[str, Any] | None = None,
67
+ engine_configs: Mapping[str, Any] | None = None,
66
68
  watch_config: bool = False,
67
69
  ) -> None:
68
70
  self._apply(
@@ -71,6 +73,7 @@ class TransportRuntime:
71
73
  allowlist=allowlist,
72
74
  config_path=config_path,
73
75
  plugin_configs=plugin_configs,
76
+ engine_configs=engine_configs,
74
77
  watch_config=watch_config,
75
78
  )
76
79
 
@@ -82,6 +85,7 @@ class TransportRuntime:
82
85
  allowlist: Iterable[str] | None = None,
83
86
  config_path: Path | None = None,
84
87
  plugin_configs: Mapping[str, Any] | None = None,
88
+ engine_configs: Mapping[str, Any] | None = None,
85
89
  watch_config: bool = False,
86
90
  ) -> None:
87
91
  self._apply(
@@ -90,6 +94,7 @@ class TransportRuntime:
90
94
  allowlist=allowlist,
91
95
  config_path=config_path,
92
96
  plugin_configs=plugin_configs,
97
+ engine_configs=engine_configs,
93
98
  watch_config=watch_config,
94
99
  )
95
100
 
@@ -101,6 +106,7 @@ class TransportRuntime:
101
106
  allowlist: Iterable[str] | None,
102
107
  config_path: Path | None,
103
108
  plugin_configs: Mapping[str, Any] | None,
109
+ engine_configs: Mapping[str, Any] | None,
104
110
  watch_config: bool,
105
111
  ) -> None:
106
112
  self._router = router
@@ -108,6 +114,7 @@ class TransportRuntime:
108
114
  self._allowlist = normalize_allowlist(allowlist)
109
115
  self._config_path = config_path
110
116
  self._plugin_configs = dict(plugin_configs or {})
117
+ self._engine_configs = dict(engine_configs or {})
111
118
  self._watch_config = watch_config
112
119
 
113
120
  @property
@@ -147,6 +154,10 @@ class TransportRuntime:
147
154
  def project_aliases(self) -> tuple[str, ...]:
148
155
  return tuple(project.alias for project in self._projects.projects.values())
149
156
 
157
+ @property
158
+ def projects(self) -> ProjectsConfig:
159
+ return self._projects
160
+
150
161
  @property
151
162
  def allowlist(self) -> set[str] | None:
152
163
  return self._allowlist
@@ -172,6 +183,16 @@ class TransportRuntime:
172
183
  )
173
184
  return dict(raw)
174
185
 
186
+ def engine_config(self, engine_id: str) -> dict[str, Any]:
187
+ if not self._engine_configs:
188
+ return {}
189
+ raw = self._engine_configs.get(engine_id)
190
+ if raw is None:
191
+ return {}
192
+ if not isinstance(raw, dict):
193
+ return {}
194
+ return dict(raw)
195
+
175
196
  def resolve_message(
176
197
  self,
177
198
  *,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yee88
3
- Version: 0.4.0
3
+ Version: 0.6.1
4
4
  Summary: Telegram bridge for Codex, Claude Code, and other agent CLIs.
5
5
  Project-URL: Homepage, https://github.com/banteg/yee88
6
6
  Project-URL: Documentation, https://yee88.dev/
@@ -36,6 +36,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
36
36
  Classifier: Programming Language :: Python :: 3.14
37
37
  Requires-Python: >=3.14
38
38
  Requires-Dist: anyio>=4.12.0
39
+ Requires-Dist: croniter>=2.0.0
39
40
  Requires-Dist: httpx>=0.28.1
40
41
  Requires-Dist: markdown-it-py
41
42
  Requires-Dist: msgspec>=0.20.0
@@ -1,4 +1,4 @@
1
- yee88/__init__.py,sha256=42STGor_9nKYXumfeV5tiyD_M8VdcddX7CEexmibPBk,22
1
+ yee88/__init__.py,sha256=baAcEjLSYFIeNZF51tOMmA_zAMhN8HvKael-UU-Ruec,22
2
2
  yee88/api.py,sha256=SnUoG0xnjgaSyqM9retJS9wd3axBEI8MCOkrBNz4w8A,2876
3
3
  yee88/backends.py,sha256=80ScxPEse-BxVfK0zA6bAaorp2GpTpCWsgX3TTqmumI,543
4
4
  yee88/backends_helpers.py,sha256=d84XilyOzVVCq5akygVbH85JbapjTaSUZKeGcAml15I,368
@@ -13,33 +13,39 @@ yee88/events.py,sha256=1yo6l5D4xFreyPoU1d4L1ckaplPizxpFeJJevP8JDtk,4225
13
13
  yee88/ids.py,sha256=uU2A7x61Xn3_Pj-YgY5-EruoOtwDSXOp2D-XduTqAEo,534
14
14
  yee88/lockfile.py,sha256=xCSsxVQMqnCN2RWFkkDOVg1qm5D-Sb4AMKr6FdP0kbY,4162
15
15
  yee88/logging.py,sha256=aqoIClE5_vlASKXsc9FjrmsmBy-7jLYOomHheSWSadA,8227
16
- yee88/markdown.py,sha256=YCx81lxBp_fchx3SN4Z-0sXNQGY1YjZJWz0m_xtk-uM,9068
16
+ yee88/markdown.py,sha256=__2qyoBXTBZVVD7DgZvjDaj67xQCJHMfU0yQ8jcY1pQ,9144
17
17
  yee88/model.py,sha256=G8Aj2nyTM6_vmHh12vEXlvhNJHR1ZBpsPBKw-DboDbs,1695
18
18
  yee88/plugins.py,sha256=XdvttyxL2j65l88P4yhGTzaoBbkCka2-PPTgJXuH0i4,9150
19
19
  yee88/presenter.py,sha256=rhPIytd2ANoe5S6kGeVvTzsPSQzO5AEMBXK3F_zJEx4,513
20
- yee88/progress.py,sha256=n5HmZcw1dSuDfhrOXiZhLXdGMMSJNFpfZ4NR_2-wOL4,3155
20
+ yee88/progress.py,sha256=tbU81DIqgsQlh9qnPlxio4esqQ16QxwTubBTIGsdRLg,3243
21
21
  yee88/router.py,sha256=pZkFI7OsUYeqjS2JmnL4gmHSPbEg8WF7_mQhzV45BCE,3853
22
- yee88/runner.py,sha256=kaXscM6WBXa8TwDhzEyf2am4V95hKXTzfb78F1l3how,21476
23
- yee88/runner_bridge.py,sha256=XGHpxTvmmk-DfiZCgNztXwboJ5m5z9GPKRK_8MxbRWA,19103
24
- yee88/runtime_loader.py,sha256=i-4IeZqcez1VxWqfXsgmHhakK5g53ecye1kW46Uzc_c,6242
22
+ yee88/runner.py,sha256=a0GZE9anqAnsZeVhzn6QaKl1CmXbMARPSrFlfLV-nac,21498
23
+ yee88/runner_bridge.py,sha256=CdD43GfU1rHktBf2FL9K4IK3wIY4BOFfUwx8XzNC-ck,19707
24
+ yee88/runtime_loader.py,sha256=LP-kuEXb4bG2MXEKHRKdYVYmwDTry3SgByKREk20sGM,6435
25
25
  yee88/scheduler.py,sha256=ss_90rpZ3aKh_GutKhMwn8KWpg5QykkE2r6-ltimExk,5590
26
- yee88/settings.py,sha256=Lmjb2FP42e8dX2lOzJ1PJCQXSM7i7gBeuHG-KT9LTXE,12057
26
+ yee88/settings.py,sha256=VO8kW83zMDba3jT_fcc90TKKmXOqljh41n15KJpxCsw,12063
27
27
  yee88/transport.py,sha256=hzVJJO4mk1CaXgzsxSiMDqYA8xKMPD-SSQKhHDnwlzE,1330
28
- yee88/transport_runtime.py,sha256=QdPEJ2rCeCsbRD_-77ZG74sRa2ws3WLAXptPPgssw2c,10260
28
+ yee88/transport_runtime.py,sha256=3NCcnnHAollwTOwpQzA9pYIF2jKNEZx3qKUIvK3d0Zk,10986
29
29
  yee88/transports.py,sha256=8igVsWgvx4d743SE98LyNuKYaci0-X61IqNiO4U-_Ck,2013
30
30
  yee88/worktrees.py,sha256=KDaT1S0-kQH5v0FmiWgG51jgGviyATVEgfoqRrLmh7Y,3907
31
- yee88/cli/__init__.py,sha256=eAvBJmePEJtwAKqRZy_Hx5O7illthdScMw6ooaFmhgI,6440
31
+ yee88/cli/__init__.py,sha256=vJjSKBL51hRAXWHK0W1P93AKbKniYc8eR25UVY_MctI,6597
32
32
  yee88/cli/config.py,sha256=gTDdaTkJGGooeGECgcOXkpF-Y7Kmqi_Iyum0ghQzazg,9560
33
+ yee88/cli/cron.py,sha256=uvzgkAmMom8sDMjLgdMcB8V7DACmmdoGTiefif1Ol3U,6215
33
34
  yee88/cli/doctor.py,sha256=FPLXk-ZSPZiHtWrncEqborcz4e1dhb3S5sUVcOw-n60,6000
34
35
  yee88/cli/init.py,sha256=y1Z08rh9t4RPhzsgJRtopgd6UvjEPYi9Wfv_t2KlrxI,3761
35
36
  yee88/cli/onboarding_cmd.py,sha256=VNJl9cpfz-Bo7LNpGUIH-VtbOFdATTn1ry21cwtyQQM,4170
36
37
  yee88/cli/plugins.py,sha256=Kxr1FgLIjN6c8il83_cfE9nix0jYPUvI6KXtol3Swow,6319
37
- yee88/cli/run.py,sha256=IlhQjdp83hmXau9RDaZ9MdguIOgDK_SIFVP0dMfQ_AY,13918
38
+ yee88/cli/reload.py,sha256=ays9R2kJ0IQEPGfxb3BY7R2mxbJBmp325dKrqqv1nw8,3552
39
+ yee88/cli/run.py,sha256=qKMJCEwoiNN4Qqt59Gmm1bDQHTboGf8JO73gMeACAck,14197
38
40
  yee88/cli/topic.py,sha256=6j_o0wpHMfkyeyj9wcjU5T5PVXw_cOM0qM5fsMDRiIw,11019
41
+ yee88/cron/__init__.py,sha256=WZN2RcqJD4i6ZPZjI6b_sKI1VxhNGzhDu2Pju5u9J0o,152
42
+ yee88/cron/manager.py,sha256=YNIjzQwIS1fMi83LzZe2pxYSu2uPbey0UKtXa6xUmq8,4050
43
+ yee88/cron/models.py,sha256=IZtUvMBr2_wZbgEWR3U9yQW-QEOMzUybDER8B7Ne8Go,224
44
+ yee88/cron/scheduler.py,sha256=ITNqpF4MXUieuuFBvVHgzOVxxR5bnhGAFYvWTJWy-IU,1922
39
45
  yee88/runners/__init__.py,sha256=McKaMqLXT9dJlgiEwKf6biD0Ns66Fk7SrxwtcP0ZgzI,30
40
46
  yee88/runners/claude.py,sha256=-s88BOSTWxp4e7r6QJgUV3RJsEMEfSjk0Z9OmnZQB9Y,15296
41
47
  yee88/runners/codex.py,sha256=oen08lhgaDdEn52x-GDcL7m4Hiaq9Vz3kb4b8KXh3yc,20956
42
- yee88/runners/mock.py,sha256=zKnxNeFkTWyWXe35wUkm3hwf1vOi_eTXfNwIxaVWIjw,6617
48
+ yee88/runners/mock.py,sha256=pC6AkPFe5mDu3qJOQh_5sewaTxzNDYvslCZP6QbuGPI,6700
43
49
  yee88/runners/opencode.py,sha256=QEvpSSRtP5Uv-kzEp6D-85o2II886IHsEV-Us8vqsjA,15018
44
50
  yee88/runners/pi.py,sha256=PH8xw_IBZzDo1KQ6AUQEPhqVRo-kb5I2OVKJrIuJq0w,16087
45
51
  yee88/runners/run_options.py,sha256=83uU9k5vnBSbk4wT4tIDkzOwMa3qL0KK-tdQgomre6w,915
@@ -49,6 +55,7 @@ yee88/schemas/claude.py,sha256=HqOik1O4u3WcMb4RN2cTVJw6VRYn3EaYj8h5Sevs1XY,5702
49
55
  yee88/schemas/codex.py,sha256=bgIsh35LuaqoOYdTl6BWR-mn-mvh3ouwes_Jn9JMVXg,3412
50
56
  yee88/schemas/opencode.py,sha256=ODhnKXTzxZ_8qaQ7AYqXB7J-XoAjQnXbGMBVTUEM2qY,1175
51
57
  yee88/schemas/pi.py,sha256=e5ETawxk8jrdJbEbeBI_pWQKeCFiBBZLEF-Wo5Fx0XY,2680
58
+ yee88/skills/yee88/SKILL.md,sha256=Bba3kuF2jrj07EVs7oI9D1TXvxkMKzwDRcOHp5sv1Zk,13320
52
59
  yee88/telegram/__init__.py,sha256=hX_Wvu920dNLTDrKlj0fsZFSewOo-_otN26O1UNPNA4,452
53
60
  yee88/telegram/api_models.py,sha256=d3H4o2sRZuFgfZfxF1Y1z_xzzCoOy3bKhMeKggYCW3w,538
54
61
  yee88/telegram/api_schemas.py,sha256=Xj-i68GFSb9Uk6GhU2Etp-gYhAat9DKYcvQN9av1NPQ,3823
@@ -58,12 +65,12 @@ yee88/telegram/chat_prefs.py,sha256=tH7dXIehYHdemN_JOMhB7VuuFOiSBcjKz3FXpfW77Dk,
58
65
  yee88/telegram/chat_sessions.py,sha256=7uL5bGX5BaQbQYTEpkWZVMh6HIImWdR_pPbqf6C4k0A,3736
59
66
  yee88/telegram/client.py,sha256=GqNSCGA0cICaOC3E-qX7E4pcUTeP8edXS9HILYZNZgU,12800
60
67
  yee88/telegram/client_api.py,sha256=6gDCrPQSBHHBhyiXX9cCwvNuQChsFXh0z8BvCgGD8Es,17692
61
- yee88/telegram/context.py,sha256=owY5ZH2iLlS6kGS0DLPLAwNXkbcn34EfdzpWa9eSVR0,4485
68
+ yee88/telegram/context.py,sha256=Hb8-k-YbAjO0EmZ35hA8yyMvl1kozgYHUL1L-lbCglA,4681
62
69
  yee88/telegram/engine_defaults.py,sha256=n6ROkTmP_s-H5AhPz_OdT62oZf0QtZJyFEDjp5gfub4,2594
63
70
  yee88/telegram/engine_overrides.py,sha256=kv2j102VP-Bqzbutd5ApBkjW3LmVwvCYixsFewVXVeY,3122
64
71
  yee88/telegram/files.py,sha256=Cvmw6r_ocSb3jLzJLGVbzr46m8cRU159majJ1-A5lvg,5053
65
- yee88/telegram/loop.py,sha256=ytp6HGoHxnXOhA-yg2bid0wZmxM15cnAK-RSx4qy_Ak,66912
66
- yee88/telegram/onboarding.py,sha256=yf_Q7LtodmHW1FbGFLqyYhvigEeoro9MQvAZf39aY_E,34213
72
+ yee88/telegram/loop.py,sha256=TpsDgEQMv6bJFCwd72lIxzhCe8V8g3p7P_q7_XMI_WM,69363
73
+ yee88/telegram/onboarding.py,sha256=QWYaJT_s2bujDxzKjsZuLytyxs_XFDRuiBrsZGRjoOw,35633
67
74
  yee88/telegram/outbox.py,sha256=OcoRyQ7zmQCXR8ZXEMK2f_7-UMRVRAbBgmJGS1u_lcU,5939
68
75
  yee88/telegram/parsing.py,sha256=5PvIPns1NnKryt3XNxPCp4BpWX1gI8kjKi4VxcQ0W-Q,7429
69
76
  yee88/telegram/render.py,sha256=tZuKUeiG6qBb7S_6Y90MBSuSFRUHVbcL-mXWC1r9fHA,6017
@@ -73,22 +80,22 @@ yee88/telegram/topics.py,sha256=TP9cFkWvbZXaJ_41Gmwv2vO6vmim1c_RkrtMkQ9RiQE,8129
73
80
  yee88/telegram/trigger_mode.py,sha256=4wFjNbEANn45lpctO9uBhjXTGh1K3LOUKWlBEA6m6Ps,2070
74
81
  yee88/telegram/types.py,sha256=7MQCR0_k1zDtF32h3VoRxPZM7IZe9K6nCcy0xW11Lus,1540
75
82
  yee88/telegram/voice.py,sha256=6GsqgVXSCUCXupyS6S235BXXwTp4gSLmYQLBYWAGdfc,3312
76
- yee88/telegram/commands/__init__.py,sha256=lZfpONZyhgsc7L02B6ZS4TWyhTShWNxIyfRQLIKvvo8,285
83
+ yee88/telegram/commands/__init__.py,sha256=XkwjElDI1ur4wjKsi3rE-sA6M4EJ-fiMCJw0tX_pawI,369
77
84
  yee88/telegram/commands/agent.py,sha256=Dtd8N0f3JyJM3gTb1CxQ8-0ez_ZR4DtrnrO9SpKBBQU,6969
78
85
  yee88/telegram/commands/cancel.py,sha256=jE93VjztNETlmAgb7FJX0sLR3O-NHy5XcraUbK13TLs,3884
79
86
  yee88/telegram/commands/dispatch.py,sha256=zcvX0V6_klf5jXqJlBwAK25e83WC9z3-KoRcDbeWre0,3572
80
- yee88/telegram/commands/executor.py,sha256=um6KbvneUpzGu8GPujtYJB-tFvWmN95xvLHVN-Vuj8g,14678
87
+ yee88/telegram/commands/executor.py,sha256=eczs7Ds_S8GVX2_OvdmN23y6zE1fhYTpIAuchfH0eRU,14781
81
88
  yee88/telegram/commands/file_transfer.py,sha256=yfopf_If9f7Scaz4dlUsfcrVvg24QmQdajLzemaENy0,17697
82
89
  yee88/telegram/commands/handlers.py,sha256=2zySjRW49e1iv2QJW6xq2m9j0t-uz9_FCJZpE7NhIDA,1834
83
90
  yee88/telegram/commands/media.py,sha256=drSKTf_BbyvXOGhS9UKwey_243Ic5XissoaxCykpd-c,5045
84
91
  yee88/telegram/commands/menu.py,sha256=AvEgKQUZageBx7TwV2-V_IGhT7AC0qUTtAHjcLIweNY,4466
85
- yee88/telegram/commands/model.py,sha256=P6rW_N6Q8OtjU3md3QUMKIgLskLuvJXB2uKoyk5vtfk,7512
92
+ yee88/telegram/commands/model.py,sha256=S8m-UmwiPsaPjfsCmw8EXfymYZ9Wh0QvN5na6QIYy5w,11177
86
93
  yee88/telegram/commands/overrides.py,sha256=lLlIuCWkKwbS5WlQOOr_Ftv_EvfHR9DhjBpWaf6FBng,4853
87
94
  yee88/telegram/commands/parse.py,sha256=0QVW1TVdBWadLbpJ9lRY3s7W4Cm62JJa9jfAaFHQmXU,887
88
95
  yee88/telegram/commands/plan.py,sha256=iKsaRBS-qIfvAaxik5ZEA_VzAnFwx7aEED8sKXNq1wE,487
89
96
  yee88/telegram/commands/reasoning.py,sha256=UFEJOHm4d0v2jFh5HC3oQGS41NYKbmJHRTaAmu_LiGo,8188
90
97
  yee88/telegram/commands/reply.py,sha256=a3zkNjKzn3qZXEZFXuflX-tdhQKQyhYDqZskMy1nS3o,580
91
- yee88/telegram/commands/topics.py,sha256=A_a6XcA2xXzw9aOpS4_OIEp5XWMGwMZfHBXni3XRg6g,10799
98
+ yee88/telegram/commands/topics.py,sha256=Vs4BsVGRfsUSlZupOiWp7vpESm_0z-4gHOWuHOEtu7U,11207
92
99
  yee88/telegram/commands/trigger.py,sha256=RgB4V7NoFdfDLk83xl3BPTsIIVr5A2zCNstR_5B_mEw,5201
93
100
  yee88/utils/__init__.py,sha256=cV9_7v07Y6XkrUju0lHdO1ia0-Q57NqyFVMaFCg--do,34
94
101
  yee88/utils/git.py,sha256=SVKcPNl2mUrv-cVHZQ5b8QXWKi33e_Jc4_vfCLwagkg,2413
@@ -96,8 +103,8 @@ yee88/utils/json_state.py,sha256=cnSvGbB9zj90GdYSyigId1M0nEx54T3A3CqqhkAm9kQ,524
96
103
  yee88/utils/paths.py,sha256=_Tp-LyFLeyGD0P0agRudLuT1NR_XTIpryxk3OYDJAGQ,1318
97
104
  yee88/utils/streams.py,sha256=TQezA-A5VCNksLOtwsJplfr8vm1xPTXoGxvik8G2NPI,1121
98
105
  yee88/utils/subprocess.py,sha256=2if6IxTZVSB1kDa8SXw3igj3E-zhKB8P4z5MVe-odzY,2169
99
- yee88-0.4.0.dist-info/METADATA,sha256=zAoT-GnNrLa7xPXJyCTdZLCgAbebErg-jg8q-mTj0so,4309
100
- yee88-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
101
- yee88-0.4.0.dist-info/entry_points.txt,sha256=P4MVZ_sZfrHaARVMImNJjoGamP8VDukARWMKfDh20V8,282
102
- yee88-0.4.0.dist-info/licenses/LICENSE,sha256=poyQ59wnbmL3Ox3TiiephfHvUpLvJl0DwLFFgqBDdHY,1063
103
- yee88-0.4.0.dist-info/RECORD,,
106
+ yee88-0.6.1.dist-info/METADATA,sha256=9oKTSX6bIhhmSC0lNs2YjUkiuIjHjvgS3Qj5-mbtAEo,4340
107
+ yee88-0.6.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
108
+ yee88-0.6.1.dist-info/entry_points.txt,sha256=P4MVZ_sZfrHaARVMImNJjoGamP8VDukARWMKfDh20V8,282
109
+ yee88-0.6.1.dist-info/licenses/LICENSE,sha256=poyQ59wnbmL3Ox3TiiephfHvUpLvJl0DwLFFgqBDdHY,1063
110
+ yee88-0.6.1.dist-info/RECORD,,
File without changes