@ixxrllg/localmind 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +290 -0
- package/dist/ask/answer.d.ts +9 -0
- package/dist/ask/answer.d.ts.map +1 -0
- package/dist/ask/answer.js +183 -0
- package/dist/ask/answer.js.map +1 -0
- package/dist/bin/localmind.d.ts +3 -0
- package/dist/bin/localmind.d.ts.map +1 -0
- package/dist/bin/localmind.js +11 -0
- package/dist/bin/localmind.js.map +1 -0
- package/dist/cli/format.d.ts +7 -0
- package/dist/cli/format.d.ts.map +1 -0
- package/dist/cli/format.js +67 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/program.d.ts +4 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +228 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +55 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/db.d.ts +50 -0
- package/dist/core/db.d.ts.map +1 -0
- package/dist/core/db.js +180 -0
- package/dist/core/db.js.map +1 -0
- package/dist/core/hash.d.ts +3 -0
- package/dist/core/hash.d.ts.map +1 -0
- package/dist/core/hash.js +8 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/logger.d.ts +12 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +45 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/paths.d.ts +16 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +43 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/graph/related.d.ts +11 -0
- package/dist/graph/related.d.ts.map +1 -0
- package/dist/graph/related.js +144 -0
- package/dist/graph/related.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/chunker.d.ts +16 -0
- package/dist/indexer/chunker.d.ts.map +1 -0
- package/dist/indexer/chunker.js +76 -0
- package/dist/indexer/chunker.js.map +1 -0
- package/dist/indexer/ignore.d.ts +6 -0
- package/dist/indexer/ignore.d.ts.map +1 -0
- package/dist/indexer/ignore.js +71 -0
- package/dist/indexer/ignore.js.map +1 -0
- package/dist/indexer/indexer.d.ts +11 -0
- package/dist/indexer/indexer.d.ts.map +1 -0
- package/dist/indexer/indexer.js +182 -0
- package/dist/indexer/indexer.js.map +1 -0
- package/dist/indexer/languages.d.ts +5 -0
- package/dist/indexer/languages.d.ts.map +1 -0
- package/dist/indexer/languages.js +144 -0
- package/dist/indexer/languages.js.map +1 -0
- package/dist/indexer/relations.d.ts +9 -0
- package/dist/indexer/relations.d.ts.map +1 -0
- package/dist/indexer/relations.js +81 -0
- package/dist/indexer/relations.js.map +1 -0
- package/dist/indexer/walker.d.ts +21 -0
- package/dist/indexer/walker.d.ts.map +1 -0
- package/dist/indexer/walker.js +86 -0
- package/dist/indexer/walker.js.map +1 -0
- package/dist/mcp/server.d.ts +5 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +291 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/search/bm25.d.ts +9 -0
- package/dist/search/bm25.d.ts.map +1 -0
- package/dist/search/bm25.js +109 -0
- package/dist/search/bm25.js.map +1 -0
- package/dist/search/embeddings.d.ts +15 -0
- package/dist/search/embeddings.d.ts.map +1 -0
- package/dist/search/embeddings.js +193 -0
- package/dist/search/embeddings.js.map +1 -0
- package/dist/search/search.d.ts +9 -0
- package/dist/search/search.d.ts.map +1 -0
- package/dist/search/search.js +47 -0
- package/dist/search/search.js.map +1 -0
- package/dist/search/tokenize.d.ts +6 -0
- package/dist/search/tokenize.d.ts.map +1 -0
- package/dist/search/tokenize.js +163 -0
- package/dist/search/tokenize.js.map +1 -0
- package/dist/status.d.ts +4 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +33 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ixxrllg and LocalMind contributors
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# LocalMind
|
|
2
|
+
|
|
3
|
+
**Give every AI agent perfect memory of your codebase — offline.**
|
|
4
|
+
|
|
5
|
+
LocalMind is a local-first knowledge & context engine for developers and AI coding agents. It indexes your repository into a searchable knowledge base (BM25 + optional embeddings), builds a lightweight import graph, answers questions with citations, and exposes everything through a **CLI** and an **MCP server**.
|
|
6
|
+
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://modelcontextprotocol.io/)
|
|
10
|
+
[](https://github.com/ixxrllg/localmind)
|
|
11
|
+
[](https://github.com/ixxrllg/localmind/issues)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why LocalMind?
|
|
16
|
+
|
|
17
|
+
| Pain | LocalMind |
|
|
18
|
+
|------|-----------|
|
|
19
|
+
| AI agents forget project context | Persistent local index + MCP tools |
|
|
20
|
+
| Cloud RAG leaks private code | 100% local by default (SQLite) |
|
|
21
|
+
| Grep is not enough for questions | BM25 ranking + extractive / generative answers |
|
|
22
|
+
| “What depends on this file?” | Import / link knowledge graph |
|
|
23
|
+
|
|
24
|
+
Works **without API keys**. Optional OpenAI or Ollama for embeddings and generative answers.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
**Requirements:** Node.js ≥ 22.5
|
|
31
|
+
|
|
32
|
+
### Install from npm
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g @ixxrllg/localmind
|
|
36
|
+
|
|
37
|
+
# in any project
|
|
38
|
+
localmind init
|
|
39
|
+
localmind index
|
|
40
|
+
localmind ask "Where is authentication handled?"
|
|
41
|
+
localmind search "jwt middleware"
|
|
42
|
+
localmind status
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or without global install:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx @ixxrllg/localmind init
|
|
49
|
+
npx @ixxrllg/localmind index
|
|
50
|
+
npx @ixxrllg/localmind ask "Where is authentication handled?"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/ixxrllg/localmind.git
|
|
57
|
+
cd localmind
|
|
58
|
+
npm install
|
|
59
|
+
npm run build
|
|
60
|
+
node dist/bin/localmind.js init --root /path/to/your/project
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## CLI
|
|
66
|
+
|
|
67
|
+
| Command | Description |
|
|
68
|
+
|---------|-------------|
|
|
69
|
+
| `localmind init` | Create `.localmind/config.json` + SQLite DB |
|
|
70
|
+
| `localmind index` | Walk repo, chunk files, build inverted index (+ optional embeddings) |
|
|
71
|
+
| `localmind index --full` | Force full re-index |
|
|
72
|
+
| `localmind search <query>` | Ranked code/doc search |
|
|
73
|
+
| `localmind ask <question>` | Q&A with citations |
|
|
74
|
+
| `localmind related <path>` | Files linked via imports / markdown links |
|
|
75
|
+
| `localmind status` | Index statistics |
|
|
76
|
+
| `localmind mcp` | Start MCP server on **stdio** |
|
|
77
|
+
| `localmind config` | Show / update config |
|
|
78
|
+
|
|
79
|
+
Global options:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
-r, --root <path> Project root (default: cwd)
|
|
83
|
+
-q, --quiet
|
|
84
|
+
--verbose
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Examples
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
localmind init --provider ollama
|
|
91
|
+
localmind index
|
|
92
|
+
localmind search "chunkText overlap" --mode bm25
|
|
93
|
+
localmind ask "How are files ignored during indexing?" --extractive
|
|
94
|
+
localmind related src/indexer/indexer.ts
|
|
95
|
+
localmind status --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## MCP (Cursor / Claude Code / agents)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
localmind mcp --root /path/to/your/project
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Example MCP config (Cursor / Claude Desktop):
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"mcpServers": {
|
|
111
|
+
"localmind": {
|
|
112
|
+
"command": "node",
|
|
113
|
+
"args": [
|
|
114
|
+
"/absolute/path/to/localmind/dist/bin/localmind.js",
|
|
115
|
+
"mcp",
|
|
116
|
+
"--root",
|
|
117
|
+
"/absolute/path/to/your/project"
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
> Replace paths with your clone of [ixxrllg/localmind](https://github.com/ixxrllg/localmind) and the project you want indexed.
|
|
125
|
+
|
|
126
|
+
### Tools
|
|
127
|
+
|
|
128
|
+
| Tool | Purpose |
|
|
129
|
+
|------|---------|
|
|
130
|
+
| `search_code` | Semantic/lexical search over the index |
|
|
131
|
+
| `ask_codebase` | Question answering with citations |
|
|
132
|
+
| `find_related_files` | Graph neighborhood for a path |
|
|
133
|
+
| `explain_file` | Dump indexed chunks for a file / line range |
|
|
134
|
+
| `index_status` | Stats |
|
|
135
|
+
| `reindex` | Incremental or full re-index |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## How it works
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
your repo
|
|
143
|
+
│
|
|
144
|
+
▼
|
|
145
|
+
walker + ignore rules (.gitignore, defaults)
|
|
146
|
+
│
|
|
147
|
+
▼
|
|
148
|
+
line-aware chunker ──► SQLite
|
|
149
|
+
│ ├─ files / chunks
|
|
150
|
+
│ ├─ BM25 inverted index (terms + postings)
|
|
151
|
+
│ ├─ import/link edges
|
|
152
|
+
│ └─ optional embeddings
|
|
153
|
+
▼
|
|
154
|
+
search (BM25 / vector / hybrid) ──► ask (extractive or LLM)
|
|
155
|
+
│
|
|
156
|
+
▼
|
|
157
|
+
CLI · MCP · library API
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- **Storage:** `.localmind/localmind.db` (Node built-in `node:sqlite`)
|
|
161
|
+
- **Default search:** BM25 (no network, no models)
|
|
162
|
+
- **Optional:** OpenAI / Ollama embeddings + chat
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Configuration
|
|
167
|
+
|
|
168
|
+
`.localmind/config.json` (created by `init`):
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"version": 1,
|
|
173
|
+
"provider": "none",
|
|
174
|
+
"include": [],
|
|
175
|
+
"exclude": [],
|
|
176
|
+
"maxFileBytes": 524288,
|
|
177
|
+
"chunkSize": 1200,
|
|
178
|
+
"chunkOverlap": 150,
|
|
179
|
+
"incremental": true
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Environment (optional)
|
|
184
|
+
|
|
185
|
+
See `.env.example`:
|
|
186
|
+
|
|
187
|
+
| Variable | Role |
|
|
188
|
+
|----------|------|
|
|
189
|
+
| `OPENAI_API_KEY` | Embeddings + generative answers |
|
|
190
|
+
| `OPENAI_BASE_URL` | Compatible gateways |
|
|
191
|
+
| `OPENAI_MODEL` | Chat model (default `gpt-4o-mini`) |
|
|
192
|
+
| `OPENAI_EMBEDDING_MODEL` | Default `text-embedding-3-small` |
|
|
193
|
+
| `OLLAMA_BASE_URL` | Default `http://127.0.0.1:11434` |
|
|
194
|
+
| `OLLAMA_MODEL` / `OLLAMA_EMBEDDING_MODEL` | Local models |
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
localmind config --provider openai
|
|
198
|
+
# or
|
|
199
|
+
localmind config --provider ollama
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Library API
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import {
|
|
208
|
+
initConfig,
|
|
209
|
+
loadConfig,
|
|
210
|
+
runIndex,
|
|
211
|
+
openDb,
|
|
212
|
+
closeDb,
|
|
213
|
+
search,
|
|
214
|
+
ask,
|
|
215
|
+
} from "@ixxrllg/localmind";
|
|
216
|
+
|
|
217
|
+
const { config } = initConfig({ root: process.cwd() });
|
|
218
|
+
await runIndex(config, { full: true });
|
|
219
|
+
|
|
220
|
+
const db = openDb(config.root);
|
|
221
|
+
const hits = await search(db, "authentication middleware");
|
|
222
|
+
const answer = await ask(db, config, "How does auth work?");
|
|
223
|
+
closeDb(db);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
npm install
|
|
232
|
+
npm run build
|
|
233
|
+
npm test
|
|
234
|
+
npm run dev -- init
|
|
235
|
+
npm run dev -- index
|
|
236
|
+
npm run dev -- ask "What is LocalMind?"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Requirements:
|
|
240
|
+
|
|
241
|
+
- **Node.js ≥ 22.5** (uses built-in `node:sqlite`)
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Project layout
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
src/
|
|
249
|
+
bin/localmind.ts CLI entry
|
|
250
|
+
cli/ Commander program + formatting
|
|
251
|
+
core/ config, paths, SQLite
|
|
252
|
+
indexer/ walk, chunk, relations, index pipeline
|
|
253
|
+
search/ tokenize, BM25, embeddings, hybrid
|
|
254
|
+
ask/ extractive + generative Q&A
|
|
255
|
+
graph/ related files
|
|
256
|
+
mcp/ MCP server + tools
|
|
257
|
+
index.ts public exports
|
|
258
|
+
tests/ unit + integration
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Roadmap
|
|
264
|
+
|
|
265
|
+
- [ ] File watcher / live re-index
|
|
266
|
+
- [ ] Tree-sitter aware chunking
|
|
267
|
+
- [ ] sqlite-vec acceleration
|
|
268
|
+
- [ ] Web UI for exploration
|
|
269
|
+
- [ ] Multi-repo workspaces
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Contributing
|
|
274
|
+
|
|
275
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Issues and PRs welcome.
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT — see [LICENSE](LICENSE).
|
|
280
|
+
|
|
281
|
+
## Author
|
|
282
|
+
|
|
283
|
+
Maintained by [@ixxrllg](https://github.com/ixxrllg).
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
<p align="center">
|
|
288
|
+
<b>Star ⭐ this repo if LocalMind saves you a context switch.</b><br/>
|
|
289
|
+
Built for agents. Useful for humans.
|
|
290
|
+
</p>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Db } from "../core/db.js";
|
|
2
|
+
import type { AskResult, LocalMindConfig, ProviderKind } from "../types.js";
|
|
3
|
+
export interface AskOptions {
|
|
4
|
+
limit?: number;
|
|
5
|
+
mode?: "auto" | "extractive" | "generative";
|
|
6
|
+
provider?: ProviderKind;
|
|
7
|
+
}
|
|
8
|
+
export declare function ask(db: Db, config: LocalMindConfig, question: string, options?: AskOptions): Promise<AskResult>;
|
|
9
|
+
//# sourceMappingURL=answer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/ask/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAa,MAAM,aAAa,CAAC;AAGvF,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,wBAAsB,GAAG,CACvB,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CA2DpB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { search } from "../search/search.js";
|
|
2
|
+
export async function ask(db, config, question, options = {}) {
|
|
3
|
+
const started = Date.now();
|
|
4
|
+
const q = question.trim();
|
|
5
|
+
if (!q) {
|
|
6
|
+
return {
|
|
7
|
+
question,
|
|
8
|
+
answer: "Please provide a non-empty question.",
|
|
9
|
+
mode: "extractive",
|
|
10
|
+
provider: "none",
|
|
11
|
+
hits: [],
|
|
12
|
+
tookMs: 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const provider = options.provider ?? config.provider;
|
|
16
|
+
const hits = await search(db, q, {
|
|
17
|
+
limit: options.limit ?? 6,
|
|
18
|
+
mode: provider === "none" ? "bm25" : "hybrid",
|
|
19
|
+
provider,
|
|
20
|
+
});
|
|
21
|
+
const wantGenerative = options.mode === "generative" ||
|
|
22
|
+
(options.mode !== "extractive" && provider !== "none");
|
|
23
|
+
if (wantGenerative && hits.length) {
|
|
24
|
+
try {
|
|
25
|
+
const answer = await generateAnswer(q, hits, provider);
|
|
26
|
+
return {
|
|
27
|
+
question: q,
|
|
28
|
+
answer,
|
|
29
|
+
mode: "generative",
|
|
30
|
+
provider,
|
|
31
|
+
hits,
|
|
32
|
+
tookMs: Date.now() - started,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const fallback = buildExtractiveAnswer(q, hits);
|
|
37
|
+
return {
|
|
38
|
+
question: q,
|
|
39
|
+
answer: `${fallback}\n\n_(Generative answer failed: ${err instanceof Error ? err.message : String(err)}; showing extractive result.)_`,
|
|
40
|
+
mode: "extractive",
|
|
41
|
+
provider,
|
|
42
|
+
hits,
|
|
43
|
+
tookMs: Date.now() - started,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
question: q,
|
|
49
|
+
answer: buildExtractiveAnswer(q, hits),
|
|
50
|
+
mode: "extractive",
|
|
51
|
+
provider: "none",
|
|
52
|
+
hits,
|
|
53
|
+
tookMs: Date.now() - started,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function buildExtractiveAnswer(question, hits) {
|
|
57
|
+
if (!hits.length) {
|
|
58
|
+
return [
|
|
59
|
+
`No relevant context found for: "${question}"`,
|
|
60
|
+
"",
|
|
61
|
+
"Tips:",
|
|
62
|
+
"- Run `localmind index` after code changes",
|
|
63
|
+
"- Try more specific identifiers (function/class names)",
|
|
64
|
+
"- Check `localmind status` for index size",
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
|
67
|
+
const lines = [
|
|
68
|
+
`## Answer (extractive)`,
|
|
69
|
+
"",
|
|
70
|
+
`Based on ${hits.length} relevant chunk(s) in the local index:`,
|
|
71
|
+
"",
|
|
72
|
+
];
|
|
73
|
+
for (const hit of hits) {
|
|
74
|
+
const snippet = trimSnippet(hit.content, 500);
|
|
75
|
+
lines.push(`### ${hit.rank}. \`${hit.path}\` (L${hit.startLine}–${hit.endLine})`);
|
|
76
|
+
lines.push(`Score: ${hit.score.toFixed(4)}`);
|
|
77
|
+
lines.push("```" + fenceLang(hit.language));
|
|
78
|
+
lines.push(snippet);
|
|
79
|
+
lines.push("```");
|
|
80
|
+
lines.push("");
|
|
81
|
+
}
|
|
82
|
+
lines.push("---");
|
|
83
|
+
lines.push("Citations only — no LLM was used. Set provider to `openai` or `ollama` (and API env vars) for generative answers.");
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
function trimSnippet(text, max) {
|
|
87
|
+
const t = text.trim();
|
|
88
|
+
if (t.length <= max)
|
|
89
|
+
return t;
|
|
90
|
+
return t.slice(0, max).trimEnd() + "\n…";
|
|
91
|
+
}
|
|
92
|
+
function fenceLang(language) {
|
|
93
|
+
const map = {
|
|
94
|
+
typescript: "ts",
|
|
95
|
+
javascript: "js",
|
|
96
|
+
python: "python",
|
|
97
|
+
rust: "rust",
|
|
98
|
+
go: "go",
|
|
99
|
+
markdown: "md",
|
|
100
|
+
json: "json",
|
|
101
|
+
yaml: "yaml",
|
|
102
|
+
shell: "bash",
|
|
103
|
+
};
|
|
104
|
+
return map[language] ?? language;
|
|
105
|
+
}
|
|
106
|
+
async function generateAnswer(question, hits, provider) {
|
|
107
|
+
const context = hits
|
|
108
|
+
.map((h, i) => `[${i + 1}] ${h.path}:${h.startLine}-${h.endLine}\n${h.content}`)
|
|
109
|
+
.join("\n\n---\n\n");
|
|
110
|
+
const system = [
|
|
111
|
+
"You are LocalMind, a precise codebase assistant.",
|
|
112
|
+
"Answer using ONLY the provided context chunks.",
|
|
113
|
+
"Cite sources as [n] with file paths.",
|
|
114
|
+
"If the context is insufficient, say what is missing.",
|
|
115
|
+
"Be concise and technical.",
|
|
116
|
+
].join(" ");
|
|
117
|
+
const user = `Question: ${question}\n\nContext:\n${context}`;
|
|
118
|
+
if (provider === "openai" || (provider === "none" && process.env.OPENAI_API_KEY)) {
|
|
119
|
+
return openaiChat(system, user);
|
|
120
|
+
}
|
|
121
|
+
if (provider === "ollama") {
|
|
122
|
+
return ollamaChat(system, user);
|
|
123
|
+
}
|
|
124
|
+
throw new Error("No generative provider configured");
|
|
125
|
+
}
|
|
126
|
+
async function openaiChat(system, user) {
|
|
127
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
128
|
+
if (!apiKey)
|
|
129
|
+
throw new Error("OPENAI_API_KEY is not set");
|
|
130
|
+
const baseUrl = (process.env.OPENAI_BASE_URL?.trim() || "https://api.openai.com/v1").replace(/\/$/, "");
|
|
131
|
+
const model = process.env.OPENAI_MODEL?.trim() || "gpt-4o-mini";
|
|
132
|
+
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
"Content-Type": "application/json",
|
|
136
|
+
Authorization: `Bearer ${apiKey}`,
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify({
|
|
139
|
+
model,
|
|
140
|
+
temperature: 0.2,
|
|
141
|
+
messages: [
|
|
142
|
+
{ role: "system", content: system },
|
|
143
|
+
{ role: "user", content: user },
|
|
144
|
+
],
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
throw new Error(`OpenAI chat failed (${res.status}): ${await res.text()}`);
|
|
149
|
+
}
|
|
150
|
+
const data = (await res.json());
|
|
151
|
+
const content = data.choices?.[0]?.message?.content?.trim();
|
|
152
|
+
if (!content)
|
|
153
|
+
throw new Error("Empty OpenAI response");
|
|
154
|
+
return content;
|
|
155
|
+
}
|
|
156
|
+
async function ollamaChat(system, user) {
|
|
157
|
+
const baseUrl = (process.env.OLLAMA_BASE_URL?.trim() ||
|
|
158
|
+
process.env.OLLAMA_HOST?.trim() ||
|
|
159
|
+
"http://127.0.0.1:11434").replace(/\/$/, "");
|
|
160
|
+
const model = process.env.OLLAMA_MODEL?.trim() || "llama3.2";
|
|
161
|
+
const res = await fetch(`${baseUrl}/api/chat`, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: { "Content-Type": "application/json" },
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
model,
|
|
166
|
+
stream: false,
|
|
167
|
+
messages: [
|
|
168
|
+
{ role: "system", content: system },
|
|
169
|
+
{ role: "user", content: user },
|
|
170
|
+
],
|
|
171
|
+
options: { temperature: 0.2 },
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
if (!res.ok) {
|
|
175
|
+
throw new Error(`Ollama chat failed (${res.status}): ${await res.text()}`);
|
|
176
|
+
}
|
|
177
|
+
const data = (await res.json());
|
|
178
|
+
const content = data.message?.content?.trim();
|
|
179
|
+
if (!content)
|
|
180
|
+
throw new Error("Empty Ollama response");
|
|
181
|
+
return content;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=answer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer.js","sourceRoot":"","sources":["../../src/ask/answer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localmind.d.ts","sourceRoot":"","sources":["../../src/bin/localmind.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCli } from "../cli/program.js";
|
|
3
|
+
runCli(process.argv).catch((err) => {
|
|
4
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5
|
+
console.error(`localmind: ${message}`);
|
|
6
|
+
if (err instanceof Error && err.stack && process.env.DEBUG) {
|
|
7
|
+
console.error(err.stack);
|
|
8
|
+
}
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=localmind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localmind.js","sourceRoot":"","sources":["../../src/bin/localmind.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC1C,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;IACvC,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AskResult, IndexStats, SearchHit, StatusInfo } from "../types.js";
|
|
2
|
+
export declare function printInitResult(root: string, created: boolean): void;
|
|
3
|
+
export declare function printIndexStats(stats: IndexStats): void;
|
|
4
|
+
export declare function printStatus(status: StatusInfo): void;
|
|
5
|
+
export declare function printSearchHits(hits: SearchHit[]): void;
|
|
6
|
+
export declare function printAskResult(result: AskResult, json?: boolean): void;
|
|
7
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGhF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CASpE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAQvD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAYpD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAuBvD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,UAAQ,GAAG,IAAI,CAYpE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
import { formatBytes } from "../status.js";
|
|
3
|
+
export function printInitResult(root, created) {
|
|
4
|
+
if (created) {
|
|
5
|
+
console.log(pc.green("✔") + ` LocalMind initialized in ${pc.bold(root)}`);
|
|
6
|
+
console.log(` Config: ${pc.dim(".localmind/config.json")}`);
|
|
7
|
+
console.log(` Next: ${pc.cyan("localmind index")}`);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
console.log(pc.yellow("⚠") + ` Already initialized: ${root}`);
|
|
11
|
+
console.log(` Use ${pc.cyan("--force")} to reinitialize.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function printIndexStats(stats) {
|
|
15
|
+
console.log(pc.green("✔") + " Index complete");
|
|
16
|
+
console.log(` Scanned: ${stats.filesScanned}`);
|
|
17
|
+
console.log(` Indexed: ${stats.filesIndexed}`);
|
|
18
|
+
console.log(` Skipped: ${stats.filesSkipped}`);
|
|
19
|
+
console.log(` Removed: ${stats.filesRemoved}`);
|
|
20
|
+
console.log(` Chunks: ${stats.chunksCreated}`);
|
|
21
|
+
console.log(` Duration: ${(stats.durationMs / 1000).toFixed(2)}s`);
|
|
22
|
+
}
|
|
23
|
+
export function printStatus(status) {
|
|
24
|
+
console.log(pc.bold("LocalMind status"));
|
|
25
|
+
console.log(` Root: ${status.root}`);
|
|
26
|
+
console.log(` Provider: ${status.config.provider}`);
|
|
27
|
+
console.log(` Files: ${status.fileCount}`);
|
|
28
|
+
console.log(` Chunks: ${status.chunkCount}`);
|
|
29
|
+
console.log(` Terms: ${status.termCount}`);
|
|
30
|
+
console.log(` Graph edges:${status.edgeCount}`);
|
|
31
|
+
console.log(` Embeddings: ${status.embeddingCount}`);
|
|
32
|
+
console.log(` DB size: ${formatBytes(status.dbSizeBytes)}`);
|
|
33
|
+
console.log(` Last index: ${status.lastIndexedAt ?? "never"}`);
|
|
34
|
+
console.log(` DB path: ${pc.dim(status.dbPath)}`);
|
|
35
|
+
}
|
|
36
|
+
export function printSearchHits(hits) {
|
|
37
|
+
if (!hits.length) {
|
|
38
|
+
console.log(pc.yellow("No results."));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const hit of hits) {
|
|
42
|
+
console.log(pc.cyan(`#${hit.rank}`) +
|
|
43
|
+
` ${pc.bold(hit.path)}` +
|
|
44
|
+
pc.dim(`:${hit.startLine}-${hit.endLine}`) +
|
|
45
|
+
pc.dim(` score=${hit.score.toFixed(4)}`));
|
|
46
|
+
const preview = hit.content
|
|
47
|
+
.split("\n")
|
|
48
|
+
.slice(0, 6)
|
|
49
|
+
.map((l) => " " + l)
|
|
50
|
+
.join("\n");
|
|
51
|
+
console.log(pc.dim(preview));
|
|
52
|
+
if (hit.content.split("\n").length > 6) {
|
|
53
|
+
console.log(pc.dim(" …"));
|
|
54
|
+
}
|
|
55
|
+
console.log();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function printAskResult(result, json = false) {
|
|
59
|
+
if (json) {
|
|
60
|
+
console.log(JSON.stringify(result, null, 2));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
console.log(result.answer);
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(pc.dim(`mode=${result.mode} provider=${result.provider} hits=${result.hits.length} took=${result.tookMs}ms`));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyBpC,wBAAgB,YAAY,IAAI,OAAO,CA6OtC;AAeD,wBAAsB,MAAM,CAAC,IAAI,WAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/D"}
|