cc-transcript 10.2.0__tar.gz → 10.3.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 (74) hide show
  1. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/PKG-INFO +1 -1
  2. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/signals.py +1 -1
  3. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/tools.py +23 -9
  4. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/pyproject.toml +1 -1
  5. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/mining.rs +5 -1
  6. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/Cargo.lock +0 -0
  7. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/Cargo.toml +0 -0
  8. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/LICENSE +0 -0
  9. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/README.md +0 -0
  10. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/__init__.py +0 -0
  11. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/__main__.py +0 -0
  12. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/_parser_rs.pyi +0 -0
  13. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/activity.py +0 -0
  14. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/activity_probe.py +0 -0
  15. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/backend.py +0 -0
  16. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/builders.py +0 -0
  17. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/cli.py +0 -0
  18. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/command.py +0 -0
  19. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/context.py +0 -0
  20. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/corrections.py +0 -0
  21. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/corrections_cli.py +0 -0
  22. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/cost.py +0 -0
  23. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/decisions.py +0 -0
  24. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/discovery.py +0 -0
  25. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/disktruth.py +0 -0
  26. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/evidence.py +0 -0
  27. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/extract/__init__.py +0 -0
  28. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/extract/correct.py +0 -0
  29. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/facts.py +0 -0
  30. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/filterspec.py +0 -0
  31. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/ids.py +0 -0
  32. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/judge/__init__.py +0 -0
  33. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/judge/llm.py +0 -0
  34. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/judge/similar.py +0 -0
  35. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/judge/verdicts.py +0 -0
  36. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/__init__.py +0 -0
  37. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/candidates.py +0 -0
  38. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/confidence.py +0 -0
  39. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/engine.py +0 -0
  40. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/filterspec.py +0 -0
  41. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/formats.py +0 -0
  42. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/sourcekind.py +0 -0
  43. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/spec.py +0 -0
  44. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/mining/store.py +0 -0
  45. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/models.py +0 -0
  46. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/notifications.py +0 -0
  47. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/parser.py +0 -0
  48. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/py.typed +0 -0
  49. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/query.py +0 -0
  50. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/render.py +0 -0
  51. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/rust.py +0 -0
  52. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/sentiment/__init__.py +0 -0
  53. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/sentiment/buckets.py +0 -0
  54. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/sentiment/engine.py +0 -0
  55. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/sentiment/lexicon.py +0 -0
  56. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/sentiment/scorespec.py +0 -0
  57. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/cc_transcript/store.py +0 -0
  58. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/Cargo.toml +0 -0
  59. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/build.rs +0 -0
  60. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/data/afinn-en-165.tsv +0 -0
  61. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/data/domain_overrides.tsv +0 -0
  62. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/activity.rs +0 -0
  63. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/command.rs +0 -0
  64. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/event.rs +0 -0
  65. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/filter.rs +0 -0
  66. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/lexicon.rs +0 -0
  67. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/lib.rs +0 -0
  68. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/model.rs +0 -0
  69. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/parse.rs +0 -0
  70. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/protocol.rs +0 -0
  71. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/python.rs +0 -0
  72. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/score.rs +0 -0
  73. {cc_transcript-10.2.0 → cc_transcript-10.3.0}/rust/src/types.rs +0 -0
  74. {cc_transcript-10.2.0 → cc_transcript-10.3.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.2.0
3
+ Version: 10.3.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -132,7 +132,7 @@ def last_edit_index(events: Sequence[TranscriptEvent], index: int, spec: MiningS
132
132
  i
133
133
  for i in range(index - 1, max(index - spec.reentry_lookback, 0) - 1, -1)
134
134
  if isinstance(event := events[i], AssistantEvent)
135
- if any(isinstance(b, ToolUseBlock) and b.name in spec.edit_tools for b in event.blocks)
135
+ if any(isinstance(b, ToolUseBlock) and matches_names(b.name, spec.edit_tools) for b in event.blocks)
136
136
  ),
137
137
  None,
138
138
  )
@@ -32,6 +32,14 @@ TOOL_ALIASES: dict[str, str] = {
32
32
 
33
33
  TOOL_ALIASES_REVERSE: dict[str, str] = {v: k for k, v in TOOL_ALIASES.items()}
34
34
 
35
+ # cc-context routes real file edits through MCP tools whose bare names match no
36
+ # builtin edit gate; alias its write surface to the builtins those gates watch so
37
+ # an edit through the MCP can't slip past a Tool("Edit"/"Write"/"MultiEdit") guard.
38
+ MCP_TOOL_ALIASES: dict[str, str] = {
39
+ "ccx_code_edit": "Edit",
40
+ "ccx_code_replace": "Write",
41
+ }
42
+
35
43
  READ_VERBS = frozenset({"get", "list", "search", "read", "view", "fetch", "query", "describe", "show", "find"})
36
44
 
37
45
 
@@ -482,28 +490,34 @@ def file_path_of(call: ToolCall) -> str | None:
482
490
 
483
491
 
484
492
  def expand_tool_names(spec: str) -> frozenset[str]:
485
- """Expand a pipe-separated tool spec to include both alias spellings."""
486
- return frozenset(
487
- (base := set(spec.split("|")))
488
- | {alias for n in base for alias in (TOOL_ALIASES.get(n), TOOL_ALIASES_REVERSE.get(n)) if alias}
489
- )
493
+ """Expand a pipe-separated tool spec to include alias and MCP bare spellings."""
494
+ base = set(spec.split("|"))
495
+ expanded = base | {alias for n in base for alias in (TOOL_ALIASES.get(n), TOOL_ALIASES_REVERSE.get(n)) if alias}
496
+ return frozenset(expanded | {bare for bare, builtin in MCP_TOOL_ALIASES.items() if builtin in expanded})
490
497
 
491
498
 
492
499
  def matches_names(actual: str, names: Container[str]) -> bool:
493
500
  """Whether ``actual`` is one of ``names``, exactly or as an MCP tool suffix.
494
501
 
495
502
  True when ``actual`` is in ``names``, or when it splits as
496
- ``mcp__<server>__<tool>`` on the first two ``__`` and ``<tool>`` is in
497
- ``names``. ``names`` is taken verbatim no alias closure; pre-expand with
498
- :func:`expand_tool_names` when aliases should match.
503
+ ``mcp__<server>__<tool>`` on the first two ``__`` and ``<tool>`` or its
504
+ :data:`MCP_TOOL_ALIASES` builtin equivalentis in ``names``. That alias
505
+ closes the edit-gate bypass where cc-context's ``ccx_code_edit`` /
506
+ ``ccx_code_replace`` write through the MCP under names no ``Edit``/``Write``
507
+ gate would catch. Harness-rename aliases are not closed over — ``names`` is
508
+ taken verbatim; pre-expand with :func:`expand_tool_names` for those.
499
509
 
500
510
  Example:
501
511
  >>> matches_names("mcp__github__Grep", {"Grep"})
502
512
  True
513
+ >>> matches_names("mcp__cc-context__ccx_code_edit", {"Edit"})
514
+ True
503
515
  >>> matches_names("Execute", {"Bash"})
504
516
  False
505
517
  """
506
- return actual in names or ((mp := mcp_parts(actual)) is not None and mp[1] in names)
518
+ return actual in names or (
519
+ (mp := mcp_parts(actual)) is not None and (mp[1] in names or MCP_TOOL_ALIASES.get(mp[1]) in names)
520
+ )
507
521
 
508
522
 
509
523
  def tool_name_matches(actual: str, spec: str) -> bool:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cc-transcript"
3
- version = "10.2.0"
3
+ version = "10.3.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"
@@ -473,7 +473,7 @@ impl Events {
473
473
  let lo = index.saturating_sub(spec.reentry_lookback);
474
474
  (lo..index)
475
475
  .rev()
476
- .find(|&i| self.entries[i].tool_uses().any(|tu| spec.edit_tools.contains(&tu.name)))
476
+ .find(|&i| self.entries[i].tool_uses().any(|tu| matches_names(&tu.name, &spec.edit_tools)))
477
477
  .map(|i| i as i64)
478
478
  }
479
479
  }
@@ -1316,6 +1316,10 @@ mod tests {
1316
1316
  assert!(matches_names("mcp__conductor__ExitPlanMode", &names));
1317
1317
  assert!(!matches_names("mcp__ExitPlanMode", &names));
1318
1318
  assert!(!matches_names("AskUserQuestion", &names));
1319
+
1320
+ let edit_names: HashSet<String> = ["Edit".to_string(), "ccx_code_edit".to_string()].into();
1321
+ assert!(matches_names("mcp__cc-context__ccx_code_edit", &edit_names));
1322
+ assert!(!matches_names("mcp__cc-context__ccx_code_read", &edit_names));
1319
1323
  }
1320
1324
 
1321
1325
  #[test]
File without changes
File without changes