mycode-cli 0.19.1__py3-none-any.whl → 0.19.2__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.
mycode_cli/sessions.py CHANGED
@@ -96,9 +96,14 @@ def derive_title(text: str) -> str:
96
96
  return title or DEFAULT_SESSION_TITLE
97
97
 
98
98
 
99
+ @dataclass
99
100
  class SessionStore(TimelineStore):
100
101
  """Session catalog and timeline façade used by the TUI and web server."""
101
102
 
103
+ # Search cache derived from ``messages.jsonl``: session_id -> ((mtime_ns, size),
104
+ # whitespace-collapsed text of each visible user/assistant message).
105
+ _search_texts: dict[str, tuple[tuple[int, int], list[str]]] = field(default_factory=dict, init=False, repr=False)
106
+
102
107
  # ------------------------------------------------------------------
103
108
  # Catalog I/O
104
109
  # ------------------------------------------------------------------
@@ -182,6 +187,7 @@ class SessionStore(TimelineStore):
182
187
  async def delete_session(self, session_id: str) -> None:
183
188
  """Delete the whole session directory: catalog, timeline, tool output."""
184
189
 
190
+ self._search_texts.pop(session_id, None)
185
191
  await asyncio.to_thread(shutil.rmtree, self.session_dir(session_id), True)
186
192
 
187
193
  # ------------------------------------------------------------------
@@ -239,6 +245,27 @@ class SessionStore(TimelineStore):
239
245
 
240
246
  return await asyncio.to_thread(load)
241
247
 
248
+ def _searchable_texts(self, session_id: str) -> list[str]:
249
+ """Visible user/assistant text per message, cached until the timeline file changes."""
250
+
251
+ try:
252
+ stat = self.messages_path(session_id).stat()
253
+ except FileNotFoundError:
254
+ return []
255
+ key = (stat.st_mtime_ns, stat.st_size)
256
+ cached = self._search_texts.get(session_id)
257
+ if cached is not None and cached[0] == key:
258
+ return cached[1]
259
+ # The key was taken before the read: a concurrent append leaves a stale
260
+ # key behind, so the next search re-reads the file.
261
+ texts = [
262
+ " ".join(flatten_message_text(message, include_thinking=False).split())
263
+ for message in self.load_messages_sync(session_id)
264
+ if message.get("role") in {"user", "assistant"}
265
+ ]
266
+ self._search_texts[session_id] = (key, texts)
267
+ return texts
268
+
242
269
  async def search_sessions(self, query: str, *, cwd: str | None = None, limit: int = 50) -> list[SessionSearchHit]:
243
270
  """Find sessions whose title or visible user/assistant text contains
244
271
  ``query`` (case-insensitive), newest first.
@@ -257,10 +284,7 @@ class SessionStore(TimelineStore):
257
284
  hits: list[SessionSearchHit] = []
258
285
  for session in sessions:
259
286
  snippet: SessionSnippet | None = None
260
- for message in self.load_messages_sync(str(session["id"])):
261
- if message.get("role") not in {"user", "assistant"}:
262
- continue
263
- text = " ".join(flatten_message_text(message, include_thinking=False).split())
287
+ for text in self._searchable_texts(str(session["id"])):
264
288
  if found := pattern.search(text):
265
289
  start, end = found.span()
266
290
  snippet = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mycode-cli
3
- Version: 0.19.1
3
+ Version: 0.19.2
4
4
  Summary: Minimal coding agent CLI and web UI built on mycode-sdk.
5
5
  Project-URL: Homepage, https://github.com/legibet/mycode
6
6
  Project-URL: Repository, https://github.com/legibet/mycode
@@ -25,7 +25,7 @@ Requires-Dist: beautifulsoup4>=4.15.0
25
25
  Requires-Dist: fastapi>=0.139.0
26
26
  Requires-Dist: httpx2>=2.10.0
27
27
  Requires-Dist: markdownify>=1.2.3
28
- Requires-Dist: mycode-sdk==0.19.1
28
+ Requires-Dist: mycode-sdk==0.19.2
29
29
  Requires-Dist: prompt-toolkit>=3.0.52
30
30
  Requires-Dist: pydantic>=2.13.4
31
31
  Requires-Dist: pygments>=2.20.0
@@ -3,7 +3,7 @@ mycode_cli/config.py,sha256=LPMUDoqOsO3q0ieoMmjP5DGBQD0ZHZCsHUWGFtDnK7I,29763
3
3
  mycode_cli/main.py,sha256=nrGjdXoQ5934lIPHboeZaS8orZRSpS9zfE8xoDKooI8,11737
4
4
  mycode_cli/permissions.py,sha256=GESSo6YxMLudE_WI_XtAHHih4sgREsIlNnXirkoR1G4,7765
5
5
  mycode_cli/runtime.py,sha256=Y0m5Va3YTnviomvpJASQrkJdu0D5-2-tOoDZSG0Eizk,2645
6
- mycode_cli/sessions.py,sha256=bnavfbaQRdWfAcFvb4IMha9tej95RhlBbIrCZ_XaRG4,10391
6
+ mycode_cli/sessions.py,sha256=7kFAiZANUa9ppKtjjijrP-Y0v1prVmS_HMnaOpDUYSo,11435
7
7
  mycode_cli/state.py,sha256=gmwQjSJkNtmxVw8G_CrGtoJorIyb59ngWz7cDhB-aiM,3491
8
8
  mycode_cli/system_prompt.py,sha256=Kke1oaCQvXNcgTvhoj8IYvxSVmlaclRai7_xzOw_Ubk,12831
9
9
  mycode_cli/tools.py,sha256=LhFa11nfrZclUdTiC6NnxJoGsS8jSkueg4K448dEKjk,28999
@@ -404,8 +404,8 @@ mycode_cli/tui/markdown.py,sha256=Rv4beSsutETXCiD7Ve-LpEC5-57doftTcuQuiP-llSI,14
404
404
  mycode_cli/tui/render.py,sha256=H-B685F5wdS8mw5hfVW8s_6CDrYnqDmeDRV0zSeGtYo,27237
405
405
  mycode_cli/tui/terminal.py,sha256=sSkGFIX7ey3DHjwrQorZQU2Kp4m3zx54EzPcZngwnWw,26441
406
406
  mycode_cli/tui/theme.py,sha256=zEht8ND4KUI6M3hD35bKcBwj5mXwUny092lSX3PogeU,3642
407
- mycode_cli-0.19.1.dist-info/METADATA,sha256=Z1Su2F-Q5R1q5okDBg6KxWqP9UcEsgg9JI8c49p_4EE,7432
408
- mycode_cli-0.19.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
409
- mycode_cli-0.19.1.dist-info/entry_points.txt,sha256=1R7YN8dxJUy5AMC4q2J5Ly9xuEom3ZbTaa8xXwxuZyk,48
410
- mycode_cli-0.19.1.dist-info/licenses/LICENSE,sha256=DSE6T-B9WhrWypr6H7y2kTZzoF1tef5xf7veUyImQsM,1064
411
- mycode_cli-0.19.1.dist-info/RECORD,,
407
+ mycode_cli-0.19.2.dist-info/METADATA,sha256=BtErMGIr3A4LvlItHbiYQ21hqfcKTdJnm2TioXYVwEY,7432
408
+ mycode_cli-0.19.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
409
+ mycode_cli-0.19.2.dist-info/entry_points.txt,sha256=1R7YN8dxJUy5AMC4q2J5Ly9xuEom3ZbTaa8xXwxuZyk,48
410
+ mycode_cli-0.19.2.dist-info/licenses/LICENSE,sha256=DSE6T-B9WhrWypr6H7y2kTZzoF1tef5xf7veUyImQsM,1064
411
+ mycode_cli-0.19.2.dist-info/RECORD,,