diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c87ff6d..1dcdac4f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -78,6 +78,16 @@ repos: always_run: true pass_filenames: false priority: 0 + - repo: local + hooks: + - id: check-doc-sync + name: check-doc-sync + entry: uv run --frozen --offline hooks/check-doc-sync.py + args: ["AGENTS.md"] + language: system + always_run: true + pass_filenames: false + priority: 0 - repo: local hooks: - id: export-module-tree diff --git a/AGENTS.md b/AGENTS.md index ddd9b1fa..18d0c38b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,201 @@ External skills can be installed if they are not present — see the `usethis-sk is complete, always run this skill to check for any issues before finishing. - ALWAYS mention which skills you've used after completing any task, in PR descriptions, and comments. +## Module Structure + + + +```text +usethis # usethis: Automate Python project setup and development tasks that are otherwise performed manually. +├── __main__ # The CLI application for usethis. +├── _config # Global configuration state for usethis. +├── _config_file # Context managers for coordinated configuration file I/O. +├── _console # Console output helpers for styled and structured printing. +├── _deps # Dependency management operations for project dependency groups. +├── _fallback # Central module for hard-coded fallback version constants. +├── _init # Project initialization and build system setup. +├── _subprocess # Subprocess invocation utilities. +├── _test # Test utilities and fixtures for the usethis test suite. +├── errors # Custom errors for the usethis package. +├── _backend # Backend dispatch and tool-specific backend implementations. +│ ├── dispatch # Backend selection and dispatch logic. +│ ├── poetry # Poetry backend implementation. +│ │ └── detect # Detection of Poetry usage in a project. +│ └── uv # uv backend implementation. +│ ├── available # Check whether the uv CLI is available. +│ ├── call # Subprocess wrappers for invoking uv commands. +│ ├── deps # Dependency group operations via the uv backend. +│ ├── detect # Detection of uv usage in a project. +│ ├── errors # Error types for the uv backend. +│ ├── init # Project initialization via uv. +│ ├── link_mode # Symlink link-mode configuration for uv. +│ ├── lockfile # Lock file creation and management for uv. +│ ├── python # Python version queries via uv. +│ ├── toml # Manager for the uv.toml configuration file. +│ └── version # Retrieve the installed uv version. +├── _core # Core business logic for usethis commands. +│ ├── author # Author metadata management for pyproject.toml. +│ ├── badge # README badge generation and management. +│ ├── browse # Open project-related URLs in a browser. +│ ├── docstyle # Docstring style configuration. +│ ├── list # List tools and their usage status. +│ ├── readme # README file creation and management. +│ ├── rule # Linter rule selection and configuration. +│ ├── show # Display project information. +│ ├── status # Development status classifier management. +│ └── tool # Tool functions to add/remove tools to/from the project. +├── _detect # The detections module. +│ ├── pre_commit # Detection of pre-commit usage in a project. +│ └── readme # Detection of README file presence. +├── _file # Configuration file reading, writing, and merging. +│ ├── dir # Project directory name utilities. +│ ├── manager # Base file manager classes for configuration file I/O. +│ ├── merge # Deep merge utilities for nested mappings. +│ ├── print_ # Pretty-printing helpers for configuration file keys. +│ ├── types_ # Shared type aliases for file operations. +│ ├── ini # INI file reading and writing. +│ │ ├── errors # Error types for INI file operations. +│ │ └── io_ # INI file I/O manager. +│ ├── pyproject_toml # pyproject.toml file reading and writing. +│ │ ├── errors # Error types for pyproject.toml operations. +│ │ ├── io_ # pyproject.toml file I/O manager. +│ │ ├── name # Project name and description extraction from pyproject.toml. +│ │ ├── project # Access the [project] section of pyproject.toml. +│ │ ├── remove # Removal of the pyproject.toml file. +│ │ ├── requires_python # Python version requirement queries from pyproject.toml. +│ │ └── valid # Validation and repair of pyproject.toml structure. +│ ├── setup_cfg # setup.cfg file reading and writing. +│ │ ├── errors # Error types for setup.cfg operations. +│ │ └── io_ # setup.cfg file I/O manager. +│ ├── toml # TOML file reading and writing. +│ │ ├── errors # Error types for TOML file operations. +│ │ └── io_ # TOML file I/O manager. +│ └── yaml # YAML file reading and writing. +│ ├── errors # Error types for YAML file operations. +│ ├── io_ # YAML file I/O manager. +│ ├── typing_ # Type aliases for YAML document values. +│ └── update # Smart update strategies for YAML sequences and mappings. +├── _integrations # Third-party tool integrations. +│ ├── ci # CI platform integrations. +│ │ └── github # GitHub CI integration. +│ │ ├── errors # Error types for GitHub CI operations. +│ │ └── tags # GitHub repository tag fetching. +│ ├── environ # Environment detection utilities. +│ │ └── python # Python version environment queries. +│ ├── mkdocs # MkDocs documentation integration. +│ │ └── core # MkDocs project setup and configuration. +│ ├── pre_commit # Pre-commit hook framework integration. +│ │ ├── cmd_ # Pre-commit command constants. +│ │ ├── core # Core pre-commit setup and teardown operations. +│ │ ├── errors # Error types for pre-commit operations. +│ │ ├── hooks # Pre-commit hook addition and removal. +│ │ ├── init # Initialization of the pre-commit configuration file. +│ │ ├── language # Pre-commit language keyword resolution. +│ │ ├── schema # Pydantic models for the pre-commit configuration schema. +│ │ ├── version # Pre-commit version inference. +│ │ └── yaml # YAML file manager for the pre-commit configuration. +│ ├── project # Project structure and metadata integration. +│ │ ├── build # Build system detection for the project. +│ │ ├── errors # Error types for project integration operations. +│ │ ├── imports # Import graph analysis for the project. +│ │ ├── layout # Project source directory layout detection. +│ │ ├── name # Project name resolution with fallback heuristics. +│ │ └── packages # Importable package discovery. +│ ├── pydantic # Pydantic model utilities. +│ │ ├── dump # Pydantic model serialization helpers. +│ │ └── typing_ # Type aliases for Pydantic model representations. +│ ├── pytest # pytest test framework integration. +│ │ └── core # pytest directory and configuration setup. +│ ├── readme # README file integration. +│ │ └── path # README file path resolution. +│ └── sonarqube # SonarQube integration. +│ ├── config # SonarQube project configuration management. +│ └── errors # Error types for SonarQube operations. +├── _pipeweld # Pipeline welding algorithm for dependency-aware ordering. +│ ├── containers # Container data structures for pipeline compositions. +│ ├── func # Pipeline welding functions. +│ ├── ops # Operation and instruction types for pipeline welding. +│ └── result # Result types for pipeline welding. +├── _python # Python language utilities. +│ └── version # Utilities for Python version information. +├── _tool # Tool management framework. +│ ├── all_ # Registry of all available tools. +│ ├── base # Base classes for tool implementations. +│ ├── config # Configuration specification types for tools. +│ ├── heuristics # Heuristic fallbacks for tool configuration. +│ ├── pre_commit # Pre-commit hook specification types for tools. +│ ├── rule # Lint rule specification types for tools. +│ ├── spec # Abstract tool specification base classes. +│ └── impl # Concrete tool implementations. +│ ├── base # Tool base implementations with side effects. +│ │ ├── codespell # Codespell tool implementation. +│ │ ├── coverage_py # Coverage.py tool implementation. +│ │ ├── deptry # deptry tool implementation. +│ │ ├── import_linter # Import Linter tool implementation. +│ │ ├── mkdocs # MkDocs tool implementation. +│ │ ├── pre_commit # pre-commit tool implementation. +│ │ ├── pyproject_fmt # pyproject-fmt tool implementation. +│ │ ├── pyproject_toml # pyproject.toml as a managed tool. +│ │ ├── pytest # pytest tool implementation. +│ │ ├── requirements_txt # requirements.txt tool implementation. +│ │ ├── ruff # Ruff tool implementation. +│ │ └── ty # ty tool implementation. +│ └── spec # Tool specification implementations. +│ ├── codespell # Codespell tool specification. +│ ├── coverage_py # Coverage.py tool specification. +│ ├── deptry # deptry tool specification. +│ ├── import_linter # Import Linter tool specification. +│ ├── mkdocs # MkDocs tool specification. +│ ├── pre_commit # pre-commit tool specification. +│ ├── pyproject_fmt # pyproject-fmt tool specification. +│ ├── pyproject_toml # pyproject.toml tool specification. +│ ├── pytest # pytest tool specification. +│ ├── requirements_txt # requirements.txt tool specification. +│ ├── ruff # Ruff tool specification. +│ └── ty # ty tool specification. +├── _toolset # Predefined groups of related tools. +│ ├── arch # Architecture enforcement toolset. +│ ├── doc # Documentation toolset. +│ ├── format_ # Code formatting toolset. +│ ├── hook # Git hook framework toolset. +│ ├── lint # Linting toolset. +│ ├── spellcheck # Spell checking toolset. +│ ├── test # Testing toolset. +│ └── typecheck # Type checking toolset. +├── _types # Shared type definitions and enumerations. +│ ├── backend # Backend enumeration for package manager selection. +│ ├── build_backend # Build backend enumeration for packaging tool selection. +│ ├── deps # Dependency model definitions. +│ ├── docstyle # Docstring style enumeration. +│ └── status # Development status enumeration for classifiers. +└── _ui # User interface layer for the CLI. + ├── app # The Typer application for usethis. + ├── options # Shared Typer option definitions. + └── interface # Typer command interface modules. + ├── arch # CLI commands for architecture enforcement tools. + ├── author # CLI commands for managing project authors. + ├── badge # CLI commands for managing README badges. + ├── browse # CLI commands for browsing project resources. + ├── doc # CLI commands for documentation tools. + ├── docstyle # CLI commands for docstring style configuration. + ├── format_ # CLI commands for code formatting tools. + ├── hook # CLI commands for git hook framework tools. + ├── init # CLI commands for project initialization. + ├── lint # CLI commands for linting tools. + ├── list # CLI commands for showing out the full usage table. + ├── readme # CLI commands for README management. + ├── rule # CLI commands for linter rule management. + ├── show # CLI commands for showing project information. + ├── spellcheck # CLI commands for spell checking tools. + ├── status # CLI commands for development status configuration. + ├── test # CLI commands for testing tools. + ├── tool # CLI commands for individual tool management. + ├── typecheck # CLI commands for type checking tools. + └── version # CLI commands for displaying version information. +``` + + + ## Lessons When you are working on a problem, you are almost always going to encounter a difficulty. This is great - it's an opportunity for learning. ALWAYS make a note explicitly of what lessons you are drawing as you complete a task or when receiving user feedback. Try and keep this structured: consider the root cause of the difficulty, and how you overcame it. After finishing work on a task, report back all your lessons. Finally, try and update the relevant skills with any new insights you've drawn, to help future agents, and/or create a new skill. diff --git a/hooks/check-doc-sync.py b/hooks/check-doc-sync.py new file mode 100644 index 00000000..8c881803 --- /dev/null +++ b/hooks/check-doc-sync.py @@ -0,0 +1,114 @@ +"""Check that sync blocks in markdown files match their source files. + +Scans markdown files for comment pairs of the form: + + ...content... + + +and verifies the content between the markers matches the referenced file, +ignoring leading/trailing whitespace differences around the block boundaries. +""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + +# Pattern matches and captures the path. +_SYNC_START = re.compile(r"^$") +# Pattern matches and captures the path. +_SYNC_END = re.compile(r"^$") +# Pattern matches a markdown fenced code block opening (e.g. ```text). +_CODEBLOCK_FENCE = re.compile(r"^```\w*$") + + +def _strip_codeblock(text: str) -> str: + """Strip a surrounding markdown fenced code block, if present.""" + stripped = text.strip() + lines = stripped.splitlines() + if ( + len(lines) >= 2 + and _CODEBLOCK_FENCE.match(lines[0]) + and lines[-1].strip() == "```" + ): + return "\n".join(lines[1:-1]) + return stripped + + +def _find_sync_blocks(text: str) -> list[tuple[str, str]]: + """Return (source_path, actual_content) pairs for every sync block.""" + blocks: list[tuple[str, str]] = [] + lines = text.splitlines(keepends=True) + i = 0 + while i < len(lines): + start_match = _SYNC_START.match(lines[i].strip()) + if start_match: + source_path = start_match.group(1) + content_lines: list[str] = [] + i += 1 + found_end = False + while i < len(lines): + end_match = _SYNC_END.match(lines[i].strip()) + if end_match and end_match.group(1) == source_path: + found_end = True + break + content_lines.append(lines[i]) + i += 1 + if not found_end: + print( + f"ERROR: No closing marker found for sync:{source_path}", + file=sys.stderr, + ) + blocks.append((source_path, "")) + else: + blocks.append((source_path, "".join(content_lines))) + i += 1 + return blocks + + +def main() -> int: + if len(sys.argv) < 2: + print("Usage: check-doc-sync.py [ ...]", file=sys.stderr) + return 1 + + failed = False + for filepath in sys.argv[1:]: + path = Path(filepath) + if not path.is_file(): + print(f"ERROR: {path} not found.", file=sys.stderr) + failed = True + continue + + text = path.read_text(encoding="utf-8") + blocks = _find_sync_blocks(text) + + for source_path, actual_content in blocks: + source = Path(source_path) + if not source.is_file(): + print( + f"ERROR: Source file {source} referenced in {path} not found.", + file=sys.stderr, + ) + failed = True + continue + + expected = source.read_text(encoding="utf-8") + + if _strip_codeblock(actual_content) != expected.strip(): + print( + f"ERROR: Content in {path} between sync:{source_path} markers " + f"is out of sync with {source}.", + file=sys.stderr, + ) + failed = True + + if failed: + return 1 + + print("All sync blocks are up to date.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())