xindex 1.0.10 → 1.0.12
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/README.md +83 -62
- package/apps/mcpApp.ts +12 -8
- package/componets/config/loadConfig.ts +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# xindex
|
|
2
2
|
|
|
3
3
|
**grep matches text. xindex matches meaning.**
|
|
4
|
+
Fully local, no cloud, no API keys, instant response.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Ask **"where is auth handled?"** — get the right files, even when they don't contain the word *auth*.
|
|
7
|
+
|
|
8
|
+
Local semantic code search for your codebase,
|
|
9
|
+
w/ an MCP server so Claude Code searches your repo directly.
|
|
10
|
+
Fewer hallucinations, fewer round-trips.
|
|
6
11
|
|
|
7
12
|
## Install
|
|
8
13
|
|
|
@@ -12,7 +17,7 @@ Requires **Node ≥22**.
|
|
|
12
17
|
npm i -g xindex
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
First run downloads a small embedding model (
|
|
20
|
+
First run downloads a small embedding model (~23MB). After that, fully offline.
|
|
16
21
|
|
|
17
22
|
## Quick start
|
|
18
23
|
|
|
@@ -26,7 +31,16 @@ Index lives in `./.xindex/` — add it to `.gitignore`.
|
|
|
26
31
|
|
|
27
32
|
## Use with Claude Code (MCP)
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
### Zero-install, all projects
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude mcp add --scope user xindex -- npx xindex
|
|
38
|
+
# remove later: claude mcp remove xindex --scope user
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Per-project
|
|
42
|
+
|
|
43
|
+
Drop this into `.mcp.json` at the repo root — `npx` fetches on demand:
|
|
30
44
|
|
|
31
45
|
```json
|
|
32
46
|
{
|
|
@@ -39,12 +53,12 @@ Drop this into `.mcp.json` at your project root — **no install required**, `np
|
|
|
39
53
|
}
|
|
40
54
|
```
|
|
41
55
|
|
|
42
|
-
|
|
56
|
+
### With a global install
|
|
43
57
|
|
|
44
|
-
1.
|
|
45
|
-
2.
|
|
58
|
+
1. `npm i -g xindex`
|
|
59
|
+
2. In `.mcp.json`, set `"command": "xindex-mcp"` with `"args": []`.
|
|
46
60
|
|
|
47
|
-
Open the project in Claude Code — it picks up the xindex MCP server and can call `xindex_search`, `xindex_index`, and `xindex_reset` directly.
|
|
61
|
+
Open the project in Claude Code — it picks up the xindex MCP server and can call `xindex_search`, `xindex_index`, and `xindex_reset` directly.
|
|
48
62
|
|
|
49
63
|
## Features
|
|
50
64
|
|
|
@@ -54,11 +68,11 @@ Open the project in Claude Code — it picks up the xindex MCP server and can ca
|
|
|
54
68
|
- **Watch mode** — keeps the index warm while you code
|
|
55
69
|
- **Gitignore-aware** — respects `.gitignore` + custom ignore rules
|
|
56
70
|
- **Zero config** — works with defaults; `.xindex.json` is optional
|
|
57
|
-
- **Tolerant** —
|
|
71
|
+
- **Tolerant** — unreadable, oversize, empty, binary files and symlinks are skipped;
|
|
58
72
|
|
|
59
73
|
## Claude Code skills (`@xi`)
|
|
60
74
|
|
|
61
|
-
Two optional
|
|
75
|
+
Two optional Claude Code skills wrap the MCP tools so you don't have to think about them:
|
|
62
76
|
|
|
63
77
|
- **`ask-xi`** — read-only discovery. `@xi where is auth handled` drafts several focused queries, runs `xindex_search` for each, and returns ranked file paths with matched keywords. Use it as a cheap first step before grepping or asking a heavier model.
|
|
64
78
|
- **`xindex`** — index management (`xindex_index`, `xindex_reset`). Reset requires explicit confirmation every time.
|
|
@@ -67,21 +81,23 @@ Keeping them separate keeps `@xi` safe to fire casually while destructive ops st
|
|
|
67
81
|
|
|
68
82
|
### Install
|
|
69
83
|
|
|
70
|
-
Pick
|
|
84
|
+
Pick a scope — project (checked in, shared with the repo) or user-global (available in every project):
|
|
71
85
|
|
|
72
86
|
```bash
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
# project-scoped
|
|
88
|
+
SKILLS=.claude/skills
|
|
89
|
+
```
|
|
90
|
+
```bash
|
|
91
|
+
# OR user-global
|
|
92
|
+
SKILLS=~/.claude/skills
|
|
78
93
|
```
|
|
79
94
|
|
|
80
|
-
Then
|
|
95
|
+
Then copy-paste the block below to create both skills:
|
|
81
96
|
|
|
82
|
-
|
|
97
|
+
```bash
|
|
98
|
+
mkdir -p "$SKILLS/ask-xi" "$SKILLS/xindex"
|
|
83
99
|
|
|
84
|
-
|
|
100
|
+
cat > "$SKILLS/ask-xi/SKILL.md" <<'EOF'
|
|
85
101
|
---
|
|
86
102
|
name: ask-xi
|
|
87
103
|
description: Discovers relevant files via xindex semantic search — preps queries, auto-indexes on empty, returns file links with keywords. Triggered by @xi.
|
|
@@ -96,11 +112,9 @@ Surface-level codebase discovery via xindex. Tool: `xindex_search` (natural-lang
|
|
|
96
112
|
4. Return file paths + brief keywords showing why each matched.
|
|
97
113
|
|
|
98
114
|
Output = file links + keywords, not analysis. **Escalate to `/ask-cursor` by default** (cheap codebase reasoning); only go to `/ask-claude` for multi-file/pattern analysis or `/ask-claude-opus` for trade-offs. For reset or full re-index, delegate to `/xindex` (owns safety rules).
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
`xindex/SKILL.md`:
|
|
115
|
+
EOF
|
|
102
116
|
|
|
103
|
-
|
|
117
|
+
cat > "$SKILLS/xindex/SKILL.md" <<'EOF'
|
|
104
118
|
---
|
|
105
119
|
name: xindex
|
|
106
120
|
description: Manages xindex semantic search — index, search, reset via MCP tools. For research questions, use /ask-xi.
|
|
@@ -118,9 +132,10 @@ Full xindex tool management. For research, use `/ask-xi`. Install: `npm i -g xin
|
|
|
118
132
|
**Scoped indexing (preferred):** index only task-relevant content-heavy folders, sequentially. Full-repo `xindex_index(["."])` only for cross-cutting discovery.
|
|
119
133
|
|
|
120
134
|
$ARGUMENTS
|
|
121
|
-
|
|
135
|
+
EOF
|
|
136
|
+
```
|
|
122
137
|
|
|
123
|
-
Both skills assume the `xindex` MCP server is registered (see the section above). Restart Claude Code after adding
|
|
138
|
+
Both skills assume the `xindex` MCP server is registered (see the section above). Restart Claude Code after adding.
|
|
124
139
|
|
|
125
140
|
---
|
|
126
141
|
|
|
@@ -129,46 +144,50 @@ Both skills assume the `xindex` MCP server is registered (see the section above)
|
|
|
129
144
|
All five binaries run from any directory; they index/search the current working directory.
|
|
130
145
|
|
|
131
146
|
### `xindex-index [paths...]`
|
|
132
|
-
|
|
147
|
+
|
|
148
|
+
Build or update the index. Defaults to `.`.
|
|
149
|
+
|
|
133
150
|
```bash
|
|
134
151
|
xindex-index .
|
|
135
152
|
xindex-index apps features
|
|
136
153
|
```
|
|
137
154
|
|
|
138
155
|
### `xindex-search <query...>`
|
|
139
|
-
|
|
156
|
+
|
|
157
|
+
Search the index. Args are joined into one query. Default limit: 7.
|
|
158
|
+
|
|
140
159
|
```bash
|
|
141
160
|
xindex-search "database migration logic"
|
|
142
161
|
xindex-search file watcher debounce
|
|
143
162
|
```
|
|
144
163
|
|
|
145
164
|
### `xindex-watch [paths...]`
|
|
146
|
-
|
|
165
|
+
|
|
166
|
+
Initial index + filesystem watch for incremental updates. Lockfile coordinates concurrent watchers; Ctrl+C releases cleanly.
|
|
167
|
+
|
|
168
|
+
Bundled inside `xindex-mcp` — no need to run separately in the primary MCP use case.
|
|
169
|
+
|
|
147
170
|
```bash
|
|
148
171
|
xindex-watch .
|
|
149
172
|
```
|
|
150
173
|
|
|
151
174
|
### `xindex-reset`
|
|
175
|
+
|
|
152
176
|
Wipe and recreate the index. Destructive.
|
|
177
|
+
|
|
153
178
|
```bash
|
|
154
179
|
xindex-reset
|
|
155
180
|
```
|
|
156
181
|
|
|
157
182
|
### `xindex-mcp`
|
|
158
|
-
Run as an MCP stdio server. Starts a background watcher by default.
|
|
159
|
-
```bash
|
|
160
|
-
xindex-mcp # with watch
|
|
161
|
-
xindex-mcp --watch-disabled # no watch
|
|
162
|
-
xindex-mcp --watch-dir=./src # watch a specific dir
|
|
163
|
-
```
|
|
164
183
|
|
|
165
|
-
|
|
184
|
+
MCP stdio server; background watcher by default. Flags: `--watch-disabled`, `--watch-dir=./src` — rare use cases; defaults are fine for most setups.
|
|
166
185
|
|
|
167
|
-
|
|
168
|
-
- **`xindex_index`** — index paths. `inputs: string[]` (at least one)
|
|
169
|
-
- **`xindex_reset`** — wipe index (destructive). No input
|
|
186
|
+
## MCP tools
|
|
170
187
|
|
|
171
|
-
|
|
188
|
+
- **`xindex_search`** — semantic search. `query: string`, `limit?: number` (default 7, max 50).
|
|
189
|
+
- **`xindex_index`** — index paths. `inputs: string[]` (at least one).
|
|
190
|
+
- **`xindex_reset`** — wipe index (destructive). No input.
|
|
172
191
|
|
|
173
192
|
## Configuration
|
|
174
193
|
|
|
@@ -177,29 +196,33 @@ Note: both CLI `xindex-search` and MCP `xindex_search` default to 7 results; MCP
|
|
|
177
196
|
Project-root file. All fields optional; unknown keys ignored; missing/empty → defaults.
|
|
178
197
|
|
|
179
198
|
- **`ignoreKeywords`** — `string[]`, default `[]`. Tokens stripped before embedding — add project slang/boilerplate polluting results. Entries ≤1 char warn.
|
|
180
|
-
- **`ignoreFiles`** — `string[]`, default `[]`. Extra globs excluded during walk/watch, on top of `.gitignore
|
|
199
|
+
- **`ignoreFiles`** — `string[]`, default `['.xindex', 'node_modules']`. Extra globs excluded during walk/watch, on top of `.gitignore`. Setting this **replaces** the default — re-include `.xindex` and `node_modules` if you still want them skipped.
|
|
181
200
|
- **`maxLines`** — `number`, default `30`. Lines per chunk — tune if chunks feel over/under-sized.
|
|
182
201
|
- **`maxFileBytes`** — `number`, default `50000`. Skip files over this (50 KB) — raise to index larger generated files.
|
|
183
|
-
- **`followSymlinks`** — `boolean`, default `false`. When `false`,
|
|
202
|
+
- **`followSymlinks`** — `boolean`, default `false`. When `false`, symlinks are skipped with a log line. `true` follows them (cycles broken via `realpath` dedup).
|
|
203
|
+
|
|
204
|
+
Full example with every option (copy-paste and trim what you don't need):
|
|
184
205
|
|
|
185
206
|
```json
|
|
186
207
|
{
|
|
187
208
|
"ignoreKeywords": ["import", "export", "function", "const"],
|
|
188
|
-
"ignoreFiles": ["
|
|
209
|
+
"ignoreFiles": [".xindex", "node_modules", "dist/**"],
|
|
210
|
+
"maxLines": 30,
|
|
211
|
+
"maxFileBytes": 50000,
|
|
212
|
+
"followSymlinks": false
|
|
189
213
|
}
|
|
190
214
|
```
|
|
191
215
|
|
|
192
|
-
Override only what you need; re-run `xindex-index .` (or let the watcher pick it up). Invalid JSON
|
|
216
|
+
Override only what you need; re-run `xindex-index .` (or let the watcher pick it up on restart). Invalid JSON or unreadable config warns and falls back to defaults; wrong-typed fields fall back silently.
|
|
193
217
|
|
|
194
218
|
### `.xindex/` folder
|
|
195
219
|
|
|
196
|
-
|
|
197
|
-
- `semantic/` — the vectra index (vectors + metadata)
|
|
198
|
-
- `lock.json` — watcher lock (coordinates watch/MCP processes)
|
|
220
|
+
Auto-created. Contains:
|
|
199
221
|
|
|
200
|
-
|
|
222
|
+
- `semantic/` — the semantic index (vectors + metadata)
|
|
223
|
+
- `lock.json` — watcher lock (coordinates watch/MCP processes)
|
|
201
224
|
|
|
202
|
-
|
|
225
|
+
**Always gitignore it** — it's already excluded from the walk by default, this is just for git:
|
|
203
226
|
|
|
204
227
|
```
|
|
205
228
|
.xindex
|
|
@@ -211,10 +234,10 @@ node_modules/
|
|
|
211
234
|
```
|
|
212
235
|
your repo xindex
|
|
213
236
|
───────── ──────
|
|
214
|
-
*.ts / *.md ──► walk ──►
|
|
215
|
-
.gitignore
|
|
216
|
-
|
|
217
|
-
CLI / MCP ◄── search ◄── embed
|
|
237
|
+
*.ts / *.md ──► walk ──► embed ──► .xindex/
|
|
238
|
+
.gitignore (semantic index)
|
|
239
|
+
▲
|
|
240
|
+
CLI / MCP ◄── search ◄── embed ◄── "question" ┘
|
|
218
241
|
```
|
|
219
242
|
|
|
220
243
|
## Project layout
|
|
@@ -222,7 +245,7 @@ node_modules/
|
|
|
222
245
|
```
|
|
223
246
|
apps/ entry points (run.*.ts) + app composers (IndexApp, SearchApp, McpApp, ...)
|
|
224
247
|
bin/ shebang wrappers invoked by npm and .mcp.json
|
|
225
|
-
componets/ shared building blocks: config, walk, watch, embed,
|
|
248
|
+
componets/ shared building blocks: config, walk, watch, embed, index adapter, logger
|
|
226
249
|
features/ domain operations: indexContent, searchIndex, removeContent, resetIndex
|
|
227
250
|
packages/ small internal libs (streamx, fun)
|
|
228
251
|
.xindex/ runtime data (gitignored)
|
|
@@ -234,21 +257,19 @@ See [CLAUDE.md](CLAUDE.md) for contributor conventions (HOF pattern, logger rule
|
|
|
234
257
|
|
|
235
258
|
## Development
|
|
236
259
|
|
|
237
|
-
Working on xindex itself?
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
git clone <repo-url> xindex
|
|
241
|
-
cd xindex
|
|
242
|
-
yarn install # or npm install
|
|
243
|
-
npm link # exposes xindex-* binaries from your working copy
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Check TypeScript compilation:
|
|
260
|
+
Working on xindex itself? From your working copy:
|
|
247
261
|
|
|
248
262
|
```bash
|
|
263
|
+
yarn install # or npm install
|
|
264
|
+
npm link # exposes xindex-* binaries from this checkout
|
|
249
265
|
yarn test.compilation
|
|
250
266
|
```
|
|
251
267
|
|
|
252
268
|
## License
|
|
253
269
|
|
|
254
270
|
MIT
|
|
271
|
+
|
|
272
|
+
## Links
|
|
273
|
+
|
|
274
|
+
- [Claude Code skills](https://docs.claude.com/en/docs/claude-code/skills)
|
|
275
|
+
|
package/apps/mcpApp.ts
CHANGED
|
@@ -39,8 +39,11 @@ export function McpApp({
|
|
|
39
39
|
// --- search ---
|
|
40
40
|
|
|
41
41
|
server.registerTool("xindex_search", {
|
|
42
|
-
title: "Search codebase",
|
|
43
|
-
description: "Semantic search
|
|
42
|
+
title: "Search codebase (semantic)",
|
|
43
|
+
description: "Semantic codebase search — finds files by meaning, not substring. " +
|
|
44
|
+
"For discovery, run 3–7 parallel queries then narrower follow-ups. " +
|
|
45
|
+
"Returns ranked file paths + matched keywords (links, not analysis). " +
|
|
46
|
+
"If sparse, run `xindex_index` on relevant roots first (scoped, one path per call).",
|
|
44
47
|
inputSchema: z.object({
|
|
45
48
|
query: z.string()
|
|
46
49
|
.describe("Natural language search query"),
|
|
@@ -65,11 +68,12 @@ export function McpApp({
|
|
|
65
68
|
|
|
66
69
|
server.registerTool("xindex_index", {
|
|
67
70
|
title: "Index files or directories",
|
|
68
|
-
description: "Index files
|
|
69
|
-
"
|
|
71
|
+
description: "Index files/directories for semantic search; recursive, respects .gitignore. " +
|
|
72
|
+
"Prefer scoped paths (one per call, e.g. `src`, `apps`) over full-repo `['.']` — " +
|
|
73
|
+
"reserve the latter for first-time setup or cross-cutting discovery.",
|
|
70
74
|
inputSchema: z.object({
|
|
71
75
|
inputs: z.array(z.string()).min(1)
|
|
72
|
-
.describe("File or directory paths to index"),
|
|
76
|
+
.describe("File or directory paths to index (one path per call preferred; scoped over full-repo)"),
|
|
73
77
|
}),
|
|
74
78
|
}, async ({inputs}) => {
|
|
75
79
|
try {
|
|
@@ -89,9 +93,9 @@ export function McpApp({
|
|
|
89
93
|
// --- reset ---
|
|
90
94
|
|
|
91
95
|
server.registerTool("xindex_reset", {
|
|
92
|
-
title: "Reset index",
|
|
93
|
-
description: "
|
|
94
|
-
"
|
|
96
|
+
title: "Reset index (destructive)",
|
|
97
|
+
description: "Wipe and recreate the semantic index. **Destructive** — MUST get explicit user confirmation; " +
|
|
98
|
+
"if ambiguous, don't run. Recovery / reindex flow: confirm → `xindex_reset` → `xindex_index(['.'])` → `xindex_search`.",
|
|
95
99
|
inputSchema: z.object({}),
|
|
96
100
|
annotations: {destructiveHint: true},
|
|
97
101
|
}, async () => {
|
|
@@ -24,7 +24,8 @@ export function LoadConfig({configPath, log}: { configPath: string, log: ILogger
|
|
|
24
24
|
raw = await readFile(configPath, "utf8");
|
|
25
25
|
} catch (e: any) {
|
|
26
26
|
if (e?.code === "ENOENT") return {...DEFAULTS};
|
|
27
|
-
|
|
27
|
+
log(`warning: cannot read ${configPath}: ${e?.message ?? e} — using defaults`);
|
|
28
|
+
return {...DEFAULTS};
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
if (!raw.trim()) return {...DEFAULTS};
|
|
@@ -33,7 +34,8 @@ export function LoadConfig({configPath, log}: { configPath: string, log: ILogger
|
|
|
33
34
|
try {
|
|
34
35
|
parsed = JSON.parse(raw);
|
|
35
36
|
} catch (e) {
|
|
36
|
-
|
|
37
|
+
log(`warning: failed to parse ${configPath}: ${e instanceof Error ? e.message : e} — using defaults`);
|
|
38
|
+
return {...DEFAULTS};
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
const toStrings = (v: unknown) => Array.isArray(v) ? v.filter((e): e is string => typeof e === "string") : [];
|