[build-system] requires = ["setuptools>=77", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] version_file = "silica_core/_version.py" # The plugin marketplace copies this tree into ~/.claude/plugins/cache # without .git, and mcp.json builds it there, so version inference has # no tags to read and raised, taking the whole MCP server down with it. fallback_version = "0.0.0" [project] name = "silica-core" dynamic = ["version"] description = "Retrieval tools for coding agents: files, search, read, code_pack, write_note over a folder, no model in the loop" readme = "README.md" requires-python = ">=3.11" license = "MIT" authors = [{ name = "Alessandro Carosia" }] classifiers = [ "Environment :: Console", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Text Processing", ] dependencies = [ "httpx", "pydantic>=2.0", "pyyaml>=6.0", "python-dotenv>=1.0", "networkx>=3", "markdown-it-py", # Serializer for every derived store; measured 5x faster than json on a # 108 MB index at every flush (ADR-0037 in the private tree). "orjson", "snowballstemmer", "stop-words>=2025.11.4", # 0.26.0 corrupts the heap walking a tree it parsed: ~900 of fastapi's test # files in one process segfault the interpreter, in tree-sitter alone with # no silica in the picture, and 0.25.2 walks the same files clean. Every # language pack version is affected, so the ceiling is on tree-sitter. "tree-sitter>=0.25,<0.26", "tree-sitter-language-pack>=1.13", # PDF text layer (BSD-3/Apache-2.0, one ~3 MB wheel). Scans need mineru, # installed separately and called as a command. "pypdfium2>=5", "mammoth>=1.9", "striprtf>=0.0.29", "xlrd>=2.0", ] [project.optional-dependencies] dev = ["pytest>=8.0", "mypy>=1.10", "import-linter>=2.0", "ruff>=0.16"] connect = ["websockets>=16.0"] mcp = ["mcp>=1.0,<2"] # The dense leg without an endpoint: a static model2vec model (~30 MB, no torch, # no ONNX) and numpy for the dot products. `SILICA_EMBEDDING_MODEL=model2vec/`. dense = ["model2vec>=0.5", "numpy>=1.26"] all = ["silica-core[connect,mcp,dense]"] [project.scripts] silica = "silica_core.entry:main" # uvx runs the console script named after the package it was given, and the # registry entry in server.json launches `uvx --from silica-core[mcp,dense] # silica-core mcp --retrieval local-hybrid`. Without this alias every registry-driven install dies at # "executable not found" with the wheel sitting right there. silica-core = "silica_core.entry:main" [project.urls] Homepage = "https://github.com/kiycoh/silica-core" Repository = "https://github.com/kiycoh/silica-core" Issues = "https://github.com/kiycoh/silica-core/issues" [tool.setuptools] # skills/ is a non-package subdir of silica (the harness skill the setup # command installs): include-package-data ships it with the wheel. include-package-data = true [tool.setuptools.packages.find] include = ["silica_core", "silica_core.*"] [tool.pytest.ini_options] testpaths = ["tests"] [tool.ruff.lint] # Dead code, not style. Measured 2026-09-03 over 711 files: this select reports # 190 findings (158 of them unused imports), ruff's own default select reports # 2245 — and those extra 2055 are churn that rewrites rationale comments and # git blame to buy nothing. Widen only for a one-shot audit, never for the gate: # --select BLE001 alone reports 594 blind excepts, which is the CLAUDE.md # "a swallowed exception must name what it tolerates" rule. That is a work list, # not a merge blocker. select = ["F", "E9"] [tool.importlinter] root_packages = ["silica_core"] include_external_packages = true [[tool.importlinter.contracts]] name = "the kernel, the driver and the converters never import a surface" type = "forbidden" source_modules = ["silica_core.kernel", "silica_core.driver", "silica_core.sources"] forbidden_modules = ["silica_core.core", "silica_core.cli", "silica_core.ui", "silica_core.onboarding", "silica_core.repl"] allow_indirect_imports = true [[tool.importlinter.contracts]] name = "the core never reaches the driver or a surface" type = "forbidden" source_modules = ["silica_core.core"] forbidden_modules = ["silica_core.driver", "silica_core.cli", "silica_core.ui", "silica_core.onboarding", "silica_core.repl"] allow_indirect_imports = true [[tool.importlinter.contracts]] # One arrow: silica-harness depends on this package, never the reverse. The # core does not know the private tree exists (its ADR-0043). name = "the core never imports the harness" type = "forbidden" source_modules = ["silica_core"] forbidden_modules = ["silica"] allow_indirect_imports = true [[tool.importlinter.contracts]] name = "the kernel makes no network call" type = "forbidden" source_modules = ["silica_core.kernel"] forbidden_modules = ["httpx", "websockets"] allow_indirect_imports = true [tool.mypy] python_version = "3.11" ignore_missing_imports = false [[tool.mypy.overrides]] module = [ "yaml", "yaml.*", "networkx", "networkx.*", # PDF providers, all three installed by the user, none a dependency here: "docling.*", "docling_core.*", "opendataloader_pdf", "mammoth", "mammoth.*", "pypdfium2", "pypdfium2.*", # Legacy office readers: both ship no stubs (xlrd has been feature-frozen # since 2.0 dropped .xlsx, which is exactly why it is safe to depend on). "xlrd", "striprtf.*", # No stubs and no py.typed: the NLP pair (stop-words, snowballstemmer) # plus two that DO have stub packages on PyPI — types-python-dateutil and # types-Pygments. Listed here rather than installed because each is reached # from one narrow call site; add the stub package instead if either ever # grows a real surface. "stop_words", "snowballstemmer", "dateutil.*", "pygments", "pygments.*", # The [rerank] extra's cross-encoder. Listed for the same reason the PDF # providers are: ci.yml deliberately syncs without it (3.8 GB of torch to # run tests that skip), so mypy would fail on a package the gate is not # meant to install — and did, on every push, while passing on a checkout # that happened to have the extra. "sentence_transformers", "sentence_transformers.*", ] ignore_missing_imports = true [[tool.mypy.overrides]] # The [dense] extra's array library ships stubs written for 3.12+ (`type` # statements), and this gate type-checks as 3.11: found, not followed. module = ["numpy", "numpy.*", "model2vec", "model2vec.*"] follow_imports = "skip" ignore_missing_imports = true