cc-session-tools 3.7.1__tar.gz → 3.8.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 (295) hide show
  1. {cc_session_tools-3.7.1/src/cc_session_tools.egg-info → cc_session_tools-3.8.0}/PKG-INFO +20 -1
  2. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/README.md +19 -0
  3. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/pyproject.toml +1 -1
  4. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/ccst.py +42 -0
  5. cc_session_tools-3.8.0/src/cc_session_tools/lib/pdata/readiness.py +319 -0
  6. cc_session_tools-3.8.0/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +162 -0
  7. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0/src/cc_session_tools.egg-info}/PKG-INFO +20 -1
  8. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools.egg-info/SOURCES.txt +3 -0
  9. cc_session_tools-3.8.0/tests/test_ccst_pdata_readiness_cli.py +95 -0
  10. cc_session_tools-3.8.0/tests/test_pdata_audit_skill.py +89 -0
  11. cc_session_tools-3.7.1/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +0 -199
  12. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/LICENSE +0 -0
  13. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/setup.cfg +0 -0
  14. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/__init__.py +0 -0
  15. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/__init__.py +0 -0
  16. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/ccd.py +0 -0
  17. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/ccmsg.py +0 -0
  18. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/ccr.py +0 -0
  19. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/ccs.py +0 -0
  20. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/ccsched.py +0 -0
  21. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
  22. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
  23. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
  24. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
  25. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/config/hooks-bundle.json +0 -0
  26. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/config/session-tag-hook.json +0 -0
  27. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/hooks_install.py +0 -0
  28. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/__init__.py +0 -0
  29. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/bun_tools.py +0 -0
  30. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/claude_flags.py +0 -0
  31. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/claude_md_install.py +0 -0
  32. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/context_overrides.py +0 -0
  33. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/db.py +0 -0
  34. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/debug.py +0 -0
  35. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/doctor.py +0 -0
  36. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
  37. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/hook_registry.py +0 -0
  38. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/install_sync.py +0 -0
  39. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/levenshtein.py +0 -0
  40. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/machine_identity.py +0 -0
  41. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
  42. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
  43. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
  44. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/lock.py +0 -0
  45. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/message.py +0 -0
  46. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
  47. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/repository.py +0 -0
  48. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/retention.py +0 -0
  49. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/service.py +0 -0
  50. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/messaging/store.py +0 -0
  51. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/occupancy.py +0 -0
  52. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/paths.py +0 -0
  53. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
  54. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/backup.py +0 -0
  55. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/classify.py +0 -0
  56. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/cutover.py +0 -0
  57. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/dump.py +0 -0
  58. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
  59. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/importers.py +0 -0
  60. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
  61. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/init_service.py +0 -0
  62. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
  63. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/naming.py +0 -0
  64. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
  65. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
  66. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
  67. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/repository.py +0 -0
  68. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
  69. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/service.py +0 -0
  70. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
  71. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/store.py +0 -0
  72. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
  73. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
  74. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
  75. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
  76. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
  77. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/verify.py +0 -0
  78. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
  79. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/picker.py +0 -0
  80. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/proc_lock.py +0 -0
  81. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/prompts.py +0 -0
  82. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/roots.py +0 -0
  83. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/rules.py +0 -0
  84. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
  85. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
  86. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
  87. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
  88. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
  89. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/due.py +0 -0
  90. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
  91. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
  92. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
  93. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
  94. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
  95. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
  96. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
  97. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
  98. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/state.py +0 -0
  99. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/store.py +0 -0
  100. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
  101. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
  102. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
  103. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/session_gc.py +0 -0
  104. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/sessions.py +0 -0
  105. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/sessions_db.py +0 -0
  106. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/sessions_repair.py +0 -0
  107. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/shell_install.py +0 -0
  108. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/tasklist.py +0 -0
  109. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/telemetry_store.py +0 -0
  110. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/lib/text_wrap.py +0 -0
  111. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +0 -0
  112. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/prompts/pdata-migration-skills-update.md +0 -0
  113. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
  114. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
  115. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
  116. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
  117. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
  118. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
  119. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
  120. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
  121. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/extract-pptx/SKILL.md +0 -0
  122. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/extract-pptx/scripts/extract_pptx.py +0 -0
  123. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/extract-to-text/SKILL.md +0 -0
  124. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/extract-to-text/scripts/extract_to_text.py +0 -0
  125. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
  126. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
  127. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
  128. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
  129. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
  130. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
  131. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
  132. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
  133. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
  134. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
  135. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/pm-pdata-design-schema/SKILL.md +0 -0
  136. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/pm-pdata-do-init/SKILL.md +0 -0
  137. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/pm-pdata-do-manual-migration/SKILL.md +0 -0
  138. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/pm-pdata-resolve-conflicts/SKILL.md +0 -0
  139. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +0 -0
  140. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
  141. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
  142. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
  143. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
  144. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
  145. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
  146. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
  147. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
  148. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
  149. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
  150. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
  151. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
  152. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
  153. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
  154. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
  155. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools.egg-info/requires.txt +0 -0
  156. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/cc_session_tools.egg-info/top_level.txt +0 -0
  157. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/__init__.py +0 -0
  158. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/attribution.py +0 -0
  159. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/cache.py +0 -0
  160. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/ccusage_wrapper.py +0 -0
  161. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/cli.py +0 -0
  162. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/data/pricing.json +0 -0
  163. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/parent_inference.py +0 -0
  164. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/parser.py +0 -0
  165. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/pricing.py +0 -0
  166. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/query.py +0 -0
  167. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/report.py +0 -0
  168. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/schema.py +0 -0
  169. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/claude_code_usage/session_names.py +0 -0
  170. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/__init__.py +0 -0
  171. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/after_response.py +0 -0
  172. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/bash_hard_deny.py +0 -0
  173. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/bash_security_review.py +0 -0
  174. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/cache.py +0 -0
  175. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/catchup.py +0 -0
  176. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/confirm_8digit.py +0 -0
  177. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/context_window_warning.py +0 -0
  178. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/last_screenshot.py +0 -0
  179. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/marker_allow.py +0 -0
  180. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/markers.py +0 -0
  181. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/messaging_deliver.py +0 -0
  182. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/model_info.py +0 -0
  183. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/normalise.py +0 -0
  184. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/pdata_sync.py +0 -0
  185. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/pending_migration.py +0 -0
  186. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/pending_rename.py +0 -0
  187. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/script_allowlist.py +0 -0
  188. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/session_tag.py +0 -0
  189. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/stats.py +0 -0
  190. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/telemetry.py +0 -0
  191. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/telemetry_query.py +0 -0
  192. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/telemetry_trim.py +0 -0
  193. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/transcript.py +0 -0
  194. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/src/hooks/worklog_guard.py +0 -0
  195. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_after_response.py +0 -0
  196. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_attribution.py +0 -0
  197. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_bash_hard_deny.py +0 -0
  198. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_bash_security_review.py +0 -0
  199. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_bash_security_review_allowlist.py +0 -0
  200. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_bun_tools.py +0 -0
  201. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cache.py +0 -0
  202. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cache_sqlite.py +0 -0
  203. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccr_orphans.py +0 -0
  204. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccs_emptiness.py +0 -0
  205. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccs_eta.py +0 -0
  206. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccs_list_mode.py +0 -0
  207. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccs_scope_flags.py +0 -0
  208. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccs_sentinel_sort.py +0 -0
  209. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccs_session_counts.py +0 -0
  210. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_bundle_discovery.py +0 -0
  211. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
  212. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_cli.py +0 -0
  213. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_context_override_cli.py +0 -0
  214. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_doctor.py +0 -0
  215. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_gc_prune.py +0 -0
  216. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_gc_report.py +0 -0
  217. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_hook_dispatcher.py +0 -0
  218. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_hooks_allowlist.py +0 -0
  219. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_hooks_install_prune.py +0 -0
  220. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
  221. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_hooks_uninstall.py +0 -0
  222. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_install_everything.py +0 -0
  223. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_machine_identity_cli.py +0 -0
  224. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_main_gate.py +0 -0
  225. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_cli.py +0 -0
  226. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_init_cli.py +0 -0
  227. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
  228. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
  229. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
  230. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_sync_cli.py +0 -0
  231. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_pdata_verify_cli.py +0 -0
  232. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_repair_sessions_cli.py +0 -0
  233. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_sessions_cli.py +0 -0
  234. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_shell_install.py +0 -0
  235. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_skills_install.py +0 -0
  236. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_skills_uninstall.py +0 -0
  237. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_telemetry_query.py +0 -0
  238. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccst_telemetry_trim.py +0 -0
  239. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_ccusage_wrapper.py +0 -0
  240. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_claude_flags.py +0 -0
  241. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_claude_md_install.py +0 -0
  242. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cli.py +0 -0
  243. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cli_ccd.py +0 -0
  244. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cli_ccd_dry_run.py +0 -0
  245. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cli_ccr.py +0 -0
  246. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cli_ccs.py +0 -0
  247. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_cli_version.py +0 -0
  248. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_confirm_8digit.py +0 -0
  249. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_context_overrides.py +0 -0
  250. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_context_window_warning.py +0 -0
  251. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_db.py +0 -0
  252. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_debug.py +0 -0
  253. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_doctor_mutes.py +0 -0
  254. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_empty_session.py +0 -0
  255. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_hook_cache.py +0 -0
  256. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_hooks_install.py +0 -0
  257. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_hooks_install_records.py +0 -0
  258. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_install_sync.py +0 -0
  259. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_last_screenshot.py +0 -0
  260. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_levenshtein.py +0 -0
  261. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_machine_identity.py +0 -0
  262. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_marker_allow.py +0 -0
  263. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_migrate_ccmsg.py +0 -0
  264. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_migrate_ccsched.py +0 -0
  265. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_migrate_sessions_db.py +0 -0
  266. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_migrate_telemetry.py +0 -0
  267. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_model_info.py +0 -0
  268. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_normalise.py +0 -0
  269. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_occupancy.py +0 -0
  270. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_parent_inference.py +0 -0
  271. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_parser.py +0 -0
  272. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_paths.py +0 -0
  273. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_pending_migration_hook.py +0 -0
  274. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_picker.py +0 -0
  275. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_pricing.py +0 -0
  276. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_proc_lock.py +0 -0
  277. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_prompts.py +0 -0
  278. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_query.py +0 -0
  279. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_report.py +0 -0
  280. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_roots.py +0 -0
  281. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_rules.py +0 -0
  282. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_scheduler_bundled_jobs.py +0 -0
  283. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_schema.py +0 -0
  284. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_script_allowlist.py +0 -0
  285. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_session_names.py +0 -0
  286. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_session_tag.py +0 -0
  287. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_sessions.py +0 -0
  288. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_sessions_db.py +0 -0
  289. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_sessions_repair.py +0 -0
  290. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_tasklist.py +0 -0
  291. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_telemetry.py +0 -0
  292. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_telemetry_store.py +0 -0
  293. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_text_wrap.py +0 -0
  294. {cc_session_tools-3.7.1 → cc_session_tools-3.8.0}/tests/test_transcript.py +0 -0
  295. {cc_session_tools-3.7.1 → cc_session_tools-3.8.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.7.1
3
+ Version: 3.8.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
@@ -928,6 +928,25 @@ ccst context-override off # re-enable the warnings
928
928
  ccst context-override status # report the current state
929
929
  ```
930
930
 
931
+ ### `ccst pdata readiness-scan`
932
+
933
+ A read-only scan of a project's CSV files for the mechanical blockers to a `ccst pdata init`
934
+ migration, used by the `pm-pdata-do-audit-and-prepare-to-migrate` skill and useful on its own:
935
+
936
+ ```sh
937
+ ccst pdata readiness-scan --project myproject # compact Markdown report
938
+ ccst pdata readiness-scan --project myproject --format json # machine-readable
939
+ ccst pdata readiness-scan --project myproject --path notes/ --findings-only # one folder's findings
940
+ ```
941
+
942
+ It reports ragged rows, empty/duplicate headers, mixed date formats, mixed `|`/`;` separators,
943
+ machine-specific paths, null-like placeholder strings, duplicate rows, comment rows, repeated
944
+ header rows, byte-order marks, CRLF line endings and unreadable files, plus a per-file inventory
945
+ and the columns that are unique on every row. Findings carry counts and row numbers, never cell
946
+ text, and a unique column is a fact rather than a recommended key. It exits 0 whenever the scan
947
+ ran (findings are not failures), and 2 when the project directory does not exist. It scans exactly
948
+ the CSVs `ccst pdata init` would classify.
949
+
931
950
  ### `ccst skills install`
932
951
 
933
952
  > You normally don't need this: `ccst install-everything --apply` runs it, and `ccst` runs that
@@ -871,6 +871,25 @@ ccst context-override off # re-enable the warnings
871
871
  ccst context-override status # report the current state
872
872
  ```
873
873
 
874
+ ### `ccst pdata readiness-scan`
875
+
876
+ A read-only scan of a project's CSV files for the mechanical blockers to a `ccst pdata init`
877
+ migration, used by the `pm-pdata-do-audit-and-prepare-to-migrate` skill and useful on its own:
878
+
879
+ ```sh
880
+ ccst pdata readiness-scan --project myproject # compact Markdown report
881
+ ccst pdata readiness-scan --project myproject --format json # machine-readable
882
+ ccst pdata readiness-scan --project myproject --path notes/ --findings-only # one folder's findings
883
+ ```
884
+
885
+ It reports ragged rows, empty/duplicate headers, mixed date formats, mixed `|`/`;` separators,
886
+ machine-specific paths, null-like placeholder strings, duplicate rows, comment rows, repeated
887
+ header rows, byte-order marks, CRLF line endings and unreadable files, plus a per-file inventory
888
+ and the columns that are unique on every row. Findings carry counts and row numbers, never cell
889
+ text, and a unique column is a fact rather than a recommended key. It exits 0 whenever the scan
890
+ ran (findings are not failures), and 2 when the project directory does not exist. It scans exactly
891
+ the CSVs `ccst pdata init` would classify.
892
+
874
893
  ### `ccst skills install`
875
894
 
876
895
  > You normally don't need this: `ccst install-everything --apply` runs it, and `ccst` runs that
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cc-session-tools"
7
- version = "3.7.1"
7
+ version = "3.8.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" }
@@ -1858,6 +1858,28 @@ def _cmd_pdata_resolve(args: argparse.Namespace) -> int:
1858
1858
  return 1 if outstanding else 0
1859
1859
 
1860
1860
 
1861
+ def _cmd_pdata_readiness_scan(args: argparse.Namespace) -> int:
1862
+ from cc_session_tools.lib.pdata import readiness, store
1863
+
1864
+ try:
1865
+ root = store.project_root(args.project)
1866
+ except ValueError as exc:
1867
+ print(f"ccst pdata readiness-scan: {exc}", file=sys.stderr)
1868
+ return 2
1869
+ if not root.is_dir():
1870
+ print(
1871
+ f"ccst pdata readiness-scan: project directory does not exist: {root}",
1872
+ file=sys.stderr,
1873
+ )
1874
+ return 2
1875
+ report = readiness.scan_project(root, path_prefix=args.path)
1876
+ if args.format == "json":
1877
+ print(readiness.format_json(report))
1878
+ else:
1879
+ print(readiness.format_markdown(report, findings_only=args.findings_only))
1880
+ return 0
1881
+
1882
+
1861
1883
  def _cmd_pdata_sync_check(args: argparse.Namespace) -> int:
1862
1884
  """The spec's "Hourly `ccsched` job" trigger: per project, rehydrate-check and - only if no
1863
1885
  rehydrate happened - dump-check. The whole decision lives in `pdata.sync_check` (shared with
@@ -3256,6 +3278,24 @@ def _build_parser() -> argparse.ArgumentParser:
3256
3278
  "the default one-line summary (--project always prints full detail)",
3257
3279
  )
3258
3280
 
3281
+ pdata_readiness_parser = pdata_sub.add_parser(
3282
+ "readiness-scan",
3283
+ help="Scan a project's CSVs for pdata-migration blockers (read-only)",
3284
+ )
3285
+ pdata_readiness_parser.add_argument("--project", metavar="NAME", required=True)
3286
+ pdata_readiness_parser.add_argument(
3287
+ "--format", choices=("markdown", "json"), default="markdown",
3288
+ help="Output format (default: markdown)",
3289
+ )
3290
+ pdata_readiness_parser.add_argument(
3291
+ "--path", metavar="PREFIX", default=None,
3292
+ help="Only scan CSVs whose project-relative path starts with PREFIX",
3293
+ )
3294
+ pdata_readiness_parser.add_argument(
3295
+ "--findings-only", action="store_true",
3296
+ help="Omit the per-file inventory from the Markdown report",
3297
+ )
3298
+
3259
3299
  pdata_dump_parser = pdata_sub.add_parser(
3260
3300
  "dump", help="Publish the current local DB state to .pdata-db-dump/latest.sql"
3261
3301
  )
@@ -3659,6 +3699,8 @@ def main() -> None:
3659
3699
  sys.exit(_cmd_pdata_reconcile_session_output(args))
3660
3700
  if args.verb == "verify":
3661
3701
  sys.exit(_cmd_pdata_verify(args))
3702
+ if args.verb == "readiness-scan":
3703
+ sys.exit(_cmd_pdata_readiness_scan(args))
3662
3704
  if args.verb == "dump":
3663
3705
  sys.exit(_cmd_pdata_dump(args))
3664
3706
  if args.verb == "rehydrate":
@@ -0,0 +1,319 @@
1
+ """Deterministic pdata-readiness scan of a project's CSV files (`ccst pdata readiness-scan`).
2
+
3
+ Reports the mechanical blockers a migration audit would otherwise find by reading: ragged rows,
4
+ mixed date formats, mixed separators, machine-specific paths, fake nulls, duplicates, BOM/CRLF and
5
+ unreadable files, plus a per-file inventory and unique-column facts. Read-only: the scan never
6
+ writes. Findings carry counts and 1-based data-record numbers (header excluded), never cell text;
7
+ `unreadable` reasons are a fixed vocabulary, so file content cannot reach a report.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import csv
12
+ import io
13
+ import json
14
+ import os
15
+ import re
16
+ from dataclasses import dataclass
17
+ from datetime import datetime
18
+ from pathlib import Path
19
+
20
+ from cc_session_tools.lib.pdata.init_paths import EXCLUDED_DIR_NAMES
21
+
22
+ FINDING_KINDS: tuple[str, ...] = (
23
+ "ragged-rows", "machine-paths", "null-strings", "duplicate-rows", "comment-rows",
24
+ "repeated-header", "mixed-date-formats", "mixed-separators", "bad-header",
25
+ "no-unique-column", "unreadable", "bom", "crlf",
26
+ )
27
+
28
+ MAX_EXAMPLE_ROWS = 3
29
+ FREE_TEXT_MEAN_LENGTH = 64
30
+ NULL_STRINGS = frozenset({"NO DATA", "N/A", "NA", "NULL", "NONE", "TBD", "UNKNOWN", "-", "--", "?"})
31
+ _MACHINE_PATH = re.compile(r"/Users/[^/\s]+|/home/[^/\s]+|/mnt/[a-z]/|[A-Za-z]:\\Users\\")
32
+ _DATE_FORMATS: tuple[tuple[str, re.Pattern[str]], ...] = (
33
+ ("iso-datetime", re.compile(r"^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}")),
34
+ ("iso-date", re.compile(r"^\d{4}-\d{2}-\d{2}$")),
35
+ ("slash", re.compile(r"^\d{1,2}/\d{1,2}/\d{2,4}$")),
36
+ ("d-month-yyyy", re.compile(r"^\d{1,2} [A-Za-z]{3,9} \d{4}$")),
37
+ ("month-d-yyyy", re.compile(r"^[A-Za-z]{3,9} \d{1,2},? \d{4}$")),
38
+ )
39
+ _BOM = b"\xef\xbb\xbf"
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class Finding:
44
+ kind: str
45
+ file: str
46
+ column: str | None = None
47
+ count: int | None = None
48
+ rows: tuple[int, ...] = ()
49
+ detail: tuple[tuple[str, int | str], ...] = ()
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class UniqueColumn:
54
+ name: str
55
+ labels: tuple[str, ...]
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class FileReport:
60
+ path: str
61
+ rows: int
62
+ columns: int
63
+ header: tuple[str, ...]
64
+ unique_columns: tuple[UniqueColumn, ...]
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ class ReadinessReport:
69
+ files: tuple[FileReport, ...]
70
+ findings: tuple[Finding, ...]
71
+ not_scanned: dict[str, int]
72
+
73
+
74
+ class _Unreadable(Exception):
75
+ def __init__(self, reason: str) -> None:
76
+ super().__init__(reason)
77
+ self.reason = reason
78
+
79
+
80
+ def _is_real_yyyymmdd(value: str) -> bool:
81
+ if not re.fullmatch(r"\d{8}", value):
82
+ return False
83
+ try:
84
+ parsed = datetime.strptime(value, "%Y%m%d")
85
+ except ValueError:
86
+ return False
87
+ return 1900 <= parsed.year <= 2100
88
+
89
+
90
+ def _date_format(value: str) -> str | None:
91
+ for name, pattern in _DATE_FORMATS:
92
+ if pattern.match(value):
93
+ return name
94
+ return "yyyymmdd" if _is_real_yyyymmdd(value) else None
95
+
96
+
97
+ def _walk(root: Path) -> list[Path]:
98
+ """Every file under root outside EXCLUDED_DIR_NAMES, without following directory symlinks -
99
+ the same exclusion set `ccst pdata init`'s classifier uses, so coverage matches migration."""
100
+ found: list[Path] = []
101
+ for dirpath, dirnames, filenames in os.walk(root, followlinks=False):
102
+ dirnames[:] = sorted(d for d in dirnames if d not in EXCLUDED_DIR_NAMES)
103
+ found.extend(Path(dirpath) / name for name in sorted(filenames))
104
+ return found
105
+
106
+
107
+ def _parse(raw: bytes) -> tuple[list[list[str]], bool, bool]:
108
+ if not raw:
109
+ raise _Unreadable("empty-file")
110
+ try:
111
+ text = raw.decode("utf-8-sig")
112
+ except UnicodeDecodeError:
113
+ raise _Unreadable("not-utf8") from None
114
+ if not text.strip():
115
+ raise _Unreadable("empty-file")
116
+ try:
117
+ records = list(csv.reader(io.StringIO(text, newline=""), strict=True))
118
+ except csv.Error as exc:
119
+ raise _Unreadable(
120
+ "field-too-large" if "field larger" in str(exc) else "csv-parse-error"
121
+ ) from None
122
+ return records, raw.startswith(_BOM), b"\r\n" in raw
123
+
124
+
125
+ def _scan_file(rel: str, raw: bytes) -> tuple[FileReport | None, list[Finding]]:
126
+ try:
127
+ records, has_bom, has_crlf = _parse(raw)
128
+ except _Unreadable as exc:
129
+ return None, [Finding("unreadable", rel, detail=(("reason", exc.reason),))]
130
+
131
+ findings: list[Finding] = []
132
+ if has_bom:
133
+ findings.append(Finding("bom", rel))
134
+ if has_crlf:
135
+ findings.append(Finding("crlf", rel))
136
+
137
+ header = records[0]
138
+ ncols = len(header)
139
+ if any(not h.strip() for h in header) or len(set(header)) != ncols:
140
+ findings.append(Finding("bad-header", rel))
141
+
142
+ data = [r for r in records[1:] if r]
143
+ nonempty = [0] * ncols
144
+ lengths = [0] * ncols
145
+ distinct: list[set[str]] = [set() for _ in range(ncols)]
146
+ all_int = [True] * ncols
147
+ dates: list[dict[str, int]] = [{} for _ in range(ncols)]
148
+ seps: list[dict[str, int]] = [{} for _ in range(ncols)]
149
+ nulls: list[dict[str, int]] = [{} for _ in range(ncols)]
150
+ null_rows: list[list[int]] = [[] for _ in range(ncols)]
151
+ path_rows: list[list[int]] = [[] for _ in range(ncols)]
152
+ ragged: list[int] = []
153
+ dup_rows: list[int] = []
154
+ comment_rows: list[int] = []
155
+ header_rows: list[int] = []
156
+ seen_rows: set[tuple[str, ...]] = set()
157
+
158
+ for number, row in enumerate(data, start=1):
159
+ if row == header:
160
+ header_rows.append(number)
161
+ if row[0].startswith("#"):
162
+ comment_rows.append(number)
163
+ if len(row) != ncols:
164
+ ragged.append(number)
165
+ key = tuple(row)
166
+ if key in seen_rows:
167
+ dup_rows.append(number)
168
+ seen_rows.add(key)
169
+ for i, value in enumerate(row[:ncols]):
170
+ if not value.strip():
171
+ continue
172
+ nonempty[i] += 1
173
+ lengths[i] += len(value)
174
+ distinct[i].add(value)
175
+ if not re.fullmatch(r"-?\d+", value.strip()):
176
+ all_int[i] = False
177
+ fmt = _date_format(value.strip())
178
+ if fmt:
179
+ dates[i][fmt] = dates[i].get(fmt, 0) + 1
180
+ for sep in ("|", ";"):
181
+ if sep in value:
182
+ seps[i][sep] = seps[i].get(sep, 0) + 1
183
+ token = value.strip().upper()
184
+ if token in NULL_STRINGS:
185
+ nulls[i][token] = nulls[i].get(token, 0) + 1
186
+ null_rows[i].append(number)
187
+ if _MACHINE_PATH.search(value):
188
+ path_rows[i].append(number)
189
+
190
+ def row_finding(kind: str, rows: list[int], column: str | None = None,
191
+ detail: tuple[tuple[str, int | str], ...] = ()) -> Finding:
192
+ return Finding(kind, rel, column, len(rows), tuple(rows[:MAX_EXAMPLE_ROWS]), detail)
193
+
194
+ if ragged:
195
+ findings.append(row_finding("ragged-rows", ragged))
196
+ if dup_rows:
197
+ findings.append(row_finding("duplicate-rows", dup_rows))
198
+ if comment_rows:
199
+ findings.append(row_finding("comment-rows", comment_rows))
200
+ if header_rows:
201
+ findings.append(row_finding("repeated-header", header_rows))
202
+ for i, name in enumerate(header):
203
+ if len(dates[i]) >= 2:
204
+ findings.append(Finding("mixed-date-formats", rel, name,
205
+ detail=tuple(sorted(dates[i].items()))))
206
+ if len(seps[i]) == 2:
207
+ findings.append(Finding("mixed-separators", rel, name,
208
+ detail=tuple(sorted(seps[i].items()))))
209
+ if path_rows[i]:
210
+ findings.append(row_finding("machine-paths", path_rows[i], name))
211
+ if null_rows[i]:
212
+ findings.append(row_finding("null-strings", null_rows[i], name,
213
+ tuple(sorted(nulls[i].items()))))
214
+
215
+ unique: list[UniqueColumn] = []
216
+ if len(data) >= 2:
217
+ for i, name in enumerate(header):
218
+ if nonempty[i] == len(data) and len(distinct[i]) == len(data):
219
+ labels: list[str] = []
220
+ if all_int[i]:
221
+ labels.append("integer-like")
222
+ if lengths[i] / len(data) > FREE_TEXT_MEAN_LENGTH:
223
+ labels.append("free-text")
224
+ unique.append(UniqueColumn(name, tuple(labels)))
225
+ if not unique:
226
+ findings.append(Finding("no-unique-column", rel))
227
+
228
+ return FileReport(rel, len(data), ncols, tuple(header), tuple(unique)), findings
229
+
230
+
231
+ def scan_project(root: Path, *, path_prefix: str | None = None) -> ReadinessReport:
232
+ files: list[FileReport] = []
233
+ findings: list[Finding] = []
234
+ not_scanned = {"json": 0, "markdown": 0, "other": 0}
235
+ for abs_path in _walk(root):
236
+ rel = abs_path.relative_to(root).as_posix()
237
+ if path_prefix and not rel.startswith(path_prefix):
238
+ continue
239
+ suffix = abs_path.suffix.lower()
240
+ if suffix != ".csv":
241
+ bucket = {".json": "json", ".md": "markdown", ".markdown": "markdown"}.get(suffix, "other")
242
+ not_scanned[bucket] += 1
243
+ continue
244
+ report, file_findings = _scan_file(rel, abs_path.read_bytes())
245
+ if report is not None:
246
+ files.append(report)
247
+ findings.extend(file_findings)
248
+ findings.sort(key=lambda f: (f.file, FINDING_KINDS.index(f.kind), f.column or ""))
249
+ return ReadinessReport(tuple(files), tuple(findings), not_scanned)
250
+
251
+
252
+ def _finding_line(f: Finding) -> str:
253
+ parts = [f"- {f.kind}: {f.file}"]
254
+ if f.column is not None:
255
+ parts.append(f"column {f.column!r}")
256
+ if f.count is not None:
257
+ parts.append(f"count={f.count}")
258
+ if f.rows:
259
+ parts.append("rows=" + ",".join(str(r) for r in f.rows))
260
+ if f.detail:
261
+ parts.append(", ".join(f"{k}={v}" for k, v in f.detail))
262
+ return " ".join(parts)
263
+
264
+
265
+ def format_markdown(report: ReadinessReport, *, findings_only: bool = False) -> str:
266
+ ns = report.not_scanned
267
+ lines = [
268
+ "# pdata readiness scan",
269
+ "",
270
+ "Row numbers are 1-based data records (header excluded), not line numbers: a quoted "
271
+ "multiline cell makes them differ.",
272
+ f"Scanned {len(report.files)} CSV file(s); not scanned: {ns['json']} JSON, "
273
+ f"{ns['markdown']} Markdown, {ns['other']} other.",
274
+ ]
275
+ if not report.files and not any(f.kind == "unreadable" for f in report.findings):
276
+ lines += ["", "No CSV files found."]
277
+ lines += ["", "## Findings"]
278
+ lines += [_finding_line(f) for f in report.findings] or ["No findings."]
279
+ if not findings_only and report.files:
280
+ lines += [
281
+ "",
282
+ "## Inventory",
283
+ "A unique column is a fact, not a recommended key: it is not a stable key until a "
284
+ "human has confirmed its values are not re-numbered per session or machine.",
285
+ ]
286
+ for f in report.files:
287
+ unique = ", ".join(
288
+ u.name + (f" ({'/'.join(u.labels)})" if u.labels else "") for u in f.unique_columns
289
+ )
290
+ lines.append(
291
+ f"- {f.path}: {f.rows} rows, {f.columns} columns"
292
+ + (f"; unique: {unique}" if unique else "")
293
+ )
294
+ return "\n".join(lines)
295
+
296
+
297
+ def format_json(report: ReadinessReport) -> str:
298
+ return json.dumps(
299
+ {
300
+ "files": [
301
+ {
302
+ "path": f.path, "rows": f.rows, "columns": f.columns,
303
+ "header": list(f.header),
304
+ "unique_columns": [{"name": u.name, "labels": list(u.labels)}
305
+ for u in f.unique_columns],
306
+ }
307
+ for f in report.files
308
+ ],
309
+ "findings": [
310
+ {
311
+ "kind": f.kind, "file": f.file, "column": f.column, "count": f.count,
312
+ "rows": list(f.rows), "detail": dict(f.detail),
313
+ }
314
+ for f in report.findings
315
+ ],
316
+ "not_scanned": report.not_scanned,
317
+ },
318
+ indent=2,
319
+ )
@@ -0,0 +1,162 @@
1
+ ---
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 feeds `ccst pdata init` (via `pm-pdata-do-init`) - the tool-native migration mechanism - and, only for the narrow record shapes it can't express, the deprecated `pm-pdata-do-manual-migration` fallback. 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
+ ---
5
+
6
+ # Audit central files
7
+
8
+ > **This audit feeds `ccst pdata init`, not a manual migration.** The readiness manifest (Phase 5)
9
+ > is a pre-flight pass for `ccst pdata init` (driven by `pm-pdata-do-init`), the default migration
10
+ > mechanism. `pm-pdata-do-manual-migration` is a deprecated fallback for record shapes `ccst pdata
11
+ > init` cannot express, not the default next step.
12
+
13
+ ## When to use this vs. `pm-update-central-files`
14
+
15
+ `pm-update-central-files` is a single-session wrap-up (WORKLOG, memory, registering new `out/`
16
+ files). This skill is a project-wide sweep of every central file (indexes, logs, registers, CSVs),
17
+ run on demand, that can span a session or more. Use it when the whole project's recordkeeping needs
18
+ a health check, not just today's session tidied up.
19
+
20
+ ## Overview
21
+
22
+ Five phases, each leaving a durable artefact. Four things called "readiness" are distinct:
23
+ the scan (`ccst pdata readiness-scan`, computed on demand), `pdata-readiness-notes.md` (Phase 4's
24
+ findings list), the Phase 5 manifest (`analysis/pdata-readiness.md` or equivalent), and
25
+ `.pdata-migration-manifest.json` (`ccst pdata init`'s own classification, not written here).
26
+
27
+ 1. **OneDrive sync-conflict cleanup** (synced folders only).
28
+ 2. **Readiness scan, then judgement audit** of each central-file domain.
29
+ 3. **Consolidated report + checkpoint table.**
30
+ 4. **pdata-readiness pass:** every blocker, file by file.
31
+ 5. **Readiness manifest** for `ccst pdata init`.
32
+
33
+ Nothing destructive happens without the user's go-ahead. Deletions are always handed over as a
34
+ reviewable `rm` script, per `resolve-onedrive-conflicts`; never run `rm` yourself, even after
35
+ approval.
36
+
37
+ ## Phase 1: OneDrive sync-conflict cleanup
38
+
39
+ Invoke `resolve-onedrive-conflicts` and run its scanner across the project tree; run it before
40
+ anything else so stale device-suffixed copies do not pollute later phases. Treat a
41
+ disproportionate `true_conflict` count as a scanner false-positive to investigate (the hyphen
42
+ heuristic can match a project's own multi-hyphen naming) and filter to genuine device-suffix
43
+ conflicts before trusting it. Split them into **central/index files** (a handful, reviewable
44
+ directly) and **bulk content files** (dozens or hundreds; give them their own agent with the
45
+ file list).
46
+
47
+ ## Phase 2: readiness scan, then judgement audit
48
+
49
+ **Step 1 - run the scan before any per-domain checking or agent dispatch:**
50
+
51
+ ```
52
+ ccst pdata readiness-scan --project <name> # whole project, compact Markdown
53
+ ccst pdata readiness-scan --project <name> --format json
54
+ ccst pdata readiness-scan --project <name> --path <domain-dir>/ --findings-only # one domain's slice
55
+ ```
56
+
57
+ It is read-only and deterministic. It reports, per CSV, `ragged-rows`, `bad-header`,
58
+ `mixed-date-formats`, `mixed-separators` (`|` with `;` only), `machine-paths`, `null-strings`,
59
+ `duplicate-rows`, `comment-rows`, `repeated-header`, `no-unique-column`, `bom`, `crlf` and
60
+ `unreadable`, plus row counts and unique columns. Treat findings as leads to verify, not facts to
61
+ act on (a lone `-` may be a real dash). Its scanned set equals what `ccst pdata init` will
62
+ classify. Row numbers are data records, not line numbers.
63
+
64
+ A **unique column is not a stable key.** Whether it is one is a judgement, and the scan is input
65
+ to that judgement, not its answer: watch especially for `row_id`-style columns numbered
66
+ independently by different sessions or machines - these are NOT stable keys even though they look
67
+ like them. A `bom` is what turns a header garbled
68
+ under `ccst pdata init`; `crlf` is worth normalising first.
69
+
70
+ **Step 2 - judgement audit.** Group the central files the project's own instructions document,
71
+ plus anything on disk that is not documented (itself a gap), into domains (usually one per
72
+ top-level folder with its own index). With **three or fewer domains**, audit them yourself in one
73
+ pass. With **more than three**, dispatch one Sonnet-tier agent per domain in parallel, per
74
+ `dispatching-parallel-agents` and `select-agent-model`, giving each only its own domain's scan
75
+ slice (`--path`, `--findings-only`). Agents check, with concrete evidence for every claim:
76
+
77
+ - **File-to-index correspondence:** every file an index describes exists; every file that should be
78
+ indexed has a row.
79
+ - **Controlled vocabularies:** any documented enum column uses only documented values.
80
+ - **Cross-reference integrity:** every row_id or back-reference resolves.
81
+ - **Documented but missing:** every file or folder the project's instructions describe exists. This
82
+ rot is common and durable because a merely-referenced file that is absent breaks nothing loudly.
83
+ - **Derived-artefact completeness:** each source has its documented mirror, and no mirror is
84
+ silently empty.
85
+ - **Own-documentation accuracy:** every path, count and reference in an atlas or index-of-indexes
86
+ checked against the live filesystem.
87
+ - **The scan's blind spots:** multi-schema single files (comment banners, section headers, a second
88
+ table under one file - `repeated-header` and `comment-rows` are only hints) and
89
+ comma-versus-semicolon list columns.
90
+ - **Key stability:** whether each unique column is really a stable key.
91
+
92
+ Agents work read-only unless a fix is unambiguous and mechanical (merging two non-overlapping logs);
93
+ anything needing editorial judgement is a recommendation. Use `resolve-onedrive-conflicts`'s
94
+ `DELETE-SAFE` / `MERGE-NEEDED` / `RENAME` / `NO ACTION` verdicts for consistency.
95
+
96
+ ## Phase 3: consolidated report + checkpoint table
97
+
98
+ Write one consolidated report to the session's `out/` covering every domain and the scan, and give
99
+ the user a checkpoint table split into:
100
+
101
+ - **Unambiguous fixes** ready on a single approval (documentation corrections, verified merges).
102
+ - **Genuine judgement calls** to weigh individually (ambiguous ownership, prune-vs-keep, which of
103
+ two diverged forks is authoritative).
104
+ - **Real gaps** found but not fixed (missing extractions, undersized indexes, structural drift):
105
+ recommend a project TODO entry or a prompt file for a dedicated session.
106
+
107
+ Hand back one reviewable delete script for every confirmed-safe deletion, exactly per
108
+ `resolve-onedrive-conflicts` (comment header with each item's evidence, `set -e`, one `rm --` per
109
+ line, told to the user as "review, then run yourself").
110
+
111
+ ## Phase 4: pdata-readiness pass
112
+
113
+ Compile `pdata-readiness-notes.md` (or equivalent): fold in the scan's findings and add what it
114
+ cannot see, listing file by file every blocker a `pm-pdata-design-schema` session would hit:
115
+
116
+ - Inconsistent separators within one logical column (including comma/semicolon).
117
+ - No safe, durable, cross-session-stable natural key - especially independently-numbered `row_id`
118
+ columns.
119
+ - Mixed date/timestamp formats.
120
+ - A file that is really several differently-shaped tables: needs a "split into multiple record
121
+ collections" decision, not a naive single-table import.
122
+ - Machine-specific or session-relative paths in a column.
123
+ - Null conventions that are not real nulls (`"NO DATA"`, an empty cell that means something else).
124
+ - A `bom` or `crlf` file that will need normalising.
125
+
126
+ This file is a findings list, not a migration plan.
127
+
128
+ ## Phase 5: write the readiness manifest (for `ccst pdata init` / `pm-pdata-do-init`)
129
+
130
+ Once the audit settles (typically two consecutive clean verification rounds, or a documented
131
+ pragmatic stop), write one manifest at a stable location (`analysis/pdata-readiness.md` for
132
+ projects with an `analysis/`-style folder, else the project's own top-level docs location), and
133
+ record its location in the project's instructions file. Sections, in this order, so `pm-pdata-do-init`
134
+ (or the deprecated `pm-pdata-do-manual-migration` fallback) can parse it deterministically:
135
+
136
+ 1. **Header:** audit date, session/agent identifier, and an explicit "STALE IF" note (e.g. "stale
137
+ if any file below was edited after `<date>`").
138
+ 2. **File inventory:** every central/index/recordkeeping file covered, with full path, row/entry
139
+ count, last-verified-clean date, and role (`index`, `register`, `log`, `narrative`,
140
+ `derived-artefact`).
141
+ 3. **Per-file migration blockers:** Phase 4's content, in the shape `pm-pdata-design-schema` and
142
+ `pm-pdata-do-init` consume.
143
+ 4. **Recommended natural key per file** where one exists cleanly, else an explicit "no stable key
144
+ found, needs a synthetic id".
145
+ 5. **Known-acceptable duplication:** anything duplicate by design, so a migration does not re-flag
146
+ it or silently drop one copy.
147
+ 6. **Open decisions:** anything needing the owner's judgement and not resolved; a later session must
148
+ not treat an unresolved item as settled.
149
+
150
+ ## Common mistakes
151
+
152
+ - Trusting the conflict scanner's raw `true_conflict` count without checking for the naming-pattern
153
+ false positive.
154
+ - Treating the scan's unique columns as recommended keys, or its findings as facts to fix blindly.
155
+ - Assuming documentation implies existence: check every documented path.
156
+ - Running `rm` yourself after approval instead of handing back a reviewable script.
157
+ - Dispatching one do-everything agent, or a fleet for a three-domain project: use one agent per
158
+ domain only above three domains.
159
+ - Skipping the readiness pass because "the project isn't migrating yet": if a `.pdata-db-dump/` or a
160
+ pdata mention exists, it earns its cost early.
161
+ - Silently fixing a judgement call an agent flagged: "safe to apply" and "needs a human decision" are
162
+ different buckets even when both are technically fixable.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-session-tools
3
- Version: 3.7.1
3
+ Version: 3.8.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
@@ -928,6 +928,25 @@ ccst context-override off # re-enable the warnings
928
928
  ccst context-override status # report the current state
929
929
  ```
930
930
 
931
+ ### `ccst pdata readiness-scan`
932
+
933
+ A read-only scan of a project's CSV files for the mechanical blockers to a `ccst pdata init`
934
+ migration, used by the `pm-pdata-do-audit-and-prepare-to-migrate` skill and useful on its own:
935
+
936
+ ```sh
937
+ ccst pdata readiness-scan --project myproject # compact Markdown report
938
+ ccst pdata readiness-scan --project myproject --format json # machine-readable
939
+ ccst pdata readiness-scan --project myproject --path notes/ --findings-only # one folder's findings
940
+ ```
941
+
942
+ It reports ragged rows, empty/duplicate headers, mixed date formats, mixed `|`/`;` separators,
943
+ machine-specific paths, null-like placeholder strings, duplicate rows, comment rows, repeated
944
+ header rows, byte-order marks, CRLF line endings and unreadable files, plus a per-file inventory
945
+ and the columns that are unique on every row. Findings carry counts and row numbers, never cell
946
+ text, and a unique column is a fact rather than a recommended key. It exits 0 whenever the scan
947
+ ran (findings are not failures), and 2 when the project directory does not exist. It scans exactly
948
+ the CSVs `ccst pdata init` would classify.
949
+
931
950
  ### `ccst skills install`
932
951
 
933
952
  > You normally don't need this: `ccst install-everything --apply` runs it, and `ccst` runs that
@@ -71,6 +71,7 @@ src/cc_session_tools/lib/pdata/init_paths.py
71
71
  src/cc_session_tools/lib/pdata/init_service.py
72
72
  src/cc_session_tools/lib/pdata/manifest.py
73
73
  src/cc_session_tools/lib/pdata/naming.py
74
+ src/cc_session_tools/lib/pdata/readiness.py
74
75
  src/cc_session_tools/lib/pdata/rehydrate.py
75
76
  src/cc_session_tools/lib/pdata/rename_group.py
76
77
  src/cc_session_tools/lib/pdata/reorganize.py
@@ -218,6 +219,7 @@ tests/test_ccst_machine_identity_cli.py
218
219
  tests/test_ccst_main_gate.py
219
220
  tests/test_ccst_pdata_cli.py
220
221
  tests/test_ccst_pdata_init_cli.py
222
+ tests/test_ccst_pdata_readiness_cli.py
221
223
  tests/test_ccst_pdata_reconcile_cli.py
222
224
  tests/test_ccst_pdata_rename_group_cli.py
223
225
  tests/test_ccst_pdata_reorganize_cli.py
@@ -264,6 +266,7 @@ tests/test_occupancy.py
264
266
  tests/test_parent_inference.py
265
267
  tests/test_parser.py
266
268
  tests/test_paths.py
269
+ tests/test_pdata_audit_skill.py
267
270
  tests/test_pending_migration_hook.py
268
271
  tests/test_picker.py
269
272
  tests/test_pricing.py