#!/usr/bin/env python3 """CAM CLI — Cursor Agents Manager.""" from __future__ import annotations import argparse import sys from pathlib import Path CAM_ROOT = Path(__file__).resolve().parent if str(CAM_ROOT) not in sys.path: sys.path.insert(0, str(CAM_ROOT)) from __version__ import __status__, __version__ # noqa: E402 from cam.config import load_config, resolve_config_path # noqa: E402 from cam.export import export_transcripts # noqa: E402 from cam.import_transcripts import import_transcripts, list_archive_sessions # noqa: E402 def _default_cam_command(config_path: Path | None) -> str: if config_path is not None: rel_config: Path | str = config_path try: rel_config = config_path.relative_to(CAM_ROOT.parent.parent) except ValueError: try: rel_config = Path("tools/cam") / config_path.relative_to(CAM_ROOT) except ValueError: rel_config = config_path return f"python tools/cam/cam.py -c {rel_config} export" return "python tools/cam/cam.py export" def cmd_paths(config_path: Path) -> int: config = load_config(config_path) print(f"workstation_id: {config.workstation_id}") print(f"workstation_label: {config.workstation_label}") print(f"project_root: {config.project_root}") print(f"cursor_project_slug:{config.cursor_project_slug}") print(f"transcripts_dir: {config.transcripts_dir}") print(f"export_dir: {config.export_dir}") print(f"index_language: {config.index_language}") print(f"config_file: {config.source_path}") return 0 def cmd_export(config_path: Path) -> int: config = load_config(config_path) main_count, sub_count = export_transcripts(config, _default_cam_command(config_path)) print( f"Exported {main_count} main sessions and {sub_count} subagents " f"to {config.export_dir}" ) return 0 def cmd_import(config_path: Path, dry_run: bool, source: Path | None) -> int: config = load_config(config_path) result = import_transcripts(config, dry_run=dry_run, source=source) mode = "Dry-run:" if dry_run else "Imported:" print( f"{mode} {result.copied} file(s) to {config.transcripts_dir}; " f"skipped {result.skipped} existing" ) return 0 def cmd_list(config_path: Path) -> int: config = load_config(config_path) sessions = list_archive_sessions(config.export_dir) if not sessions: print(f"No sessions in {config.export_dir / 'raw'}") return 0 for session_id, parent_id, path in sessions: parent = parent_id or "-" print(f"{session_id}\tparent={parent}\t{path}") print(f"Total: {len(sessions)}") return 0 def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="cam", description="Cursor Agents Manager — export and import agent transcripts", ) parser.add_argument( "-V", "--version", action="version", version=f"%(prog)s {__version__} ({__status__})", ) parser.add_argument( "-c", "--config", type=Path, help="Path to workstation YAML config (default: auto-detect)", ) sub = parser.add_subparsers(dest="command", required=True) sub.add_parser("paths", help="Show resolved paths from config").set_defaults( handler="paths" ) sub.add_parser("export", help="Export Cursor transcripts to project archive").set_defaults( handler="export" ) sub.add_parser("list", help="List sessions in export archive").set_defaults(handler="list") import_parser = sub.add_parser( "import", help="Import archived transcripts into Cursor agent-transcripts folder", ) import_parser.add_argument( "--dry-run", action="store_true", help="Show what would be copied without writing files", ) import_parser.add_argument( "--source", type=Path, help="Archive root (default: export.output_dir from config)", ) import_parser.set_defaults(handler="import") return parser def main(argv: list[str] | None = None) -> int: parser = build_parser() args = parser.parse_args(argv) try: config_path = resolve_config_path(CAM_ROOT, args.config) except FileNotFoundError as exc: print(f"Error: {exc}", file=sys.stderr) return 2 try: if args.handler == "paths": return cmd_paths(config_path) if args.handler == "export": return cmd_export(config_path) if args.handler == "list": return cmd_list(config_path) if args.handler == "import": return cmd_import(config_path, args.dry_run, args.source) except (FileNotFoundError, ValueError) as exc: print(f"Error: {exc}", file=sys.stderr) return 1 parser.error(f"Unknown command: {args.handler}") return 2 if __name__ == "__main__": raise SystemExit(main())