Scan working directory when no paths are provided - #24
Conversation
Reviewer's GuideIntroduces 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 CLIsequenceDiagram
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
Class diagram for discover_markdown_files and CLI argument changesclassDiagram
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)
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Note Other AI code review bot(s) detectedCodeRabbit 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 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. 📒 Files selected for processing (3)
Summary by CodeRabbit
WalkthroughImplement 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 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
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.
⛔ Files ignored due to path filters (1)
uv.lockis 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.mddocs/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.mddocs/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: ignoresparingly 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.pynixie/unittests/test_discover_markdown_files.pynixie/cli.py
⚙️ CodeRabbit Configuration File
**/*.py: - Keep cyclomatic complexity ≤ 12
- Follow single responsibility and CQRS (command/query segregation)
- Docstrings must follow the
numpystyle 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 byFIXME:or a ticket link, and used only as a last resort.- Use
pytestfixtures for shared setup (conftest.pyorfixtures/)- Replace duplicate tests with
@pytest.mark.parametrize- Prefer
pytest-mockorunittest.mockfor 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/caseor 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.pynixie/unittests/test_discover_markdown_files.pynixie/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.pynixie/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 innixie/cli.pyto match (see separate comment in cli.py).nixie/cli.py (1)
33-34: Importpathspec(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.abcandPathunderTYPE_CHECKINGto satisfy static typing without incurring runtime imports. Good.
28-33: Use an async test double to interceptmaincleanly.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 viasys.argvand assert onSystemExit.Patch
sys.argv, switch CWD, and callcli()to exercise the integration path end-to-end. This is the right level for an integration test.
There was a problem hiding this comment.
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.
⛔ Files ignored due to path filters (1)
uv.lockis 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: ignoresparingly 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.pytests/integration/test_no_args.pynixie/cli.pytests/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
numpystyle 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 byFIXME:or a ticket link, and used only as a last resort.- Use
pytestfixtures for shared setup (conftest.pyorfixtures/)- Replace duplicate tests with
@pytest.mark.parametrize- Prefer
pytest-mockorunittest.mockfor 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/caseor 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.pytests/integration/test_no_args.pynixie/cli.pytests/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.pytests/integration/test_no_args.pytests/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.pytests/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.mdREADME.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.mdREADME.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.0matches 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
.gitignorematches 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 correctlySet 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 correctlyImport 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 rootCreate 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 ignoresAssert 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 rulesDrive collect_markdown_files over an explicit directory while honouring .gitignore. This complements discover_markdown_files coverage well.
There was a problem hiding this comment.
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 directoryWait, 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.
📒 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: ignoresparingly 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.pynixie/cli.py
⚙️ CodeRabbit Configuration File
**/*.py: - Keep cyclomatic complexity ≤ 12
- Follow single responsibility and CQRS (command/query segregation)
- Docstrings must follow the
numpystyle 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 byFIXME:or a ticket link, and used only as a last resort.- Use
pytestfixtures for shared setup (conftest.pyorfixtures/)- Replace duplicate tests with
@pytest.mark.parametrize- Prefer
pytest-mockorunittest.mockfor 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/caseor 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.pynixie/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.mddocs/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.mddocs/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:-
Automatically discovering Markdown files in the current working directory when no explicit paths are provided, while respecting the
.gitignorefile. This is achieved by adding a newdiscover_markdown_files()function innixie/cli.pythat uses thepathspeclibrary to walk the directory tree and yield non-ignored Markdown files. -
Updating the
collect_markdown_files()function to also honor the.gitignorefile when iterating over the provided paths. -
Modifying the CLI argument parsing to make the
pathsargument optional, with a default behavior of discovering Markdown files when no paths are provided. -
Adding unit tests in
nixie/unittests/test_discover_markdown_files.pyto validate the.gitignorehandling when discovering Markdown files. -
Adding integration tests in
tests/integration/test_no_args.pyandtests/integration/test_gitignore_paths.pyto verify the CLI behavior when no arguments are provided and when explicit paths are passed, respectively. -
Updating the
README.mdandCHANGELOG.mdfiles to document the new default file discovery behavior and.gitignoresupport.
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_specfunction 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_filesfunction 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
specandrel_pathare availableREADME.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.
There was a problem hiding this comment.
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 becauseisinstancedoes not accept union types. Also, KeyboardInterrupt/SystemExit are not caught by theexcept Exceptionclause anyway. Adjust the clause to catchBaseExceptionand 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 alignedThe entry now explicitly states that only the working directory’s
.gitignoreis 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 readableThe 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.
📒 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: ignoresparingly 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.pytests/integration/test_no_args.pynixie/cli.py
⚙️ CodeRabbit Configuration File
**/*.py: - Keep cyclomatic complexity ≤ 12
- Follow single responsibility and CQRS (command/query segregation)
- Docstrings must follow the
numpystyle 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 byFIXME:or a ticket link, and used only as a last resort.- Use
pytestfixtures for shared setup (conftest.pyorfixtures/)- Replace duplicate tests with
@pytest.mark.parametrize- Prefer
pytest-mockorunittest.mockfor 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/caseor 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.pytests/integration/test_no_args.pynixie/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.pytests/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:-
Automatically discovering Markdown files in the current working directory when no explicit paths are provided, while respecting the
.gitignorefile. This is achieved by adding a newdiscover_markdown_files()function innixie/cli.pythat uses thepathspeclibrary to walk the directory tree and yield non-ignored Markdown files. -
Updating the
collect_markdown_files()function to also honor the.gitignorefile when iterating over the provided paths. -
Modifying the CLI argument parsing to make the
pathsargument optional, with a default behavior of discovering Markdown files when no paths are provided. -
Adding unit tests in
nixie/unittests/test_discover_markdown_files.pyto validate the.gitignorehandling when discovering Markdown files. -
Adding integration tests in
tests/integration/test_no_args.pyandtests/integration/test_gitignore_paths.pyto verify the CLI behavior when no arguments are provided and when explicit paths are passed, respectively. -
Updating the
README.mdandCHANGELOG.mdfiles to document the new default file discovery behavior and.gitignoresupport.
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 — goodUse of UTF‑8 and a dedicated helper to build the PathSpec is correct and robust.
448-455: Handle empty discovery gracefully — good UXExiting 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 — niceThe
cwd_tmpfixture deduplicates chdir setup across tests and follows pytest best practices.
22-34: Validate ignore of directories listed in .gitignore — goodAssert deterministic ordering and correct exclusion of ignored directories. This locks in the discovery contract.
48-62: Assert re-inclusion and deterministic ordering — solid coverageVerify negation with
!and ordering. The docstring is explicit about the path-sorted ordering, which matches implementation.
86-97: Cover directory-expansion path with .gitignore — goodExercise
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 — correctThe test nails the CLI’s empty discovery flow and checks the user-facing message precisely.
There was a problem hiding this comment.
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.
📒 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: ignoresparingly 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
numpystyle 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 byFIXME:or a ticket link, and used only as a last resort.- Use
pytestfixtures for shared setup (conftest.pyorfixtures/)- Replace duplicate tests with
@pytest.mark.parametrize- Prefer
pytest-mockorunittest.mockfor 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/caseor 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:-
Automatically discovering Markdown files in the current working directory when no explicit paths are provided, while respecting the
.gitignorefile. This is achieved by adding a newdiscover_markdown_files()function innixie/cli.pythat uses thepathspeclibrary to walk the directory tree and yield non-ignored Markdown files. -
Updating the
collect_markdown_files()function to also honor the.gitignorefile when iterating over the provided paths. -
Modifying the CLI argument parsing to make the
pathsargument optional, with a default behavior of discovering Markdown files when no paths are provided. -
Adding unit tests in
nixie/unittests/test_discover_markdown_files.pyto validate the.gitignorehandling when discovering Markdown files. -
Adding integration tests in
tests/integration/test_no_args.pyandtests/integration/test_gitignore_paths.pyto verify the CLI behavior when no arguments are provided and when explicit paths are passed, respectively. -
Updating the
README.mdandCHANGELOG.mdfiles to document the new default file discovery behavior and.gitignoresupport.
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 boilerplateThe cwd_tmp fixture cleanly centralises working-directory setup and matches pytest idioms. No changes needed.
Summary
Testing
make check-fmtmake lintmake typecheckmake testmake markdownlint(fails: reference link definitions missing in .rules/ and other files)/root/.bun/bin/markdownlint-cli2 README.md docs/CHANGELOG.mdmake nixiehttps://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:
Enhancements:
Documentation:
Tests: