cc-session-tools 3.9.0__tar.gz → 3.9.2__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.9.0/src/cc_session_tools.egg-info → cc_session_tools-3.9.2}/PKG-INFO +3 -3
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/README.md +2 -2
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/pyproject.toml +1 -1
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/ccst.py +40 -3
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/migrate_sessions_db.py +81 -8
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/doctor.py +56 -7
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/init_paths.py +32 -4
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/init_service.py +5 -1
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/sessions_db.py +47 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2/src/cc_session_tools.egg-info}/PKG-INFO +3 -3
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools.egg-info/SOURCES.txt +1 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_init_cli.py +55 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_migrate_sessions_db.py +3 -3
- cc_session_tools-3.9.2/tests/test_sessions_db_migration_chain.py +464 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/LICENSE +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/setup.cfg +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/ccd.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/ccmsg.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/ccr.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/ccs.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/ccsched.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/config/hooks-bundle.json +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/config/session-tag-hook.json +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/hooks_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/bun_tools.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/claude_flags.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/claude_md_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/context_overrides.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/db.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/debug.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/hook_registry.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/install_sync.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/levenshtein.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/machine_identity.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/lock.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/message.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/repository.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/retention.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/service.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/messaging/store.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/occupancy.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/paths.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/backup.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/classify.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/cutover.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/dump.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/importers.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/naming.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/readiness.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/repository.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/service.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/store.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/verify.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/picker.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/proc_lock.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/prompts.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/roots.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/rules.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/due.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/state.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/store.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/session_gc.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/sessions.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/sessions_repair.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/shell_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/tasklist.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/telemetry_store.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/text_wrap.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/prompts/pdata-migration-skills-update.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/extract-pptx/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/extract-pptx/scripts/extract_pptx.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/extract-to-text/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/extract-to-text/scripts/extract_to_text.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-pdata-design-schema/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-pdata-do-init/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-pdata-do-manual-migration/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-pdata-resolve-conflicts/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools.egg-info/requires.txt +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools.egg-info/top_level.txt +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/attribution.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/cache.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/ccusage_wrapper.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/data/pricing.json +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/parent_inference.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/parser.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/pricing.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/query.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/report.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/schema.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/claude_code_usage/session_names.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/__init__.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/after_response.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/bash_hard_deny.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/bash_security_review.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/cache.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/catchup.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/confirm_8digit.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/context_window_warning.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/last_screenshot.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/marker_allow.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/markers.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/messaging_deliver.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/model_info.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/normalise.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/pdata_sync.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/pending_migration.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/pending_rename.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/script_allowlist.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/session_tag.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/stats.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/telemetry.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/telemetry_query.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/telemetry_trim.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/transcript.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/hooks/worklog_guard.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_after_response.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_attribution.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_bash_hard_deny.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_bash_security_review.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_bash_security_review_allowlist.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_bun_tools.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cache.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cache_sqlite.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccr_orphans.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccs_emptiness.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccs_eta.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccs_list_mode.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccs_scope_flags.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccs_sentinel_sort.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccs_session_counts.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_bundle_discovery.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_context_override_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_doctor.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_gc_prune.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_gc_report.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_hook_dispatcher.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_hooks_allowlist.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_hooks_install_prune.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_hooks_uninstall.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_install_everything.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_machine_identity_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_main_gate.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_readiness_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_sync_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_pdata_verify_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_repair_sessions_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_sessions_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_shell_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_skills_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_skills_uninstall.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_telemetry_query.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccst_telemetry_trim.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_ccusage_wrapper.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_claude_flags.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_claude_md_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cli.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cli_ccd.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cli_ccd_dry_run.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cli_ccr.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cli_ccs.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_cli_version.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_confirm_8digit.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_context_overrides.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_context_window_warning.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_db.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_debug.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_doctor_mutes.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_empty_session.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_hook_cache.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_hooks_install.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_hooks_install_records.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_install_sync.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_last_screenshot.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_levenshtein.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_machine_identity.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_marker_allow.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_migrate_ccmsg.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_migrate_ccsched.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_migrate_telemetry.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_model_info.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_normalise.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_occupancy.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_parent_inference.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_parser.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_paths.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_pdata_audit_skill.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_pending_migration_hook.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_picker.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_pricing.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_proc_lock.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_prompts.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_query.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_report.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_roots.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_rules.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_scheduler_bundled_jobs.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_schema.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_script_allowlist.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_session_names.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_session_tag.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_sessions.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_sessions_db.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_sessions_repair.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_tasklist.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_telemetry.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_telemetry_store.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_text_wrap.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/tests/test_transcript.py +0 -0
- {cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/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.9.
|
|
3
|
+
Version: 3.9.2
|
|
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
|
|
@@ -192,8 +192,8 @@ Messages can also be addressed to a specific session (`--to-session`) or to a fr
|
|
|
192
192
|
description for whichever session picks it up first (`--to-description`) - project-
|
|
193
193
|
addressed is by far the most common pattern in practice.
|
|
194
194
|
|
|
195
|
-
The easiest way to use
|
|
196
|
-
another session (e.g. by name tag and/or location).
|
|
195
|
+
The easiest way to use messages when you need them is just to tell your session to send
|
|
196
|
+
a message to another session (e.g. by name tag and/or location).
|
|
197
197
|
|
|
198
198
|
## Scheduled housekeeping: `ccsched`
|
|
199
199
|
|
|
@@ -135,8 +135,8 @@ Messages can also be addressed to a specific session (`--to-session`) or to a fr
|
|
|
135
135
|
description for whichever session picks it up first (`--to-description`) - project-
|
|
136
136
|
addressed is by far the most common pattern in practice.
|
|
137
137
|
|
|
138
|
-
The easiest way to use
|
|
139
|
-
another session (e.g. by name tag and/or location).
|
|
138
|
+
The easiest way to use messages when you need them is just to tell your session to send
|
|
139
|
+
a message to another session (e.g. by name tag and/or location).
|
|
140
140
|
|
|
141
141
|
## Scheduled housekeeping: `ccsched`
|
|
142
142
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cc-session-tools"
|
|
7
|
-
version = "3.9.
|
|
7
|
+
version = "3.9.2"
|
|
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" }
|
|
@@ -2340,12 +2340,44 @@ _MIGRATE_ALL_BANNER = (
|
|
|
2340
2340
|
)
|
|
2341
2341
|
|
|
2342
2342
|
|
|
2343
|
+
def _migrate_all_sessions_step(args: argparse.Namespace) -> int:
|
|
2344
|
+
"""The sessions step of `migrate all`: the legacy import writes uuid-keyed rows, so a
|
|
2345
|
+
pre-uuid `sessions.db` is rebuilt first (`sessions migrate-uuid`, with its own backup and
|
|
2346
|
+
concurrent-writer guard). A failed or refused rebuild skips the import."""
|
|
2347
|
+
import sqlite3
|
|
2348
|
+
|
|
2349
|
+
from cc_session_tools.cli.migrate_sessions_db import migrate_uuid
|
|
2350
|
+
from cc_session_tools.lib import sessions_db
|
|
2351
|
+
|
|
2352
|
+
db_path = sessions_db.default_db_path()
|
|
2353
|
+
try:
|
|
2354
|
+
pre_uuid = sessions_db.pre_uuid_sessions_table(db_path)
|
|
2355
|
+
except sqlite3.DatabaseError as exc:
|
|
2356
|
+
print(f"cannot read {db_path}: {exc} - not a valid sessions.db; nothing was changed",
|
|
2357
|
+
file=sys.stderr)
|
|
2358
|
+
return 1
|
|
2359
|
+
if pre_uuid:
|
|
2360
|
+
print(f"{db_path} is on the pre-3.0.0 schema - rebuilding it first "
|
|
2361
|
+
"(`ccst sessions migrate-uuid`) so the legacy import can write to it.")
|
|
2362
|
+
try:
|
|
2363
|
+
rc = migrate_uuid(db_path=db_path, dry_run=args.dry_run)
|
|
2364
|
+
except ValueError as exc:
|
|
2365
|
+
print(str(exc), file=sys.stderr)
|
|
2366
|
+
return 1
|
|
2367
|
+
if rc != 0:
|
|
2368
|
+
return rc
|
|
2369
|
+
if args.dry_run:
|
|
2370
|
+
print("The legacy import's own dry-run is skipped until the rebuild has run.")
|
|
2371
|
+
return 0
|
|
2372
|
+
return _cmd_sessions_migrate(argparse.Namespace(
|
|
2373
|
+
dry_run=args.dry_run, sessions_db=None, tags_dir=None, mutes_file=None))
|
|
2374
|
+
|
|
2375
|
+
|
|
2343
2376
|
def _cmd_migrate_all(args: argparse.Namespace) -> int:
|
|
2344
2377
|
print(_MIGRATE_ALL_BANNER)
|
|
2345
2378
|
|
|
2346
2379
|
steps: list[tuple[str, object]] = [
|
|
2347
|
-
("sessions", lambda:
|
|
2348
|
-
dry_run=args.dry_run, sessions_db=None, tags_dir=None, mutes_file=None))),
|
|
2380
|
+
("sessions", lambda: _migrate_all_sessions_step(args)),
|
|
2349
2381
|
("ccmsg", lambda: _cmd_migrate_ccmsg(argparse.Namespace(
|
|
2350
2382
|
old_root=None, backup_dir=None, dry_run=args.dry_run))),
|
|
2351
2383
|
("ccsched", lambda: _cmd_migrate_ccsched(argparse.Namespace(
|
|
@@ -2355,14 +2387,19 @@ def _cmd_migrate_all(args: argparse.Namespace) -> int:
|
|
|
2355
2387
|
]
|
|
2356
2388
|
|
|
2357
2389
|
overall_rc = 0
|
|
2390
|
+
failed: list[str] = []
|
|
2358
2391
|
for name, step in steps:
|
|
2359
2392
|
print(f"\n=== {name} ===")
|
|
2360
2393
|
rc = step() # type: ignore[operator]
|
|
2361
2394
|
if rc != 0:
|
|
2362
2395
|
overall_rc = rc
|
|
2396
|
+
failed.append(name)
|
|
2363
2397
|
|
|
2364
2398
|
print()
|
|
2365
|
-
if
|
|
2399
|
+
if failed:
|
|
2400
|
+
print(f"Steps that failed: {', '.join(failed)}. Review the ERROR/ABORT/refusal lines "
|
|
2401
|
+
"above; a failed step leaves its own old files untouched and is safe to re-run.")
|
|
2402
|
+
elif args.dry_run:
|
|
2366
2403
|
print("Dry run complete — re-run without --dry-run once you're satisfied.")
|
|
2367
2404
|
else:
|
|
2368
2405
|
print("All migrations attempted. Review any ERROR/ABORT lines above; a "
|
{cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/cli/migrate_sessions_db.py
RENAMED
|
@@ -114,10 +114,15 @@ def _read_legacy_sessions_rows(conn: sqlite3.Connection) -> list[sqlite3.Row]:
|
|
|
114
114
|
"""Every row from the pre-3.0.0 (project_dir, basename) `sessions` table, via raw SQL -
|
|
115
115
|
not sessions_db.list_sessions()/_row_to_session, which require the `uuid` column this
|
|
116
116
|
migration is the one adding. Safe to call against either schema shape: only the columns
|
|
117
|
-
that exist pre-3.0.0 are selected.
|
|
117
|
+
that exist pre-3.0.0 are selected; `updated_at` (absent from v1.0.0 through v2.12.x) is read
|
|
118
|
+
as NULL when the table has no such column."""
|
|
119
|
+
updated_at = (
|
|
120
|
+
"updated_at" if "updated_at" in sessions_db.sessions_column_names(conn)
|
|
121
|
+
else "NULL AS updated_at"
|
|
122
|
+
)
|
|
118
123
|
return conn.execute(
|
|
119
124
|
"SELECT project_dir, basename, start_date, last_opened, last_active, "
|
|
120
|
-
"discovered_at, updated_at FROM sessions"
|
|
125
|
+
f"discovered_at, {updated_at} FROM sessions"
|
|
121
126
|
).fetchall()
|
|
122
127
|
|
|
123
128
|
|
|
@@ -162,6 +167,26 @@ def print_uuid_migration_dry_run(plan: UuidMigrationPlan, *, db_path: Path) -> N
|
|
|
162
167
|
print("backing up: this rebuilds the sessions table and is a one-way schema change.")
|
|
163
168
|
|
|
164
169
|
|
|
170
|
+
def _record_uuid_marker_only(db_path: Path, *, dry_run: bool) -> int:
|
|
171
|
+
"""`sessions` is already uuid-keyed, or there is no `sessions` table to rebuild: there is
|
|
172
|
+
nothing to rebuild, only the completion marker to record (creating the current schema first
|
|
173
|
+
if the table is missing)."""
|
|
174
|
+
if dry_run:
|
|
175
|
+
print(f"{db_path} needs no rebuild - only its 3.0.0 completion marker would be "
|
|
176
|
+
"recorded. Re-run with --write to record it.")
|
|
177
|
+
return 0
|
|
178
|
+
conn = sessions_db.connect(path=db_path)
|
|
179
|
+
try:
|
|
180
|
+
db.record_migration(
|
|
181
|
+
conn, sessions_db.SESSIONS_UUID_MIGRATION, applied_at=sessions_db._now_iso()
|
|
182
|
+
)
|
|
183
|
+
conn.commit()
|
|
184
|
+
finally:
|
|
185
|
+
conn.close()
|
|
186
|
+
print(f"{db_path} needed no rebuild - recorded its 3.0.0 completion marker.")
|
|
187
|
+
return 0
|
|
188
|
+
|
|
189
|
+
|
|
165
190
|
def migrate_uuid(*, db_path: Path, dry_run: bool, backup_dir: Path | None = None) -> int:
|
|
166
191
|
"""The 3.0.0 schema migration: rebuild `sessions` from the pre-3.0.0
|
|
167
192
|
(project_dir, basename) primary key to (project_dir, basename, uuid), backfilling each
|
|
@@ -185,15 +210,35 @@ def migrate_uuid(*, db_path: Path, dry_run: bool, backup_dir: Path | None = None
|
|
|
185
210
|
# be held by a live hook write, defeating the point of checking before touching anything.
|
|
186
211
|
conn = db.connect(db_path)
|
|
187
212
|
try:
|
|
188
|
-
if sessions_db.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
213
|
+
if not sessions_db.sessions_table_exists(conn):
|
|
214
|
+
marker_only = True
|
|
215
|
+
legacy_rows: list[sqlite3.Row] = []
|
|
216
|
+
else:
|
|
217
|
+
keyed = sessions_db.sessions_schema_is_uuid_keyed(conn)
|
|
218
|
+
recorded = sessions_db.migration_marker_recorded(
|
|
219
|
+
conn, sessions_db.SESSIONS_UUID_MIGRATION
|
|
220
|
+
)
|
|
221
|
+
if keyed and recorded:
|
|
222
|
+
print(f"{db_path} is already migrated to the uuid-aware schema - nothing to do.")
|
|
223
|
+
return 0
|
|
224
|
+
marker_only = keyed
|
|
225
|
+
if not keyed:
|
|
226
|
+
missing = sessions_db.missing_rebuild_columns(conn)
|
|
227
|
+
if missing:
|
|
228
|
+
print(
|
|
229
|
+
f"cannot migrate {db_path}: its `sessions` table is missing "
|
|
230
|
+
f"column(s) the rebuild copies: {', '.join(missing)}. Nothing was "
|
|
231
|
+
"changed; restore the file from a backup or repair it by hand.",
|
|
232
|
+
file=sys.stderr,
|
|
233
|
+
)
|
|
234
|
+
return 1
|
|
235
|
+
legacy_rows = [] if keyed else _read_legacy_sessions_rows(conn)
|
|
194
236
|
finally:
|
|
195
237
|
conn.close()
|
|
196
238
|
|
|
239
|
+
if marker_only:
|
|
240
|
+
return _record_uuid_marker_only(db_path, dry_run=dry_run)
|
|
241
|
+
|
|
197
242
|
plan = plan_uuid_migration(legacy_rows)
|
|
198
243
|
|
|
199
244
|
if dry_run:
|
|
@@ -418,6 +463,30 @@ def _tar_backup(sources: list[Path], *, backup_dir: Path) -> Path | None:
|
|
|
418
463
|
return dest
|
|
419
464
|
|
|
420
465
|
|
|
466
|
+
def refuse_if_sessions_db_pre_uuid(db_path: Path) -> int | None:
|
|
467
|
+
"""Pre-flight for the legacy import: it writes uuid-keyed rows, which a `sessions.db` still
|
|
468
|
+
on the pre-3.0.0 schema cannot accept. Returns a non-zero exit code (after printing the
|
|
469
|
+
fix) when the import must not run, else None. Read-only; must run before anything that could
|
|
470
|
+
connect-with-DDL, write, or back up."""
|
|
471
|
+
try:
|
|
472
|
+
pre_uuid = sessions_db.pre_uuid_sessions_table(db_path)
|
|
473
|
+
except sqlite3.DatabaseError as exc:
|
|
474
|
+
print(f"cannot read {db_path}: {exc} - not a valid sessions.db; nothing was changed",
|
|
475
|
+
file=sys.stderr)
|
|
476
|
+
return 1
|
|
477
|
+
if not pre_uuid:
|
|
478
|
+
return None
|
|
479
|
+
print(
|
|
480
|
+
f"cannot run the legacy flat-file import: {db_path} is still on the pre-3.0.0 schema "
|
|
481
|
+
"(no uuid column), which the import cannot write to. Nothing was changed. First run "
|
|
482
|
+
"`ccst sessions migrate-uuid --write` from a plain terminal with no other `claude` "
|
|
483
|
+
"session running (it takes a backup first), then re-run this - or run `ccst migrate "
|
|
484
|
+
"all`, which does both in order.",
|
|
485
|
+
file=sys.stderr,
|
|
486
|
+
)
|
|
487
|
+
return 1
|
|
488
|
+
|
|
489
|
+
|
|
421
490
|
def run_migration(
|
|
422
491
|
*,
|
|
423
492
|
dry_run: bool,
|
|
@@ -427,6 +496,10 @@ def run_migration(
|
|
|
427
496
|
roots: list[Path],
|
|
428
497
|
backup_dir: Path,
|
|
429
498
|
) -> int:
|
|
499
|
+
refusal = refuse_if_sessions_db_pre_uuid(db_path)
|
|
500
|
+
if refusal is not None:
|
|
501
|
+
return refusal
|
|
502
|
+
|
|
430
503
|
print(f"Sessions DB : {db_path}")
|
|
431
504
|
print(f"Tags source : {tags_dir}")
|
|
432
505
|
print(f"Mutes source : {mutes_file}")
|
|
@@ -675,7 +675,26 @@ def _migration_recorded(db_path: Path, marker_name: str) -> bool:
|
|
|
675
675
|
conn.close()
|
|
676
676
|
|
|
677
677
|
|
|
678
|
-
def
|
|
678
|
+
def _sessions_marker() -> str:
|
|
679
|
+
from cc_session_tools.lib.sessions_db import LEGACY_FLAT_FILE_MIGRATION
|
|
680
|
+
|
|
681
|
+
return LEGACY_FLAT_FILE_MIGRATION
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _sessions_db_is_pre_uuid(db_path: Path) -> bool:
|
|
685
|
+
"""`sessions_db.pre_uuid_sessions_table`, with an unreadable file counting as not pre-uuid
|
|
686
|
+
(other checks already report a database that fails to open)."""
|
|
687
|
+
from cc_session_tools.lib import sessions_db
|
|
688
|
+
|
|
689
|
+
try:
|
|
690
|
+
return sessions_db.pre_uuid_sessions_table(db_path)
|
|
691
|
+
except sqlite3.DatabaseError:
|
|
692
|
+
return False
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def check_pending_data_store_migration(
|
|
696
|
+
paths: LegacyMigrationPaths, *, sessions_db_path: Path | None = None,
|
|
697
|
+
) -> list[CheckResult]:
|
|
679
698
|
"""Detect legacy flat-file data left over from a pre-1.0.0 install.
|
|
680
699
|
|
|
681
700
|
:func:`check_data_stores` can't distinguish a fresh install (nothing to
|
|
@@ -696,6 +715,10 @@ def check_pending_data_store_migration(paths: LegacyMigrationPaths) -> list[Chec
|
|
|
696
715
|
fills from the first hook fire after install, so counting rows reported "already
|
|
697
716
|
migrated" for anyone who opened a session before migrating, and the FAIL that should
|
|
698
717
|
have prompted them never fired.
|
|
718
|
+
|
|
719
|
+
`sessions_db_path` is the sessions.db the `sessions` finding reads; pass the same path the
|
|
720
|
+
3.0.0 uuid check uses so the two findings can never describe different files (defaults to
|
|
721
|
+
`paths.data_home / "sessions.db"`).
|
|
699
722
|
"""
|
|
700
723
|
from cc_session_tools.lib.messaging.repository import (
|
|
701
724
|
LEGACY_FLAT_FILE_MIGRATION as _CCMSG_MIGRATION,
|
|
@@ -724,7 +747,7 @@ def check_pending_data_store_migration(paths: LegacyMigrationPaths) -> list[Chec
|
|
|
724
747
|
"sessions": (
|
|
725
748
|
(paths.tags_dir, paths.mutes_file),
|
|
726
749
|
_count_legacy_sessions(paths.tags_dir, paths.mutes_file),
|
|
727
|
-
paths.data_home / "sessions.db",
|
|
750
|
+
sessions_db_path if sessions_db_path is not None else paths.data_home / "sessions.db",
|
|
728
751
|
_SESSIONS_MIGRATION,
|
|
729
752
|
),
|
|
730
753
|
"telemetry": (
|
|
@@ -748,6 +771,13 @@ def check_pending_data_store_migration(paths: LegacyMigrationPaths) -> list[Chec
|
|
|
748
771
|
already_migrated = _migration_recorded(new_db_path, marker_name)
|
|
749
772
|
evidence = f"the import is recorded in {new_db_path.name}"
|
|
750
773
|
if not already_migrated:
|
|
774
|
+
rebuild_note = ""
|
|
775
|
+
if store_name == "sessions" and _sessions_db_is_pre_uuid(new_db_path):
|
|
776
|
+
rebuild_note = (
|
|
777
|
+
f" It will first rebuild {new_db_path.name} to the 3.0.0 uuid schema "
|
|
778
|
+
"(taking a backup; close any running `claude` sessions first) - the "
|
|
779
|
+
"import cannot write to the old schema."
|
|
780
|
+
)
|
|
751
781
|
results.append(
|
|
752
782
|
CheckResult(
|
|
753
783
|
name=name,
|
|
@@ -756,7 +786,7 @@ def check_pending_data_store_migration(paths: LegacyMigrationPaths) -> list[Chec
|
|
|
756
786
|
f"{legacy_count} unmigrated item(s) at {existing}; run "
|
|
757
787
|
"`ccst migrate all` from a plain terminal (NOT inside a Claude Code "
|
|
758
788
|
"session — the delete step is blocked by bash-hard-deny) to migrate "
|
|
759
|
-
f"into {new_db_path}"
|
|
789
|
+
f"into {new_db_path}.{rebuild_note}"
|
|
760
790
|
),
|
|
761
791
|
)
|
|
762
792
|
)
|
|
@@ -942,7 +972,9 @@ def check_sessions_project_dir_absolute(sessions_db_path: Path) -> list[CheckRes
|
|
|
942
972
|
)]
|
|
943
973
|
|
|
944
974
|
|
|
945
|
-
def check_sessions_uuid_migration(
|
|
975
|
+
def check_sessions_uuid_migration(
|
|
976
|
+
sessions_db_path: Path, *, legacy_sessions_pending: bool = False,
|
|
977
|
+
) -> list[CheckResult]:
|
|
946
978
|
"""FAIL if sessions.db still has the pre-3.0.0 (project_dir, basename) primary key -
|
|
947
979
|
forked sessions (Ctrl-L) silently clobber each other's activity rows until
|
|
948
980
|
`ccst sessions migrate-uuid --write` runs. Distinguishes "never migrated" from
|
|
@@ -956,7 +988,10 @@ def check_sessions_uuid_migration(sessions_db_path: Path) -> list[CheckResult]:
|
|
|
956
988
|
A sessions.db that doesn't exist yet reads OK: connect() records the migration
|
|
957
989
|
marker at creation time for any brand-new file (sessions_db.py), so a genuinely
|
|
958
990
|
fresh install is never in a pending state - there is simply nothing to check here
|
|
959
|
-
yet, and this check must not create the file itself just to inspect it.
|
|
991
|
+
yet, and this check must not create the file itself just to inspect it.
|
|
992
|
+
|
|
993
|
+
`legacy_sessions_pending` says the 1.0.0 flat-file import is also still pending; the two FAIL
|
|
994
|
+
branches then note that `ccst migrate all` performs both, in the order that works."""
|
|
960
995
|
import sqlite3
|
|
961
996
|
|
|
962
997
|
from cc_session_tools.lib import db as db_lib
|
|
@@ -1000,6 +1035,11 @@ def check_sessions_uuid_migration(sessions_db_path: Path) -> list[CheckResult]:
|
|
|
1000
1035
|
"run `ccst sessions migrate-uuid --write` from a plain terminal with no other "
|
|
1001
1036
|
"`claude` session running (it takes a backup first; see --help)"
|
|
1002
1037
|
)
|
|
1038
|
+
if legacy_sessions_pending:
|
|
1039
|
+
remediation += (
|
|
1040
|
+
"; the legacy import is also pending, and `ccst migrate all` performs both in "
|
|
1041
|
+
"the order that works"
|
|
1042
|
+
)
|
|
1003
1043
|
if not pk_is_uuid_keyed and not marker_present:
|
|
1004
1044
|
return [CheckResult(
|
|
1005
1045
|
name=name, status=Status.FAIL,
|
|
@@ -1158,7 +1198,8 @@ def run_all_checks(
|
|
|
1158
1198
|
|
|
1159
1199
|
# Pending legacy-data migration
|
|
1160
1200
|
if legacy_migration_paths is not None:
|
|
1161
|
-
results.extend(check_pending_data_store_migration(
|
|
1201
|
+
results.extend(check_pending_data_store_migration(
|
|
1202
|
+
legacy_migration_paths, sessions_db_path=sessions_db_path))
|
|
1162
1203
|
|
|
1163
1204
|
# Pending ccst pdata init cutover (spec §7.1 step 7)
|
|
1164
1205
|
if projects_root is not None:
|
|
@@ -1172,7 +1213,15 @@ def run_all_checks(
|
|
|
1172
1213
|
# since project-dir-absolute's own check degrades to a WARN pointing back at it when
|
|
1173
1214
|
# the uuid migration hasn't run yet (a pre-migration schema can't be queried the same way).
|
|
1174
1215
|
if sessions_db_path is not None:
|
|
1175
|
-
|
|
1216
|
+
legacy_sessions_pending = (
|
|
1217
|
+
legacy_migration_paths is not None
|
|
1218
|
+
and _count_legacy_sessions(
|
|
1219
|
+
legacy_migration_paths.tags_dir, legacy_migration_paths.mutes_file
|
|
1220
|
+
) > 0
|
|
1221
|
+
and not _migration_recorded(sessions_db_path, _sessions_marker())
|
|
1222
|
+
)
|
|
1223
|
+
results.extend(check_sessions_uuid_migration(
|
|
1224
|
+
sessions_db_path, legacy_sessions_pending=legacy_sessions_pending))
|
|
1176
1225
|
results.extend(check_sessions_project_dir_absolute(sessions_db_path))
|
|
1177
1226
|
|
|
1178
1227
|
# Optional dependencies for claude-code-usage's reconcile / ccusage blocks
|
{cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/init_paths.py
RENAMED
|
@@ -112,13 +112,41 @@ def project_db_dir_override(rehearse: Path | None) -> Iterator[None]:
|
|
|
112
112
|
override_dir = rehearse / REHEARSAL_DB_DIRNAME
|
|
113
113
|
previous = os.environ.get(store.PROJECT_DB_DIR_ENV)
|
|
114
114
|
os.environ[store.PROJECT_DB_DIR_ENV] = str(override_dir)
|
|
115
|
+
_live_db_dir_env_stack.append(previous)
|
|
115
116
|
try:
|
|
116
117
|
yield
|
|
117
118
|
finally:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
_live_db_dir_env_stack.pop()
|
|
120
|
+
_restore_db_dir_env(previous)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# The CCST_PROJECT_DB_DIR value in effect before each active project_db_dir_override (None =
|
|
124
|
+
# unset), outermost first - what live_project_db_dir() restores.
|
|
125
|
+
_live_db_dir_env_stack: list[str | None] = []
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _restore_db_dir_env(value: str | None) -> None:
|
|
129
|
+
if value is None:
|
|
130
|
+
os.environ.pop(store.PROJECT_DB_DIR_ENV, None)
|
|
131
|
+
else:
|
|
132
|
+
os.environ[store.PROJECT_DB_DIR_ENV] = value
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@contextmanager
|
|
136
|
+
def live_project_db_dir() -> Iterator[None]:
|
|
137
|
+
"""Inside a project_db_dir_override block, temporarily point store.db_path() back at the
|
|
138
|
+
real per-project .db dir. For questions about the machine rather than the rehearsal copy
|
|
139
|
+
(e.g. "is this project already migrated here?"): a `cp -r` rehearsal copy never contains the
|
|
140
|
+
real .db, so the sandbox always answers "no". No-op outside an override."""
|
|
141
|
+
if not _live_db_dir_env_stack:
|
|
142
|
+
yield
|
|
143
|
+
return
|
|
144
|
+
inside = os.environ.get(store.PROJECT_DB_DIR_ENV)
|
|
145
|
+
_restore_db_dir_env(_live_db_dir_env_stack[0])
|
|
146
|
+
try:
|
|
147
|
+
yield
|
|
148
|
+
finally:
|
|
149
|
+
_restore_db_dir_env(inside)
|
|
122
150
|
|
|
123
151
|
|
|
124
152
|
@contextmanager
|
{cc_session_tools-3.9.0 → cc_session_tools-3.9.2}/src/cc_session_tools/lib/pdata/init_service.py
RENAMED
|
@@ -324,7 +324,11 @@ def _pending_adoption(*, project: str, project_root: Path) -> dump.DumpInfo | No
|
|
|
324
324
|
"project. Publish a fresh dump from a known-good machine (ccst pdata dump --force) "
|
|
325
325
|
"or reconcile manually (ccst pdata resolve) once those commands exist."
|
|
326
326
|
)
|
|
327
|
-
|
|
327
|
+
# The live .db, not a --rehearse sandbox's: a rehearsal copy never contains the real .db, so
|
|
328
|
+
# the sandbox would always look unmigrated and rehearsal would never reach classification.
|
|
329
|
+
with init_paths.live_project_db_dir():
|
|
330
|
+
already_migrated = _already_locally_migrated(project)
|
|
331
|
+
if already_migrated:
|
|
328
332
|
return None
|
|
329
333
|
return info
|
|
330
334
|
|
|
@@ -153,6 +153,53 @@ def sessions_schema_is_uuid_keyed(conn: sqlite3.Connection) -> bool:
|
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
# Columns of the pre-3.0.0 `sessions` table that the uuid rebuild copies into the new table.
|
|
157
|
+
# `updated_at` is deliberately absent: databases from v1.0.0 through v2.12.x never had it.
|
|
158
|
+
REBUILD_REQUIRED_COLUMNS = (
|
|
159
|
+
"project_dir", "basename", "start_date", "last_opened", "last_active", "discovered_at",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def sessions_table_exists(conn: sqlite3.Connection) -> bool:
|
|
164
|
+
return conn.execute(
|
|
165
|
+
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='sessions'"
|
|
166
|
+
).fetchone() is not None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def sessions_column_names(conn: sqlite3.Connection) -> set[str]:
|
|
170
|
+
return {row["name"] for row in conn.execute("PRAGMA table_info(sessions)")}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def missing_rebuild_columns(conn: sqlite3.Connection) -> list[str]:
|
|
174
|
+
"""Columns the 3.0.0 rebuild must copy that the existing `sessions` table lacks."""
|
|
175
|
+
present = sessions_column_names(conn)
|
|
176
|
+
return [c for c in REBUILD_REQUIRED_COLUMNS if c not in present]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def migration_marker_recorded(conn: sqlite3.Connection, name: str) -> bool:
|
|
180
|
+
"""`db.migration_applied`, but False (not an error) on a database that predates the
|
|
181
|
+
`migrations` table (v1.0.0 through roughly v2.8)."""
|
|
182
|
+
try:
|
|
183
|
+
return db.migration_applied(conn, name)
|
|
184
|
+
except sqlite3.OperationalError:
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def pre_uuid_sessions_table(path: Path) -> bool:
|
|
189
|
+
"""True iff `path` exists, has a `sessions` table, and that table is not uuid-keyed - the
|
|
190
|
+
one state in which the 1.0.0 legacy import cannot write. A missing file or a missing
|
|
191
|
+
`sessions` table is False: `connect()` creates the current schema for both. Read-only, so it
|
|
192
|
+
never creates or alters anything; raises sqlite3.DatabaseError for a file that is not a
|
|
193
|
+
valid database."""
|
|
194
|
+
if not path.exists():
|
|
195
|
+
return False
|
|
196
|
+
conn = db.connect(path, readonly=True)
|
|
197
|
+
try:
|
|
198
|
+
return sessions_table_exists(conn) and not sessions_schema_is_uuid_keyed(conn)
|
|
199
|
+
finally:
|
|
200
|
+
conn.close()
|
|
201
|
+
|
|
202
|
+
|
|
156
203
|
def connect(*, path: Path | None = None, readonly: bool = False) -> sqlite3.Connection:
|
|
157
204
|
"""Open sessions.db (or an explicit override path — used by tests and by
|
|
158
205
|
ccst doctor --mutes-file). readonly=True skips schema creation; callers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cc-session-tools
|
|
3
|
-
Version: 3.9.
|
|
3
|
+
Version: 3.9.2
|
|
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
|
|
@@ -192,8 +192,8 @@ Messages can also be addressed to a specific session (`--to-session`) or to a fr
|
|
|
192
192
|
description for whichever session picks it up first (`--to-description`) - project-
|
|
193
193
|
addressed is by far the most common pattern in practice.
|
|
194
194
|
|
|
195
|
-
The easiest way to use
|
|
196
|
-
another session (e.g. by name tag and/or location).
|
|
195
|
+
The easiest way to use messages when you need them is just to tell your session to send
|
|
196
|
+
a message to another session (e.g. by name tag and/or location).
|
|
197
197
|
|
|
198
198
|
## Scheduled housekeeping: `ccsched`
|
|
199
199
|
|
|
@@ -368,3 +368,58 @@ def test_pdata_init_write_still_adopts_after_an_earlier_dry_run(base_env, tmp_pa
|
|
|
368
368
|
assert "Adopting existing pdata from sync dump" in r_write.stdout
|
|
369
369
|
assert "macbook" in r_write.stdout
|
|
370
370
|
assert "SUCCESS" in r_write.stdout
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _publish_dump(base_env, project_dir, monkeypatch, *, with_record):
|
|
374
|
+
"""Build a published dump for project `demo`; keep the live .db only if `with_record`."""
|
|
375
|
+
from cc_session_tools.lib.pdata import dump, repository, service, vector_clock_store
|
|
376
|
+
|
|
377
|
+
monkeypatch.setenv("CCST_PROJECT_DB_DIR", base_env["CCST_PROJECT_DB_DIR"])
|
|
378
|
+
con = repository.connect("demo")
|
|
379
|
+
with repository._immediate(con):
|
|
380
|
+
vector_clock_store.write_vector(con, {"macbook": 3}, updated_at=100)
|
|
381
|
+
dump.write_latest(con, project_root=project_dir, machine_id="macbook", vector={"macbook": 3})
|
|
382
|
+
con.close()
|
|
383
|
+
db_file = Path(base_env["CCST_PROJECT_DB_DIR"]) / "demo.db"
|
|
384
|
+
if with_record:
|
|
385
|
+
service.add_record(project="demo", record_group="notes", content="live content", file_path=None, fields={})
|
|
386
|
+
else:
|
|
387
|
+
db_file.unlink()
|
|
388
|
+
return db_file
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def test_pdata_init_rehearse_classifies_when_live_project_already_migrated(
|
|
392
|
+
base_env, tmp_path, monkeypatch,
|
|
393
|
+
):
|
|
394
|
+
"""A rehearsal copy is a plain `cp -r` of the project: it carries the published dump but not
|
|
395
|
+
the real .db. The adopt-vs-classify decision must still use the real .db, so a project that
|
|
396
|
+
already has local pdata content gets a classification report, not an adoption message."""
|
|
397
|
+
project_dir = tmp_path / "projects" / "demo"
|
|
398
|
+
project_dir.mkdir(parents=True)
|
|
399
|
+
(project_dir / "ideas.csv").write_text("idea\nfirst\n")
|
|
400
|
+
db_file = _publish_dump(base_env, project_dir, monkeypatch, with_record=True)
|
|
401
|
+
rehearsal_dir = tmp_path / "rehearsal-demo"
|
|
402
|
+
shutil.copytree(project_dir, rehearsal_dir)
|
|
403
|
+
before = db_file.read_bytes()
|
|
404
|
+
|
|
405
|
+
r = _run(base_env, "pdata", "init", "--project", "demo", "--rehearse", str(rehearsal_dir))
|
|
406
|
+
|
|
407
|
+
assert r.returncode == 0, r.stderr
|
|
408
|
+
assert "a published sync dump already exists" not in r.stdout
|
|
409
|
+
assert "ideas.csv" in r.stdout
|
|
410
|
+
assert db_file.read_bytes() == before
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def test_pdata_init_rehearse_still_reports_adoption_when_no_local_content(
|
|
414
|
+
base_env, tmp_path, monkeypatch,
|
|
415
|
+
):
|
|
416
|
+
project_dir = tmp_path / "projects" / "demo"
|
|
417
|
+
project_dir.mkdir(parents=True)
|
|
418
|
+
_publish_dump(base_env, project_dir, monkeypatch, with_record=False)
|
|
419
|
+
rehearsal_dir = tmp_path / "rehearsal-demo"
|
|
420
|
+
shutil.copytree(project_dir, rehearsal_dir)
|
|
421
|
+
|
|
422
|
+
r = _run(base_env, "pdata", "init", "--project", "demo", "--rehearse", str(rehearsal_dir))
|
|
423
|
+
|
|
424
|
+
assert r.returncode == 0, r.stderr
|
|
425
|
+
assert "a published sync dump already exists" in r.stdout
|
|
@@ -249,9 +249,9 @@ def test_dry_run_does_not_record_marker(layout):
|
|
|
249
249
|
# ---------------------------------------------------------------------------
|
|
250
250
|
|
|
251
251
|
def _seed_old_schema_db(db_path: Path, rows: list[dict]) -> None:
|
|
252
|
-
"""Build a
|
|
253
|
-
`updated_at` column
|
|
254
|
-
|
|
252
|
+
"""Build a pre-3.0.0 sessions.db in the v2.13-v2.14.x shape: 2-column PK, no uuid column,
|
|
253
|
+
with the `updated_at` column. Releases v1.0.0 through v2.12.x had no `updated_at`; both
|
|
254
|
+
shapes are covered in test_sessions_db_migration_chain.py."""
|
|
255
255
|
import sqlite3
|
|
256
256
|
|
|
257
257
|
conn = sqlite3.connect(str(db_path))
|