xindex 1.0.26 → 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 CHANGED
@@ -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"
@@ -197,7 +197,7 @@ MCP stdio server; background watcher by default. Flags: `--watch-disabled`, `--w
197
197
 
198
198
  ## MCP tools
199
199
 
200
- - **`xindex_search`** — semantic search. `query: string`, `limit?: number` (default 7, max 50).
200
+ - **`xindex_search`** — semantic search. `query: string`, `limit?: number` (default 7, capped at `searchMaxLimit` from `.xindex.json`, default 12).
201
201
  - **`xindex_index`** — index paths. `inputs: string[]` (at least one).
202
202
  - **`xindex_reset`** — wipe index (destructive). No input.
203
203
 
@@ -210,6 +210,8 @@ Project-root file. All fields optional; unknown keys ignored; missing/empty →
210
210
  - **`ignoreKeywords`** — `string[]`, default `[]`. Tokens stripped before embedding — add project slang/boilerplate polluting results. Entries ≤1 char warn.
211
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.
212
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.
213
215
  - **`maxFileBytes`** — `number`, default `50000`. Skip files over this (50 KB) — raise to index larger generated files.
214
216
  - **`followSymlinks`** — `boolean`, default `false`. When `false`, symlinks are skipped with a log line. `true` follows them (cycles broken via `realpath` dedup).
215
217
 
@@ -220,6 +222,8 @@ Full example with every option (copy-paste and trim what you don't need):
220
222
  "ignoreKeywords": ["import", "export", "function", "const"],
221
223
  "ignoreFiles": [".xindex", "node_modules", "dist/**"],
222
224
  "maxLines": 30,
225
+ "searchDefaultLimit": 7,
226
+ "searchMaxLimit": 12,
223
227
  "maxFileBytes": 50000,
224
228
  "followSymlinks": false
225
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).max(50).default(config.searchDefaultLimit)
54
- .describe(`Max results to return default ${config.searchDefaultLimit}, max 50)`),
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({searchIndex: searchContentIndex, searchDefaultLimit: config.searchDefaultLimit});
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({
@@ -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({searchIndex: searchContentIndex, searchDefaultLimit: config.searchDefaultLimit});
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({searchIndex, searchDefaultLimit = 7}: {
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: DEFAULT_SEARCH_DEFAULT_LIMIT,
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
- searchDefaultLimit: Math.max(1, toNum(parsed.searchDefaultLimit, DEFAULT_SEARCH_DEFAULT_LIMIT)),
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,
@@ -0,0 +1,6 @@
1
+ export const SEARCH_DEFAULT_LIMIT = 7;
2
+ export const SEARCH_MAX_LIMIT = 12;
3
+
4
+ export function clampSearchLimit(limit: number, max = SEARCH_MAX_LIMIT): number {
5
+ return Math.min(max, Math.max(1, limit));
6
+ }
@@ -3,6 +3,7 @@ export type IXindexConfig = {
3
3
  ignoreFiles: string[];
4
4
  maxLines: number;
5
5
  searchDefaultLimit: number;
6
+ searchMaxLimit: number;
6
7
  searchConcurrency: number;
7
8
  maxFileBytes: number;
8
9
  followSymlinks: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xindex",
3
- "version": "1.0.26",
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",