cc-pushback 0.2.0__tar.gz → 0.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 (24) hide show
  1. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/PKG-INFO +2 -2
  2. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/__init__.py +0 -3
  3. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/cli.py +0 -23
  4. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/report.py +4 -4
  5. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/pyproject.toml +2 -2
  6. cc_pushback-0.2.0/cc_pushback/migrate.py +0 -124
  7. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/LICENSE +0 -0
  8. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/README.md +0 -0
  9. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/__main__.py +0 -0
  10. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/claude.py +0 -0
  11. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/dashboard.py +0 -0
  12. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/detectors.py +0 -0
  13. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/enrich.py +0 -0
  14. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/evaluate.py +0 -0
  15. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/formats.py +0 -0
  16. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/golden_triage.json +0 -0
  17. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/models.py +0 -0
  18. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/py.typed +0 -0
  19. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/refine.py +0 -0
  20. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/scan.py +0 -0
  21. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/serve.py +0 -0
  22. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/spec.py +0 -0
  23. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/store.py +0 -0
  24. {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/triage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-pushback
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Learn your pushback style from past Claude Code feedback and code reviews, and replicate it with a language model.
5
5
  Keywords:
6
6
  Author: Yasyf Mohamedali
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3 :: Only
15
15
  Classifier: Typing :: Typed
16
16
  Requires-Dist: anyio>=4.4
17
- Requires-Dist: cc-transcript>=2.0,<3
17
+ Requires-Dist: cc-transcript>=3.0,<4
18
18
  Requires-Dist: click>=8
19
19
  Requires-Dist: fastapi>=0.115
20
20
  Requires-Dist: pydantic>=2
@@ -3,7 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from cc_pushback.detectors import Detector, detect
6
- from cc_pushback.migrate import MigrationReport, migrate_corpus
7
6
  from cc_pushback.models import DedupKey, FeedbackCandidate, SourceKind, dedup_key
8
7
  from cc_pushback.scan import ScanReport, scan
9
8
  from cc_pushback.spec import PUSHBACK_SPEC
@@ -18,10 +17,8 @@ __all__ = [
18
17
  "Detector",
19
18
  "FeedbackCandidate",
20
19
  "FeedbackStore",
21
- "MigrationReport",
22
20
  "ScanReport",
23
21
  "dedup_key",
24
22
  "detect",
25
- "migrate_corpus",
26
23
  "scan",
27
24
  ]
@@ -369,29 +369,6 @@ async def enrich(tier: TModel, limit: int | None, concurrency: int, db: Path | N
369
369
  )
370
370
 
371
371
 
372
- @main.command(name="migrate-corpus")
373
- @click.option(
374
- "--db",
375
- type=click.Path(dir_okay=False, path_type=Path),
376
- default=None,
377
- help="Database path. Defaults to ~/.cc-pushback/feedback.db.",
378
- )
379
- @coro
380
- async def migrate_corpus_(db: Path | None) -> None:
381
- """Convert a pre-2.0 corpus in place to the cc-transcript 2.0 shapes.
382
-
383
- One-time and idempotent: legacy ``context_json`` snapshots become
384
- ``cc-transcript.context/1`` documents (previews only, summary fidelity,
385
- ``origin='migrated'``), the ``event_uuid`` and ``triage.fidelity`` columns
386
- are added, and rows already in the new schema are skipped.
387
- """
388
- from cc_pushback.migrate import migrate_corpus
389
-
390
- async with await FeedbackStore.open(db or FeedbackStore.default_path()) as store:
391
- report = await migrate_corpus(store)
392
- click.echo(f"migrated {report.migrated} rows ({report.skipped} already current)")
393
-
394
-
395
372
  @main.command()
396
373
  @click.option("--jsonl", is_flag=True, help="Emit full pairs as JSON lines for fine-tuning export.")
397
374
  @click.option(
@@ -20,7 +20,7 @@ from pathlib import Path
20
20
  from typing import TYPE_CHECKING
21
21
 
22
22
  from cc_transcript.context import ContextWindow
23
- from cc_transcript.mining import NOISE_FLOOR, effective_confidence
23
+ from cc_transcript.mining import NOISE_FLOOR
24
24
  from cc_transcript.mining.confidence import from_payload
25
25
 
26
26
  from cc_pushback.claude import claude_available, run_claude
@@ -157,7 +157,7 @@ class Sample:
157
157
  window: ContextWindow
158
158
  origin_path: str | None
159
159
  session_id: str | None
160
- signal: CandidateSignal | None = None
160
+ signal: CandidateSignal
161
161
 
162
162
  @classmethod
163
163
  def from_row(cls, row: Mapping[str, object]) -> Sample:
@@ -172,7 +172,7 @@ class Sample:
172
172
  window=ContextWindow.from_json(str(row["context_json"])),
173
173
  origin_path=str(row["origin_path"]) if row["origin_path"] else None,
174
174
  session_id=str(row["session_id"]) if row["session_id"] else None,
175
- signal=from_payload(payload.get("signal")),
175
+ signal=from_payload(payload["signal"]),
176
176
  )
177
177
 
178
178
 
@@ -445,7 +445,7 @@ class Summary:
445
445
 
446
446
 
447
447
  def is_noise(sample: Sample) -> bool:
448
- return effective_confidence(sample.signal) < NOISE_FLOOR
448
+ return sample.signal.confidence < NOISE_FLOOR
449
449
 
450
450
 
451
451
  def project_label(origin_path: str) -> str:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cc-pushback"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Learn your pushback style from past Claude Code feedback and code reviews, and replicate it with a language model."
5
5
  readme = "README.md"
6
6
  license = "PolyForm-Noncommercial-1.0.0"
@@ -18,7 +18,7 @@ classifiers = [
18
18
  requires-python = ">=3.13"
19
19
  dependencies = [
20
20
  "anyio>=4.4",
21
- "cc-transcript>=2.0,<3",
21
+ "cc-transcript>=3.0,<4",
22
22
  "click>=8",
23
23
  "fastapi>=0.115",
24
24
  "pydantic>=2",
@@ -1,124 +0,0 @@
1
- """One-time conversion of a pre-2.0 corpus onto the cc-transcript 2.0 shapes.
2
-
3
- Legacy databases persisted ``context_json`` as the pre-2.0 ``ContextSnapshot``
4
- shape and carried the event uuid in an ``origin_uuid`` column. ``migrate-corpus``
5
- rewrites every legacy row into a ``cc-transcript.context/1`` document — previews
6
- only, labeled summary fidelity, ``origin='migrated'``, ``anchor`` null where the
7
- row has no resolvable uuid — and adds the columns the platform shapes need
8
- (``feedback_events.event_uuid``, ``triage.fidelity``). Idempotent: rows already
9
- in the new schema are skipped.
10
- """
11
-
12
- from __future__ import annotations
13
-
14
- import json
15
- from dataclasses import dataclass
16
- from itertools import zip_longest
17
- from typing import TYPE_CHECKING
18
-
19
- from cc_transcript.context import ContextWindow, SchemaError, TurnRef
20
- from cc_transcript.ids import EventRef, EventUuid, SessionId
21
-
22
- if TYPE_CHECKING:
23
- from collections.abc import Mapping
24
- from typing import Any
25
-
26
- import aiosqlite
27
-
28
- from cc_pushback.store import FeedbackStore
29
-
30
- MIGRATED_PREVIEW_CHARS = 2000
31
-
32
-
33
- @dataclass(frozen=True, slots=True)
34
- class MigrationReport:
35
- """The outcome of one ``migrate-corpus`` pass.
36
-
37
- Attributes:
38
- migrated: How many rows were converted to the new context schema.
39
- skipped: How many rows already carried it.
40
- """
41
-
42
- migrated: int
43
- skipped: int
44
-
45
-
46
- def turn_preview(turn: Mapping[str, Any]) -> str:
47
- tools = "".join(
48
- f"\n {name}({input})" if input else f"\n {name}()"
49
- for name, input in zip_longest(turn["tool_calls"], turn.get("tool_inputs", ()), fillvalue="")
50
- )
51
- return f"{turn['role']}: {turn['text']}{tools}"
52
-
53
-
54
- def migrated_turn(turn: Mapping[str, Any]) -> TurnRef:
55
- return TurnRef(
56
- role="user" if turn["role"] == "user" else "assistant",
57
- refs=(),
58
- preview=turn_preview(turn),
59
- tool_digests=(),
60
- )
61
-
62
-
63
- def window_from_snapshot(context_json: str, anchor: EventRef | None) -> ContextWindow:
64
- data = json.loads(context_json)
65
- return ContextWindow(
66
- anchor=anchor,
67
- before=tuple(migrated_turn(turn) for turn in data["before"]),
68
- trigger=migrated_turn(data["trigger"]) if data["trigger"] else None,
69
- after=tuple(migrated_turn(turn) for turn in data["after"]),
70
- fidelity="summary",
71
- preview_chars=MIGRATED_PREVIEW_CHARS,
72
- origin="migrated",
73
- )
74
-
75
-
76
- def anchor_of(row: Mapping[str, Any]) -> EventRef | None:
77
- match row["session_id"], row["event_uuid"]:
78
- case str() as session, str() as uuid:
79
- return EventRef(SessionId(session), EventUuid(uuid))
80
- case _:
81
- return None
82
-
83
-
84
- async def column_names(conn: aiosqlite.Connection, table: str) -> set[str]:
85
- cur = await conn.execute(f"PRAGMA table_info({table})")
86
- return {str(row["name"]) async for row in cur}
87
-
88
-
89
- async def migrate_corpus(store: FeedbackStore) -> MigrationReport:
90
- """Converts a legacy corpus in place, idempotently.
91
-
92
- Adds the ``event_uuid`` column (backfilled from the legacy ``origin_uuid``)
93
- and the ``triage.fidelity`` column (existing verdicts were judged on baked
94
- summaries, so they default to ``'summary'`` and stay re-judgeable via
95
- ``triage --refresh-summary``), then rewrites every legacy ``context_json``
96
- snapshot into a ``cc-transcript.context/1`` document.
97
-
98
- Args:
99
- store: The open feedback store to migrate.
100
-
101
- Returns:
102
- The pass's migrated/skipped row counts.
103
- """
104
- conn = store.store.conn
105
- if "event_uuid" not in await column_names(conn, "feedback_events"):
106
- await conn.execute("ALTER TABLE feedback_events ADD COLUMN event_uuid TEXT")
107
- await conn.execute("UPDATE feedback_events SET event_uuid = origin_uuid")
108
- if "fidelity" not in await column_names(conn, "triage"):
109
- await conn.execute("ALTER TABLE triage ADD COLUMN fidelity TEXT NOT NULL DEFAULT 'summary'")
110
- cur = await conn.execute("SELECT id, session_id, event_uuid, context_json FROM feedback_events")
111
- rows = [dict(row) async for row in cur]
112
- migrated = skipped = 0
113
- for row in rows:
114
- try:
115
- ContextWindow.from_json(str(row["context_json"]))
116
- except SchemaError:
117
- window = window_from_snapshot(str(row["context_json"]), anchor_of(row))
118
- await conn.execute(
119
- "UPDATE feedback_events SET context_json = ? WHERE id = ?", (window.to_json(), row["id"])
120
- )
121
- migrated += 1
122
- else:
123
- skipped += 1
124
- return MigrationReport(migrated=migrated, skipped=skipped)
File without changes
File without changes