cc-session-tools 2.2.0__tar.gz → 2.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_session_tools-2.2.0/src/cc_session_tools.egg-info → cc_session_tools-2.3.0}/PKG-INFO +1 -1
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/pyproject.toml +14 -2
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/ccst.py +9 -17
- cc_session_tools-2.3.0/src/cc_session_tools/config/hooks-bundle.json +118 -0
- cc_session_tools-2.3.0/src/cc_session_tools/config/session-tag-hook.json +16 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +230 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/delete-sessions/SKILL.md +158 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +429 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +89 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +129 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +43 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +28 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +125 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +79 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +81 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/move-session/SKILL.md +229 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +67 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +91 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/move-session/scripts/move_session.py +1089 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/pm-pdata-conflict-resolution/SKILL.md +67 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/pm-pdata-schema-design/SKILL.md +82 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/pm-project-init/SKILL.md +142 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +152 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +74 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +109 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +65 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +72 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +162 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +53 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +6 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +34 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/select-agent-model/SKILL.md +52 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/send-session-message/SKILL.md +63 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/update-command-cache/SKILL.md +62 -0
- cc_session_tools-2.3.0/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +218 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0/src/cc_session_tools.egg-info}/PKG-INFO +1 -1
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools.egg-info/SOURCES.txt +33 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/bash_hard_deny.py +55 -3
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/catchup.py +15 -4
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/messaging_deliver.py +9 -4
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_bash_hard_deny.py +129 -0
- cc_session_tools-2.3.0/tests/test_ccst_bundle_discovery.py +52 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_doctor.py +11 -11
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_hooks_install_prune.py +1 -1
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_hooks_install_zero_arg.py +3 -3
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_skills_uninstall.py +3 -3
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/LICENSE +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/README.md +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/setup.cfg +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/ccd.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/ccmsg.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/ccr.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/ccs.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/ccsched.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/hooks_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/claude_flags.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/claude_md_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/db.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/debug.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/doctor.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/hook_registry.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/levenshtein.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/lock.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/message.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/repository.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/retention.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/service.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/messaging/store.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/paths.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/backup.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/classify.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/cutover.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/importers.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/init_service.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/naming.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/repository.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/service.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/store.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/pdata/verify.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/picker.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/prompts.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/roots.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/rules.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/due.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/state.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/store.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/session_gc.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/sessions.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/sessions_db.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/sessions_repair.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/shell_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/tasklist.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools/lib/telemetry_store.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools.egg-info/requires.txt +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cc_session_tools.egg-info/top_level.txt +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/after_response.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/bash_security_review.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/cache.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/confirm_8digit.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/last_screenshot.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/marker_allow.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/markers.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/normalise.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/pending_migration.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/session_tag.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/stats.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/telemetry.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/telemetry_query.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/telemetry_trim.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/transcript.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/cccs_hooks/worklog_guard.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/__init__.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/attribution.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/cache.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/ccusage_wrapper.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/data/pricing.json +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/parent_inference.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/parser.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/pricing.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/query.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/report.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/schema.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/src/claude_code_usage/session_names.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_after_response.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_attribution.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_bash_security_review.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cache.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cache_sqlite.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccr_orphans.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccs_emptiness.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccs_eta.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccs_list_mode.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccs_scope_flags.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccs_sentinel_sort.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccs_session_counts.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_gc_report.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_hook_dispatcher.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_hooks_uninstall.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_install_everything.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_pdata_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_pdata_init_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_pdata_verify_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_repair_sessions_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_sessions_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_shell_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_skills_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_telemetry_query.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccst_telemetry_trim.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_ccusage_wrapper.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_claude_flags.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_claude_md_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cli.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cli_ccd.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cli_ccd_dry_run.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cli_ccr.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cli_ccs.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_cli_version.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_confirm_8digit.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_db.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_debug.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_doctor_mutes.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_empty_session.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_hook_cache.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_hooks_install.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_last_screenshot.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_levenshtein.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_marker_allow.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_migrate_ccmsg.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_migrate_ccsched.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_migrate_sessions_db.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_migrate_telemetry.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_normalise.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_parent_inference.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_parser.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_paths.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_pending_migration_hook.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_picker.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_pricing.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_prompts.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_query.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_report.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_roots.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_rules.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_scheduler_bundled_jobs.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_schema.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_session_names.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_session_tag.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_sessions.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_sessions_db.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_sessions_repair.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_tasklist.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_telemetry.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_telemetry_store.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_transcript.py +0 -0
- {cc_session_tools-2.2.0 → cc_session_tools-2.3.0}/tests/test_worklog_guard.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cc-session-tools"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.0"
|
|
8
8
|
description = "Claude Code session management CLI tools (ccd, ccr, ccs, claude-code-usage, ccst) and hook library (cccs_hooks)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -55,12 +55,24 @@ cccs-stats = "cccs_hooks.stats:main"
|
|
|
55
55
|
|
|
56
56
|
[tool.setuptools.packages.find]
|
|
57
57
|
where = ["src"]
|
|
58
|
+
# skills/ and config/ are bundled data, not importable code - without this
|
|
59
|
+
# exclude, setuptools' auto-discovery treats every skills/<name>/ subdirectory
|
|
60
|
+
# containing a .py file (scripts, dev-only test suites) as its own importable
|
|
61
|
+
# sub-package, which then bypasses exclude-package-data entirely (that option
|
|
62
|
+
# only filters within the file's *own* discovered package, and a wrongly
|
|
63
|
+
# auto-discovered "cc_session_tools.skills.<name>.tests" package was never
|
|
64
|
+
# covered by an exclude-package-data entry keyed just "cc_session_tools").
|
|
65
|
+
exclude = ["cc_session_tools.skills", "cc_session_tools.skills.*", "cc_session_tools.config", "cc_session_tools.config.*"]
|
|
58
66
|
|
|
59
67
|
[tool.setuptools.package-data]
|
|
60
68
|
claude_code_usage = ["data/*.json"]
|
|
69
|
+
cc_session_tools = ["skills/**/*", "config/*"]
|
|
70
|
+
|
|
71
|
+
[tool.setuptools.exclude-package-data]
|
|
72
|
+
cc_session_tools = ["**/tests/*", "**/__pycache__/*"]
|
|
61
73
|
|
|
62
74
|
[tool.pytest.ini_options]
|
|
63
|
-
testpaths = ["tests", "tests/scheduler", "skills/move-session/tests", "skills/list-empty-sessions/tests", "skills/delete-sessions/tests", "skills/generate-8digit-code/tests", "skills/reduce-persistent-context/tests", "skills/update-command-cache/tests"]
|
|
75
|
+
testpaths = ["tests", "tests/scheduler", "src/cc_session_tools/skills/move-session/tests", "src/cc_session_tools/skills/list-empty-sessions/tests", "src/cc_session_tools/skills/delete-sessions/tests", "src/cc_session_tools/skills/generate-8digit-code/tests", "src/cc_session_tools/skills/reduce-persistent-context/tests", "src/cc_session_tools/skills/update-command-cache/tests"]
|
|
64
76
|
addopts = "-q"
|
|
65
77
|
pythonpath = ["src"]
|
|
66
78
|
|
|
@@ -88,17 +88,15 @@ from cc_session_tools.lib.hook_registry import HOOK_DESCRIPTIONS, HOOK_VERBS
|
|
|
88
88
|
def _discover_source_dir() -> Path:
|
|
89
89
|
"""Return the bundled skills/ directory.
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
skills/ is packaged inside cc_session_tools (declared as package-data in
|
|
92
|
+
pyproject.toml), so it sits at the same relative depth from this module
|
|
93
|
+
whether cc_session_tools is an editable source checkout or an installed
|
|
94
|
+
wheel: src/cc_session_tools/cli/ccst.py -> ../skills. No install-location
|
|
95
|
+
fallback is needed - a fallback here would just mask a broken install.
|
|
93
96
|
"""
|
|
94
|
-
|
|
95
|
-
here = Path(__file__).resolve()
|
|
96
|
-
candidate = here.parent.parent.parent.parent / "skills"
|
|
97
|
+
candidate = Path(__file__).resolve().parent.parent / "skills"
|
|
97
98
|
if candidate.is_dir():
|
|
98
99
|
return candidate
|
|
99
|
-
fallback = Path.home() / "repos" / "claude-code-session-tools" / "skills"
|
|
100
|
-
if fallback.is_dir():
|
|
101
|
-
return fallback
|
|
102
100
|
raise FileNotFoundError(
|
|
103
101
|
"Cannot locate bundled skills/ directory. "
|
|
104
102
|
"Run from the source tree or use --source to specify the path explicitly."
|
|
@@ -108,18 +106,12 @@ def _discover_source_dir() -> Path:
|
|
|
108
106
|
def _discover_bundle() -> Path:
|
|
109
107
|
"""Return the bundled config/hooks-bundle.json path.
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
config/ is packaged inside cc_session_tools the same way skills/ is (see
|
|
110
|
+
_discover_source_dir) - src/cc_session_tools/cli/ccst.py -> ../config.
|
|
113
111
|
"""
|
|
114
|
-
|
|
115
|
-
candidate = here.parent.parent.parent.parent / "config" / "hooks-bundle.json"
|
|
112
|
+
candidate = Path(__file__).resolve().parent.parent / "config" / "hooks-bundle.json"
|
|
116
113
|
if candidate.is_file():
|
|
117
114
|
return candidate
|
|
118
|
-
fallback = (
|
|
119
|
-
Path.home() / "repos" / "claude-code-session-tools" / "config" / "hooks-bundle.json"
|
|
120
|
-
)
|
|
121
|
-
if fallback.is_file():
|
|
122
|
-
return fallback
|
|
123
115
|
raise FileNotFoundError(
|
|
124
116
|
"Cannot locate bundled config/hooks-bundle.json. "
|
|
125
117
|
"Run from the source tree or use --source to specify the path explicitly."
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "ccst hooks run session-tag",
|
|
9
|
+
"timeout": 5,
|
|
10
|
+
"statusMessage": "Checking cc wrapper session tag..."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "command",
|
|
14
|
+
"command": "ccst hooks run messaging-deliver",
|
|
15
|
+
"timeout": 10,
|
|
16
|
+
"statusMessage": "Delivering inter-session messages..."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "ccst hooks run catchup",
|
|
21
|
+
"timeout": 10,
|
|
22
|
+
"statusMessage": "Reconciling scheduled-task catch-up..."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"type": "command",
|
|
26
|
+
"command": "ccst hooks run pending-migration",
|
|
27
|
+
"timeout": 5,
|
|
28
|
+
"statusMessage": "Checking for unmigrated legacy data stores..."
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"UserPromptSubmit": [
|
|
34
|
+
{
|
|
35
|
+
"hooks": [
|
|
36
|
+
{
|
|
37
|
+
"type": "command",
|
|
38
|
+
"command": "ccst hooks run last-screenshot",
|
|
39
|
+
"timeout": 5,
|
|
40
|
+
"statusMessage": "Resolving latest screenshot for >lss..."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"command": "ccst hooks run messaging-deliver",
|
|
45
|
+
"timeout": 10,
|
|
46
|
+
"statusMessage": "Checking for new inter-session messages..."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"type": "command",
|
|
50
|
+
"command": "ccst hooks run catchup",
|
|
51
|
+
"timeout": 10,
|
|
52
|
+
"statusMessage": "Surfacing scheduled-task catch-up..."
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"PreToolUse": [
|
|
58
|
+
{
|
|
59
|
+
"matcher": "Bash",
|
|
60
|
+
"hooks": [
|
|
61
|
+
{
|
|
62
|
+
"type": "command",
|
|
63
|
+
"command": "ccst hooks run bash-hard-deny",
|
|
64
|
+
"timeout": 5,
|
|
65
|
+
"statusMessage": "Checking Bash command against hard-deny rules..."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"type": "command",
|
|
69
|
+
"command": "ccst hooks run marker-allow",
|
|
70
|
+
"timeout": 5,
|
|
71
|
+
"statusMessage": "Checking for skill-marker touch..."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "command",
|
|
75
|
+
"command": "ccst hooks run bash-security-review",
|
|
76
|
+
"timeout": 30,
|
|
77
|
+
"statusMessage": "Reviewing shell command for security risks..."
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"hooks": [
|
|
83
|
+
{
|
|
84
|
+
"type": "command",
|
|
85
|
+
"command": "ccst hooks run confirm-8digit",
|
|
86
|
+
"timeout": 10,
|
|
87
|
+
"statusMessage": "Checking 8-digit confirmation gate..."
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"Stop": [
|
|
93
|
+
{
|
|
94
|
+
"hooks": [
|
|
95
|
+
{
|
|
96
|
+
"type": "command",
|
|
97
|
+
"command": "ccst hooks run after-response",
|
|
98
|
+
"timeout": 10,
|
|
99
|
+
"statusMessage": "Recording session activity..."
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"PreCompact": [
|
|
105
|
+
{
|
|
106
|
+
"matcher": "manual",
|
|
107
|
+
"hooks": [
|
|
108
|
+
{
|
|
109
|
+
"type": "command",
|
|
110
|
+
"command": "ccst hooks run worklog-guard",
|
|
111
|
+
"timeout": 5,
|
|
112
|
+
"statusMessage": "Checking WORKLOG.md freshness..."
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analyse-cc-usage
|
|
3
|
+
description: Analyse the user's Claude Code usage across projects, sessions, MCP servers, plugins, tools, time and models, in tokens AND dollars. Use whenever the user asks about their Claude Code usage in any way - "how much have I spent", "tokens used last week", "which project costs the most", "how much did Opus cost in April", "MCP usage by project", "compare Sonnet and Opus", "show my sessions in oneshot last month", "how often am I using the github plugin", "give me a usage report for the quarter", "what tools do I lean on", "any spike in usage recently". Backed by the cc-session-tools repo at ~/repos/claude-code-session-tools (also on GitHub at raffishquartan/claude-code-session-tools). Reconciles token totals against ccusage so we know the numbers are right.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# analyse-cc-usage
|
|
7
|
+
|
|
8
|
+
Use this skill whenever the user asks anything about their Claude Code usage.
|
|
9
|
+
The CLI behind this skill (`claude-code-usage`) is the canonical source.
|
|
10
|
+
Run it behind the scenes and summarise the result in the chat - don't
|
|
11
|
+
make the user look at raw markdown tables unless they ask for them.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
Trigger phrases (non-exhaustive):
|
|
16
|
+
|
|
17
|
+
- "how much have I spent on Claude Code"
|
|
18
|
+
- "tokens used [yesterday|this week|in April|...]"
|
|
19
|
+
- "[which|what] project [costs the most|uses the most tokens|...]"
|
|
20
|
+
- "MCP usage by project"
|
|
21
|
+
- "Opus vs Sonnet usage"
|
|
22
|
+
- "show my sessions in <project> last month"
|
|
23
|
+
- "how often am I using the <plugin> plugin"
|
|
24
|
+
- "full usage report for [quarter|year|...]"
|
|
25
|
+
- "any spike in usage recently"
|
|
26
|
+
- "do I have any expensive sessions"
|
|
27
|
+
- "what tools am I leaning on"
|
|
28
|
+
|
|
29
|
+
If the user's question maps to one of those, invoke the CLI rather than
|
|
30
|
+
guessing or trying to read the JSONLs yourself.
|
|
31
|
+
|
|
32
|
+
## How to translate questions into CLI invocations
|
|
33
|
+
|
|
34
|
+
The CLI's grammar:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
claude-code-usage query
|
|
38
|
+
[--since YYYY-MM-DD] [--until YYYY-MM-DD]
|
|
39
|
+
[--project NAME] [--session NAME_OR_UUID] [--model SUBSTR]
|
|
40
|
+
[--mcp SERVER] [--plugin PLUGIN] [--tool TOOL]
|
|
41
|
+
[--group-by D1,D2,...]
|
|
42
|
+
[--format markdown|csv|json] [--top N] [--sort COL]
|
|
43
|
+
[--session-format name|uuid|both]
|
|
44
|
+
[--include-children]
|
|
45
|
+
[--exclude-hooks]
|
|
46
|
+
|
|
47
|
+
claude-code-usage children <PARENT_SESSION>
|
|
48
|
+
[--format markdown|csv|json] [--top N] [--sort COL]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`--include-children`: when grouping by session, add a cost breakdown showing
|
|
52
|
+
agent subagent activity and hook session costs. Two child types are handled:
|
|
53
|
+
|
|
54
|
+
- **Agent subagents** (the `Agent()` tool): their JSONL files record the
|
|
55
|
+
parent's `sessionId`, so their costs are **already included** in `cost_usd`.
|
|
56
|
+
`--include-children` adds `agent_cost_usd` (what fraction of `cost_usd` came
|
|
57
|
+
from agent dispatches) and keeps the parent row visible.
|
|
58
|
+
- **Hook sessions** (bash-security-review): these have their own UUID and are
|
|
59
|
+
billed separately. They are removed from the session list and their cost is
|
|
60
|
+
folded into `child_cost_usd` on the parent row.
|
|
61
|
+
|
|
62
|
+
Added columns: `agent_cost_usd`, `child_session_count`, `child_token_total`,
|
|
63
|
+
`child_cost_usd`, `total_cost_usd` (= `cost_usd + child_cost_usd`).
|
|
64
|
+
|
|
65
|
+
**Important:** `query --group-by session` already includes agent subagent costs
|
|
66
|
+
in `cost_usd`. Do NOT add `agent_cost_usd` to `cost_usd` — that would
|
|
67
|
+
double-count. `agent_cost_usd` is a breakdown of cost already in `cost_usd`.
|
|
68
|
+
|
|
69
|
+
`--exclude-hooks`: strip `bash-security-review.sh` hook sessions
|
|
70
|
+
(classified as `initiation_type="hook-security-review"`) from all results
|
|
71
|
+
before grouping. Useful whenever you want per-session cost breakdowns without
|
|
72
|
+
the tiny ~$1.60 hook sessions distorting the totals. Use `--include-hooks`
|
|
73
|
+
(alias for the default) to make it explicit that hooks are included.
|
|
74
|
+
|
|
75
|
+
`--group-by` dimensions: `project, session, model, mcp, plugin, tool,
|
|
76
|
+
day, week, month, year`. Combine freely.
|
|
77
|
+
|
|
78
|
+
When grouping by `session`, the output column defaults to the human
|
|
79
|
+
display name set via `claude -n` / `ccd <tag>` (e.g.
|
|
80
|
+
`20260509-oneshot-test-claude-usage-skill`). Sessions whose name we
|
|
81
|
+
have never seen in `~/.claude/sessions/*.json` (the live registry, which
|
|
82
|
+
is ephemeral) are rendered as `sess-<uuid8>`. Pass
|
|
83
|
+
`--session-format uuid` to get the full UUID instead, or
|
|
84
|
+
`--session-format both` for a `session_name` + `session_id` pair.
|
|
85
|
+
|
|
86
|
+
`--session` filters accept either a name (full or case-insensitive
|
|
87
|
+
substring) or a UUID (full or 4+ char prefix), so users who think in
|
|
88
|
+
names don't need to look up UUIDs.
|
|
89
|
+
|
|
90
|
+
Recipes:
|
|
91
|
+
|
|
92
|
+
| User asks | Run |
|
|
93
|
+
|-------------------------------------------------------|-------------------------------------------------------------------------------------------|
|
|
94
|
+
| "How much have I spent in the last week?" | `claude-code-usage query --since "$(date -d '7 days ago' +%F)"` |
|
|
95
|
+
| "Tokens by project last week" | `claude-code-usage query --since "$(date -d '7 days ago' +%F)" --group-by project` |
|
|
96
|
+
| "Opus vs Sonnet usage in April" | `claude-code-usage query --since 2026-04-01 --until 2026-05-01 --group-by model` |
|
|
97
|
+
| "MCP usage by project for Feb / Mar / Apr by month" | `claude-code-usage query --since 2026-02-01 --until 2026-05-01 --group-by project,mcp,month` |
|
|
98
|
+
| "Sessions in oneshot in April 2026" | `claude-code-usage query --project oneshot --since 2026-04-01 --until 2026-05-01 --group-by session` |
|
|
99
|
+
| "Top sessions in coparenting by cost" | `claude-code-usage query --project coparenting --group-by session` |
|
|
100
|
+
| "What's the UUID for the test-claude-usage session?" | `claude-code-usage query --session test-claude-usage --group-by session --session-format both` |
|
|
101
|
+
| "Show today's sessions with UUIDs" | `claude-code-usage query --since "$(date +%F)" --group-by session --session-format both` |
|
|
102
|
+
| "How much does opentabs eat in tokens?" | `claude-code-usage query --mcp opentabs --group-by month` |
|
|
103
|
+
| "Which native tools cost the most?" | `claude-code-usage query --group-by tool` then filter to kind=native in your summary |
|
|
104
|
+
| "Full report for Q2" | `claude-code-usage report --since 2026-04-01 --until 2026-07-01 --output cc-sessions/<tag>/out/usage-report.md` |
|
|
105
|
+
| "Are my numbers right?" / "Reconcile against ccusage" | `claude-code-usage reconcile --since 2026-04-01 --until 2026-05-01` |
|
|
106
|
+
| "Show session cost breakdown (interactive vs agents vs hooks)" | `claude-code-usage query --group-by session --include-children` |
|
|
107
|
+
| "What did this session's agents / hooks cost?" | `claude-code-usage children <session-name-or-uuid>` |
|
|
108
|
+
| "Session cost breakdown excluding hook fires" | `claude-code-usage query --group-by session --exclude-hooks` |
|
|
109
|
+
|
|
110
|
+
After running, summarise the headline figure(s) in chat in plain English.
|
|
111
|
+
For complex breakdowns, save the full table to the session's `out/`
|
|
112
|
+
directory and reference the path in the summary.
|
|
113
|
+
|
|
114
|
+
## What's in the data
|
|
115
|
+
|
|
116
|
+
Each row of the fact table is one billable assistant message and carries:
|
|
117
|
+
`ts, session_id, project_name (= basename(cwd)), model, input_tokens,
|
|
118
|
+
cache_creation_5m, cache_creation_1h, cache_read, output_tokens,
|
|
119
|
+
tool_calls, message_id, ...`. Token-to-tool attribution splits a
|
|
120
|
+
message's tokens evenly across its `tool_use` blocks; tool-call counts
|
|
121
|
+
are tracked separately as a more reliable metric.
|
|
122
|
+
|
|
123
|
+
**Parent/child session grouping (v0.3.0):** The parser classifies each
|
|
124
|
+
session as `regular`, `hook`, or `subagent` and infers a `parent_session_id`
|
|
125
|
+
where possible. Hook sessions (bash security-review checks) are linked to
|
|
126
|
+
their parent when the `bash-security-review.sh` hook was able to embed a
|
|
127
|
+
session-name prefix in the prompt - this only happens when exactly one
|
|
128
|
+
`cc-sessions/` directory exists at the time the hook fires. Use
|
|
129
|
+
`--include-children` with `--group-by session` to fold hook session
|
|
130
|
+
tokens/cost into the parent row; use `children <session>` to list a
|
|
131
|
+
parent's children individually.
|
|
132
|
+
|
|
133
|
+
**Session metadata columns (v0.5.0):** Two new session-level columns:
|
|
134
|
+
- `initiation_type`: `hook-security-review` | `prompt-file` | `interactive` |
|
|
135
|
+
`unknown`. Classified from the first real user text block. Use
|
|
136
|
+
`--exclude-hooks` to filter out hook sessions in any query.
|
|
137
|
+
- `is_sidechain`: `True` if any record in the session has `isSidechain=True`.
|
|
138
|
+
|
|
139
|
+
TODO: `--group-by initiation_type` is not yet implemented as a grouping
|
|
140
|
+
dimension but would allow breaking usage down by session initiation type.
|
|
141
|
+
|
|
142
|
+
`ccusage` is the canonical source of dollar figures. Our `cost_usd` is
|
|
143
|
+
a self-consistent estimate that may differ - the `reconcile` sub-command
|
|
144
|
+
shows both side by side. **Token counts are guaranteed to reconcile with
|
|
145
|
+
ccusage within 0.5%**.
|
|
146
|
+
|
|
147
|
+
Session display names are sourced from `~/.claude/sessions/*.json`
|
|
148
|
+
(live registry, set by `claude -n`/`ccd`) and merged into a persistent
|
|
149
|
+
on-disk cache at `<cache_dir>/session_names.json` so any name we ever
|
|
150
|
+
saw is preserved after the live record is pruned. **As of v0.5.0,
|
|
151
|
+
`warm-cache` also scans JSONL files for `custom-title` records** (written
|
|
152
|
+
when the user runs `/rename` in a session), so sessions whose PID file was
|
|
153
|
+
pruned before `warm-cache` ran will now be resolved correctly. UUID-only
|
|
154
|
+
fallback (`sess-<uuid8>`) covers sessions launched without `-n` that were
|
|
155
|
+
never renamed.
|
|
156
|
+
|
|
157
|
+
## Setup on a new machine
|
|
158
|
+
|
|
159
|
+
This block lives in the skill body (not the frontmatter / preview note)
|
|
160
|
+
so that it travels with the skill when it's deployed elsewhere.
|
|
161
|
+
|
|
162
|
+
The recipes above call `claude-code-usage` directly (not `uv run
|
|
163
|
+
claude-code-usage`). For that to work, the CLI must be on `PATH`. The
|
|
164
|
+
canonical install is via `uv tool install ~/repos/claude-code-session-tools`,
|
|
165
|
+
which builds a wheel from the local clone and drops shims into `uv tool dir
|
|
166
|
+
--bin` (typically `~/.local/bin`). Non-editable install means the source is
|
|
167
|
+
copied at install time - no fragile `.pth` pointer that breaks when a git
|
|
168
|
+
worktree is deleted.
|
|
169
|
+
|
|
170
|
+
> **WARNING:** Never run `uv tool install` from inside a git worktree. If you
|
|
171
|
+
> develop on a feature branch in a worktree and need to test the CLI, use
|
|
172
|
+
> `uv run` within the worktree instead. After merging a PR, run
|
|
173
|
+
> `uv tool install ~/repos/claude-code-session-tools` from any directory to
|
|
174
|
+
> update the global install.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# 1. Clone the repo
|
|
178
|
+
git clone https://github.com/raffishquartan/claude-code-session-tools.git \
|
|
179
|
+
~/repos/claude-code-session-tools
|
|
180
|
+
|
|
181
|
+
# 2. Install the CLI globally (puts ccd, ccr, ccs, claude-code-usage on PATH)
|
|
182
|
+
# Install uv first if needed: https://docs.astral.sh/uv/getting-started/
|
|
183
|
+
uv tool install ~/repos/claude-code-session-tools
|
|
184
|
+
claude-code-usage --version # should print the version
|
|
185
|
+
# If tools are not found, ensure `uv tool dir --bin` is on your PATH
|
|
186
|
+
# (run `uv tool update-shell` or add it to your shell rc file).
|
|
187
|
+
|
|
188
|
+
# 3. (Optional) project-local dev venv with test deps, for hacking on
|
|
189
|
+
# the CLI. Not required for normal skill use.
|
|
190
|
+
uv sync --extra dev
|
|
191
|
+
uv run pytest # all 350+ tests should pass
|
|
192
|
+
|
|
193
|
+
# 4. Install ccusage globally so reconciliation works
|
|
194
|
+
# Install bun first if needed: https://bun.com/docs/installation
|
|
195
|
+
bun add -g ccusage
|
|
196
|
+
ccusage --version
|
|
197
|
+
|
|
198
|
+
# 5. The skill lives inside the cc-session-tools repo at skills/analyse-cc-usage/.
|
|
199
|
+
# Symlink it to ~/.claude/skills/ so it is discoverable:
|
|
200
|
+
ln -s ~/repos/claude-code-session-tools/src/cc_session_tools/skills/analyse-cc-usage \
|
|
201
|
+
~/.claude/skills/analyse-cc-usage
|
|
202
|
+
|
|
203
|
+
# 6. Warm the cache (first run is the slow one)
|
|
204
|
+
claude-code-usage warm-cache
|
|
205
|
+
|
|
206
|
+
# 7. Sanity-check by reconciling against ccusage
|
|
207
|
+
claude-code-usage reconcile
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The repo can also be sourced from GitHub directly (via the upstream
|
|
211
|
+
remote) if you don't want to clone it - see the README's "Install"
|
|
212
|
+
section for that path.
|
|
213
|
+
|
|
214
|
+
## Performance notes
|
|
215
|
+
|
|
216
|
+
The mtime-keyed parquet cache makes re-runs sub-second:
|
|
217
|
+
|
|
218
|
+
- Cold parse of `~/.claude/projects` (1.7 GB / 7,256 files): ~11 s
|
|
219
|
+
- Warm with no changes: < 1 s
|
|
220
|
+
- Warm with a few new sessions since last run: ~3 s
|
|
221
|
+
|
|
222
|
+
Cache location: `~/.cache/claude-code-usage/parquet/` by default
|
|
223
|
+
(platformdirs user_cache_dir). Override with `--cache-dir`.
|
|
224
|
+
|
|
225
|
+
## Repo
|
|
226
|
+
|
|
227
|
+
- Local: `~/repos/claude-code-session-tools`
|
|
228
|
+
- GitHub: <https://github.com/raffishquartan/claude-code-session-tools> (public, MIT)
|
|
229
|
+
- Run `uv run pytest` after any change. The schema sanity check is the
|
|
230
|
+
early warning if Anthropic ever changes the JSONL format.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: delete-sessions
|
|
3
|
+
description: Use when the user wants to permanently delete one or more Claude Code sessions. Triggers on "delete session", "delete sessions", "remove empty sessions", "clean up sessions", "delete these sessions", "remove these cc-sessions", "/delete-sessions". Inputs must be explicit session basenames; the user (or the list-empty-sessions skill) must supply them. Gates every deletion behind an 8-digit confirmation code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) 2026 raffishquartan. All rights reserved.
|
|
8
|
+
Licensed for personal use only.
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
# Delete sessions
|
|
12
|
+
|
|
13
|
+
Permanently delete one or more Claude Code sessions: the `cc-sessions/<basename>/`
|
|
14
|
+
directory, the JSONL transcript, the `.tag` file, and (optionally) the
|
|
15
|
+
`~/.claude/tasks/<encoded>/` task directory.
|
|
16
|
+
|
|
17
|
+
**Dry-run by default.** The script prints a deletion plan and exits 0. Pass
|
|
18
|
+
`--execute` to actually delete. Always review the plan before executing.
|
|
19
|
+
|
|
20
|
+
**8-digit confirmation gate.** In `--execute` mode the script generates an
|
|
21
|
+
8-digit code, prints it, and requires the user to type it back exactly. The
|
|
22
|
+
deletion proceeds only if the code matches.
|
|
23
|
+
|
|
24
|
+
## When to use
|
|
25
|
+
|
|
26
|
+
- The user explicitly names one or more session basenames and asks to delete
|
|
27
|
+
them.
|
|
28
|
+
- The `list-empty-sessions` skill has produced a list of basenames and the
|
|
29
|
+
user confirms they want to remove them.
|
|
30
|
+
|
|
31
|
+
## When NOT to use
|
|
32
|
+
|
|
33
|
+
- Listing which sessions are empty — that is the `list-empty-sessions` skill.
|
|
34
|
+
This skill deletes; it does not discover.
|
|
35
|
+
- Cleaning up hook-security-check transcripts — that is the
|
|
36
|
+
`clean-hook-sessions` skill.
|
|
37
|
+
- The user wants to move or rename a session — use `move-session`.
|
|
38
|
+
|
|
39
|
+
## Inputs
|
|
40
|
+
|
|
41
|
+
Positional arguments only: one or more session **basenames** (the
|
|
42
|
+
`YYYYMMDD-<tag>` directory name under `cc-sessions/`).
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
python3 ~/.claude/skills/delete-sessions/scripts/delete_sessions.py \
|
|
46
|
+
20260516-demo-empty 20260517-beta-empty
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The script searches for each basename locally first (under the current
|
|
50
|
+
project's `cc-sessions/`), then across all configured roots. It does **not**
|
|
51
|
+
accept filesystem paths — pass the basename, not the full path.
|
|
52
|
+
|
|
53
|
+
## Pre-flight checks (all required — fail fast on any)
|
|
54
|
+
|
|
55
|
+
1. **Format check.** Every basename must match the session-name format
|
|
56
|
+
(`YYYYMMDD-<tag>` pattern). If any does not, the script exits 1 immediately.
|
|
57
|
+
|
|
58
|
+
2. **Existence check.** Every basename must resolve to a real
|
|
59
|
+
`cc-sessions/<basename>/` directory somewhere reachable. If any is missing,
|
|
60
|
+
the script exits 1 with a list of what could not be found.
|
|
61
|
+
|
|
62
|
+
3. **In-session refusal.** If any of the named sessions is the *currently
|
|
63
|
+
running* CC session, the script refuses outright with exit code 2. There is
|
|
64
|
+
no override — see "In-session refusal" below.
|
|
65
|
+
|
|
66
|
+
4. **Empty-only guard.** By default the script refuses to delete non-empty
|
|
67
|
+
sessions (those that contain at least one real user-typed message). Pass
|
|
68
|
+
`--allow-non-empty` to bypass this. When blocked, the script lists which
|
|
69
|
+
sessions are non-empty so the user knows what is preventing deletion.
|
|
70
|
+
|
|
71
|
+
## What is deleted (per session)
|
|
72
|
+
|
|
73
|
+
| Artefact | Path |
|
|
74
|
+
|----------|------|
|
|
75
|
+
| Session folder | `<project-cwd>/cc-sessions/<basename>/` |
|
|
76
|
+
| JSONL transcript | `~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` |
|
|
77
|
+
| Tag mapping row | `~/.local/share/claude/sessions.db` — `session_tags` row keyed by `<uuid>`, removed via `sessions_db.delete_tag` (not a filesystem artefact) |
|
|
78
|
+
| Enumeration row | `~/.local/share/claude/sessions.db` — `sessions` row keyed by `(<project-cwd>, <basename>)`, removed via `sessions_db.delete_session_row` so the session stops appearing in `ccr`/`ccs` |
|
|
79
|
+
| Tasks directory | `~/.claude/tasks/<encoded-session-dir>/` (if present, skipped with `--no-tasks`) |
|
|
80
|
+
|
|
81
|
+
The dry-run plan prints the exact paths that would be deleted so you can
|
|
82
|
+
verify before committing.
|
|
83
|
+
|
|
84
|
+
## In-session refusal
|
|
85
|
+
|
|
86
|
+
If the user asks to delete the session they are currently inside, the script
|
|
87
|
+
exits 2 and explains why. There is no override flag.
|
|
88
|
+
|
|
89
|
+
**Detection signals** (same as `move-session`): refusal fires if `in_cc` AND
|
|
90
|
+
**either** (a) the session JSONL was modified within the last 30 seconds, OR
|
|
91
|
+
(b) `realpath(getcwd()) == session_project_cwd` AND the session JSONL is the
|
|
92
|
+
most-recently-modified non-hook-security JSONL in the project key directory.
|
|
93
|
+
|
|
94
|
+
## Quick reference
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
python3 ~/.claude/skills/delete-sessions/scripts/delete_sessions.py \
|
|
98
|
+
[--allow-non-empty] \
|
|
99
|
+
[--no-tasks] \
|
|
100
|
+
[--execute] \
|
|
101
|
+
<basename> [<basename> ...]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Flag | Meaning |
|
|
105
|
+
|------|---------|
|
|
106
|
+
| `<basename>` | One or more `YYYYMMDD-<tag>` basenames. Required. |
|
|
107
|
+
| `--allow-non-empty` | Also delete sessions that contain real user messages. Still 8-digit gated. |
|
|
108
|
+
| `--no-tasks` | Skip `~/.claude/tasks/<encoded>/` deletion. |
|
|
109
|
+
| `--execute` | Actually delete. Without this flag the script only prints a plan. |
|
|
110
|
+
|
|
111
|
+
## 8-digit confirmation flow (--execute only)
|
|
112
|
+
|
|
113
|
+
1. The script prints the deletion plan.
|
|
114
|
+
2. It generates a fresh 8-digit code and prints it.
|
|
115
|
+
3. The user types the code back.
|
|
116
|
+
4. If the code matches exactly, deletion proceeds. If not, the script exits 1
|
|
117
|
+
with no deletions made.
|
|
118
|
+
|
|
119
|
+
The model's role: present the code to the user with the line
|
|
120
|
+
`"Respond with <code> to confirm deletion."` and then pass the user's reply
|
|
121
|
+
into the script's stdin.
|
|
122
|
+
|
|
123
|
+
## Deletion behaviour
|
|
124
|
+
|
|
125
|
+
- Artefacts are deleted one at a time; each deletion is reported on its own
|
|
126
|
+
line.
|
|
127
|
+
- On any `OSError` the error is printed and the script continues (best-effort
|
|
128
|
+
for the remaining artefacts).
|
|
129
|
+
- Exit `0` if every artefact was either deleted or already absent.
|
|
130
|
+
- Exit `1` if any `OSError` occurred during deletion.
|
|
131
|
+
|
|
132
|
+
## Common mistakes
|
|
133
|
+
|
|
134
|
+
- **Forgetting `--execute`.** Default is dry-run. The plan is printed but
|
|
135
|
+
nothing is deleted. Check the output for `DRY-RUN` before reporting success.
|
|
136
|
+
|
|
137
|
+
- **Trying to delete the currently-running session.** The script detects this
|
|
138
|
+
and refuses with exit 2. Exit CC first, then re-run the script from a normal
|
|
139
|
+
shell.
|
|
140
|
+
|
|
141
|
+
- **Trying to delete a non-empty session without `--allow-non-empty`.** If the
|
|
142
|
+
JSONL contains even one real user message, the script refuses by default.
|
|
143
|
+
This is intentional — non-empty sessions might contain valuable work. Review
|
|
144
|
+
the session contents before using `--allow-non-empty`.
|
|
145
|
+
|
|
146
|
+
- **Confusing basename with just the tag portion.** The script expects the full
|
|
147
|
+
`YYYYMMDD-<tag>` basename (e.g. `20260516-demo-session`), not just the tag
|
|
148
|
+
(`demo-session`). The `list-empty-sessions` skill outputs full basenames
|
|
149
|
+
ready for copy-paste.
|
|
150
|
+
|
|
151
|
+
## Defaults and safety
|
|
152
|
+
|
|
153
|
+
- **Dry-run by default.** Pass `--execute` to make filesystem changes.
|
|
154
|
+
- **Empty-only by default.** Pass `--allow-non-empty` to also delete sessions
|
|
155
|
+
with real user messages.
|
|
156
|
+
- **8-digit gated.** All `--execute` runs require a confirmation code.
|
|
157
|
+
- **No source clobber recovery.** Deletion is permanent. Unlike `move-session`,
|
|
158
|
+
there is no cleanup script — once you confirm, the files are gone.
|