# Copyright 2026 Julien Bombled # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 [build-system] requires = ["hatchling>=1.27"] build-backend = "hatchling.build" [project] name = "datacron" dynamic = ["version"] description = "Local-first MCP server that exposes a Markdown vault through MCP." readme = "README.md" requires-python = ">=3.11" license = "Apache-2.0" license-files = ["LICENSE"] authors = [ { name = "Julien Bombled" }, ] keywords = ["mcp", "markdown", "vault", "claude", "obsidian", "local-first"] classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: Markdown", "Typing :: Typed", ] dependencies = [ # Core "pydantic>=2.6,<3", "pydantic-settings>=2.2", "python-frontmatter>=1.1", "python-ulid>=2.4", "pyyaml>=6.0", "tomli-w>=1.0", "truststore>=0.10.4", "typer>=0.12", # MCP server (Phase 0 Sem 2) "mcp>=2,<3", # Indexing (Phase 0 Sem 2-3) "aiosqlite>=0.20", "mistletoe>=1.3", # Eval (Phase 0 Sem 4) "rich>=13.7", ] [project.optional-dependencies] dev = [ "httpx>=0.28,<1", "ruff>=0.13.2", "mypy>=1.11", "packaging>=24.0", "pytest>=9.0.3", "pytest-asyncio>=0.24", "pytest-cov>=5.0", "hypothesis>=6.112", "types-PyYAML>=6.0", ] build = [ "pyinstaller>=6.0", ] [project.scripts] datacron = "datacron.cli:app" datacron-mcp = "datacron.cli:mcp_entry" [project.urls] Homepage = "https://github.com/VBlackJack/Datacron" Documentation = "https://github.com/VBlackJack/Datacron/blob/main/README.md" Source = "https://github.com/VBlackJack/Datacron" Issues = "https://github.com/VBlackJack/Datacron/issues" [tool.hatch.version] path = "src/datacron/__init__.py" [tool.hatch.build.targets.wheel] packages = ["src/datacron"] [tool.hatch.build.targets.sdist] include = [ "src/datacron", "README.md", "README.fr.md", "LICENSE", "docs/en/spec.md", "docs/fr/spec.md", "pyproject.toml", ] # --------------------------------------------------------------------------- # Ruff # --------------------------------------------------------------------------- [tool.ruff] line-length = 100 target-version = "py311" src = ["src", "tests"] extend-exclude = [ ".venv", "build", "dist", ] [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "B", # bugbear "UP", # pyupgrade "N", # pep8-naming "ASYNC", # async-style "S", # bandit "C4", # comprehensions "PIE", # pie "PT", # pytest-style "RET", # return "SIM", # simplify "TID", # tidy-imports "TCH", # type-checking "PL", # pylint "RUF", # ruff ] ignore = [ "S101", # assert allowed in tests "PLR0913", # too many args "PLR2004", # magic value comparison (covered case-by-case) "TC001", # Pydantic resolves annotations at runtime "TC002", "TC003", "ASYNC240", # we wrap blocking I/O via asyncio.to_thread deliberately ] [tool.ruff.lint.per-file-ignores] "tests/**/*.py" = ["S101", "PLR2004", "S603", "S607", "PLR0915", "PLC0415"] "src/datacron/cli.py" = ["B008", "PLR0915"] [tool.ruff.lint.isort] known-first-party = ["datacron"] [tool.ruff.format] quote-style = "double" indent-style = "space" line-ending = "lf" # --------------------------------------------------------------------------- # Mypy # --------------------------------------------------------------------------- [tool.mypy] python_version = "3.11" strict = true files = ["src", "tests"] plugins = ["pydantic.mypy"] warn_unused_ignores = true warn_return_any = true warn_unreachable = true disallow_untyped_decorators = true [[tool.mypy.overrides]] module = [ "frontmatter.*", "mistletoe.*", "mcp.*", ] ignore_missing_imports = true [tool.pydantic-mypy] init_forbid_extra = true init_typed = true warn_required_dynamic_aliases = true # --------------------------------------------------------------------------- # Pytest # --------------------------------------------------------------------------- [tool.pytest.ini_options] minversion = "8.0" testpaths = ["tests"] addopts = [ "-ra", "--strict-markers", # Disable the cacheprovider plugin: .pytest_cache writes via the same shared # path on Windows have hit ACL errors in this project; we don't rely on # --last-failed/--ff so the cache is pure overhead. Restore if needed. "-p", "no:cacheprovider", "--strict-config", ] asyncio_mode = "auto" markers = [ "integration: end-to-end tests that exercise external binaries (e.g. ripgrep)", "invariants: blocking reliability properties and global baseline checks", "slow: long-running tests", ] filterwarnings = [ "error", "ignore::DeprecationWarning:pydantic.*", # Windows ProactorEventLoop emits unraisable warnings after asyncio.run(). "ignore::pytest.PytestUnraisableExceptionWarning", ] [tool.coverage.run] source = ["src/datacron"] branch = true [tool.coverage.report] fail_under = 85 exclude_lines = [ "pragma: no cover", "raise NotImplementedError", "if TYPE_CHECKING:", ] [dependency-groups] dev = [ "pytest-xdist>=3.8.0", ] [tool.datacron.release] expected_email = "222413562+VBlackJack@users.noreply.github.com"