cc-transcript 10.7.0__tar.gz → 10.8.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 (79) hide show
  1. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/PKG-INFO +1 -1
  2. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/discovery.py +20 -1
  3. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/judge/verdicts.py +12 -2
  4. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/pyproject.toml +1 -1
  5. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/Cargo.lock +0 -0
  6. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/Cargo.toml +0 -0
  7. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/LICENSE +0 -0
  8. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/README.md +0 -0
  9. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/__init__.py +0 -0
  10. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/__main__.py +0 -0
  11. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/_parser_rs.pyi +0 -0
  12. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/activity.py +0 -0
  13. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/activity_probe.py +0 -0
  14. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/backend.py +0 -0
  15. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/builders.py +0 -0
  16. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/cli.py +0 -0
  17. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/command.py +0 -0
  18. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/context.py +0 -0
  19. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/corrections.py +0 -0
  20. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/corrections_cli.py +0 -0
  21. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/cost.py +0 -0
  22. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/decisions.py +0 -0
  23. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/disktruth.py +0 -0
  24. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/evidence.py +0 -0
  25. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/extract/__init__.py +0 -0
  26. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/extract/correct.py +0 -0
  27. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/facts.py +0 -0
  28. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/filterspec.py +0 -0
  29. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/ids.py +0 -0
  30. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/judge/__init__.py +0 -0
  31. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/judge/llm.py +0 -0
  32. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/judge/similar.py +0 -0
  33. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/ledger.py +0 -0
  34. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/__init__.py +0 -0
  35. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/candidates.py +0 -0
  36. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/confidence.py +0 -0
  37. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/engine.py +0 -0
  38. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/filterspec.py +0 -0
  39. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/formats.py +0 -0
  40. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/signals.py +0 -0
  41. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/sourcekind.py +0 -0
  42. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/spec.py +0 -0
  43. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/mining/store.py +0 -0
  44. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/models.py +0 -0
  45. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/notifications.py +0 -0
  46. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/parser.py +0 -0
  47. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/py.typed +0 -0
  48. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/query.py +0 -0
  49. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/render.py +0 -0
  50. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/rust.py +0 -0
  51. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/sentiment/__init__.py +0 -0
  52. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/sentiment/buckets.py +0 -0
  53. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/sentiment/engine.py +0 -0
  54. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/sentiment/lexicon.py +0 -0
  55. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/sentiment/scorespec.py +0 -0
  56. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/store.py +0 -0
  57. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/cc_transcript/tools.py +0 -0
  58. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/Cargo.toml +0 -0
  59. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/build.rs +0 -0
  60. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/data/afinn-en-165.tsv +0 -0
  61. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/data/domain_overrides.tsv +0 -0
  62. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/activity.rs +0 -0
  63. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/command.rs +0 -0
  64. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/event.rs +0 -0
  65. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/filter.rs +0 -0
  66. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/generated/command.rs +0 -0
  67. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/generated/mining.rs +0 -0
  68. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/generated/mod.rs +0 -0
  69. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/generated/protocol.rs +0 -0
  70. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/lexicon.rs +0 -0
  71. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/lib.rs +0 -0
  72. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/mining.rs +0 -0
  73. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/model.rs +0 -0
  74. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/parse.rs +0 -0
  75. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/protocol.rs +0 -0
  76. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/python.rs +0 -0
  77. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/score.rs +0 -0
  78. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/types.rs +0 -0
  79. {cc_transcript-10.7.0 → cc_transcript-10.8.0}/rust/src/value.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-transcript
3
- Version: 10.7.0
3
+ Version: 10.8.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -14,6 +14,15 @@ if TYPE_CHECKING:
14
14
 
15
15
  CLAUDE_PROJECTS_DIR = Path.home() / ".claude" / "projects"
16
16
 
17
+ TRANSCRIPT_MEMO: dict[tuple[SessionId, Path], Path] = {}
18
+ """Positive-hit memo for :func:`find_transcript_sync`, keyed by ``(session_id, resolved root)``.
19
+
20
+ Only successful lookups are cached: a miss may become a hit once Claude Code
21
+ writes the file, so ``None`` is never stored. A cached hit is revalidated with
22
+ ``Path.exists`` before it is reused — a transcript file can be pruned from disk —
23
+ and a stale entry falls through to a fresh scan.
24
+ """
25
+
17
26
 
18
27
  class TranscriptExpiredError(RuntimeError):
19
28
  """A session's transcript file is gone from disk.
@@ -106,10 +115,18 @@ def find_transcript_sync(session_id: SessionId, *, root: Path | None = None) ->
106
115
  :data:`CLAUDE_PROJECTS_DIR`), resolving symlinks — cc-pool gives one
107
116
  transcript several path spellings — and deduping by resolved real path.
108
117
 
118
+ A successful lookup is memoized in ``TRANSCRIPT_MEMO`` under
119
+ ``(session_id, resolved root)`` and revalidated on the next hit, so a
120
+ repeated probe skips the recursive glob while a pruned transcript still
121
+ forces a fresh scan.
122
+
109
123
  Returns:
110
124
  The newest-mtime real path, or None when no transcript exists.
111
125
  """
112
126
  base = root or CLAUDE_PROJECTS_DIR
127
+ key = (session_id, base.resolve())
128
+ if (cached := TRANSCRIPT_MEMO.get(key)) is not None and cached.exists():
129
+ return cached
113
130
  if not base.exists():
114
131
  return None
115
132
  candidates: dict[Path, float] = {}
@@ -120,7 +137,9 @@ def find_transcript_sync(session_id: SessionId, *, root: Path | None = None) ->
120
137
  candidates[real] = real.stat().st_mtime
121
138
  except OSError:
122
139
  continue
123
- return max(candidates, key=candidates.__getitem__, default=None)
140
+ if (hit := max(candidates, key=candidates.__getitem__, default=None)) is not None:
141
+ TRANSCRIPT_MEMO[key] = hit
142
+ return hit
124
143
 
125
144
 
126
145
  async def find_transcript(session_id: SessionId, *, root: Path | None = None) -> Path | None:
@@ -282,6 +282,7 @@ class VerdictStoreMixin:
282
282
  prompt_version: int,
283
283
  limit: int | None = None,
284
284
  refresh_summary: bool = False,
285
+ probe_hydration: bool = True,
285
286
  ) -> list[dict[str, object]]:
286
287
  """Returns events lacking a verdict for ``(role, prompt_version)``, unjudged first.
287
288
 
@@ -298,7 +299,15 @@ class VerdictStoreMixin:
298
299
  full fidelity once their windows hydrate again. A summary row
299
300
  whose context window no longer hydrates — its transcript expired
300
301
  or a ref was compacted away — is dropped, so a dead transcript
301
- stops burning the cap.
302
+ stops burning the cap (unless ``probe_hydration`` is False).
303
+ probe_hydration: When True (default), each summary-refresh row is
304
+ probed with a transcript-discovery hydration check and dropped
305
+ when its window no longer hydrates. When False, that per-row
306
+ probe — and the recursive transcript scan behind it — is skipped
307
+ entirely: every summary-refresh row is returned unprobed, so some
308
+ may name a transcript that is no longer discoverable. Only the
309
+ ``refresh_summary`` path probes, so this flag is a no-op
310
+ otherwise. The default preserves the probing behavior exactly.
302
311
 
303
312
  Returns:
304
313
  One dict per event with the columns needed to build its prompt.
@@ -327,7 +336,8 @@ class VerdictStoreMixin:
327
336
  ) as cur:
328
337
  async for raw in cur:
329
338
  row = dict(raw)
330
- if row.pop("verdict_id") is None or await hydratable(str(row["context_json"])):
339
+ fresh = row.pop("verdict_id") is None
340
+ if not probe_hydration or fresh or await hydratable(str(row["context_json"])):
331
341
  kept.append(row)
332
342
  if limit is not None and len(kept) >= limit:
333
343
  break
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cc-transcript"
3
- version = "10.7.0"
3
+ version = "10.8.0"
4
4
  description = "Grep every Claude Code session you've ever run."
5
5
  readme = "README.md"
6
6
  license = "PolyForm-Noncommercial-1.0.0"
File without changes
File without changes