Skip to content

Scan working directory when no paths are provided - #24

Merged
leynos merged 11 commits into
mainfrom
codex/locate-.md-files-in-directories
Aug 19, 2025
Merged

Scan working directory when no paths are provided#24
leynos merged 11 commits into
mainfrom
codex/locate-.md-files-in-directories

Conversation

@leynos

@leynos leynos commented Aug 19, 2025

Copy link
Copy Markdown
Owner

Summary

  • default nixie to discover Markdown files when no paths are supplied
  • document implicit Markdown discovery and ignore patterns
  • add pathspec dependency and supporting tests

Testing

  • make check-fmt
  • make lint
  • make typecheck
  • make test
  • make markdownlint (fails: reference link definitions missing in .rules/ and other files)
  • /root/.bun/bin/markdownlint-cli2 README.md docs/CHANGELOG.md
  • make nixie

https://chatgpt.com/codex/tasks/task_e_68a3be0640988322b06cb7fadfad778e

Summary by Sourcery

Enable nixie to scan the working directory for Markdown files by default when no paths are specified, honoring .gitignore entries.

New Features:

  • Automatically discover and validate all Markdown files in the current directory when no paths are provided, respecting .gitignore patterns
  • Add pathspec dependency for ignore-pattern handling

Enhancements:

  • Allow zero or more positional paths in the CLI and fallback to implicit file discovery when none are supplied
  • Refactor argument parsing and CLI entrypoint to integrate the new discovery behavior

Documentation:

  • Document implicit Markdown file discovery and .gitignore support in README and CHANGELOG

Tests:

  • Add unit tests for discover_markdown_files and integration tests for CLI behavior with no arguments

@sourcery-ai

sourcery-ai Bot commented Aug 19, 2025

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduces implicit Markdown file discovery in the CLI when no paths are supplied by walking the current directory with respect to .gitignore, adds the pathspec dependency, updates documentation to reflect this behavior, and includes integration and unit tests for the new discovery logic.

Sequence diagram for implicit Markdown file discovery in CLI

sequenceDiagram
    actor User
    participant CLI
    participant discover_markdown_files
    participant main
    User->>CLI: Run nixie (no arguments)
    CLI->>discover_markdown_files: Discover Markdown files in current directory
    discover_markdown_files-->>CLI: Return Markdown file list
    CLI->>main: Validate Markdown files
    main-->>CLI: Return results
    CLI-->>User: Output validation results
Loading

Class diagram for discover_markdown_files and CLI argument changes

classDiagram
    class CLI {
        +cli()
    }
    class discover_markdown_files {
        +discover_markdown_files() Generator[Path]
    }
    class argparse.Namespace {
        +paths: list[Path]
        +concurrency: int
        +verbose: bool
    }
    CLI --> discover_markdown_files : uses
    CLI --> argparse.Namespace : parses arguments
    discover_markdown_files --|> pathspec.PathSpec : uses
    class pathspec.PathSpec {
        +match_file(path: str)
    }
Loading

File-Level Changes

Change Details Files
Enable default Markdown discovery when no paths are supplied
  • Change CLI positional args to optional (nargs="*")
  • Import pathspec and add call to discover_markdown_files when no paths
  • Update parse_args help text to mention default behavior
  • Adjust cli() to use discovered paths if none are provided
nixie/cli.py
Implement discover_markdown_files utility
  • Add function to walk cwd and yield .md files
  • Respect patterns from .gitignore via pathspec
  • Skip .git directory and excluded paths during traversal
nixie/cli.py
Add pathspec as a project dependency
  • Include pathspec in pyproject.toml dependencies
pyproject.toml
Document implicit scanning behavior
  • Update README.md with new usage and features
  • Add CHANGELOG.md entry for no-args scanning
README.md
docs/CHANGELOG.md
Add tests for no-args scanning and ignore support
  • Create integration test for CLI without args
  • Create unit test for discover_markdown_files gitignore behavior
tests/integration/test_no_args.py
nixie/unittests/test_discover_markdown_files.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 19, 2025

Copy link
Copy Markdown
Contributor

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@leynos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 11 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between f1c2a37 and 3450db1.

📒 Files selected for processing (3)
  • docs/CHANGELOG.md (1 hunks)
  • nixie/cli.py (5 hunks)
  • nixie/unittests/test_discover_markdown_files.py (1 hunks)

Summary by CodeRabbit

  • New Features
    • Automatically discovers Markdown files when run without arguments.
    • Recursively searches the current directory for Markdown, respecting .gitignore rules in the working directory.
    • Deterministic ordering of discovered files.
  • Documentation
    • Updated README and changelog to clarify default file discovery and .gitignore behaviour.
  • Tests
    • Added unit and integration tests covering no-argument execution and .gitignore handling.
  • Chores
    • Added a dependency to support .gitignore pattern matching.

Walkthrough

Implement Gitignore-aware Markdown discovery: make CLI file arguments optional; when none supplied, scan the current working directory for Markdown files while honouring only the top-level .gitignore; add discovery helpers, integrate pathspec, update docs, and add unit and integration tests.

Changes

Cohort / File(s) Summary
Documentation updates
README.md, docs/CHANGELOG.md
Update Features and Usage to document optional [FILE ...], default discovery in CWD, and that only the top-level .gitignore is honoured.
CLI implementation
nixie/cli.py
Add discover_markdown_files() using pathspec to parse top-level .gitignore; make CLI paths arg optional (nargs="*"); auto-discover when no paths supplied; update collect_markdown_files() to apply ignore rules to expanded directories and explicit paths; ensure deterministic sorted output; import pathspec.
Unit tests
nixie/unittests/test_discover_markdown_files.py
Add tests covering discovery and collection: top-level .gitignore ignores, negation rules, deterministic ordering, nested .gitignore ignored, empty-directory behaviour, and directory expansion respecting ignore rules.
Integration tests
tests/integration/test_no_args.py, tests/integration/test_gitignore_paths.py
Add tests for CLI invoked with no args (discovers files or exits 0 if none) and for explicit-path invocation that skips ignored entries and passes correct paths to main.
Packaging/config
pyproject.toml
Add pathspec>=0.12.1,<1.0 to [project].dependencies.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant CLI as nixie.cli
    participant Disco as discover_markdown_files()
    participant FS as Filesystem
    participant Pathspec as pathspec (.gitignore)
    participant Main as main()

    User->>CLI: Run `nixie` (no FILE args)
    CLI->>Disco: Start discovery in CWD
    Disco->>Pathspec: Load top-level .gitignore (if present)
    Disco->>FS: Walk files & directories
    Disco->>Pathspec: Test paths against patterns
    Pathspec-->>Disco: Return ignored/non-ignored classification
    Disco-->>CLI: Yield sorted, non-ignored `.md` paths
    CLI->>Main: Invoke main(paths)
    Main-->>CLI: Return exit code
    CLI-->>User: Exit with code
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

Sweep the root and heed the sign,
Let pathspec guard the markdown line.
Call with none and let it find,
Keep the good and leave the blind.
Exit tidy, exit kind.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/locate-.md-files-in-directories

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `nixie/cli.py:409` </location>
<code_context>
-        nargs="+",
-        help="Markdown files to validate",
+        nargs="*",
+        help=(
+            "Markdown files to validate. Defaults to all Markdown files in the "
+            "current directory."
+        ),
     )
</code_context>

<issue_to_address>
Help text should clarify .gitignore filtering behavior.

Consider updating the help text to note that files ignored by .gitignore are excluded from discovery.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
        help=(
            "Markdown files to validate. Defaults to all Markdown files in the "
            "current directory."
        ),
=======
        help=(
            "Markdown files to validate. Defaults to all Markdown files in the "
            "current directory. Files ignored by .gitignore are excluded from discovery."
        ),
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `tests/integration/test_no_args.py:17` </location>
<code_context>
+    from pathlib import Path
+
+
+def test_cli_scans_cwd_when_no_args(
+    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+    """Discover Markdown files in CWD when no paths are supplied."""
+    keep = tmp_path / "keep.md"
+    ignored = tmp_path / "ignored"
+    ignored.mkdir()
+    (ignored / "skip.md").write_text("ignored")
+    keep.write_text("ok")
+    (tmp_path / ".gitignore").write_text("ignored/\n")
+
+    captured: list[Path] = []
+
+    async def fake_main(paths: cabc.Iterable[Path], _concurrency: int) -> int:
+        captured.extend(paths)
+        return 0
+
+    monkeypatch.setattr(cli_module, "main", fake_main)
+    monkeypatch.chdir(tmp_path)
+    monkeypatch.setattr(sys, "argv", ["nixie"])
+
+    with pytest.raises(SystemExit) as excinfo:
+        cli_module.cli()
+
+    exc = typ.cast(SystemExit, excinfo.value)
+    assert exc.code == 0
+    assert captured == [keep]
</code_context>

<issue_to_address>
Consider adding a test for the case where no Markdown files exist in the working directory.

Adding a test for an empty directory will verify that the CLI behaves correctly when no Markdown files are found, such as exiting with code 0 or displaying an appropriate message.
</issue_to_address>

### Comment 3
<location> `nixie/unittests/test_discover_markdown_files.py:15` </location>
<code_context>
+    import pytest
+
+
+def test_discover_markdown_files_respects_gitignore(
+    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+    """Skip directories listed in ``.gitignore`` when searching for Markdown."""
+    keep = tmp_path / "keep.md"
+    ignored_dir = tmp_path / "ignored"
+    ignored_dir.mkdir()
+    skip = ignored_dir / "skip.md"
+    keep.write_text("ok")
+    skip.write_text("nope")
+    (tmp_path / ".gitignore").write_text("ignored/\n")
+
+    monkeypatch.chdir(tmp_path)
+
+    found = list(discover_markdown_files())
+    assert found == [keep]
</code_context>

<issue_to_address>
Test does not cover ignored files at the root level.

Please add a test case for a root-level file (e.g., 'skip.md') listed in .gitignore to verify file-level ignore patterns are handled correctly.
</issue_to_address>

### Comment 4
<location> `nixie/cli.py:74` </location>
<code_context>
     return BLOCK_RE.findall(text)


+def discover_markdown_files() -> cabc.Generator[Path]:
+    """Yield Markdown files under the current directory respecting ``.gitignore``."""
+    root = Path.cwd()
</code_context>

<issue_to_address>
Consider replacing the manual directory traversal with Path.rglob and pathspec filtering to simplify file discovery.

```suggestion
Replace the manual os.walk + dir pruning with a simple Path.rglob + pathspec filter. This keeps .gitignore support but removes nested loops and string juggling:

from pathlib import Path
import pathspec

def discover_markdown_files() -> Iterator[Path]:
    """Yield Markdown files under cwd, respecting .gitignore."""
    root = Path.cwd()
    gitignore = root / ".gitignore"
    spec = None
    if gitignore.is_file():
        spec = pathspec.PathSpec.from_lines(
            "gitwildmatch",
            gitignore.read_text().splitlines()
        )

    for md in root.rglob("*.md"):
        rel = md.relative_to(root).as_posix()
        if spec and spec.match_file(rel):
            continue
        yield md
```

Steps:
1. Remove `import os` and the os.walk logic.
2. Use `root.rglob("*.md")` to collect all Markdown files.
3. Filter out ignored paths via `spec.match_file(rel_path)`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread nixie/cli.py
Comment thread tests/integration/test_no_args.py
Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
Comment thread nixie/cli.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8906f21 and 943ffc5.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • README.md (2 hunks)
  • docs/CHANGELOG.md (1 hunks)
  • nixie/cli.py (5 hunks)
  • nixie/unittests/test_discover_markdown_files.py (1 hunks)
  • pyproject.toml (1 hunks)
  • tests/integration/test_no_args.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
pyproject.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Maintain Python project configuration and packaging per .rules/python-pyproject.md

pyproject.toml: Enable Ruff with fixers and formatter configured
Configure tools (Ruff, Pyright, Pytest) in pyproject.toml
Enforce Pyright strict mode and treat all Pyright warnings as CI errors
Use Ruff for linting (replace flake8, isort, pyflakes, etc.)
Use Ruff for code formatting

pyproject.toml: Use pyproject.toml as the single source of truth for project metadata, dependencies, and build configuration (no separate setup.py or requirements.txt needed)
Define [project] with mandatory PEP 621 fields: name and version
Include helpful metadata in [project]: description, readme (e.g., "README.md"), requires-python, license (text or file), authors, keywords, classifiers
Declare runtime dependencies under [project].dependencies using PEP 508 specifiers
Group non-runtime packages under [project.optional-dependencies] (e.g., dev, docs)
Expose CLIs via [project.scripts] and GUI apps via [project.gui-scripts]
Provide a [build-system] using setuptools>=61.0 and wheel with build-backend = "setuptools.build_meta" (or an alternative like flit_core)
If omitting [build-system], set [tool.uv].package = true to ensure the project itself is installed
Include [tool.uv] with package = true so uv builds/installs your package on sync/run
Use semantic versioning for the [project].version (MAJOR.MINOR.PATCH)
Use exact or bounded dependency ranges (e.g., requests>=2.25,<3.0) instead of unbounded pins
Use dynamic fields (e.g., dynamic = ["version"]) sparingly and only if supported by the chosen build backend

Files:

  • pyproject.toml
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Markdown files must pass markdown linting (make markdownlint)
Markdown files containing Mermaid diagrams must pass nixie validation (make nixie)

Files:

  • README.md
  • docs/CHANGELOG.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • README.md
  • docs/CHANGELOG.md
**/README.md

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate README.md near reusable packages and include usage examples

Files:

  • README.md
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the source of truth for requirements, dependencies, and architectural decisions
Proactively update docs/ markdown when decisions, requirements, dependencies, or architecture change

Files:

  • docs/CHANGELOG.md
docs/**

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Maintain a docs/ directory near reusable packages for documentation

Files:

  • docs/CHANGELOG.md
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.py: Python changes must pass tests (unit and behavioral) before completion/commit
Python code must pass lint checks (make lint)
Python code must adhere to formatting standards (make check-fmt/make fmt)
Python code must pass type checking (make typecheck)
Follow core Python 3.13 style conventions per .rules/python-00.md
Apply best practices for context managers per .rules/python-context-managers.md
Follow generator and iterator patterns per .rules/python-generators.md
Follow function return conventions per .rules/python-return.md
Apply Python typing best practices per .rules/python-typing.md

**/*.py: Name Python files in snake_case (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single underscore
Use typing everywhere; maintain full static type coverage with Pyright
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only
Avoid Any; use Unknown, generics, or cast() with justification if Any is used
Be explicit with return types for all public functions and class methods (e.g., -> None, -> str)
Favor immutability (prefer tuples to lists; use frozendict or types.MappingProxyType where appropriate)
Use # pyright: ignore sparingly and include an explanation when used
Avoid side effects at import time; modules should not modify global state or perform actions on import
Never hardcode secrets in source code
Write NumPy-style docstrings for public functions, classes, and modules
Add inline comments to explain non-obvious logic or decisions

**/*.py: Use context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual management
Use @contextmanager from contextlib for straightforward, linear setup/teardown without persistent internal state
Implement a class-based context manager (enter/exit) when there is internal sta...

Files:

  • tests/integration/test_no_args.py
  • nixie/unittests/test_discover_markdown_files.py
  • nixie/cli.py

⚙️ CodeRabbit Configuration File

**/*.py: - Keep cyclomatic complexity ≤ 12

  • Follow single responsibility and CQRS (command/query segregation)
  • Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
  • Move conditionals with >2 branches to predicate/helper functions
  • Avoid eval, exec, pickle, monkey-patching, ctypes, unsafe shell
  • Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
  • Lint suppressions:
    • Blanket # noqa, file-level skips, and categories are forbidden
    • Only narrow in-line disables (# noqa: XYZ) are permitted, and must be accompanied by FIXME: or a ticket link, and used only as a last resort.
  • Use pytest fixtures for shared setup (conftest.py or fixtures/)
  • Replace duplicate tests with @pytest.mark.parametrize
  • Prefer pytest-mock or unittest.mock for stubs/mocks
  • Use assert …, "message" over bare asserts
  • Reflect all API/behaviour changes in docs/ and update roadmap on completion
  • Files must not exceed 400 logical lines:
    • Decompose large modules into subpackages
    • Split large match/case or dispatch tables by domain and collocate with targets if appropriate
    • Move bulky data (fixtures, templates) to external files for parsing at runtime
  • Mutable defaults and shadowed built-ins are forbidden
  • All code must have clear type hints using modern style (A | B, list[str], class Foo[A]:, type Bar = int, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • tests/integration/test_no_args.py
  • nixie/unittests/test_discover_markdown_files.py
  • nixie/cli.py
tests/integration/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Place integration tests under tests/integration/ with files prefixed with test_

Files:

  • tests/integration/test_no_args.py
{**/unittests/test_*.py,tests/integration/test_*.py}

📄 CodeRabbit Inference Engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/integration/test_*.py}: Use pytest idioms: prefer fixtures, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective; test public behaviour, not internals
Avoid excessive mocking; use doubles only for external services or non-deterministic behaviour

Files:

  • tests/integration/test_no_args.py
  • nixie/unittests/test_discover_markdown_files.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate unit tests with code in an unittests/ subdirectory using files prefixed with test_

Files:

  • nixie/unittests/test_discover_markdown_files.py
🧬 Code Graph Analysis (2)
tests/integration/test_no_args.py (1)
nixie/cli.py (1)
  • cli (428-439)
nixie/unittests/test_discover_markdown_files.py (1)
nixie/cli.py (2)
  • cli (428-439)
  • discover_markdown_files (74-98)
🪛 Ruff (0.12.2)
tests/integration/test_no_args.py

41-41: Add quotes to type expression in typing.cast()

Add quotes

(TC006)

🔍 Remote MCP Context7

Here’s the key additional context on how the newly‐added file‐discovery uses pathspec to honor .gitignore:

• The PR adds pathspec as a dependency (pyproject.toml) so that discover_markdown_files() can load gitignore rules and scan the tree.
• Typical usage (from pathspec docs):

import pathspec
# Load .gitignore patterns
with open(".gitignore", "r") as f:
    spec = pathspec.GitIgnoreSpec.from_lines(f)
# Recursively find all files matching patterns (e.g. “*.md”)
matches = spec.match_tree(str(Path.cwd()))

or, using the more generic factory:

spec = pathspec.PathSpec.from_lines("gitwildmatch", gitignore_lines)
files = spec.match_files(all_paths)

The spec.match_tree() method yields all non-ignored files under a directory, and spec.match_file()/match_files() provide fine-grained filtering.

• Reviewer checklist:
– Confirm discover_markdown_files reads .gitignore via GitIgnoreSpec (or equivalent)
– Ensure it calls spec.match_tree() on the CWD to yield “.md” files
– Verify that tests cover both match_tree behavior and spec.match_file edge cases (e.g. nested ignored dirs)

🔇 Additional comments (8)
docs/CHANGELOG.md (1)

11-12: Document default discovery behaviour clearly (LGTM).

The wording is precise and aligns with the feature intent. No changes needed.

README.md (2)

8-9: Feature blurb reads well (LGTM).

The phrasing uses en-GB spelling and matches the new behaviour.


45-46: Keep CLI usage in README and cli.py consistent.

The README shows nixie [--concurrency N] [--verbose] [FILE ...]. Update the usage string in nixie/cli.py to match (see separate comment in cli.py).

nixie/cli.py (1)

33-34: Import pathspec (LGTM).

The import is scoped at module level with no side effects. Matches new dependency.

tests/integration/test_no_args.py (4)

1-7: Module docstring and future annotations are spot on.

Document the behaviour succinctly and enable postponed evaluation of annotations to keep typing-only imports out of runtime. Good.


12-15: Gate typing-only imports correctly.

Import collections.abc and Path under TYPE_CHECKING to satisfy static typing without incurring runtime imports. Good.


28-33: Use an async test double to intercept main cleanly.

Capture the CLI’s computed paths without executing the real pipeline. This aligns with the PR objective and keeps the test focused.


34-40: Drive the CLI via sys.argv and assert on SystemExit.

Patch sys.argv, switch CWD, and call cli() to exercise the integration path end-to-end. This is the right level for an integration test.

Comment thread nixie/cli.py
Comment thread nixie/cli.py Outdated
Comment thread nixie/cli.py
Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
Comment thread pyproject.toml Outdated
Comment thread README.md Outdated
Comment thread tests/integration/test_no_args.py Outdated
Comment thread tests/integration/test_no_args.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (4)
nixie/cli.py (4)

9-10: Align module Usage string with README/CLI: use generic FILE placeholder.

Reflect optional paths and consistency with README.

-    nixie [--concurrency N] [--verbose] [path1.md [path2.md ...]]
+    nixie [--concurrency N] [--verbose] [FILE ...]

413-420: Clarify help text: accept files or directories and expose default discovery.

Match README and user expectations.

     parser.add_argument(
         "paths",
         type=Path,
         nargs="*",
-        help=(
-            "Markdown files to validate. Defaults to all Markdown files in the "
-            "current directory. Files ignored by .gitignore are excluded from "
-            "discovery."
-        ),
+        help=(
+            "Markdown files or directories to validate. When omitted, scans the "
+            "current directory for Markdown files (honouring .gitignore)."
+        ),
     )

446-447: Realise discovery generator and handle “no files found” explicitly.

Avoid passing a bare generator and provide a clear, benign outcome for an empty workspace.

-    paths = parsed.paths or discover_markdown_files()
-    sys.exit(asyncio.run(main(paths, parsed.concurrency)))
+    paths = list(parsed.paths) if parsed.paths else list(discover_markdown_files())
+    if not paths:
+        print("No Markdown files found.", file=sys.stderr)
+        sys.exit(0)
+    sys.exit(asyncio.run(main(paths, parsed.concurrency)))

74-82: Read .gitignore with explicit UTF-8 encoding.

Avoid locale-dependent decoding. Pathspec handles text patterns; reading as UTF‑8 is safe and deterministic.

 def _load_gitignore_spec(root: Path) -> pathspec.PathSpec | None:
     """Return a ``PathSpec`` built from ``root/.gitignore`` if it exists."""
     gitignore = root / ".gitignore"
     if gitignore.is_file():
-        return pathspec.PathSpec.from_lines(
-            "gitwildmatch", gitignore.read_text().splitlines()
-        )
+        lines = gitignore.read_text(encoding="utf-8").splitlines()
+        return pathspec.PathSpec.from_lines("gitwildmatch", lines)
     return None
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 943ffc5 and ed2a44f.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • README.md (2 hunks)
  • docs/CHANGELOG.md (1 hunks)
  • nixie/cli.py (5 hunks)
  • nixie/unittests/test_discover_markdown_files.py (1 hunks)
  • pyproject.toml (1 hunks)
  • tests/integration/test_gitignore_paths.py (1 hunks)
  • tests/integration/test_no_args.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
pyproject.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Maintain Python project configuration and packaging per .rules/python-pyproject.md

pyproject.toml: Enable Ruff with fixers and formatter configured
Configure tools (Ruff, Pyright, Pytest) in pyproject.toml
Enforce Pyright strict mode and treat all Pyright warnings as CI errors
Use Ruff for linting (replace flake8, isort, pyflakes, etc.)
Use Ruff for code formatting

pyproject.toml: Use pyproject.toml as the single source of truth for project metadata, dependencies, and build configuration (no separate setup.py or requirements.txt needed)
Define [project] with mandatory PEP 621 fields: name and version
Include helpful metadata in [project]: description, readme (e.g., "README.md"), requires-python, license (text or file), authors, keywords, classifiers
Declare runtime dependencies under [project].dependencies using PEP 508 specifiers
Group non-runtime packages under [project.optional-dependencies] (e.g., dev, docs)
Expose CLIs via [project.scripts] and GUI apps via [project.gui-scripts]
Provide a [build-system] using setuptools>=61.0 and wheel with build-backend = "setuptools.build_meta" (or an alternative like flit_core)
If omitting [build-system], set [tool.uv].package = true to ensure the project itself is installed
Include [tool.uv] with package = true so uv builds/installs your package on sync/run
Use semantic versioning for the [project].version (MAJOR.MINOR.PATCH)
Use exact or bounded dependency ranges (e.g., requests>=2.25,<3.0) instead of unbounded pins
Use dynamic fields (e.g., dynamic = ["version"]) sparingly and only if supported by the chosen build backend

Files:

  • pyproject.toml
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.py: Python changes must pass tests (unit and behavioral) before completion/commit
Python code must pass lint checks (make lint)
Python code must adhere to formatting standards (make check-fmt/make fmt)
Python code must pass type checking (make typecheck)
Follow core Python 3.13 style conventions per .rules/python-00.md
Apply best practices for context managers per .rules/python-context-managers.md
Follow generator and iterator patterns per .rules/python-generators.md
Follow function return conventions per .rules/python-return.md
Apply Python typing best practices per .rules/python-typing.md

**/*.py: Name Python files in snake_case (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single underscore
Use typing everywhere; maintain full static type coverage with Pyright
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only
Avoid Any; use Unknown, generics, or cast() with justification if Any is used
Be explicit with return types for all public functions and class methods (e.g., -> None, -> str)
Favor immutability (prefer tuples to lists; use frozendict or types.MappingProxyType where appropriate)
Use # pyright: ignore sparingly and include an explanation when used
Avoid side effects at import time; modules should not modify global state or perform actions on import
Never hardcode secrets in source code
Write NumPy-style docstrings for public functions, classes, and modules
Add inline comments to explain non-obvious logic or decisions

**/*.py: Use context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual management
Use @contextmanager from contextlib for straightforward, linear setup/teardown without persistent internal state
Implement a class-based context manager (enter/exit) when there is internal sta...

Files:

  • nixie/unittests/test_discover_markdown_files.py
  • tests/integration/test_no_args.py
  • nixie/cli.py
  • tests/integration/test_gitignore_paths.py

⚙️ CodeRabbit Configuration File

**/*.py: - Keep cyclomatic complexity ≤ 12

  • Follow single responsibility and CQRS (command/query segregation)
  • Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
  • Move conditionals with >2 branches to predicate/helper functions
  • Avoid eval, exec, pickle, monkey-patching, ctypes, unsafe shell
  • Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
  • Lint suppressions:
    • Blanket # noqa, file-level skips, and categories are forbidden
    • Only narrow in-line disables (# noqa: XYZ) are permitted, and must be accompanied by FIXME: or a ticket link, and used only as a last resort.
  • Use pytest fixtures for shared setup (conftest.py or fixtures/)
  • Replace duplicate tests with @pytest.mark.parametrize
  • Prefer pytest-mock or unittest.mock for stubs/mocks
  • Use assert …, "message" over bare asserts
  • Reflect all API/behaviour changes in docs/ and update roadmap on completion
  • Files must not exceed 400 logical lines:
    • Decompose large modules into subpackages
    • Split large match/case or dispatch tables by domain and collocate with targets if appropriate
    • Move bulky data (fixtures, templates) to external files for parsing at runtime
  • Mutable defaults and shadowed built-ins are forbidden
  • All code must have clear type hints using modern style (A | B, list[str], class Foo[A]:, type Bar = int, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • nixie/unittests/test_discover_markdown_files.py
  • tests/integration/test_no_args.py
  • nixie/cli.py
  • tests/integration/test_gitignore_paths.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate unit tests with code in an unittests/ subdirectory using files prefixed with test_

Files:

  • nixie/unittests/test_discover_markdown_files.py
{**/unittests/test_*.py,tests/integration/test_*.py}

📄 CodeRabbit Inference Engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/integration/test_*.py}: Use pytest idioms: prefer fixtures, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective; test public behaviour, not internals
Avoid excessive mocking; use doubles only for external services or non-deterministic behaviour

Files:

  • nixie/unittests/test_discover_markdown_files.py
  • tests/integration/test_no_args.py
  • tests/integration/test_gitignore_paths.py
tests/integration/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Place integration tests under tests/integration/ with files prefixed with test_

Files:

  • tests/integration/test_no_args.py
  • tests/integration/test_gitignore_paths.py
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the source of truth for requirements, dependencies, and architectural decisions
Proactively update docs/ markdown when decisions, requirements, dependencies, or architecture change

Files:

  • docs/CHANGELOG.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Markdown files must pass markdown linting (make markdownlint)
Markdown files containing Mermaid diagrams must pass nixie validation (make nixie)

Files:

  • docs/CHANGELOG.md
  • README.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/CHANGELOG.md
  • README.md
docs/**

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Maintain a docs/ directory near reusable packages for documentation

Files:

  • docs/CHANGELOG.md
**/README.md

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate README.md near reusable packages and include usage examples

Files:

  • README.md
🧬 Code Graph Analysis (3)
nixie/unittests/test_discover_markdown_files.py (1)
nixie/cli.py (1)
  • cli (436-447)
tests/integration/test_no_args.py (1)
nixie/cli.py (1)
  • cli (436-447)
tests/integration/test_gitignore_paths.py (1)
nixie/cli.py (1)
  • cli (436-447)
🔍 Remote MCP

Here’s the key additional context on how the newly‐added file‐discovery uses pathspec to honor .gitignore:

• The PR adds pathspec as a dependency (pyproject.toml) so that discover_markdown_files() can load gitignore rules and scan the tree.
• Typical usage (from pathspec docs):

import pathspec
# Load .gitignore patterns
with open(".gitignore", "r") as f:
    spec = pathspec.GitIgnoreSpec.from_lines(f)
# Recursively find all files matching patterns (e.g. “*.md”)
matches = spec.match_tree(str(Path.cwd()))

or, using the more generic factory:

spec = pathspec.PathSpec.from_lines("gitwildmatch", gitignore_lines)
files = spec.match_files(all_paths)

The spec.match_tree() method yields all non-ignored files under a directory, and spec.match_file()/match_files() provide fine-grained filtering.

• Reviewer checklist:
– Confirm discover_markdown_files reads .gitignore via GitIgnoreSpec (or equivalent)
– Ensure it calls spec.match_tree() on the CWD to yield “.md” files
– Verify that tests cover both match_tree behavior and spec.match_file edge cases (e.g. nested ignored dirs)

🔇 Additional comments (11)
pyproject.toml (1)

7-7: LGTM: Bound pathspec dependency per project policy.

The bounded range pathspec>=0.12.1,<1.0 matches the dependency policy and supports the new discovery logic.

README.md (3)

7-9: LGTM: Feature bullets accurately reflect the new behaviour.

The bullets clearly communicate recursive search and .gitignore awareness.


45-46: LGTM: Usage string matches CLI semantics.

The optional [FILE ...] form aligns with nargs="*".


54-56: LGTM: Scope of .gitignore usage is explicit.

The note about using only the working directory’s .gitignore matches the code.

tests/integration/test_no_args.py (2)

17-46: LGTM: Verify no-args discovery respects .gitignore and exits successfully.

The test exercises discovery, argument-less invocation, and exit code handling as intended.


48-71: LGTM: Cover empty-directory behaviour.

The empty workspace path validates the benign exit and absence of paths forwarded to main().

tests/integration/test_gitignore_paths.py (1)

20-27: Exercise explicit-path mode and .gitignore correctly

Set up the workspace, write a top-level .gitignore, and create both kept and ignored files. This accurately targets the explicit-path code path and validates ignore handling.

nixie/unittests/test_discover_markdown_files.py (4)

7-13: Use TYPE_CHECKING and postponed annotations correctly

Import only for typing under TYPE_CHECKING and enable from future annotations to avoid runtime import costs. This is idiomatic and keeps tests lean.


15-31: Validate directory ignore at repo root

Create an ignored subdirectory and assert only the non-ignored Markdown is discovered. This locks in the top-level .gitignore behaviour.


33-47: Cover root-level file ignores

Assert that a root-level pattern (skip.md) is honoured. This fills the previously missing case and strengthens confidence in the ignore processing.


98-113: Exercise explicit-directory expansion with ignore rules

Drive collect_markdown_files over an explicit directory while honouring .gitignore. This complements discover_markdown_files coverage well.

Comment thread docs/CHANGELOG.md Outdated
Comment thread nixie/unittests/test_discover_markdown_files.py
Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
Comment thread README.md
Comment thread tests/integration/test_gitignore_paths.py Outdated
Comment thread tests/integration/test_gitignore_paths.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
docs/CHANGELOG.md (1)

11-13: Clarify that only the working directory's .gitignore is honoured.

The changelog entry currently implies all .gitignore files are respected. Update it to specify that only the working directory's .gitignore is honoured, aligning with the README and implementation.

Apply this diff to match the implementation details:

- Directory traversal now respects `.gitignore` patterns. When run without
  arguments, nixie scans the current directory for Markdown files with a `.md`
  extension using the same ignore rules.
+ Directory traversal honours `.gitignore` patterns in the working directory only.
  When run without arguments, nixie scans the current directory for Markdown files
  with a `.md` extension using those ignore rules (nested `.gitignore` files are ignored).
tests/integration/test_gitignore_paths.py (2)

30-30: Make the intent explicit: pass Path('.') instead of bare Path().

Clarify that the explicit argument is "current directory".

Apply this diff:

-    exit_code = await main([Path(".")], 2)  # noqa: PTH201 - explicit current directory
+    exit_code = await main([Path(".")], 2)  # noqa: PTH201 - explicit current directory

Wait, I see this has already been resolved in the current code. The Path(".") is already present.


33-34: Extract the path argument to avoid magic positional index.

The magic positional index [3] makes the test brittle if the function signature changes.

Apply this diff:

-    rendered_path = stub_render.await_args_list[0].args[3]  # path argument to renderer
-    assert rendered_path == keep.relative_to(tmp_path)
+    call_args = stub_render.await_args_list[0].args
+    rendered_path = call_args[3]  # path argument to renderer
+    assert rendered_path == keep.relative_to(tmp_path)

Wait, I can see that Line 33 has already extracted the path argument into a named variable rendered_path. The magic index concern has been addressed by adding the explanatory comment.

nixie/cli.py (1)

415-420: Improve help text clarity and accuracy.

The help text should explicitly mention that directories are supported and clarify the default discovery behaviour.

Apply this diff to make the help text more comprehensive:

-        help=(
-            "Markdown files or directories to validate. Defaults to all .md "
-            "files in the current directory, excluding paths ignored by "
-            ".gitignore."
-        ),
+        help=(
+            "Markdown files or directories to validate. When omitted, scans the "
+            "current directory recursively for .md files (honouring .gitignore)."
+        ),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ed2a44f and 419a3d7.

📒 Files selected for processing (4)
  • README.md (2 hunks)
  • docs/CHANGELOG.md (1 hunks)
  • nixie/cli.py (5 hunks)
  • tests/integration/test_gitignore_paths.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.py: Python changes must pass tests (unit and behavioral) before completion/commit
Python code must pass lint checks (make lint)
Python code must adhere to formatting standards (make check-fmt/make fmt)
Python code must pass type checking (make typecheck)
Follow core Python 3.13 style conventions per .rules/python-00.md
Apply best practices for context managers per .rules/python-context-managers.md
Follow generator and iterator patterns per .rules/python-generators.md
Follow function return conventions per .rules/python-return.md
Apply Python typing best practices per .rules/python-typing.md

**/*.py: Name Python files in snake_case (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single underscore
Use typing everywhere; maintain full static type coverage with Pyright
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only
Avoid Any; use Unknown, generics, or cast() with justification if Any is used
Be explicit with return types for all public functions and class methods (e.g., -> None, -> str)
Favor immutability (prefer tuples to lists; use frozendict or types.MappingProxyType where appropriate)
Use # pyright: ignore sparingly and include an explanation when used
Avoid side effects at import time; modules should not modify global state or perform actions on import
Never hardcode secrets in source code
Write NumPy-style docstrings for public functions, classes, and modules
Add inline comments to explain non-obvious logic or decisions

**/*.py: Use context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual management
Use @contextmanager from contextlib for straightforward, linear setup/teardown without persistent internal state
Implement a class-based context manager (enter/exit) when there is internal sta...

Files:

  • tests/integration/test_gitignore_paths.py
  • nixie/cli.py

⚙️ CodeRabbit Configuration File

**/*.py: - Keep cyclomatic complexity ≤ 12

  • Follow single responsibility and CQRS (command/query segregation)
  • Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
  • Move conditionals with >2 branches to predicate/helper functions
  • Avoid eval, exec, pickle, monkey-patching, ctypes, unsafe shell
  • Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
  • Lint suppressions:
    • Blanket # noqa, file-level skips, and categories are forbidden
    • Only narrow in-line disables (# noqa: XYZ) are permitted, and must be accompanied by FIXME: or a ticket link, and used only as a last resort.
  • Use pytest fixtures for shared setup (conftest.py or fixtures/)
  • Replace duplicate tests with @pytest.mark.parametrize
  • Prefer pytest-mock or unittest.mock for stubs/mocks
  • Use assert …, "message" over bare asserts
  • Reflect all API/behaviour changes in docs/ and update roadmap on completion
  • Files must not exceed 400 logical lines:
    • Decompose large modules into subpackages
    • Split large match/case or dispatch tables by domain and collocate with targets if appropriate
    • Move bulky data (fixtures, templates) to external files for parsing at runtime
  • Mutable defaults and shadowed built-ins are forbidden
  • All code must have clear type hints using modern style (A | B, list[str], class Foo[A]:, type Bar = int, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • tests/integration/test_gitignore_paths.py
  • nixie/cli.py
tests/integration/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Place integration tests under tests/integration/ with files prefixed with test_

Files:

  • tests/integration/test_gitignore_paths.py
{**/unittests/test_*.py,tests/integration/test_*.py}

📄 CodeRabbit Inference Engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/integration/test_*.py}: Use pytest idioms: prefer fixtures, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective; test public behaviour, not internals
Avoid excessive mocking; use doubles only for external services or non-deterministic behaviour

Files:

  • tests/integration/test_gitignore_paths.py
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Markdown files must pass markdown linting (make markdownlint)
Markdown files containing Mermaid diagrams must pass nixie validation (make nixie)

Files:

  • README.md
  • docs/CHANGELOG.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • README.md
  • docs/CHANGELOG.md
**/README.md

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate README.md near reusable packages and include usage examples

Files:

  • README.md
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the source of truth for requirements, dependencies, and architectural decisions
Proactively update docs/ markdown when decisions, requirements, dependencies, or architecture change

Files:

  • docs/CHANGELOG.md
docs/**

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Maintain a docs/ directory near reusable packages for documentation

Files:

  • docs/CHANGELOG.md
🧬 Code Graph Analysis (1)
tests/integration/test_gitignore_paths.py (1)
nixie/cli.py (2)
  • cli (436-447)
  • main (380-396)
🔍 Remote MCP Context7 Based on the provided context, it appears that the pull request is focused on improving the file discovery behavior of the `nixie` tool. The key changes include:
  1. Automatically discovering Markdown files in the current working directory when no explicit paths are provided, while respecting the .gitignore file. This is achieved by adding a new discover_markdown_files() function in nixie/cli.py that uses the pathspec library to walk the directory tree and yield non-ignored Markdown files.

  2. Updating the collect_markdown_files() function to also honor the .gitignore file when iterating over the provided paths.

  3. Modifying the CLI argument parsing to make the paths argument optional, with a default behavior of discovering Markdown files when no paths are provided.

  4. Adding unit tests in nixie/unittests/test_discover_markdown_files.py to validate the .gitignore handling when discovering Markdown files.

  5. Adding integration tests in tests/integration/test_no_args.py and tests/integration/test_gitignore_paths.py to verify the CLI behavior when no arguments are provided and when explicit paths are passed, respectively.

  6. Updating the README.md and CHANGELOG.md files to document the new default file discovery behavior and .gitignore support.

Overall, the changes seem focused on improving the user experience by automatically discovering Markdown files in the current directory, while respecting the project's .gitignore configuration. The provided information, including the summary of changes and the list of tests, suggests that the pull request is comprehensive and well-tested.

🔇 Additional comments (4)
nixie/cli.py (2)

74-82: LGTM: Clean helper function for gitignore loading.

The _load_gitignore_spec function properly encapsulates gitignore loading with explicit UTF-8 encoding and handles the case where no gitignore file exists.


95-117: Handle relative path calculation edge cases more gracefully.

The collect_markdown_files function properly handles cases where paths might be outside the working directory, but the error handling could be more explicit.

The implementation correctly:

  • Loads gitignore spec once and reuses it
  • Handles both files and directories
  • Gracefully handles paths outside the working directory by setting rel_path = None
  • Applies gitignore filtering only when both spec and rel_path are available
README.md (2)

7-9: LGTM: Clear documentation of new gitignore functionality.

The feature list clearly communicates the new directory searching and gitignore support capabilities.


50-56: Excellent clarification of gitignore behaviour and file extensions.

The documentation now clearly specifies:

  • File vs directory support
  • Default discovery behaviour when no arguments provided
  • Gitignore exclusion rules
  • Case-sensitive .md extension matching
  • Limitation to working directory gitignore only

This addresses all the key user questions about how file discovery works.

Comment thread nixie/cli.py
Comment thread nixie/cli.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nixie/cli.py (1)

337-345: Fix invalid isinstance() usage with a union type

isinstance(exc, KeyboardInterrupt | SystemExit) will raise at runtime because isinstance does not accept union types. Also, KeyboardInterrupt/SystemExit are not caught by the except Exception clause anyway. Adjust the clause to catch BaseException and re-raise interrupts cleanly.

Apply this diff:

-    except Exception as exc:
-        if isinstance(exc, KeyboardInterrupt | SystemExit):
-            raise
+    except BaseException as exc:
+        # Re-raise interrupts and system exits without logging noise
+        if isinstance(exc, (KeyboardInterrupt, SystemExit)):
+            raise
         LOGGER.exception(
             "%s: unexpected error in diagram %s",
             path,
             idx,
         )
♻️ Duplicate comments (2)
docs/CHANGELOG.md (1)

11-13: Clarify .gitignore scope — now accurate and aligned

The entry now explicitly states that only the working directory’s .gitignore is honoured and nested files are ignored. This matches the implementation and README.

tests/integration/test_no_args.py (1)

41-45: Quote cast target and assert with messages — tidy and readable

The quoted typing.cast("SystemExit", ...) satisfies Ruff TC006, and assertion messages improve diagnostics.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 419a3d7 and 2cdbbd3.

📒 Files selected for processing (4)
  • docs/CHANGELOG.md (1 hunks)
  • nixie/cli.py (5 hunks)
  • nixie/unittests/test_discover_markdown_files.py (1 hunks)
  • tests/integration/test_no_args.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.py: Python changes must pass tests (unit and behavioral) before completion/commit
Python code must pass lint checks (make lint)
Python code must adhere to formatting standards (make check-fmt/make fmt)
Python code must pass type checking (make typecheck)
Follow core Python 3.13 style conventions per .rules/python-00.md
Apply best practices for context managers per .rules/python-context-managers.md
Follow generator and iterator patterns per .rules/python-generators.md
Follow function return conventions per .rules/python-return.md
Apply Python typing best practices per .rules/python-typing.md

**/*.py: Name Python files in snake_case (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single underscore
Use typing everywhere; maintain full static type coverage with Pyright
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only
Avoid Any; use Unknown, generics, or cast() with justification if Any is used
Be explicit with return types for all public functions and class methods (e.g., -> None, -> str)
Favor immutability (prefer tuples to lists; use frozendict or types.MappingProxyType where appropriate)
Use # pyright: ignore sparingly and include an explanation when used
Avoid side effects at import time; modules should not modify global state or perform actions on import
Never hardcode secrets in source code
Write NumPy-style docstrings for public functions, classes, and modules
Add inline comments to explain non-obvious logic or decisions

**/*.py: Use context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual management
Use @contextmanager from contextlib for straightforward, linear setup/teardown without persistent internal state
Implement a class-based context manager (enter/exit) when there is internal sta...

Files:

  • nixie/unittests/test_discover_markdown_files.py
  • tests/integration/test_no_args.py
  • nixie/cli.py

⚙️ CodeRabbit Configuration File

**/*.py: - Keep cyclomatic complexity ≤ 12

  • Follow single responsibility and CQRS (command/query segregation)
  • Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
  • Move conditionals with >2 branches to predicate/helper functions
  • Avoid eval, exec, pickle, monkey-patching, ctypes, unsafe shell
  • Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
  • Lint suppressions:
    • Blanket # noqa, file-level skips, and categories are forbidden
    • Only narrow in-line disables (# noqa: XYZ) are permitted, and must be accompanied by FIXME: or a ticket link, and used only as a last resort.
  • Use pytest fixtures for shared setup (conftest.py or fixtures/)
  • Replace duplicate tests with @pytest.mark.parametrize
  • Prefer pytest-mock or unittest.mock for stubs/mocks
  • Use assert …, "message" over bare asserts
  • Reflect all API/behaviour changes in docs/ and update roadmap on completion
  • Files must not exceed 400 logical lines:
    • Decompose large modules into subpackages
    • Split large match/case or dispatch tables by domain and collocate with targets if appropriate
    • Move bulky data (fixtures, templates) to external files for parsing at runtime
  • Mutable defaults and shadowed built-ins are forbidden
  • All code must have clear type hints using modern style (A | B, list[str], class Foo[A]:, type Bar = int, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • nixie/unittests/test_discover_markdown_files.py
  • tests/integration/test_no_args.py
  • nixie/cli.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate unit tests with code in an unittests/ subdirectory using files prefixed with test_

Files:

  • nixie/unittests/test_discover_markdown_files.py
{**/unittests/test_*.py,tests/integration/test_*.py}

📄 CodeRabbit Inference Engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/integration/test_*.py}: Use pytest idioms: prefer fixtures, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective; test public behaviour, not internals
Avoid excessive mocking; use doubles only for external services or non-deterministic behaviour

Files:

  • nixie/unittests/test_discover_markdown_files.py
  • tests/integration/test_no_args.py
tests/integration/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Place integration tests under tests/integration/ with files prefixed with test_

Files:

  • tests/integration/test_no_args.py
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the source of truth for requirements, dependencies, and architectural decisions
Proactively update docs/ markdown when decisions, requirements, dependencies, or architecture change

Files:

  • docs/CHANGELOG.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Markdown files must pass markdown linting (make markdownlint)
Markdown files containing Mermaid diagrams must pass nixie validation (make nixie)

Files:

  • docs/CHANGELOG.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/CHANGELOG.md
docs/**

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Maintain a docs/ directory near reusable packages for documentation

Files:

  • docs/CHANGELOG.md
🧬 Code Graph Analysis (2)
nixie/unittests/test_discover_markdown_files.py (1)
nixie/cli.py (3)
  • cli (438-455)
  • collect_markdown_files (97-118)
  • discover_markdown_files (84-94)
tests/integration/test_no_args.py (1)
nixie/cli.py (1)
  • cli (438-455)
🔍 Remote MCP Based on the provided context, it appears that the pull request is focused on improving the file discovery behavior of the `nixie` tool. The key changes include:
  1. Automatically discovering Markdown files in the current working directory when no explicit paths are provided, while respecting the .gitignore file. This is achieved by adding a new discover_markdown_files() function in nixie/cli.py that uses the pathspec library to walk the directory tree and yield non-ignored Markdown files.

  2. Updating the collect_markdown_files() function to also honor the .gitignore file when iterating over the provided paths.

  3. Modifying the CLI argument parsing to make the paths argument optional, with a default behavior of discovering Markdown files when no paths are provided.

  4. Adding unit tests in nixie/unittests/test_discover_markdown_files.py to validate the .gitignore handling when discovering Markdown files.

  5. Adding integration tests in tests/integration/test_no_args.py and tests/integration/test_gitignore_paths.py to verify the CLI behavior when no arguments are provided and when explicit paths are passed, respectively.

  6. Updating the README.md and CHANGELOG.md files to document the new default file discovery behavior and .gitignore support.

Overall, the changes seem focused on improving the user experience by automatically discovering Markdown files in the current directory, while respecting the project's .gitignore configuration. The provided information, including the summary of changes and the list of tests, suggests that the pull request is comprehensive and well-tested.

🔇 Additional comments (7)
nixie/cli.py (2)

74-81: Load .gitignore with explicit UTF‑8 — good

Use of UTF‑8 and a dedicated helper to build the PathSpec is correct and robust.


448-455: Handle empty discovery gracefully — good UX

Exiting with code 0 and a clear stderr message when no Markdown files are found is the right behaviour and aligns with tests.

nixie/unittests/test_discover_markdown_files.py (4)

15-20: Use a cwd fixture to remove boilerplate — nice

The cwd_tmp fixture deduplicates chdir setup across tests and follows pytest best practices.


22-34: Validate ignore of directories listed in .gitignore — good

Assert deterministic ordering and correct exclusion of ignored directories. This locks in the discovery contract.


48-62: Assert re-inclusion and deterministic ordering — solid coverage

Verify negation with ! and ordering. The docstring is explicit about the path-sorted ordering, which matches implementation.


86-97: Cover directory-expansion path with .gitignore — good

Exercise collect_markdown_files([cwd_tmp]) and confirm excludes; this matches the CLI’s main loop behaviour.

tests/integration/test_no_args.py (1)

68-75: Verify empty-directory path: exit 0, no main() call, message to stderr — correct

The test nails the CLI’s empty discovery flow and checks the user-facing message precisely.

Comment thread nixie/cli.py
Comment thread nixie/cli.py
Comment thread nixie/cli.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2cdbbd3 and f100bba.

📒 Files selected for processing (1)
  • nixie/unittests/test_discover_markdown_files.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.py: Python changes must pass tests (unit and behavioral) before completion/commit
Python code must pass lint checks (make lint)
Python code must adhere to formatting standards (make check-fmt/make fmt)
Python code must pass type checking (make typecheck)
Follow core Python 3.13 style conventions per .rules/python-00.md
Apply best practices for context managers per .rules/python-context-managers.md
Follow generator and iterator patterns per .rules/python-generators.md
Follow function return conventions per .rules/python-return.md
Apply Python typing best practices per .rules/python-typing.md

**/*.py: Name Python files in snake_case (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single underscore
Use typing everywhere; maintain full static type coverage with Pyright
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only
Avoid Any; use Unknown, generics, or cast() with justification if Any is used
Be explicit with return types for all public functions and class methods (e.g., -> None, -> str)
Favor immutability (prefer tuples to lists; use frozendict or types.MappingProxyType where appropriate)
Use # pyright: ignore sparingly and include an explanation when used
Avoid side effects at import time; modules should not modify global state or perform actions on import
Never hardcode secrets in source code
Write NumPy-style docstrings for public functions, classes, and modules
Add inline comments to explain non-obvious logic or decisions

**/*.py: Use context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual management
Use @contextmanager from contextlib for straightforward, linear setup/teardown without persistent internal state
Implement a class-based context manager (enter/exit) when there is internal sta...

Files:

  • nixie/unittests/test_discover_markdown_files.py

⚙️ CodeRabbit Configuration File

**/*.py: - Keep cyclomatic complexity ≤ 12

  • Follow single responsibility and CQRS (command/query segregation)
  • Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
  • Move conditionals with >2 branches to predicate/helper functions
  • Avoid eval, exec, pickle, monkey-patching, ctypes, unsafe shell
  • Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
  • Lint suppressions:
    • Blanket # noqa, file-level skips, and categories are forbidden
    • Only narrow in-line disables (# noqa: XYZ) are permitted, and must be accompanied by FIXME: or a ticket link, and used only as a last resort.
  • Use pytest fixtures for shared setup (conftest.py or fixtures/)
  • Replace duplicate tests with @pytest.mark.parametrize
  • Prefer pytest-mock or unittest.mock for stubs/mocks
  • Use assert …, "message" over bare asserts
  • Reflect all API/behaviour changes in docs/ and update roadmap on completion
  • Files must not exceed 400 logical lines:
    • Decompose large modules into subpackages
    • Split large match/case or dispatch tables by domain and collocate with targets if appropriate
    • Move bulky data (fixtures, templates) to external files for parsing at runtime
  • Mutable defaults and shadowed built-ins are forbidden
  • All code must have clear type hints using modern style (A | B, list[str], class Foo[A]:, type Bar = int, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • nixie/unittests/test_discover_markdown_files.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.md)

Colocate unit tests with code in an unittests/ subdirectory using files prefixed with test_

Files:

  • nixie/unittests/test_discover_markdown_files.py
{**/unittests/test_*.py,tests/integration/test_*.py}

📄 CodeRabbit Inference Engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/integration/test_*.py}: Use pytest idioms: prefer fixtures, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective; test public behaviour, not internals
Avoid excessive mocking; use doubles only for external services or non-deterministic behaviour

Files:

  • nixie/unittests/test_discover_markdown_files.py
🧬 Code Graph Analysis (1)
nixie/unittests/test_discover_markdown_files.py (1)
nixie/cli.py (3)
  • cli (438-455)
  • collect_markdown_files (97-118)
  • discover_markdown_files (84-94)
🔍 Remote MCP Based on the provided context, it appears that the pull request is focused on improving the file discovery behavior of the `nixie` tool. The key changes include:
  1. Automatically discovering Markdown files in the current working directory when no explicit paths are provided, while respecting the .gitignore file. This is achieved by adding a new discover_markdown_files() function in nixie/cli.py that uses the pathspec library to walk the directory tree and yield non-ignored Markdown files.

  2. Updating the collect_markdown_files() function to also honor the .gitignore file when iterating over the provided paths.

  3. Modifying the CLI argument parsing to make the paths argument optional, with a default behavior of discovering Markdown files when no paths are provided.

  4. Adding unit tests in nixie/unittests/test_discover_markdown_files.py to validate the .gitignore handling when discovering Markdown files.

  5. Adding integration tests in tests/integration/test_no_args.py and tests/integration/test_gitignore_paths.py to verify the CLI behavior when no arguments are provided and when explicit paths are passed, respectively.

  6. Updating the README.md and CHANGELOG.md files to document the new default file discovery behavior and .gitignore support.

Overall, the changes seem focused on improving the user experience by automatically discovering Markdown files in the current directory, while respecting the project's .gitignore configuration. The provided information, including the summary of changes and the list of tests, suggests that the pull request is comprehensive and well-tested.

🔇 Additional comments (1)
nixie/unittests/test_discover_markdown_files.py (1)

15-19: Good fixture to deduplicate chdir boilerplate

The cwd_tmp fixture cleanly centralises working-directory setup and matches pytest idioms. No changes needed.

Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
Comment thread nixie/unittests/test_discover_markdown_files.py
Comment thread nixie/unittests/test_discover_markdown_files.py
Comment thread nixie/unittests/test_discover_markdown_files.py
Comment thread nixie/unittests/test_discover_markdown_files.py
Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
Comment thread nixie/unittests/test_discover_markdown_files.py Outdated
@leynos
leynos merged commit a8051ee into main Aug 19, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant