personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Replace black, isort, and flake8 with ruff

Consolidate three separate lint/format tools into one. Ruff handles
formatting, import sorting, and linting with auto-fix support, so
`make format` now fixes issues like unused imports automatically.

- Remove black, isort, flake8 deps (+ 4 transitive); add ruff
- Merge .flake8 config into pyproject.toml [tool.ruff] section
- Update Makefile format/ci targets to use ruff
- Fix two pre-existing unused imports (os, get_facets)
- Correct target-version from py39 to py310

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+175 -311
-3
.flake8
··· 1 - [flake8] 2 - extend-ignore = W503,W504,E501,E203,E402 3 - exclude = .venv,scratch,logs,build,dist,*.egg-info,.mypy_cache,.pytest_cache,__pycache__
+1 -3
AGENTS.md
··· 97 97 ## Coding Standards & Style 98 98 99 99 ### Language & Tools 100 - * **Black** (`make format`) - Code formatting 101 - * **isort** (`make format`) - Import sorting 102 - * **flake8** (`make lint`) - Linting 100 + * **Ruff** (`make format`) - Formatting, linting, and import sorting 103 101 * **mypy** (`make check`) - Type checking 104 102 * Configuration in `pyproject.toml` 105 103
+12 -16
Makefile
··· 106 106 107 107 # Venv tool shortcuts 108 108 PYTEST := $(VENV_BIN)/pytest 109 - BLACK := $(VENV_BIN)/black 110 - ISORT := $(VENV_BIN)/isort 111 - FLAKE8 := $(VENV_BIN)/flake8 109 + RUFF := $(VENV_BIN)/ruff 112 110 MYPY := $(VENV_BIN)/mypy 113 111 114 112 # Run core tests (excluding integration and app tests) ··· 159 157 @echo "Running all tests (core + apps + integration)..." 160 158 $(TEST_ENV) $(PYTEST) tests/ -v --cov=. && $(TEST_ENV) $(PYTEST) apps/ -v --cov=. --cov-append 161 159 162 - # Auto-format code, then report any remaining issues 163 - # Linting config: .flake8 | Formatting config: pyproject.toml [tool.black] / [tool.isort] 160 + # Auto-format and fix code, then report any remaining issues 164 161 format: .installed 165 - @echo "Formatting code with black and isort..." 166 - @$(BLACK) . 167 - @$(ISORT) . 162 + @echo "Formatting and fixing code with ruff..." 163 + @$(RUFF) format . 164 + @$(RUFF) check --fix . 168 165 @echo "" 169 166 @echo "Checking for remaining issues..." 170 - @FLAKE8_OK=true; MYPY_OK=true; \ 171 - $(FLAKE8) . || FLAKE8_OK=false; \ 167 + @RUFF_OK=true; MYPY_OK=true; \ 168 + $(RUFF) check . || RUFF_OK=false; \ 172 169 $(MYPY) . || MYPY_OK=false; \ 173 - if $$FLAKE8_OK && $$MYPY_OK; then \ 170 + if $$RUFF_OK && $$MYPY_OK; then \ 174 171 echo ""; \ 175 172 echo "All clean!"; \ 176 173 else \ ··· 206 203 ci: .installed 207 204 @echo "Running CI checks..." 208 205 @echo "=== Checking formatting ===" 209 - @$(BLACK) --check . || { echo "Run 'make format' to fix formatting"; exit 1; } 210 - @$(ISORT) --check-only . || { echo "Run 'make format' to fix imports"; exit 1; } 206 + @$(RUFF) format --check . || { echo "Run 'make format' to fix formatting"; exit 1; } 211 207 @echo "" 212 - @echo "=== Running flake8 ===" 213 - @$(FLAKE8) . || exit 1 208 + @echo "=== Running ruff ===" 209 + @$(RUFF) check . || { echo "Run 'make format' to auto-fix"; exit 1; } 214 210 @echo "" 215 211 @echo "=== Running mypy ===" 216 212 @$(MYPY) . || true ··· 252 248 $(PYTHON) --version 253 249 @echo "" 254 250 @echo "=== Key package versions ===" 255 - @$(UV) pip list | grep -E "^(pytest|black|flake8|mypy|isort|Flask|numpy|Pillow|openai|anthropic|google-genai)" || true 251 + @$(UV) pip list | grep -E "^(pytest|ruff|mypy|Flask|numpy|Pillow|openai|anthropic|google-genai)" || true 256 252 257 253 # Install pre-commit hooks (if using pre-commit) 258 254 pre-commit: .installed
+1 -3
apps/dev/events.py
··· 21 21 This handler matches all events via wildcards and logs them at DEBUG level. 22 22 Useful for understanding event flow during development. 23 23 """ 24 - logger.debug( 25 - f"[dev] Event: {ctx.tract}/{ctx.event} - " f"keys: {list(ctx.msg.keys())}" 26 - ) 24 + logger.debug(f"[dev] Event: {ctx.tract}/{ctx.event} - keys: {list(ctx.msg.keys())}")
+1 -2
apps/speakers/routes.py
··· 42 42 load_all_journal_entities, 43 43 load_journal_entity, 44 44 ) 45 - from think.utils import day_dirs, day_path, iter_segments, now_ms 45 + from think.utils import day_dirs, day_path, iter_segments, now_ms, segment_parse 46 46 from think.utils import segment_key as validate_segment_key 47 - from think.utils import segment_parse 48 47 from think.utils import segment_path as get_segment_path 49 48 50 49 logger = logging.getLogger(__name__)
+5 -5
apps/todos/routes.py
··· 565 565 except OSError: 566 566 yesterday_content = "" 567 567 568 - prompt = f"""Generate a TODO checklist for {day_date.strftime('%Y-%m-%d')} in the {facet} facet. 568 + prompt = f"""Generate a TODO checklist for {day_date.strftime("%Y-%m-%d")} in the {facet} facet. 569 569 570 - Current date/time: {datetime.now().strftime('%Y-%m-%d %H:%M')} 571 - Target day: {day_date.strftime('%Y-%m-%d')} 570 + Current date/time: {datetime.now().strftime("%Y-%m-%d %H:%M")} 571 + Target day: {day_date.strftime("%Y-%m-%d")} 572 572 Target facet: {facet} 573 573 Target file: facets/{facet}/todos/{day}.jsonl 574 574 ··· 657 657 658 658 prompt = f"""Review the past week and generate high-impact todos for {facet} facet. 659 659 660 - Current date/time: {datetime.now().strftime('%Y-%m-%d %H:%M')} 661 - Target day: {day_date.strftime('%Y-%m-%d')} 660 + Current date/time: {datetime.now().strftime("%Y-%m-%d %H:%M")} 661 + Target day: {day_date.strftime("%Y-%m-%d")} 662 662 Target facet: {facet} 663 663 Target file: facets/{facet}/todos/{day}.jsonl 664 664
+1 -1
apps/todos/tests/test_todo.py
··· 251 251 252 252 result = upcoming(limit=2, today="20240104") 253 253 254 - expected = "### Work: 20240105\n" "[ ] Task one\n" "[ ] Task two" 254 + expected = "### Work: 20240105\n[ ] Task one\n[ ] Task two" 255 255 256 256 assert result == expected 257 257
+1 -2
apps/transcripts/routes.py
··· 30 30 from observe.utils import AUDIO_EXTENSIONS, VIDEO_EXTENSIONS 31 31 from think.cluster import cluster_scan, cluster_segments 32 32 from think.models import get_usage_cost 33 - from think.utils import day_dirs, day_path 33 + from think.utils import day_dirs, day_path, segment_path 34 34 from think.utils import segment_key as validate_segment_key 35 - from think.utils import segment_path 36 35 37 36 # Regex for HHMMSS time format validation 38 37 TIME_RE = re.compile(r"\d{6}")
+1 -1
observe/describe.py
··· 238 238 pil_img.close() 239 239 logger.debug( 240 240 f"Skipping frame at {timestamp:.2f}s " 241 - f"(Convey UI covers {mask_area/frame_area:.0%})" 241 + f"(Convey UI covers {mask_area / frame_area:.0%})" 242 242 ) 243 243 continue 244 244 # Mask the Convey region with black
-4
observe/sense.py
··· 697 697 logger.info("Listening for observe.observing events via Callosum") 698 698 699 699 while self.running_flag: 700 - 701 700 # Emit status every 5 seconds if there's activity 702 701 now = time.time() 703 702 if now - self.last_status_emit >= 5: ··· 778 777 to_process = [] 779 778 segment_meta_cache: Dict[str, Optional[Dict[str, Any]]] = {} 780 779 for stream_name, seg_key, seg_path in iter_segments(day): 781 - 782 780 # Apply segment filter if specified 783 781 if segment_filter and seg_key != segment_filter: 784 782 continue ··· 885 883 return deleted 886 884 887 885 for _stream_name, seg_key, seg_path in iter_segments(day_dir): 888 - 889 886 # Apply segment filter if specified 890 887 if segment_filter and seg_key != segment_filter: 891 888 continue ··· 951 948 return {"processed": [], "unprocessed": [], "pending_segments": 0} 952 949 953 950 for stream_name, seg_key, seg_path in iter_segments(day_dir): 954 - 955 951 # Check each file in the segment 956 952 for file_path in seg_path.iterdir(): 957 953 if not file_path.is_file():
+9 -18
pyproject.toml
··· 78 78 "opencv-python", 79 79 80 80 # Development tools 81 - "black", 82 - "flake8", 83 - "isort", 81 + "ruff", 84 82 "mypy", 85 83 "pytest", 86 84 "pytest-asyncio", ··· 116 114 ] 117 115 118 116 # Development tools configuration 119 - [tool.black] 120 - target-version = ['py39'] 121 - extend-exclude = ''' 122 - ( 123 - \.venv 124 - | scratch 125 - | logs 126 - | build 127 - | dist 128 - ) 129 - ''' 117 + [tool.ruff] 118 + target-version = "py310" 119 + exclude = [".venv", "scratch", "logs", "build", "dist"] 130 120 131 - [tool.isort] 132 - profile = "black" 133 - extend_skip = ["journal", ".venv", "scratch", "logs", "build", "dist"] 121 + [tool.ruff.lint] 122 + select = ["F", "E", "W", "I"] 123 + ignore = ["E501", "E203", "E402"] 134 124 135 - # NOTE: flake8 config lives in .flake8 (flake8 does not read pyproject.toml) 125 + [tool.ruff.lint.per-file-ignores] 126 + "observe/gnome/activity.py" = ["E402"] 136 127 137 128 [tool.mypy] 138 129 python_version = "3.12"
+1 -2
sol.py
··· 222 222 # Not found 223 223 available = sorted(set(COMMANDS.keys()) | set(ALIASES.keys())) 224 224 raise ValueError( 225 - f"Unknown command: {name}\n" 226 - f"Available commands: {', '.join(available[:10])}..." 225 + f"Unknown command: {name}\nAvailable commands: {', '.join(available[:10])}..." 227 226 ) 228 227 229 228
+18 -18
tests/integration/test_anthropic_provider.py
··· 87 87 pytest.fail(f"Failed to parse JSON line: {line}\nError: {e}") 88 88 89 89 # Verify we have events 90 - assert ( 91 - len(events) >= 2 92 - ), f"Expected at least start and finish events, got {len(events)}" 90 + assert len(events) >= 2, ( 91 + f"Expected at least start and finish events, got {len(events)}" 92 + ) 93 93 94 94 # Check start event 95 95 start_event = events[0] ··· 114 114 else: 115 115 pytest.fail(f"Unexpected error: {finish_event}") 116 116 117 - assert ( 118 - finish_event["event"] == "finish" 119 - ), f"Expected finish event, got: {finish_event}" 117 + assert finish_event["event"] == "finish", ( 118 + f"Expected finish event, got: {finish_event}" 119 + ) 120 120 assert isinstance(finish_event["ts"], int) 121 121 assert "result" in finish_event 122 122 123 123 # The result should contain "2" 124 124 result_text = finish_event["result"].lower() 125 - assert ( 126 - "2" in result_text or "two" in result_text 127 - ), f"Expected '2' in response, got: {finish_event['result']}" 125 + assert "2" in result_text or "two" in result_text, ( 126 + f"Expected '2' in response, got: {finish_event['result']}" 127 + ) 128 128 129 129 # Check for no errors 130 130 error_events = [e for e in events if e.get("event") == "error"] ··· 212 212 else: 213 213 pytest.fail(f"Unexpected error: {finish_event}") 214 214 215 - assert ( 216 - finish_event["event"] == "finish" 217 - ), f"Expected finish event, got: {finish_event}" 215 + assert finish_event["event"] == "finish", ( 216 + f"Expected finish event, got: {finish_event}" 217 + ) 218 218 result_text = finish_event["result"].lower() 219 - assert ( 220 - "4" in result_text or "four" in result_text 221 - ), f"Expected '4' in response, got: {finish_event['result']}" 219 + assert "4" in result_text or "four" in result_text, ( 220 + f"Expected '4' in response, got: {finish_event['result']}" 221 + ) 222 222 223 223 224 224 @pytest.mark.integration ··· 251 251 ) 252 252 253 253 # Verify truncation was detected via finish_reason 254 - assert ( 255 - result["finish_reason"] == "max_tokens" 256 - ), f"Expected max_tokens finish_reason, got: {result['finish_reason']}" 254 + assert result["finish_reason"] == "max_tokens", ( 255 + f"Expected max_tokens finish_reason, got: {result['finish_reason']}" 256 + ) 257 257 # Partial text should be present 258 258 assert isinstance(result["text"], str)
+2 -2
tests/integration/test_batch.py
··· 79 79 batch_seq = Batch(max_concurrent=1) 80 80 for i in range(2): 81 81 req = batch_seq.create( 82 - contents=f"Count to {i+1}. Reply with just the number.", 82 + contents=f"Count to {i + 1}. Reply with just the number.", 83 83 context=TEST_CONTEXT, 84 84 model=_TIMING_MODEL, 85 85 ) ··· 95 95 batch_conc = Batch(max_concurrent=2) 96 96 for i in range(2): 97 97 req = batch_conc.create( 98 - contents=f"Count to {i+1}. Reply with just the number.", 98 + contents=f"Count to {i + 1}. Reply with just the number.", 99 99 context=TEST_CONTEXT, 100 100 model=_TIMING_MODEL, 101 101 )
+15 -15
tests/integration/test_google_provider.py
··· 87 87 pytest.fail(f"Failed to parse JSON line: {line}\nError: {e}") 88 88 89 89 # Verify we have events 90 - assert ( 91 - len(events) >= 2 92 - ), f"Expected at least start and finish events, got {len(events)}" 90 + assert len(events) >= 2, ( 91 + f"Expected at least start and finish events, got {len(events)}" 92 + ) 93 93 94 94 # Check start event 95 95 start_event = events[0] ··· 107 107 108 108 # The result should contain "2" 109 109 result_text = finish_event["result"].lower() 110 - assert ( 111 - "2" in result_text or "two" in result_text 112 - ), f"Expected '2' in response, got: {finish_event['result']}" 110 + assert "2" in result_text or "two" in result_text, ( 111 + f"Expected '2' in response, got: {finish_event['result']}" 112 + ) 113 113 114 114 # Check for no errors 115 115 error_events = [e for e in events if e.get("event") == "error"] ··· 198 198 else: 199 199 pytest.fail(f"Unexpected error: {error_msg}\nTrace: {trace}") 200 200 201 - assert ( 202 - finish_event["event"] == "finish" 203 - ), f"Expected finish event, got: {finish_event}" 201 + assert finish_event["event"] == "finish", ( 202 + f"Expected finish event, got: {finish_event}" 203 + ) 204 204 assert "result" in finish_event, f"No result in finish event: {finish_event}" 205 205 if finish_event["result"]: 206 206 result_text = finish_event["result"].lower() 207 - assert ( 208 - "4" in result_text or "four" in result_text 209 - ), f"Expected '4' in response, got: {finish_event['result']}" 207 + assert "4" in result_text or "four" in result_text, ( 208 + f"Expected '4' in response, got: {finish_event['result']}" 209 + ) 210 210 211 211 212 212 @pytest.mark.integration ··· 239 239 ) 240 240 241 241 # Verify truncation was detected via finish_reason 242 - assert ( 243 - result["finish_reason"] == "max_tokens" 244 - ), f"Expected max_tokens finish_reason, got: {result['finish_reason']}" 242 + assert result["finish_reason"] == "max_tokens", ( 243 + f"Expected max_tokens finish_reason, got: {result['finish_reason']}" 244 + ) 245 245 # Partial text should be present 246 246 assert isinstance(result["text"], str)
+24 -24
tests/integration/test_openai_provider.py
··· 87 87 pytest.fail(f"Failed to parse JSON line: {line}\nError: {e}") 88 88 89 89 # Verify we have events 90 - assert ( 91 - len(events) >= 2 92 - ), f"Expected at least start and finish events, got {len(events)}" 90 + assert len(events) >= 2, ( 91 + f"Expected at least start and finish events, got {len(events)}" 92 + ) 93 93 94 94 # Check start event 95 95 start_event = events[0] ··· 107 107 108 108 # The result should contain "2" 109 109 result_text = finish_event["result"].lower() 110 - assert ( 111 - "2" in result_text or "two" in result_text 112 - ), f"Expected '2' in response, got: {finish_event['result']}" 110 + assert "2" in result_text or "two" in result_text, ( 111 + f"Expected '2' in response, got: {finish_event['result']}" 112 + ) 113 113 114 114 # Check for no errors 115 115 error_events = [e for e in events if e.get("event") == "error"] ··· 190 190 # If we have thinking events, verify their structure 191 191 for thinking in thinking_events: 192 192 assert "summary" in thinking, f"Thinking event missing 'summary': {thinking}" 193 - assert isinstance( 194 - thinking["summary"], str 195 - ), f"Thinking summary should be string: {thinking}" 193 + assert isinstance(thinking["summary"], str), ( 194 + f"Thinking summary should be string: {thinking}" 195 + ) 196 196 assert len(thinking["summary"]) > 0, "Thinking summary should not be empty" 197 197 assert "model" in thinking, f"Thinking event missing 'model': {thinking}" 198 198 assert "ts" in thinking, f"Thinking event missing 'ts': {thinking}" ··· 202 202 finish_event = events[-1] 203 203 assert finish_event["event"] == "finish" 204 204 result_text = finish_event["result"].lower() 205 - assert ( 206 - "6" in result_text or "six" in result_text 207 - ), f"Expected '6' in response, got: {finish_event['result']}" 205 + assert "6" in result_text or "six" in result_text, ( 206 + f"Expected '6' in response, got: {finish_event['result']}" 207 + ) 208 208 209 209 # Log whether we got thinking events for debugging 210 210 print(f"Received {len(thinking_events)} thinking events") ··· 269 269 error_events = [e for e in events if e.get("event") == "error"] 270 270 for err in error_events: 271 271 error_msg = err.get("error", "") 272 - assert ( 273 - "Invalid value: 'text'" not in error_msg 274 - ), f"Got content type format error - regression! Error: {error_msg}" 275 - assert ( 276 - "input_text" not in error_msg or "Supported values" not in error_msg 277 - ), f"Got content type format error - regression! Error: {error_msg}" 272 + assert "Invalid value: 'text'" not in error_msg, ( 273 + f"Got content type format error - regression! Error: {error_msg}" 274 + ) 275 + assert "input_text" not in error_msg or "Supported values" not in error_msg, ( 276 + f"Got content type format error - regression! Error: {error_msg}" 277 + ) 278 278 279 279 # Verify we got past the format validation (start event was emitted) 280 280 start_events = [e for e in events if e.get("event") == "start"] ··· 284 284 finish_events = [e for e in events if e.get("event") == "finish"] 285 285 if finish_events: 286 286 result_text = finish_events[0].get("result", "").lower() 287 - assert ( 288 - "moonshot" in result_text 289 - ), f"Expected 'moonshot' in response, got: {finish_events[0].get('result')}" 287 + assert "moonshot" in result_text, ( 288 + f"Expected 'moonshot' in response, got: {finish_events[0].get('result')}" 289 + ) 290 290 291 291 292 292 @pytest.mark.integration ··· 319 319 ) 320 320 321 321 # Verify truncation was detected via finish_reason 322 - assert ( 323 - result["finish_reason"] == "max_tokens" 324 - ), f"Expected max_tokens finish_reason, got: {result['finish_reason']}" 322 + assert result["finish_reason"] == "max_tokens", ( 323 + f"Expected max_tokens finish_reason, got: {result['finish_reason']}" 324 + ) 325 325 # Partial text should be present 326 326 assert isinstance(result["text"], str)
+4 -4
tests/test_cortex.py
··· 315 315 mock_process = MagicMock() 316 316 mock_process.poll.return_value = 0 317 317 mock_process.stdout = StringIO( 318 - "Plain text output\n" '{"event": "finish", "ts": 1234567890}\n' 318 + 'Plain text output\n{"event": "finish", "ts": 1234567890}\n' 319 319 ) 320 320 321 321 agent = AgentProcess(agent_id, mock_process, log_path) ··· 410 410 mock_process = MagicMock() 411 411 mock_process.poll.return_value = 1 # Error exit 412 412 mock_process.stderr = StringIO( 413 - "Error: Something went wrong\n" "Stack trace line 1\n" "Stack trace line 2\n" 413 + "Error: Something went wrong\nStack trace line 1\nStack trace line 2\n" 414 414 ) 415 415 416 416 agent = AgentProcess(agent_id, mock_process, log_path) ··· 435 435 436 436 # File with finish event 437 437 file_path.write_text( 438 - '{"event": "start", "ts": 123}\n' '{"event": "finish", "ts": 124}\n' 438 + '{"event": "start", "ts": 123}\n{"event": "finish", "ts": 124}\n' 439 439 ) 440 440 assert cortex_service._has_finish_event(file_path) is True 441 441 442 442 # File with error event 443 443 file_path.write_text( 444 - '{"event": "start", "ts": 123}\n' '{"event": "error", "ts": 124}\n' 444 + '{"event": "start", "ts": 123}\n{"event": "error", "ts": 124}\n' 445 445 ) 446 446 assert cortex_service._has_finish_event(file_path) is True 447 447
-1
tests/test_cortex_client.py
··· 4 4 """Tests for cortex_client module with Callosum.""" 5 5 6 6 import json 7 - import os 8 7 import shutil 9 8 import tempfile 10 9 import threading
+6 -6
tests/test_generators.py
··· 77 77 assert meta.get("schedule") == "segment", f"{key} should have schedule=segment" 78 78 79 79 # Verify no overlap 80 - assert not set(daily.keys()) & set( 81 - segment.keys() 82 - ), "daily and segment should not overlap" 80 + assert not set(daily.keys()) & set(segment.keys()), ( 81 + "daily and segment should not overlap" 82 + ) 83 83 84 84 # Unknown schedule returns empty dict 85 85 assert muse.get_muse_configs(type="generate", schedule="hourly") == {} ··· 118 118 for key, meta in generators.items(): 119 119 sched = meta.get("schedule") 120 120 if sched is not None: 121 - assert ( 122 - sched in valid_schedules 123 - ), f"Generator '{key}' has invalid schedule '{sched}'" 121 + assert sched in valid_schedules, ( 122 + f"Generator '{key}' has invalid schedule '{sched}'" 123 + ) 124 124 125 125 126 126 def test_speakers_has_required_audio():
+6 -2
tests/test_logs_cli.py
··· 61 61 def test_parse_log_line_supervisor(): 62 62 from think.logs_cli import parse_log_line 63 63 64 - result = parse_log_line("2026-02-09T10:00:00 [supervisor:log] INFO Starting service") 64 + result = parse_log_line( 65 + "2026-02-09T10:00:00 [supervisor:log] INFO Starting service" 66 + ) 65 67 assert result is not None 66 68 assert result.service == "supervisor" 67 69 assert result.stream == "log" ··· 308 310 from think import logs_cli 309 311 310 312 day = datetime.now().strftime("%Y%m%d") 311 - lines = [f"2026-02-09T10:{i:02d}:00 [echo:stdout] special line {i}" for i in range(10)] 313 + lines = [ 314 + f"2026-02-09T10:{i:02d}:00 [echo:stdout] special line {i}" for i in range(10) 315 + ] 312 316 make_journal(tmp_path, day, {"echo": lines}) 313 317 monkeypatch.setenv("JOURNAL_PATH", str(tmp_path)) 314 318
+12 -12
tests/test_models.py
··· 236 236 post = frontmatter.load(path) 237 237 meta = post.metadata or {} 238 238 239 - assert required_keys <= set( 240 - meta.keys() 241 - ), f"{rel_path} missing keys: {required_keys - set(meta.keys())}" 239 + assert required_keys <= set(meta.keys()), ( 240 + f"{rel_path} missing keys: {required_keys - set(meta.keys())}" 241 + ) 242 242 assert meta["tier"] in ( 243 243 TIER_PRO, 244 244 TIER_FLASH, 245 245 TIER_LITE, 246 246 ), f"{rel_path} has invalid tier: {meta['tier']}" 247 - assert ( 248 - isinstance(meta["label"], str) and meta["label"] 249 - ), f"{rel_path} has invalid label: {meta['label']}" 250 - assert ( 251 - isinstance(meta["group"], str) and meta["group"] 252 - ), f"{rel_path} has invalid group: {meta['group']}" 247 + assert isinstance(meta["label"], str) and meta["label"], ( 248 + f"{rel_path} has invalid label: {meta['label']}" 249 + ) 250 + assert isinstance(meta["group"], str) and meta["group"], ( 251 + f"{rel_path} has invalid group: {meta['group']}" 252 + ) 253 253 254 254 255 255 def test_prompt_contexts_in_registry(): ··· 478 478 479 479 for context, config in registry.items(): 480 480 assert isinstance(config, dict), f"{context} should be a dict" 481 - assert required_keys <= set( 482 - config.keys() 483 - ), f"{context} missing keys: {required_keys - set(config.keys())}" 481 + assert required_keys <= set(config.keys()), ( 482 + f"{context} missing keys: {required_keys - set(config.keys())}" 483 + ) 484 484 assert config["tier"] in ( 485 485 TIER_PRO, 486 486 TIER_FLASH,
+3 -3
tests/test_sol.py
··· 280 280 """Test that all commands in groups exist in registry.""" 281 281 for group_name, commands in sol.GROUPS.items(): 282 282 for cmd in commands: 283 - assert ( 284 - cmd in sol.COMMANDS 285 - ), f"Command '{cmd}' in group '{group_name}' not in registry" 283 + assert cmd in sol.COMMANDS, ( 284 + f"Command '{cmd}' in group '{group_name}' not in registry" 285 + ) 286 286 287 287 def test_critical_commands_registered(self): 288 288 """Test that critical commands are registered."""
+3 -3
think/cortex.py
··· 393 393 if model: 394 394 with self.lock: 395 395 if agent.agent_id in self.agent_requests: 396 - self.agent_requests[agent.agent_id][ 397 - "model" 398 - ] = model 396 + self.agent_requests[agent.agent_id]["model"] = ( 397 + model 398 + ) 399 399 400 400 # Handle finish or error event 401 401 if event.get("event") in ["finish", "error"]:
+3 -4
think/dream.py
··· 23 23 from think.facets import ( 24 24 get_active_facets, 25 25 get_enabled_facets, 26 - get_facets, 27 26 load_segment_facets, 28 27 ) 29 28 from think.muse import get_muse_configs, get_output_path ··· 403 402 raw_facets = load_segment_facets(day, segment, stream=stream) 404 403 active_facets = set(f for f in raw_facets if f in enabled_facets) 405 404 406 - spawned: list[tuple[str, str, dict, str | None]] = ( 407 - [] 408 - ) # (agent_id, name, config, facet) 405 + spawned: list[ 406 + tuple[str, str, dict, str | None] 407 + ] = [] # (agent_id, name, config, facet) 409 408 group_success = 0 410 409 group_failed = 0 411 410
+1 -1
think/formatters.py
··· 305 305 if path: 306 306 print(f" path: {path}") 307 307 if intro: 308 - print(f" intro: \"{intro[:60]}{'...' if len(intro) > 60 else ''}\"") 308 + print(f' intro: "{intro[:60]}{"..." if len(intro) > 60 else ""}"') 309 309 print(f" {preview[:70]}{'...' if len(preview) > 70 else ''}") 310 310 print() 311 311
+2 -3
think/importers/cli.py
··· 95 95 if backend is None: 96 96 available = ", ".join(b.name for b in backends) or "(none)" 97 97 raise SystemExit( 98 - f"Unknown sync backend: {backend_name}\n" f"Available backends: {available}" 98 + f"Unknown sync backend: {backend_name}\nAvailable backends: {available}" 99 99 ) 100 100 101 101 mode = "save" if not dry_run else "catalog" ··· 579 579 if msg.get("error"): 580 580 errors = msg.get("errors", []) 581 581 logger.warning( 582 - f"Segment {seg} failed: {errors} " 583 - f"({len(pending)} remaining)" 582 + f"Segment {seg} failed: {errors} ({len(pending)} remaining)" 584 583 ) 585 584 failed_segments.append(seg) 586 585 else:
+12 -4
think/logs_cli.py
··· 166 166 health_dir = get_today_health_dir() 167 167 if health_dir: 168 168 for log_path in get_day_log_files(health_dir): 169 - raw_lines = tail_lines(log_path, 0) if has_filters else tail_lines(log_path, args.c) 169 + raw_lines = ( 170 + tail_lines(log_path, 0) if has_filters else tail_lines(log_path, args.c) 171 + ) 170 172 for raw in raw_lines: 171 173 parsed = parse_log_line(raw) 172 174 if parsed and _matches_filters(parsed, args): ··· 184 186 185 187 lines.sort(key=lambda line: line.timestamp) 186 188 if has_filters and args.c: 187 - lines = lines[-args.c:] 189 + lines = lines[-args.c :] 188 190 use_color = sys.stdout.isatty() 189 191 last_service = None 190 192 for line in lines: ··· 234 236 if line: 235 237 parsed = parse_log_line(line) 236 238 current_service = parsed.service if parsed else None 237 - if use_color and current_service and current_service != last_service: 239 + if ( 240 + use_color 241 + and current_service 242 + and current_service != last_service 243 + ): 238 244 if last_service is not None: 239 245 print(flush=True) 240 - print(_service_header(current_service, use_color), flush=True) 246 + print( 247 + _service_header(current_service, use_color), flush=True 248 + ) 241 249 last_service = current_service 242 250 print(line, flush=True) 243 251 line = fh.readline()
+3 -6
think/providers/cli.py
··· 192 192 binary = self.cmd[0] 193 193 if not shutil.which(binary): 194 194 raise RuntimeError( 195 - f"CLI tool '{binary}' not found. " 196 - f"Install it and ensure it's on PATH." 195 + f"CLI tool '{binary}' not found. Install it and ensure it's on PATH." 197 196 ) 198 197 199 198 import os ··· 276 275 if result: 277 276 # CLI failed but produced output — warn and return what we got 278 277 LOG.warning( 279 - "CLI process exited with code %d but produced output. " 280 - "Stderr: %s", 278 + "CLI process exited with code %d but produced output. Stderr: %s", 281 279 return_code, 282 280 stderr_text, 283 281 ) ··· 299 297 stderr_text, 300 298 ) 301 299 raise RuntimeError( 302 - f"CLI process exited with code {return_code}. " 303 - f"Stderr: {stderr_text}" 300 + f"CLI process exited with code {return_code}. Stderr: {stderr_text}" 304 301 ) 305 302 306 303 return result
-1
think/tools/call.py
··· 170 170 171 171 day = get_sol_day() 172 172 if write: 173 - 174 173 # Read markdown from stdin 175 174 markdown = sys.stdin.read() 176 175 if not markdown.strip():
+1 -3
think/top.py
··· 49 49 self.STATUS_TIMEOUT = 5 # Seconds before auto-clearing service status 50 50 # Fixed column width for log truncation (icon + name + pid + time + mem + cpu + age + spacing) 51 51 self.LOG_FIXED_WIDTH = 63 52 - self.last_log_lines = ( 53 - {} 54 - ) # Maps ref -> (timestamp, stream, line) for most recent log 52 + self.last_log_lines = {} # Maps ref -> (timestamp, stream, line) for most recent log 55 53 self.cpu_cache = {} # Maps pid -> last cpu_percent value 56 54 self.cpu_procs = {} # Maps pid -> Process object for cpu tracking 57 55 self.running_tasks = {} # Maps ref -> task info from logs tract
+27 -139
uv.lock
··· 238 238 ] 239 239 240 240 [[package]] 241 - name = "black" 242 - version = "26.1.0" 243 - source = { registry = "https://pypi.org/simple" } 244 - dependencies = [ 245 - { name = "click" }, 246 - { name = "mypy-extensions" }, 247 - { name = "packaging" }, 248 - { name = "pathspec" }, 249 - { name = "platformdirs" }, 250 - { name = "pytokens" }, 251 - { name = "tomli", marker = "python_full_version < '3.11'" }, 252 - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, 253 - ] 254 - sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" } 255 - wheels = [ 256 - { url = "https://files.pythonhosted.org/packages/51/1b/523329e713f965ad0ea2b7a047eeb003007792a0353622ac7a8cb2ee6fef/black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168", size = 1849661, upload-time = "2026-01-18T04:59:12.425Z" }, 257 - { url = "https://files.pythonhosted.org/packages/14/82/94c0640f7285fa71c2f32879f23e609dd2aa39ba2641f395487f24a578e7/black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d", size = 1689065, upload-time = "2026-01-18T04:59:13.993Z" }, 258 - { url = "https://files.pythonhosted.org/packages/f0/78/474373cbd798f9291ed8f7107056e343fd39fef42de4a51c7fd0d360840c/black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0", size = 1751502, upload-time = "2026-01-18T04:59:15.971Z" }, 259 - { url = "https://files.pythonhosted.org/packages/29/89/59d0e350123f97bc32c27c4d79563432d7f3530dca2bff64d855c178af8b/black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24", size = 1400102, upload-time = "2026-01-18T04:59:17.8Z" }, 260 - { url = "https://files.pythonhosted.org/packages/e1/bc/5d866c7ae1c9d67d308f83af5462ca7046760158bbf142502bad8f22b3a1/black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89", size = 1207038, upload-time = "2026-01-18T04:59:19.543Z" }, 261 - { url = "https://files.pythonhosted.org/packages/30/83/f05f22ff13756e1a8ce7891db517dbc06200796a16326258268f4658a745/black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5", size = 1831956, upload-time = "2026-01-18T04:59:21.38Z" }, 262 - { url = "https://files.pythonhosted.org/packages/7d/f2/b2c570550e39bedc157715e43927360312d6dd677eed2cc149a802577491/black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68", size = 1672499, upload-time = "2026-01-18T04:59:23.257Z" }, 263 - { url = "https://files.pythonhosted.org/packages/7a/d7/990d6a94dc9e169f61374b1c3d4f4dd3037e93c2cc12b6f3b12bc663aa7b/black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14", size = 1735431, upload-time = "2026-01-18T04:59:24.729Z" }, 264 - { url = "https://files.pythonhosted.org/packages/36/1c/cbd7bae7dd3cb315dfe6eeca802bb56662cc92b89af272e014d98c1f2286/black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c", size = 1400468, upload-time = "2026-01-18T04:59:27.381Z" }, 265 - { url = "https://files.pythonhosted.org/packages/59/b1/9fe6132bb2d0d1f7094613320b56297a108ae19ecf3041d9678aec381b37/black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4", size = 1207332, upload-time = "2026-01-18T04:59:28.711Z" }, 266 - { url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" }, 267 - { url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" }, 268 - { url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" }, 269 - { url = "https://files.pythonhosted.org/packages/49/f9/71c161c4c7aa18bdda3776b66ac2dc07aed62053c7c0ff8bbda8c2624fe2/black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791", size = 1406466, upload-time = "2026-01-18T04:59:35.177Z" }, 270 - { url = "https://files.pythonhosted.org/packages/4a/8b/a7b0f974e473b159d0ac1b6bcefffeb6bec465898a516ee5cc989503cbc7/black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954", size = 1216393, upload-time = "2026-01-18T04:59:37.18Z" }, 271 - { url = "https://files.pythonhosted.org/packages/79/04/fa2f4784f7237279332aa735cdfd5ae2e7730db0072fb2041dadda9ae551/black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304", size = 1877781, upload-time = "2026-01-18T04:59:39.054Z" }, 272 - { url = "https://files.pythonhosted.org/packages/cf/ad/5a131b01acc0e5336740a039628c0ab69d60cf09a2c87a4ec49f5826acda/black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9", size = 1699670, upload-time = "2026-01-18T04:59:41.005Z" }, 273 - { url = "https://files.pythonhosted.org/packages/da/7c/b05f22964316a52ab6b4265bcd52c0ad2c30d7ca6bd3d0637e438fc32d6e/black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b", size = 1775212, upload-time = "2026-01-18T04:59:42.545Z" }, 274 - { url = "https://files.pythonhosted.org/packages/a6/a3/e8d1526bea0446e040193185353920a9506eab60a7d8beb062029129c7d2/black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b", size = 1409953, upload-time = "2026-01-18T04:59:44.357Z" }, 275 - { url = "https://files.pythonhosted.org/packages/c7/5a/d62ebf4d8f5e3a1daa54adaab94c107b57be1b1a2f115a0249b41931e188/black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca", size = 1217707, upload-time = "2026-01-18T04:59:45.719Z" }, 276 - { url = "https://files.pythonhosted.org/packages/6a/83/be35a175aacfce4b05584ac415fd317dd6c24e93a0af2dcedce0f686f5d8/black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115", size = 1871864, upload-time = "2026-01-18T04:59:47.586Z" }, 277 - { url = "https://files.pythonhosted.org/packages/a5/f5/d33696c099450b1274d925a42b7a030cd3ea1f56d72e5ca8bbed5f52759c/black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79", size = 1701009, upload-time = "2026-01-18T04:59:49.443Z" }, 278 - { url = "https://files.pythonhosted.org/packages/1b/87/670dd888c537acb53a863bc15abbd85b22b429237d9de1b77c0ed6b79c42/black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af", size = 1767806, upload-time = "2026-01-18T04:59:50.769Z" }, 279 - { url = "https://files.pythonhosted.org/packages/fe/9c/cd3deb79bfec5bcf30f9d2100ffeec63eecce826eb63e3961708b9431ff1/black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f", size = 1433217, upload-time = "2026-01-18T04:59:52.218Z" }, 280 - { url = "https://files.pythonhosted.org/packages/4e/29/f3be41a1cf502a283506f40f5d27203249d181f7a1a2abce1c6ce188035a/black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0", size = 1245773, upload-time = "2026-01-18T04:59:54.457Z" }, 281 - { url = "https://files.pythonhosted.org/packages/e4/3d/51bdb3ecbfadfaf825ec0c75e1de6077422b4afa2091c6c9ba34fbfc0c2d/black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede", size = 204010, upload-time = "2026-01-18T04:50:09.978Z" }, 282 - ] 283 - 284 - [[package]] 285 241 name = "blessed" 286 242 version = "1.30.0" 287 243 source = { registry = "https://pypi.org/simple" } ··· 865 821 ] 866 822 867 823 [[package]] 868 - name = "flake8" 869 - version = "7.3.0" 870 - source = { registry = "https://pypi.org/simple" } 871 - dependencies = [ 872 - { name = "mccabe" }, 873 - { name = "pycodestyle" }, 874 - { name = "pyflakes" }, 875 - ] 876 - sdist = { url = "https://files.pythonhosted.org/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872", size = 48326, upload-time = "2025-06-20T19:31:35.838Z" } 877 - wheels = [ 878 - { url = "https://files.pythonhosted.org/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", size = 57922, upload-time = "2025-06-20T19:31:34.425Z" }, 879 - ] 880 - 881 - [[package]] 882 824 name = "flask" 883 825 version = "3.1.2" 884 826 source = { registry = "https://pypi.org/simple" } ··· 1170 1112 ] 1171 1113 1172 1114 [[package]] 1173 - name = "isort" 1174 - version = "7.0.0" 1175 - source = { registry = "https://pypi.org/simple" } 1176 - sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" } 1177 - wheels = [ 1178 - { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, 1179 - ] 1180 - 1181 - [[package]] 1182 1115 name = "itsdangerous" 1183 1116 version = "2.2.0" 1184 1117 source = { registry = "https://pypi.org/simple" } ··· 1599 1532 { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, 1600 1533 { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, 1601 1534 { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, 1602 - ] 1603 - 1604 - [[package]] 1605 - name = "mccabe" 1606 - version = "0.7.0" 1607 - source = { registry = "https://pypi.org/simple" } 1608 - sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } 1609 - wheels = [ 1610 - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, 1611 1535 ] 1612 1536 1613 1537 [[package]] ··· 2454 2378 sdist = { url = "https://files.pythonhosted.org/packages/22/d9/1728840a22a4ef8a8f479b9156aa2943cd98c3907accd3849fb0d5f82bfd/pycairo-1.29.0.tar.gz", hash = "sha256:f3f7fde97325cae80224c09f12564ef58d0d0f655da0e3b040f5807bd5bd3142", size = 665871, upload-time = "2025-11-11T19:13:01.584Z" } 2455 2379 2456 2380 [[package]] 2457 - name = "pycodestyle" 2458 - version = "2.14.0" 2459 - source = { registry = "https://pypi.org/simple" } 2460 - sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } 2461 - wheels = [ 2462 - { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, 2463 - ] 2464 - 2465 - [[package]] 2466 2381 name = "pycparser" 2467 2382 version = "3.0" 2468 2383 source = { registry = "https://pypi.org/simple" } ··· 2628 2543 sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } 2629 2544 wheels = [ 2630 2545 { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, 2631 - ] 2632 - 2633 - [[package]] 2634 - name = "pyflakes" 2635 - version = "3.4.0" 2636 - source = { registry = "https://pypi.org/simple" } 2637 - sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669, upload-time = "2025-06-20T18:45:27.834Z" } 2638 - wheels = [ 2639 - { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" }, 2640 2546 ] 2641 2547 2642 2548 [[package]] ··· 2848 2754 ] 2849 2755 2850 2756 [[package]] 2851 - name = "pytokens" 2852 - version = "0.4.1" 2853 - source = { registry = "https://pypi.org/simple" } 2854 - sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } 2855 - wheels = [ 2856 - { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, 2857 - { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, 2858 - { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, 2859 - { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, 2860 - { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, 2861 - { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, 2862 - { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, 2863 - { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, 2864 - { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, 2865 - { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, 2866 - { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, 2867 - { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, 2868 - { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, 2869 - { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, 2870 - { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, 2871 - { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, 2872 - { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, 2873 - { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, 2874 - { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, 2875 - { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, 2876 - { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, 2877 - { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, 2878 - { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, 2879 - { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, 2880 - { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, 2881 - { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, 2882 - { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, 2883 - { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, 2884 - { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, 2885 - { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, 2886 - { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, 2887 - ] 2888 - 2889 - [[package]] 2890 2757 name = "pytz" 2891 2758 version = "2024.2" 2892 2759 source = { registry = "https://pypi.org/simple" } ··· 3275 3142 ] 3276 3143 3277 3144 [[package]] 3145 + name = "ruff" 3146 + version = "0.15.2" 3147 + source = { registry = "https://pypi.org/simple" } 3148 + sdist = { url = "https://files.pythonhosted.org/packages/06/04/eab13a954e763b0606f460443fcbf6bb5a0faf06890ea3754ff16523dce5/ruff-0.15.2.tar.gz", hash = "sha256:14b965afee0969e68bb871eba625343b8673375f457af4abe98553e8bbb98342", size = 4558148, upload-time = "2026-02-19T22:32:20.271Z" } 3149 + wheels = [ 3150 + { url = "https://files.pythonhosted.org/packages/2f/70/3a4dc6d09b13cb3e695f28307e5d889b2e1a66b7af9c5e257e796695b0e6/ruff-0.15.2-py3-none-linux_armv6l.whl", hash = "sha256:120691a6fdae2f16d65435648160f5b81a9625288f75544dc40637436b5d3c0d", size = 10430565, upload-time = "2026-02-19T22:32:41.824Z" }, 3151 + { url = "https://files.pythonhosted.org/packages/71/0b/bb8457b56185ece1305c666dc895832946d24055be90692381c31d57466d/ruff-0.15.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a89056d831256099658b6bba4037ac6dd06f49d194199215befe2bb10457ea5e", size = 10820354, upload-time = "2026-02-19T22:32:07.366Z" }, 3152 + { url = "https://files.pythonhosted.org/packages/2d/c1/e0532d7f9c9e0b14c46f61b14afd563298b8b83f337b6789ddd987e46121/ruff-0.15.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e36dee3a64be0ebd23c86ffa3aa3fd3ac9a712ff295e192243f814a830b6bd87", size = 10170767, upload-time = "2026-02-19T22:32:13.188Z" }, 3153 + { url = "https://files.pythonhosted.org/packages/47/e8/da1aa341d3af017a21c7a62fb5ec31d4e7ad0a93ab80e3a508316efbcb23/ruff-0.15.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9fb47b6d9764677f8c0a193c0943ce9a05d6763523f132325af8a858eadc2b9", size = 10529591, upload-time = "2026-02-19T22:32:02.547Z" }, 3154 + { url = "https://files.pythonhosted.org/packages/93/74/184fbf38e9f3510231fbc5e437e808f0b48c42d1df9434b208821efcd8d6/ruff-0.15.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f376990f9d0d6442ea9014b19621d8f2aaf2b8e39fdbfc79220b7f0c596c9b80", size = 10260771, upload-time = "2026-02-19T22:32:36.938Z" }, 3155 + { url = "https://files.pythonhosted.org/packages/05/ac/605c20b8e059a0bc4b42360414baa4892ff278cec1c91fff4be0dceedefd/ruff-0.15.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dcc987551952d73cbf5c88d9fdee815618d497e4df86cd4c4824cc59d5dd75f", size = 11045791, upload-time = "2026-02-19T22:32:31.642Z" }, 3156 + { url = "https://files.pythonhosted.org/packages/fd/52/db6e419908f45a894924d410ac77d64bdd98ff86901d833364251bd08e22/ruff-0.15.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42a47fd785cbe8c01b9ff45031af875d101b040ad8f4de7bbb716487c74c9a77", size = 11879271, upload-time = "2026-02-19T22:32:29.305Z" }, 3157 + { url = "https://files.pythonhosted.org/packages/3e/d8/7992b18f2008bdc9231d0f10b16df7dda964dbf639e2b8b4c1b4e91b83af/ruff-0.15.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe9f49354866e575b4c6943856989f966421870e85cd2ac94dccb0a9dcb2fea", size = 11303707, upload-time = "2026-02-19T22:32:22.492Z" }, 3158 + { url = "https://files.pythonhosted.org/packages/d7/02/849b46184bcfdd4b64cde61752cc9a146c54759ed036edd11857e9b8443b/ruff-0.15.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7a672c82b5f9887576087d97be5ce439f04bbaf548ee987b92d3a7dede41d3a", size = 11149151, upload-time = "2026-02-19T22:32:44.234Z" }, 3159 + { url = "https://files.pythonhosted.org/packages/70/04/f5284e388bab60d1d3b99614a5a9aeb03e0f333847e2429bebd2aaa1feec/ruff-0.15.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ecc64f46f7019e2bcc3cdc05d4a7da958b629a5ab7033195e11a438403d956", size = 11091132, upload-time = "2026-02-19T22:32:24.691Z" }, 3160 + { url = "https://files.pythonhosted.org/packages/fa/ae/88d844a21110e14d92cf73d57363fab59b727ebeabe78009b9ccb23500af/ruff-0.15.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8dcf243b15b561c655c1ef2f2b0050e5d50db37fe90115507f6ff37d865dc8b4", size = 10504717, upload-time = "2026-02-19T22:32:26.75Z" }, 3161 + { url = "https://files.pythonhosted.org/packages/64/27/867076a6ada7f2b9c8292884ab44d08fd2ba71bd2b5364d4136f3cd537e1/ruff-0.15.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dab6941c862c05739774677c6273166d2510d254dac0695c0e3f5efa1b5585de", size = 10263122, upload-time = "2026-02-19T22:32:10.036Z" }, 3162 + { url = "https://files.pythonhosted.org/packages/e7/ef/faf9321d550f8ebf0c6373696e70d1758e20ccdc3951ad7af00c0956be7c/ruff-0.15.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b9164f57fc36058e9a6806eb92af185b0697c9fe4c7c52caa431c6554521e5c", size = 10735295, upload-time = "2026-02-19T22:32:39.227Z" }, 3163 + { url = "https://files.pythonhosted.org/packages/2f/55/e8089fec62e050ba84d71b70e7834b97709ca9b7aba10c1a0b196e493f97/ruff-0.15.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:80d24fcae24d42659db7e335b9e1531697a7102c19185b8dc4a028b952865fd8", size = 11241641, upload-time = "2026-02-19T22:32:34.617Z" }, 3164 + { url = "https://files.pythonhosted.org/packages/23/01/1c30526460f4d23222d0fabd5888868262fd0e2b71a00570ca26483cd993/ruff-0.15.2-py3-none-win32.whl", hash = "sha256:fd5ff9e5f519a7e1bd99cbe8daa324010a74f5e2ebc97c6242c08f26f3714f6f", size = 10507885, upload-time = "2026-02-19T22:32:15.635Z" }, 3165 + { url = "https://files.pythonhosted.org/packages/5c/10/3d18e3bbdf8fc50bbb4ac3cc45970aa5a9753c5cb51bf9ed9a3cd8b79fa3/ruff-0.15.2-py3-none-win_amd64.whl", hash = "sha256:d20014e3dfa400f3ff84830dfb5755ece2de45ab62ecea4af6b7262d0fb4f7c5", size = 11623725, upload-time = "2026-02-19T22:32:04.947Z" }, 3166 + { url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" }, 3167 + ] 3168 + 3169 + [[package]] 3278 3170 name = "scikit-learn" 3279 3171 version = "1.7.2" 3280 3172 source = { registry = "https://pypi.org/simple" } ··· 3650 3542 dependencies = [ 3651 3543 { name = "anthropic" }, 3652 3544 { name = "av" }, 3653 - { name = "black" }, 3654 3545 { name = "blessed" }, 3655 3546 { name = "dbus-next", marker = "sys_platform == 'linux'" }, 3656 3547 { name = "desktop-notifier" }, 3657 3548 { name = "faster-whisper" }, 3658 - { name = "flake8" }, 3659 3549 { name = "flask", extra = ["async"] }, 3660 3550 { name = "flask-sock" }, 3661 3551 { name = "genai-prices" }, 3662 3552 { name = "google-genai" }, 3663 - { name = "isort" }, 3664 3553 { name = "markdown" }, 3665 3554 { name = "mistune" }, 3666 3555 { name = "mypy" }, ··· 3688 3577 { name = "rapidfuzz" }, 3689 3578 { name = "requests" }, 3690 3579 { name = "resemblyzer" }, 3580 + { name = "ruff" }, 3691 3581 { name = "setproctitle" }, 3692 3582 { name = "soundcard" }, 3693 3583 { name = "soundfile" }, ··· 3701 3591 requires-dist = [ 3702 3592 { name = "anthropic" }, 3703 3593 { name = "av" }, 3704 - { name = "black" }, 3705 3594 { name = "blessed", specifier = ">=1.20.0" }, 3706 3595 { name = "dbus-next", marker = "sys_platform == 'linux'" }, 3707 3596 { name = "desktop-notifier" }, 3708 3597 { name = "faster-whisper", specifier = ">=1.0.0" }, 3709 - { name = "flake8" }, 3710 3598 { name = "flask", extras = ["async"] }, 3711 3599 { name = "flask-sock" }, 3712 3600 { name = "genai-prices" }, 3713 3601 { name = "google-genai" }, 3714 - { name = "isort" }, 3715 3602 { name = "markdown" }, 3716 3603 { name = "mistune" }, 3717 3604 { name = "mypy" }, ··· 3738 3625 { name = "rapidfuzz" }, 3739 3626 { name = "requests" }, 3740 3627 { name = "resemblyzer", specifier = ">=0.1.0" }, 3628 + { name = "ruff" }, 3741 3629 { name = "setproctitle" }, 3742 3630 { name = "soundcard" }, 3743 3631 { name = "soundfile" },