Files
cam/cam/naming.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

68 lines
1.9 KiB
Python

"""Context codes and readable archive file names."""
from __future__ import annotations
import re
import unicodedata
CODE_PATTERN = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9._-]*$")
UUID_PATTERN = re.compile(
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
re.IGNORECASE,
)
FORBIDDEN_FILENAME = re.compile(r'[\\/:*?"<>|]')
def validate_context_code(code: str) -> str:
code = code.strip()
if not code or not CODE_PATTERN.match(code):
raise ValueError(
f"Invalid context code '{code}'. "
"Use letters, digits, dots, dashes (e.g. w1-1, w1.git.001, sync-crm3)."
)
return code
def slugify_title(title: str, max_len: int = 50) -> str:
text = " ".join(title.split())
if not text:
return "untitled"
normalized = unicodedata.normalize("NFKD", text)
cleaned = FORBIDDEN_FILENAME.sub("-", normalized)
cleaned = re.sub(r"\s+", "-", cleaned.strip())
cleaned = re.sub(r"-{2,}", "-", cleaned).strip("-")
if not cleaned:
cleaned = "untitled"
if len(cleaned) > max_len:
cleaned = cleaned[: max_len - 1].rstrip("-")
return cleaned
def markdown_filename(code: str, title: str, *, slug_max: int = 50) -> str:
slug = slugify_title(title, slug_max)
return f"{code}--{slug}.md"
def matches_context_pattern(code: str, pattern: str | None) -> bool:
if not pattern:
return True
import fnmatch
return fnmatch.fnmatchcase(code, pattern)
def filter_context_codes(codes: list[str], pattern: str | None, explicit: str | None) -> list[str]:
if explicit:
explicit = validate_context_code(explicit)
if pattern and not matches_context_pattern(explicit, pattern):
return []
return [explicit] if explicit in codes else []
if pattern:
return [c for c in codes if matches_context_pattern(c, pattern)]
return list(codes)
def is_uuid_name(name: str) -> bool:
return bool(UUID_PATTERN.match(name))