cc-session-tools 3.5.3__tar.gz → 3.7.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-3.5.3/src/cc_session_tools.egg-info → cc_session_tools-3.7.0}/PKG-INFO +3 -3
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/README.md +2 -2
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/pyproject.toml +1 -1
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/ccst.py +81 -0
- cc_session_tools-3.7.0/src/cc_session_tools/lib/bun_tools.py +39 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/doctor.py +68 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +22 -2
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0/src/cc_session_tools.egg-info}/PKG-INFO +3 -3
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools.egg-info/SOURCES.txt +6 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/ccusage_wrapper.py +12 -6
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/bash_security_review.py +166 -13
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/cache.py +94 -0
- cc_session_tools-3.7.0/src/hooks/script_allowlist.py +145 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_bash_security_review.py +78 -4
- cc_session_tools-3.7.0/tests/test_bash_security_review_allowlist.py +330 -0
- cc_session_tools-3.7.0/tests/test_bun_tools.py +67 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_doctor.py +101 -0
- cc_session_tools-3.7.0/tests/test_ccst_hooks_allowlist.py +114 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccusage_wrapper.py +55 -0
- cc_session_tools-3.7.0/tests/test_script_allowlist.py +288 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/LICENSE +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/setup.cfg +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/ccd.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/ccmsg.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/ccr.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/ccs.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/ccsched.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/config/hooks-bundle.json +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/config/session-tag-hook.json +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/hooks_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/claude_flags.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/claude_md_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/context_overrides.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/db.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/debug.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/hook_registry.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/install_sync.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/levenshtein.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/machine_identity.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/lock.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/message.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/repository.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/retention.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/service.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/messaging/store.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/occupancy.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/paths.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/backup.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/classify.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/cutover.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/dump.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/importers.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/init_service.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/naming.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/repository.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/service.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/store.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/verify.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/picker.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/proc_lock.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/prompts.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/roots.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/rules.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/due.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/state.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/store.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/session_gc.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/sessions.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/sessions_db.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/sessions_repair.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/shell_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/tasklist.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/lib/telemetry_store.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/prompts/pdata-migration-skills-update.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/extract-pptx/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/extract-pptx/scripts/extract_pptx.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/extract-to-text/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/extract-to-text/scripts/extract_to_text.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-pdata-design-schema/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-pdata-do-init/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-pdata-do-manual-migration/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-pdata-resolve-conflicts/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools.egg-info/requires.txt +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/cc_session_tools.egg-info/top_level.txt +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/attribution.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/cache.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/data/pricing.json +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/parent_inference.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/parser.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/pricing.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/query.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/report.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/schema.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/claude_code_usage/session_names.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/__init__.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/after_response.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/bash_hard_deny.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/catchup.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/confirm_8digit.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/context_window_warning.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/last_screenshot.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/marker_allow.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/markers.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/messaging_deliver.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/model_info.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/normalise.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/pdata_sync.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/pending_migration.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/pending_rename.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/session_tag.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/stats.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/telemetry.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/telemetry_query.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/telemetry_trim.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/transcript.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/src/hooks/worklog_guard.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_after_response.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_attribution.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_bash_hard_deny.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cache.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cache_sqlite.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccr_orphans.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccs_emptiness.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccs_eta.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccs_list_mode.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccs_scope_flags.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccs_sentinel_sort.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccs_session_counts.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_bundle_discovery.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_context_override_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_gc_prune.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_gc_report.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_hook_dispatcher.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_hooks_install_prune.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_hooks_uninstall.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_install_everything.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_machine_identity_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_main_gate.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_init_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_sync_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_pdata_verify_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_repair_sessions_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_sessions_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_shell_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_skills_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_skills_uninstall.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_telemetry_query.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_ccst_telemetry_trim.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_claude_flags.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_claude_md_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cli.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cli_ccd.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cli_ccd_dry_run.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cli_ccr.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cli_ccs.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_cli_version.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_confirm_8digit.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_context_overrides.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_context_window_warning.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_db.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_debug.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_doctor_mutes.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_empty_session.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_hook_cache.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_hooks_install.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_install_sync.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_last_screenshot.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_levenshtein.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_machine_identity.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_marker_allow.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_migrate_ccmsg.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_migrate_ccsched.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_migrate_sessions_db.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_migrate_telemetry.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_model_info.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_normalise.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_occupancy.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_parent_inference.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_parser.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_paths.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_pending_migration_hook.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_picker.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_pricing.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_proc_lock.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_prompts.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_query.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_report.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_roots.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_rules.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_scheduler_bundled_jobs.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_schema.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_session_names.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_session_tag.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_sessions.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_sessions_db.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_sessions_repair.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_tasklist.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_telemetry.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_telemetry_store.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_transcript.py +0 -0
- {cc_session_tools-3.5.3 → cc_session_tools-3.7.0}/tests/test_worklog_guard.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cc-session-tools
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.0
|
|
4
4
|
Summary: Claude Code session management CLI tools (ccd, ccr, ccs, claude-code-usage, ccst) and hook library (hooks package)
|
|
5
5
|
Author: raffishquartan
|
|
6
6
|
License: MIT License
|
|
@@ -105,7 +105,7 @@ bundled scheduled jobs:
|
|
|
105
105
|
| **`session-tag`** (SessionStart) | Writes a `<uuid>.tag` file so `claude-code-usage` can map session UUIDs to human-readable names. |
|
|
106
106
|
| **`last-screenshot`** (UserPromptSubmit) | Resolves your newest screenshot for the `>lss` token and injects its path. Requires `CCST_SCREENSHOT_DIR`. |
|
|
107
107
|
| **`bash-hard-deny`** (PreToolUse) | Categorical hard-deny gate for Bash: destructive file ops (`rm`/`rmdir`/`unlink`/`shred`, incl. inline python/node and script-file/heredoc forms), delete-by-move to a tmp-like location, `git branch`/`git push` branch deletion, `gh api`/`gh release` delete, destructive curl/wget methods, `sudo`, and direct telemetry-log reads. Runs before every other Bash hook; a match here can never be overridden in-session (the message tells you to run the command yourself in a separate terminal). |
|
|
108
|
-
| **`bash-security-review`** (PreToolUse) | Tiered Bash command security review with
|
|
108
|
+
| **`bash-security-review`** (PreToolUse) | Tiered Bash command security review: trivial/read-only pre-filters, heuristic escalation, a reviewed-script allowlist (script path + content hash, managed with `ccst hooks allowlist list\|add\|remove\|verify`), a command cache, and a slimmed `claude -p` LLM fallback (~770 tokens per review instead of ~50k+). |
|
|
109
109
|
| **`marker-allow`** (PreToolUse) | Auto-approves a bare `touch` of a skill marker under `~/.cache/claude/markers/` (and nothing else), so marker-gated skills can refresh their TTL marker without a permission prompt. |
|
|
110
110
|
| **`confirm-8digit`** (PreToolUse) | Blocks a configurable set of high-stakes tool calls unless the user repeats back an 8-digit confirmation code. |
|
|
111
111
|
| **`after-response`** (Stop) | Touches a `.last-active` sentinel so `ccs --order-by active` can sort sessions by recency of Claude activity. |
|
|
@@ -151,7 +151,7 @@ If you've ever tried to remember which `1f4a8b3c-...` UUID is the session where
|
|
|
151
151
|
|
|
152
152
|
- **Python 3.11+** (3.12+ recommended)
|
|
153
153
|
- **The `claude` CLI on your `$PATH`.** Install it first via [the official Claude Code instructions](https://docs.anthropic.com/en/docs/claude-code/setup) and verify with `claude --version`.
|
|
154
|
-
- **`ccusage` (optional)** - if on `$PATH
|
|
154
|
+
- **`ccusage` (optional)** - if on `$PATH` (or in bun's global bin dir, `~/.bun/bin`), `claude-code-usage reconcile` cross-checks dollar totals against it. Skipped gracefully if missing. Needs `bun` to install; `ccst doctor` reports both under `deps:bun` / `deps:ccusage` and prints the install command. See the `analyse-cc-usage` skill's setup section.
|
|
155
155
|
- **`ripgrep` (optional)** - `ccs --contents` prefers `rg`; falls back to threaded Python `grep` if missing.
|
|
156
156
|
|
|
157
157
|
### Install and set up
|
|
@@ -48,7 +48,7 @@ bundled scheduled jobs:
|
|
|
48
48
|
| **`session-tag`** (SessionStart) | Writes a `<uuid>.tag` file so `claude-code-usage` can map session UUIDs to human-readable names. |
|
|
49
49
|
| **`last-screenshot`** (UserPromptSubmit) | Resolves your newest screenshot for the `>lss` token and injects its path. Requires `CCST_SCREENSHOT_DIR`. |
|
|
50
50
|
| **`bash-hard-deny`** (PreToolUse) | Categorical hard-deny gate for Bash: destructive file ops (`rm`/`rmdir`/`unlink`/`shred`, incl. inline python/node and script-file/heredoc forms), delete-by-move to a tmp-like location, `git branch`/`git push` branch deletion, `gh api`/`gh release` delete, destructive curl/wget methods, `sudo`, and direct telemetry-log reads. Runs before every other Bash hook; a match here can never be overridden in-session (the message tells you to run the command yourself in a separate terminal). |
|
|
51
|
-
| **`bash-security-review`** (PreToolUse) | Tiered Bash command security review with
|
|
51
|
+
| **`bash-security-review`** (PreToolUse) | Tiered Bash command security review: trivial/read-only pre-filters, heuristic escalation, a reviewed-script allowlist (script path + content hash, managed with `ccst hooks allowlist list\|add\|remove\|verify`), a command cache, and a slimmed `claude -p` LLM fallback (~770 tokens per review instead of ~50k+). |
|
|
52
52
|
| **`marker-allow`** (PreToolUse) | Auto-approves a bare `touch` of a skill marker under `~/.cache/claude/markers/` (and nothing else), so marker-gated skills can refresh their TTL marker without a permission prompt. |
|
|
53
53
|
| **`confirm-8digit`** (PreToolUse) | Blocks a configurable set of high-stakes tool calls unless the user repeats back an 8-digit confirmation code. |
|
|
54
54
|
| **`after-response`** (Stop) | Touches a `.last-active` sentinel so `ccs --order-by active` can sort sessions by recency of Claude activity. |
|
|
@@ -94,7 +94,7 @@ If you've ever tried to remember which `1f4a8b3c-...` UUID is the session where
|
|
|
94
94
|
|
|
95
95
|
- **Python 3.11+** (3.12+ recommended)
|
|
96
96
|
- **The `claude` CLI on your `$PATH`.** Install it first via [the official Claude Code instructions](https://docs.anthropic.com/en/docs/claude-code/setup) and verify with `claude --version`.
|
|
97
|
-
- **`ccusage` (optional)** - if on `$PATH
|
|
97
|
+
- **`ccusage` (optional)** - if on `$PATH` (or in bun's global bin dir, `~/.bun/bin`), `claude-code-usage reconcile` cross-checks dollar totals against it. Skipped gracefully if missing. Needs `bun` to install; `ccst doctor` reports both under `deps:bun` / `deps:ccusage` and prints the install command. See the `analyse-cc-usage` skill's setup section.
|
|
98
98
|
- **`ripgrep` (optional)** - `ccs --contents` prefers `rg`; falls back to threaded Python `grep` if missing.
|
|
99
99
|
|
|
100
100
|
### Install and set up
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cc-session-tools"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.7.0"
|
|
8
8
|
description = "Claude Code session management CLI tools (ccd, ccr, ccs, claude-code-usage, ccst) and hook library (hooks package)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -6,6 +6,8 @@ Current subcommands:
|
|
|
6
6
|
hooks install [--hook <name>] Merge hook entries from the bundled set (or a
|
|
7
7
|
custom --source) into a target settings.json.
|
|
8
8
|
hooks uninstall [--hook <name>] Remove hook entries from a target settings.json.
|
|
9
|
+
hooks allowlist list|add|remove|verify
|
|
10
|
+
Manage the bash-security-review reviewed-script allowlist.
|
|
9
11
|
hooks run <name> Run a Claude Code hook by name.
|
|
10
12
|
Available hooks: bash-hard-deny,
|
|
11
13
|
bash-security-review, marker-allow,
|
|
@@ -1949,6 +1951,65 @@ def _cmd_machine_identity_confirm(args: argparse.Namespace) -> int:
|
|
|
1949
1951
|
# ---------- hooks run ----------
|
|
1950
1952
|
|
|
1951
1953
|
|
|
1954
|
+
def _cmd_hooks_allowlist(args: argparse.Namespace) -> int:
|
|
1955
|
+
"""`ccst hooks allowlist list|add|remove|verify` - the reviewed-script allowlist store."""
|
|
1956
|
+
import hashlib
|
|
1957
|
+
|
|
1958
|
+
from hooks import cache as cache_mod
|
|
1959
|
+
from hooks import script_allowlist
|
|
1960
|
+
|
|
1961
|
+
verb = args.allowlist_verb
|
|
1962
|
+
if verb == "list":
|
|
1963
|
+
entries = cache_mod.allowlist_list()
|
|
1964
|
+
if not entries:
|
|
1965
|
+
print("No allowlisted scripts.")
|
|
1966
|
+
return 0
|
|
1967
|
+
for e in entries:
|
|
1968
|
+
print(
|
|
1969
|
+
f"{e.script_path} source={e.source} sha256={e.sha256[:8]} added={e.added_at} "
|
|
1970
|
+
f"last_used={e.last_used or '-'} uses={e.use_count}"
|
|
1971
|
+
)
|
|
1972
|
+
return 0
|
|
1973
|
+
if verb == "add":
|
|
1974
|
+
path = Path(args.script).expanduser().resolve()
|
|
1975
|
+
if not path.is_file():
|
|
1976
|
+
print(f"error: {args.script}: not a file", file=sys.stderr)
|
|
1977
|
+
return 2
|
|
1978
|
+
root = script_allowlist.find_project_root(path.parent)
|
|
1979
|
+
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
1980
|
+
cache_mod.allowlist_upsert(
|
|
1981
|
+
str(path), digest, str(root), source="manual", overwrite_source=True
|
|
1982
|
+
)
|
|
1983
|
+
print(f"Allowlisted {path} (sha256 {digest[:8]}, project {root})")
|
|
1984
|
+
return 0
|
|
1985
|
+
if verb == "remove":
|
|
1986
|
+
path = Path(args.script).expanduser().resolve()
|
|
1987
|
+
if not cache_mod.allowlist_remove(str(path)):
|
|
1988
|
+
print(f"error: {path} is not in the allowlist", file=sys.stderr)
|
|
1989
|
+
return 1
|
|
1990
|
+
print(f"Removed {path}")
|
|
1991
|
+
return 0
|
|
1992
|
+
# verify
|
|
1993
|
+
drift = 0
|
|
1994
|
+
entries = cache_mod.allowlist_list()
|
|
1995
|
+
if not entries:
|
|
1996
|
+
print("No allowlisted scripts.")
|
|
1997
|
+
return 0
|
|
1998
|
+
for e in entries:
|
|
1999
|
+
p = Path(e.script_path)
|
|
2000
|
+
if not p.is_file():
|
|
2001
|
+
print(f"MISSING {e.script_path}")
|
|
2002
|
+
drift += 1
|
|
2003
|
+
continue
|
|
2004
|
+
current = hashlib.sha256(p.read_bytes()).hexdigest()
|
|
2005
|
+
if current != e.sha256:
|
|
2006
|
+
print(f"CHANGED {e.script_path} {e.sha256[:8]} -> {current[:8]}")
|
|
2007
|
+
drift += 1
|
|
2008
|
+
else:
|
|
2009
|
+
print(f"OK {e.script_path}")
|
|
2010
|
+
return 1 if drift else 0
|
|
2011
|
+
|
|
2012
|
+
|
|
1952
2013
|
def _cmd_hooks_run(args: argparse.Namespace) -> int:
|
|
1953
2014
|
"""Dispatch to the named hook, or exit 1 (never 2) if it is unknown.
|
|
1954
2015
|
|
|
@@ -2598,6 +2659,24 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
2598
2659
|
help="Write changes (default: dry run)",
|
|
2599
2660
|
)
|
|
2600
2661
|
|
|
2662
|
+
# hooks allowlist
|
|
2663
|
+
allowlist_parser = hooks_sub.add_parser(
|
|
2664
|
+
"allowlist",
|
|
2665
|
+
help="Manage the bash-security-review reviewed-script allowlist",
|
|
2666
|
+
)
|
|
2667
|
+
allowlist_sub = allowlist_parser.add_subparsers(dest="allowlist_verb", metavar="<action>")
|
|
2668
|
+
allowlist_sub.required = True
|
|
2669
|
+
allowlist_sub.add_parser("list", help="List allowlisted scripts")
|
|
2670
|
+
allowlist_add = allowlist_sub.add_parser(
|
|
2671
|
+
"add", help="Allowlist a script at its current content hash (you vouch for it)"
|
|
2672
|
+
)
|
|
2673
|
+
allowlist_add.add_argument("script", metavar="<script>", help="Path to the script")
|
|
2674
|
+
allowlist_remove = allowlist_sub.add_parser("remove", help="Remove a script from the allowlist")
|
|
2675
|
+
allowlist_remove.add_argument("script", metavar="<script>", help="Path to the script")
|
|
2676
|
+
allowlist_sub.add_parser(
|
|
2677
|
+
"verify", help="Report allowlisted scripts that changed or no longer exist (exit 1 on drift)"
|
|
2678
|
+
)
|
|
2679
|
+
|
|
2601
2680
|
# hooks run
|
|
2602
2681
|
run_parser = hooks_sub.add_parser(
|
|
2603
2682
|
"run",
|
|
@@ -3500,6 +3579,8 @@ def main() -> None:
|
|
|
3500
3579
|
sys.exit(_cmd_hooks_install(args))
|
|
3501
3580
|
if args.verb == "uninstall":
|
|
3502
3581
|
sys.exit(_cmd_hooks_uninstall(args))
|
|
3582
|
+
if args.verb == "allowlist":
|
|
3583
|
+
sys.exit(_cmd_hooks_allowlist(args))
|
|
3503
3584
|
if args.verb == "run":
|
|
3504
3585
|
sys.exit(_cmd_hooks_run(args))
|
|
3505
3586
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Locate tools installed through bun (bun itself, ccusage).
|
|
2
|
+
|
|
3
|
+
`bun add -g` installs into ``$BUN_INSTALL/bin`` (default ``~/.bun/bin``). That directory is
|
|
4
|
+
only on ``PATH`` if the user edited a shell rc file that the current shell type reads, which
|
|
5
|
+
non-interactive shells (hooks, scheduled jobs, subagent Bash calls) often do not. Resolving the
|
|
6
|
+
directory directly keeps a correct install findable from every shell type.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import dataclasses
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Upstream's installer works on macOS and on Linux/WSL (which also needs `unzip` installed).
|
|
16
|
+
BUN_INSTALL_COMMAND = "curl -fsSL https://bun.sh/install | bash"
|
|
17
|
+
CCUSAGE_INSTALL_COMMAND = "bun add -g ccusage"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
21
|
+
class Resolved:
|
|
22
|
+
path: str
|
|
23
|
+
on_path: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def bun_bin_dir() -> Path:
|
|
27
|
+
install = os.environ.get("BUN_INSTALL")
|
|
28
|
+
return (Path(install) if install else Path.home() / ".bun") / "bin"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def resolve(name: str, *, bin_dir: Path | None = None) -> Resolved | None:
|
|
32
|
+
"""Find *name* on ``PATH`` first, then in bun's global bin directory."""
|
|
33
|
+
found = shutil.which(name)
|
|
34
|
+
if found is not None:
|
|
35
|
+
return Resolved(path=found, on_path=True)
|
|
36
|
+
candidate = (bin_dir if bin_dir is not None else bun_bin_dir()) / name
|
|
37
|
+
if candidate.is_file() and os.access(candidate, os.X_OK):
|
|
38
|
+
return Resolved(path=str(candidate), on_path=False)
|
|
39
|
+
return None
|
|
@@ -21,6 +21,7 @@ from enum import Enum
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import TYPE_CHECKING, Any
|
|
23
23
|
|
|
24
|
+
from cc_session_tools.lib import bun_tools
|
|
24
25
|
from cc_session_tools.lib.db import connect as _db_connect
|
|
25
26
|
from cc_session_tools.lib.db import migration_applied as _migration_applied
|
|
26
27
|
from cc_session_tools.lib.hook_registry import HOOK_VERBS, hook_name_from_command
|
|
@@ -121,6 +122,69 @@ def check_env_dir(
|
|
|
121
122
|
return CheckResult(name=name, status=Status.OK, reason=str(p))
|
|
122
123
|
|
|
123
124
|
|
|
125
|
+
def _check_bun_tool(
|
|
126
|
+
name: str, *, install_hint: str, bin_dir: Path | None, timeout: float
|
|
127
|
+
) -> CheckResult:
|
|
128
|
+
"""WARN-only check for an optional bun-installed tool (see `bun_tools.resolve`)."""
|
|
129
|
+
check_name = f"deps:{name}"
|
|
130
|
+
resolved = bun_tools.resolve(name, bin_dir=bin_dir)
|
|
131
|
+
if resolved is None:
|
|
132
|
+
return CheckResult(name=check_name, status=Status.WARN, reason=f"{name!r} not found; {install_hint}")
|
|
133
|
+
if not resolved.on_path:
|
|
134
|
+
found_dir = Path(resolved.path).parent
|
|
135
|
+
return CheckResult(
|
|
136
|
+
name=check_name,
|
|
137
|
+
status=Status.WARN,
|
|
138
|
+
reason=(
|
|
139
|
+
f"installed at {resolved.path} but {found_dir} is not on PATH; add it to PATH "
|
|
140
|
+
f"(for non-interactive shells too: ~/.zshenv for zsh, ~/.profile for bash)"
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
try:
|
|
144
|
+
result = subprocess.run(
|
|
145
|
+
[resolved.path, "--version"], capture_output=True, text=True, timeout=timeout
|
|
146
|
+
)
|
|
147
|
+
except (OSError, subprocess.TimeoutExpired) as e:
|
|
148
|
+
return CheckResult(
|
|
149
|
+
name=check_name, status=Status.WARN, reason=f"could not run {name} --version: {e}"
|
|
150
|
+
)
|
|
151
|
+
if result.returncode != 0:
|
|
152
|
+
return CheckResult(
|
|
153
|
+
name=check_name, status=Status.WARN, reason=f"{name} --version exited {result.returncode}"
|
|
154
|
+
)
|
|
155
|
+
return CheckResult(
|
|
156
|
+
name=check_name,
|
|
157
|
+
status=Status.OK,
|
|
158
|
+
reason=(result.stdout + result.stderr).strip().split("\n")[0],
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def check_bun(bin_dir: Path | None = None, timeout: float = 5.0) -> CheckResult:
|
|
163
|
+
"""WARN (never FAIL) if bun, needed to install ccusage, is missing or not on PATH."""
|
|
164
|
+
return _check_bun_tool(
|
|
165
|
+
"bun",
|
|
166
|
+
install_hint=(
|
|
167
|
+
f"install with `{bun_tools.BUN_INSTALL_COMMAND}` (macOS and Linux/WSL; "
|
|
168
|
+
"Linux/WSL also needs `unzip`)"
|
|
169
|
+
),
|
|
170
|
+
bin_dir=bin_dir,
|
|
171
|
+
timeout=timeout,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def check_ccusage(bin_dir: Path | None = None, timeout: float = 5.0) -> CheckResult:
|
|
176
|
+
"""WARN (never FAIL) if ccusage, used by `claude-code-usage reconcile`, is missing.
|
|
177
|
+
|
|
178
|
+
ccusage is optional: claude-code-usage works without it apart from `reconcile`.
|
|
179
|
+
"""
|
|
180
|
+
return _check_bun_tool(
|
|
181
|
+
"ccusage",
|
|
182
|
+
install_hint=f"install bun first (see deps:bun), then `{bun_tools.CCUSAGE_INSTALL_COMMAND}`",
|
|
183
|
+
bin_dir=bin_dir,
|
|
184
|
+
timeout=timeout,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
124
188
|
def check_settings_json(settings_path: Path) -> CheckResult:
|
|
125
189
|
"""Verify settings.json exists and is valid JSON."""
|
|
126
190
|
name = "settings.json"
|
|
@@ -1111,6 +1175,10 @@ def run_all_checks(
|
|
|
1111
1175
|
results.extend(check_sessions_uuid_migration(sessions_db_path))
|
|
1112
1176
|
results.extend(check_sessions_project_dir_absolute(sessions_db_path))
|
|
1113
1177
|
|
|
1178
|
+
# Optional dependencies for claude-code-usage's reconcile / ccusage blocks
|
|
1179
|
+
results.append(check_bun())
|
|
1180
|
+
results.append(check_ccusage())
|
|
1181
|
+
|
|
1114
1182
|
# PyPI version check
|
|
1115
1183
|
if not skip_pypi:
|
|
1116
1184
|
results.append(check_pypi_version(installed_version))
|
|
@@ -206,8 +206,23 @@ claude-code-usage --version # should print the version
|
|
|
206
206
|
uv sync --extra dev
|
|
207
207
|
uv run pytest # all 350+ tests should pass
|
|
208
208
|
|
|
209
|
-
#
|
|
210
|
-
#
|
|
209
|
+
# 4a. Install bun (needed to install ccusage). Same command on macOS and Linux/WSL.
|
|
210
|
+
# Linux/WSL: install `unzip` first (e.g. `sudo apt install unzip`), and install bun
|
|
211
|
+
# inside the Linux environment - a Windows-side bun is not visible to WSL tools.
|
|
212
|
+
# macOS alternative: `brew install oven-sh/bun/bun`.
|
|
213
|
+
curl -fsSL https://bun.sh/install | bash
|
|
214
|
+
bun --version
|
|
215
|
+
|
|
216
|
+
# 4b. Make bun's global bin dir (default ~/.bun/bin) resolvable on PATH, including in
|
|
217
|
+
# non-interactive shells (hooks, scheduled jobs). The installer edits the rc file of the
|
|
218
|
+
# shell you ran it from, which non-interactive shells often skip; also add it to
|
|
219
|
+
# ~/.zshenv (zsh) or ~/.profile (bash):
|
|
220
|
+
# export PATH="$HOME/.bun/bin:$PATH"
|
|
221
|
+
# claude-code-usage also falls back to ~/.bun/bin ($BUN_INSTALL/bin) on its own, so
|
|
222
|
+
# reconcile works without this; the PATH edit is what makes the `ccusage` command itself
|
|
223
|
+
# (e.g. `ccusage blocks`) available.
|
|
224
|
+
|
|
225
|
+
# 4c. Install ccusage globally so reconciliation works
|
|
211
226
|
bun add -g ccusage
|
|
212
227
|
ccusage --version
|
|
213
228
|
|
|
@@ -223,6 +238,11 @@ claude-code-usage warm-cache
|
|
|
223
238
|
claude-code-usage reconcile
|
|
224
239
|
```
|
|
225
240
|
|
|
241
|
+
**Existing machine that already has CCST:** `ccst doctor` reports `deps:bun` and `deps:ccusage`
|
|
242
|
+
(WARN when missing or not on `PATH`, with the exact fix). `ccst install-everything` runs doctor at
|
|
243
|
+
its end, so upgrading surfaces a missing install; CCST never installs bun or ccusage itself. Mute
|
|
244
|
+
with `ccst doctor --mute deps:ccusage` if you deliberately skip it.
|
|
245
|
+
|
|
226
246
|
The repo can also be sourced from GitHub directly (via the upstream
|
|
227
247
|
remote) if you don't want to clone it - see the README's "Install"
|
|
228
248
|
section for that path.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cc-session-tools
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.0
|
|
4
4
|
Summary: Claude Code session management CLI tools (ccd, ccr, ccs, claude-code-usage, ccst) and hook library (hooks package)
|
|
5
5
|
Author: raffishquartan
|
|
6
6
|
License: MIT License
|
|
@@ -105,7 +105,7 @@ bundled scheduled jobs:
|
|
|
105
105
|
| **`session-tag`** (SessionStart) | Writes a `<uuid>.tag` file so `claude-code-usage` can map session UUIDs to human-readable names. |
|
|
106
106
|
| **`last-screenshot`** (UserPromptSubmit) | Resolves your newest screenshot for the `>lss` token and injects its path. Requires `CCST_SCREENSHOT_DIR`. |
|
|
107
107
|
| **`bash-hard-deny`** (PreToolUse) | Categorical hard-deny gate for Bash: destructive file ops (`rm`/`rmdir`/`unlink`/`shred`, incl. inline python/node and script-file/heredoc forms), delete-by-move to a tmp-like location, `git branch`/`git push` branch deletion, `gh api`/`gh release` delete, destructive curl/wget methods, `sudo`, and direct telemetry-log reads. Runs before every other Bash hook; a match here can never be overridden in-session (the message tells you to run the command yourself in a separate terminal). |
|
|
108
|
-
| **`bash-security-review`** (PreToolUse) | Tiered Bash command security review with
|
|
108
|
+
| **`bash-security-review`** (PreToolUse) | Tiered Bash command security review: trivial/read-only pre-filters, heuristic escalation, a reviewed-script allowlist (script path + content hash, managed with `ccst hooks allowlist list\|add\|remove\|verify`), a command cache, and a slimmed `claude -p` LLM fallback (~770 tokens per review instead of ~50k+). |
|
|
109
109
|
| **`marker-allow`** (PreToolUse) | Auto-approves a bare `touch` of a skill marker under `~/.cache/claude/markers/` (and nothing else), so marker-gated skills can refresh their TTL marker without a permission prompt. |
|
|
110
110
|
| **`confirm-8digit`** (PreToolUse) | Blocks a configurable set of high-stakes tool calls unless the user repeats back an 8-digit confirmation code. |
|
|
111
111
|
| **`after-response`** (Stop) | Touches a `.last-active` sentinel so `ccs --order-by active` can sort sessions by recency of Claude activity. |
|
|
@@ -151,7 +151,7 @@ If you've ever tried to remember which `1f4a8b3c-...` UUID is the session where
|
|
|
151
151
|
|
|
152
152
|
- **Python 3.11+** (3.12+ recommended)
|
|
153
153
|
- **The `claude` CLI on your `$PATH`.** Install it first via [the official Claude Code instructions](https://docs.anthropic.com/en/docs/claude-code/setup) and verify with `claude --version`.
|
|
154
|
-
- **`ccusage` (optional)** - if on `$PATH
|
|
154
|
+
- **`ccusage` (optional)** - if on `$PATH` (or in bun's global bin dir, `~/.bun/bin`), `claude-code-usage reconcile` cross-checks dollar totals against it. Skipped gracefully if missing. Needs `bun` to install; `ccst doctor` reports both under `deps:bun` / `deps:ccusage` and prints the install command. See the `analyse-cc-usage` skill's setup section.
|
|
155
155
|
- **`ripgrep` (optional)** - `ccs --contents` prefers `rg`; falls back to threaded Python `grep` if missing.
|
|
156
156
|
|
|
157
157
|
### Install and set up
|
|
@@ -23,6 +23,7 @@ src/cc_session_tools/cli/migrate_telemetry.py
|
|
|
23
23
|
src/cc_session_tools/config/hooks-bundle.json
|
|
24
24
|
src/cc_session_tools/config/session-tag-hook.json
|
|
25
25
|
src/cc_session_tools/lib/__init__.py
|
|
26
|
+
src/cc_session_tools/lib/bun_tools.py
|
|
26
27
|
src/cc_session_tools/lib/claude_flags.py
|
|
27
28
|
src/cc_session_tools/lib/claude_md_install.py
|
|
28
29
|
src/cc_session_tools/lib/context_overrides.py
|
|
@@ -176,6 +177,7 @@ src/hooks/normalise.py
|
|
|
176
177
|
src/hooks/pdata_sync.py
|
|
177
178
|
src/hooks/pending_migration.py
|
|
178
179
|
src/hooks/pending_rename.py
|
|
180
|
+
src/hooks/script_allowlist.py
|
|
179
181
|
src/hooks/session_tag.py
|
|
180
182
|
src/hooks/stats.py
|
|
181
183
|
src/hooks/telemetry.py
|
|
@@ -187,6 +189,8 @@ tests/test_after_response.py
|
|
|
187
189
|
tests/test_attribution.py
|
|
188
190
|
tests/test_bash_hard_deny.py
|
|
189
191
|
tests/test_bash_security_review.py
|
|
192
|
+
tests/test_bash_security_review_allowlist.py
|
|
193
|
+
tests/test_bun_tools.py
|
|
190
194
|
tests/test_cache.py
|
|
191
195
|
tests/test_cache_sqlite.py
|
|
192
196
|
tests/test_ccr_orphans.py
|
|
@@ -204,6 +208,7 @@ tests/test_ccst_doctor.py
|
|
|
204
208
|
tests/test_ccst_gc_prune.py
|
|
205
209
|
tests/test_ccst_gc_report.py
|
|
206
210
|
tests/test_ccst_hook_dispatcher.py
|
|
211
|
+
tests/test_ccst_hooks_allowlist.py
|
|
207
212
|
tests/test_ccst_hooks_install_prune.py
|
|
208
213
|
tests/test_ccst_hooks_install_zero_arg.py
|
|
209
214
|
tests/test_ccst_hooks_uninstall.py
|
|
@@ -268,6 +273,7 @@ tests/test_roots.py
|
|
|
268
273
|
tests/test_rules.py
|
|
269
274
|
tests/test_scheduler_bundled_jobs.py
|
|
270
275
|
tests/test_schema.py
|
|
276
|
+
tests/test_script_allowlist.py
|
|
271
277
|
tests/test_session_names.py
|
|
272
278
|
tests/test_session_tag.py
|
|
273
279
|
tests/test_sessions.py
|
|
@@ -10,11 +10,12 @@ that's our domain.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
|
-
import shutil
|
|
14
13
|
import subprocess
|
|
15
14
|
from dataclasses import dataclass, field
|
|
16
15
|
from typing import Any
|
|
17
16
|
|
|
17
|
+
from cc_session_tools.lib import bun_tools
|
|
18
|
+
|
|
18
19
|
|
|
19
20
|
@dataclass
|
|
20
21
|
class DailyEntry:
|
|
@@ -52,11 +53,11 @@ class ReconcileDiff:
|
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
class CcusageNotInstalled(RuntimeError):
|
|
55
|
-
"""Raised when ccusage is
|
|
56
|
+
"""Raised when ccusage is neither on PATH nor in bun's global bin directory."""
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
def is_available() -> bool:
|
|
59
|
-
return
|
|
60
|
+
return bun_tools.resolve("ccusage") is not None
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
def run_daily(since: str | None = None, until: str | None = None,
|
|
@@ -65,9 +66,14 @@ def run_daily(since: str | None = None, until: str | None = None,
|
|
|
65
66
|
|
|
66
67
|
Dates are formatted as YYYYMMDD per ccusage's CLI.
|
|
67
68
|
"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
resolved = bun_tools.resolve("ccusage")
|
|
70
|
+
if resolved is None:
|
|
71
|
+
raise CcusageNotInstalled(
|
|
72
|
+
"ccusage not found on PATH or in bun's global bin directory. Follow the "
|
|
73
|
+
"analyse-cc-usage skill's setup step 4 (install bun, then "
|
|
74
|
+
f"`{bun_tools.CCUSAGE_INSTALL_COMMAND}`); `ccst doctor` shows the current state."
|
|
75
|
+
)
|
|
76
|
+
cmd = [resolved.path, "daily", "--json"]
|
|
71
77
|
if offline:
|
|
72
78
|
cmd.append("--offline")
|
|
73
79
|
if since:
|