cc-session-tools 3.12.0__tar.gz → 3.12.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.
Files changed (304) hide show
  1. {cc_session_tools-3.12.0/src/cc_session_tools.egg-info → cc_session_tools-3.12.2}/PKG-INFO +1 -1
  2. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/pyproject.toml +1 -1
  3. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/doctor.py +3 -3
  4. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/install_sync.py +2 -1
  5. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/service.py +8 -3
  6. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/cutover.py +2 -2
  7. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/readiness.py +16 -0
  8. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/service.py +7 -5
  9. cc_session_tools-3.12.2/src/cc_session_tools/lib/timefmt.py +17 -0
  10. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +2 -2
  11. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2/src/cc_session_tools.egg-info}/PKG-INFO +1 -1
  12. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools.egg-info/SOURCES.txt +3 -0
  13. cc_session_tools-3.12.2/tests/test_date_format_conformance.py +140 -0
  14. cc_session_tools-3.12.2/tests/test_timefmt.py +23 -0
  15. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/LICENSE +0 -0
  16. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/README.md +0 -0
  17. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/setup.cfg +0 -0
  18. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/__init__.py +0 -0
  19. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/__init__.py +0 -0
  20. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/ccd.py +0 -0
  21. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/ccmsg.py +0 -0
  22. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/ccr.py +0 -0
  23. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/ccs.py +0 -0
  24. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/ccsched.py +0 -0
  25. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/ccst.py +0 -0
  26. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
  27. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
  28. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
  29. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
  30. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/config/api-keys.example +0 -0
  31. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/config/hooks-bundle.json +0 -0
  32. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/config/session-tag-hook.json +0 -0
  33. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/hooks_install.py +0 -0
  34. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/__init__.py +0 -0
  35. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/api_keys.py +0 -0
  36. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/bun_tools.py +0 -0
  37. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/claude_flags.py +0 -0
  38. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/claude_md_install.py +0 -0
  39. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/context_overrides.py +0 -0
  40. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/db.py +0 -0
  41. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/debug.py +0 -0
  42. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
  43. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/hook_registry.py +0 -0
  44. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/levenshtein.py +0 -0
  45. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/machine_identity.py +0 -0
  46. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
  47. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
  48. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
  49. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/lock.py +0 -0
  50. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/message.py +0 -0
  51. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
  52. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/repository.py +0 -0
  53. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/retention.py +0 -0
  54. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/messaging/store.py +0 -0
  55. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/occupancy.py +0 -0
  56. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/paths.py +0 -0
  57. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
  58. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/backup.py +0 -0
  59. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/classify.py +0 -0
  60. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/dump.py +0 -0
  61. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
  62. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/importers.py +0 -0
  63. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
  64. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/init_service.py +0 -0
  65. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
  66. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/naming.py +0 -0
  67. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
  68. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
  69. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
  70. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/repository.py +0 -0
  71. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
  72. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
  73. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/store.py +0 -0
  74. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
  75. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
  76. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
  77. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
  78. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
  79. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/verify.py +0 -0
  80. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
  81. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/picker.py +0 -0
  82. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/proc_lock.py +0 -0
  83. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/prompts.py +0 -0
  84. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/roots.py +0 -0
  85. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/rules.py +0 -0
  86. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
  87. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
  88. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
  89. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
  90. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
  91. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/due.py +0 -0
  92. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
  93. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
  94. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
  95. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
  96. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
  97. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
  98. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
  99. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
  100. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/state.py +0 -0
  101. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/store.py +0 -0
  102. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
  103. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
  104. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
  105. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/session_gc.py +0 -0
  106. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/sessions.py +0 -0
  107. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/sessions_db.py +0 -0
  108. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/sessions_repair.py +0 -0
  109. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/shell_install.py +0 -0
  110. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/tasklist.py +0 -0
  111. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/telemetry_store.py +0 -0
  112. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/lib/text_wrap.py +0 -0
  113. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
  114. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
  115. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
  116. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
  117. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
  118. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
  119. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
  120. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
  121. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/extract-pptx/SKILL.md +0 -0
  122. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/extract-pptx/scripts/extract_pptx.py +0 -0
  123. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/extract-to-text/SKILL.md +0 -0
  124. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/extract-to-text/scripts/extract_to_text.py +0 -0
  125. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
  126. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
  127. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
  128. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
  129. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
  130. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
  131. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
  132. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
  133. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
  134. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
  135. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-design-schema/SKILL.md +0 -0
  136. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-do-init/SKILL.md +0 -0
  137. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-do-manual-migration/SKILL.md +0 -0
  138. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-do-update-consuming-skills/SKILL.md +0 -0
  139. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-do-update-project-docs/SKILL.md +0 -0
  140. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-pdata-resolve-conflicts/SKILL.md +0 -0
  141. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +0 -0
  142. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
  143. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
  144. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
  145. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
  146. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
  147. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
  148. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
  149. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
  150. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
  151. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
  152. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
  153. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
  154. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
  155. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
  156. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
  157. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools.egg-info/requires.txt +0 -0
  158. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/cc_session_tools.egg-info/top_level.txt +0 -0
  159. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/__init__.py +0 -0
  160. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/attribution.py +0 -0
  161. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/cache.py +0 -0
  162. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/ccusage_wrapper.py +0 -0
  163. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/cli.py +0 -0
  164. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/data/pricing.json +0 -0
  165. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/parent_inference.py +0 -0
  166. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/parser.py +0 -0
  167. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/pricing.py +0 -0
  168. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/query.py +0 -0
  169. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/report.py +0 -0
  170. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/schema.py +0 -0
  171. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/claude_code_usage/session_names.py +0 -0
  172. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/__init__.py +0 -0
  173. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/after_response.py +0 -0
  174. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/bash_hard_deny.py +0 -0
  175. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/bash_security_review.py +0 -0
  176. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/cache.py +0 -0
  177. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/catchup.py +0 -0
  178. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/confirm_8digit.py +0 -0
  179. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/context_window_warning.py +0 -0
  180. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/last_screenshot.py +0 -0
  181. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/marker_allow.py +0 -0
  182. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/markers.py +0 -0
  183. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/messaging_deliver.py +0 -0
  184. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/model_info.py +0 -0
  185. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/normalise.py +0 -0
  186. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/pdata_sync.py +0 -0
  187. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/pending_migration.py +0 -0
  188. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/pending_rename.py +0 -0
  189. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/script_allowlist.py +0 -0
  190. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/session_tag.py +0 -0
  191. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/stats.py +0 -0
  192. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/telemetry.py +0 -0
  193. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/telemetry_query.py +0 -0
  194. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/telemetry_trim.py +0 -0
  195. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/transcript.py +0 -0
  196. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/src/hooks/worklog_guard.py +0 -0
  197. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_after_response.py +0 -0
  198. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_api_keys.py +0 -0
  199. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_attribution.py +0 -0
  200. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_bash_hard_deny.py +0 -0
  201. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_bash_security_review.py +0 -0
  202. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_bash_security_review_allowlist.py +0 -0
  203. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_bun_tools.py +0 -0
  204. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cache.py +0 -0
  205. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cache_sqlite.py +0 -0
  206. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccr_orphans.py +0 -0
  207. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccs_emptiness.py +0 -0
  208. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccs_eta.py +0 -0
  209. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccs_list_mode.py +0 -0
  210. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccs_scope_flags.py +0 -0
  211. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccs_sentinel_sort.py +0 -0
  212. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccs_session_counts.py +0 -0
  213. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_bundle_discovery.py +0 -0
  214. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
  215. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_cli.py +0 -0
  216. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_context_override_cli.py +0 -0
  217. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_doctor.py +0 -0
  218. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_gc_prune.py +0 -0
  219. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_gc_report.py +0 -0
  220. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_hook_dispatcher.py +0 -0
  221. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_hooks_allowlist.py +0 -0
  222. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_hooks_install_prune.py +0 -0
  223. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
  224. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_hooks_uninstall.py +0 -0
  225. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_install_everything.py +0 -0
  226. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_machine_identity_cli.py +0 -0
  227. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_main_gate.py +0 -0
  228. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_cli.py +0 -0
  229. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_init_cli.py +0 -0
  230. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_readiness_cli.py +0 -0
  231. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
  232. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
  233. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
  234. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_sync_cli.py +0 -0
  235. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_pdata_verify_cli.py +0 -0
  236. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_repair_sessions_cli.py +0 -0
  237. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_sessions_cli.py +0 -0
  238. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_shell_install.py +0 -0
  239. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_skills_install.py +0 -0
  240. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_skills_uninstall.py +0 -0
  241. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_telemetry_query.py +0 -0
  242. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccst_telemetry_trim.py +0 -0
  243. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_ccusage_wrapper.py +0 -0
  244. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_claude_flags.py +0 -0
  245. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_claude_md_install.py +0 -0
  246. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_clean_hook_sessions_format.py +0 -0
  247. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli.py +0 -0
  248. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli_api_launchers.py +0 -0
  249. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli_ccd.py +0 -0
  250. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli_ccd_dry_run.py +0 -0
  251. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli_ccr.py +0 -0
  252. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli_ccs.py +0 -0
  253. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_cli_version.py +0 -0
  254. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_confirm_8digit.py +0 -0
  255. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_context_overrides.py +0 -0
  256. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_context_window_warning.py +0 -0
  257. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_db.py +0 -0
  258. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_debug.py +0 -0
  259. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_doctor_mutes.py +0 -0
  260. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_empty_session.py +0 -0
  261. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_hook_cache.py +0 -0
  262. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_hooks_install.py +0 -0
  263. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_hooks_install_records.py +0 -0
  264. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_install_sync.py +0 -0
  265. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_last_screenshot.py +0 -0
  266. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_levenshtein.py +0 -0
  267. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_machine_identity.py +0 -0
  268. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_marker_allow.py +0 -0
  269. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_migrate_ccmsg.py +0 -0
  270. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_migrate_ccsched.py +0 -0
  271. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_migrate_sessions_db.py +0 -0
  272. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_migrate_telemetry.py +0 -0
  273. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_model_info.py +0 -0
  274. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_normalise.py +0 -0
  275. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_occupancy.py +0 -0
  276. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_parent_inference.py +0 -0
  277. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_parser.py +0 -0
  278. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_paths.py +0 -0
  279. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_pdata_audit_skill.py +0 -0
  280. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_pdata_post_migration_skills.py +0 -0
  281. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_pending_migration_hook.py +0 -0
  282. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_picker.py +0 -0
  283. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_pricing.py +0 -0
  284. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_proc_lock.py +0 -0
  285. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_prompts.py +0 -0
  286. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_query.py +0 -0
  287. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_report.py +0 -0
  288. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_roots.py +0 -0
  289. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_rules.py +0 -0
  290. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_scheduler_bundled_jobs.py +0 -0
  291. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_schema.py +0 -0
  292. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_script_allowlist.py +0 -0
  293. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_session_names.py +0 -0
  294. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_session_tag.py +0 -0
  295. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_sessions.py +0 -0
  296. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_sessions_db.py +0 -0
  297. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_sessions_db_migration_chain.py +0 -0
  298. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_sessions_repair.py +0 -0
  299. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_tasklist.py +0 -0
  300. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_telemetry.py +0 -0
  301. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_telemetry_store.py +0 -0
  302. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_text_wrap.py +0 -0
  303. {cc_session_tools-3.12.0 → cc_session_tools-3.12.2}/tests/test_transcript.py +0 -0
  304. {cc_session_tools-3.12.0 → cc_session_tools-3.12.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.12.0
3
+ Version: 3.12.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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cc-session-tools"
7
- version = "3.12.0"
7
+ version = "3.12.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" }
@@ -14,7 +14,6 @@ import os
14
14
  import shutil
15
15
  import sqlite3
16
16
  import subprocess
17
- import time
18
17
  from dataclasses import dataclass
19
18
  from enum import Enum
20
19
  from pathlib import Path
@@ -29,6 +28,7 @@ from cc_session_tools.lib.pdata.init_paths import (
29
28
  MIGRATED_MANIFEST_FILENAME,
30
29
  )
31
30
  from cc_session_tools.lib.text_wrap import wrap_block
31
+ from cc_session_tools.lib.timefmt import format_utc, format_utc_epoch
32
32
 
33
33
  if TYPE_CHECKING:
34
34
  from cc_session_tools.lib.install_sync import FailedAttempt
@@ -480,7 +480,7 @@ def check_install_everything_synced(
480
480
  reason=(
481
481
  f"installed {installed_version}, and the automatic install sync already "
482
482
  f"failed for this version (rc {failed_attempt.rc} at "
483
- f"{failed_attempt.at.strftime('%Y-%m-%d %H:%M UTC')}) — "
483
+ f"{format_utc(failed_attempt.at)}) — "
484
484
  "run `ccst install-everything --apply` to see why"
485
485
  ),
486
486
  )
@@ -854,7 +854,7 @@ def check_pending_pdata_migration(projects_root: Path) -> list[CheckResult]:
854
854
 
855
855
 
856
856
  def _fmt_epoch(epoch: int) -> str:
857
- return time.strftime("%Y-%m-%d %H:%M UTC", time.gmtime(epoch))
857
+ return format_utc_epoch(epoch)
858
858
 
859
859
 
860
860
  def check_pdata_verify(projects: list[str]) -> list[CheckResult]:
@@ -24,6 +24,7 @@ from enum import Enum
24
24
  from pathlib import Path
25
25
 
26
26
  from cc_session_tools.lib import sessions_db
27
+ from cc_session_tools.lib.timefmt import format_utc
27
28
 
28
29
  _SYNCED_VERSION_KEY = "synced_version"
29
30
 
@@ -301,7 +302,7 @@ def ensure_synced(*, noun: str | None, verb: str | None, installed_version: str)
301
302
  )
302
303
  print(
303
304
  f"ccst: install config is out of sync (installed {installed_version}, {state}) "
304
- f"and the last auto-sync failed at {last_failure.at.strftime('%Y-%m-%d %H:%M UTC')} — "
305
+ f"and the last auto-sync failed at {format_utc(last_failure.at)} — "
305
306
  "run `ccst install-everything --apply` to see why.",
306
307
  file=sys.stderr,
307
308
  )
@@ -8,6 +8,7 @@ from dataclasses import dataclass, field
8
8
  from datetime import datetime, timezone
9
9
  from typing import Literal
10
10
 
11
+ from cc_session_tools.lib.timefmt import format_utc
11
12
  from cc_session_tools.lib.messaging import cursor as cursor_mod
12
13
  from cc_session_tools.lib.messaging import repository
13
14
  from cc_session_tools.lib.messaging import retention
@@ -136,9 +137,13 @@ def list_messages(
136
137
 
137
138
 
138
139
  def format_sent_at(sent_at: str) -> str:
139
- """Render a stored `sent_at` machine timestamp as `yyyy-MM-dd HH:mm UTC` for a human."""
140
- sent = datetime.strptime(sent_at, "%Y-%m-%dT%H:%M:%SZ")
141
- return sent.strftime("%Y-%m-%d %H:%M UTC")
140
+ """Render a stored `sent_at` machine timestamp as `yyyy-MM-dd HH:mm UTC` for a human; a value
141
+ not in the writer's shape is returned as stored so the message stays readable."""
142
+ try:
143
+ sent = datetime.strptime(sent_at, "%Y-%m-%dT%H:%M:%SZ")
144
+ except ValueError:
145
+ return sent_at
146
+ return format_utc(sent)
142
147
 
143
148
 
144
149
  def relative_age(sent_at: str, now: datetime) -> str:
@@ -2,7 +2,6 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import os
5
- import time
6
5
  from collections.abc import Callable
7
6
  from dataclasses import dataclass
8
7
  from pathlib import Path
@@ -12,6 +11,7 @@ from cc_session_tools.lib.pdata.init_paths import (
12
11
  MIGRATED_MANIFEST_FILENAME,
13
12
  )
14
13
  from cc_session_tools.lib.pdata.manifest import ManifestEntry
14
+ from cc_session_tools.lib.timefmt import format_utc_epoch
15
15
 
16
16
 
17
17
  _POINTER_BODY_PREFIX = "This file's data now lives in pdata record group"
@@ -190,7 +190,7 @@ def archive_entries(
190
190
  archive_root = project_root / MIGRATED_ARCHIVE_DIRNAME
191
191
  archive_root.mkdir(parents=True, exist_ok=True)
192
192
  manifest_path = archive_root / MIGRATED_MANIFEST_FILENAME
193
- now = time.strftime("%Y-%m-%d %H:%M UTC", time.gmtime())
193
+ now = format_utc_epoch()
194
194
  outcomes: list[PointerPlan] = []
195
195
  with manifest_path.open("a", encoding="utf-8") as log:
196
196
  for index, entry in enumerate(entries):
@@ -89,10 +89,26 @@ def _is_real_yyyymmdd(value: str) -> bool:
89
89
  return 1900 <= parsed.year <= 2100
90
90
 
91
91
 
92
+ _COMPACT_DATETIME = re.compile(r"(\d{8})T(\d{2})(\d{2})(\d{2})?Z?")
93
+
94
+
95
+ def _is_compact_datetime(value: str) -> bool:
96
+ match = _COMPACT_DATETIME.fullmatch(value)
97
+ if match is None:
98
+ return False
99
+ date_part, hour, minute, second = match.groups()
100
+ return (
101
+ _is_real_yyyymmdd(date_part)
102
+ and int(hour) <= 23 and int(minute) <= 59 and int(second or 0) <= 59
103
+ )
104
+
105
+
92
106
  def _date_format(value: str) -> str | None:
93
107
  for name, pattern in _DATE_FORMATS:
94
108
  if pattern.match(value):
95
109
  return name
110
+ if _is_compact_datetime(value):
111
+ return "compact-datetime"
96
112
  return "yyyymmdd" if _is_real_yyyymmdd(value) else None
97
113
 
98
114
 
@@ -499,11 +499,13 @@ def date_field_warnings(
499
499
  A field is date-like when its name ends `_at` or `_date`, or its description contains the
500
500
  word "date"; a name ending `_text` (verbatim text) never is. Numeric fields are skipped
501
501
  (an integer `_at` field holds an epoch, not a date string), as are empty and null values.
502
- Advisory only: this reads the schema and writes nothing."""
503
- columns = {
504
- str(c["name"]): c for c in schema_show(project=project, record_group=record_group)
505
- if c["source"] == "extension"
506
- }
502
+ Advisory only: this reads the schema and writes nothing, and returns no warnings if the schema
503
+ cannot be read, so it can never change the outcome of the write it follows."""
504
+ try:
505
+ schema = schema_show(project=project, record_group=record_group)
506
+ except (sqlite3.Error, ValueError):
507
+ return [] # advisory check; the write it follows has already committed
508
+ columns = {str(c["name"]): c for c in schema if c["source"] == "extension"}
507
509
  warnings: list[str] = []
508
510
  for name, value in fields.items():
509
511
  column = columns.get(name)
@@ -0,0 +1,17 @@
1
+ """The one definition of how CCST renders a UTC time for a human (`yyyy-MM-dd HH:mm UTC`)."""
2
+ from __future__ import annotations
3
+
4
+ import time
5
+ from datetime import datetime
6
+
7
+ HUMAN_UTC_FORMAT = "%Y-%m-%d %H:%M UTC"
8
+
9
+
10
+ def format_utc(moment: datetime) -> str:
11
+ """Render a datetime that is already in UTC."""
12
+ return moment.strftime(HUMAN_UTC_FORMAT)
13
+
14
+
15
+ def format_utc_epoch(epoch: float | None = None) -> str:
16
+ """Render an epoch (seconds) in UTC; the current time when omitted."""
17
+ return time.strftime(HUMAN_UTC_FORMAT, time.gmtime(epoch))
@@ -57,8 +57,8 @@ ccst pdata readiness-scan --project <name> --path <domain-dir>/ --findings-only
57
57
  ```
58
58
 
59
59
  It is read-only and deterministic. It reports, per CSV, `ragged-rows`, `bad-header`,
60
- `mixed-date-formats`, `non-iso-dates` (any dotted `yyyy.MM.dd`, compact `yyyyMMdd`, slash or
61
- long-form value, even when the whole column uses it; advisory, since an 8-digit identifier that
60
+ `mixed-date-formats`, `non-iso-dates` (any dotted `yyyy.MM.dd`, compact `yyyyMMdd` or `yyyyMMddTHHmm`, slash
61
+ or long-form value, even when the whole column uses it; advisory, since an 8-digit identifier that
62
62
  is a real date is flagged too), `mixed-separators` (`|` with `;` only), `machine-paths`, `null-strings`,
63
63
  `duplicate-rows`, `comment-rows`, `repeated-header`, `no-unique-column`, `bom`, `crlf` and
64
64
  `unreadable`, plus row counts and unique columns. Treat findings as leads to verify, not facts to
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-session-tools
3
- Version: 3.12.0
3
+ Version: 3.12.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
@@ -52,6 +52,7 @@ src/cc_session_tools/lib/shell_install.py
52
52
  src/cc_session_tools/lib/tasklist.py
53
53
  src/cc_session_tools/lib/telemetry_store.py
54
54
  src/cc_session_tools/lib/text_wrap.py
55
+ src/cc_session_tools/lib/timefmt.py
55
56
  src/cc_session_tools/lib/messaging/__init__.py
56
57
  src/cc_session_tools/lib/messaging/addressing.py
57
58
  src/cc_session_tools/lib/messaging/cursor.py
@@ -249,6 +250,7 @@ tests/test_cli_version.py
249
250
  tests/test_confirm_8digit.py
250
251
  tests/test_context_overrides.py
251
252
  tests/test_context_window_warning.py
253
+ tests/test_date_format_conformance.py
252
254
  tests/test_db.py
253
255
  tests/test_debug.py
254
256
  tests/test_doctor_mutes.py
@@ -295,5 +297,6 @@ tests/test_tasklist.py
295
297
  tests/test_telemetry.py
296
298
  tests/test_telemetry_store.py
297
299
  tests/test_text_wrap.py
300
+ tests/test_timefmt.py
298
301
  tests/test_transcript.py
299
302
  tests/test_worklog_guard.py
@@ -0,0 +1,140 @@
1
+ """Every date format string under src/ is a convention form or a listed exemption.
2
+
3
+ The convention: filenames and folder names use `yyyy.MM.dd`; everything else is ISO `yyyy-MM-dd`
4
+ (`yyyy-MM-dd HH:mm` for a time of day, ISO 8601 with `T` and `Z` for machine timestamps).
5
+ A new format string must be added to ALLOWED below with one of the REASONS, or the test fails; an
6
+ entry that no longer matches a format string in src/ must be deleted.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import ast
11
+ import re
12
+ from pathlib import Path
13
+
14
+ SRC = Path(__file__).parent.parent / "src"
15
+
16
+ REASONS = {
17
+ "machine-iso": "stored, compared or exchanged timestamp: ISO 8601 with T and Z",
18
+ "identifier": "session tag or other identifier that contains a date",
19
+ "backup-stamp": "backup, snapshot or migration stamp in a file name",
20
+ "human-iso": "printed for a human in the ISO text form",
21
+ "folder-name": "year or month folder name",
22
+ "time-only": "time of day with no date",
23
+ "input-parse": "accepts a user-supplied value, writes nothing",
24
+ "detection-parse": "recognises a value in data, writes nothing",
25
+ }
26
+
27
+ _MACHINE = "%Y-%m-%dT%H:%M:%SZ"
28
+ _HUMAN = "%Y-%m-%d %H:%M"
29
+ _BACKUP_UTC = "%Y%m%dT%H%M%SZ"
30
+
31
+ ALLOWED: dict[tuple[str, str], str] = {
32
+ ("cc_session_tools/cli/ccd.py", "%Y%m%d"): "identifier",
33
+ ("cc_session_tools/cli/ccr.py", _HUMAN): "human-iso",
34
+ ("cc_session_tools/cli/ccs.py", "%Y%m%d"): "identifier",
35
+ ("cc_session_tools/cli/ccs.py", "%Y-%m-%d"): "input-parse",
36
+ ("cc_session_tools/cli/ccs.py", _HUMAN): "human-iso",
37
+ ("cc_session_tools/cli/ccs.py", "%Y-%m-%dT%H:%M"): "input-parse",
38
+ ("cc_session_tools/cli/ccs.py", "%Y-%m-%dT%H:%M:%S"): "input-parse",
39
+ ("cc_session_tools/cli/ccsched.py", _MACHINE): "machine-iso",
40
+ ("cc_session_tools/cli/ccst.py", "%Y%m%d%H%M%S"): "backup-stamp",
41
+ ("cc_session_tools/cli/ccst.py", _BACKUP_UTC): "backup-stamp",
42
+ ("cc_session_tools/cli/ccst.py", _HUMAN): "human-iso",
43
+ ("cc_session_tools/cli/migrate_ccmsg.py", _BACKUP_UTC): "backup-stamp",
44
+ ("cc_session_tools/cli/migrate_ccsched.py", _BACKUP_UTC): "backup-stamp",
45
+ ("cc_session_tools/cli/migrate_ccsched.py", _MACHINE): "machine-iso",
46
+ ("cc_session_tools/cli/migrate_sessions_db.py", _BACKUP_UTC): "backup-stamp",
47
+ ("cc_session_tools/cli/migrate_telemetry.py", _BACKUP_UTC): "backup-stamp",
48
+ ("cc_session_tools/lib/install_sync.py", _MACHINE): "machine-iso",
49
+ ("cc_session_tools/lib/messaging/repository.py", _MACHINE): "machine-iso",
50
+ ("cc_session_tools/lib/messaging/retention.py", _MACHINE): "machine-iso",
51
+ ("cc_session_tools/lib/messaging/service.py", _MACHINE): "machine-iso",
52
+ ("cc_session_tools/lib/messaging/store.py", _BACKUP_UTC): "backup-stamp",
53
+ ("cc_session_tools/lib/pdata/backup.py", "%Y%m%d-%H%M%S"): "backup-stamp",
54
+ ("cc_session_tools/lib/pdata/dump.py", _HUMAN): "human-iso",
55
+ ("cc_session_tools/lib/pdata/init_service.py", _MACHINE): "machine-iso",
56
+ ("cc_session_tools/lib/pdata/readiness.py", "%Y%m%d"): "detection-parse",
57
+ ("cc_session_tools/lib/pdata/reorganize.py", "%Y"): "folder-name",
58
+ ("cc_session_tools/lib/pdata/reorganize.py", "%m"): "folder-name",
59
+ ("cc_session_tools/lib/proc_lock.py", _MACHINE): "machine-iso",
60
+ ("cc_session_tools/lib/scheduler/cursor.py", _MACHINE): "machine-iso",
61
+ ("cc_session_tools/lib/scheduler/registry.py", _MACHINE): "machine-iso",
62
+ ("cc_session_tools/lib/scheduler/state.py", _MACHINE): "machine-iso",
63
+ ("cc_session_tools/lib/scheduler/surface.py", _MACHINE): "machine-iso",
64
+ ("cc_session_tools/lib/sessions_db.py", _MACHINE): "machine-iso",
65
+ ("cc_session_tools/lib/telemetry_store.py", _MACHINE): "machine-iso",
66
+ ("cc_session_tools/lib/timefmt.py", "%Y-%m-%d %H:%M UTC"): "human-iso",
67
+ ("cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py",
68
+ "%Y%m%d-%H%M%S"): "backup-stamp",
69
+ ("cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py",
70
+ _HUMAN): "human-iso",
71
+ ("cc_session_tools/skills/move-session/scripts/move_session.py",
72
+ "%Y%m%d-%H%M%S"): "backup-stamp",
73
+ ("cc_session_tools/skills/move-session/scripts/move_session.py",
74
+ "%Y-%m-%dT%H:%M:%S.%f"): "machine-iso",
75
+ ("claude_code_usage/query.py", "%Y"): "human-iso",
76
+ ("claude_code_usage/query.py", "%Y-%m"): "human-iso",
77
+ ("claude_code_usage/query.py", "%Y-%m-%d"): "human-iso",
78
+ ("hooks/bash_security_review.py", "%Y%m%d-%H%M"): "identifier",
79
+ ("hooks/cache.py", _MACHINE): "machine-iso",
80
+ ("hooks/cache.py", f"strftime('{_MACHINE}', datetime('now', ?))"): "machine-iso",
81
+ ("hooks/context_window_warning.py", "%H:%M"): "time-only",
82
+ ("hooks/session_tag.py", "%Y%m%d"): "identifier",
83
+ ("hooks/telemetry_query.py", _MACHINE): "machine-iso",
84
+ ("hooks/telemetry_trim.py", _MACHINE): "machine-iso",
85
+ }
86
+
87
+ # A year, month, hour, minute, second or fraction directive. Bare %d/%s are logging placeholders.
88
+ _DIRECTIVE = re.compile(r"%[YymHMSfBb]")
89
+ _MAX_FORMAT_LENGTH = 80
90
+
91
+
92
+ def _docstring_ids(tree: ast.AST) -> set[int]:
93
+ ids: set[int] = set()
94
+ for node in ast.walk(tree):
95
+ if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
96
+ first = node.body[0] if node.body else None
97
+ if (
98
+ isinstance(first, ast.Expr)
99
+ and isinstance(first.value, ast.Constant)
100
+ and isinstance(first.value.value, str)
101
+ ):
102
+ ids.add(id(first.value))
103
+ return ids
104
+
105
+
106
+ def _format_strings() -> set[tuple[str, str]]:
107
+ found: set[tuple[str, str]] = set()
108
+ for path in sorted(SRC.rglob("*.py")):
109
+ if "tests" in path.relative_to(SRC).parts:
110
+ continue
111
+ tree = ast.parse(path.read_text(encoding="utf-8"))
112
+ docstrings = _docstring_ids(tree)
113
+ for node in ast.walk(tree):
114
+ if (
115
+ isinstance(node, ast.Constant)
116
+ and isinstance(node.value, str)
117
+ and id(node) not in docstrings
118
+ and len(node.value) < _MAX_FORMAT_LENGTH
119
+ and _DIRECTIVE.search(node.value)
120
+ ):
121
+ found.add((path.relative_to(SRC).as_posix(), node.value))
122
+ return found
123
+
124
+
125
+ def test_every_allowlist_entry_has_a_known_reason() -> None:
126
+ assert set(ALLOWED.values()) <= set(REASONS)
127
+
128
+
129
+ def test_no_date_format_string_outside_the_allowlist() -> None:
130
+ unlisted = sorted(_format_strings() - set(ALLOWED))
131
+ assert not unlisted, (
132
+ "date format string(s) not in the convention: use yyyy-MM-dd / yyyy-MM-dd HH:mm for "
133
+ "output and ISO 8601 with T and Z for stored timestamps, or add an ALLOWED entry with a "
134
+ f"reason from REASONS if it is exempt: {unlisted}"
135
+ )
136
+
137
+
138
+ def test_no_stale_allowlist_entry() -> None:
139
+ stale = sorted(set(ALLOWED) - _format_strings())
140
+ assert not stale, f"ALLOWED entries that match no format string in src/: {stale}"
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timezone
4
+
5
+ from cc_session_tools.lib.timefmt import HUMAN_UTC_FORMAT, format_utc, format_utc_epoch
6
+
7
+
8
+ def test_format_utc_renders_text_form() -> None:
9
+ assert format_utc(datetime(2026, 10, 5, 14, 30, 59, tzinfo=timezone.utc)) == "2026-10-05 14:30 UTC"
10
+
11
+
12
+ def test_format_utc_epoch_renders_text_form() -> None:
13
+ assert format_utc_epoch(1_790_000_000) == "2026-09-21 14:13 UTC"
14
+
15
+
16
+ def test_format_utc_epoch_defaults_to_now() -> None:
17
+ import re
18
+
19
+ assert re.fullmatch(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2} UTC", format_utc_epoch())
20
+
21
+
22
+ def test_format_constant_is_the_documented_form() -> None:
23
+ assert HUMAN_UTC_FORMAT == "%Y-%m-%d %H:%M UTC"