cc-session-tools 3.9.2__tar.gz → 3.10.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 (298) hide show
  1. {cc_session_tools-3.9.2/src/cc_session_tools.egg-info → cc_session_tools-3.10.1}/PKG-INFO +1 -1
  2. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/pyproject.toml +3 -3
  3. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/ccst.py +21 -52
  4. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/cutover.py +9 -1
  5. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/init_service.py +196 -97
  6. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-pdata-do-init/SKILL.md +16 -0
  7. cc_session_tools-3.10.1/src/cc_session_tools/skills/pm-pdata-do-update-consuming-skills/SKILL.md +114 -0
  8. cc_session_tools-3.10.1/src/cc_session_tools/skills/pm-pdata-do-update-project-docs/SKILL.md +103 -0
  9. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1/src/cc_session_tools.egg-info}/PKG-INFO +1 -1
  10. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools.egg-info/SOURCES.txt +3 -2
  11. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_bundle_discovery.py +35 -62
  12. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_init_cli.py +15 -9
  13. cc_session_tools-3.10.1/tests/test_pdata_post_migration_skills.py +123 -0
  14. cc_session_tools-3.9.2/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +0 -134
  15. cc_session_tools-3.9.2/src/cc_session_tools/prompts/pdata-migration-skills-update.md +0 -137
  16. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/LICENSE +0 -0
  17. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/README.md +0 -0
  18. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/setup.cfg +0 -0
  19. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/__init__.py +0 -0
  20. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/__init__.py +0 -0
  21. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/ccd.py +0 -0
  22. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/ccmsg.py +0 -0
  23. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/ccr.py +0 -0
  24. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/ccs.py +0 -0
  25. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/ccsched.py +0 -0
  26. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
  27. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
  28. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
  29. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
  30. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/config/hooks-bundle.json +0 -0
  31. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/config/session-tag-hook.json +0 -0
  32. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/hooks_install.py +0 -0
  33. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/__init__.py +0 -0
  34. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/bun_tools.py +0 -0
  35. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/claude_flags.py +0 -0
  36. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/claude_md_install.py +0 -0
  37. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/context_overrides.py +0 -0
  38. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/db.py +0 -0
  39. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/debug.py +0 -0
  40. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/doctor.py +0 -0
  41. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
  42. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/hook_registry.py +0 -0
  43. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/install_sync.py +0 -0
  44. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/levenshtein.py +0 -0
  45. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/machine_identity.py +0 -0
  46. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
  47. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
  48. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
  49. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/lock.py +0 -0
  50. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/message.py +0 -0
  51. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
  52. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/repository.py +0 -0
  53. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/retention.py +0 -0
  54. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/service.py +0 -0
  55. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/messaging/store.py +0 -0
  56. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/occupancy.py +0 -0
  57. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/paths.py +0 -0
  58. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
  59. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/backup.py +0 -0
  60. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/classify.py +0 -0
  61. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/dump.py +0 -0
  62. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
  63. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/importers.py +0 -0
  64. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
  65. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/manifest.py +0 -0
  66. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/naming.py +0 -0
  67. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/readiness.py +0 -0
  68. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
  69. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
  70. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
  71. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/repository.py +0 -0
  72. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
  73. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/service.py +0 -0
  74. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
  75. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/store.py +0 -0
  76. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
  77. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
  78. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
  79. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
  80. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
  81. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/verify.py +0 -0
  82. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
  83. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/picker.py +0 -0
  84. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/proc_lock.py +0 -0
  85. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/prompts.py +0 -0
  86. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/roots.py +0 -0
  87. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/rules.py +0 -0
  88. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
  89. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
  90. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
  91. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
  92. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
  93. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/due.py +0 -0
  94. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
  95. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
  96. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
  97. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
  98. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
  99. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
  100. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
  101. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
  102. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/state.py +0 -0
  103. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/store.py +0 -0
  104. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
  105. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
  106. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
  107. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/session_gc.py +0 -0
  108. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/sessions.py +0 -0
  109. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/sessions_db.py +0 -0
  110. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/sessions_repair.py +0 -0
  111. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/shell_install.py +0 -0
  112. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/tasklist.py +0 -0
  113. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/telemetry_store.py +0 -0
  114. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/lib/text_wrap.py +0 -0
  115. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
  116. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
  117. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
  118. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
  119. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
  120. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
  121. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
  122. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
  123. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/extract-pptx/SKILL.md +0 -0
  124. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/extract-pptx/scripts/extract_pptx.py +0 -0
  125. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/extract-to-text/SKILL.md +0 -0
  126. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/extract-to-text/scripts/extract_to_text.py +0 -0
  127. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
  128. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
  129. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
  130. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
  131. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
  132. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
  133. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
  134. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
  135. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
  136. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
  137. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-pdata-design-schema/SKILL.md +0 -0
  138. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +0 -0
  139. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-pdata-do-manual-migration/SKILL.md +0 -0
  140. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-pdata-resolve-conflicts/SKILL.md +0 -0
  141. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +0 -0
  142. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
  143. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
  144. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
  145. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
  146. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
  147. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
  148. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
  149. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
  150. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
  151. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
  152. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
  153. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
  154. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
  155. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
  156. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
  157. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools.egg-info/requires.txt +0 -0
  158. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/cc_session_tools.egg-info/top_level.txt +0 -0
  159. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/__init__.py +0 -0
  160. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/attribution.py +0 -0
  161. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/cache.py +0 -0
  162. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/ccusage_wrapper.py +0 -0
  163. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/cli.py +0 -0
  164. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/data/pricing.json +0 -0
  165. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/parent_inference.py +0 -0
  166. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/parser.py +0 -0
  167. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/pricing.py +0 -0
  168. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/query.py +0 -0
  169. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/report.py +0 -0
  170. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/schema.py +0 -0
  171. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/claude_code_usage/session_names.py +0 -0
  172. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/__init__.py +0 -0
  173. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/after_response.py +0 -0
  174. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/bash_hard_deny.py +0 -0
  175. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/bash_security_review.py +0 -0
  176. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/cache.py +0 -0
  177. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/catchup.py +0 -0
  178. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/confirm_8digit.py +0 -0
  179. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/context_window_warning.py +0 -0
  180. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/last_screenshot.py +0 -0
  181. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/marker_allow.py +0 -0
  182. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/markers.py +0 -0
  183. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/messaging_deliver.py +0 -0
  184. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/model_info.py +0 -0
  185. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/normalise.py +0 -0
  186. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/pdata_sync.py +0 -0
  187. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/pending_migration.py +0 -0
  188. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/pending_rename.py +0 -0
  189. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/script_allowlist.py +0 -0
  190. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/session_tag.py +0 -0
  191. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/stats.py +0 -0
  192. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/telemetry.py +0 -0
  193. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/telemetry_query.py +0 -0
  194. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/telemetry_trim.py +0 -0
  195. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/transcript.py +0 -0
  196. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/src/hooks/worklog_guard.py +0 -0
  197. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_after_response.py +0 -0
  198. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_attribution.py +0 -0
  199. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_bash_hard_deny.py +0 -0
  200. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_bash_security_review.py +0 -0
  201. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_bash_security_review_allowlist.py +0 -0
  202. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_bun_tools.py +0 -0
  203. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cache.py +0 -0
  204. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cache_sqlite.py +0 -0
  205. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccr_orphans.py +0 -0
  206. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccs_emptiness.py +0 -0
  207. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccs_eta.py +0 -0
  208. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccs_list_mode.py +0 -0
  209. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccs_scope_flags.py +0 -0
  210. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccs_sentinel_sort.py +0 -0
  211. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccs_session_counts.py +0 -0
  212. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
  213. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_cli.py +0 -0
  214. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_context_override_cli.py +0 -0
  215. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_doctor.py +0 -0
  216. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_gc_prune.py +0 -0
  217. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_gc_report.py +0 -0
  218. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_hook_dispatcher.py +0 -0
  219. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_hooks_allowlist.py +0 -0
  220. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_hooks_install_prune.py +0 -0
  221. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
  222. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_hooks_uninstall.py +0 -0
  223. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_install_everything.py +0 -0
  224. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_machine_identity_cli.py +0 -0
  225. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_main_gate.py +0 -0
  226. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_cli.py +0 -0
  227. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_readiness_cli.py +0 -0
  228. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
  229. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
  230. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
  231. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_sync_cli.py +0 -0
  232. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_pdata_verify_cli.py +0 -0
  233. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_repair_sessions_cli.py +0 -0
  234. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_sessions_cli.py +0 -0
  235. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_shell_install.py +0 -0
  236. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_skills_install.py +0 -0
  237. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_skills_uninstall.py +0 -0
  238. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_telemetry_query.py +0 -0
  239. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccst_telemetry_trim.py +0 -0
  240. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_ccusage_wrapper.py +0 -0
  241. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_claude_flags.py +0 -0
  242. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_claude_md_install.py +0 -0
  243. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cli.py +0 -0
  244. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cli_ccd.py +0 -0
  245. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cli_ccd_dry_run.py +0 -0
  246. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cli_ccr.py +0 -0
  247. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cli_ccs.py +0 -0
  248. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_cli_version.py +0 -0
  249. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_confirm_8digit.py +0 -0
  250. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_context_overrides.py +0 -0
  251. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_context_window_warning.py +0 -0
  252. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_db.py +0 -0
  253. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_debug.py +0 -0
  254. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_doctor_mutes.py +0 -0
  255. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_empty_session.py +0 -0
  256. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_hook_cache.py +0 -0
  257. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_hooks_install.py +0 -0
  258. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_hooks_install_records.py +0 -0
  259. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_install_sync.py +0 -0
  260. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_last_screenshot.py +0 -0
  261. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_levenshtein.py +0 -0
  262. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_machine_identity.py +0 -0
  263. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_marker_allow.py +0 -0
  264. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_migrate_ccmsg.py +0 -0
  265. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_migrate_ccsched.py +0 -0
  266. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_migrate_sessions_db.py +0 -0
  267. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_migrate_telemetry.py +0 -0
  268. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_model_info.py +0 -0
  269. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_normalise.py +0 -0
  270. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_occupancy.py +0 -0
  271. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_parent_inference.py +0 -0
  272. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_parser.py +0 -0
  273. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_paths.py +0 -0
  274. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_pdata_audit_skill.py +0 -0
  275. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_pending_migration_hook.py +0 -0
  276. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_picker.py +0 -0
  277. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_pricing.py +0 -0
  278. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_proc_lock.py +0 -0
  279. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_prompts.py +0 -0
  280. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_query.py +0 -0
  281. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_report.py +0 -0
  282. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_roots.py +0 -0
  283. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_rules.py +0 -0
  284. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_scheduler_bundled_jobs.py +0 -0
  285. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_schema.py +0 -0
  286. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_script_allowlist.py +0 -0
  287. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_session_names.py +0 -0
  288. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_session_tag.py +0 -0
  289. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_sessions.py +0 -0
  290. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_sessions_db.py +0 -0
  291. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_sessions_db_migration_chain.py +0 -0
  292. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_sessions_repair.py +0 -0
  293. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_tasklist.py +0 -0
  294. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_telemetry.py +0 -0
  295. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_telemetry_store.py +0 -0
  296. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_text_wrap.py +0 -0
  297. {cc_session_tools-3.9.2 → cc_session_tools-3.10.1}/tests/test_transcript.py +0 -0
  298. {cc_session_tools-3.9.2 → cc_session_tools-3.10.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.9.2
3
+ Version: 3.10.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.9.2"
7
+ version = "3.10.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" }
@@ -62,11 +62,11 @@ where = ["src"]
62
62
  # only filters within the file's *own* discovered package, and a wrongly
63
63
  # auto-discovered "cc_session_tools.skills.<name>.tests" package was never
64
64
  # covered by an exclude-package-data entry keyed just "cc_session_tools").
65
- exclude = ["cc_session_tools.skills", "cc_session_tools.skills.*", "cc_session_tools.config", "cc_session_tools.config.*", "cc_session_tools.prompts", "cc_session_tools.prompts.*"]
65
+ exclude = ["cc_session_tools.skills", "cc_session_tools.skills.*", "cc_session_tools.config", "cc_session_tools.config.*"]
66
66
 
67
67
  [tool.setuptools.package-data]
68
68
  claude_code_usage = ["data/*.json"]
69
- cc_session_tools = ["skills/**/*", "config/*", "prompts/**/*"]
69
+ cc_session_tools = ["skills/**/*", "config/*"]
70
70
 
71
71
  [tool.setuptools.exclude-package-data]
72
72
  cc_session_tools = ["**/tests/*", "**/__pycache__/*"]
@@ -160,56 +160,25 @@ def _discover_bundle() -> Path:
160
160
  )
161
161
 
162
162
 
163
- def _discover_prompts_dir() -> Path:
164
- """Return the bundled prompts/ directory (pdata-migration follow-up prompts).
165
-
166
- prompts/ is packaged inside cc_session_tools the same way skills/ and config/ are (see
167
- _discover_source_dir) - src/cc_session_tools/cli/ccst.py -> ../prompts. No install-location
168
- fallback, same reasoning as the other two: a fallback here would just mask a broken install.
169
- """
170
- candidate = Path(__file__).resolve().parent.parent / "prompts"
171
- if candidate.is_dir():
172
- return candidate
173
- raise FileNotFoundError(
174
- "Cannot locate bundled prompts/ directory. Run from the source tree or reinstall ccst."
175
- )
176
-
177
-
178
- def _print_migration_prompt_reminders(
179
- project_root: Path, *labeled_filenames: tuple[str, str]
163
+ def _print_post_migration_skill_reminders(
164
+ project_root: Path, *labeled_skills: tuple[str, str]
180
165
  ) -> None:
181
- """Print one "<label>: <path>" reminder line per (label, filename) pair, for the bundled
182
- prompts/ directory, followed by a line telling the user to run it in a fresh context (a new
183
- Claude Code session, or a dispatched Agent subagent) started in project_root.
184
-
185
- Each prompt's own "Run it via" section already spells out the `cd <project> && claude -p
186
- ...` invocation (see e.g. pdata-migration-skills-update.md) - that only works if the
187
- session's cwd is the project root, since every prompt's Step 1 checks for it and aborts
188
- otherwise. What the fresh-context requirement is actually protecting against is an
189
- orchestrating session rationalizing away a stale reference it half-remembers writing
190
- earlier in the same conversation - a dispatched Agent subagent (same cwd) gets that same
191
- benefit without shelling out to a literal second process, so both are offered here rather
192
- than only the `claude -p` invocation. Printing the reminder without saying so reads as "open
193
- this file", which invites running it inline in whatever session called `ccst pdata init` -
194
- the wrong cwd entirely.
195
-
196
- Best-effort, deliberately swallowing a missing prompts/ directory: these reminders are a
197
- nicety layered on top of an otherwise-already-completed classification or migration, not
198
- the operation itself — a broken/partial install must never turn a successful --write (rows
199
- already written, files already cut over) or dry-run (proposal already written) into a
200
- reported failure just because this follow-up reminder couldn't be printed.
166
+ """Print one "<label>: <skill-name>" reminder per (label, skill) pair, each followed by a
167
+ line telling the user to run it in a fresh context rooted at project_root.
168
+
169
+ Each skill's Step 1 checks that cwd is the project root and aborts otherwise, so a bare skill
170
+ name invites running it inline in whatever session called `ccst pdata init` - the wrong cwd,
171
+ and the wrong context: the fresh-context requirement protects against an orchestrating
172
+ session rationalizing away a stale reference it half-remembers writing earlier in the same
173
+ conversation. A dispatched Agent subagent whose prompt starts with a `cd` into project_root
174
+ gets that same benefit as a new Claude Code session, so both are offered.
201
175
  """
202
- try:
203
- prompts_dir = _discover_prompts_dir()
204
- except FileNotFoundError as exc:
205
- print(f"({exc})", file=sys.stderr)
206
- return
207
- for label, filename in labeled_filenames:
208
- print(f"{label}: {prompts_dir / filename}")
176
+ for label, skill in labeled_skills:
177
+ print(f"{label}: {skill}")
209
178
  print(
210
- f" Run in a new Claude Code session started in {project_root} (its own "
211
- f"\"Run it via\" section has the exact command), or dispatch it as an Agent "
212
- f"subagent with the same cwd - not inline in this session."
179
+ f" Run it in a fresh context rooted at {project_root}: dispatch an Agent subagent "
180
+ f"whose prompt starts with `cd {project_root}`, or start a new Claude Code session "
181
+ f"there - not inline in this session."
213
182
  )
214
183
 
215
184
 
@@ -1236,9 +1205,9 @@ def _cmd_pdata_init(args: argparse.Namespace) -> int:
1236
1205
  # apply here.
1237
1206
  return 0
1238
1207
  print(f"\nProposal: {result.proposal_path}")
1239
- _print_migration_prompt_reminders(
1208
+ _print_post_migration_skill_reminders(
1240
1209
  result.proposal_path.parent,
1241
- ("After a successful --write, update project docs", "pdata-migration-claude-md-update.md"),
1210
+ ("After a successful --write, update project docs", "pm-pdata-do-update-project-docs"),
1242
1211
  )
1243
1212
  return 0
1244
1213
 
@@ -1286,10 +1255,10 @@ def _cmd_pdata_init(args: argparse.Namespace) -> int:
1286
1255
  print()
1287
1256
  print(write_result.report) # spec §7.1 step 4's diff report, for review
1288
1257
  print(f"\nVerify: ccst pdata verify --project {args.project} --full")
1289
- _print_migration_prompt_reminders(
1258
+ _print_post_migration_skill_reminders(
1290
1259
  project_root,
1291
- ("Update project docs", "pdata-migration-claude-md-update.md"),
1292
- ("Update consuming skills", "pdata-migration-skills-update.md"),
1260
+ ("Update project docs", "pm-pdata-do-update-project-docs"),
1261
+ ("Update consuming skills", "pm-pdata-do-update-consuming-skills"),
1293
1262
  )
1294
1263
  print("SUCCESS")
1295
1264
  return 0
@@ -2,6 +2,7 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import time
5
+ from collections.abc import Callable
5
6
  from pathlib import Path
6
7
 
7
8
  from cc_session_tools.lib.pdata.init_paths import (
@@ -59,6 +60,7 @@ def _write_pointer_file(*, project_root: Path, project: str, entry: ManifestEntr
59
60
  def archive_entries(
60
61
  *, project_root: Path, entries: list[ManifestEntry], project: str,
61
62
  write_pointer_files: bool = True,
63
+ on_archived: Callable[[ManifestEntry], None] | None = None,
62
64
  ) -> None:
63
65
  """Move every db-owned entry's source file into project_root/.pdata-migrated/,
64
66
  preserving its relative path, and append one line per entry to MANIFEST.md.
@@ -69,7 +71,11 @@ def archive_entries(
69
71
  Markdown pointer file back at each entry's original path so anything else in the
70
72
  project that cites the old path by name finds a live signpost instead of a silent
71
73
  404. Pass write_pointer_files=False (--leave-no-pointer-files at the CLI) to
72
- suppress this."""
74
+ suppress this.
75
+
76
+ `on_archived`, if given, is called with each entry immediately after its source has been
77
+ moved (before its log line and pointer file are written), so a caller that sees this
78
+ function raise part-way knows exactly which entries were already cut over."""
73
79
  if not entries:
74
80
  return
75
81
  archive_root = project_root / MIGRATED_ARCHIVE_DIRNAME
@@ -82,6 +88,8 @@ def archive_entries(
82
88
  destination = archive_root / entry.path
83
89
  destination.parent.mkdir(parents=True, exist_ok=True)
84
90
  source.rename(destination)
91
+ if on_archived is not None:
92
+ on_archived(entry)
85
93
  log.write(
86
94
  f"- {now} — {entry.path} — migrated source, superseded by ccst pdata "
87
95
  f"(record_group={entry.db_group()})\n"
@@ -213,6 +213,69 @@ def _rollback(*, project: str, created_ids: list[int]) -> list[str]:
213
213
  return rollback_failures
214
214
 
215
215
 
216
+ def _rollback_after_error(
217
+ exc: BaseException, *, project: str, created_ids: list[int],
218
+ on_progress: Callable[[str], None] | None,
219
+ ) -> None:
220
+ """Roll back `created_ids` after an error `write()` did not anticipate, without ever
221
+ replacing `exc` (the caller re-raises it): a rollback that itself fails is reported through
222
+ `on_progress` and attached to `exc` as a note naming every record id still live, so the
223
+ manual cleanup the failure implies is not a hunt."""
224
+ if not created_ids:
225
+ return
226
+ try:
227
+ failures = _rollback(project=project, created_ids=created_ids)
228
+ except Exception as rollback_exc:
229
+ note = (
230
+ f"rollback of {len(created_ids)} inserted row(s) aborted: {rollback_exc!r} - rows "
231
+ f"from this run may still be live; record ids: {created_ids}"
232
+ )
233
+ exc.add_note(note)
234
+ _emit(on_progress, note)
235
+ return
236
+ rolled_back = len(created_ids) - len(failures)
237
+ _emit(
238
+ on_progress,
239
+ f"Unexpected {type(exc).__name__} - rolled back {rolled_back} of "
240
+ f"{len(created_ids)} inserted row(s)",
241
+ )
242
+ if failures:
243
+ exc.add_note("rows from this run still live:\n" + "\n".join(failures))
244
+ for failure in failures:
245
+ _emit(on_progress, f" {failure}")
246
+
247
+
248
+ def _recover_from_failed_cutover(
249
+ exc: BaseException, *, project: str, m: Manifest, proposal_path: Path,
250
+ written_entries: list[ManifestEntry], archived: list[ManifestEntry],
251
+ entry_rows: dict[str, list[tuple[int, ImportRow]]],
252
+ on_progress: Callable[[str], None] | None,
253
+ ) -> None:
254
+ """Cutover failed part-way. An entry whose source was already moved into the archive is cut
255
+ over: its rows are its live copy, so they stay and the entry is marked migrated (a rerun then
256
+ skips it). An entry still in place has its rows rolled back, so a rerun imports it once
257
+ rather than twice."""
258
+ if archived:
259
+ stamp = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
260
+ for entry in archived:
261
+ entry.migrated_at = stamp
262
+ try:
263
+ manifest.save(m, proposal_path)
264
+ except Exception as save_exc:
265
+ exc.add_note(
266
+ f"could not record {len(archived)} already cut-over entr(ies) as migrated in "
267
+ f"{proposal_path}: {save_exc!r}; mark these migrated by hand before re-running: "
268
+ f"{', '.join(e.path for e in archived)}"
269
+ )
270
+ archived_paths = {e.path for e in archived}
271
+ pending_ids = [
272
+ record_id
273
+ for entry in written_entries if entry.path not in archived_paths
274
+ for record_id, _ in entry_rows[entry.path]
275
+ ]
276
+ _rollback_after_error(exc, project=project, created_ids=pending_ids, on_progress=on_progress)
277
+
278
+
216
279
  def _check_db_not_locked(project: str) -> None:
217
280
  """Pre-flight concurrency guard (inter-session message 20260819T114156Z-3ec7,
218
281
  gap #2): refuse to start --write if another connection already holds a write
@@ -432,105 +495,125 @@ def write(
432
495
  db_owned = [e for e in m.entries if e.classification == "db-owned"]
433
496
  already_migrated = [e for e in db_owned if e.migrated_at is not None]
434
497
  to_import = [e for e in db_owned if e.migrated_at is None]
435
- _emit(on_progress, f"Importing {len(to_import)} file(s)...")
436
- if already_migrated:
437
- _emit(
438
- on_progress,
439
- f" skipping {len(already_migrated)} already-migrated file(s): "
440
- f"{', '.join(e.path for e in already_migrated)}",
498
+ # Everything from the first insert through the backup either returns a WriteResult
499
+ # (rolling back by hand) or, for any exception this code did not anticipate, rolls the
500
+ # inserted rows back here before re-raising - never leaves them live with the manifest
501
+ # unmarked and cutover unrun.
502
+ try:
503
+ _emit(on_progress, f"Importing {len(to_import)} file(s)...")
504
+ if already_migrated:
505
+ _emit(
506
+ on_progress,
507
+ f" skipping {len(already_migrated)} already-migrated file(s): "
508
+ f"{', '.join(e.path for e in already_migrated)}",
509
+ )
510
+ for entry in to_import:
511
+ try:
512
+ source_path = project_root / entry.path
513
+ if not source_path.exists():
514
+ # entry.migrated_at is None here (to_import excludes already-migrated
515
+ # entries above), so this is not the ordinary "already cut over by an
516
+ # earlier --write" case — the source is genuinely missing for an entry
517
+ # this run believes still needs importing. Fail with a specific,
518
+ # actionable message (still caught by this loop's own except clause
519
+ # below, so it contributes to `reasons`/rollback exactly like any other
520
+ # per-entry import failure) instead of a bare FileNotFoundError only
521
+ # discoverable via the rollback reasons list.
522
+ raise ValueError(
523
+ f"{entry.path}: marked db-owned with no migrated_at, but its source "
524
+ f"file does not exist at {source_path} — if this entry was already "
525
+ f"migrated by another means, set its classification to folder-owned "
526
+ f"(or otherwise correct the manifest) before running --write again"
527
+ )
528
+ _emit(on_progress, f" importing {entry.path} -> group={entry.db_group()}...")
529
+ for spec in entry.fields:
530
+ service.schema_add_field(
531
+ project=project, record_group=entry.db_group(),
532
+ field_name=spec.name, sql_type=spec.sql_type,
533
+ description=spec.description, default=spec.default,
534
+ )
535
+ rows_for_entry: list[tuple[int, ImportRow]] = []
536
+ for row in import_entry(project_root, entry):
537
+ record = service.add_record(
538
+ project=project, record_group=entry.db_group(),
539
+ content=row.content, file_path=row.file_path,
540
+ fields=row.fields, created_at=row.created_at,
541
+ )
542
+ created_ids.append(record.id)
543
+ rows_for_entry.append((record.id, row))
544
+ entry_rows[entry.path] = rows_for_entry
545
+ _emit(on_progress, f" {len(rows_for_entry)} row(s) imported")
546
+ written_entries.append(entry)
547
+ except (ValueError, OSError, csv.Error) as exc:
548
+ reasons.append(f"{entry.path}: {exc}")
549
+
550
+ _emit(on_progress, "Verifying imported rows...")
551
+ reasons.extend(
552
+ _verify(project=project, project_root=project_root,
553
+ written_entries=written_entries, entry_rows=entry_rows)
441
554
  )
442
- for entry in to_import:
443
- try:
444
- source_path = project_root / entry.path
445
- if not source_path.exists():
446
- # entry.migrated_at is None here (to_import excludes already-migrated
447
- # entries above), so this is not the ordinary "already cut over by an
448
- # earlier --write" case — the source is genuinely missing for an entry
449
- # this run believes still needs importing. Fail with a specific,
450
- # actionable message (still caught by this loop's own except clause
451
- # below, so it contributes to `reasons`/rollback exactly like any other
452
- # per-entry import failure) instead of a bare FileNotFoundError only
453
- # discoverable via the rollback reasons list.
454
- raise ValueError(
455
- f"{entry.path}: marked db-owned with no migrated_at, but its source "
456
- f"file does not exist at {source_path} — if this entry was already "
457
- f"migrated by another means, set its classification to folder-owned "
458
- f"(or otherwise correct the manifest) before running --write again"
459
- )
460
- _emit(on_progress, f" importing {entry.path} -> group={entry.db_group()}...")
461
- for spec in entry.fields:
462
- service.schema_add_field(
463
- project=project, record_group=entry.db_group(),
464
- field_name=spec.name, sql_type=spec.sql_type,
465
- description=spec.description, default=spec.default,
466
- )
467
- rows_for_entry: list[tuple[int, ImportRow]] = []
468
- for row in import_entry(project_root, entry):
469
- record = service.add_record(
470
- project=project, record_group=entry.db_group(),
471
- content=row.content, file_path=row.file_path,
472
- fields=row.fields, created_at=row.created_at,
473
- )
474
- created_ids.append(record.id)
475
- rows_for_entry.append((record.id, row))
476
- entry_rows[entry.path] = rows_for_entry
477
- _emit(on_progress, f" {len(rows_for_entry)} row(s) imported")
478
- written_entries.append(entry)
479
- except (ValueError, OSError, csv.Error) as exc:
480
- reasons.append(f"{entry.path}: {exc}")
481
-
482
- _emit(on_progress, "Verifying imported rows...")
483
- reasons.extend(
484
- _verify(project=project, project_root=project_root,
485
- written_entries=written_entries, entry_rows=entry_rows)
486
- )
487
555
 
488
- if reasons:
489
- _emit(on_progress, "Verification failed — rolling back inserted rows...")
490
- return WriteResult(
491
- created_record_ids=[], entries_written=[], backup_path=None,
492
- failure=WriteFailure(
493
- reasons=reasons + _rollback(project=project, created_ids=created_ids)
494
- ),
556
+ if reasons:
557
+ _emit(on_progress, "Verification failed — rolling back inserted rows...")
558
+ return WriteResult(
559
+ created_record_ids=[], entries_written=[], backup_path=None,
560
+ failure=WriteFailure(
561
+ reasons=reasons + _rollback(project=project, created_ids=created_ids)
562
+ ),
563
+ )
564
+
565
+ # Still inside both overrides: a rehearsed run's backup must land in the
566
+ # rehearsal sandbox (backup_dir_override), never in the real backup dir.
567
+ _emit(on_progress, "Backing up project and database before cutover...")
568
+ try:
569
+ backup_path = backup.create_backup(
570
+ project=project, project_root=project_root, on_progress=on_progress,
571
+ )
572
+ _emit(on_progress, f"Backup written: {backup_path}")
573
+ except backup.BackupError as exc:
574
+ _emit(on_progress, "Backup failed — rolling back inserted rows...")
575
+ return WriteResult(
576
+ created_record_ids=[], entries_written=[], backup_path=None,
577
+ failure=WriteFailure(
578
+ reasons=[str(exc)] + _rollback(project=project, created_ids=created_ids)
579
+ ),
580
+ )
581
+ except BaseException as exc:
582
+ _rollback_after_error(
583
+ exc, project=project, created_ids=created_ids, on_progress=on_progress,
495
584
  )
585
+ raise
496
586
 
497
- # Still inside both overrides: a rehearsed run's backup must land in the
498
- # rehearsal sandbox (backup_dir_override), never in the real backup dir.
499
- _emit(on_progress, "Backing up project and database before cutover...")
587
+ _emit(on_progress, f"Cutting over {len(written_entries)} file(s)...")
588
+ archived: list[ManifestEntry] = []
500
589
  try:
501
- backup_path = backup.create_backup(
502
- project=project, project_root=project_root, on_progress=on_progress,
590
+ cutover.archive_entries(
591
+ project_root=project_root, entries=written_entries, project=project,
592
+ write_pointer_files=not leave_no_pointer_files, on_archived=archived.append,
503
593
  )
504
- _emit(on_progress, f"Backup written: {backup_path}")
505
- except backup.BackupError as exc:
506
- _emit(on_progress, "Backup failed — rolling back inserted rows...")
507
- return WriteResult(
508
- created_record_ids=[], entries_written=[], backup_path=None,
509
- failure=WriteFailure(
510
- reasons=[str(exc)] + _rollback(project=project, created_ids=created_ids)
511
- ),
594
+ # Mark every entry this run actually cut over as migrated, and persist that back to
595
+ # the manifest — write() only ever `manifest.load`s otherwise, so without this a
596
+ # later --write would re-attempt (and, pre-fix, roll back) these same now-archived
597
+ # entries. written_entries holds the same ManifestEntry objects m.entries does (see
598
+ # the `to_import`/`written_entries.append(entry)` loop above), so mutating here
599
+ # mutates m.
600
+ migrated_at = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
601
+ for entry in written_entries:
602
+ entry.migrated_at = migrated_at
603
+ manifest.save(m, proposal_path)
604
+ except BaseException as exc:
605
+ _recover_from_failed_cutover(
606
+ exc, project=project, m=m, proposal_path=proposal_path,
607
+ written_entries=written_entries, archived=archived, entry_rows=entry_rows,
608
+ on_progress=on_progress,
512
609
  )
513
-
514
- _emit(on_progress, f"Cutting over {len(written_entries)} file(s)...")
515
- cutover.archive_entries(
516
- project_root=project_root, entries=written_entries, project=project,
517
- write_pointer_files=not leave_no_pointer_files,
518
- )
519
- # Mark every entry this run actually cut over as migrated, and persist that back to
520
- # the manifest — write() only ever `manifest.load`s otherwise, so without this a later
521
- # --write would re-attempt (and, pre-fix, roll back) these same now-archived entries.
522
- # written_entries holds the same ManifestEntry objects m.entries does (see the
523
- # `to_import`/`written_entries.append(entry)` loop above), so mutating here mutates m.
524
- migrated_at = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
525
- for entry in written_entries:
526
- entry.migrated_at = migrated_at
527
- manifest.save(m, proposal_path)
528
- return WriteResult(
529
- created_record_ids=created_ids,
530
- entries_written=[e.path for e in written_entries],
531
- backup_path=backup_path, failure=None,
532
- report=_render_diff_report(written_entries=written_entries, entry_rows=entry_rows),
533
- )
610
+ raise
611
+ return WriteResult(
612
+ created_record_ids=created_ids,
613
+ entries_written=[e.path for e in written_entries],
614
+ backup_path=backup_path, failure=None,
615
+ report=_render_diff_report(written_entries=written_entries, entry_rows=entry_rows),
616
+ )
534
617
 
535
618
 
536
619
  def _verify(
@@ -544,12 +627,18 @@ def _verify(
544
627
  reasons: list[str] = []
545
628
  for entry in written_entries:
546
629
  rows = entry_rows[entry.path]
547
- expected_count = count_source_rows(project_root, entry)
548
- if len(rows) != expected_count:
630
+ try:
631
+ expected_count = count_source_rows(project_root, entry)
632
+ except (ValueError, OSError, csv.Error) as exc:
549
633
  reasons.append(
550
- f"{entry.path}: imported {len(rows)} row(s) but re-counting the source "
551
- f"gives {expected_count} — entry-count parity check failed"
634
+ f"{entry.path}: could not re-count the source rows for the parity check: {exc}"
552
635
  )
636
+ else:
637
+ if len(rows) != expected_count:
638
+ reasons.append(
639
+ f"{entry.path}: imported {len(rows)} row(s) but re-counting the source "
640
+ f"gives {expected_count} — entry-count parity check failed"
641
+ )
553
642
  for record_id, import_row in rows:
554
643
  record = service.get_record(project=project, record_id=record_id)
555
644
  assert record is not None, (
@@ -562,7 +651,17 @@ def _verify(
562
651
  )
563
652
  if record.file_path:
564
653
  target = project_root / record.file_path
565
- if not target.exists():
654
+ try:
655
+ resolves = target.exists()
656
+ except OSError as exc:
657
+ # A string the OS rejects outright (e.g. "File name too long") raises
658
+ # instead of returning False; it is the same "does not resolve" outcome.
659
+ reasons.append(
660
+ f"{entry.path}: record {record_id} file_path "
661
+ f"{record.file_path!r} cannot be checked under {project_root}: {exc}"
662
+ )
663
+ continue
664
+ if not resolves:
566
665
  reasons.append(
567
666
  f"{entry.path}: record {record_id} file_path "
568
667
  f"{record.file_path!r} does not resolve under {project_root}"
@@ -210,6 +210,22 @@ Step 3's garbled-header recipe) - so anything else in the project citing the old
210
210
  a live signpost instead of a silent 404. Pass `--leave-no-pointer-files` if this run shouldn't
211
211
  leave one (e.g. a rehearsal, or a project convention against extra files at migrated paths).
212
212
 
213
+ ### Then run both post-migration skills
214
+
215
+ After a successful `--write` (not after a dry-run or a rehearsal), update everything that still
216
+ describes or uses the old flat-file layout. Run these in order, each in its own fresh-context
217
+ `Agent` subagent whose prompt begins with `cd ~/cc/<project>` - not inline in this session, which
218
+ has just written the migration and would rationalize away stale references it remembers:
219
+
220
+ 1. `pm-pdata-do-update-project-docs` - rewrites the project's `CLAUDE.md` and other top-level docs
221
+ to name the pdata record_groups and `ccst pdata` commands, and reports folder-layout drift.
222
+ 2. `pm-pdata-do-update-consuming-skills` - finds skills that literally reference the old paths and
223
+ rewrites direct reads/writes to `ccst pdata` commands, flagging anything it cannot rewrite
224
+ mechanically.
225
+
226
+ Relay each subagent's report to the user, including anything flagged for a human decision. If a
227
+ later `--write` round adds more record groups, run both again; they are safe to re-run.
228
+
213
229
  ## 8. Rollback, if something's found wrong post-cutover
214
230
 
215
231
  There is no `--rollback` flag. Recover by hand:
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: pm-pdata-do-update-consuming-skills
3
+ description: Use after `ccst pdata init --write` has migrated a ~/cc/<project>'s flat files into its pdata store - finds Claude Code skills under ~/.claude/skills/ that literally reference the project's old flat-file paths, rewrites direct reads/writes to the equivalent `ccst pdata` commands, and flags anything that cannot be rewritten mechanically for a human decision. Matches by literal path only. Triggers - "update the skills that read the old <project> files", "fix skills that use the old paths after the pdata migration", "/pm-pdata-do-update-consuming-skills"; also run automatically as a step of pm-pdata-do-init. Do NOT use for the project's own docs (pm-pdata-do-update-project-docs) or the migration itself (pm-pdata-do-init).
4
+ ---
5
+
6
+ # pm-pdata-do-update-consuming-skills
7
+
8
+ Finds skills that read or write a project's old flat-file paths directly and switches them to the
9
+ equivalent `ccst pdata` command. Run it after `ccst pdata init --write` has cut a project's data
10
+ over.
11
+
12
+ ## Run this in a fresh context
13
+
14
+ Run this skill in a fresh context, not inline in the session that just ran `ccst pdata init
15
+ --write`. The point of a fresh read is that it cannot rationalize away a stale reference it
16
+ half-remembers writing earlier in the same conversation. Either is acceptable:
17
+
18
+ - An orchestrating session dispatches it as an `Agent` subagent. The Agent tool cannot set a cwd,
19
+ so the subagent's prompt must begin by changing into the project root (`cd ~/cc/<project>`).
20
+ - A new Claude Code session started in `~/cc/<project>`.
21
+
22
+ Running it inline in the migrating session is not acceptable.
23
+
24
+ ## Step 1 - Verify project context
25
+
26
+ Check the cwd is a `~/cc/<project>` project directory with a `CLAUDE.md`:
27
+
28
+ ```sh
29
+ pwd && basename "$(pwd)" && ls CLAUDE.md 2>/dev/null && echo "project CLAUDE.md confirmed"
30
+ ```
31
+
32
+ If `CLAUDE.md` is present, the `basename` output is the project name to search for in Step 2. If
33
+ not, print the following and stop:
34
+
35
+ > ERROR: This skill must be run from a `~/cc/<project>` directory containing a `CLAUDE.md`.
36
+ > Aborting without touching any skills.
37
+
38
+ ## Step 2 - Find every skill referencing this project's old flat-file paths
39
+
40
+ Search every skill's `SKILL.md` and any scripts under its directory for literal mentions of this
41
+ project's pre-migration paths:
42
+
43
+ ```sh
44
+ grep -rl "~/cc/<project>/<old-path>" ~/.claude/skills/*/ 2>/dev/null
45
+ grep -rl "cc/<project>/<old-path>" ~/.claude/skills/*/ 2>/dev/null # relative-path equivalents
46
+ ```
47
+
48
+ Substitute the project's actual name and the pre-migration path(s) from the project's
49
+ `ccst-pdata-init-write.log`, or from the classification report `ccst pdata init` produced. If
50
+ neither is available, check `<project-root>/.pdata-migrated/`: the migration archives the
51
+ original pre-cutover files there, unchanged, under their original relative paths. For example, a
52
+ project `home` that migrated its Tesco order history would search for `~/cc/home/data/tesco/*.csv`
53
+ or `home/data/tesco` across every skill's `SKILL.md` and scripts.
54
+
55
+ Match by literal path only. Do not widen the search to skills whose name merely sounds related to
56
+ the project or its domain: a skill named `do-tesco-shop` is not evidence on its own that it
57
+ touches this project's files. Only a literal path match is.
58
+
59
+ If Step 2 finds no matches at all, say so in the report and stop. There is nothing to do.
60
+
61
+ ## Step 3 - Classify each match
62
+
63
+ For each skill with a literal path match, read the matching lines and enough surrounding
64
+ code or prose to be sure which category applies. Do not guess from the skill's name or its
65
+ one-line description.
66
+
67
+ - **Direct read/write** - the skill's script (or its documented instructions) opens, parses,
68
+ appends to, or otherwise operates on the file at that path as part of normal execution. This
69
+ needs rewriting.
70
+ - **Passing mention** - the path appears only in prose (an example, a comment about history, a
71
+ reference to "where this used to live") and no code path touches the file. This may not need
72
+ changing; leave it alone.
73
+
74
+ ## Step 4 - Rewrite direct read/write matches
75
+
76
+ For each direct read/write, rewrite the file I/O to use the equivalent `ccst pdata` command:
77
+
78
+ - Reading to list or browse records: `ccst pdata list --project <name> --group <record_group>
79
+ [--format json]`.
80
+ - Reading one record: `ccst pdata get --project <name> --id <id>`.
81
+ - Filtering or searching: `ccst pdata query --project <name> --group <record_group> --where
82
+ '<field> <op> <value>'`.
83
+ - Appending a record: `ccst pdata add` (check `ccst pdata add --help` for the record shape the
84
+ target record_group expects).
85
+ - Editing a record: `ccst pdata update` (version-checked; check `ccst pdata update --help`).
86
+
87
+ Keep the rewrite mechanical and narrow: change the I/O call to go through `ccst pdata`, and do not
88
+ otherwise restructure the skill's logic or prose beyond what reflects the new access path. If the
89
+ skill's script parsed CSV columns or JSON keys directly, map them to the pdata record fields using
90
+ `ccst pdata schema show --project <name> --group <record_group>` (it lists the group's registered
91
+ extension fields) rather than guessing field names.
92
+
93
+ Do not touch skills unrelated to this project. A match that on closer reading references a
94
+ different project's similarly-named folder is not this skill's concern - skip it.
95
+
96
+ ## Step 5 - Flag anything unsafe to rewrite mechanically
97
+
98
+ Some direct matches do more than read a file or append a row: custom locking, multi-file
99
+ transactions, format transformations that do not map onto one `ccst pdata` command, or logic that
100
+ depends on file-level properties (mtime, file size, row order) that pdata does not expose the same
101
+ way.
102
+
103
+ Do not attempt to rewrite these. List each in the report as needing a human decision, with enough
104
+ detail (skill name, file, what it does, why it does not map cleanly) for the user to decide.
105
+ Silently skipping a match is not acceptable: every match found in Step 2 must appear in the
106
+ report as "updated", "left alone (passing mention only)", or "needs a human decision".
107
+
108
+ ## Step 6 - Report
109
+
110
+ Summarise for the user:
111
+
112
+ 1. Which skills had a literal path match (Step 2).
113
+ 2. For each, its category (Step 3) and outcome: updated, left alone, or needs a human decision.
114
+ 3. If there were no matches at all, say so plainly. That is a valid and complete outcome.