xindex 1.0.25 → 1.0.27
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 +16 -3
- package/apps/mcpApp.ts +3 -2
- package/apps/run.mcp.ts +5 -1
- package/apps/run.reindex.ts +23 -0
- package/apps/run.search.ts +5 -1
- package/apps/searchApp.ts +8 -2
- package/bin/xindex-reindex +2 -0
- package/components/config/loadConfig.ts +6 -3
- package/components/config/searchLimits.ts +6 -0
- package/components/config/xindexConfig.ts +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -144,7 +144,7 @@ Both skills assume the `xindex` MCP server is registered (see the section above)
|
|
|
144
144
|
|
|
145
145
|
## CLI reference
|
|
146
146
|
|
|
147
|
-
All
|
|
147
|
+
All six binaries run from any directory; they index/search the current working directory.
|
|
148
148
|
|
|
149
149
|
### `xindex-index [paths...]`
|
|
150
150
|
|
|
@@ -157,7 +157,7 @@ xindex-index apps features
|
|
|
157
157
|
|
|
158
158
|
### `xindex-search <query...>`
|
|
159
159
|
|
|
160
|
-
Search the index. Args are joined into one query. Default limit: 7.
|
|
160
|
+
Search the index. Args are joined into one query. Default limit: 7 (capped by `searchMaxLimit`, default 12).
|
|
161
161
|
|
|
162
162
|
```bash
|
|
163
163
|
xindex-search "database migration logic"
|
|
@@ -182,13 +182,22 @@ Wipe and recreate the index. Destructive.
|
|
|
182
182
|
xindex-reset
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
+
### `xindex-reindex [paths...]`
|
|
186
|
+
|
|
187
|
+
Reset the index, then build it again. Same as `xindex-reset` followed by `xindex-index`. Defaults to `.`.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
xindex-reindex
|
|
191
|
+
xindex-reindex apps features
|
|
192
|
+
```
|
|
193
|
+
|
|
185
194
|
### `xindex-mcp`
|
|
186
195
|
|
|
187
196
|
MCP stdio server; background watcher by default. Flags: `--watch-disabled`, `--watch-dir=./src` — rare use cases; defaults are fine for most setups.
|
|
188
197
|
|
|
189
198
|
## MCP tools
|
|
190
199
|
|
|
191
|
-
- **`xindex_search`** — semantic search. `query: string`, `limit?: number` (default 7,
|
|
200
|
+
- **`xindex_search`** — semantic search. `query: string`, `limit?: number` (default 7, capped at `searchMaxLimit` from `.xindex.json`, default 12).
|
|
192
201
|
- **`xindex_index`** — index paths. `inputs: string[]` (at least one).
|
|
193
202
|
- **`xindex_reset`** — wipe index (destructive). No input.
|
|
194
203
|
|
|
@@ -201,6 +210,8 @@ Project-root file. All fields optional; unknown keys ignored; missing/empty →
|
|
|
201
210
|
- **`ignoreKeywords`** — `string[]`, default `[]`. Tokens stripped before embedding — add project slang/boilerplate polluting results. Entries ≤1 char warn.
|
|
202
211
|
- **`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.
|
|
203
212
|
- **`maxLines`** — `number`, default `30`. Lines per chunk — tune if chunks feel over/under-sized.
|
|
213
|
+
- **`searchDefaultLimit`** — `number`, default `7`. Results per search when no `limit` is passed.
|
|
214
|
+
- **`searchMaxLimit`** — `number`, default `12`. Hard cap on `limit` (MCP and CLI); values above this are clamped. `searchDefaultLimit` cannot exceed this.
|
|
204
215
|
- **`maxFileBytes`** — `number`, default `50000`. Skip files over this (50 KB) — raise to index larger generated files.
|
|
205
216
|
- **`followSymlinks`** — `boolean`, default `false`. When `false`, symlinks are skipped with a log line. `true` follows them (cycles broken via `realpath` dedup).
|
|
206
217
|
|
|
@@ -211,6 +222,8 @@ Full example with every option (copy-paste and trim what you don't need):
|
|
|
211
222
|
"ignoreKeywords": ["import", "export", "function", "const"],
|
|
212
223
|
"ignoreFiles": [".xindex", "node_modules", "dist/**"],
|
|
213
224
|
"maxLines": 30,
|
|
225
|
+
"searchDefaultLimit": 7,
|
|
226
|
+
"searchMaxLimit": 12,
|
|
214
227
|
"maxFileBytes": 50000,
|
|
215
228
|
"followSymlinks": false
|
|
216
229
|
}
|
package/apps/mcpApp.ts
CHANGED
|
@@ -50,8 +50,9 @@ export function McpApp({
|
|
|
50
50
|
inputSchema: z.object({
|
|
51
51
|
query: z.string()
|
|
52
52
|
.describe("Natural language search query"),
|
|
53
|
-
limit: z.number().int().min(1).
|
|
54
|
-
.
|
|
53
|
+
limit: z.number().int().min(1).default(config.searchDefaultLimit)
|
|
54
|
+
.transform(v => Math.min(v, config.searchMaxLimit))
|
|
55
|
+
.describe(`Max results (default ${config.searchDefaultLimit}, capped at ${config.searchMaxLimit})`),
|
|
55
56
|
windowLines: z.number().int().min(1).default(config.maxLines).optional()
|
|
56
57
|
.describe("Optional line-window size for per-file snippet locating"),
|
|
57
58
|
includePaths: z.array(z.string()).optional()
|
package/apps/run.mcp.ts
CHANGED
|
@@ -29,7 +29,11 @@ const indexApp = async (inputs: string[]) => {
|
|
|
29
29
|
await rawIndexApp(inputs);
|
|
30
30
|
await flush();
|
|
31
31
|
};
|
|
32
|
-
const search = SearchApp({
|
|
32
|
+
const search = SearchApp({
|
|
33
|
+
searchIndex: searchContentIndex,
|
|
34
|
+
searchDefaultLimit: config.searchDefaultLimit,
|
|
35
|
+
searchMaxLimit: config.searchMaxLimit,
|
|
36
|
+
});
|
|
33
37
|
|
|
34
38
|
const appId = AppId();
|
|
35
39
|
const watcherLock = WatcherLock({
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {BuildComponents} from "../components/buildComponents.js";
|
|
2
|
+
import {BufferedLoggerToStdOut} from "../components/logger.js";
|
|
3
|
+
import {WalkFiles} from "../components/walkFiles.js";
|
|
4
|
+
import {IndexApp} from "./indexApp.js";
|
|
5
|
+
import {AppId} from "../components/appId.js";
|
|
6
|
+
import {INDEXING_COALESCE_MS} from "../components/config/INDEXING_COALESCE_MS.js";
|
|
7
|
+
|
|
8
|
+
const appId = AppId();
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
const log = BufferedLoggerToStdOut();
|
|
11
|
+
const {indexContentBatch, getIndexStats, resetIndex, config, flush} = await BuildComponents({log, indexingCoalesceMs: INDEXING_COALESCE_MS});
|
|
12
|
+
const walkFiles = WalkFiles({cwd, log, ignoreFiles: config.ignoreFiles, followSymlinks: config.followSymlinks});
|
|
13
|
+
const indexApp = IndexApp({walkFiles, indexContent: indexContentBatch, log, maxFileBytes: config.maxFileBytes});
|
|
14
|
+
|
|
15
|
+
const inputs = process.argv.slice(2);
|
|
16
|
+
if (!inputs.length) inputs.push(".");
|
|
17
|
+
|
|
18
|
+
log(`[${appId}] resetting index...`);
|
|
19
|
+
await resetIndex();
|
|
20
|
+
log(`[${appId}] started, indexing: ${inputs.join(", ")}`);
|
|
21
|
+
await indexApp(inputs);
|
|
22
|
+
await flush();
|
|
23
|
+
log(`[${appId}] done:`, await getIndexStats());
|
package/apps/run.search.ts
CHANGED
|
@@ -5,7 +5,11 @@ import {FormatSearchResults} from "../components/index/formatSearchResults.js";
|
|
|
5
5
|
|
|
6
6
|
const log = BufferedLoggerToStdOut();
|
|
7
7
|
const {searchContentIndex, config} = await BuildComponents({log});
|
|
8
|
-
const search = SearchApp({
|
|
8
|
+
const search = SearchApp({
|
|
9
|
+
searchIndex: searchContentIndex,
|
|
10
|
+
searchDefaultLimit: config.searchDefaultLimit,
|
|
11
|
+
searchMaxLimit: config.searchMaxLimit,
|
|
12
|
+
});
|
|
9
13
|
|
|
10
14
|
const query = process.argv.slice(2).join(" ");
|
|
11
15
|
if (!query) {
|
package/apps/searchApp.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import {clampSearchLimit, SEARCH_DEFAULT_LIMIT} from "../components/config/searchLimits.js";
|
|
1
2
|
import {IIndexRecord, ISearchIndex, ISearchIndexOptions} from "../features/searchIndex.js";
|
|
2
3
|
|
|
3
4
|
export type ISearchApp = (query: string, options?: Partial<ISearchIndexOptions>) => Promise<IIndexRecord[]>;
|
|
4
5
|
|
|
5
|
-
export function SearchApp({
|
|
6
|
+
export function SearchApp({
|
|
7
|
+
searchIndex,
|
|
8
|
+
searchDefaultLimit = SEARCH_DEFAULT_LIMIT,
|
|
9
|
+
searchMaxLimit,
|
|
10
|
+
}: {
|
|
6
11
|
searchIndex: ISearchIndex;
|
|
7
12
|
searchDefaultLimit?: number;
|
|
13
|
+
searchMaxLimit: number;
|
|
8
14
|
}): ISearchApp {
|
|
9
15
|
return async function search(query, options = {}) {
|
|
10
16
|
const normalizedOptions: ISearchIndexOptions = {
|
|
11
|
-
limit: options.limit ?? searchDefaultLimit,
|
|
17
|
+
limit: clampSearchLimit(options.limit ?? searchDefaultLimit, searchMaxLimit),
|
|
12
18
|
prefetchMultiplier: options.prefetchMultiplier,
|
|
13
19
|
scoreThreshold: options.scoreThreshold,
|
|
14
20
|
locateInFiles: options.locateInFiles,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {readFile} from "fs/promises";
|
|
2
|
+
import {clampSearchLimit, SEARCH_DEFAULT_LIMIT, SEARCH_MAX_LIMIT} from "./searchLimits.js";
|
|
2
3
|
import {IXindexConfig} from "./xindexConfig.js";
|
|
3
4
|
import {ILogger} from "../logger.js";
|
|
4
5
|
|
|
5
6
|
const DEFAULT_MAX_LINES = 30;
|
|
6
|
-
const DEFAULT_SEARCH_DEFAULT_LIMIT = 7;
|
|
7
7
|
const DEFAULT_SEARCH_CONCURRENCY = 4;
|
|
8
8
|
const DEFAULT_MAX_FILE_BYTES = 50_000;
|
|
9
9
|
const DEFAULT_FOLLOW_SYMLINKS = false;
|
|
@@ -13,7 +13,8 @@ const DEFAULTS: IXindexConfig = {
|
|
|
13
13
|
ignoreKeywords: [],
|
|
14
14
|
ignoreFiles: DEFAULT_IGNORE_FILES,
|
|
15
15
|
maxLines: DEFAULT_MAX_LINES,
|
|
16
|
-
searchDefaultLimit:
|
|
16
|
+
searchDefaultLimit: SEARCH_DEFAULT_LIMIT,
|
|
17
|
+
searchMaxLimit: SEARCH_MAX_LIMIT,
|
|
17
18
|
searchConcurrency: DEFAULT_SEARCH_CONCURRENCY,
|
|
18
19
|
maxFileBytes: DEFAULT_MAX_FILE_BYTES,
|
|
19
20
|
followSymlinks: DEFAULT_FOLLOW_SYMLINKS,
|
|
@@ -45,11 +46,13 @@ export function LoadConfig({configPath, log}: { configPath: string, log: ILogger
|
|
|
45
46
|
const toStrings = (v: unknown) => Array.isArray(v) ? v.filter((e): e is string => typeof e === "string") : [];
|
|
46
47
|
const toNum = (v: unknown, def: number): number => typeof v === "number" ? v : def;
|
|
47
48
|
|
|
49
|
+
const searchMaxLimit = clampSearchLimit(toNum(parsed.searchMaxLimit, SEARCH_MAX_LIMIT));
|
|
48
50
|
const config: IXindexConfig = {
|
|
49
51
|
ignoreKeywords: toStrings(parsed.ignoreKeywords),
|
|
50
52
|
ignoreFiles: toStrings(parsed.ignoreFiles),
|
|
51
53
|
maxLines: toNum(parsed.maxLines, DEFAULT_MAX_LINES),
|
|
52
|
-
|
|
54
|
+
searchMaxLimit,
|
|
55
|
+
searchDefaultLimit: clampSearchLimit(toNum(parsed.searchDefaultLimit, SEARCH_DEFAULT_LIMIT), searchMaxLimit),
|
|
53
56
|
searchConcurrency: Math.max(1, toNum(parsed.searchConcurrency, DEFAULT_SEARCH_CONCURRENCY)),
|
|
54
57
|
maxFileBytes: toNum(parsed.maxFileBytes, DEFAULT_MAX_FILE_BYTES),
|
|
55
58
|
followSymlinks: typeof parsed.followSymlinks === "boolean" ? parsed.followSymlinks : DEFAULT_FOLLOW_SYMLINKS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xindex",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"description": "Local semantic code search — index codebase, search by meaning or keywords",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "xindex.ts",
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
"xindex-search": "bin/xindex-search",
|
|
11
11
|
"xindex-mcp": "bin/xindex-mcp",
|
|
12
12
|
"xindex-reset": "bin/xindex-reset",
|
|
13
|
+
"xindex-reindex": "bin/xindex-reindex",
|
|
13
14
|
"xindex-watch": "bin/xindex-watch"
|
|
14
15
|
},
|
|
15
16
|
"scripts": {
|
|
16
17
|
"index": "tsx apps/run.index.ts",
|
|
17
18
|
"search": "tsx apps/run.search.ts",
|
|
18
19
|
"reset": "tsx apps/run.reset.ts",
|
|
20
|
+
"reindex": "tsx apps/run.reindex.ts",
|
|
19
21
|
"test": "npm run test.functional && npm run test.compilation",
|
|
20
22
|
"dev.ci": "npm run test && npm run test.npx",
|
|
21
23
|
"mcp": "tsx apps/run.mcp.ts",
|