cc-session-tools 3.2.1__tar.gz → 3.3.1__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 (279) hide show
  1. {cc_session_tools-3.2.1/src/cc_session_tools.egg-info → cc_session_tools-3.3.1}/PKG-INFO +1 -1
  2. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/pyproject.toml +1 -1
  3. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/ccst.py +20 -5
  4. cc_session_tools-3.3.1/src/cc_session_tools/lib/pdata/cutover.py +90 -0
  5. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/init_service.py +70 -6
  6. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/manifest.py +9 -0
  7. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/resolve.py +3 -3
  8. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/service.py +1 -1
  9. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +9 -0
  10. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/prompts/pdata-migration-skills-update.md +9 -0
  11. {cc_session_tools-3.2.1/src/cc_session_tools/skills/pm-pdata-schema-design → cc_session_tools-3.3.1/src/cc_session_tools/skills/pm-pdata-design-schema}/SKILL.md +1 -1
  12. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +17 -8
  13. {cc_session_tools-3.2.1/src/cc_session_tools/skills/pm-pdata-do-migrate → cc_session_tools-3.3.1/src/cc_session_tools/skills/pm-pdata-do-manual-migration}/SKILL.md +22 -6
  14. {cc_session_tools-3.2.1/src/cc_session_tools/skills/pm-pdata-conflict-resolution → cc_session_tools-3.3.1/src/cc_session_tools/skills/pm-pdata-resolve-conflicts}/SKILL.md +1 -1
  15. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/pm-project-init/SKILL.md +84 -1
  16. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +1 -1
  17. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1/src/cc_session_tools.egg-info}/PKG-INFO +1 -1
  18. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools.egg-info/SOURCES.txt +3 -3
  19. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_bundle_discovery.py +9 -8
  20. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_init_cli.py +29 -0
  21. cc_session_tools-3.2.1/src/cc_session_tools/lib/pdata/cutover.py +0 -34
  22. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/LICENSE +0 -0
  23. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/README.md +0 -0
  24. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/setup.cfg +0 -0
  25. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/__init__.py +0 -0
  26. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/__init__.py +0 -0
  27. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/ccd.py +0 -0
  28. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/ccmsg.py +0 -0
  29. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/ccr.py +0 -0
  30. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/ccs.py +0 -0
  31. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/ccsched.py +0 -0
  32. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
  33. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
  34. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
  35. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
  36. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/config/hooks-bundle.json +0 -0
  37. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/config/session-tag-hook.json +0 -0
  38. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/hooks_install.py +0 -0
  39. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/__init__.py +0 -0
  40. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/claude_flags.py +0 -0
  41. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/claude_md_install.py +0 -0
  42. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/context_overrides.py +0 -0
  43. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/db.py +0 -0
  44. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/debug.py +0 -0
  45. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/doctor.py +0 -0
  46. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
  47. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/hook_registry.py +0 -0
  48. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/install_sync.py +0 -0
  49. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/levenshtein.py +0 -0
  50. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/machine_identity.py +0 -0
  51. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
  52. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
  53. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
  54. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/lock.py +0 -0
  55. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/message.py +0 -0
  56. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
  57. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/repository.py +0 -0
  58. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/retention.py +0 -0
  59. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/service.py +0 -0
  60. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/messaging/store.py +0 -0
  61. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/occupancy.py +0 -0
  62. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/paths.py +0 -0
  63. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
  64. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/backup.py +0 -0
  65. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/classify.py +0 -0
  66. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/dump.py +0 -0
  67. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
  68. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/importers.py +0 -0
  69. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
  70. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/naming.py +0 -0
  71. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
  72. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
  73. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
  74. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/repository.py +0 -0
  75. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
  76. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/store.py +0 -0
  77. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
  78. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
  79. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
  80. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
  81. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
  82. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/verify.py +0 -0
  83. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
  84. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/picker.py +0 -0
  85. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/proc_lock.py +0 -0
  86. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/prompts.py +0 -0
  87. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/roots.py +0 -0
  88. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/rules.py +0 -0
  89. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
  90. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
  91. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
  92. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
  93. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
  94. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/due.py +0 -0
  95. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
  96. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
  97. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
  98. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
  99. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
  100. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
  101. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
  102. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
  103. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/state.py +0 -0
  104. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/store.py +0 -0
  105. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
  106. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
  107. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
  108. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/session_gc.py +0 -0
  109. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/sessions.py +0 -0
  110. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/sessions_db.py +0 -0
  111. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/sessions_repair.py +0 -0
  112. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/shell_install.py +0 -0
  113. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/tasklist.py +0 -0
  114. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/lib/telemetry_store.py +0 -0
  115. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
  116. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
  117. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
  118. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
  119. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
  120. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
  121. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
  122. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
  123. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
  124. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
  125. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
  126. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
  127. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
  128. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
  129. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
  130. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
  131. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
  132. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
  133. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
  134. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
  135. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
  136. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
  137. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
  138. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
  139. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
  140. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
  141. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
  142. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
  143. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
  144. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
  145. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
  146. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
  147. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
  148. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools.egg-info/requires.txt +0 -0
  149. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/cc_session_tools.egg-info/top_level.txt +0 -0
  150. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/__init__.py +0 -0
  151. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/attribution.py +0 -0
  152. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/cache.py +0 -0
  153. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/ccusage_wrapper.py +0 -0
  154. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/cli.py +0 -0
  155. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/data/pricing.json +0 -0
  156. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/parent_inference.py +0 -0
  157. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/parser.py +0 -0
  158. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/pricing.py +0 -0
  159. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/query.py +0 -0
  160. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/report.py +0 -0
  161. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/schema.py +0 -0
  162. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/claude_code_usage/session_names.py +0 -0
  163. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/__init__.py +0 -0
  164. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/after_response.py +0 -0
  165. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/bash_hard_deny.py +0 -0
  166. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/bash_security_review.py +0 -0
  167. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/cache.py +0 -0
  168. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/catchup.py +0 -0
  169. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/confirm_8digit.py +0 -0
  170. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/context_window_warning.py +0 -0
  171. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/last_screenshot.py +0 -0
  172. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/marker_allow.py +0 -0
  173. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/markers.py +0 -0
  174. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/messaging_deliver.py +0 -0
  175. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/model_info.py +0 -0
  176. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/normalise.py +0 -0
  177. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/pdata_sync.py +0 -0
  178. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/pending_migration.py +0 -0
  179. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/pending_rename.py +0 -0
  180. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/session_tag.py +0 -0
  181. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/stats.py +0 -0
  182. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/telemetry.py +0 -0
  183. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/telemetry_query.py +0 -0
  184. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/telemetry_trim.py +0 -0
  185. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/transcript.py +0 -0
  186. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/src/hooks/worklog_guard.py +0 -0
  187. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_after_response.py +0 -0
  188. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_attribution.py +0 -0
  189. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_bash_hard_deny.py +0 -0
  190. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_bash_security_review.py +0 -0
  191. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cache.py +0 -0
  192. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cache_sqlite.py +0 -0
  193. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccr_orphans.py +0 -0
  194. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccs_emptiness.py +0 -0
  195. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccs_eta.py +0 -0
  196. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccs_list_mode.py +0 -0
  197. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccs_scope_flags.py +0 -0
  198. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccs_sentinel_sort.py +0 -0
  199. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccs_session_counts.py +0 -0
  200. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
  201. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_cli.py +0 -0
  202. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_context_override_cli.py +0 -0
  203. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_doctor.py +0 -0
  204. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_gc_prune.py +0 -0
  205. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_gc_report.py +0 -0
  206. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_hook_dispatcher.py +0 -0
  207. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_hooks_install_prune.py +0 -0
  208. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
  209. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_hooks_uninstall.py +0 -0
  210. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_install_everything.py +0 -0
  211. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_machine_identity_cli.py +0 -0
  212. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_main_gate.py +0 -0
  213. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_cli.py +0 -0
  214. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
  215. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
  216. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
  217. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_sync_cli.py +0 -0
  218. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_pdata_verify_cli.py +0 -0
  219. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_repair_sessions_cli.py +0 -0
  220. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_sessions_cli.py +0 -0
  221. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_shell_install.py +0 -0
  222. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_skills_install.py +0 -0
  223. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_skills_uninstall.py +0 -0
  224. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_telemetry_query.py +0 -0
  225. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccst_telemetry_trim.py +0 -0
  226. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_ccusage_wrapper.py +0 -0
  227. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_claude_flags.py +0 -0
  228. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_claude_md_install.py +0 -0
  229. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cli.py +0 -0
  230. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cli_ccd.py +0 -0
  231. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cli_ccd_dry_run.py +0 -0
  232. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cli_ccr.py +0 -0
  233. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cli_ccs.py +0 -0
  234. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_cli_version.py +0 -0
  235. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_confirm_8digit.py +0 -0
  236. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_context_overrides.py +0 -0
  237. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_context_window_warning.py +0 -0
  238. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_db.py +0 -0
  239. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_debug.py +0 -0
  240. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_doctor_mutes.py +0 -0
  241. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_empty_session.py +0 -0
  242. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_hook_cache.py +0 -0
  243. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_hooks_install.py +0 -0
  244. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_install_sync.py +0 -0
  245. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_last_screenshot.py +0 -0
  246. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_levenshtein.py +0 -0
  247. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_machine_identity.py +0 -0
  248. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_marker_allow.py +0 -0
  249. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_migrate_ccmsg.py +0 -0
  250. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_migrate_ccsched.py +0 -0
  251. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_migrate_sessions_db.py +0 -0
  252. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_migrate_telemetry.py +0 -0
  253. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_model_info.py +0 -0
  254. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_normalise.py +0 -0
  255. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_occupancy.py +0 -0
  256. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_parent_inference.py +0 -0
  257. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_parser.py +0 -0
  258. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_paths.py +0 -0
  259. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_pending_migration_hook.py +0 -0
  260. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_picker.py +0 -0
  261. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_pricing.py +0 -0
  262. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_proc_lock.py +0 -0
  263. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_prompts.py +0 -0
  264. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_query.py +0 -0
  265. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_report.py +0 -0
  266. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_roots.py +0 -0
  267. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_rules.py +0 -0
  268. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_scheduler_bundled_jobs.py +0 -0
  269. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_schema.py +0 -0
  270. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_session_names.py +0 -0
  271. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_session_tag.py +0 -0
  272. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_sessions.py +0 -0
  273. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_sessions_db.py +0 -0
  274. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_sessions_repair.py +0 -0
  275. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_tasklist.py +0 -0
  276. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_telemetry.py +0 -0
  277. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_telemetry_store.py +0 -0
  278. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/tests/test_transcript.py +0 -0
  279. {cc_session_tools-3.2.1 → cc_session_tools-3.3.1}/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.2.1
3
+ Version: 3.3.1
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.2.1"
7
+ version = "3.3.1"
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" }
@@ -173,14 +173,19 @@ def _print_migration_prompt_reminders(
173
173
  project_root: Path, *labeled_filenames: tuple[str, str]
174
174
  ) -> None:
175
175
  """Print one "<label>: <path>" reminder line per (label, filename) pair, for the bundled
176
- prompts/ directory, followed by a line telling the user to run it in a fresh Claude Code
177
- session started in project_root.
176
+ prompts/ directory, followed by a line telling the user to run it in a fresh context (a new
177
+ Claude Code session, or a dispatched Agent subagent) started in project_root.
178
178
 
179
179
  Each prompt's own "Run it via" section already spells out the `cd <project> && claude -p
180
180
  ...` invocation (see e.g. pdata-migration-skills-update.md) - that only works if the
181
181
  session's cwd is the project root, since every prompt's Step 1 checks for it and aborts
182
- otherwise. Printing the reminder without saying so reads as "open this file", which invites
183
- running it inline in whatever session called `ccst pdata init` - the wrong cwd entirely.
182
+ otherwise. What the fresh-context requirement is actually protecting against is an
183
+ orchestrating session rationalizing away a stale reference it half-remembers writing
184
+ earlier in the same conversation - a dispatched Agent subagent (same cwd) gets that same
185
+ benefit without shelling out to a literal second process, so both are offered here rather
186
+ than only the `claude -p` invocation. Printing the reminder without saying so reads as "open
187
+ this file", which invites running it inline in whatever session called `ccst pdata init` -
188
+ the wrong cwd entirely.
184
189
 
185
190
  Best-effort, deliberately swallowing a missing prompts/ directory: these reminders are a
186
191
  nicety layered on top of an otherwise-already-completed classification or migration, not
@@ -197,7 +202,8 @@ def _print_migration_prompt_reminders(
197
202
  print(f"{label}: {prompts_dir / filename}")
198
203
  print(
199
204
  f" Run in a new Claude Code session started in {project_root} (its own "
200
- f"\"Run it via\" section has the exact command) - not in this session."
205
+ f"\"Run it via\" section has the exact command), or dispatch it as an Agent "
206
+ f"subagent with the same cwd - not inline in this session."
201
207
  )
202
208
 
203
209
 
@@ -1178,6 +1184,7 @@ def _cmd_pdata_init(args: argparse.Namespace) -> int:
1178
1184
  try:
1179
1185
  write_result = init_service.write(
1180
1186
  project=args.project, rehearse=rehearse, on_progress=print,
1187
+ leave_no_pointer_files=args.leave_no_pointer_files,
1181
1188
  )
1182
1189
  except (FileNotFoundError, ValueError) as exc:
1183
1190
  print(f"ccst pdata: {exc}", file=sys.stderr)
@@ -3035,6 +3042,14 @@ def _build_parser() -> argparse.ArgumentParser:
3035
3042
  "<project>/ccst-pdata-init-write.log"
3036
3043
  ),
3037
3044
  )
3045
+ pdata_init_parser.add_argument(
3046
+ "--leave-no-pointer-files", action="store_true",
3047
+ help=(
3048
+ "By default, --write leaves a Markdown pointer file at each migrated entry's "
3049
+ "original path (record_group, schema, an example query command). Pass this "
3050
+ "flag to suppress that and leave nothing behind at the original path."
3051
+ ),
3052
+ )
3038
3053
 
3039
3054
  pdata_reorganize_parser = pdata_sub.add_parser(
3040
3055
  "reorganize", help="Split a flat folder into a nested (by-year or by-year-month) structure"
@@ -0,0 +1,90 @@
1
+ """Cutover: archive migrated-source originals, never delete them (spec §7.1 steps 6-7)."""
2
+ from __future__ import annotations
3
+
4
+ import time
5
+ from pathlib import Path
6
+
7
+ from cc_session_tools.lib.pdata.init_paths import (
8
+ MIGRATED_ARCHIVE_DIRNAME,
9
+ MIGRATED_MANIFEST_FILENAME,
10
+ )
11
+ from cc_session_tools.lib.pdata.manifest import ManifestEntry
12
+
13
+
14
+ def _pointer_file_content(*, project: str, entry: ManifestEntry) -> str:
15
+ """Generic pointer content (spec pdata/init-pointer-files): the record_group now
16
+ holding this file's data, its field/schema table, and an example query — plus,
17
+ when the entry carries preface_text (set by hand per pm-project-init's
18
+ garbled-CSV-header fix recipe), that text verbatim under its own heading."""
19
+ lines = [
20
+ f"# {Path(entry.path).name} — migrated to pdata",
21
+ "",
22
+ f"This file's data now lives in pdata record group `{entry.db_group()}` for "
23
+ f"project `{project}`. The original was moved to "
24
+ f"`{MIGRATED_ARCHIVE_DIRNAME}/{entry.path}` and is no longer read from here.",
25
+ "",
26
+ "## Schema",
27
+ "",
28
+ "| field | sql_type | description |",
29
+ "| --- | --- | --- |",
30
+ ]
31
+ for spec in entry.fields:
32
+ lines.append(f"| {spec.name} | {spec.sql_type} | {spec.description or ''} |")
33
+ lines += [
34
+ "",
35
+ "## Example query",
36
+ "",
37
+ "```sh",
38
+ f"ccst pdata list --project {project} --group {entry.db_group()}",
39
+ "```",
40
+ ]
41
+ if entry.preface_text:
42
+ lines += [
43
+ "",
44
+ "## Preserved preface text (removed from the original file before migration)",
45
+ "",
46
+ entry.preface_text,
47
+ ]
48
+ return "\n".join(lines) + "\n"
49
+
50
+
51
+ def _write_pointer_file(*, project_root: Path, project: str, entry: ManifestEntry) -> None:
52
+ pointer_path = project_root / Path(entry.path).with_suffix(".md")
53
+ pointer_path.parent.mkdir(parents=True, exist_ok=True)
54
+ pointer_path.write_text(
55
+ _pointer_file_content(project=project, entry=entry), encoding="utf-8",
56
+ )
57
+
58
+
59
+ def archive_entries(
60
+ *, project_root: Path, entries: list[ManifestEntry], project: str,
61
+ write_pointer_files: bool = True,
62
+ ) -> None:
63
+ """Move every db-owned entry's source file into project_root/.pdata-migrated/,
64
+ preserving its relative path, and append one line per entry to MANIFEST.md.
65
+ Never deletes — cutover only relocates within project_root (spec §7.1 step 6);
66
+ deleting the archive is a manual, human-directed action (step 7).
67
+
68
+ By default (write_pointer_files=True, spec pdata/init-pointer-files), also writes a
69
+ Markdown pointer file back at each entry's original path so anything else in the
70
+ project that cites the old path by name finds a live signpost instead of a silent
71
+ 404. Pass write_pointer_files=False (--leave-no-pointer-files at the CLI) to
72
+ suppress this."""
73
+ if not entries:
74
+ return
75
+ archive_root = project_root / MIGRATED_ARCHIVE_DIRNAME
76
+ archive_root.mkdir(parents=True, exist_ok=True)
77
+ manifest_path = archive_root / MIGRATED_MANIFEST_FILENAME
78
+ now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
79
+ with manifest_path.open("a", encoding="utf-8") as log:
80
+ for entry in entries:
81
+ source = project_root / entry.path
82
+ destination = archive_root / entry.path
83
+ destination.parent.mkdir(parents=True, exist_ok=True)
84
+ source.rename(destination)
85
+ log.write(
86
+ f"- {now} — {entry.path} — migrated source, superseded by ccst pdata "
87
+ f"(record_group={entry.db_group()})\n"
88
+ )
89
+ if write_pointer_files:
90
+ _write_pointer_file(project_root=project_root, project=project, entry=entry)
@@ -8,7 +8,7 @@ import csv
8
8
  import sqlite3
9
9
  from collections.abc import Callable
10
10
  from dataclasses import dataclass
11
- from datetime import datetime
11
+ from datetime import UTC, datetime
12
12
  from pathlib import Path
13
13
 
14
14
  from cc_session_tools.lib import db
@@ -96,6 +96,29 @@ def dry_run(*, project: str, rehearse: Path | None = None) -> DryRunResult:
96
96
  return DryRunResult(manifest=m, report=_render_report(m), proposal_path=proposal_path)
97
97
 
98
98
 
99
+ # A field name proposed by _classify_csv is derived from a real CSV header cell (see
100
+ # classify._slugify_field_name) — an ordinary column name slugifies to something short.
101
+ # A garbled header (prose/provenance text on the "header" row, spec pdata/
102
+ # init-classification-report) instead produces long, many-word sentence-fragment names
103
+ # like `pending_chris_s_manual_review`. Neither threshold alone is exact, but a name
104
+ # tripping either is worth a reviewer's second look.
105
+ _SUSPICIOUS_FIELD_NAME_CHAR_THRESHOLD = 40
106
+ _SUSPICIOUS_FIELD_NAME_WORD_THRESHOLD = 5
107
+
108
+
109
+ def _looks_like_garbled_header(field_names: list[str]) -> bool:
110
+ """True when multiple proposed field names read as sentence fragments rather than
111
+ short domain nouns — see spec pdata/init-classification-report. A single long name
112
+ isn't enough on its own (a legitimately verbose but real column header shouldn't be
113
+ flagged); this looks for the pattern repeating across an entry's fields."""
114
+ suspicious = sum(
115
+ 1 for name in field_names
116
+ if len(name) > _SUSPICIOUS_FIELD_NAME_CHAR_THRESHOLD
117
+ or name.count("_") + 1 >= _SUSPICIOUS_FIELD_NAME_WORD_THRESHOLD
118
+ )
119
+ return suspicious >= 2
120
+
121
+
99
122
  def _render_report(m: Manifest) -> str:
100
123
  if not m.entries:
101
124
  return f"ccst pdata init — {m.project}: no files found, empty base schema created."
@@ -109,6 +132,12 @@ def _render_report(m: Manifest) -> str:
109
132
  f" [db-owned] {e.path} -> group={e.record_group} "
110
133
  f"strategy={e.strategy} fields={field_names}"
111
134
  )
135
+ if _looks_like_garbled_header(field_names):
136
+ lines.append(
137
+ " ⚠ worth double-checking — these field names read as sentence "
138
+ "fragments, not column names; this file's header may not be what it "
139
+ "looks like (see pm-project-init's garbled-CSV-header caveat)"
140
+ )
112
141
  lines.append(
113
142
  "Review/override entries in the proposal file listed below before running --write."
114
143
  )
@@ -352,6 +381,7 @@ def _adopt_from_dump(
352
381
  def write(
353
382
  *, project: str, rehearse: Path | None = None,
354
383
  on_progress: Callable[[str], None] | None = None,
384
+ leave_no_pointer_files: bool = False,
355
385
  ) -> WriteResult:
356
386
  project_root = init_paths.resolve_project_root(project, rehearse=rehearse)
357
387
 
@@ -396,11 +426,33 @@ def write(
396
426
  _check_db_not_locked(project)
397
427
 
398
428
  db_owned = [e for e in m.entries if e.classification == "db-owned"]
399
- _emit(on_progress, f"Importing {len(db_owned)} file(s)...")
400
- for entry in m.entries:
401
- if entry.classification != "db-owned":
402
- continue
429
+ already_migrated = [e for e in db_owned if e.migrated_at is not None]
430
+ to_import = [e for e in db_owned if e.migrated_at is None]
431
+ _emit(on_progress, f"Importing {len(to_import)} file(s)...")
432
+ if already_migrated:
433
+ _emit(
434
+ on_progress,
435
+ f" skipping {len(already_migrated)} already-migrated file(s): "
436
+ f"{', '.join(e.path for e in already_migrated)}",
437
+ )
438
+ for entry in to_import:
403
439
  try:
440
+ source_path = project_root / entry.path
441
+ if not source_path.exists():
442
+ # entry.migrated_at is None here (to_import excludes already-migrated
443
+ # entries above), so this is not the ordinary "already cut over by an
444
+ # earlier --write" case — the source is genuinely missing for an entry
445
+ # this run believes still needs importing. Fail with a specific,
446
+ # actionable message (still caught by this loop's own except clause
447
+ # below, so it contributes to `reasons`/rollback exactly like any other
448
+ # per-entry import failure) instead of a bare FileNotFoundError only
449
+ # discoverable via the rollback reasons list.
450
+ raise ValueError(
451
+ f"{entry.path}: marked db-owned with no migrated_at, but its source "
452
+ f"file does not exist at {source_path} — if this entry was already "
453
+ f"migrated by another means, set its classification to folder-owned "
454
+ f"(or otherwise correct the manifest) before running --write again"
455
+ )
404
456
  _emit(on_progress, f" importing {entry.path} -> group={entry.db_group()}...")
405
457
  for spec in entry.fields:
406
458
  service.schema_add_field(
@@ -456,7 +508,19 @@ def write(
456
508
  )
457
509
 
458
510
  _emit(on_progress, f"Cutting over {len(written_entries)} file(s)...")
459
- cutover.archive_entries(project_root=project_root, entries=written_entries)
511
+ cutover.archive_entries(
512
+ project_root=project_root, entries=written_entries, project=project,
513
+ write_pointer_files=not leave_no_pointer_files,
514
+ )
515
+ # Mark every entry this run actually cut over as migrated, and persist that back to
516
+ # the manifest — write() only ever `manifest.load`s otherwise, so without this a later
517
+ # --write would re-attempt (and, pre-fix, roll back) these same now-archived entries.
518
+ # written_entries holds the same ManifestEntry objects m.entries does (see the
519
+ # `to_import`/`written_entries.append(entry)` loop above), so mutating here mutates m.
520
+ migrated_at = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
521
+ for entry in written_entries:
522
+ entry.migrated_at = migrated_at
523
+ manifest.save(m, proposal_path)
460
524
  return WriteResult(
461
525
  created_record_ids=created_ids,
462
526
  entries_written=[e.path for e in written_entries],
@@ -40,6 +40,15 @@ class ManifestEntry:
40
40
  content_column: str | None = None
41
41
  file_path_column: str | None = None
42
42
  fields: list[FieldSpec] = field(default_factory=list)
43
+ # Set by init_service.write() on successful cutover (ISO-8601 UTC) — a later
44
+ # --write skips any entry that already carries one instead of re-importing it
45
+ # (spec pdata/init-write-idempotency). None for an entry never yet migrated.
46
+ migrated_at: str | None = None
47
+ # Hand-set during review (pm-project-init's garbled-CSV-header fix recipe):
48
+ # header/provenance text stripped from the source file before migration,
49
+ # preserved verbatim here so cutover.archive_entries can carry it into the
50
+ # entry's pointer file (spec pdata/init-pointer-files). None when not applicable.
51
+ preface_text: str | None = None
43
52
 
44
53
  def __post_init__(self) -> None:
45
54
  if self.classification not in _VALID_CLASSIFICATIONS:
@@ -8,7 +8,7 @@ overwrite (that is rehydrate.py's job for the clean fast-forward case; this modu
8
8
 
9
9
  Never auto-merges, never silently keeps one side and discards the other — every record surfaced
10
10
  here needs an explicit choice from the caller (ultimately Chris, via the CLI/skill), matching the
11
- existing `pm-pdata-conflict-resolution` skill's single-file-conflict framing exactly. Every
11
+ existing `pm-pdata-resolve-conflicts` skill's single-file-conflict framing exactly. Every
12
12
  record: `apply_resolution` is all-or-nothing over the whole diff, for the reasons in its own
13
13
  docstring.
14
14
 
@@ -342,7 +342,7 @@ def _apply_resolution(
342
342
  f"record_id(s) {collisions}: id collision (the same id was independently assigned to "
343
343
  f"two unrelated records) — not resolvable as a local/dump choice, since either pick "
344
344
  f"would discard one side's real record; needs a manual, out-of-band fix (see the "
345
- f"pm-pdata-conflict-resolution skill, point 4)"
345
+ f"pm-pdata-resolve-conflicts skill, point 4)"
346
346
  )
347
347
 
348
348
  group_mismatches = sorted(rid for rid, rd in by_id.items() if rd.group_mismatch)
@@ -351,7 +351,7 @@ def _apply_resolution(
351
351
  f"record_id(s) {group_mismatches}: group mismatch (same id and created_at, but the "
352
352
  f"two sides disagree on record_group) — not resolvable as a local/dump choice. "
353
353
  f"Compare each side's content/file_path before assuming a rename is safe (see the "
354
- f"pm-pdata-conflict-resolution skill, point 5, for why it may instead be a "
354
+ f"pm-pdata-resolve-conflicts skill, point 5, for why it may instead be a "
355
355
  f"same-second id collision), then retry"
356
356
  )
357
357
 
@@ -310,7 +310,7 @@ def update_record(
310
310
  content and fields are independent — updating fields never re-serializes content, and vice
311
311
  versa. This is deliberate (it's what lets a --field-only update skip resending content), not
312
312
  a gap: a record group whose content is designed to mirror its fields is the caller's own
313
- duplication to keep in sync, on every write, by passing both. See pm-pdata-schema-design
313
+ duplication to keep in sync, on every write, by passing both. See pm-pdata-design-schema
314
314
  SKILL.md's "content and extension fields are never auto-synced" section."""
315
315
  if content is None and file_path is None and not fields:
316
316
  raise ValueError(
@@ -12,6 +12,15 @@ cd ~/cc/<project> && \
12
12
  claude -p "Check that you are executing with a ~/cc/<project> directory as your cwd, containing a CLAUDE.md. If you are not then exit. If you are then use this file as your prompt: <path-to-cc-session-tools>/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md"
13
13
  ```
14
14
 
15
+ **What matters is a fresh context, not literally a new process.** The reason this prompt asks
16
+ for a new session is a genuinely fresh, unbiased read that won't rationalize away a stale
17
+ reference it half-remembers writing earlier in the same conversation. An orchestrating session
18
+ that just ran `ccst pdata init --write` can dispatch this prompt as an `Agent` subagent (with its
19
+ cwd set to the project root) instead of shelling out to a literal `claude -p` process — a
20
+ dispatched subagent gets the same fresh-context benefit without leaving the orchestrating
21
+ session's control flow. Either is acceptable; what is not acceptable is running this prompt
22
+ inline in the same context that just did the migration.
23
+
15
24
  ---
16
25
 
17
26
  ## Step 1 — Verify project context
@@ -12,6 +12,15 @@ cd ~/cc/<project> && \
12
12
  claude -p "Check that you are executing with a ~/cc/<project> directory as your cwd. If you are not then exit. If you are then use this file as your prompt: <path-to-cc-session-tools>/src/cc_session_tools/prompts/pdata-migration-skills-update.md"
13
13
  ```
14
14
 
15
+ **What matters is a fresh context, not literally a new process.** The reason this prompt asks
16
+ for a new session is a genuinely fresh, unbiased read that won't rationalize away a stale
17
+ reference it half-remembers writing earlier in the same conversation. An orchestrating session
18
+ that just ran `ccst pdata init --write` can dispatch this prompt as an `Agent` subagent (with its
19
+ cwd set to the project root) instead of shelling out to a literal `claude -p` process — a
20
+ dispatched subagent gets the same fresh-context benefit without leaving the orchestrating
21
+ session's control flow. Either is acceptable; what is not acceptable is running this prompt
22
+ inline in the same context that just did the migration.
23
+
15
24
  ---
16
25
 
17
26
  ## Step 1 — Verify project context
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: pm-pdata-schema-design
2
+ name: pm-pdata-design-schema
3
3
  description: Use before writing a genuinely new kind of structured data into any project's ccst pdata store - deciding whether it fits an existing record_group, needs a brand-new group, and whether that group needs an extension table or free-text content suffices. Triggers on "I need to track a new kind of record", "what record_group should this be", "does this need an extension table", "add a new field to ccst pdata", or any session about to call `ccst pdata add`/`schema add-field` for a record shape it hasn't used before. Prevents lazy catch-all groups (`notes`/`misc`) that silently recreate the EAV undifferentiated-bucket problem the schema deliberately rejected.
4
4
  ---
5
5
 
@@ -1,10 +1,16 @@
1
1
  ---
2
2
  name: pm-pdata-do-audit-and-prepare-to-migrate
3
- description: Full consistency/completeness audit of a project's central and recordkeeping files - OneDrive sync-conflict cleanup, cross-checking every index/register/log against what it claims to describe, and a pdata-migration readiness pass. Ends by writing a readiness manifest file that the companion `pm-pdata-do-migrate` skill consumes to actually run the migration. Triggers on "review all my central files", "audit the project files", "check everything is up to date and consistent", "get this project ready for pdata", "/pm-pdata-do-audit-and-prepare-to-migrate", or any request for a thorough review/health-check of a project's index/log/register files (as opposed to a single-session wrap-up, which is pm-update-central-files). Especially relevant when a project is scheduled to migrate to pdata - this audit is the recommended prerequisite pass, since a schema design should not be built against files that still have unresolved sync-conflict duplicates or known dangling rows.
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-project-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
4
  ---
5
5
 
6
6
  # Audit central files
7
7
 
8
+ > **This audit feeds `ccst pdata init`, not a manual migration.** The readiness manifest this
9
+ > skill produces (Phase 5) is a pre-flight pass for `ccst pdata init` (driven by the
10
+ > `pm-project-init` skill) - the tool-native, default migration mechanism. `pm-pdata-do-manual-migration`
11
+ > is a deprecated fallback for record shapes `ccst pdata init` genuinely can't express; it is not
12
+ > the default next step after this audit.
13
+
8
14
  ## When to use this vs. `pm-update-central-files`
9
15
 
10
16
  `pm-update-central-files` is a lightweight, single-session wrap-up: rewrite WORKLOG, apply
@@ -115,7 +121,7 @@ Give the user a single reviewable delete script for every confirmed-safe deletio
115
121
  ## Phase 4: pdata-readiness pass
116
122
 
117
123
  Compile a dedicated `pdata-readiness-notes.md` (or equivalent) listing, file by file, every
118
- blocker a future `pm-pdata-schema-design` session would hit if it tried to design a schema
124
+ blocker a future `pm-pdata-design-schema` session would hit if it tried to design a schema
119
125
  against the file as it currently stands:
120
126
 
121
127
  - Inconsistent separator/delimiter use within one logical column.
@@ -131,10 +137,10 @@ against the file as it currently stands:
131
137
  means something different from a genuinely blank one).
132
138
 
133
139
  This file is deliberately NOT a migration plan - it's a findings list for whoever runs the actual
134
- `pm-pdata-schema-design` session next, so that work isn't designed against a still-messy
140
+ `pm-pdata-design-schema` session next, so that work isn't designed against a still-messy
135
141
  substrate.
136
142
 
137
- ## Phase 5: write the readiness manifest (for `pm-pdata-do-migrate` to consume)
143
+ ## Phase 5: write the readiness manifest (for `ccst pdata init`/`pm-project-init` to consume)
138
144
 
139
145
  Once the audit reaches a settled state (per your own stopping rule - typically two consecutive
140
146
  clean verification rounds, or a documented pragmatic stop), write one machine-and-human-readable
@@ -145,7 +151,9 @@ otherwise the project's own top-level docs location. Its existence and location
145
151
  recorded in the project's own instructions file (`CLAUDE.md`/`AGENTS.md`/equivalent) so a future
146
152
  session doesn't have to search for it.
147
153
 
148
- The manifest must have, in this order, so `pm-pdata-do-migrate` can parse it deterministically:
154
+ The manifest must have, in this order, so a `pm-project-init` session running `ccst pdata init`
155
+ (or, for the narrow record shapes it can't express, the deprecated `pm-pdata-do-manual-migration`
156
+ fallback) can parse it deterministically:
149
157
 
150
158
  1. **Header**: audit date, session/agent identifier, and an explicit "STALE IF" note - state the
151
159
  condition that would invalidate this manifest (e.g. "stale if any of the files below have
@@ -153,8 +161,8 @@ The manifest must have, in this order, so `pm-pdata-do-migrate` can parse it det
153
161
  2. **File inventory**: every central/index/recordkeeping file the audit covered, with: full path,
154
162
  row/entry count, last-verified-clean date, and its role (`index`, `register`, `log`,
155
163
  `narrative`, `derived-artefact`).
156
- 3. **Per-file migration blockers**, in the exact shape `pm-pdata-schema-design` and
157
- `pm-pdata-do-migrate` expect to consume - this is the same content this skill's own Phase 4
164
+ 3. **Per-file migration blockers**, in the exact shape `pm-pdata-design-schema` and
165
+ `pm-project-init` expect to consume - this is the same content this skill's own Phase 4
158
166
  already produces, just formalised into the manifest rather than a standalone prose report:
159
167
  inconsistent separators, unstable/non-existent natural keys, mixed date formats, multi-schema
160
168
  single files, machine-specific paths, null-value conventions that aren't real nulls.
@@ -165,7 +173,8 @@ The manifest must have, in this order, so `pm-pdata-do-migrate` can parse it det
165
173
  a migration pass doesn't waste time re-flagging it or, worse, silently drops one copy.
166
174
  6. **Open decisions** - anything the audit found but explicitly did NOT resolve because it needed
167
175
  the project owner's judgement (scope calls, curation completeness, which of two forks is
168
- canonical). List these plainly; `pm-pdata-do-migrate` must not treat an unresolved item as settled.
176
+ canonical). List these plainly; a later migration session must not treat an unresolved item as
177
+ settled.
169
178
 
170
179
  ## Common mistakes
171
180
 
@@ -1,17 +1,33 @@
1
1
  ---
2
- name: pm-pdata-do-migrate
3
- description: Execute a project's actual migration from flat central/index files to a ccst pdata record store - build a migration plan, adversarially verify the plan itself, present it for the user's confirmation, then execute it and write a results file. Consumes the readiness manifest pm-pdata-do-audit-and-prepare-to-migrate produces if one exists, but always explores the project independently too, so it works even without that manifest. Triggers on "migrate this project to pdata", "run the pdata migration", "/pm-pdata-do-migrate", or any request to actually move a project's CSV/index/log files into pdata (as opposed to just auditing readiness, which is pm-pdata-do-audit-and-prepare-to-migrate, or designing the target schema in the abstract, which is pm-pdata-schema-design).
2
+ name: pm-pdata-do-manual-migration
3
+ description: Execute a project's actual migration from flat central/index files to a ccst pdata record store - build a migration plan, adversarially verify the plan itself, present it for the user's confirmation, then execute it and write a results file. Consumes the readiness manifest pm-pdata-do-audit-and-prepare-to-migrate produces if one exists, but always explores the project independently too, so it works even without that manifest. Triggers on "migrate this project to pdata", "run the pdata migration", "/pm-pdata-do-manual-migration", or any request to actually move a project's CSV/index/log files into pdata (as opposed to just auditing readiness, which is pm-pdata-do-audit-and-prepare-to-migrate, or designing the target schema in the abstract, which is pm-pdata-design-schema).
4
4
  ---
5
5
 
6
- # Migrate a project to pdata
6
+ # Manually migrate a project to pdata (deprecated fallback)
7
+
8
+ > **Deprecated fallback — `ccst pdata init` is the standard mechanism.** `ccst pdata init`
9
+ > (driven by the `pm-project-init` skill) is the tool-native migration mechanism: classification,
10
+ > a human-reviewable manifest, backup, verify, atomic cutover, and shipped post-write doc/skill-
11
+ > update prompts. It is the default choice wherever it is available, and this skill is superseded
12
+ > by it for any file `ccst pdata init`'s classifier can already handle (a normal CSV/JSON file
13
+ > with a real header row). This skill remains only as a deprecated manual fallback for the narrow
14
+ > case where the programmatic approach genuinely doesn't fit - e.g. a record shape none of `ccst
15
+ > pdata init`'s five import strategies (`whole-file`, `delimited-sections`, `csv-rows`,
16
+ > `json-array-rows`, `json-singleton`) can express.
17
+ >
18
+ > **Before doing anything else, stop and get the user's explicit confirmation** that: (a) they
19
+ > understand `ccst pdata init` is the standard, tool-native mechanism and this skill is a
20
+ > deprecated fallback; and (b) they have a specific reason the programmatic approach doesn't fit
21
+ > this case. Do not proceed to Step 1, or any later step, without that confirmation - if the user
22
+ > hasn't already stated a reason, ask them for one before continuing.
7
23
 
8
24
  ## When to use this vs. the other pm-pdata-* skills
9
25
 
10
26
  - `pm-pdata-do-audit-and-prepare-to-migrate` - readiness audit. Finds and fixes problems in the CURRENT flat files.
11
27
  Produces a readiness manifest. Does not touch pdata at all.
12
- - `pm-pdata-schema-design` - designs the TARGET record-group schema in the abstract (field names,
28
+ - `pm-pdata-design-schema` - designs the TARGET record-group schema in the abstract (field names,
13
29
  types, groupings) before any data moves.
14
- - `pm-pdata-do-migrate` (this skill) - takes whatever schema exists (designed via the skill above,
30
+ - `pm-pdata-do-manual-migration` (this skill) - takes whatever schema exists (designed via the skill above,
15
31
  or designed inline as part of this skill's own plan if no separate design session happened) and
16
32
  actually moves the data: `ccst pdata add`/`batch` calls, verification, and a results record.
17
33
 
@@ -43,7 +59,7 @@ audit pass would.
43
59
 
44
60
  ## Step 2: design or confirm the target schema
45
61
 
46
- For each central file being migrated, decide (or confirm, if `pm-pdata-schema-design` already
62
+ For each central file being migrated, decide (or confirm, if `pm-pdata-design-schema` already
47
63
  ran):
48
64
 
49
65
  - **Record group name** - typically the file's own conceptual name (`correspondence-log`,
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: pm-pdata-conflict-resolution
2
+ name: pm-pdata-resolve-conflicts
3
3
  description: Use immediately when `ccst pdata update` or `ccst pdata delete` exits 3 (a version conflict) - takes the CLI's current-vs-attempted diff and presents it to Chris for reconciliation, per spec §6.2's "the session asks Chris how to reconcile" contract. Also use when `ccst pdata resolve` reports a cross-machine fork (a `.pdata-db-dump` conflict between two laptops), or phrasing like "pdata update conflict", "version conflict on a record", "someone else already updated this row", "sync conflict", "pdata fork between machines". Never auto-retries, never silently picks a side, never discards either version.
4
4
  ---
5
5