repobrain 0.1.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.
Files changed (72) hide show
  1. repobrain-0.1.0.dist-info/METADATA +257 -0
  2. repobrain-0.1.0.dist-info/RECORD +72 -0
  3. repobrain-0.1.0.dist-info/WHEEL +4 -0
  4. repobrain-0.1.0.dist-info/entry_points.txt +2 -0
  5. repobrain-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. repomind/__init__.py +15 -0
  7. repomind/cli/__init__.py +3 -0
  8. repomind/cli/commands/__init__.py +1 -0
  9. repomind/cli/commands/costs.py +75 -0
  10. repomind/cli/commands/index.py +86 -0
  11. repomind/cli/commands/query.py +50 -0
  12. repomind/cli/commands/review.py +152 -0
  13. repomind/cli/commands/serve.py +56 -0
  14. repomind/cli/commands/status.py +74 -0
  15. repomind/cli/main.py +35 -0
  16. repomind/config/__init__.py +10 -0
  17. repomind/config/schema.py +106 -0
  18. repomind/core/__init__.py +4 -0
  19. repomind/core/coordinator.py +135 -0
  20. repomind/core/indexer.py +352 -0
  21. repomind/generation/__init__.py +13 -0
  22. repomind/generation/cost_tracker.py +91 -0
  23. repomind/generation/generator.py +156 -0
  24. repomind/generation/prompts.py +128 -0
  25. repomind/generation/rag.py +58 -0
  26. repomind/git/__init__.py +16 -0
  27. repomind/git/cochange.py +83 -0
  28. repomind/git/history.py +119 -0
  29. repomind/git/metrics.py +95 -0
  30. repomind/git/pr_analyzer.py +265 -0
  31. repomind/graph/__init__.py +4 -0
  32. repomind/graph/analyzer.py +64 -0
  33. repomind/graph/builder.py +111 -0
  34. repomind/mcp/__init__.py +3 -0
  35. repomind/mcp/server.py +500 -0
  36. repomind/parsing/__init__.py +12 -0
  37. repomind/parsing/dynamic_hints/__init__.py +14 -0
  38. repomind/parsing/dynamic_hints/base.py +22 -0
  39. repomind/parsing/dynamic_hints/django.py +126 -0
  40. repomind/parsing/dynamic_hints/node.py +87 -0
  41. repomind/parsing/dynamic_hints/pytest.py +70 -0
  42. repomind/parsing/dynamic_hints/registry.py +37 -0
  43. repomind/parsing/languages/__init__.py +18 -0
  44. repomind/parsing/languages/base.py +23 -0
  45. repomind/parsing/languages/go.py +84 -0
  46. repomind/parsing/languages/python.py +159 -0
  47. repomind/parsing/languages/typescript.py +93 -0
  48. repomind/parsing/parser.py +72 -0
  49. repomind/parsing/symbols.py +36 -0
  50. repomind/storage/__init__.py +14 -0
  51. repomind/storage/graph/__init__.py +3 -0
  52. repomind/storage/graph/store.py +102 -0
  53. repomind/storage/sql/__init__.py +10 -0
  54. repomind/storage/sql/database.py +186 -0
  55. repomind/storage/sql/repositories/__init__.py +6 -0
  56. repomind/storage/sql/repositories/costs.py +74 -0
  57. repomind/storage/sql/repositories/decisions.py +57 -0
  58. repomind/storage/sql/repositories/files.py +128 -0
  59. repomind/storage/sql/repositories/git_metrics.py +135 -0
  60. repomind/storage/vector/__init__.py +4 -0
  61. repomind/storage/vector/embedder.py +45 -0
  62. repomind/storage/vector/store.py +159 -0
  63. repomind/utils/__init__.py +18 -0
  64. repomind/utils/async_utils.py +57 -0
  65. repomind/utils/file_utils.py +135 -0
  66. repomind/utils/hash_utils.py +20 -0
  67. repomind/utils/logging.py +35 -0
  68. repomind/webhook/__init__.py +3 -0
  69. repomind/webhook/handlers/__init__.py +4 -0
  70. repomind/webhook/handlers/pr.py +92 -0
  71. repomind/webhook/handlers/push.py +46 -0
  72. repomind/webhook/server.py +62 -0
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.4
2
+ Name: repobrain
3
+ Version: 0.1.0
4
+ Summary: Codebase intelligence that thinks ahead — outperforms repowise on every dimension
5
+ Project-URL: Homepage, https://pinexai.github.io/repomind
6
+ Project-URL: Repository, https://github.com/pinexai/repomind
7
+ Project-URL: Documentation, https://pinexai.github.io/repomind
8
+ Project-URL: Bug Tracker, https://github.com/pinexai/repomind/issues
9
+ Project-URL: Changelog, https://github.com/pinexai/repomind/blob/main/CHANGELOG.md
10
+ Author-email: Pinaki Mishra <pinaki@pinexai.com>
11
+ License: MIT License
12
+
13
+ Copyright (c) 2026 Pinaki Mishra
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+ License-File: LICENSE
33
+ Keywords: claude,codebase,developer-tools,intelligence,mcp
34
+ Classifier: Development Status :: 4 - Beta
35
+ Classifier: Environment :: Console
36
+ Classifier: Intended Audience :: Developers
37
+ Classifier: License :: OSI Approved :: MIT License
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Topic :: Software Development :: Documentation
42
+ Classifier: Topic :: Software Development :: Libraries
43
+ Classifier: Typing :: Typed
44
+ Requires-Python: >=3.12
45
+ Requires-Dist: aiosqlite>=0.20
46
+ Requires-Dist: anthropic>=0.40
47
+ Requires-Dist: click>=8.1
48
+ Requires-Dist: fastapi>=0.115
49
+ Requires-Dist: fastmcp>=2.0
50
+ Requires-Dist: gitpython>=3.1
51
+ Requires-Dist: httpx>=0.27
52
+ Requires-Dist: lancedb>=0.12
53
+ Requires-Dist: networkx>=3.3
54
+ Requires-Dist: openai>=1.50
55
+ Requires-Dist: pyarrow>=14.0
56
+ Requires-Dist: pydantic-settings>=2.5
57
+ Requires-Dist: pydantic>=2.8
58
+ Requires-Dist: python-dotenv>=1.0
59
+ Requires-Dist: rich>=13.7
60
+ Requires-Dist: scipy>=1.11
61
+ Requires-Dist: structlog>=24.0
62
+ Requires-Dist: tenacity>=9.0
63
+ Requires-Dist: tree-sitter>=0.21
64
+ Requires-Dist: uvicorn[standard]>=0.32
65
+ Provides-Extra: dev
66
+ Requires-Dist: build>=1.2; extra == 'dev'
67
+ Requires-Dist: mkdocs-autorefs>=1.0; extra == 'dev'
68
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
69
+ Requires-Dist: mypy>=1.11; extra == 'dev'
70
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
71
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
72
+ Requires-Dist: pytest>=8.0; extra == 'dev'
73
+ Requires-Dist: ruff>=0.6; extra == 'dev'
74
+ Requires-Dist: twine>=5.0; extra == 'dev'
75
+ Requires-Dist: types-networkx; extra == 'dev'
76
+ Description-Content-Type: text/markdown
77
+
78
+ # repomind — Codebase Intelligence That Thinks Ahead
79
+
80
+ [![PyPI version](https://img.shields.io/pypi/v/repomind.svg)](https://pypi.org/project/repomind/)
81
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
82
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
83
+ [![CI](https://github.com/pinexai/repomind/actions/workflows/ci.yml/badge.svg)](https://github.com/pinexai/repomind/actions/workflows/ci.yml)
84
+ [![Docs](https://img.shields.io/badge/docs-pinexai.github.io%2Frepomind-blue)](https://pinexai.github.io/repomind)
85
+
86
+ > **10× faster indexing. RAG-aware documentation. PR blast radius. Temporal hotspots.**
87
+ >
88
+ > repomind is a codebase intelligence MCP server for Claude that fixes every critical flaw in repowise — then goes further.
89
+
90
+ ---
91
+
92
+ ## What's Wrong With Repowise (and How We Fix It)
93
+
94
+ | # | Repowise Flaw | repomind Fix |
95
+ |---|---------------|--------------|
96
+ | 1 | **RAG context never used during generation** — vector store populated but never queried | `RAGAwareDocGenerator` fetches dependency docs from LanceDB *before* every LLM call |
97
+ | 2 | **25+ min initial indexing** — no parallelism | 7-stage async pipeline; parse runs in `ProcessPoolExecutor`, git + parse run concurrently |
98
+ | 3 | **3 stores with no atomic transactions** — 5–15% silent consistency failures | `AtomicStorageCoordinator.transaction()` buffers + rolls back SQL, LanceDB, and NetworkX |
99
+ | 4 | **Hardcoded 500-commit limit** | `GitConfig.max_commits = 10_000` — fully configurable |
100
+ | 5 | **Dynamic imports invisible** (Django, pytest, importlib) — 20–40% missing graph edges | `DjangoDynamicHints`, `PytestDynamicHints`, `NodeDynamicHints` in `HintRegistry` |
101
+ | 6 | **Incremental updates miss global percentile recalculation** | `upsert()` always triggers `PERCENT_RANK()` window function refresh |
102
+ | 7 | **No PR blast radius analysis** | `PRBlastRadiusAnalyzer` + `repomind review <PR>` + `get_pr_impact` MCP tool |
103
+ | 8 | **Temporal blindness** — 3-year-old commits weighted same as yesterday's | Exponential decay: `score += exp(-ln(2) * age_days / halflife) * complexity` |
104
+ | 9 | **Zero cost visibility** | `TokenspyCostAdapter` wraps every Anthropic call; `repomind costs` CLI |
105
+ | 10 | **Conservative dead code** misses real candidates | Dynamic hint edges recovered; configurable sensitivity threshold |
106
+
107
+ ---
108
+
109
+ ## Installation
110
+
111
+ ```bash
112
+ pip install repomind
113
+ ```
114
+
115
+ **Requirements:** Python 3.12+, an Anthropic API key.
116
+
117
+ ---
118
+
119
+ ## Quick Start
120
+
121
+ ```bash
122
+ # 1. Configure
123
+ cp .env.example .env
124
+ # Edit .env — set ANTHROPIC_API_KEY
125
+
126
+ # 2. Index your repo
127
+ repomind index /path/to/your/repo
128
+
129
+ # 3. Analyze a PR
130
+ repomind review 42
131
+
132
+ # 4. Start MCP server for Claude Code
133
+ repomind serve
134
+ ```
135
+
136
+ Then add to your Claude Code MCP config:
137
+
138
+ ```json
139
+ {
140
+ "mcpServers": {
141
+ "repomind": {
142
+ "command": "repomind",
143
+ "args": ["serve", "--mcp-only"]
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## CLI Commands
152
+
153
+ | Command | Description |
154
+ |---------|-------------|
155
+ | `repomind index [PATH]` | Index a repository (full or incremental) |
156
+ | `repomind review <PR>` | Analyze PR blast radius and risk score |
157
+ | `repomind serve` | Start MCP server (+ optional webhook) |
158
+ | `repomind status` | Show hotspot rankings and index health |
159
+ | `repomind query "<NL>"` | Natural language codebase search |
160
+ | `repomind costs [--since DATE]` | Show per-operation LLM spend |
161
+
162
+ **Rich progress during indexing:**
163
+ ```
164
+ [=====> ] 47% | Stage: Generating Docs | Files: 234/500 | Cost: $0.23 | ETA: 4m12s
165
+ ```
166
+
167
+ ---
168
+
169
+ ## MCP Tools (12 total)
170
+
171
+ | Tool | New? | Description |
172
+ |------|------|-------------|
173
+ | `explain_file` | — | File docs with RAG-injected dependency context |
174
+ | `explain_symbol` | — | Symbol-level explanation |
175
+ | `get_hotspots` | — | Temporal decay–weighted churn hotspots |
176
+ | `get_ownership` | — | Temporal-weighted file ownership |
177
+ | `get_dependencies` | — | Import graph with dynamic hint edges |
178
+ | `get_architectural_decisions` | — | ADR search and retrieval |
179
+ | `search_codebase` | — | Semantic vector search |
180
+ | `get_cochange_patterns` | — | Temporal co-change analysis |
181
+ | `get_pr_impact` | **NEW** | Full blast radius for a PR |
182
+ | `get_knowledge_map` | **NEW** | Knowledge silos, bus factor, onboarding targets |
183
+ | `get_test_gaps` | **NEW** | Untested code ranked by risk score |
184
+ | `get_security_hotspots` | **NEW** | Auth/input/SQL risk surfaces |
185
+
186
+ ---
187
+
188
+ ## Architecture
189
+
190
+ ```
191
+ repomind index /repo
192
+ |
193
+ v
194
+ +-----------------------------------------------------+
195
+ | AsyncIndexingPipeline (7 stages) |
196
+ | |
197
+ | 1. Discovery -> file manifest to SQL |
198
+ | 2. Parse -> ProcessPoolExecutor (CPU-bound) |
199
+ | 3. Graph Build -+ concurrent |
200
+ | 4. Git Analysis -+ (asyncio.gather) |
201
+ | 5. Embedding -> ThreadPoolExecutor + semaphore |
202
+ | 6. RAG Doc Gen -> LanceDB deps fetched FIRST |
203
+ | 7. Atomic Commit-> AtomicStorageCoordinator.txn() |
204
+ +-----------------------------------------------------+
205
+ |
206
+ v
207
+ +----------+ +--------------+ +----------------+
208
+ | SQLite | | LanceDB | | NetworkX Graph |
209
+ | (files, | | (embeddings, | | (dependency |
210
+ | metrics)| | docs) | | graph) |
211
+ +----------+ +--------------+ +----------------+
212
+ ```
213
+
214
+ **Atomic transactions across all three stores:**
215
+ ```python
216
+ async with coordinator.transaction() as txn:
217
+ txn.pending_sql.append(...)
218
+ txn.pending_vectors.append(...)
219
+ txn.pending_edges.append(...)
220
+ # On exception: SQL rollback + vector delete + graph node removal
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Configuration
226
+
227
+ ```bash
228
+ # .env
229
+ ANTHROPIC_API_KEY=sk-ant-...
230
+ REPOMIND_DATA_DIR=~/.repomind
231
+ REPOMIND_MAX_COMMITS=10000
232
+ REPOMIND_DECAY_HALFLIFE_DAYS=180
233
+ REPOMIND_GENERATION_CONCURRENCY=5
234
+ REPOMIND_MCP_PORT=8766
235
+ REPOMIND_WEBHOOK_PORT=8765
236
+ REPOMIND_WEBHOOK_SECRET=your-github-webhook-secret
237
+ ```
238
+
239
+ ---
240
+
241
+ ## Documentation
242
+
243
+ Full docs at **[pinexai.github.io/repomind](https://pinexai.github.io/repomind)**
244
+
245
+ - [Installation & Quick Start](https://pinexai.github.io/repomind/getting-started/quickstart/)
246
+ - [CLI Reference](https://pinexai.github.io/repomind/cli/)
247
+ - [MCP Tools Reference](https://pinexai.github.io/repomind/mcp/overview/)
248
+ - [Architecture Deep Dive](https://pinexai.github.io/repomind/architecture/pipeline/)
249
+ - [repomind vs repowise](https://pinexai.github.io/repomind/comparison/)
250
+
251
+ ---
252
+
253
+ ## License
254
+
255
+ MIT — see [LICENSE](LICENSE).
256
+
257
+ Built by [pinexai](https://github.com/pinexai).
@@ -0,0 +1,72 @@
1
+ repomind/__init__.py,sha256=U39yMGaYpc-Ojn78VF_fou0axZjKeU3WSe8oRkhuZkQ,563
2
+ repomind/cli/__init__.py,sha256=78xQXAVUXVZWoJ_aEwZM8TtTrOWtH8e1q2umnxV6hoA,41
3
+ repomind/cli/main.py,sha256=-j-rLEl-0xzYviDv6xBkLKUWJ3vWxrzXVy1Fhat9DrI,1113
4
+ repomind/cli/commands/__init__.py,sha256=ZgebDddFSsH7OC0ZjpvzOouWrwfLdfWilzAwu5de8AE,23
5
+ repomind/cli/commands/costs.py,sha256=JvQqLIOzwUcq7PU9cYoNEvN8Ox5i4JDoT32zY5aBCzo,2792
6
+ repomind/cli/commands/index.py,sha256=z_1dpOhYWZxrY4D_15kVcLf8h47ULIXpEerVvoossSQ,3008
7
+ repomind/cli/commands/query.py,sha256=xVA-ca7CdQX9id5rukE0hB0srsx34y7lQb70f2OlZbQ,1533
8
+ repomind/cli/commands/review.py,sha256=VfEpTHunxYtoz0e7BrWzCF9fezmkFEIUY-lM16n_hlw,5019
9
+ repomind/cli/commands/serve.py,sha256=Wne4rdHFXuKCNTLJwyBBbMNitwVpIp7mNoXA7BeFskg,1743
10
+ repomind/cli/commands/status.py,sha256=eb9OcN525aZGoOb0FLJ-SFCq6pCKlEVCEWsrOLLbqk8,2408
11
+ repomind/config/__init__.py,sha256=KovX68NjtdaFTuktBiaJJalLpQbGzkOygCdvASOtGzo,230
12
+ repomind/config/schema.py,sha256=tyMO4PBsd13TPC84Z0WLeNBh9ptmliowS_VilrYC4cQ,3490
13
+ repomind/core/__init__.py,sha256=yrV2bZZC1Rt0cQpJTrfecNNQRBT5LYyGDCQeuamiqGo,196
14
+ repomind/core/coordinator.py,sha256=0UeNQdjI5X4wUrhZz_vv7OPLFiwe15D-ePFb_-xNUt0,5194
15
+ repomind/core/indexer.py,sha256=axlG5tHNisoGisGLpeJRVsi3O8KRwi72w3AqUwpn15c,13952
16
+ repomind/generation/__init__.py,sha256=ZtA7Mh4nw7D57QQ9kRJNOYy48D8Su7NVV01lQkpRlls,362
17
+ repomind/generation/cost_tracker.py,sha256=yhc_cRyOfLjZnb-6Gg9J1Zn-lS8zpc95rjoAQWJgcD8,2800
18
+ repomind/generation/generator.py,sha256=WaDC9pFXnTF9X4ajWqGyz8xNgqonRhVKc6qLNA2leSM,5555
19
+ repomind/generation/prompts.py,sha256=9S6t_LBujHxJcoe7UXEIitzfj6AUwxp74Z4q7bPO_Wg,3722
20
+ repomind/generation/rag.py,sha256=iVqsAx-si-gge3WaRjV5D_8fb-NMvb1Y1wkbnZUtKTc,2082
21
+ repomind/git/__init__.py,sha256=SWllm_SDMeWez9NW8xf6FUAfQi1E6WM8MeaTsNazifI,470
22
+ repomind/git/cochange.py,sha256=aW_GaOszB1hisioX69VUznIBlwms5yA_bR2W74o9fgk,2672
23
+ repomind/git/history.py,sha256=zuAEwUYBJu2XSbKamr_NdeONACfymj48pYDHd0NeBFI,4293
24
+ repomind/git/metrics.py,sha256=BNwz8pMuVhvsX4aI7Uu24t5fmm6uAoaUyydG6n5E518,3363
25
+ repomind/git/pr_analyzer.py,sha256=7Alx0ps62WMkqKOhpGVayGbHW43e1reECLnDUN6jWlc,9676
26
+ repomind/graph/__init__.py,sha256=B1nl31Ypsf6IaIJ-2ewJKr8kDdCaZhj0FCB0ksGkS2g,123
27
+ repomind/graph/analyzer.py,sha256=ocPOxoYroErQviriBKTv_m_rnEoTiWiR3XHW_2Vo49w,2403
28
+ repomind/graph/builder.py,sha256=rVVblazMDM5esLAF1njgs6V8Rg9-2HQuze8haqF4-N0,3964
29
+ repomind/mcp/__init__.py,sha256=4MQiYH-c5SG_7GbpLka4Gzc95nmJFi4CgD77xaCyCGo,73
30
+ repomind/mcp/server.py,sha256=3is2zPXxPkQp1N6VUnt3PSkr1qlA8ruFG5VzKmW08HM,17951
31
+ repomind/parsing/__init__.py,sha256=IlZh-41F-qL3n_TXlvURLkYwGZI4uIflcZO7vwlPRKo,274
32
+ repomind/parsing/parser.py,sha256=26YM4ihjlaiWYW1kmz4GyyNuknMi0L6cOPCIo4XO8p0,2387
33
+ repomind/parsing/symbols.py,sha256=GBa45eMyoDNhx1NrRETv1LH4yaCxRzjUuFv2Vci8FkI,982
34
+ repomind/parsing/dynamic_hints/__init__.py,sha256=4ZTjlZSvFfO6pjpNFSYKf8QPf3ttYcS2ncJCOHDRu_8,358
35
+ repomind/parsing/dynamic_hints/base.py,sha256=Tmi7dNzGLk_v0TRboLunie6MEQIouSAAOFfaa2epSFo,600
36
+ repomind/parsing/dynamic_hints/django.py,sha256=HQrVvZ9eMfGTS4w8kMo_LemM50fn3--X_B1ncLvwTdM,5165
37
+ repomind/parsing/dynamic_hints/node.py,sha256=DZlMIN8GpHql6JljxN93kNUtcTOsBQsii9xWPsajq1A,3516
38
+ repomind/parsing/dynamic_hints/pytest.py,sha256=G42aqeBb2m64ENFHHwN-HMrDvDpPVBmqio8AuzuI3g8,2834
39
+ repomind/parsing/dynamic_hints/registry.py,sha256=IdYx-m7KWJT2t1rapfvcKphJMArAy8Ro6X5I3FJVl6k,1176
40
+ repomind/parsing/languages/__init__.py,sha256=_h897iEMbXUoA7zBlivbWZmgpYVgy486iGz9QpeEzzQ,550
41
+ repomind/parsing/languages/base.py,sha256=_ypiBcuCcQaL__P-2HFOozXPJ6riBpFRvs7eE5qO4iM,624
42
+ repomind/parsing/languages/go.py,sha256=bJ6v1YTJ_3HMnxeuI2AKyYoGkf7_MDLt-CEjb3_Aao8,3562
43
+ repomind/parsing/languages/python.py,sha256=-s5PsNibIHRx_7EvXYQRDCweboxAme_o9jqzWaEdsHk,7460
44
+ repomind/parsing/languages/typescript.py,sha256=Z5GT_NmWzeCTc0QgmoUi-BUC5niqHFuK2PDukZ70mMc,3984
45
+ repomind/storage/__init__.py,sha256=mdt0B73wSyo0ziNv_8j0HRuEBJeLF3_i37OVeoY4Abw,376
46
+ repomind/storage/graph/__init__.py,sha256=rMqY8x_OvY5MfHt1tVZfK9FCLH9DlEaoZ0_uqDNb1pY,56
47
+ repomind/storage/graph/store.py,sha256=WiqYnEUlE8KkaXEPKlfuAKUu7yU8Q-0Kn5UoxgoIW9A,3325
48
+ repomind/storage/sql/__init__.py,sha256=sg9bOxr2E0_PpzSQ7PFLxsrMfDpkAGr7Y63ltpYaSgI,269
49
+ repomind/storage/sql/database.py,sha256=ZPYfsoPGRp6QKG58M31avBq9Wj5Ux1avcdrUyQxGA4k,6252
50
+ repomind/storage/sql/repositories/__init__.py,sha256=N_o4fJ9HSIALHAM87XBO_0n5EOATLRkkNfKq9P75Fiw,250
51
+ repomind/storage/sql/repositories/costs.py,sha256=-O4s6x0PUpFROZr5cJIotCD6FZ0V29Swmz1nopK-uyI,2494
52
+ repomind/storage/sql/repositories/decisions.py,sha256=RWrgMb4jps9A3YxgHeOxs4m7GFT6RHAUTNMrWdaQa90,1867
53
+ repomind/storage/sql/repositories/files.py,sha256=s9IxYJz_EvQKEkG9XSHPe1W8JlNLT0oLPdP-lDwEjhA,4490
54
+ repomind/storage/sql/repositories/git_metrics.py,sha256=aQqcBWcbfj26lGa_0jovybzR4qziWt0ydDi7CrduIe0,5079
55
+ repomind/storage/vector/__init__.py,sha256=-R5FX0bVqTUmPoikAzMshOKLIFw1Ogg7ewG4GYKMq2w,113
56
+ repomind/storage/vector/embedder.py,sha256=tSrjHVxscMTI-56-RSjDPOKTeFH0QhehhAavx8M5NjI,1486
57
+ repomind/storage/vector/store.py,sha256=aGiOnCzKyxnm5wp_4hLCCe9eS9t2-aHpf836RwHu7M0,5678
58
+ repomind/utils/__init__.py,sha256=xv0akUJz9GjR86Zv9hmVAaKThNMkIgWsUfrPTB7aj2w,493
59
+ repomind/utils/async_utils.py,sha256=V04XHoE7XY0WD1M64ddNBhtxqS346-IGY7JmTeEi7Vw,1542
60
+ repomind/utils/file_utils.py,sha256=iTwbL9_az1_YBUEj--JFzHA5mL92vbdH8Q9O7Oq11UU,3629
61
+ repomind/utils/hash_utils.py,sha256=9m4poe7kdDwFIGZSNDnpY9ye8XvW6eFmjxHIKISvPWw,519
62
+ repomind/utils/logging.py,sha256=KLz8piht76jlCxSEb-8V3Q7E_a4osnvxuojpTsdKOSE,1094
63
+ repomind/webhook/__init__.py,sha256=OV0TrZ36wU0E-r_h3HtWZ2vZI0BRsI5M4bSPrIiEsPs,57
64
+ repomind/webhook/server.py,sha256=WX4aLvP-rVjx-oxaqgJCw3jfv2SW_9useVPR88dPj54,1888
65
+ repomind/webhook/handlers/__init__.py,sha256=M_6wpZCxxVLzeJqDdvwDy_JX1wUiFPnTutv_9FKwnks,116
66
+ repomind/webhook/handlers/pr.py,sha256=mA0NJvJAYAA-cvzjT7tGI0SsQaNuoo67lqEVLOqNG4c,3700
67
+ repomind/webhook/handlers/push.py,sha256=1K9H4jXqrl1OytdaP2qfG2ZYRFG_fsHsXqC_6fj6ovM,1712
68
+ repobrain-0.1.0.dist-info/METADATA,sha256=b-HgG5hnAlj-LHwTk1IvCVolXBDsZNV4NuxhsFzloqc,10009
69
+ repobrain-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
70
+ repobrain-0.1.0.dist-info/entry_points.txt,sha256=Csm1Xgf07XJvghfoiCIOS-TZ-EbHXVegyuAsSAyKPBw,51
71
+ repobrain-0.1.0.dist-info/licenses/LICENSE,sha256=06y7JewaJxbT3k2MSVA-G4vdNIi_Xhy_-2iKJ6Os6Bo,1070
72
+ repobrain-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ repomind = repomind.cli.main:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pinaki Mishra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
repomind/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """
2
+ repomind — Codebase intelligence that thinks ahead.
3
+
4
+ Outperforms repowise on every dimension:
5
+ - 10x faster indexing (async parallel pipeline)
6
+ - RAG-during-generation (deps docs fetched BEFORE generating)
7
+ - Atomic 3-store consistency (no silent failures)
8
+ - Temporal decay scoring (recent commits weighted more)
9
+ - PR blast radius analysis (before merge)
10
+ - Knowledge maps & security hotspots (new)
11
+ - Configurable git depth (no hardcoded 500-commit limit)
12
+ - Dynamic import detection (Django, pytest, Node)
13
+ - LLM cost tracking built-in
14
+ """
15
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from .main import cli
2
+
3
+ __all__ = ["cli"]
@@ -0,0 +1 @@
1
+ # CLI commands package
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from pathlib import Path
5
+
6
+ import click
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+ from rich import box
10
+
11
+ from ...config import RepomindConfig
12
+ from ...storage.sql import AsyncSQLiteDB
13
+ from ...utils.hash_utils import repo_id
14
+
15
+ console = Console()
16
+
17
+
18
+ @click.command()
19
+ @click.option("--since", default=None, help="Filter from date (ISO format)")
20
+ @click.option("--by", "group_by", type=click.Choice(["operation", "model", "day"]), default="operation")
21
+ @click.option("--repo", default=".", help="Repo root")
22
+ def costs_cmd(since: str | None, group_by: str, repo: str) -> None:
23
+ """Show LLM cost breakdown powered by tokenspy-compatible tracking."""
24
+ asyncio.run(_run_costs(repo, since, group_by))
25
+
26
+
27
+ async def _run_costs(repo: str, since: str | None, group_by: str) -> None:
28
+ config = RepomindConfig(repo_path=Path(repo).resolve())
29
+ rid = repo_id(config.repo_path)
30
+
31
+ if not config.db_path.exists():
32
+ console.print("[yellow]No index found. Run `repomind index` first.[/yellow]")
33
+ return
34
+
35
+ db = AsyncSQLiteDB(config.db_path)
36
+ await db.connect()
37
+
38
+ if group_by == "operation":
39
+ rows = await db.fetchall(
40
+ """SELECT operation, COUNT(*) as calls,
41
+ SUM(input_tokens+output_tokens) as tokens,
42
+ SUM(cost_usd) as cost
43
+ FROM llm_costs WHERE repo_id=? GROUP BY operation ORDER BY cost DESC""",
44
+ (rid,),
45
+ )
46
+ table = Table("Operation", "Calls", "Tokens", "Cost (USD)", box=box.ROUNDED)
47
+ total_cost = 0.0
48
+ for row in rows:
49
+ table.add_row(row["operation"], str(row["calls"]), f"{row['tokens']:,}", f"${row['cost']:.4f}")
50
+ total_cost += row["cost"]
51
+ console.print(table)
52
+ console.print(f"\n[bold]Total: [green]${total_cost:.4f}[/green][/bold]")
53
+
54
+ elif group_by == "model":
55
+ rows = await db.fetchall(
56
+ "SELECT model, COUNT(*) as calls, SUM(cost_usd) as cost FROM llm_costs WHERE repo_id=? GROUP BY model ORDER BY cost DESC",
57
+ (rid,),
58
+ )
59
+ table = Table("Model", "Calls", "Cost (USD)", box=box.ROUNDED)
60
+ for row in rows:
61
+ table.add_row(row["model"], str(row["calls"]), f"${row['cost']:.4f}")
62
+ console.print(table)
63
+
64
+ elif group_by == "day":
65
+ rows = await db.fetchall(
66
+ """SELECT DATE(called_at) as day, COUNT(*) as calls, SUM(cost_usd) as cost
67
+ FROM llm_costs WHERE repo_id=? GROUP BY day ORDER BY day DESC LIMIT 30""",
68
+ (rid,),
69
+ )
70
+ table = Table("Day", "Calls", "Cost (USD)", box=box.ROUNDED)
71
+ for row in rows:
72
+ table.add_row(row["day"], str(row["calls"]), f"${row['cost']:.4f}")
73
+ console.print(table)
74
+
75
+ await db.close()
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from pathlib import Path
5
+
6
+ import click
7
+ from rich.console import Console
8
+ from rich.progress import BarColumn, MofNCompleteColumn, Progress, SpinnerColumn, TextColumn, TimeRemainingColumn
9
+
10
+ from ...config import RepomindConfig
11
+ from ...core.indexer import AsyncIndexingPipeline, IndexingProgress
12
+
13
+ console = Console()
14
+
15
+
16
+ @click.command()
17
+ @click.argument("repo_path", default=".", required=False)
18
+ @click.option("--full", "incremental", flag_value=False, default=True, help="Force full re-index")
19
+ @click.option("--incremental", "incremental", flag_value=True, default=True, help="Only changed files")
20
+ @click.option("--max-commits", default=None, type=int, help="Override git history depth")
21
+ @click.option("--concurrency", default=None, type=int, help="Worker pool size")
22
+ @click.option("--no-docs", is_flag=True, default=False, help="Skip LLM doc generation")
23
+ @click.option("--dry-run", is_flag=True, default=False, help="Show what would be indexed")
24
+ @click.pass_context
25
+ def index_cmd(
26
+ ctx: click.Context,
27
+ repo_path: str,
28
+ incremental: bool,
29
+ max_commits: int | None,
30
+ concurrency: int | None,
31
+ no_docs: bool,
32
+ dry_run: bool,
33
+ ) -> None:
34
+ """Index a repository for codebase intelligence."""
35
+ config = RepomindConfig(repo_path=Path(repo_path).resolve())
36
+ if max_commits:
37
+ config.git.max_commits = max_commits
38
+ if concurrency:
39
+ config.indexing.worker_processes = concurrency
40
+
41
+ if dry_run:
42
+ from ...utils.file_utils import walk_repo
43
+ files = walk_repo(
44
+ config.repo_path,
45
+ config.indexing.exclude_patterns,
46
+ config.indexing.languages,
47
+ config.indexing.max_file_size_bytes,
48
+ )
49
+ console.print(f"[bold]Dry run: would index [cyan]{len(files)}[/cyan] files[/bold]")
50
+ for f in files[:20]:
51
+ console.print(f" {f.relative_to(config.repo_path)}")
52
+ if len(files) > 20:
53
+ console.print(f" ... and {len(files) - 20} more")
54
+ return
55
+
56
+ asyncio.run(_run_index(config, incremental))
57
+
58
+
59
+ async def _run_index(config: RepomindConfig, incremental: bool) -> None:
60
+ pipeline = AsyncIndexingPipeline(config)
61
+
62
+ with Progress(
63
+ SpinnerColumn(),
64
+ TextColumn("[bold blue]{task.description}"),
65
+ BarColumn(),
66
+ MofNCompleteColumn(),
67
+ TextColumn("• Cost: [green]${task.fields[cost]:.3f}[/green]"),
68
+ TimeRemainingColumn(),
69
+ console=console,
70
+ transient=False,
71
+ ) as progress:
72
+ task = progress.add_task("Starting...", total=100, cost=0.0)
73
+
74
+ def on_progress(p: IndexingProgress) -> None:
75
+ progress.update(
76
+ task,
77
+ description=p.stage,
78
+ completed=int(p.pct),
79
+ total=100,
80
+ cost=p.cost_so_far,
81
+ )
82
+
83
+ pipeline.on_progress(on_progress)
84
+ await pipeline.run(incremental=incremental)
85
+
86
+ console.print("[bold green]✓ Indexing complete![/bold green]")
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from pathlib import Path
5
+
6
+ import click
7
+ from rich.console import Console
8
+
9
+ from ...config import RepomindConfig
10
+ from ...storage.sql import AsyncSQLiteDB
11
+ from ...utils.hash_utils import repo_id
12
+
13
+ console = Console()
14
+
15
+
16
+ @click.command()
17
+ @click.argument("query_text")
18
+ @click.option("--repo", default=".", help="Repo root")
19
+ @click.option("--top-k", default=10, show_default=True)
20
+ def query_cmd(query_text: str, repo: str, top_k: int) -> None:
21
+ """Natural language query against the indexed codebase."""
22
+ asyncio.run(_run_query(repo, query_text, top_k))
23
+
24
+
25
+ async def _run_query(repo: str, query_text: str, top_k: int) -> None:
26
+ config = RepomindConfig(repo_path=Path(repo).resolve())
27
+ rid = repo_id(config.repo_path)
28
+
29
+ db = AsyncSQLiteDB(config.db_path)
30
+ await db.connect()
31
+
32
+ like = f"%{query_text}%"
33
+ rows = await db.fetchall(
34
+ """SELECT DISTINCT f.path, f.language, s.name, s.kind
35
+ FROM files f
36
+ LEFT JOIN symbols s ON s.file_id = f.id
37
+ WHERE f.repo_id = ? AND (f.path LIKE ? OR LOWER(s.name) LIKE ?)
38
+ LIMIT ?""",
39
+ (rid, like, like.lower(), top_k),
40
+ )
41
+
42
+ if not rows:
43
+ console.print(f"[yellow]No results for '{query_text}'[/yellow]")
44
+ else:
45
+ console.print(f"\n[bold]Results for '{query_text}':[/bold]\n")
46
+ for row in rows:
47
+ sym = f" [{row['kind']}] {row['name']}" if row["name"] else ""
48
+ console.print(f"[cyan]{row['path']}[/cyan]{sym}")
49
+
50
+ await db.close()