cc-session-tools 3.2.0__tar.gz → 3.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cc_session_tools-3.2.0/src/cc_session_tools.egg-info → cc_session_tools-3.3.0}/PKG-INFO +1 -1
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/pyproject.toml +1 -1
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/ccst.py +20 -5
- cc_session_tools-3.3.0/src/cc_session_tools/lib/pdata/cutover.py +90 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/init_service.py +70 -6
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/manifest.py +9 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/prompts/pdata-migration-claude-md-update.md +9 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/prompts/pdata-migration-skills-update.md +9 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-pdata-do-audit-and-prepare-to-migrate/SKILL.md +14 -5
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-pdata-do-migrate/SKILL.md +16 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-project-init/SKILL.md +83 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-project-layout-reference/SKILL.md +22 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0/src/cc_session_tools.egg-info}/PKG-INFO +1 -1
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_init_cli.py +29 -0
- cc_session_tools-3.2.0/src/cc_session_tools/lib/pdata/cutover.py +0 -34
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/LICENSE +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/README.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/setup.cfg +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/ccd.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/ccmsg.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/ccr.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/ccs.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/ccsched.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/migrate_ccmsg.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/migrate_ccsched.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/migrate_sessions_db.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/cli/migrate_telemetry.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/config/hooks-bundle.json +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/config/session-tag-hook.json +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/hooks_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/claude_flags.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/claude_md_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/context_overrides.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/db.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/debug.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/doctor.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/doctor_mutes.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/hook_registry.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/install_sync.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/levenshtein.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/machine_identity.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/addressing.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/cursor.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/lock.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/message.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/move_safety.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/repository.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/retention.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/service.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/messaging/store.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/occupancy.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/paths.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/backup.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/classify.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/dump.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/formatting.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/importers.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/init_paths.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/naming.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/rehydrate.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/rename_group.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/reorganize.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/repository.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/resolve.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/service.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/session_output.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/store.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/sync_check.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/sync_lock.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/sync_notify.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/vector_clock.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/vector_clock_store.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/verify.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/write_log.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/picker.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/proc_lock.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/prompts.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/roots.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/rules.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/bundled_jobs.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/cadence.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/cursor.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/digest.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/due.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/duration.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/jobspec.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/ledger.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/lock.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/notify.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/reconcile.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/registry.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/runner.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/state.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/store.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/surface.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/throttle.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/scheduler/worker.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/session_gc.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/sessions.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/sessions_db.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/sessions_repair.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/shell_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/tasklist.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/telemetry_store.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/analyse-cc-usage/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/clean-hook-sessions/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/clean-hook-sessions/scripts/clean-hook-sessions.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/compact-smart/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/context-override/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/delete-sessions/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/delete-sessions/scripts/delete_sessions.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/do-executor-critic-assessor-loop/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/find-claude-code-session/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/generate-8digit-code/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/generate-8digit-code/scripts/generate_8digit_code.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/list-empty-sessions/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/list-empty-sessions/scripts/list_empty_sessions.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/manage-recurring-cc-jobs-using-ccsched/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/move-session/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/move-session/hooks/sessionstart-pending-rename.sh +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/move-session/scripts/cc_session_rules.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/move-session/scripts/move_session.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-pdata-conflict-resolution/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-pdata-schema-design/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/pm-update-central-files/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/CALIBRATION.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/analyze_context.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/duplicates.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/measure.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/report.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/tokens.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/reduce-persistent-context/scripts/usage.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/select-agent-model/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/send-session-message/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/update-command-cache/SKILL.md +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/skills/update-command-cache/scripts/update_command_cache.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools.egg-info/SOURCES.txt +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools.egg-info/dependency_links.txt +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools.egg-info/entry_points.txt +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools.egg-info/requires.txt +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools.egg-info/top_level.txt +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/attribution.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/cache.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/ccusage_wrapper.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/data/pricing.json +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/parent_inference.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/parser.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/pricing.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/query.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/report.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/schema.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/claude_code_usage/session_names.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/__init__.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/after_response.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/bash_hard_deny.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/bash_security_review.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/cache.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/catchup.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/confirm_8digit.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/context_window_warning.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/last_screenshot.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/marker_allow.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/markers.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/messaging_deliver.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/model_info.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/normalise.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/pdata_sync.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/pending_migration.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/pending_rename.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/session_tag.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/stats.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/telemetry.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/telemetry_query.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/telemetry_trim.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/transcript.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/hooks/worklog_guard.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_after_response.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_attribution.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_bash_hard_deny.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_bash_security_review.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cache.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cache_sqlite.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccr_orphans.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccs_emptiness.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccs_eta.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccs_list_mode.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccs_scope_flags.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccs_sentinel_sort.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccs_session_counts.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_bundle_discovery.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_ccsched_jobs_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_context_override_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_doctor.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_gc_prune.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_gc_report.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_hook_dispatcher.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_hooks_install_prune.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_hooks_install_zero_arg.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_hooks_uninstall.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_install_everything.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_machine_identity_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_main_gate.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_reconcile_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_rename_group_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_reorganize_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_sync_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_pdata_verify_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_repair_sessions_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_sessions_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_shell_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_skills_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_skills_uninstall.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_telemetry_query.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccst_telemetry_trim.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_ccusage_wrapper.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_claude_flags.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_claude_md_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cli.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cli_ccd.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cli_ccd_dry_run.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cli_ccr.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cli_ccs.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_cli_version.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_confirm_8digit.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_context_overrides.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_context_window_warning.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_db.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_debug.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_doctor_mutes.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_empty_session.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_hook_cache.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_hooks_install.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_install_sync.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_last_screenshot.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_levenshtein.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_machine_identity.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_marker_allow.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_migrate_ccmsg.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_migrate_ccsched.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_migrate_sessions_db.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_migrate_telemetry.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_model_info.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_normalise.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_occupancy.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_parent_inference.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_parser.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_paths.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_pending_migration_hook.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_picker.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_pricing.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_proc_lock.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_prompts.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_query.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_report.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_roots.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_rules.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_scheduler_bundled_jobs.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_schema.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_session_names.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_session_tag.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_sessions.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_sessions_db.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_sessions_repair.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_tasklist.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_telemetry.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_telemetry_store.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_transcript.py +0 -0
- {cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/tests/test_worklog_guard.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cc-session-tools"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.3.0"
|
|
8
8
|
description = "Claude Code session management CLI tools (ccd, ccr, ccs, claude-code-usage, ccst) and hook library (hooks package)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -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
|
|
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.
|
|
183
|
-
|
|
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)
|
|
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)
|
{cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/init_service.py
RENAMED
|
@@ -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
|
-
|
|
400
|
-
for
|
|
401
|
-
|
|
402
|
-
|
|
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(
|
|
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],
|
{cc_session_tools-3.2.0 → cc_session_tools-3.3.0}/src/cc_session_tools/lib/pdata/manifest.py
RENAMED
|
@@ -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:
|
|
@@ -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,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
|
|
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-migrate` 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-migrate`
|
|
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
|
|
@@ -134,7 +140,7 @@ This file is deliberately NOT a migration plan - it's a findings list for whoeve
|
|
|
134
140
|
`pm-pdata-schema-design` 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-
|
|
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-
|
|
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-migrate`
|
|
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
|
|
@@ -154,7 +162,7 @@ The manifest must have, in this order, so `pm-pdata-do-migrate` can parse it det
|
|
|
154
162
|
row/entry count, last-verified-clean date, and its role (`index`, `register`, `log`,
|
|
155
163
|
`narrative`, `derived-artefact`).
|
|
156
164
|
3. **Per-file migration blockers**, in the exact shape `pm-pdata-schema-design` and
|
|
157
|
-
`pm-
|
|
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;
|
|
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
|
|
|
@@ -5,6 +5,22 @@ description: Execute a project's actual migration from flat central/index files
|
|
|
5
5
|
|
|
6
6
|
# Migrate a project to pdata
|
|
7
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.
|
|
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.
|
|
@@ -62,6 +62,51 @@ that renames a `record_group` to match an existing one is **not** re-checked. Be
|
|
|
62
62
|
proposal, scan it yourself for two entries sharing a `record_group` that you did not deliberately
|
|
63
63
|
intend to merge.
|
|
64
64
|
|
|
65
|
+
### Watch for garbled CSV headers
|
|
66
|
+
|
|
67
|
+
A CSV whose first row is actually prose/provenance text (a `#`-prefixed comment, or a multi-
|
|
68
|
+
sentence caveat wrapped in CSV quoting) rather than real column names produces a `[db-owned]`
|
|
69
|
+
entry whose proposed field names read as garbled sentence fragments (e.g.
|
|
70
|
+
`pending_chris_s_manual_review`, `generated_2026_08_12_do_not_edit`) rather than short domain
|
|
71
|
+
nouns - syntactically valid-looking identifiers that don't obviously scream "wrong" on a fast
|
|
72
|
+
skim. The dry-run report flags a likely case of this automatically (a "worth double-checking"
|
|
73
|
+
annotation on the entry's line), but always eyeball field-name **shape**, not just presence, for
|
|
74
|
+
every `[db-owned]` entry - the annotation is a heuristic, not a guarantee.
|
|
75
|
+
|
|
76
|
+
The content on a garbled header line is usually legitimate and valuable (provenance/caveat text a
|
|
77
|
+
later reader genuinely needs), not junk to discard. Fix recipe:
|
|
78
|
+
|
|
79
|
+
1. Read past the suspect first line to confirm a clean, comma-separated real header exists
|
|
80
|
+
further down (usually line 2).
|
|
81
|
+
2. Strip the bad line(s) from the source CSV, preserving the removed text exactly - you'll need it
|
|
82
|
+
in step 4.
|
|
83
|
+
3. Correct the manifest entry: fix `fields` to match the real header, set a real `content_column`,
|
|
84
|
+
confirm `classification: db-owned`.
|
|
85
|
+
4. Set the entry's `preface_text` field (in `.pdata-migration-manifest.json`) to the exact text you
|
|
86
|
+
removed in step 2. `--write`'s cutover step carries this into the entry's pointer file (Step 7)
|
|
87
|
+
automatically, under its own heading - you don't need to compose that by hand.
|
|
88
|
+
|
|
89
|
+
### Also check every `db-owned` entry against two deferred-file criteria
|
|
90
|
+
|
|
91
|
+
Beyond field-name shape, review every `db-owned` entry (not just folder-owned markdown/text files)
|
|
92
|
+
against two further questions before approving the proposal - a structurally clean CSV can still
|
|
93
|
+
be a poor migration candidate for reasons the classifier has no way to know about:
|
|
94
|
+
|
|
95
|
+
- **Does this file live somewhere the project's own docs say only a human should touch?** (e.g. a
|
|
96
|
+
`references/` folder documented as "only Chris adds/removes/reorganises files here"). Migrating
|
|
97
|
+
it moves the original out of that folder as part of cutover, which conflicts with the folder's
|
|
98
|
+
documented purpose. If so, flip the entry to `folder-owned` and flag it explicitly to Chris as a
|
|
99
|
+
real open decision - do not silently include or silently exclude it.
|
|
100
|
+
- **How does this source file actually get updated going forward - incrementally appended, or
|
|
101
|
+
periodically replaced wholesale with a fresh full export?** An append-only file (new rows added
|
|
102
|
+
over time, however often) is a good migration candidate regardless of update frequency. A file
|
|
103
|
+
whose maintenance model is "drop in a new full export periodically" is a poor candidate under
|
|
104
|
+
the current tooling even if the CSV itself is clean: `ccst pdata` has no bulk-replace/upsert
|
|
105
|
+
verb, so every future refresh would mean soft-deleting every old record and individually
|
|
106
|
+
re-adding every new one, one CLI call per row. Flag this distinction explicitly to Chris rather
|
|
107
|
+
than deciding it unilaterally - the append-vs-wholesale-replacement axis, not update frequency,
|
|
108
|
+
is what actually matters here.
|
|
109
|
+
|
|
65
110
|
## 4. Hand-edit the proposal to encode overrides
|
|
66
111
|
|
|
67
112
|
Edit `.pdata-migration-manifest.json` directly (or the legacy `.ccst-pdata-proposal.json` name,
|
|
@@ -90,6 +135,25 @@ Two entries may legitimately feed the same `record_group`, but if they share a f
|
|
|
90
135
|
must give it the same `sql_type` - `--write` rejects a mismatched pair up front rather than
|
|
91
136
|
silently dropping one side's type.
|
|
92
137
|
|
|
138
|
+
**`file_path_column` pitfall:** don't map `file_path_column` directly to a column whose values are
|
|
139
|
+
relative to a subfolder rather than the project root (e.g. an index-of-a-subfolder CSV like
|
|
140
|
+
`references/INDEX.csv` whose own `file_path` column is relative to `references/`, not the project
|
|
141
|
+
root) - `--write`'s file_path-resolution verification will fail with a bare "does not resolve
|
|
142
|
+
under `<project_root>`" and no further guidance. Instead, map that column to a plain named field
|
|
143
|
+
(e.g. `source_file_path`) and note in the field's `description` what path it's relative to.
|
|
144
|
+
|
|
145
|
+
**`content_column: null` default caveat:** an entry with `content_column` left unset falls back to
|
|
146
|
+
a raw `json.dumps(row)` dump as every record's content - rarely what you want for readable `ccst
|
|
147
|
+
pdata list` output. Actively choose a real narrative column for every `db-owned` entry before
|
|
148
|
+
`--write`; don't accept the JSON-blob default by omission.
|
|
149
|
+
|
|
150
|
+
**Value transforms the generic importer can't express:** a computed field (e.g. an `is_canonical`
|
|
151
|
+
flag for a known duplicate key) or splitting one source column into two has no 1:1 mapping in
|
|
152
|
+
`csv-rows`. Don't try to force it into the manifest - import the entry normally via `csv-rows`
|
|
153
|
+
first, then apply a post-`--write` `ccst pdata schema add-field` (to add the new field) followed
|
|
154
|
+
by a per-record `ccst pdata update` pass (to populate it). This is the standard recipe for any
|
|
155
|
+
field the source file doesn't already carry as its own column.
|
|
156
|
+
|
|
93
157
|
Never delete the proposal file to "start over" without good reason - doing so discards every
|
|
94
158
|
override made so far. If new files appear in the project after the first dry run, add entries for
|
|
95
159
|
them by hand rather than deleting and reclassifying everything.
|
|
@@ -102,6 +166,14 @@ explicit approval. **Never invoke `--write` without it.**
|
|
|
102
166
|
|
|
103
167
|
## 6. Run the write phase
|
|
104
168
|
|
|
169
|
+
A real migration of an existing, non-trivial project is typically **iterative, not a single
|
|
170
|
+
terminal `--write`**: expect multiple `--write` rounds as source files get fixed (garbled
|
|
171
|
+
headers), deferred decisions get resolved, and corrected entries get re-approved. Don't plan for
|
|
172
|
+
or promise a one-shot `--write`. A `--write` round only ever imports entries with no `migrated_at`
|
|
173
|
+
already set - entries already cut over by an earlier successful `--write` are skipped
|
|
174
|
+
automatically, so a later round picking up newly-fixed or newly-added files is safe to run without
|
|
175
|
+
re-touching what already migrated.
|
|
176
|
+
|
|
105
177
|
```sh
|
|
106
178
|
ccst pdata init --project <project> [--rehearse <path>] --write
|
|
107
179
|
```
|
|
@@ -132,6 +204,12 @@ Archived originals live at `<project-root>/.pdata-migrated/` and are **never aut
|
|
|
132
204
|
himself - that WARN is expected and not urgent. Do not delete the archive without his explicit
|
|
133
205
|
instruction.
|
|
134
206
|
|
|
207
|
+
By default, `--write` also leaves a `.md` pointer file at each cut-over entry's original path
|
|
208
|
+
(record_group, field/schema table, an example query command, and any preserved preface text - see
|
|
209
|
+
Step 3's garbled-header recipe) - so anything else in the project citing the old path by name finds
|
|
210
|
+
a live signpost instead of a silent 404. Pass `--leave-no-pointer-files` if this run shouldn't
|
|
211
|
+
leave one (e.g. a rehearsal, or a project convention against extra files at migrated paths).
|
|
212
|
+
|
|
135
213
|
## 8. Rollback, if something's found wrong post-cutover
|
|
136
214
|
|
|
137
215
|
There is no `--rollback` flag. Recover by hand:
|
|
@@ -149,6 +227,11 @@ There is no `--rollback` flag. Recover by hand:
|
|
|
149
227
|
3. Fix whatever was wrong in the proposal (classification, strategy, field mapping) before
|
|
150
228
|
re-attempting.
|
|
151
229
|
|
|
230
|
+
A genuine intentional re-migration of an entry (e.g. after restoring the backup and deciding to
|
|
231
|
+
redo it) also requires clearing that entry's `migrated_at` field by hand in the manifest, alongside
|
|
232
|
+
the DB/proposal fixes above - `--write` otherwise skips any entry that already carries one (Step 6),
|
|
233
|
+
by design, so it won't be re-imported until you do.
|
|
234
|
+
|
|
152
235
|
## Never do without explicit Chris approval
|
|
153
236
|
|
|
154
237
|
- Invoke `--write` on an unreviewed proposal.
|
|
@@ -81,3 +81,25 @@ derivable per file. Topic-based splitting (`analysis/`, or any project-specific
|
|
|
81
81
|
natural date) has no tool support and stays a manual `git mv`/`mv` pass, since grouping by topic
|
|
82
82
|
needs human judgement the tool can't safely substitute for. Anything simpler than a split
|
|
83
83
|
(renaming one folder, adding a new folder type) is also a plain `git mv`/`mv`.
|
|
84
|
+
|
|
85
|
+
## 6. File-type-segment vocabulary for meetings-and-calls/
|
|
86
|
+
|
|
87
|
+
Files within `meetings-and-calls/` follow a `--`-delimited naming convention:
|
|
88
|
+
|
|
89
|
+
`<yyyy.MM.dd>-<HHmm>--<participants>--<purpose>--<file type>.<ext>`
|
|
90
|
+
|
|
91
|
+
The `<file type>` segment recognises three core values, common enough to expect in any project
|
|
92
|
+
with recorded meetings or calls:
|
|
93
|
+
|
|
94
|
+
| File type | What it holds |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `summary` | Auto-generated summary of the call (e.g. Plaud) |
|
|
97
|
+
| `transcript` | Raw or auto-generated transcript of the call |
|
|
98
|
+
| `audio` | The raw recording itself (`.mp3`/`.mp4`), before any transcription or summary exists |
|
|
99
|
+
|
|
100
|
+
This isn't exhaustive - as with the five folders in §1, a project may extend the vocabulary with
|
|
101
|
+
values for distinctions specific to its own work: `analysis` and `transcript-whisper` (maxella,
|
|
102
|
+
for a session's own write-up of a meeting and a Whisper-generated transcript alongside the Plaud
|
|
103
|
+
one), or `reasoning-summary` and `notes-email` (pbt, for a modelled reasoning summary and an
|
|
104
|
+
emailed notes write-up). Promote a project-specific value to this shared list once it recurs
|
|
105
|
+
across multiple projects - the same bar the five folders in §1 were held to.
|
|
@@ -106,6 +106,35 @@ def test_pdata_init_write_end_to_end_imports_and_cuts_over(base_env, tmp_path):
|
|
|
106
106
|
assert "first" in r_list.stdout and "second" in r_list.stdout
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
def test_pdata_init_write_leaves_pointer_file_by_default(base_env, tmp_path):
|
|
110
|
+
project_dir = tmp_path / "projects" / "demo"
|
|
111
|
+
project_dir.mkdir(parents=True)
|
|
112
|
+
(project_dir / "ideas.csv").write_text("idea\nfirst\nsecond\n")
|
|
113
|
+
base_env["CCST_PDATA_BACKUP_DIR"] = str(tmp_path / "backups")
|
|
114
|
+
|
|
115
|
+
_run(base_env, "pdata", "init", "--project", "demo")
|
|
116
|
+
r_write = _run(base_env, "pdata", "init", "--project", "demo", "--write")
|
|
117
|
+
|
|
118
|
+
assert r_write.returncode == 0, r_write.stderr
|
|
119
|
+
pointer = project_dir / "ideas.md"
|
|
120
|
+
assert pointer.exists()
|
|
121
|
+
assert "ideas" in pointer.read_text()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_pdata_init_write_leave_no_pointer_files_suppresses_pointer(base_env, tmp_path):
|
|
125
|
+
project_dir = tmp_path / "projects" / "demo"
|
|
126
|
+
project_dir.mkdir(parents=True)
|
|
127
|
+
(project_dir / "ideas.csv").write_text("idea\nfirst\nsecond\n")
|
|
128
|
+
base_env["CCST_PDATA_BACKUP_DIR"] = str(tmp_path / "backups")
|
|
129
|
+
|
|
130
|
+
_run(base_env, "pdata", "init", "--project", "demo")
|
|
131
|
+
r_write = _run(base_env, "pdata", "init", "--project", "demo", "--write",
|
|
132
|
+
"--leave-no-pointer-files")
|
|
133
|
+
|
|
134
|
+
assert r_write.returncode == 0, r_write.stderr
|
|
135
|
+
assert not (project_dir / "ideas.md").exists()
|
|
136
|
+
|
|
137
|
+
|
|
109
138
|
def test_pdata_init_write_without_prior_dry_run_errors(base_env, tmp_path):
|
|
110
139
|
project_dir = tmp_path / "projects" / "demo"
|
|
111
140
|
project_dir.mkdir(parents=True)
|
|
@@ -1,34 +0,0 @@
|
|
|
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 archive_entries(*, project_root: Path, entries: list[ManifestEntry]) -> None:
|
|
15
|
-
"""Move every db-owned entry's source file into project_root/.pdata-migrated/,
|
|
16
|
-
preserving its relative path, and append one line per entry to MANIFEST.md.
|
|
17
|
-
Never deletes — cutover only relocates within project_root (spec §7.1 step 6);
|
|
18
|
-
deleting the archive is a manual, human-directed action (step 7)."""
|
|
19
|
-
if not entries:
|
|
20
|
-
return
|
|
21
|
-
archive_root = project_root / MIGRATED_ARCHIVE_DIRNAME
|
|
22
|
-
archive_root.mkdir(parents=True, exist_ok=True)
|
|
23
|
-
manifest_path = archive_root / MIGRATED_MANIFEST_FILENAME
|
|
24
|
-
now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
|
25
|
-
with manifest_path.open("a", encoding="utf-8") as log:
|
|
26
|
-
for entry in entries:
|
|
27
|
-
source = project_root / entry.path
|
|
28
|
-
destination = archive_root / entry.path
|
|
29
|
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
30
|
-
source.rename(destination)
|
|
31
|
-
log.write(
|
|
32
|
-
f"- {now} — {entry.path} — migrated source, superseded by ccst pdata "
|
|
33
|
-
f"(record_group={entry.db_group()})\n"
|
|
34
|
-
)
|
|
File without changes
|