This file provides guidance to LLM Agents such as Codex, Gemini, Claude Code (claude.ai/code), etc. when working with code in this repository.
- ALL tests MUST pass for code to be considered complete and working
- Never describe code as "working as expected" if there are ANY failing tests
- Even if specific feature tests pass, failing tests elsewhere indicate broken functionality
- Changes that break existing tests must be fixed before considering implementation complete
- A successful implementation must pass linting, type checking, AND all existing tests
vcspull is a Python tool for managing and synchronizing multiple git, svn, and mercurial repositories via YAML or JSON configuration files. It allows users to pull/update multiple repositories in a single command, optionally filtering by repository name, path, or VCS URL.
# Install development dependencies with uv
uv pip install -e .
# Alternative: Use uv sync to install from pyproject.toml
uv sync# Run all tests
uv run pytest
# Run specific test(s)
uv run pytest tests/test_cli.py
uv run pytest tests/test_cli.py::test_sync
# Watch mode for tests (auto re-run on file changes)
uv run ptw .
# or
just start
# Run tests with coverage
uv run py.test --cov -v# Format code with ruff
uv run ruff format .
# or
just ruff-format
# Run ruff linting with auto-fixes
uv run ruff check . --fix --show-fixes
# or
just ruff
# Run mypy type checking
uv run mypy
# or
just mypy
# Watch mode for linting (using entr)
just watch-ruff
just watch-mypy# Build documentation
just build-docs
# Start documentation server (auto-reload)
just start-docsFollow this workflow for code changes:
- Format First:
uv run ruff format . - Run Tests:
uv run py.test - Run Linting:
uv run ruff check . --fix --show-fixes - Check Types:
uv run mypy - Verify Tests Again:
uv run py.test
-
Configuration
config.py: Handles loading and parsing of YAML/JSON configuration files_internal/config_reader.py: Low-level config file reading
-
CLI
cli/__init__.py: Main CLI entry point with argument parsingcli/sync.py: Repository synchronization functionalitycli/add.py: Adding new repositories to configuration
-
Repository Management
- Uses
libvcspackage for VCS operations (git, svn, hg) - Supports custom remotes and URL schemes
- Uses
Configuration files are stored as YAML or JSON in either:
~/.vcspull.yaml/.json(home directory)~/.config/vcspull/directory (XDG config)
Example format:
~/code/:
flask: "git+https://github.com/mitsuhiko/flask.git"
~/study/c:
awesome: "git+git://git.naquadah.org/awesome.git"For standard library modules:
- Use namespace imports:
import enuminstead offrom enum import Enum - For typing, use
import typing as tand access via namespace:t.NamedTuple, etc.
For third-party packages: Use idiomatic import styles for each library (e.g., from pygments.token import Token is fine).
Always: Use from __future__ import annotations at the top of all Python files.
Follow NumPy docstring style for all functions and methods:
"""Short description of the function or class.
Detailed description using reStructuredText format.
Parameters
----------
param1 : type
Description of param1
param2 : type
Description of param2
Returns
-------
type
Description of return value
"""All functions and methods MUST have working doctests. Doctests serve as both documentation and tests.
CRITICAL RULES:
- Doctests MUST actually execute - never comment out function calls or similar
- Doctests MUST NOT be converted to
.. code-block::as a workaround (code-blocks don't run) - If you cannot create a working doctest, STOP and ask for help
Available tools for doctests:
doctest_namespacefixtures (inherited from libvcs):tmp_path,create_git_remote_repo,create_hg_remote_repo,create_svn_remote_repo- Ellipsis for variable output:
# doctest: +ELLIPSIS - Update
conftest.pyto add new fixtures todoctest_namespace
# doctest: +SKIP is NOT permitted - it's just another workaround that doesn't test anything. If a VCS binary might not be installed, pytest already handles skipping via skip_if_binaries_missing. Use the fixtures properly.
Using fixtures in doctests:
>>> from vcspull.config import extract_repos
>>> config = {'~/code/': {'myrepo': 'git+https://github.com/user/repo'}}
>>> repos = extract_repos(config) # doctest: +ELLIPSIS
>>> len(repos)
1When output varies, use ellipsis:
>>> repo_dir = tmp_path / 'repo' # tmp_path from doctest_namespace
>>> repo_dir.mkdir()
>>> repo_dir # doctest: +ELLIPSIS
PosixPath('.../repo')Use functional tests only: Write tests as standalone functions (test_*), not classes. Avoid class TestFoo: groupings - use descriptive function names and file organization instead. This applies to pytest tests, not doctests.
When writing tests, leverage libvcs's pytest plugin fixtures:
create_git_remote_repo,create_svn_remote_repo,create_hg_remote_repo: Factory fixturesgit_repo,svn_repo,hg_repo: Pre-made repository instancesset_home,gitconfig,hgconfig,git_commit_envvars: Environment fixtures
Example:
def test_vcspull_sync(git_repo):
# git_repo is already a GitSync instance with a clean repository
# Use it directly in your testsFor multi-line commits, use heredoc to preserve formatting:
git commit -m "$(cat <<'EOF'
feat(Component[method]) add feature description
why: Explanation of the change.
what:
- First change
- Second change
EOF
)"Use typing.NamedTuple for parameterized tests:
class SyncFixture(t.NamedTuple):
test_id: str # For test naming
sync_args: list[str]
expected_exit_code: int
expected_in_out: ExpectedOutput = None
@pytest.mark.parametrize(
list(SyncFixture._fields),
SYNC_REPO_FIXTURES,
ids=[test.test_id for test in SYNC_REPO_FIXTURES],
)
def test_sync(
# Parameters and fixtures...
):
# Test implementation- Use
monkeypatchfor environment, globals, attributes - Use
mocker(from pytest-mock) for application code - Document every mock with comments explaining WHAT is being mocked and WHY
- Use project helper functions like
vcspull.tests.helpers.write_configorsave_config_yaml - Avoid direct
yaml.dumporfile.write_textfor config creation
Format commit messages as:
Component/File(commit-type[Subcomponent/method]): Concise description
why: Explanation of necessity or impact.
what:
- Specific technical changes made
- Focused on a single topic
Common commit types:
- feat: New features or enhancements
- fix: Bug fixes
- refactor: Code restructuring without functional change
- docs: Documentation updates
- chore: Maintenance (dependencies, tooling, config)
- test: Test-related updates
- style: Code style and formatting
Example:
cli/add(feat[add_repo]): Add support for custom remote URLs
why: Enable users to specify alternative remote URLs for repositories
what:
- Add remote_url parameter to add_repo function
- Update CLI argument parser to accept --remote-url option
- Add tests for the new functionality
When writing documentation (README, CHANGES, docs/), follow these rules for code blocks:
One command per code block. This makes commands individually copyable.
Put explanations outside the code block, not as comments inside.
Good:
Search for a term across all fields:
$ vcspull search djangoSearch by repository name:
$ vcspull search "name:flask"Bad:
# Search for a term across all fields
$ vcspull search django
# Search by repository name
$ vcspull search "name:flask"When stuck in debugging loops:
- Pause and acknowledge the loop
- Minimize to MVP: Remove all debugging cruft and experimental code
- Document the issue comprehensively for a fresh approach
- Format for portability (using quadruple backticks)