cc-session-tools 3.0.0__tar.gz → 3.2.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.
Files changed (278) hide show
  1. {cc_session_tools-3.0.0/src/cc_session_tools.egg-info → cc_session_tools-3.2.0}/PKG-INFO +5 -1
  2. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/README.md +4 -0
  3. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/pyproject.toml +1 -1
  4. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/ccmsg.py +3 -1
  5. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/ccst.py +10 -1
  6. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/doctor.py +50 -0
  7. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/service.py +7 -4
  8. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/repository.py +28 -4
  9. cc_session_tools-3.2.0/src/cc_session_tools/skills/compact-smart/SKILL.md +87 -0
  10. {cc_session_tools-3.0.0/src/cc_session_tools/skills/pm-pdata-audit → cc_session_tools-3.2.0/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate}/SKILL.md +7 -7
  11. {cc_session_tools-3.0.0/src/cc_session_tools/skills/pm-pdata-migrate → cc_session_tools-3.2.0/src/cc_session_tools/skills/pm-pdata-do-migrate}/SKILL.md +10 -10
  12. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0/src/cc_session_tools.egg-info}/PKG-INFO +5 -1
  13. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools.egg-info/SOURCES.txt +3 -2
  14. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/confirm_8digit.py +23 -21
  15. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_bundle_discovery.py +21 -8
  16. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_doctor.py +71 -0
  17. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_cli.py +23 -0
  18. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_confirm_8digit.py +114 -0
  19. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/LICENSE +0 -0
  20. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/setup.cfg +0 -0
  21. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/__init__.py +0 -0
  22. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/__init__.py +0 -0
  23. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/ccd.py +0 -0
  24. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/ccr.py +0 -0
  25. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/ccs.py +0 -0
  26. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/ccsched.py +0 -0
  27. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
  28. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
  29. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
  30. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
  31. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/config/hooks-bundle.json +0 -0
  32. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/config/session-tag-hook.json +0 -0
  33. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/hooks_install.py +0 -0
  34. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/__init__.py +0 -0
  35. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/claude_flags.py +0 -0
  36. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/claude_md_install.py +0 -0
  37. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/context_overrides.py +0 -0
  38. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/db.py +0 -0
  39. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/debug.py +0 -0
  40. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
  41. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/hook_registry.py +0 -0
  42. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/install_sync.py +0 -0
  43. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/levenshtein.py +0 -0
  44. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/machine_identity.py +0 -0
  45. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
  46. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
  47. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
  48. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/lock.py +0 -0
  49. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/message.py +0 -0
  50. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
  51. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/repository.py +0 -0
  52. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/retention.py +0 -0
  53. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/messaging/store.py +0 -0
  54. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/occupancy.py +0 -0
  55. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/paths.py +0 -0
  56. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
  57. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/backup.py +0 -0
  58. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/classify.py +0 -0
  59. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/cutover.py +0 -0
  60. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/dump.py +0 -0
  61. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
  62. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/importers.py +0 -0
  63. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
  64. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/init_service.py +0 -0
  65. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
  66. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/naming.py +0 -0
  67. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
  68. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
  69. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
  70. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
  71. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/service.py +0 -0
  72. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
  73. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/store.py +0 -0
  74. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
  75. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
  76. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
  77. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
  78. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
  79. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/verify.py +0 -0
  80. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
  81. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/picker.py +0 -0
  82. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/proc_lock.py +0 -0
  83. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/prompts.py +0 -0
  84. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/roots.py +0 -0
  85. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/rules.py +0 -0
  86. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
  87. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
  88. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
  89. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
  90. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
  91. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/due.py +0 -0
  92. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
  93. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
  94. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
  95. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
  96. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
  97. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
  98. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
  99. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
  100. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/state.py +0 -0
  101. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/store.py +0 -0
  102. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
  103. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
  104. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
  105. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/session_gc.py +0 -0
  106. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/sessions.py +0 -0
  107. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/sessions_db.py +0 -0
  108. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/sessions_repair.py +0 -0
  109. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/shell_install.py +0 -0
  110. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/tasklist.py +0 -0
  111. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/lib/telemetry_store.py +0 -0
  112. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +0 -0
  113. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/prompts/pdata-migration-skills-update.md +0 -0
  114. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
  115. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
  116. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
  117. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
  118. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
  119. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
  120. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
  121. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
  122. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
  123. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
  124. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
  125. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
  126. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
  127. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
  128. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
  129. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
  130. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
  131. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/pm-pdata-conflict-resolution/SKILL.md +0 -0
  132. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/pm-pdata-schema-design/SKILL.md +0 -0
  133. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/pm-project-init/SKILL.md +0 -0
  134. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +0 -0
  135. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
  136. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
  137. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
  138. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
  139. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
  140. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
  141. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
  142. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
  143. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
  144. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
  145. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
  146. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
  147. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
  148. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
  149. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
  150. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools.egg-info/requires.txt +0 -0
  151. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/cc_session_tools.egg-info/top_level.txt +0 -0
  152. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/__init__.py +0 -0
  153. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/attribution.py +0 -0
  154. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/cache.py +0 -0
  155. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/ccusage_wrapper.py +0 -0
  156. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/cli.py +0 -0
  157. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/data/pricing.json +0 -0
  158. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/parent_inference.py +0 -0
  159. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/parser.py +0 -0
  160. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/pricing.py +0 -0
  161. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/query.py +0 -0
  162. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/report.py +0 -0
  163. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/schema.py +0 -0
  164. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/claude_code_usage/session_names.py +0 -0
  165. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/__init__.py +0 -0
  166. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/after_response.py +0 -0
  167. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/bash_hard_deny.py +0 -0
  168. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/bash_security_review.py +0 -0
  169. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/cache.py +0 -0
  170. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/catchup.py +0 -0
  171. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/context_window_warning.py +0 -0
  172. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/last_screenshot.py +0 -0
  173. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/marker_allow.py +0 -0
  174. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/markers.py +0 -0
  175. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/messaging_deliver.py +0 -0
  176. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/model_info.py +0 -0
  177. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/normalise.py +0 -0
  178. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/pdata_sync.py +0 -0
  179. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/pending_migration.py +0 -0
  180. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/pending_rename.py +0 -0
  181. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/session_tag.py +0 -0
  182. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/stats.py +0 -0
  183. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/telemetry.py +0 -0
  184. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/telemetry_query.py +0 -0
  185. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/telemetry_trim.py +0 -0
  186. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/transcript.py +0 -0
  187. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/src/hooks/worklog_guard.py +0 -0
  188. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_after_response.py +0 -0
  189. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_attribution.py +0 -0
  190. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_bash_hard_deny.py +0 -0
  191. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_bash_security_review.py +0 -0
  192. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cache.py +0 -0
  193. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cache_sqlite.py +0 -0
  194. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccr_orphans.py +0 -0
  195. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccs_emptiness.py +0 -0
  196. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccs_eta.py +0 -0
  197. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccs_list_mode.py +0 -0
  198. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccs_scope_flags.py +0 -0
  199. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccs_sentinel_sort.py +0 -0
  200. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccs_session_counts.py +0 -0
  201. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
  202. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_cli.py +0 -0
  203. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_context_override_cli.py +0 -0
  204. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_gc_prune.py +0 -0
  205. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_gc_report.py +0 -0
  206. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_hook_dispatcher.py +0 -0
  207. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_hooks_install_prune.py +0 -0
  208. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
  209. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_hooks_uninstall.py +0 -0
  210. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_install_everything.py +0 -0
  211. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_machine_identity_cli.py +0 -0
  212. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_main_gate.py +0 -0
  213. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_init_cli.py +0 -0
  214. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
  215. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
  216. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
  217. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_sync_cli.py +0 -0
  218. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_pdata_verify_cli.py +0 -0
  219. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_repair_sessions_cli.py +0 -0
  220. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_sessions_cli.py +0 -0
  221. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_shell_install.py +0 -0
  222. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_skills_install.py +0 -0
  223. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_skills_uninstall.py +0 -0
  224. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_telemetry_query.py +0 -0
  225. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccst_telemetry_trim.py +0 -0
  226. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_ccusage_wrapper.py +0 -0
  227. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_claude_flags.py +0 -0
  228. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_claude_md_install.py +0 -0
  229. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cli.py +0 -0
  230. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cli_ccd.py +0 -0
  231. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cli_ccd_dry_run.py +0 -0
  232. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cli_ccr.py +0 -0
  233. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cli_ccs.py +0 -0
  234. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_cli_version.py +0 -0
  235. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_context_overrides.py +0 -0
  236. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_context_window_warning.py +0 -0
  237. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_db.py +0 -0
  238. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_debug.py +0 -0
  239. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_doctor_mutes.py +0 -0
  240. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_empty_session.py +0 -0
  241. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_hook_cache.py +0 -0
  242. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_hooks_install.py +0 -0
  243. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_install_sync.py +0 -0
  244. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_last_screenshot.py +0 -0
  245. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_levenshtein.py +0 -0
  246. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_machine_identity.py +0 -0
  247. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_marker_allow.py +0 -0
  248. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_migrate_ccmsg.py +0 -0
  249. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_migrate_ccsched.py +0 -0
  250. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_migrate_sessions_db.py +0 -0
  251. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_migrate_telemetry.py +0 -0
  252. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_model_info.py +0 -0
  253. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_normalise.py +0 -0
  254. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_occupancy.py +0 -0
  255. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_parent_inference.py +0 -0
  256. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_parser.py +0 -0
  257. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_paths.py +0 -0
  258. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_pending_migration_hook.py +0 -0
  259. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_picker.py +0 -0
  260. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_pricing.py +0 -0
  261. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_proc_lock.py +0 -0
  262. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_prompts.py +0 -0
  263. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_query.py +0 -0
  264. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_report.py +0 -0
  265. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_roots.py +0 -0
  266. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_rules.py +0 -0
  267. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_scheduler_bundled_jobs.py +0 -0
  268. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_schema.py +0 -0
  269. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_session_names.py +0 -0
  270. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_session_tag.py +0 -0
  271. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_sessions.py +0 -0
  272. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_sessions_db.py +0 -0
  273. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_sessions_repair.py +0 -0
  274. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_tasklist.py +0 -0
  275. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_telemetry.py +0 -0
  276. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_telemetry_store.py +0 -0
  277. {cc_session_tools-3.0.0 → cc_session_tools-3.2.0}/tests/test_transcript.py +0 -0
  278. {cc_session_tools-3.0.0 → cc_session_tools-3.2.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.0.0
3
+ Version: 3.2.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
@@ -575,6 +575,10 @@ Curates the SHA-256 command cache the `bash-security-review` hook reads (`CCCS_U
575
575
 
576
576
  Archives (tar.gz, verified) then deletes `bash-security-review`'s own hook-security-check session transcripts — those whose first user message begins "Review this shell command for security risks" — which otherwise pile up by the thousands and pollute `claude --resume`/`--continue`. Triggers on "clean up hook sessions", "archive old hook security sessions", or `--resume` being cluttered with security-check conversations. Dry-run by default; 8-digit gated for `--execute` (via `generate-8digit-code`). Also runs unattended weekly via the bundled `clean-hook-sessions-weekly` job (see [Scheduled-task catch-up](#scheduled-task-catch-up)).
577
577
 
578
+ ### `compact-smart`
579
+
580
+ Drafts the compaction-preservation paragraph you'd otherwise ask Claude for by hand before `/compact`, then hands back the exact `/compact <paragraph>` command to run — it never invokes `/compact` itself (Claude Code excludes that command from what a skill can invoke). Triggers on `/compact-smart`, "give me a paragraph to pass to compact", "draft a compact instruction", "help me compact this session". Covers decisions and their rationale (especially mid-session corrections), file/PR/branch pointers, standing constraints, and outstanding tasks split into "Claude's next steps" vs. "the user's own to-dos" — then shows you the draft to confirm or edit (including dropping an unrelated thread) before finalizing. Refreshes the session's `working/WORKLOG.md` if one already exists (never creates one), which also clears the `worklog-guard` PreCompact hook's staleness gate for the command it hands back.
581
+
578
582
  See `docs/design.md` for the full design and CLI contract.
579
583
 
580
584
  ## Inter-session messaging
@@ -518,6 +518,10 @@ Curates the SHA-256 command cache the `bash-security-review` hook reads (`CCCS_U
518
518
 
519
519
  Archives (tar.gz, verified) then deletes `bash-security-review`'s own hook-security-check session transcripts — those whose first user message begins "Review this shell command for security risks" — which otherwise pile up by the thousands and pollute `claude --resume`/`--continue`. Triggers on "clean up hook sessions", "archive old hook security sessions", or `--resume` being cluttered with security-check conversations. Dry-run by default; 8-digit gated for `--execute` (via `generate-8digit-code`). Also runs unattended weekly via the bundled `clean-hook-sessions-weekly` job (see [Scheduled-task catch-up](#scheduled-task-catch-up)).
520
520
 
521
+ ### `compact-smart`
522
+
523
+ Drafts the compaction-preservation paragraph you'd otherwise ask Claude for by hand before `/compact`, then hands back the exact `/compact <paragraph>` command to run — it never invokes `/compact` itself (Claude Code excludes that command from what a skill can invoke). Triggers on `/compact-smart`, "give me a paragraph to pass to compact", "draft a compact instruction", "help me compact this session". Covers decisions and their rationale (especially mid-session corrections), file/PR/branch pointers, standing constraints, and outstanding tasks split into "Claude's next steps" vs. "the user's own to-dos" — then shows you the draft to confirm or edit (including dropping an unrelated thread) before finalizing. Refreshes the session's `working/WORKLOG.md` if one already exists (never creates one), which also clears the `worklog-guard` PreCompact hook's staleness gate for the command it hands back.
524
+
521
525
  See `docs/design.md` for the full design and CLI contract.
522
526
 
523
527
  ## Inter-session messaging
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cc-session-tools"
7
- version = "3.0.0"
7
+ version = "3.2.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" }
@@ -206,8 +206,10 @@ def _cmd_list(args: argparse.Namespace) -> int:
206
206
  partition=args.partition,
207
207
  from_uuid=args.from_uuid,
208
208
  )
209
+ now = datetime.now(timezone.utc)
209
210
  for r in rows:
210
- print(f"[{r.id}] {r.status:8} {r.to_kind}={r.to_value} · {r.subject}")
211
+ age = service.relative_age(r.sent_at, now)
212
+ print(f"[{r.id}] {r.status:8} {r.to_kind}={r.to_value} · {r.subject} · {age}")
211
213
  return 0
212
214
 
213
215
 
@@ -2921,7 +2921,16 @@ def _build_parser() -> argparse.ArgumentParser:
2921
2921
  pdata_schema_sub.required = True
2922
2922
 
2923
2923
  pdata_schema_add_field_parser = pdata_schema_sub.add_parser(
2924
- "add-field", help="Add/describe an extension-table field (idempotent)",
2924
+ "add-field",
2925
+ help="Add/describe an extension-table field (idempotent; re-run to edit its description)",
2926
+ description=(
2927
+ "Add an extension-table field, or edit an existing field's description. "
2928
+ "Idempotent: re-running this command against a field name that already exists "
2929
+ "updates its --description in place rather than erroring or duplicating the "
2930
+ "column - there is no separate 'edit-description' command, this is it. "
2931
+ "Re-running with a DIFFERENT --field TYPE than the field already has is "
2932
+ "rejected with an error; add-field never changes an existing column's type."
2933
+ ),
2925
2934
  )
2926
2935
  pdata_schema_add_field_parser.add_argument("--project", required=True, metavar="NAME")
2927
2936
  pdata_schema_add_field_parser.add_argument("--group", required=True, metavar="RECORD_GROUP")
@@ -205,6 +205,51 @@ def check_no_stale_hooks(settings: dict[str, Any]) -> list[CheckResult]:
205
205
  ]
206
206
 
207
207
 
208
+ def check_no_stale_skill_symlinks(skills_target_dir: Path) -> list[CheckResult]:
209
+ """WARN for every symlink under the installed skills directory whose target no longer
210
+ exists - the case a bundled skill's rename or removal leaves behind
211
+ (``ccst skills install`` only ever creates symlinks for skills currently in the bundled
212
+ source; it never removes one for a name that stopped being bundled, and
213
+ :func:`check_skill_symlink` only checks skills that ARE still bundled, so nothing else
214
+ catches this). Mirrors :func:`check_no_stale_hooks`'s shape for the same underlying
215
+ problem (a rename/removal leaving a stale registration behind), but WARN rather than
216
+ FAIL: a broken skill symlink degrades a feature, it does not break every matching event
217
+ of a running session the way a stale hook registration does.
218
+
219
+ OK (no findings) if the directory doesn't exist yet - nothing to check, not an error."""
220
+ if not skills_target_dir.is_dir():
221
+ return [CheckResult(
222
+ name="skills:no-stale",
223
+ status=Status.OK,
224
+ reason="skills directory does not exist yet",
225
+ )]
226
+
227
+ stale = [
228
+ entry for entry in sorted(skills_target_dir.iterdir())
229
+ if entry.is_symlink() and not entry.exists()
230
+ ]
231
+
232
+ if not stale:
233
+ return [CheckResult(
234
+ name="skills:no-stale",
235
+ status=Status.OK,
236
+ reason="no symlinks for removed/renamed skills",
237
+ )]
238
+
239
+ return [
240
+ CheckResult(
241
+ name=f"skills:stale:{entry.name}",
242
+ status=Status.WARN,
243
+ reason=(
244
+ f"{entry} points to {os.readlink(entry)}, which no longer exists - the "
245
+ f"bundled skill it named was likely renamed or removed. Safe to remove: "
246
+ f"rm {entry}"
247
+ ),
248
+ )
249
+ for entry in stale
250
+ ]
251
+
252
+
208
253
  def check_skill_symlink(skill_name: str, skill_src: Path, skills_dir: Path) -> CheckResult:
209
254
  """Check that skills_dir/<skill_name> is a valid CCST skill symlink.
210
255
 
@@ -1031,6 +1076,11 @@ def run_all_checks(
1031
1076
  )
1032
1077
  )
1033
1078
 
1079
+ # ...and the reverse direction: symlinks for skills that no longer exist (renamed/removed).
1080
+ # Runs regardless of whether skills_source_dir resolved above - an orphaned symlink can be
1081
+ # reported even if source discovery failed for an unrelated reason.
1082
+ results.extend(check_no_stale_skill_symlinks(skills_target_dir))
1083
+
1034
1084
  # Bundled ccsched jobs
1035
1085
  from cc_session_tools.lib.scheduler import bundled_jobs
1036
1086
 
@@ -116,6 +116,7 @@ class MessageRow:
116
116
  to_value: str
117
117
  from_session: str
118
118
  subject: str
119
+ sent_at: str
119
120
 
120
121
 
121
122
  def list_messages(
@@ -128,13 +129,15 @@ def list_messages(
128
129
  return [
129
130
  MessageRow(
130
131
  id=m.id, status=m.status, to_kind=m.to_kind, to_value=m.to_value,
131
- from_session=m.from_session, subject=m.subject,
132
+ from_session=m.from_session, subject=m.subject, sent_at=m.sent_at,
132
133
  )
133
134
  for m in repository.list_rows(status=status, partition=partition, from_uuid=from_uuid)
134
135
  ]
135
136
 
136
137
 
137
- def _relative_age(sent_at: str, now: datetime) -> str:
138
+ def relative_age(sent_at: str, now: datetime) -> str:
139
+ """Public: used by both this module's own digest formatting and ccmsg.py's `list`
140
+ output - a genuine cross-module utility, not internal-only."""
138
141
  sent = datetime.strptime(sent_at, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
139
142
  minutes = int((now - sent).total_seconds() // 60)
140
143
  if minutes < 1:
@@ -150,7 +153,7 @@ def _relative_age(sent_at: str, now: datetime) -> str:
150
153
  def _digest_line(message: Message, now: datetime) -> str:
151
154
  return (
152
155
  f"[{message.id}] from {message.from_session} ({message.from_project}) · "
153
- f"{message.subject} · {_relative_age(message.sent_at, now)}"
156
+ f"{message.subject} · {relative_age(message.sent_at, now)}"
154
157
  )
155
158
 
156
159
 
@@ -213,7 +216,7 @@ def _collect_receipts(ctx: SessionContext, now: datetime) -> list[str]:
213
216
  who = message.read_by_session or "a session"
214
217
  lines.append(
215
218
  f'✓ read: "{message.subject}" by {who} '
216
- f"({_relative_age(message.sent_at, now)}) [{message.id}]"
219
+ f"({relative_age(message.sent_at, now)}) [{message.id}]"
217
220
  )
218
221
  shown.append(message.id)
219
222
  repository.mark_receipts_shown(shown)
@@ -169,6 +169,14 @@ def list_extension_columns(conn: sqlite3.Connection, record_group: str) -> list[
169
169
  ]
170
170
 
171
171
 
172
+ def _extension_column_type(conn: sqlite3.Connection, table: str, field_name: str) -> str | None:
173
+ """The stored SQL type of an existing extension column, or None if it doesn't exist."""
174
+ for r in conn.execute(f'PRAGMA table_info("{table}")'):
175
+ if r["name"] == field_name:
176
+ return str(r["type"])
177
+ return None
178
+
179
+
172
180
  def add_extension_column(
173
181
  conn: sqlite3.Connection,
174
182
  record_group: str,
@@ -179,16 +187,32 @@ def add_extension_column(
179
187
  ) -> bool:
180
188
  """Idempotent: creates ext_<group> if missing (backfilling existing rows — see
181
189
  ensure_extension_table), then ADD COLUMN if field_name isn't already a column (no-op if it
182
- already exists — spec §5's schema add-field idempotency). Returns True iff a column was
183
- actually added, False on the no-op path — a caller gating a side effect (e.g. a
184
- vector-clock bump) on whether this call changed anything needs that distinction, not just
185
- "did it run without erroring". Caller owns the transaction."""
190
+ already exists with the SAME type — spec §5's schema add-field idempotency). Returns True
191
+ iff a column was actually added, False on the no-op path — a caller gating a side effect
192
+ (e.g. a vector-clock bump) on whether this call changed anything needs that distinction,
193
+ not just "did it run without erroring". Caller owns the transaction.
194
+
195
+ Raises ValueError if field_name already exists with a DIFFERENT sql_type than requested -
196
+ rather than silently keeping the original type with no error, which is what this function
197
+ used to do (a rerun could look successful while dropping the type change entirely). An
198
+ actual column-type change is a real ALTER-TABLE-cannot-do-this rebuild, the same complexity
199
+ class as a primary-key change - disproportionate for this metadata-editing command; the
200
+ remediation text below is the deliberate manual escape hatch instead of building one."""
186
201
  naming.validate_field_name(field_name)
187
202
  normalized_type = _normalize_column_type(sql_type)
188
203
  ensure_extension_table(conn, record_group)
189
204
  table = naming.extension_table_name(record_group)
190
205
  existing = set(list_extension_columns(conn, record_group))
191
206
  if field_name in existing:
207
+ current_type = _extension_column_type(conn, table, field_name)
208
+ if current_type is not None and current_type != normalized_type:
209
+ raise ValueError(
210
+ f"field {field_name!r} in record_group {record_group!r} already has type "
211
+ f"{current_type!r}; you requested {normalized_type!r}. add-field never changes "
212
+ f"an existing column's type in place. To actually change it, drop and recreate "
213
+ f"the extension column by hand (this loses existing data in that column), or "
214
+ f"open an issue if this needs first-class support."
215
+ )
192
216
  return False
193
217
  if default is None:
194
218
  conn.execute(f'ALTER TABLE "{table}" ADD COLUMN "{field_name}" {normalized_type}')
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: compact-smart
3
+ description: Draft a compaction-preservation paragraph from the current session and hand back the exact /compact command to run - automates what the user otherwise asks for by hand ("give me a paragraph to pass to /compact"). Use when the user runs /compact-smart, or says "give me a paragraph to pass to compact", "draft a compact instruction", "help me compact this session", "what should I tell compact to keep", or is about to /compact and wants to be sure nothing important gets lost.
4
+ ---
5
+
6
+ # compact-smart
7
+
8
+ Drafts the instructions `/compact [instructions]` should carry, then hands back the exact
9
+ command to run. Does **not** invoke `/compact` itself - Claude Code's own docs explicitly
10
+ exclude `/compact` from the set of built-in commands a skill can invoke, so the deliverable
11
+ here is a ready-to-run command, not a compaction event.
12
+
13
+ ## Why this exists
14
+
15
+ Real usage (a week of transcripts across every project) shows the user asking, by hand, for
16
+ "a paragraph to pass to /compact" roughly 3 times for every bare `/compact` call - and
17
+ re-explaining what counts as "relevant" nearly every time. What already survives compaction
18
+ automatically (project-root `CLAUDE.md`, memory files, system/environment info) is re-loaded
19
+ fresh from disk; what does **not** reliably survive is anything that only ever existed in
20
+ the conversation itself - decisions, corrections, standing agreements, outstanding to-dos.
21
+ A `/compact` instruction protects those for the *next* compaction only; the resulting
22
+ summary is itself just conversation text again and can be trimmed by a *later* one - so this
23
+ skill also refreshes the session's `WORKLOG.md`, if it has one, as a second, more durable
24
+ safeguard.
25
+
26
+ ## What to draft
27
+
28
+ Review the conversation so far (you already have it loaded - no need to re-read the
29
+ transcript file from disk) and produce a paragraph covering, in this order:
30
+
31
+ 1. **Decisions and their rationale** - especially any point where the initial plan or
32
+ approach was found wrong and corrected mid-session. State the corrected approach and WHY
33
+ it changed, not just what the original plan was.
34
+ 2. **Concrete artifact pointers** - file paths, PR numbers, branch names, commit hashes,
35
+ OpenSpec change names, or anything else someone would need to go find the actual work.
36
+ 3. **Standing constraints or policies** established or confirmed during this session that
37
+ must carry forward (e.g. "no attribution lines in commits", "confirm before opening a
38
+ PR", a data-handling rule agreed mid-session). These are easy for a generic summary to
39
+ drop silently because they're usually stated once, early, and never repeated.
40
+ 4. **Outstanding tasks, split into two distinct groups**: what you (Claude) will do next,
41
+ and - separately and explicitly labeled - anything that is the *user's own* action to
42
+ take (e.g. "you still need to delete branch X yourself", "review the PR before merging").
43
+ Do not fold these two categories together.
44
+ 5. **An explicit "what happens next" statement.** Never leave this implicit.
45
+
46
+ Keep it dense and concrete - file paths and specific facts, not vague summary language.
47
+
48
+ ## Handling `working/WORKLOG.md`
49
+
50
+ Check whether the current session has one: `$CLD_SESSION_DIR/working/WORKLOG.md`.
51
+
52
+ - **If it exists**: update it with the same substance as the draft above (append or rewrite
53
+ its "current state" section - match whatever structure the file already uses). This
54
+ repo's own `worklog-guard` PreCompact hook blocks a manual `/compact` if this file is more
55
+ than an hour stale, so refreshing it here also clears that gate for the command you're
56
+ about to hand back.
57
+ - **If it does not exist**: do not create one. `ccd`/`ccr` never create a WORKLOG.md
58
+ automatically - it is opt-in scratch content the user or Claude may choose to keep during
59
+ a session, not mandatory state. Creating one here for the first time would be the skill
60
+ inventing a convention that has never applied to this session.
61
+
62
+ ## Confirmation step
63
+
64
+ Show the drafted paragraph to the user before finalizing anything. Do not skip this even if
65
+ the draft feels obviously complete - the point is to catch a miss before compaction discards
66
+ the detail, not after. Accept:
67
+
68
+ - Approval as-is, or
69
+ - Free-form edits, or
70
+ - An explicit exclusion ("drop the X thread, keep everything else") - a stale, unrelated
71
+ tangent that happened to occur in the same session is a real, recurring case; when asked,
72
+ remove that content from the paragraph rather than trying to talk the user out of it.
73
+
74
+ This is a single review-and-edit round, not a multi-question interview - draft first, then
75
+ let the user correct it in one pass. Asking a series of clarifying questions before drafting
76
+ anything reintroduces exactly the friction this skill exists to remove.
77
+
78
+ ## Final output
79
+
80
+ Once confirmed, present the exact command on its own line, ready to copy or type verbatim:
81
+
82
+ ```
83
+ /compact <finalized paragraph>
84
+ ```
85
+
86
+ Do not summarize what you're about to do instead of showing the literal command - the
87
+ command itself is the deliverable.
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: pm-pdata-audit
3
- description: Full consistency/completeness audit of a project's central and recordkeeping files - OneDrive sync-conflict cleanup, cross-checking every index/register/log against what it claims to describe, and a pdata-migration readiness pass. Ends by writing a readiness manifest file that the companion `pm-pdata-migrate` skill consumes to actually run the migration. Triggers on "review all my central files", "audit the project files", "check everything is up to date and consistent", "get this project ready for pdata", "/pm-pdata-audit", or any request for a thorough review/health-check of a project's index/log/register files (as opposed to a single-session wrap-up, which is pm-update-central-files). Especially relevant when a project is scheduled to migrate to pdata - this audit is the recommended prerequisite pass, since a schema design should not be built against files that still have unresolved sync-conflict duplicates or known dangling rows.
2
+ name: pm-pdata-do-audit-and-prepare-to-migrate
3
+ description: Full consistency/completeness audit of a project's central and recordkeeping files - OneDrive sync-conflict cleanup, cross-checking every index/register/log against what it claims to describe, and a pdata-migration readiness pass. Ends by writing a readiness manifest file that the companion `pm-pdata-do-migrate` skill consumes to actually run the migration. Triggers on "review all my central files", "audit the project files", "check everything is up to date and consistent", "get this project ready for pdata", "/pm-pdata-do-audit-and-prepare-to-migrate", or any request for a thorough review/health-check of a project's index/log/register files (as opposed to a single-session wrap-up, which is pm-update-central-files). Especially relevant when a project is scheduled to migrate to pdata - this audit is the recommended prerequisite pass, since a schema design should not be built against files that still have unresolved sync-conflict duplicates or known dangling rows.
4
4
  ---
5
5
 
6
6
  # Audit central files
@@ -11,7 +11,7 @@ description: Full consistency/completeness audit of a project's central and reco
11
11
  memory rules, register new `out/` files, and surface a handful of session-specific checkpoint
12
12
  items before the user exits.
13
13
 
14
- `pm-pdata-audit` is a much bigger, standalone task: a project-wide sweep of every
14
+ `pm-pdata-do-audit-and-prepare-to-migrate` is a much bigger, standalone task: a project-wide sweep of every
15
15
  central file (indexes, logs, registers, CSVs) that could span an entire session or more, run on
16
16
  demand rather than at every session's end. Use it when the user wants the whole project's
17
17
  recordkeeping health-checked, not just today's session tidied up. It is squarely a
@@ -134,7 +134,7 @@ This file is deliberately NOT a migration plan - it's a findings list for whoeve
134
134
  `pm-pdata-schema-design` session next, so that work isn't designed against a still-messy
135
135
  substrate.
136
136
 
137
- ## Phase 5: write the readiness manifest (for `pm-pdata-migrate` to consume)
137
+ ## Phase 5: write the readiness manifest (for `pm-pdata-do-migrate` to consume)
138
138
 
139
139
  Once the audit reaches a settled state (per your own stopping rule - typically two consecutive
140
140
  clean verification rounds, or a documented pragmatic stop), write one machine-and-human-readable
@@ -145,7 +145,7 @@ otherwise the project's own top-level docs location. Its existence and location
145
145
  recorded in the project's own instructions file (`CLAUDE.md`/`AGENTS.md`/equivalent) so a future
146
146
  session doesn't have to search for it.
147
147
 
148
- The manifest must have, in this order, so `pm-pdata-migrate` can parse it deterministically:
148
+ The manifest must have, in this order, so `pm-pdata-do-migrate` can parse it deterministically:
149
149
 
150
150
  1. **Header**: audit date, session/agent identifier, and an explicit "STALE IF" note - state the
151
151
  condition that would invalidate this manifest (e.g. "stale if any of the files below have
@@ -154,7 +154,7 @@ The manifest must have, in this order, so `pm-pdata-migrate` can parse it determ
154
154
  row/entry count, last-verified-clean date, and its role (`index`, `register`, `log`,
155
155
  `narrative`, `derived-artefact`).
156
156
  3. **Per-file migration blockers**, in the exact shape `pm-pdata-schema-design` and
157
- `pm-pdata-migrate` expect to consume - this is the same content this skill's own Phase 4
157
+ `pm-pdata-do-migrate` expect to consume - this is the same content this skill's own Phase 4
158
158
  already produces, just formalised into the manifest rather than a standalone prose report:
159
159
  inconsistent separators, unstable/non-existent natural keys, mixed date formats, multi-schema
160
160
  single files, machine-specific paths, null-value conventions that aren't real nulls.
@@ -165,7 +165,7 @@ The manifest must have, in this order, so `pm-pdata-migrate` can parse it determ
165
165
  a migration pass doesn't waste time re-flagging it or, worse, silently drops one copy.
166
166
  6. **Open decisions** - anything the audit found but explicitly did NOT resolve because it needed
167
167
  the project owner's judgement (scope calls, curation completeness, which of two forks is
168
- canonical). List these plainly; `pm-pdata-migrate` must not treat an unresolved item as settled.
168
+ canonical). List these plainly; `pm-pdata-do-migrate` must not treat an unresolved item as settled.
169
169
 
170
170
  ## Common mistakes
171
171
 
@@ -1,21 +1,21 @@
1
1
  ---
2
- name: pm-pdata-migrate
3
- description: Execute a project's actual migration from flat central/index files to a ccst pdata record store - build a migration plan, adversarially verify the plan itself, present it for the user's confirmation, then execute it and write a results file. Consumes the readiness manifest pm-pdata-audit produces if one exists, but always explores the project independently too, so it works even without that manifest. Triggers on "migrate this project to pdata", "run the pdata migration", "/pm-pdata-migrate", or any request to actually move a project's CSV/index/log files into pdata (as opposed to just auditing readiness, which is pm-pdata-audit, or designing the target schema in the abstract, which is pm-pdata-schema-design).
2
+ name: pm-pdata-do-migrate
3
+ description: Execute a project's actual migration from flat central/index files to a ccst pdata record store - build a migration plan, adversarially verify the plan itself, present it for the user's confirmation, then execute it and write a results file. Consumes the readiness manifest pm-pdata-do-audit-and-prepare-to-migrate produces if one exists, but always explores the project independently too, so it works even without that manifest. Triggers on "migrate this project to pdata", "run the pdata migration", "/pm-pdata-do-migrate", or any request to actually move a project's CSV/index/log files into pdata (as opposed to just auditing readiness, which is pm-pdata-do-audit-and-prepare-to-migrate, or designing the target schema in the abstract, which is pm-pdata-schema-design).
4
4
  ---
5
5
 
6
6
  # Migrate a project to pdata
7
7
 
8
8
  ## When to use this vs. the other pm-pdata-* skills
9
9
 
10
- - `pm-pdata-audit` - readiness audit. Finds and fixes problems in the CURRENT flat files.
10
+ - `pm-pdata-do-audit-and-prepare-to-migrate` - readiness audit. Finds and fixes problems in the CURRENT flat files.
11
11
  Produces a readiness manifest. Does not touch pdata at all.
12
12
  - `pm-pdata-schema-design` - designs the TARGET record-group schema in the abstract (field names,
13
13
  types, groupings) before any data moves.
14
- - `pm-pdata-migrate` (this skill) - takes whatever schema exists (designed via the skill above,
14
+ - `pm-pdata-do-migrate` (this skill) - takes whatever schema exists (designed via the skill above,
15
15
  or designed inline as part of this skill's own plan if no separate design session happened) and
16
16
  actually moves the data: `ccst pdata add`/`batch` calls, verification, and a results record.
17
17
 
18
- Run `pm-pdata-audit` first if the project hasn't been through it recently - a migration
18
+ Run `pm-pdata-do-audit-and-prepare-to-migrate` first if the project hasn't been through it recently - a migration
19
19
  plan built against files with known unresolved duplicates or dangling rows just bakes the mess
20
20
  into pdata. This skill doesn't refuse to run without that prerequisite (see below), but it will
21
21
  re-derive the same checks itself if skipped, which costs more and finds less than a dedicated
@@ -23,7 +23,7 @@ audit pass would.
23
23
 
24
24
  ## Step 1: gather readiness information (works with or without a manifest)
25
25
 
26
- 1. Look for a readiness manifest from `pm-pdata-audit` (check the project's own
26
+ 1. Look for a readiness manifest from `pm-pdata-do-audit-and-prepare-to-migrate` (check the project's own
27
27
  instructions file for where it said one was written, typically `analysis/pdata-readiness.md`
28
28
  or similar). If found: read it, and check its own "STALE IF" condition - if any listed file
29
29
  has been edited more recently than the manifest's audit date, treat the manifest as a starting
@@ -32,7 +32,7 @@ audit pass would.
32
32
  project's central/index/recordkeeping files: read the project's own instructions file in full,
33
33
  list every central file it documents, and for each one note its row/entry count, its apparent
34
34
  natural key (if any), and any obvious structural issue (multi-schema single files, inconsistent
35
- separators, machine-specific paths) - the same categories `pm-pdata-audit` Phase 4
35
+ separators, machine-specific paths) - the same categories `pm-pdata-do-audit-and-prepare-to-migrate` Phase 4
36
36
  checks for. If a manifest exists, use this pass to spot-check a sample of its claims rather
37
37
  than skipping it entirely - a stale or partially-wrong manifest is worse than no manifest if
38
38
  trusted blindly.
@@ -50,7 +50,7 @@ ran):
50
50
  `filings-index`), not its filename.
51
51
  - **Natural key** - prefer a field that's already unique, stable, and won't be reassigned by a
52
52
  future session (a project-prefixed sequential id like `FIL-001` is a good sign; a per-session
53
- independently-renumbered `row_id` is not - see `pm-pdata-audit`'s own warnings about
53
+ independently-renumbered `row_id` is not - see `pm-pdata-do-audit-and-prepare-to-migrate`'s own warnings about
54
54
  this exact failure mode, which shows up often). If nothing qualifies, mint a synthetic id
55
55
  at migration time and record the mapping.
56
56
  - **Field types and normalisation** - fix the specific blockers the manifest/your own audit
@@ -127,13 +127,13 @@ have N records instead of the source file's row count" without re-deriving the r
127
127
 
128
128
  ## Common mistakes
129
129
 
130
- - Refusing to run without a `pm-pdata-audit` manifest present - always fall back to your
130
+ - Refusing to run without a `pm-pdata-do-audit-and-prepare-to-migrate` manifest present - always fall back to your
131
131
  own exploration instead; the manifest is an optimisation, not a hard dependency.
132
132
  - Trusting a present manifest without checking its staleness condition against current file
133
133
  states - a manifest from a project that's had further edits since is actively dangerous if
134
134
  taken at face value.
135
135
  - Migrating a session-relative or otherwise unstable field as if it were a durable natural key -
136
- this is the single most common blocker `pm-pdata-audit` finds in practice; take its
136
+ this is the single most common blocker `pm-pdata-do-audit-and-prepare-to-migrate` finds in practice; take its
137
137
  warnings about this seriously rather than assuming "it looks like an id column" is enough.
138
138
  - Executing before the user has explicitly confirmed the plan summary in step 5 - a bulk pdata
139
139
  write is exactly the kind of hard-to-reverse, outward-facing action that needs confirmation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-session-tools
3
- Version: 3.0.0
3
+ Version: 3.2.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
@@ -575,6 +575,10 @@ Curates the SHA-256 command cache the `bash-security-review` hook reads (`CCCS_U
575
575
 
576
576
  Archives (tar.gz, verified) then deletes `bash-security-review`'s own hook-security-check session transcripts — those whose first user message begins "Review this shell command for security risks" — which otherwise pile up by the thousands and pollute `claude --resume`/`--continue`. Triggers on "clean up hook sessions", "archive old hook security sessions", or `--resume` being cluttered with security-check conversations. Dry-run by default; 8-digit gated for `--execute` (via `generate-8digit-code`). Also runs unattended weekly via the bundled `clean-hook-sessions-weekly` job (see [Scheduled-task catch-up](#scheduled-task-catch-up)).
577
577
 
578
+ ### `compact-smart`
579
+
580
+ Drafts the compaction-preservation paragraph you'd otherwise ask Claude for by hand before `/compact`, then hands back the exact `/compact <paragraph>` command to run — it never invokes `/compact` itself (Claude Code excludes that command from what a skill can invoke). Triggers on `/compact-smart`, "give me a paragraph to pass to compact", "draft a compact instruction", "help me compact this session". Covers decisions and their rationale (especially mid-session corrections), file/PR/branch pointers, standing constraints, and outstanding tasks split into "Claude's next steps" vs. "the user's own to-dos" — then shows you the draft to confirm or edit (including dropping an unrelated thread) before finalizing. Refreshes the session's `working/WORKLOG.md` if one already exists (never creates one), which also clears the `worklog-guard` PreCompact hook's staleness gate for the command it hands back.
581
+
578
582
  See `docs/design.md` for the full design and CLI contract.
579
583
 
580
584
  ## Inter-session messaging
@@ -108,6 +108,7 @@ src/cc_session_tools/prompts/pdata-migration-skills-update.md
108
108
  src/cc_session_tools/skills/analyse-cc-usage/SKILL.md
109
109
  src/cc_session_tools/skills/clean-hook-sessions/SKILL.md
110
110
  src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py
111
+ src/cc_session_tools/skills/compact-smart/SKILL.md
111
112
  src/cc_session_tools/skills/context-override/SKILL.md
112
113
  src/cc_session_tools/skills/delete-sessions/SKILL.md
113
114
  src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py
@@ -122,9 +123,9 @@ src/cc_session_tools/skills/move-session/SKILL.md
122
123
  src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh
123
124
  src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py
124
125
  src/cc_session_tools/skills/move-session/scripts/move_session.py
125
- src/cc_session_tools/skills/pm-pdata-audit/SKILL.md
126
126
  src/cc_session_tools/skills/pm-pdata-conflict-resolution/SKILL.md
127
- src/cc_session_tools/skills/pm-pdata-migrate/SKILL.md
127
+ src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md
128
+ src/cc_session_tools/skills/pm-pdata-do-migrate/SKILL.md
128
129
  src/cc_session_tools/skills/pm-pdata-schema-design/SKILL.md
129
130
  src/cc_session_tools/skills/pm-project-init/SKILL.md
130
131
  src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md
@@ -11,7 +11,11 @@ checks (skill-marker exceptions short-circuit them):
11
11
 
12
12
  If all three hold and the assistant-to-user reply gap is under 30 minutes,
13
13
  the call is allowed. Otherwise the hook either prints a warning (default,
14
- ``CCCS_ENFORCE_8DIGIT=warn``) or blocks with exit 2 (``=block``).
14
+ ``CCCS_ENFORCE_8DIGIT=warn``) or blocks with exit 2 (``=block``) - and, either
15
+ way, pushes the same message as a Telegram notification via
16
+ ``lib.scheduler.notify.send_telegram`` (best-effort; never changes the exit
17
+ code or stderr message on its own failure), so the block/warn reaches the
18
+ user even when they are away from the terminal.
15
19
 
16
20
  Skill-marker exceptions live under ``~/.cache/claude/markers/`` (override with
17
21
  the ``CCCS_MARKERS_DIR`` env var) and have a 1-hour TTL based on file mtime.
@@ -31,6 +35,7 @@ import sys
31
35
  import time
32
36
  from typing import Literal
33
37
 
38
+ from cc_session_tools.lib.scheduler.notify import send_telegram
34
39
  from hooks.markers import markers_dir
35
40
  from hooks.transcript import (
36
41
  TranscriptNotFound,
@@ -209,14 +214,13 @@ def verify(
209
214
  try:
210
215
  turns = load_transcript(session_id=session_id, cwd=cwd)
211
216
  except TranscriptNotFound as e:
212
- return VerificationResult(
213
- exit_code=2,
214
- message=(
215
- f"[8digit-block] cannot verify 8-digit confirmation for "
216
- f"{tool_name}: transcript not found ({e}). Re-run from a real "
217
- f"Claude Code session, or set the appropriate skill marker."
218
- ),
217
+ message = (
218
+ f"[8digit-block] cannot verify 8-digit confirmation for "
219
+ f"{tool_name}: transcript not found ({e}). Re-run from a real "
220
+ f"Claude Code session, or set the appropriate skill marker."
219
221
  )
222
+ send_telegram(message)
223
+ return VerificationResult(exit_code=2, message=message)
220
224
 
221
225
  # 3. Three-part verification.
222
226
  failures: list[str] = []
@@ -251,21 +255,19 @@ def verify(
251
255
 
252
256
  detail = "; ".join(failures) if failures else "verification failed"
253
257
  if enforce == "block":
254
- return VerificationResult(
255
- exit_code=2,
256
- message=(
257
- f"[8digit-block] {tool_name}: {detail}. Generate a fresh "
258
- f"8-digit code, share it as 'Respond with NNNNNNNN ...', and "
259
- f"only proceed if the user replies with exactly that string."
260
- ),
258
+ message = (
259
+ f"[8digit-block] {tool_name}: {detail}. Generate a fresh "
260
+ f"8-digit code, share it as 'Respond with NNNNNNNN ...', and "
261
+ f"only proceed if the user replies with exactly that string."
261
262
  )
262
- return VerificationResult(
263
- exit_code=0,
264
- message=(
265
- f"[8digit-warn] {tool_name}: {detail}. "
266
- f"(Set CCCS_ENFORCE_8DIGIT=block to enforce.)"
267
- ),
263
+ send_telegram(message)
264
+ return VerificationResult(exit_code=2, message=message)
265
+ message = (
266
+ f"[8digit-warn] {tool_name}: {detail}. "
267
+ f"(Set CCCS_ENFORCE_8DIGIT=block to enforce.)"
268
268
  )
269
+ send_telegram(message)
270
+ return VerificationResult(exit_code=0, message=message)
269
271
 
270
272
 
271
273
  def main(argv: list[str] | None = None) -> int:
@@ -141,12 +141,25 @@ def test_bundled_prompts_are_no_longer_placeholders():
141
141
 
142
142
 
143
143
  def test_pm_pdata_audit_and_migrate_are_bundled_skills():
144
- """pm-pdata-audit and pm-pdata-migrate were built directly under ~/.claude/skills/ in an
145
- earlier session - moved into this repo's bundled skills/ so ccst skills install /
146
- install-everything provision them on every machine, not just the one they were authored
147
- on. No registry to update - _discover_skills is a directory scan (any subdir with a
148
- SKILL.md), so this test is really asserting the directories/files exist and are named
149
- correctly."""
144
+ """pm-pdata-do-audit-and-prepare-to-migrate and pm-pdata-do-migrate (renamed from
145
+ pm-pdata-audit/pm-pdata-migrate in 3.1.0 for clarity) were built directly under
146
+ ~/.claude/skills/ in an earlier session - moved into this repo's bundled skills/ so
147
+ ccst skills install / install-everything provision them on every machine, not just the
148
+ one they were authored on. No registry to update - _discover_skills is a directory scan
149
+ (any subdir with a SKILL.md), so this test is really asserting the directories/files
150
+ exist and are named correctly."""
150
151
  discovered = {p.name for p in ccst._discover_skills(ccst._discover_source_dir())}
151
- assert "pm-pdata-audit" in discovered
152
- assert "pm-pdata-migrate" in discovered
152
+ assert "pm-pdata-do-audit-and-prepare-to-migrate" in discovered
153
+ assert "pm-pdata-do-migrate" in discovered
154
+
155
+
156
+ def test_compact_smart_is_a_bundled_skill():
157
+ """compact-smart drafts a /compact preservation paragraph and hands back the exact
158
+ command to run - see openspec/changes/add-compact-smart-skill (archived) for the design.
159
+ Pure instruction skill, no backing script - this discovery assertion plus a frontmatter
160
+ sanity check is the extent of what's mechanically testable (design.md Decision 1)."""
161
+ skills = {p.name: p for p in ccst._discover_skills(ccst._discover_source_dir())}
162
+ assert "compact-smart" in skills
163
+ text = (skills["compact-smart"] / "SKILL.md").read_text()
164
+ assert text.startswith("---\nname: compact-smart\n")
165
+ assert "/compact-smart" in text