- Python 3.11+ only. Use
tomllib(stdlib),matchstatements where they clarify,Patheverywhere (neveros.path),from __future__ import annotationsat the top of every module. - No dependencies beyond
anthropic. Everything else is stdlib. Exception:pytest,pytest-mock,ruffin dev dependencies only. - Type hints everywhere. Return types on all functions. No bare
dictorlist— usedict[str, Path],list[Path], etc. Pathfor all filesystem operations. Never concatenate strings to build paths.
Each module has one job. Don't let them bleed:
sync.pycalls the API and returns text. It does not write files.cli.pyhandles I/O (print, argparse). It does not contain business logic.providers/detect paths. They do not create directories or write files.config.pyloads and saves config. It does not call the API.
If you find yourself importing cli from sync or sync from providers,
stop and reconsider the design.
Keep functions small and single-purpose. If a function is doing two things, split it. The test for this: can you describe what it does in one sentence without using "and"?
Prefer explicit parameters over reading from global state:
# Good
def refresh_memory_content(notes: str, current_memory: str, config: Config) -> dict:
...
# Bad — reads global config internally, hard to test
def refresh_memory_content(notes: str) -> dict:
config = Config.load() # hidden dependency
...- Use specific exceptions, not bare
except Exception. - Errors that the user can fix → print to stderr with a fix suggestion, return exit code 1.
- Errors that are bugs → let them propagate with a full traceback.
- Never swallow exceptions silently.
# Good
try:
path = provider.detect()
except PermissionError as e:
print(f"Error: can't access sync folder: {e}", file=sys.stderr)
print("Check folder permissions or run: memsync config set sync_root /path", file=sys.stderr)
return 4
# Bad
try:
path = provider.detect()
except Exception:
path = None- Success output → stdout
- Errors → stderr
- Keep success output minimal. Users will run this in terminal sessions — wall-of-text output is noise.
- Use
✓and✗for status indicators inmemsync statusandmemsync providers. - Emoji in output: only the two above, nowhere else.
| Thing | Convention | Example |
|---|---|---|
| Modules | snake_case | claude_md.py |
| Classes | PascalCase | OneDriveProvider |
| Functions | snake_case | refresh_memory_content |
| Constants | UPPER_SNAKE | SYSTEM_PROMPT |
| CLI commands | hyphen-case | memsync dry-run |
| Config keys | snake_case | keep_days |
| Provider names | lowercase, no hyphens | "onedrive", "icloud", "gdrive" |
A module is done when:
- All functions have type hints
- All functions have docstrings (one line is fine for obvious things)
- Tests exist and pass on Mac, Windows, Linux (CI green)
- No hardcoded paths, model strings, or magic numbers
feat: add iCloud provider detection
fix: restore hard constraints dropped by compaction
refactor: extract backup logic into backups.py
test: add provider detection tests with mocked filesystem
docs: update adding-a-provider guide
First word: feat, fix, refactor, test, docs, chore.
Present tense. No period at the end.
- Don't use
print()for debugging — use proper logging or remove before commit - Don't use
os.path— usepathlib.Path - Don't use
open()withoutencoding="utf-8" - Don't write to
~/.claude/CLAUDE.mdwithout backing up first - Don't call the Anthropic API in any path except
sync.py - Don't import from
cli.pyin any other module