honestcode 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of honestcode might be problematic. Click here for more details.
- honestcode/__init__.py +19 -0
- honestcode/cli.py +338 -0
- honestcode/graph/__init__.py +13 -0
- honestcode/graph/graph_store.py +229 -0
- honestcode/graph/watcher.py +116 -0
- honestcode/honest/__init__.py +19 -0
- honestcode/honest/project_binding.py +157 -0
- honestcode/honest/router.py +200 -0
- honestcode/honest/symbol_index.py +291 -0
- honestcode/mcp/__init__.py +46 -0
- honestcode/mcp/knowledge_base.py +141 -0
- honestcode/mcp/server.py +305 -0
- honestcode/mcp/tools.py +1267 -0
- honestcode/py.typed +0 -0
- honestcode/sandbox/__init__.py +195 -0
- honestcode/structure/__init__.py +6 -0
- honestcode/structure/extractor.py +296 -0
- honestcode/structure/multi_lang.py +180 -0
- honestcode/structure/relations.py +43 -0
- honestcode/structure/utils.py +31 -0
- honestcode-0.2.0.dist-info/METADATA +811 -0
- honestcode-0.2.0.dist-info/RECORD +26 -0
- honestcode-0.2.0.dist-info/WHEEL +5 -0
- honestcode-0.2.0.dist-info/entry_points.txt +3 -0
- honestcode-0.2.0.dist-info/licenses/LICENSE +21 -0
- honestcode-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: honestcode
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A lightweight MCP server that detects AI code hallucinations in real time — undefined symbols, wrong API calls, dead code, and type mismatches.
|
|
5
|
+
Author: HonestCode Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Fengrru/honestcode
|
|
8
|
+
Project-URL: Documentation, https://github.com/Fengrru/honestcode#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/Fengrru/honestcode
|
|
10
|
+
Project-URL: Issues, https://github.com/Fengrru/honestcode/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/Fengrru/honestcode/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: mcp,model-context-protocol,hallucination-detection,code-generation,static-analysis,llm,ai,code-analysis,dead-code,type-checking
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: mcp<2.0,>=1.6.0
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pre-commit>=3.0; extra == "dev"
|
|
37
|
+
Provides-Extra: multi-language
|
|
38
|
+
Requires-Dist: tree-sitter<0.26,>=0.21; extra == "multi-language"
|
|
39
|
+
Requires-Dist: tree-sitter-javascript>=0.21; extra == "multi-language"
|
|
40
|
+
Requires-Dist: tree-sitter-typescript>=0.21; extra == "multi-language"
|
|
41
|
+
Requires-Dist: tree-sitter-go>=0.21; extra == "multi-language"
|
|
42
|
+
Requires-Dist: tree-sitter-rust>=0.21; extra == "multi-language"
|
|
43
|
+
Requires-Dist: tree-sitter-java>=0.21; extra == "multi-language"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
<div align="center">
|
|
47
|
+
|
|
48
|
+
# HonestCode MCP Server
|
|
49
|
+
|
|
50
|
+
**Catch AI code hallucinations before they reach your editor.**
|
|
51
|
+
|
|
52
|
+
[](https://github.com/Fengrru/honestcode/actions/workflows/ci.yml)
|
|
53
|
+
[](https://pypi.org/project/honestcode/)
|
|
54
|
+
[](https://pypi.org/project/honestcode/)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
[](https://pypi.org/project/honestcode/)
|
|
57
|
+
|
|
58
|
+
A lightweight [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that verifies
|
|
59
|
+
AI-generated code against your actual project structure and installed dependencies — detecting
|
|
60
|
+
undefined symbols, wrong API calls, dead code, and type mismatches in real time.
|
|
61
|
+
|
|
62
|
+
**100% local. No data leaves your machine.**
|
|
63
|
+
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Contents
|
|
69
|
+
|
|
70
|
+
- [Why HonestCode?](#why-honestcode)
|
|
71
|
+
- [Performance](#performance)
|
|
72
|
+
- [Quick Start](#quick-start)
|
|
73
|
+
- [Environment Variables](#environment-variables)
|
|
74
|
+
- [Multi-language support (optional)](#multi-language-support-optional)
|
|
75
|
+
- [Supported Platforms and Agents](#supported-platforms-and-agents)
|
|
76
|
+
- [Tool Reference](#tool-reference)
|
|
77
|
+
- [CLI Usage](#cli-usage)
|
|
78
|
+
- [Library Usage](#library-usage)
|
|
79
|
+
- [Architecture](#architecture)
|
|
80
|
+
- [When NOT to use HonestCode](#when-not-to-use-honestcode)
|
|
81
|
+
- [Troubleshooting](#troubleshooting)
|
|
82
|
+
- [Development](#development)
|
|
83
|
+
- [Security](#security)
|
|
84
|
+
- [Telemetry](#telemetry)
|
|
85
|
+
- [Roadmap](#roadmap)
|
|
86
|
+
- [Changelog](#changelog)
|
|
87
|
+
- [Contributing](#contributing)
|
|
88
|
+
- [License](#license)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Why HonestCode?
|
|
93
|
+
|
|
94
|
+
AI coding assistants hallucinate. They invent function names, fabricate library APIs, and produce
|
|
95
|
+
dead code. **HonestCode** acts as a deterministic verification layer between the model and
|
|
96
|
+
your editor — pure AST analysis, no LLM calls, no network requests.
|
|
97
|
+
|
|
98
|
+
```mermaid
|
|
99
|
+
graph LR
|
|
100
|
+
A["AI Model generates code"] -->|sends code| B["HonestCode verifies against project"]
|
|
101
|
+
B -->|clean code| C["Editor receives verified code"]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### One tool by default: `scan_file`
|
|
105
|
+
|
|
106
|
+
By default the server exposes **one primary tool** — `scan_file`. Give it a Python file and it
|
|
107
|
+
returns every undefined symbol, incorrect API call, and structural issue in a single trip.
|
|
108
|
+
|
|
109
|
+
Exposing a single tool is deliberate. Measured agent behavior shows that one well-aimed tool
|
|
110
|
+
steers agents to a direct answer better than a menu of narrower ones — fewer mis-picks, fewer
|
|
111
|
+
round-trips. **16 additional tools** exist for power users; opt in via the `HONESTCODE_TOOLS`
|
|
112
|
+
environment variable (see [Environment Variables](#environment-variables)).
|
|
113
|
+
|
|
114
|
+
### Key differentiators
|
|
115
|
+
|
|
116
|
+
| | HonestCode | grep / Read | RAG over code |
|
|
117
|
+
|:--|:-----------------|:------------|:--------------|
|
|
118
|
+
| **Deterministic** | AST-based, reproducible | Exact string match | Embedding variance |
|
|
119
|
+
| **Cross-file** | Module-qualified symbols | Single-file only | Chunk-level |
|
|
120
|
+
| **Type-aware** | Argument counts, None iteration | No | No |
|
|
121
|
+
| **Zero latency** | Pre-indexed, cached | Per-query scan | Per-query embed |
|
|
122
|
+
| **100% local** | No network calls | No | Often requires API |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Performance
|
|
127
|
+
|
|
128
|
+
Numbers below are **measured, not estimated**. Re-run them yourself:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Local project
|
|
132
|
+
python scripts/benchmark.py --target /path/to/project --repeat 5
|
|
133
|
+
# External real-world repos (clones them into a temp dir)
|
|
134
|
+
python scripts/benchmark.py --repos psf/requests pallets/flask --format markdown
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Measured on this repository (43 Python modules, ~7400 LOC, Python 3.14, median
|
|
138
|
+
of 3 runs on a developer laptop, Windows):
|
|
139
|
+
|
|
140
|
+
| Operation | Measured | Note |
|
|
141
|
+
|:----------|---------:|:-----|
|
|
142
|
+
| `index_project` (cold) | ~143 ms | Content-hash cached; skips unchanged files |
|
|
143
|
+
| `index_project` (cached) | < 1 ms | In-memory cache hit |
|
|
144
|
+
| `scan_file` | ~35 ms | AST parse + symbol lookup |
|
|
145
|
+
| `check_api` | < 1 ms | Dictionary lookup on cached API index |
|
|
146
|
+
| `explore_call_graph` (hot) | ~1 ms | **SQLite graph cache hit**; cold rebuild ~278 ms |
|
|
147
|
+
| `find_dead_code` | ~1.4 ms | Reads the persisted reference graph |
|
|
148
|
+
| `find_similar_code` | ~3750 ms | O(n²) `SequenceMatcher`; dominated by pair count |
|
|
149
|
+
| `search_code` | ~16 ms | FTS5 candidate narrowing + regex on hits |
|
|
150
|
+
|
|
151
|
+
The call graph (definitions + references) is persisted to SQLite next to the
|
|
152
|
+
symbol index, keyed by content hashes of every source file. Graph queries
|
|
153
|
+
(`explore_call_graph`, `find_dead_code`, `explore_impact`, `affected_files`)
|
|
154
|
+
read from disk instead of re-parsing the project on each call — the first call
|
|
155
|
+
after a change pays the cold rebuild, every later call is a cache hit.
|
|
156
|
+
`find_similar_code` uses a length-ratio pre-filter but is still quadratic in
|
|
157
|
+
the number of functions; this is fine for small/medium projects but will need
|
|
158
|
+
a token-hash scheme for large codebases.
|
|
159
|
+
|
|
160
|
+
> Note: the "hot" graph numbers above are in-process cache hits. A fresh
|
|
161
|
+
> process pays one full-tree SHA-256 pass (to validate the SQLite cache's
|
|
162
|
+
> freshness) on its *first* graph query after startup, then every later call
|
|
163
|
+
> is a cache hit. On this repo that validation adds roughly the cost of a
|
|
164
|
+
> cold `index_project` (~143 ms).
|
|
165
|
+
|
|
166
|
+
Token savings vs. grep + Read exploration: **~60% fewer tool calls**, **~45%
|
|
167
|
+
fewer tokens** on architecture questions spanning multiple files.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Quick Start
|
|
172
|
+
|
|
173
|
+
### 1. Install
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pip install honestcode
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Or from source:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git clone https://github.com/Fengrru/honestcode.git
|
|
183
|
+
cd honestcode-mcp
|
|
184
|
+
pip install -e .
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
> **Requires** Python >= 3.10
|
|
188
|
+
|
|
189
|
+
### 2. Configure your MCP client
|
|
190
|
+
|
|
191
|
+
#### Claude Code
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
claude mcp add honestcode -- honestcode-mcp
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Or manually add to `~/.claude.json`:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"mcpServers": {
|
|
202
|
+
"honestcode": {
|
|
203
|
+
"command": "honestcode-mcp"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Cursor
|
|
210
|
+
|
|
211
|
+
Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"mcpServers": {
|
|
216
|
+
"honestcode": {
|
|
217
|
+
"command": "honestcode-mcp"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### VS Code
|
|
224
|
+
|
|
225
|
+
Add to `.vscode/mcp.json`:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"servers": {
|
|
230
|
+
"honestcode": {
|
|
231
|
+
"command": "honestcode-mcp"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Windsurf
|
|
238
|
+
|
|
239
|
+
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"mcpServers": {
|
|
244
|
+
"honestcode": {
|
|
245
|
+
"command": "honestcode-mcp"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### Python (direct invocation)
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"mcpServers": {
|
|
256
|
+
"honestcode": {
|
|
257
|
+
"command": "python",
|
|
258
|
+
"args": ["-m", "honestcode.mcp.server"]
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 3. Verify setup
|
|
265
|
+
|
|
266
|
+
Restart your MCP client. The agent should be able to call `scan_file`. If not, see
|
|
267
|
+
[Troubleshooting](#troubleshooting).
|
|
268
|
+
|
|
269
|
+
### 4. Use it
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
1. Index your project → index_project("/path/to/project")
|
|
273
|
+
2. Load dependency APIs → load_project_deps("/path/to/project")
|
|
274
|
+
3. Verify generated code → scan_file("/path/to/project/generated.py")
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Typical agent workflow — one call answers the question:
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
Agent: "Does this code have any hallucinated APIs?"
|
|
281
|
+
Tool: scan_file("src/services/auth.py")
|
|
282
|
+
Result: 2 issues found — undefined_call: validate_token (line 12),
|
|
283
|
+
undefined_call: db.fetch_all (line 27)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Environment Variables
|
|
289
|
+
|
|
290
|
+
| Variable | Default | Description |
|
|
291
|
+
|:---------|:--------|:------------|
|
|
292
|
+
| `HONESTCODE_TOOLS` | `scan_file` | Comma-separated tool names to expose (or `all`). `scan_file` is always included. |
|
|
293
|
+
| `HONESTCODE_INDEX_DIR` | `~/.cache/honestcode` | Directory for cached symbol indices |
|
|
294
|
+
| `HONESTCODE_CACHE_DIR` | `~/.cache/honestcode` | Directory for SQLite graph caches (override for CI/tests) |
|
|
295
|
+
| `HONESTCODE_TIMEOUT` | `10` | Seconds before `execute_code` is killed |
|
|
296
|
+
| `HONESTCODE_MEMORY_MB` | `256` | MB memory limit for sandboxed execution (POSIX) |
|
|
297
|
+
| `HONESTCODE_LOG_LEVEL` | `INFO` | Logging verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
|
|
298
|
+
|
|
299
|
+
### `HONESTCODE_TOOLS`
|
|
300
|
+
|
|
301
|
+
Controls which tools the MCP server exposes. By default only `scan_file` is
|
|
302
|
+
registered; set this to a comma-separated list to expose more, or to `all` to
|
|
303
|
+
expose every tool:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
export HONESTCODE_TOOLS=index,check_symbol,check_api,validate_types
|
|
307
|
+
export HONESTCODE_TOOLS=all
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`scan_file` is always included even if not listed. Unknown names are ignored
|
|
311
|
+
with a warning. Available tool names: `scan_file`, `index`, `deps`,
|
|
312
|
+
`check_symbol`, `check_api`, `execute_code`, `validate_types`,
|
|
313
|
+
`find_dead_code`, `find_similar_code`, `explore_call_graph`,
|
|
314
|
+
`explore_impact`, `affected_files`, `stop_watching`, `search_code`,
|
|
315
|
+
`load_package_apis`, `get_project_stats`, `choose_tool`.
|
|
316
|
+
|
|
317
|
+
> A `honestcode.toml` config file for per-project rules and ignore
|
|
318
|
+
> patterns is planned — tracked on the [Roadmap](#roadmap).
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Multi-language support (optional)
|
|
323
|
+
|
|
324
|
+
Python is the fully supported language (stdlib `ast`). For JavaScript,
|
|
325
|
+
TypeScript, Go, Rust and Java, symbol extraction is available via the
|
|
326
|
+
optional tree-sitter extras:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
pip install -e "honestcode[multi-language]"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Once installed, `scan_file` accepts `.js`/`.ts`/`.go`/`.rs`/`.java` files and
|
|
333
|
+
reports undefined call sites using tree-sitter's grammar. Without the extras,
|
|
334
|
+
`scan_file` returns a clear message pointing at the install command instead of
|
|
335
|
+
failing. This keeps the default install dependency-free.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Supported Platforms and Agents
|
|
340
|
+
|
|
341
|
+
Runs anywhere **Python 3.10+** does — no native build step, no `node_modules`,
|
|
342
|
+
no external services.
|
|
343
|
+
|
|
344
|
+
| Platform | Support |
|
|
345
|
+
|:---------|:--------|
|
|
346
|
+
| Windows | First-class (stdio + SSE, sandboxed execution) |
|
|
347
|
+
| macOS | First-class (stdio + SSE, sandboxed execution) |
|
|
348
|
+
| Linux | First-class (stdio + SSE, sandboxed execution, RLIMIT limits) |
|
|
349
|
+
|
|
350
|
+
| Agent / Client | Config file | Auto-install |
|
|
351
|
+
|:---------------|:------------|:-------------|
|
|
352
|
+
| Claude Code | `~/.claude.json` | `honestcode install --client claude` |
|
|
353
|
+
| Cursor | `.cursor/mcp.json` | `honestcode install --client cursor` |
|
|
354
|
+
| VS Code | `.vscode/mcp.json` | `honestcode install --client vscode` |
|
|
355
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | Manual (see Quick Start) |
|
|
356
|
+
| Any stdio MCP client | `command: honestcode-mcp` | — |
|
|
357
|
+
| Any SSE MCP client | `--transport sse --port 8000` | — |
|
|
358
|
+
|
|
359
|
+
See [Quick Start](#quick-start) for the manual JSON snippets and the
|
|
360
|
+
[CLI Usage](#cli-usage) section for `install`/`init`/`root` project binding.
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Tool Reference
|
|
365
|
+
|
|
366
|
+
### Primary tool (always exposed)
|
|
367
|
+
|
|
368
|
+
#### `scan_file(file_path)`
|
|
369
|
+
|
|
370
|
+
AST-based scan for undefined calls, missing imports, and incorrect API usage in a Python file.
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
scan_file("/path/to/project/src/auth.py")
|
|
374
|
+
# {
|
|
375
|
+
# "success": true,
|
|
376
|
+
# "file": "/path/to/project/src/auth.py",
|
|
377
|
+
# "issues": [
|
|
378
|
+
# {"type": "undefined_call", "name": "validate_token", "line": 12},
|
|
379
|
+
# {"type": "undefined_call", "name": "db.fetch_all", "line": 27}
|
|
380
|
+
# ],
|
|
381
|
+
# "defined_symbols": 342
|
|
382
|
+
# }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Issue types:** `undefined_call` — function/method not defined in project or loaded dependencies.
|
|
386
|
+
|
|
387
|
+
### Additional tools (opt-in)
|
|
388
|
+
|
|
389
|
+
Enable via the `HONESTCODE_TOOLS` environment variable (see
|
|
390
|
+
[Environment Variables](#environment-variables)):
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
export HONESTCODE_TOOLS=index,check_symbol,check_api,execute_code,validate_types
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Tool | Purpose | Speed |
|
|
397
|
+
|:-----|:--------|:------|
|
|
398
|
+
| `index_project` | Build module-qualified symbol index with content-hash caching | < 1s |
|
|
399
|
+
| `load_project_deps` | Parse `requirements.txt` / `pyproject.toml` and load dependency APIs | < 5s |
|
|
400
|
+
| `check_symbol` | Verify an identifier is defined in the project | < 5ms |
|
|
401
|
+
| `check_api` | Verify a library API call exists (with fuzzy typo suggestions) | < 10ms |
|
|
402
|
+
| `validate_types` | Structural checks: None iteration, wrong arg counts, calling constants | < 20ms |
|
|
403
|
+
| `execute_code` | Run code in sandboxed subprocess with timeout and memory limits | varies |
|
|
404
|
+
| `explore_call_graph` | Return callers, callees, and blast-radius summary of a symbol | < 5ms (hot) |
|
|
405
|
+
| `explore_impact` | Blast radius of a symbol: transitively impacted symbols/files | < 10ms (hot) |
|
|
406
|
+
| `affected_files` | Files/tests affected by a git diff, via reverse call-graph trace | < 50ms |
|
|
407
|
+
| `find_dead_code` | Find unused symbols with entrypoint support | < 10ms (hot) |
|
|
408
|
+
| `find_similar_code` | Detect function-level code clones via sequence similarity | < 2s |
|
|
409
|
+
| `search_code` | Regex search across project source files (FTS5-accelerated) | < 100ms |
|
|
410
|
+
| `load_package_apis` | Load API signatures for a single installed package | < 3s |
|
|
411
|
+
| `get_project_stats` | Return index statistics (symbol count, dependency APIs) | < 5ms |
|
|
412
|
+
| `stop_watching` | Stop the background file watcher started by `index_project(watch=True)` | < 5ms |
|
|
413
|
+
| `choose_tool` | Map a natural-language query to the best tool (debugging) | < 5ms |
|
|
414
|
+
|
|
415
|
+
<details>
|
|
416
|
+
<summary>Detailed parameter reference</summary>
|
|
417
|
+
|
|
418
|
+
**`index_project(root_path, force_rebuild=False, watch=False)`**
|
|
419
|
+
|
|
420
|
+
Build or reuse the project symbol index. Returns indexed symbol count, root path, and cache status.
|
|
421
|
+
Set `watch=True` to start a background file watcher that re-indexes (and invalidates the graph
|
|
422
|
+
cache) whenever a source file changes, keeping the index fresh for long-running sessions.
|
|
423
|
+
|
|
424
|
+
**`explore_impact(symbol_name, max_depth=3)`**
|
|
425
|
+
|
|
426
|
+
Compute the blast radius of a symbol: which other symbols and files would be affected if it
|
|
427
|
+
changed, following callers and callees up to `max_depth` hops.
|
|
428
|
+
|
|
429
|
+
**`affected_files(base="HEAD", head=None, max_depth=4)`**
|
|
430
|
+
|
|
431
|
+
Trace a `git diff` through the call graph to find which files — especially tests — may be affected
|
|
432
|
+
by uncommitted changes (`base` vs the working tree) or a range of commits (`base..head`).
|
|
433
|
+
CI-friendly: run it after each commit to know exactly which tests to run.
|
|
434
|
+
|
|
435
|
+
**`load_project_deps(root_path)`**
|
|
436
|
+
|
|
437
|
+
Parse `requirements.txt` or `pyproject.toml` and load public API signatures of installed packages.
|
|
438
|
+
|
|
439
|
+
**`check_symbol(symbol_name, file_path=None)`**
|
|
440
|
+
|
|
441
|
+
Verify a symbol is defined in the indexed project. Symbols use module-qualified names (`pkg.core.main`).
|
|
442
|
+
|
|
443
|
+
**`check_api(api_name)`**
|
|
444
|
+
|
|
445
|
+
Check if a library API exists. Returns fuzzy suggestions for typos.
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
check_api("math.sqrt") # valid
|
|
449
|
+
check_api("math.sqrtt") # invalid → suggests "math.sqrt"
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**`validate_types(code)`**
|
|
453
|
+
|
|
454
|
+
Lightweight structural checks on code snippets:
|
|
455
|
+
- Iterating over `None` (including `dict.get()` without default)
|
|
456
|
+
- Wrong argument counts for common builtins (`len`, `sum`, etc.)
|
|
457
|
+
- Calling constant values
|
|
458
|
+
- String methods on non-string constants
|
|
459
|
+
|
|
460
|
+
**`execute_code(code, prelude="", known_names=None)`**
|
|
461
|
+
|
|
462
|
+
Run code in a sandboxed subprocess with timeout and temp working directory.
|
|
463
|
+
|
|
464
|
+
**`explore_call_graph(symbol_name)`**
|
|
465
|
+
|
|
466
|
+
Return definitions, callers, and callees of a symbol.
|
|
467
|
+
|
|
468
|
+
**`find_dead_code(entrypoints=None, ignore_patterns=None, include_tests=True)`**
|
|
469
|
+
|
|
470
|
+
Find unused symbols. Provide entrypoints to keep known roots alive.
|
|
471
|
+
|
|
472
|
+
**`find_similar_code(threshold=0.85)`**
|
|
473
|
+
|
|
474
|
+
Find function-level code clones across the project. Uses length-ratio pre-filter for performance.
|
|
475
|
+
|
|
476
|
+
**`search_code(pattern, glob="*.py")`**
|
|
477
|
+
|
|
478
|
+
Regex search across project source files.
|
|
479
|
+
|
|
480
|
+
**`load_package_apis(package_name)`**
|
|
481
|
+
|
|
482
|
+
Load and cache API signatures for a specific installed package.
|
|
483
|
+
|
|
484
|
+
**`get_project_stats()`**
|
|
485
|
+
|
|
486
|
+
Return statistics about the currently indexed project.
|
|
487
|
+
|
|
488
|
+
</details>
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## CLI Usage
|
|
493
|
+
|
|
494
|
+
Every MCP tool has a CLI equivalent under the `honestcode` command, for
|
|
495
|
+
scripts and non-MCP harnesses. Output is JSON; exit code is non-zero when
|
|
496
|
+
issues are found (so it drops into CI pipelines cleanly).
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
# Index a project
|
|
500
|
+
honestcode index /path/to/project
|
|
501
|
+
honestcode index /path/to/project --force
|
|
502
|
+
|
|
503
|
+
# Load dependency APIs
|
|
504
|
+
honestcode deps /path/to/project
|
|
505
|
+
|
|
506
|
+
# Check a symbol
|
|
507
|
+
honestcode check-symbol pkg.core.helper
|
|
508
|
+
|
|
509
|
+
# Check an API
|
|
510
|
+
honestcode check-api pandas.read_csv
|
|
511
|
+
|
|
512
|
+
# Scan a file (exit 1 if issues found)
|
|
513
|
+
honestcode scan src/main.py
|
|
514
|
+
|
|
515
|
+
# Validate a code snippet (exit 1 if issues found)
|
|
516
|
+
honestcode validate "for x in None: pass"
|
|
517
|
+
|
|
518
|
+
# Execute code in a sandbox
|
|
519
|
+
honestcode execute "print(1+1)"
|
|
520
|
+
|
|
521
|
+
# Find dead code (exit 1 if any dead symbols found)
|
|
522
|
+
honestcode dead-code --entrypoints pkg.cli.main --no-tests
|
|
523
|
+
|
|
524
|
+
# Find similar code (exit 1 if any clones found)
|
|
525
|
+
honestcode similar --threshold 0.85
|
|
526
|
+
|
|
527
|
+
# Explore callers/callees of a symbol (includes blast-radius summary)
|
|
528
|
+
honestcode call-graph pkg.core.helper
|
|
529
|
+
|
|
530
|
+
# Blast radius of a symbol
|
|
531
|
+
honestcode impact pkg.core.helper --depth 3
|
|
532
|
+
|
|
533
|
+
# Files/tests affected by uncommitted changes (CI killer)
|
|
534
|
+
honestcode affected --base HEAD
|
|
535
|
+
honestcode affected --base main --head feature-branch
|
|
536
|
+
|
|
537
|
+
# Search code (exit 1 if matches found)
|
|
538
|
+
honestcode search "def \w+_helper"
|
|
539
|
+
|
|
540
|
+
# Load a single package's APIs
|
|
541
|
+
honestcode load-package numpy
|
|
542
|
+
|
|
543
|
+
# Show index statistics
|
|
544
|
+
honestcode stats
|
|
545
|
+
|
|
546
|
+
# Show which tool a query maps to
|
|
547
|
+
honestcode choose-tool "is my_symbol defined"
|
|
548
|
+
|
|
549
|
+
# Bind a directory as a project (creates .honestcode/) and discover the root
|
|
550
|
+
honestcode init /path/to/project
|
|
551
|
+
honestcode root
|
|
552
|
+
|
|
553
|
+
# Register the MCP server with Cursor / VS Code / Claude Code
|
|
554
|
+
honestcode install --dry-run
|
|
555
|
+
honestcode install --client cursor
|
|
556
|
+
|
|
557
|
+
# Keep an index fresh during long work sessions (Ctrl+C to stop)
|
|
558
|
+
honestcode watch /path/to/project
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
You can also invoke it as a module:
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
python -m honestcode.cli scan src/main.py
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
> The `honestcode-mcp` command starts the **MCP server** (stdio by
|
|
568
|
+
> default); use `honestcode-mcp --transport sse --port 8000` for SSE.
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Library Usage
|
|
573
|
+
|
|
574
|
+
Every MCP tool is also a plain function in `honestcode.mcp.tools`, so the
|
|
575
|
+
same verification logic can run inside your own Python code — no MCP client
|
|
576
|
+
required:
|
|
577
|
+
|
|
578
|
+
```python
|
|
579
|
+
from honestcode.mcp.tools import check_symbol, index_project, scan_file
|
|
580
|
+
|
|
581
|
+
index_project("/path/to/project") # build/reuse the symbol index
|
|
582
|
+
print(scan_file("/path/to/project/src/auth.py")) # hallucination scan
|
|
583
|
+
print(check_symbol("pkg.core.main")) # symbol lookup
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
Runnable examples live in [`examples/`](examples/README.md) — index & check,
|
|
587
|
+
API typo detection, scan & validate, and dead-code discovery:
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
pip install -e .
|
|
591
|
+
python examples/01_index_and_check.py /path/to/project
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## Architecture
|
|
597
|
+
|
|
598
|
+
### Data flow
|
|
599
|
+
|
|
600
|
+
```mermaid
|
|
601
|
+
graph TD
|
|
602
|
+
Client["MCP Client"] -->|tool call| Tools["tools.py (17 tools)"]
|
|
603
|
+
Tools -->|parse file| Extractor["StructureExtractor (Python ast)"]
|
|
604
|
+
Tools -->|lookup symbol| Index["Symbol Index (cache)"]
|
|
605
|
+
Tools -->|graph queries| Graph["Graph Store (SQLite + FTS5)"]
|
|
606
|
+
Tools -->|check API| KB["Knowledge Base (dep APIs)"]
|
|
607
|
+
Index -->|persists| Graph
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Module layout
|
|
611
|
+
|
|
612
|
+
```
|
|
613
|
+
honestcode/
|
|
614
|
+
├── mcp/ # MCP server layer
|
|
615
|
+
│ ├── server.py # FastMCP entry point (stdio + SSE), tool whitelist
|
|
616
|
+
│ ├── tools.py # 17 tool implementations
|
|
617
|
+
│ └── knowledge_base.py # Dependency API signature cache
|
|
618
|
+
├── honest/ # Core hallucination detection
|
|
619
|
+
│ ├── router.py # NL query → tool routing
|
|
620
|
+
│ ├── symbol_index.py # Project-wide symbol index + caching
|
|
621
|
+
│ └── project_binding.py # .honestcode/ binding + MCP client auto-config
|
|
622
|
+
├── graph/ # Persistent graph layer
|
|
623
|
+
│ ├── graph_store.py # SQLite call graph (definitions/refs) + FTS5 index
|
|
624
|
+
│ └── watcher.py # Zero-dependency polling file watcher
|
|
625
|
+
├── structure/ # Code structure extraction
|
|
626
|
+
│ ├── extractor.py # AST-based parser (Python ast)
|
|
627
|
+
│ ├── multi_lang.py # Optional tree-sitter extraction (JS/TS/Go/Rust/Java)
|
|
628
|
+
│ ├── relations.py # Edge/relation data structures
|
|
629
|
+
│ └── utils.py # Shared AST utilities
|
|
630
|
+
├── sandbox/ # Sandboxed execution
|
|
631
|
+
│ └── __init__.py # Subprocess executor with timeout + resource limits
|
|
632
|
+
└── cli.py # Command-line interface (every tool as a subcommand)
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Class responsibilities
|
|
636
|
+
|
|
637
|
+
| Module | Class/Function | Responsibility |
|
|
638
|
+
|:-------|:---------------|:---------------|
|
|
639
|
+
| `mcp/server.py` | `FastMCP` | MCP protocol handling, stdio + SSE transport, tool whitelist |
|
|
640
|
+
| `mcp/tools.py` | Tool functions | 17 hallucination-detection tools |
|
|
641
|
+
| `mcp/knowledge_base.py` | `APIKnowledgeBase` | Load/cache dependency API signatures via `importlib` + `inspect` |
|
|
642
|
+
| `honest/symbol_index.py` | `ProjectIndex` | Module-qualified symbol index with content-hash caching |
|
|
643
|
+
| `honest/project_binding.py` | `init/install` | `.honestcode/` project binding + MCP client auto-configuration |
|
|
644
|
+
| `honest/router.py` | `HonestRouter` | Map natural-language queries to tool intents |
|
|
645
|
+
| `graph/graph_store.py` | `GraphCache` | SQLite call graph (definitions/refs) + FTS5 full-text index |
|
|
646
|
+
| `graph/watcher.py` | `ProjectWatcher` | Polling file watcher with debounce for auto-re-indexing |
|
|
647
|
+
| `structure/extractor.py` | `StructureExtractor` | Parse Python AST, extract defs/imports/edges |
|
|
648
|
+
| `structure/multi_lang.py` | `extract_symbols()` | Optional tree-sitter extraction (JS/TS/Go/Rust/Java) |
|
|
649
|
+
| `structure/utils.py` | `call_name()` | Extract dotted names from AST call nodes |
|
|
650
|
+
| `sandbox/__init__.py` | `SandboxExecutor` | Isolated subprocess with timeout + resource limits |
|
|
651
|
+
| `cli.py` | `main()` | Argparse-based CLI mirroring every MCP tool |
|
|
652
|
+
|
|
653
|
+
### Design decisions
|
|
654
|
+
|
|
655
|
+
| Decision | Rationale |
|
|
656
|
+
|:---------|:----------|
|
|
657
|
+
| **Standard-library `ast`** | Call graphs and file scans use Python's `ast` module — no native parser dependency, reproducible across platforms |
|
|
658
|
+
| **Module-qualified symbols** | Index stores `pkg.module.func` so cross-file references are unambiguous |
|
|
659
|
+
| **Persistent graph (SQLite + FTS5)** | The call graph is stored on disk keyed by content hashes, so graph queries skip re-parsing; FTS5 narrows regex searches to candidate files |
|
|
660
|
+
| **Lazy loading + caching** | Dependency APIs and project indices are cached with content-hash invalidation |
|
|
661
|
+
| **Zero-dependency watcher** | File watching uses stdlib polling + debounce instead of a native watcher, keeping the install footprint small |
|
|
662
|
+
| **Thread-safe global state** | Tool state protected by `RLock` for concurrent MCP requests |
|
|
663
|
+
| **Subprocess sandbox** | `execute_code` runs in isolation with timeout, memory limits, and restricted PYTHONPATH |
|
|
664
|
+
| **Primary tool pattern** | One well-aimed tool (`scan_file`) reduces agent mis-picks vs. 17-tool menu |
|
|
665
|
+
| **CLI parity** | Every MCP tool has a 1:1 CLI subcommand so the same checks run in CI without an MCP client |
|
|
666
|
+
|
|
667
|
+
### Output safety
|
|
668
|
+
|
|
669
|
+
All tools enforce output limits to prevent context window bloat:
|
|
670
|
+
|
|
671
|
+
| Tool | Max output | Strategy |
|
|
672
|
+
|:-----|:-----------|:---------|
|
|
673
|
+
| `scan_file` | 50 issues | Truncated with `"truncated": true` |
|
|
674
|
+
| `check_api` | 3 suggestions | Fuzzy match capped at top-3 |
|
|
675
|
+
| `find_dead_code` | 200 symbols | Filtered by `ignore_patterns` |
|
|
676
|
+
| `find_similar_code` | 50 pairs | Length-ratio pre-filter |
|
|
677
|
+
| `search_code` | 100 matches | Regex `finditer` with line limits |
|
|
678
|
+
| `explore_call_graph` | Full | No limit (bounded by project size) |
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## When NOT to use HonestCode
|
|
683
|
+
|
|
684
|
+
| Scenario | Why it doesn't fit | Alternative |
|
|
685
|
+
|:---------|:-------------------|:------------|
|
|
686
|
+
| Runtime behavior questions | AST is static; can't trace execution | `cProfile`, `py-spy`, logging |
|
|
687
|
+
| Non-Python projects | Python is first-class; other languages need `[multi-language]` extras | CodeGraph (20+ languages) |
|
|
688
|
+
| Type inference across packages | Structural checks only, not full type solver | `mypy`, `pyright` |
|
|
689
|
+
| Tiny repos (< 20 files) | Index overhead exceeds benefit | Direct `grep` |
|
|
690
|
+
| Highly active monorepos | Index may lag behind rapid changes | `watch` mode or CI-integrated checks |
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## Troubleshooting
|
|
695
|
+
|
|
696
|
+
| Symptom | Likely cause | Fix |
|
|
697
|
+
|:--------|:-------------|:----|
|
|
698
|
+
| No tools visible in agent | Server not started | Restart MCP client; check MCP config JSON |
|
|
699
|
+
| `Project not indexed` error | `index_project` not called | Call `index_project("/path/to/project")` first |
|
|
700
|
+
| All symbols report `undefined` | Dependencies not loaded | Call `load_project_deps("/path/to/project")` |
|
|
701
|
+
| `execute_code` timeout | Code contains infinite loop | Increase timeout or fix the code |
|
|
702
|
+
| `SyntaxError` on scan | File has invalid Python | Fix syntax before scanning |
|
|
703
|
+
| Slow `index_project` | Large project, first run | Subsequent runs use cache; use `force_rebuild=False` |
|
|
704
|
+
| Windows: no memory limit | OS limitation | Use Docker/WSL2 for untrusted code |
|
|
705
|
+
|
|
706
|
+
### Verify setup
|
|
707
|
+
|
|
708
|
+
```bash
|
|
709
|
+
# Check the server is importable
|
|
710
|
+
python -c "from honestcode.mcp.server import main; print('OK')"
|
|
711
|
+
|
|
712
|
+
# Check the CLI works
|
|
713
|
+
python -m honestcode.cli --help
|
|
714
|
+
|
|
715
|
+
# Run tests
|
|
716
|
+
python -m pytest tests/ -q
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## Development
|
|
722
|
+
|
|
723
|
+
```bash
|
|
724
|
+
# Clone and install
|
|
725
|
+
git clone https://github.com/Fengrru/honestcode.git
|
|
726
|
+
cd honestcode-mcp
|
|
727
|
+
python -m venv .venv
|
|
728
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
729
|
+
pip install -e ".[dev]"
|
|
730
|
+
|
|
731
|
+
# Run tests
|
|
732
|
+
pytest
|
|
733
|
+
|
|
734
|
+
# Lint & format
|
|
735
|
+
ruff check honestcode tests scripts
|
|
736
|
+
ruff format honestcode tests scripts
|
|
737
|
+
|
|
738
|
+
# Pre-commit hooks
|
|
739
|
+
pre-commit install
|
|
740
|
+
pre-commit run --all-files
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for pull request guidelines.
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
## Security
|
|
748
|
+
|
|
749
|
+
`execute_code` runs in a subprocess with:
|
|
750
|
+
- **Timeout protection** (default 10s, configurable)
|
|
751
|
+
- **Memory limits** (256 MB on POSIX via `RLIMIT_AS`)
|
|
752
|
+
- **CPU time limits** (POSIX via `RLIMIT_CPU`)
|
|
753
|
+
- **Restricted `PYTHONPATH`** (empty, no inherited modules)
|
|
754
|
+
- **Temporary working directory** (cleaned up after execution)
|
|
755
|
+
- **No console window** on Windows (`CREATE_NO_WINDOW`)
|
|
756
|
+
|
|
757
|
+
This catches accidental mistakes but is **not** a hardened security boundary. For untrusted code,
|
|
758
|
+
use a container or dedicated VM.
|
|
759
|
+
|
|
760
|
+
See [SECURITY.md](SECURITY.md) for vulnerability reporting.
|
|
761
|
+
|
|
762
|
+
---
|
|
763
|
+
|
|
764
|
+
## Telemetry
|
|
765
|
+
|
|
766
|
+
**HonestCode collects no telemetry.** There are no analytics libraries,
|
|
767
|
+
no background services, and no phone-home endpoints:
|
|
768
|
+
|
|
769
|
+
- All parsing, indexing, and verification run **100% locally** — source code
|
|
770
|
+
never leaves your machine.
|
|
771
|
+
- The server makes **no outbound network requests** of its own. The only
|
|
772
|
+
network traffic in your stack comes from the LLM provider your MCP client is
|
|
773
|
+
configured to use.
|
|
774
|
+
- Every tool returns deterministic JSON you can diff, so verification results
|
|
775
|
+
are auditable in CI.
|
|
776
|
+
|
|
777
|
+
This is a design invariant, not a toggle.
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## Roadmap
|
|
782
|
+
|
|
783
|
+
- [x] Persistent call graph (SQLite) + FTS5 full-text search
|
|
784
|
+
- [x] `explore_impact` blast-radius analysis
|
|
785
|
+
- [x] `affected_files` git-diff → affected tests (CI killer)
|
|
786
|
+
- [x] File watcher for automatic re-indexing
|
|
787
|
+
- [x] Project binding (`.honestcode/`) + `install` for MCP clients
|
|
788
|
+
- [x] Multi-language symbol extraction via optional tree-sitter extras
|
|
789
|
+
- [ ] VS Code extension with inline diagnostics
|
|
790
|
+
- [ ] GitHub Action for PR-level hallucination checks
|
|
791
|
+
- [ ] Remote dependency API caching (PyPI index)
|
|
792
|
+
- [ ] Configurable rules and ignore patterns
|
|
793
|
+
- [ ] Adaptive output budgeting based on project size
|
|
794
|
+
|
|
795
|
+
---
|
|
796
|
+
|
|
797
|
+
## Changelog
|
|
798
|
+
|
|
799
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
800
|
+
|
|
801
|
+
---
|
|
802
|
+
|
|
803
|
+
## Contributing
|
|
804
|
+
|
|
805
|
+
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) first.
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
## License
|
|
810
|
+
|
|
811
|
+
[MIT](LICENSE) - Copyright (c) 2026 HonestCode Team
|