Metadata-Version: 2.4
Name: sanad
Version: 0.9.14
Summary: Sanad - a graph brain for AI coding agents: deterministic knowledge graph, graph-ops reasoning, lens debate council, edit blast-radius prediction, and a hallucination gate that rejects any claim without a chain of proof. No answer without Sanad.
License: MIT License
        
        Copyright (c) 2026 Safi Shamsi
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/safishamsi/graphify
Project-URL: Repository, https://github.com/safishamsi/graphify
Project-URL: Issues, https://github.com/safishamsi/graphify/issues
Keywords: claude,claude-code,codex,opencode,kilo,cursor,gemini,aider,kiro,pi,devin,knowledge-graph,rag,graphrag,obsidian,community-detection,tree-sitter,leiden,llm
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: networkx>=3.4
Requires-Dist: numpy>=1.21
Requires-Dist: rapidfuzz>=3.0
Requires-Dist: tree-sitter<0.26,>=0.23.0
Requires-Dist: tree-sitter-python<0.26,>=0.23
Requires-Dist: tree-sitter-javascript<0.26,>=0.23
Requires-Dist: tree-sitter-typescript<0.25,>=0.23
Requires-Dist: tree-sitter-go<0.26,>=0.23
Requires-Dist: tree-sitter-rust<0.25,>=0.23
Requires-Dist: tree-sitter-java<0.25,>=0.23
Requires-Dist: tree-sitter-groovy<0.3,>=0.1
Requires-Dist: tree-sitter-c<0.25,>=0.23
Requires-Dist: tree-sitter-cpp<0.25,>=0.23
Requires-Dist: tree-sitter-ruby<0.25,>=0.23
Requires-Dist: tree-sitter-c-sharp<0.25,>=0.23
Requires-Dist: tree-sitter-kotlin<2.0,>=1.0
Requires-Dist: tree-sitter-scala<0.27,>=0.23
Requires-Dist: tree-sitter-php<0.25,>=0.23
Requires-Dist: tree-sitter-swift<0.9,>=0.7
Requires-Dist: tree-sitter-lua<0.6,>=0.2
Requires-Dist: tree-sitter-zig<2.0,>=1.0
Requires-Dist: tree-sitter-powershell<0.28,>=0.26
Requires-Dist: tree-sitter-elixir<0.5,>=0.3
Requires-Dist: tree-sitter-objc<4.0,>=3.0
Requires-Dist: tree-sitter-julia<0.25,>=0.23
Requires-Dist: tree-sitter-verilog<2.0,>=1.0
Requires-Dist: tree-sitter-fortran<0.8,>=0.6
Requires-Dist: tree-sitter-bash<0.27,>=0.23
Requires-Dist: tree-sitter-json<0.26,>=0.23
Provides-Extra: mcp
Requires-Dist: mcp; extra == "mcp"
Requires-Dist: starlette>=1.3.1; extra == "mcp"
Provides-Extra: neo4j
Requires-Dist: neo4j; extra == "neo4j"
Provides-Extra: falkordb
Requires-Dist: falkordb; extra == "falkordb"
Provides-Extra: pdf
Requires-Dist: pypdf>=6.12.0; extra == "pdf"
Requires-Dist: markdownify; extra == "pdf"
Provides-Extra: watch
Requires-Dist: watchdog; extra == "watch"
Provides-Extra: svg
Requires-Dist: matplotlib; extra == "svg"
Requires-Dist: numpy>=2.0; python_version >= "3.13" and extra == "svg"
Provides-Extra: leiden
Requires-Dist: graspologic; python_version < "3.13" and extra == "leiden"
Provides-Extra: office
Requires-Dist: python-docx; extra == "office"
Requires-Dist: openpyxl; extra == "office"
Provides-Extra: google
Requires-Dist: openpyxl; extra == "google"
Provides-Extra: postgres
Requires-Dist: psycopg[binary]; extra == "postgres"
Provides-Extra: video
Requires-Dist: faster-whisper; python_version >= "3.11" and extra == "video"
Requires-Dist: yt-dlp>=2026.6.9; extra == "video"
Provides-Extra: kimi
Requires-Dist: openai; extra == "kimi"
Requires-Dist: tiktoken; extra == "kimi"
Provides-Extra: ollama
Requires-Dist: openai; extra == "ollama"
Provides-Extra: bedrock
Requires-Dist: boto3; extra == "bedrock"
Provides-Extra: anthropic
Requires-Dist: anthropic; extra == "anthropic"
Provides-Extra: gemini
Requires-Dist: openai; extra == "gemini"
Requires-Dist: tiktoken; extra == "gemini"
Provides-Extra: openai
Requires-Dist: openai; extra == "openai"
Requires-Dist: tiktoken; extra == "openai"
Provides-Extra: chinese
Requires-Dist: jieba; extra == "chinese"
Provides-Extra: sql
Requires-Dist: tree-sitter-sql; extra == "sql"
Provides-Extra: pascal
Requires-Dist: tree-sitter-pascal; extra == "pascal"
Provides-Extra: dm
Requires-Dist: tree-sitter-dm; extra == "dm"
Provides-Extra: terraform
Requires-Dist: tree-sitter-hcl; extra == "terraform"
Provides-Extra: all
Requires-Dist: mcp; extra == "all"
Requires-Dist: starlette>=1.3.1; extra == "all"
Requires-Dist: neo4j; extra == "all"
Requires-Dist: falkordb; extra == "all"
Requires-Dist: pypdf>=6.12.0; extra == "all"
Requires-Dist: markdownify; extra == "all"
Requires-Dist: watchdog; extra == "all"
Requires-Dist: graspologic; python_version < "3.13" and extra == "all"
Requires-Dist: python-docx; extra == "all"
Requires-Dist: openpyxl; extra == "all"
Requires-Dist: faster-whisper; python_version >= "3.11" and extra == "all"
Requires-Dist: yt-dlp>=2026.6.9; extra == "all"
Requires-Dist: matplotlib; extra == "all"
Requires-Dist: numpy>=2.0; python_version >= "3.13" and extra == "all"
Requires-Dist: openai; extra == "all"
Requires-Dist: tiktoken; extra == "all"
Requires-Dist: boto3; extra == "all"
Requires-Dist: anthropic; extra == "all"
Requires-Dist: tree-sitter-sql; extra == "all"
Requires-Dist: jieba; extra == "all"
Requires-Dist: tree-sitter-dm; extra == "all"
Requires-Dist: tree-sitter-hcl; extra == "all"
Requires-Dist: tree-sitter-pascal; extra == "all"
Dynamic: license-file

<div align="center">

# Sanad — سند

**A graph brain for AI coding agents.**
*No answer without a chain of proof.*

[العربية](README.ar.md)

</div>

---

In hadith scholarship, no statement is accepted without a **sanad** — a verified chain of transmission back to the source. Sanad applies the same standard to AI and code: **no claim about your codebase passes unless the knowledge graph can prove it.**

Sanad maps any project into a deterministic knowledge graph (tree-sitter AST — no LLM, zero cost, nothing leaves your machine), then gives AI coding agents four abilities that don't exist together anywhere else:

| Pillar | Command | What it does |
|---|---|---|
| 🛡️ **Hallucination Gate** | `sanad verify` | Every structural claim an AI makes ("X calls Y", "Z is defined in F") is mechanically judged against the graph: **VERIFIED** (with proof path), **VERIFIED_INDIRECT**, **UNKNOWN**, or **REFUTED**. `--strict` blocks refuted answers with a non-zero exit code. |
| 🔮 **Blast-Radius Oracle** | `sanad predict` / `check-impact` | Before an edit: predicts every file the change should ripple into and saves it as a contract. After the edit: diffs reality against the contract. A change outside the prediction — the classic "I edited something I didn't understand" — is flagged as a **DEVIATION**. |
| 🧠 **Graph-Ops Thinking** | `sanad ops` / `think` | Instead of pasting files into a model, the model emits tiny ops (`find`, `callers`, `path`, `expand`...) and the graph engine computes locally for free. Nodes get short refs (`n1`, `n2`) so follow-ups cost a few tokens. Small models investigate like detectives — deep multi-hop reasoning at near-constant token cost, and they *cannot* invent a function that doesn't exist. |
| ⚖️ **Lens Council** | `sanad council` | The same small model is convened as several voices — usage lens, dependency lens, architecture lens, evidence lens — each running its own scoped investigation. A reconciliation step merges them, then the Gate judges the consensus and **forces a revision** of any refuted claim. |
| 🩹 **Scar Tissue** | `sanad scars` | Mines your git history (local, no LLM) into per-file **danger scores** (how often edits here needed a fix/revert) and **co-change couples**. `predict`/`check-impact` then warn: *"you're touching A without B, but 87% of past A-edits also touched B"*. Experience no frontier model has cold. |
| 🧬 **Genetic Lock** | `sanad lock-check` / `lock-gen` / `lock-grammar` | The graph compiles into the project's symbol space. Generated code that references a nonexistent API is rejected with the nearest real symbol suggested (`lock-check`, repair-loop `lock-gen`) — or made **unspeakable at decode time** via enum-schema / GBNF artifacts for local models (`lock-grammar`). |
| 🌪️ **Wind Tunnel** | `sanad tunnel` | Slices the minimal import closure for a change out of the graph, copies it into a scratch sandbox, then **really imports it and really runs** the graph-selected tests — optionally with a model's draft laid over a file — before the change ever lands. Reality instead of imagination, for zero tokens. |
| 🧪 **Sterile Memory** | `sanad memory` / `think --remember` | Permanent knowledge that cannot rot: an insight is admitted **only** if the Gate proves every claim in it; `reverify` re-judges the whole store as the code evolves, quarantining what stopped being true (and resurrecting it after a revert). Every verified investigation makes the system permanently smarter. |

## Measured, not promised

All numbers below are from real runs on this repository's own graph (10,900+ nodes):

- **Gate evaluation**: 10 true claims + 10 planted hallucinations (wrong files, invented functions, reversed call directions) → **10/10 true claims passed, 10/10 hallucinations caught**. During development the gate also refuted a claim sourced from the project's *own stale documentation* — a real hallucination, caught mechanically.
- **Think loop**: `gemini-3.1-flash-lite` (the cheapest tier) answered a real architecture question in 9 ops, **11.8k input / 213 output tokens (~$0.0066)** — answer fully correct and gate-verified 10/10.
- **Council**: three voices, 15 ops, consensus on a blast-radius question — **13/13 claims verified, ~$0.0088** total.
- **Genetic Lock**: planted hallucinations (`sanitize_labell`, fabricated import paths) caught with the correct suggestion first; with the graph-grounded API menu, the first draft came out **violation-free at ~$0.0004**.
- **Wind Tunnel**: a logic-sabotaged draft (correct syntax, wrong behavior) was exposed by really running the graph-selected tests in a sliced sandbox — in seconds, for **zero tokens**.
- **Scar Tissue**: mined 1,000+ commits in seconds; flagged a module where **67% of its 30 past edits** needed a follow-up fix.

## Quickstart

```bash
# 1. Build the graph (local, free, no API key needed)
sanad update .

# 2. Gate an AI's answer about your code
sanad verify "The \`login()\` function calls \`validate_token()\`." --strict

# 3. Hold an edit accountable
sanad predict "src/auth.py"        # before editing
# ... edit, then: sanad update .
sanad check-impact --strict        # DEVIATION = it touched what it didn't predict

# 4. Let a small model think with the graph
sanad think "Which module is the security chokepoint?" --verify

# 5. Convene the council
sanad council "What breaks if we change sanitize_label's signature?"

# 6. Or drive the ops yourself / from any agent (no API key)
sanad ops --new "find auth"
sanad ops "callers n1"

# 7. Inject 20 years of instinct from your git history (local, free)
sanad scars .
sanad scars --file src/auth.py

# 8. Generate code that cannot reference a nonexistent API
sanad lock-gen "add a helper that sanitizes labels using the project's sanitizer"
sanad lock-check draft.py            # judge any code against the symbol space

# 9. Test a draft against reality before it lands
sanad tunnel "src/auth.py" --draft new_auth.py --at src/auth.py

# 10. Grow a memory that cannot rot
sanad memory add "\`login()\` calls \`validate_token()\`."   # only enters if proven
sanad memory reverify                                       # re-judge after code changes
sanad think "..." --remember                                # auto-store verified answers
```

Works with **Claude Code, Gemini, Cursor, Codex, Copilot and 15+ agents** — `sanad install` registers the skill. Graph reasoning backends: Gemini, Claude, OpenAI, DeepSeek, Kimi, Ollama (local), Bedrock, Azure.

## Why not just RAG?

Vector search retrieves *similar text*; it cannot tell you that a claim is **false**. Sanad's graph is built deterministically from the AST, so it is ground truth for structure: who calls whom, what imports what, where things are defined. That's what makes refutation — not just retrieval — possible, and refutation is what kills hallucinations.

```
question ──▶ small model ──▶ op (a few tokens)
                 ▲               │
                 │               ▼
        compact result ◀── graph engine (local, free, cannot lie)
                 │
                 ▼
        answer ──▶ Hallucination Gate ──▶ proof-carrying reply
```

## Credits & license

Sanad is built on **[graphify](https://github.com/Graphify-Labs/graphify)** by Graphify Labs (MIT) — the deterministic extraction pipeline, graph builder, and query tools come from that excellent foundation (the upstream README is preserved at `docs/graphify-upstream-README.md`). The Sanad layer — hallucination gate (`factcheck.py`), blast-radius oracle (`impact.py`), graph-ops thinking engine (`graphmind.py`), and lens council (`council.py`) — plus full Windows support, ships under the same MIT license.

**لا إجابة بلا سند — No answer without a chain of proof.**
