cc-sentiment 0.1.1__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 (33) hide show
  1. cc_sentiment-0.1.1/.gitignore +61 -0
  2. cc_sentiment-0.1.1/AGENTS.md +143 -0
  3. cc_sentiment-0.1.1/CLAUDE.md +1 -0
  4. cc_sentiment-0.1.1/LICENSE +21 -0
  5. cc_sentiment-0.1.1/PKG-INFO +84 -0
  6. cc_sentiment-0.1.1/README.md +52 -0
  7. cc_sentiment-0.1.1/cc_sentiment/__init__.py +0 -0
  8. cc_sentiment-0.1.1/cc_sentiment/benchmark.py +367 -0
  9. cc_sentiment-0.1.1/cc_sentiment/cli.py +42 -0
  10. cc_sentiment-0.1.1/cc_sentiment/engines.py +471 -0
  11. cc_sentiment-0.1.1/cc_sentiment/labeled_data.py +436 -0
  12. cc_sentiment-0.1.1/cc_sentiment/models.py +212 -0
  13. cc_sentiment-0.1.1/cc_sentiment/patches/__init__.py +23 -0
  14. cc_sentiment-0.1.1/cc_sentiment/patches/pr999.patch +233 -0
  15. cc_sentiment-0.1.1/cc_sentiment/pipeline.py +200 -0
  16. cc_sentiment-0.1.1/cc_sentiment/sentiment.py +157 -0
  17. cc_sentiment-0.1.1/cc_sentiment/signing.py +235 -0
  18. cc_sentiment-0.1.1/cc_sentiment/transcripts.py +166 -0
  19. cc_sentiment-0.1.1/cc_sentiment/tui.py +1085 -0
  20. cc_sentiment-0.1.1/cc_sentiment/upload.py +94 -0
  21. cc_sentiment-0.1.1/pyproject.toml +72 -0
  22. cc_sentiment-0.1.1/tests/__init__.py +0 -0
  23. cc_sentiment-0.1.1/tests/fixtures/sample_transcript.jsonl +12 -0
  24. cc_sentiment-0.1.1/tests/helpers.py +34 -0
  25. cc_sentiment-0.1.1/tests/test_cli.py +52 -0
  26. cc_sentiment-0.1.1/tests/test_engines.py +220 -0
  27. cc_sentiment-0.1.1/tests/test_models.py +146 -0
  28. cc_sentiment-0.1.1/tests/test_pipeline.py +201 -0
  29. cc_sentiment-0.1.1/tests/test_sentiment.py +149 -0
  30. cc_sentiment-0.1.1/tests/test_signing.py +157 -0
  31. cc_sentiment-0.1.1/tests/test_transcripts.py +217 -0
  32. cc_sentiment-0.1.1/tests/test_tui.py +445 -0
  33. cc_sentiment-0.1.1/tests/test_upload.py +104 -0
@@ -0,0 +1,61 @@
1
+ # macOS
2
+ .DS_Store
3
+ ._*
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ build/
11
+ dist/
12
+ *.egg-info/
13
+ *.egg
14
+ .Python
15
+ .venv/
16
+ venv/
17
+ .env
18
+ .env.*
19
+
20
+ # uv
21
+ uv.lock
22
+
23
+ # pytest / coverage
24
+ .pytest_cache/
25
+ htmlcov/
26
+ .coverage
27
+ .coverage.*
28
+
29
+ # mypy / pyright / ruff
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # Node / Bun
34
+ node_modules/
35
+ .svelte-kit/
36
+ bun.lockb
37
+
38
+ # MLX model artifacts
39
+ *.safetensors
40
+ *.gguf
41
+ *.bin
42
+ *.npz
43
+ models/
44
+ mlx_models/
45
+ .mlx_cache/
46
+
47
+ # Modal
48
+ .modal/
49
+
50
+ # IDE
51
+ .idea/
52
+ .vscode/
53
+ *.swp
54
+ *.swo
55
+
56
+ # Project
57
+ .serena/
58
+ .context/
59
+
60
+ .claude/settings.local.json
61
+ .vercel
@@ -0,0 +1,143 @@
1
+ # client/ — macOS CLI
2
+
3
+ macOS Apple Silicon CLI tool. Discovers Claude Code conversation transcripts, runs them through Gemma 4 via MLX for local sentiment analysis, signs results with GitHub SSH keys, and uploads to the server.
4
+
5
+ ## Tech Stack
6
+
7
+ - **Runtime**: Python 3.12+, macOS Apple Silicon only
8
+ - **ML inference**: MLX (`mlx-lm`) for local Gemma 4 inference on Apple Silicon GPU
9
+ - **Model**: `mlx-community/gemma-4-e4b-it-4bit` (4-bit quantized, ~2.5GB) — also benchmark `unsloth/gemma-4-E4B-it-UD-MLX-4bit`
10
+ - **CLI**: `click` or `typer`
11
+ - **HTTP**: `httpx` for async uploads
12
+ - **Signing**: `ssh-keygen -Y sign` via subprocess
13
+ - **Packaging**: `uv tool install` from pyproject.toml with `[project.scripts]` entry point
14
+
15
+ ## Commands
16
+
17
+ ```bash
18
+ uv sync # Install dependencies
19
+ uv run cc-sentiment scan # Discover and score new transcripts
20
+ uv run cc-sentiment upload # Upload pending scores to server
21
+ uv run cc-sentiment scan --upload # Scan and upload in one step
22
+ uv run cc-sentiment setup # Configure GitHub username, verify SSH keys
23
+ uv run pytest client/ # Run tests
24
+ ```
25
+
26
+ ## Directory Structure (planned)
27
+
28
+ ```
29
+ client/
30
+ ├── pyproject.toml
31
+ ├── cli.py # CLI entry point (click/typer commands)
32
+ ├── transcripts.py # Transcript discovery and parsing
33
+ ├── sentiment.py # MLX inference, prompt construction, score extraction
34
+ ├── signing.py # GitHub SSH key discovery and payload signing
35
+ ├── upload.py # HTTP client for server API
36
+ ├── models.py # Pydantic models for transcripts, scores, payloads
37
+ └── tests/
38
+ ├── test_transcripts.py
39
+ ├── test_sentiment.py
40
+ ├── test_signing.py
41
+ └── fixtures/
42
+ └── sample_transcript.jsonl
43
+ ```
44
+
45
+ ## Transcript Discovery
46
+
47
+ Claude Code stores conversations at `~/.claude/projects/<project-slug>/<uuid>.jsonl`. Each line is a JSON object representing a conversation turn.
48
+
49
+ The client:
50
+ 1. Walks `~/.claude/projects/` recursively for `.jsonl` files
51
+ 2. Tracks already-processed files in `~/.cc-sentiment/state.json`
52
+ 3. Skips files unchanged since last scan (by mtime)
53
+ 4. Parses each JSONL file into a conversation
54
+
55
+ ### JSONL Structure
56
+
57
+ Each line contains (at minimum):
58
+ - `type`: message type (`human`, `assistant`, `tool_use`, `tool_result`, etc.)
59
+ - `message`: the content object
60
+ - Timestamps in conversation metadata
61
+
62
+ Extract user messages (`type: "human"`) as the primary sentiment signal. Error tool results and assistant apologies are secondary signals.
63
+
64
+ ## MLX Inference
65
+
66
+ ### Model Loading
67
+
68
+ ```python
69
+ from mlx_lm import load, generate
70
+
71
+ model, tokenizer = load("mlx-community/gemma-4-e4b-it-4bit")
72
+ ```
73
+
74
+ Model is downloaded once and cached in `~/.cache/huggingface/`.
75
+
76
+ ### Sentiment Prompt
77
+
78
+ Score each conversation on a 1-5 Likert scale:
79
+ - **1** — Deeply frustrated, angry, giving up
80
+ - **2** — Annoyed, things aren't working
81
+ - **3** — Neutral, transactional
82
+ - **4** — Satisfied, things are working
83
+ - **5** — Delighted, impressed, flow state
84
+
85
+ The prompt must:
86
+ - Present the conversation (or representative sample if too long)
87
+ - Ask for a single integer score with brief justification
88
+ - Use structured output (JSON) for reliable extraction
89
+ - Be versioned — prompt changes shift the dataset, so the version is included in uploaded records
90
+
91
+ ### Inference Config
92
+
93
+ - `max_tokens`: 100 (score + short justification)
94
+ - `temperature`: 0.0 (deterministic scoring)
95
+ - Each conversation gets its own inference call (context matters)
96
+
97
+ ## GitHub SSH Signing
98
+
99
+ ### Key Discovery
100
+
101
+ Look for SSH keys in order:
102
+ 1. `~/.ssh/id_ed25519` (preferred)
103
+ 2. `~/.ssh/id_rsa`
104
+ 3. Keys listed in `~/.ssh/config`
105
+
106
+ GitHub username from `git config github.user` or `~/.cc-sentiment/config.toml`.
107
+
108
+ ### Setup Flow
109
+
110
+ `cc-sentiment setup`:
111
+ 1. Ask for GitHub username (or read from git config)
112
+ 2. Fetch `https://github.com/<username>.keys`
113
+ 3. Find matching local private key
114
+ 4. If no match: print instructions for adding an SSH key to GitHub
115
+ 5. Save config to `~/.cc-sentiment/config.toml`
116
+
117
+ ### Signing Protocol
118
+
119
+ ```bash
120
+ echo '<canonical_json>' | ssh-keygen -Y sign -f ~/.ssh/id_ed25519 -n cc-sentiment
121
+ ```
122
+
123
+ Namespace `cc-sentiment` prevents signature reuse across applications. Canonical JSON = sorted keys, compact encoding, no whitespace.
124
+
125
+ ## Upload Protocol
126
+
127
+ 1. Collect pending records (scored but not yet uploaded) from `~/.cc-sentiment/state.json`
128
+ 2. Serialize records to canonical JSON
129
+ 3. Sign with user's SSH key
130
+ 4. `POST` to server `/upload` with signed payload
131
+ 5. On success, mark records as uploaded in state
132
+ 6. On failure, retain for retry on next run
133
+
134
+ ## Style Specifics
135
+
136
+ All rules from root `AGENTS.md` apply, plus:
137
+
138
+ - **MLX isolated in `sentiment.py`.** No MLX imports leak into other modules. Keeps the CLI testable on non-Apple-Silicon (mock sentiment module).
139
+ - **Subprocess calls use explicit argument lists.** Never `shell=True`. Always `subprocess.run(["ssh-keygen", "-Y", "sign", ...])`.
140
+ - **Local state is a single JSON file.** `~/.cc-sentiment/state.json` for processed transcripts and pending uploads. No database for the client.
141
+ - **CLI commands are thin.** Parse args, call library modules, format output. No business logic in CLI handlers.
142
+ - **All network calls through `upload.py`.** Single module owns the HTTP client, base URL, retry logic.
143
+ - **Fail loudly on wrong platform.** If MLX unavailable (not Apple Silicon), crash at import time with a clear message.
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yasyf Mohamedali
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: cc-sentiment
3
+ Version: 0.1.1
4
+ Summary: macOS CLI for Claude Code conversation sentiment analysis
5
+ Project-URL: Homepage, https://sentiments.cc
6
+ Project-URL: Repository, https://github.com/yasyf/cc-sentiment
7
+ Project-URL: Issues, https://github.com/yasyf/cc-sentiment/issues
8
+ Author-email: Yasyf Mohamedali <yasyfm@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: apple-silicon,claude-code,mlx,sentiment
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.13.11
21
+ Requires-Dist: anyio>=4.4
22
+ Requires-Dist: click>=8.1
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: mlx-lm>=0.20
25
+ Requires-Dist: orjson>=3.10
26
+ Requires-Dist: pydantic>=2.7
27
+ Requires-Dist: python-gnupg>=0.5
28
+ Requires-Dist: rich>=13.0
29
+ Requires-Dist: tenacity>=8.0
30
+ Requires-Dist: textual>=3.0
31
+ Description-Content-Type: text/markdown
32
+
33
+ # cc-sentiment
34
+
35
+ A macOS CLI that scores your Claude Code conversations on-device and contributes the numbers to an open dashboard at [sentiments.cc](https://sentiments.cc).
36
+
37
+ Your conversations stay on your Mac. Only anonymous numeric scores are uploaded.
38
+
39
+ ## Install & run
40
+
41
+ The fast path — keeps the [omlx](https://github.com/jundot/omlx) grammar-constrained inference engine:
42
+
43
+ ```bash
44
+ uvx --from https://sentiments.cc/run cc-sentiment
45
+ ```
46
+
47
+ Or from PyPI (falls back to the pure `mlx-lm` engine):
48
+
49
+ ```bash
50
+ uvx cc-sentiment
51
+ ```
52
+
53
+ Requires macOS on Apple Silicon, Python 3.13+, and [uv](https://docs.astral.sh/uv/).
54
+
55
+ The bare command walks you through setup (linking your GitHub account so uploads are attributable), scores your transcripts, and uploads the scores.
56
+
57
+ ## What gets uploaded
58
+
59
+ Only numbers and timestamps. For each 5-minute bucket of a conversation:
60
+
61
+ - Sentiment score (1–5, scored locally by Gemma 4)
62
+ - Read:edit ratio, edits-without-prior-read %, write:edit ratio, tool calls per turn, subagent spawn rate
63
+ - Turn count, thinking present/chars
64
+ - Claude model and Claude Code version
65
+ - Your GitHub handle (so uploads are attributable)
66
+
67
+ Your conversation text, file contents, file paths, and tool inputs/outputs never leave your machine.
68
+
69
+ ## Commands
70
+
71
+ | Command | Description |
72
+ |---------|-------------|
73
+ | `cc-sentiment` | Run the whole flow — set up if needed, then scan and upload |
74
+ | `cc-sentiment setup` | Link your GitHub account for attributable uploads |
75
+ | `cc-sentiment scan --upload` | Score new transcripts and upload |
76
+ | `cc-sentiment scan` | Score transcripts without uploading |
77
+ | `cc-sentiment upload` | Upload previously scored results |
78
+ | `cc-sentiment rescan` | Clear state and re-score everything |
79
+
80
+ ## Links
81
+
82
+ - Dashboard: [sentiments.cc](https://sentiments.cc)
83
+ - Source: [github.com/yasyf/cc-sentiment](https://github.com/yasyf/cc-sentiment)
84
+ - Issues: [github.com/yasyf/cc-sentiment/issues](https://github.com/yasyf/cc-sentiment/issues)
@@ -0,0 +1,52 @@
1
+ # cc-sentiment
2
+
3
+ A macOS CLI that scores your Claude Code conversations on-device and contributes the numbers to an open dashboard at [sentiments.cc](https://sentiments.cc).
4
+
5
+ Your conversations stay on your Mac. Only anonymous numeric scores are uploaded.
6
+
7
+ ## Install & run
8
+
9
+ The fast path — keeps the [omlx](https://github.com/jundot/omlx) grammar-constrained inference engine:
10
+
11
+ ```bash
12
+ uvx --from https://sentiments.cc/run cc-sentiment
13
+ ```
14
+
15
+ Or from PyPI (falls back to the pure `mlx-lm` engine):
16
+
17
+ ```bash
18
+ uvx cc-sentiment
19
+ ```
20
+
21
+ Requires macOS on Apple Silicon, Python 3.13+, and [uv](https://docs.astral.sh/uv/).
22
+
23
+ The bare command walks you through setup (linking your GitHub account so uploads are attributable), scores your transcripts, and uploads the scores.
24
+
25
+ ## What gets uploaded
26
+
27
+ Only numbers and timestamps. For each 5-minute bucket of a conversation:
28
+
29
+ - Sentiment score (1–5, scored locally by Gemma 4)
30
+ - Read:edit ratio, edits-without-prior-read %, write:edit ratio, tool calls per turn, subagent spawn rate
31
+ - Turn count, thinking present/chars
32
+ - Claude model and Claude Code version
33
+ - Your GitHub handle (so uploads are attributable)
34
+
35
+ Your conversation text, file contents, file paths, and tool inputs/outputs never leave your machine.
36
+
37
+ ## Commands
38
+
39
+ | Command | Description |
40
+ |---------|-------------|
41
+ | `cc-sentiment` | Run the whole flow — set up if needed, then scan and upload |
42
+ | `cc-sentiment setup` | Link your GitHub account for attributable uploads |
43
+ | `cc-sentiment scan --upload` | Score new transcripts and upload |
44
+ | `cc-sentiment scan` | Score transcripts without uploading |
45
+ | `cc-sentiment upload` | Upload previously scored results |
46
+ | `cc-sentiment rescan` | Clear state and re-score everything |
47
+
48
+ ## Links
49
+
50
+ - Dashboard: [sentiments.cc](https://sentiments.cc)
51
+ - Source: [github.com/yasyf/cc-sentiment](https://github.com/yasyf/cc-sentiment)
52
+ - Issues: [github.com/yasyf/cc-sentiment/issues](https://github.com/yasyf/cc-sentiment/issues)
File without changes