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.
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/PKG-INFO +2 -2
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/__init__.py +0 -3
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/cli.py +0 -23
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/report.py +4 -4
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/pyproject.toml +2 -2
- cc_pushback-0.2.0/cc_pushback/migrate.py +0 -124
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/LICENSE +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/README.md +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/__main__.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/claude.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/dashboard.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/detectors.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/enrich.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/evaluate.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/formats.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/golden_triage.json +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/models.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/py.typed +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/refine.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/scan.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/serve.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/spec.py +0 -0
- {cc_pushback-0.2.0 → cc_pushback-0.3.0}/cc_pushback/store.py +0 -0
- {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.
|
|
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>=
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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>=
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|