[project] name = "forensic-deepdive" version = "0.10.0" description = "Forensic deep-dive of any codebase (9 languages). Persistent code knowledge graph + MCP server (9 composite tools incl. hybrid NL query, Mermaid export & cross-stack trace) for AI coding agents, with 5 durable markdown artifacts (MAP, HOTPATHS, ARCHAEOLOGY, MENTAL_MODEL, AGENT_BRIEF) as the human-readable projection." readme = "README.md" requires-python = ">=3.11" license = { text = "Apache-2.0" } authors = [{ name = "Dhevenddra" }] keywords = [ "claude-code", "cursor", "continue", "aider", "codex", "codebase-analysis", "tree-sitter", "agent-skills", "onboarding", ] classifiers = [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] dependencies = [ # DEC-088: `tree_sitter` is imported directly across ~30 modules # (static/parse.py uses Parser/Tree/Query/QueryCursor — QueryCursor is a # 0.25+ API). It was only ever present transitively in dev; a clean wheel # install of tree-sitter-language-pack 1.9.x does NOT pull it (the pack # declares no tree-sitter dependency and bundles its own native bindings), so # `forensic info`/extract crashed (ModuleNotFoundError, then an ABI mismatch: # the pack's `Language` is rejected by a mismatched standalone `Parser`). # Pin the dev-proven, ABI-compatible PAIR (the 796-test environment): the # 1.8.x pack line against tree-sitter 0.25.x. Both ship broad cp310-abi3 # wheels (Linux/macOS-arm64/Windows). Bumping the pair is a deliberate, # re-tested change, not an implicit float. "tree-sitter>=0.25,<0.26", "tree-sitter-language-pack>=1.8,<1.9", "networkx>=3.3", "typer>=0.12", # DEC-077 (v0.7 Track B): rich is the load-bearing dependency for the styled CLI # (cli/style/*). It was already a direct dep (DEC-071 assumed it was only transitive via # typer — it was not); v0.7 makes that intentional and widens the pin to the installed # major (15.x is locked; the Console/Theme/Panel APIs we use are stable across 14–15). # textual + pyfiglet stay deferred (no new transitive package). "rich>=14,<16", "pygithub>=2.3", "httpx>=0.27", "pydantic>=2.7", "jinja2>=3.1", "real-ladybug>=0.15.3", # Upper bound is load-bearing (DEC-118): mcp 2.0.0 removed `mcp.server.fastmcp`, # which `mcp_server/server.py` imports. Unbounded, a clean `pip install` resolved # to 2.0.0 and every wheel install broke on import while uv.lock kept the dev # environment green. Raise the ceiling only with a tested port to the 2.x API. "mcp>=1.27.1,<2", ] [project.optional-dependencies] graphiti = ["graphiti-core>=0.28"] mcp = ["mcp>=1.0,<2"] # DEC-042 (v0.3 Item E): opt-in offline ONNX semantic retriever for hybrid NL # query. The only numpy in the tree (core stays numpy/scipy-free, DEC-011). The # embedding model is NOT bundled or auto-downloaded — point FORENSIC_SEMANTIC_MODEL # at a local ONNX model. Absent ⇒ NL query runs lexical+structural and says so. semantic = ["onnxruntime>=1.17", "tokenizers>=0.15", "numpy>=1.26"] # DEC-048 (v0.4 Item I): opt-in YAML OpenAPI/Swagger spec parsing for the codegen # shortcut. JSON specs parse zero-dep (stdlib); only YAML needs pyyaml. Absent ⇒ # YAML specs are skipped loudly (logged), JSON specs still work. openapi = ["pyyaml>=6.0"] # DEC-099/100 (v0.9 Track A): the human-facing interactive layer (`forensic # repl` + `forensic browse`, later onboard/deepdive). Heavier than the # agent-first core needs (extract + serve stay lean), so it is opt-in. # prompt_toolkit and textual are both MIT. They are never nested: the REPL/ # shell own the prompt loop; the Textual App is launched blocking (DEC-100). interactive = ["prompt-toolkit>=3.0", "textual>=0.80"] dev = ["pytest>=8.0", "pytest-cov>=5.0", "ruff>=0.6", "ty"] all = ["forensic-deepdive[graphiti,mcp,openapi,interactive,dev]"] # DEC-087/088 (v0.8): the FastContext usefulness experiment harness in # experiments/fastcontext/ is a PEP 735 dependency-group, not a package extra — # it is dev/experiment tooling, NOT a user-facing optional feature, and is never # shipped (the wheel packages only src/forensic_deepdive). Installed with # `uv sync --group experiment`. `datasets` is only for the real SWE-bench Arm-A # run; the harness `--self-test` path needs none of it. [dependency-groups] experiment = ["datasets>=2.0"] [project.scripts] forensic = "forensic_deepdive.cli:app" # DEC-089: a package-name alias so `uvx forensic-deepdive serve --repo .` works # directly (uvx resolves a command matching the package name). `forensic` stays # the primary/documented binary (DEC-010); this is the MCP-config ergonomic form # used uniformly across the registry server.json and every client install block. forensic-deepdive = "forensic_deepdive.cli:app" # DEC-102 (v0.9): the session shell gets its own verb — `deepdive` is the noun # users say, and the shell is a different mode from the `forensic` subcommands # (one held-open graph, in-session commands). Needs the [interactive] extra. deepdive = "forensic_deepdive.cli.interactive.shell:main" [project.urls] Homepage = "https://github.com/Dhevenddra/forensic-deepdive" Issues = "https://github.com/Dhevenddra/forensic-deepdive/issues" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/forensic_deepdive"] # Vendored Sigma.js/graphology UI client + HTML/CSS/JS (DEC-053). Non-Python # assets must be force-included so they ship in the wheel. artifacts = [ "src/forensic_deepdive/serve/assets/**/*.js", "src/forensic_deepdive/serve/assets/**/*.css", "src/forensic_deepdive/serve/assets/**/*.html", "src/forensic_deepdive/serve/assets/**/*.md", ] [tool.ruff] line-length = 100 target-version = "py311" # Test fixtures are sample code, not project code — don't lint or autofix them. extend-exclude = ["tests/fixtures"] [tool.ruff.lint] select = ["E", "F", "I", "N", "UP", "B", "SIM"] [tool.pytest.ini_options] testpaths = ["tests"] python_files = ["test_*.py"] addopts = "-x --tb=short" # tests/fixtures/ contains synthetic sample code (including files named like # test_*.py inside fixture repos). Don't recurse into fixture dirs. norecursedirs = ["fixtures", "expected_emit"]