Files
cam/cam/import_transcripts.py
T
mk 2cab3c2b02 Release v0.1.0: context codes and readable archive file names.
- registry.yaml maps codes (w1-1, w1.git.001) to sessions and stable paths
- Markdown files named {code}--{slug}.md for Cursor @ picker
- Selective export/import via --context and --mask
- cam code list|set|show for manual code assignment
- Re-export updates same file per context code

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 09:41:57 +03:00

103 lines
2.8 KiB
Python

"""Import archived transcripts back into Cursor agent-transcripts folder."""
from __future__ import annotations
import shutil
from dataclasses import dataclass
from pathlib import Path
from cam.config import CamConfig
from cam.paths import cursor_transcript_path
from cam.registry import ContextRegistry
@dataclass
class ImportResult:
copied: int = 0
skipped: int = 0
paths: list[str] | None = None
def __post_init__(self) -> None:
if self.paths is None:
self.paths = []
def import_transcripts(
config: CamConfig,
*,
dry_run: bool = False,
source: Path | None = None,
context: str | None = None,
context_mask: str | None = None,
) -> ImportResult:
archive_root = (source or config.export_dir).resolve()
raw_root = archive_root / "raw"
transcripts_root = config.transcripts_dir
if not raw_root.is_dir():
raise FileNotFoundError(f"Archive raw/ folder not found: {raw_root}")
registry = ContextRegistry(archive_root, config)
registry.load()
allowed = registry.sessions_matching(pattern=context_mask, explicit=context)
if context or context_mask:
if not allowed:
raise ValueError(f"No contexts match filter: {context or context_mask}")
allowed_ids = {r.session_id for r in allowed}
else:
allowed_ids = {r.session_id for r in registry.contexts.values()}
result = ImportResult()
for record in registry.contexts.values():
if record.session_id not in allowed_ids:
continue
raw_path = archive_root / record.raw_file
if not raw_path.is_file():
continue
dest = cursor_transcript_path(
transcripts_root,
record.session_id,
record.parent_session_id,
)
result.paths.append(f"{record.code} -> {dest}")
if dest.exists() and not config.overwrite_existing:
result.skipped += 1
continue
if dry_run:
result.copied += 1
continue
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(raw_path, dest)
result.copied += 1
return result
def list_archive_contexts(
archive_root: Path,
config: CamConfig,
*,
context_mask: str | None = None,
) -> list[tuple[str, str, str, Path | None]]:
registry = ContextRegistry(archive_root, config)
registry.load()
rows: list[tuple[str, str, str, Path | None]] = []
for record in registry.sessions_matching(pattern=context_mask):
raw_path = archive_root / record.raw_file
rows.append(
(
record.code,
record.session_id,
record.title,
raw_path if raw_path.is_file() else None,
)
)
rows.sort(key=lambda r: r[0])
return rows