xindex 1.0.0 → 1.0.2
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/.ai/research/.gitkeep +0 -0
- package/.ai/task/.gitkeep +0 -0
- package/README.md +54 -89
- package/apps/run.search.ts +0 -3
- package/componets/index/formatSearchResults.ts +2 -2
- package/media/MEDIUM.md +139 -0
- package/media/SOCIAL.md +102 -0
- package/package.json +1 -1
- package/.ai/research/2026-04-10-file-watching.md +0 -79
- package/.ai/research/2026-04-10-mcp-output-format.md +0 -129
- package/.ai/task/INDEX.md +0 -12
- package/.ai/task/done/INDEX.md +0 -3
- package/.ai/task/done/task.2026-04-09-local-ai-research-protos.log.md +0 -98
- package/.ai/task/done/task.2026-04-09-local-ai-research-protos.md +0 -102
- package/.ai/task/task.2026-04-10-cluster-config.log.md +0 -19
- package/.ai/task/task.2026-04-10-cluster-config.md +0 -118
- package/.ai/task/task.2026-04-10-dir-indexing.log.md +0 -8
- package/.ai/task/task.2026-04-10-dir-indexing.md +0 -92
- package/.ai/task/task.2026-04-10-line-clustering.log.md +0 -50
- package/.ai/task/task.2026-04-10-line-clustering.md +0 -176
- package/.ai/task/task.2026-04-10-object-store.log.md +0 -7
- package/.ai/task/task.2026-04-10-object-store.md +0 -81
- package/.ai/task/task.2026-04-10-search-config.log.md +0 -46
- package/.ai/task/task.2026-04-10-search-config.md +0 -274
- package/.ai/task/task.2026-04-10-watch-indexing.log.md +0 -32
- package/.ai/task/task.2026-04-10-watch-indexing.md +0 -101
- package/.ai/task/task.2026-04-10-xindex-mcp.log.md +0 -5
- package/.ai/task/task.2026-04-10-xindex-mcp.md +0 -92
- package/.ai/task/task.2026-04-10-xindex-mcp.report.md +0 -113
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# Task: xindex-watch — Continuous Indexing with File Watcher
|
|
2
|
-
|
|
3
|
-
## Context
|
|
4
|
-
|
|
5
|
-
Two entry points:
|
|
6
|
-
- `xindex-index` — one-time batch job: index all declared paths (default cwd), exit with status
|
|
7
|
-
- `xindex-watch` — **new**: index all, then watch for changes continuously, Ctrl+C to stop
|
|
8
|
-
|
|
9
|
-
Both default to cwd when no args. Watch handles create, update, move, delete events via a merged stream.
|
|
10
|
-
|
|
11
|
-
**Current pipeline** (`apps/indexApp.ts`):
|
|
12
|
-
```
|
|
13
|
-
from(walkFiles(inputs))
|
|
14
|
-
.pipe(tap(log))
|
|
15
|
-
.pipe(map(readFile → extractKeywords → cleanUp → indexContent))
|
|
16
|
-
→ run()
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
**Key pieces already in place:**
|
|
20
|
-
- `WalkFiles` (`componets/walkFiles.ts`) — async generator yielding relative paths, .gitignore-aware
|
|
21
|
-
- `Writer` (`packages/streamx/src/writer.ts`) — push-based stream writer (backpressure-aware)
|
|
22
|
-
- `merge` (`packages/streamx/src/merge.ts`) — combines multiple streams
|
|
23
|
-
- `from` / `of` / `run` — streamx core
|
|
24
|
-
|
|
25
|
-
**Node.js `fs.watch`** (recursive works on all platforms since Node 22+):
|
|
26
|
-
- `fs.watch(dir, {recursive: true})` — macOS (FSEvents), Windows (ReadDirectoryChangesW), Linux (inotify, fd-per-dir)
|
|
27
|
-
- Returns `AsyncIterable<FileChangeInfo>` with `.eventType` ("rename" | "change") and `.filename`
|
|
28
|
-
- "rename" = create, delete, or move; "change" = content modified
|
|
29
|
-
- Need to `stat()` after event to distinguish create vs delete (file exists → create/update; doesn't exist → delete)
|
|
30
|
-
- Known issues: duplicate events per save, null filenames possible → handled by batchTimed dedup
|
|
31
|
-
|
|
32
|
-
**No external dep needed** — `node:fs/promises` `watch()` returns async iterable directly. If issues arise, chokidar v5 (1 dep, ESM-only) is a drop-in upgrade. See [research](../research/2026-04-10-file-watching.md).
|
|
33
|
-
|
|
34
|
-
## Goal
|
|
35
|
-
|
|
36
|
-
Add `xindex-watch` entry point: index all provided paths (or cwd by default), then watch for changes and keep the index up to date continuously. Ctrl+C gracefully stops. Update `xindex-index` to default to cwd but remain a one-time job.
|
|
37
|
-
|
|
38
|
-
## Diagram
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
bin/xindex-index → run.index.ts bin/xindex-watch → run.watch.ts
|
|
42
|
-
│ one-time, exits │ continuous, SIGINT to stop
|
|
43
|
-
│ │
|
|
44
|
-
├─ inputs || [cwd] ├─ inputs || [cwd]
|
|
45
|
-
└─ IndexApp(inputs) ├─ SIGINT → app.stop()
|
|
46
|
-
│ └─ WatchApp(inputs)
|
|
47
|
-
└─ walkFiles → index → exit │
|
|
48
|
-
├── INITIAL: from(walkFiles)
|
|
49
|
-
│ → map(path → {type:"index", path})
|
|
50
|
-
│
|
|
51
|
-
├── WATCH: from(watchFiles)
|
|
52
|
-
│ → yields {type:"index"|"remove", path}
|
|
53
|
-
│
|
|
54
|
-
└── merge(initial, watch)
|
|
55
|
-
│
|
|
56
|
-
├── batchTimed (debounce)
|
|
57
|
-
├── dedup per path
|
|
58
|
-
├── flat()
|
|
59
|
-
├── tap(log)
|
|
60
|
-
├── type:"index" → readFile → extractKeywords → cleanUp → indexContent
|
|
61
|
-
├── type:"remove" → removeContent
|
|
62
|
-
│
|
|
63
|
-
▼
|
|
64
|
-
runs until SIGINT
|
|
65
|
-
|
|
66
|
-
Event → Vectra:
|
|
67
|
-
created → upsert (add)
|
|
68
|
-
updated → upsert (overwrite)
|
|
69
|
-
deleted → deleteItem
|
|
70
|
-
moved → deleteItem(old) + upsert(new) (two fs.watch events)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Steps
|
|
74
|
-
|
|
75
|
-
### 1. RemoveContent + Wiring
|
|
76
|
-
- **RemoveContent HOF** — create `componets/index/removeContent.ts` wrapping Vectra `deleteItem(id)`, matching `indexContent.ts` pattern
|
|
77
|
-
- **ContentIndexDriver** — add `removeContent` to interface and factory
|
|
78
|
-
- **BuildComponents** — expose `removeContent` in return object
|
|
79
|
-
|
|
80
|
-
### 2. WatchFiles Component
|
|
81
|
-
- **FileEvent type** — define `{type: "index"|"remove", path: string}` in `componets/watchFiles.ts` (exported, shared with WatchApp)
|
|
82
|
-
- **WatchFiles HOF** — create `componets/watchFiles.ts`, uses `Writer<FileEvent>` + `fs.watch(dir, {recursive:true})`; returns `{stream, stop}`
|
|
83
|
-
- **Event mapping** — "change" + stat exists → index; "rename" + stat exists → index; "rename" + stat throws → remove; filter through gitignore rules
|
|
84
|
-
- **Stop method** — close FSWatcher handles + `writer.finish()` to end the stream
|
|
85
|
-
|
|
86
|
-
### 3. WatchApp
|
|
87
|
-
- **WatchApp HOF** — create `apps/watchApp.ts`: merge walk stream (mapped to FileEvents) + watch stream → `batchTimed(20, 150)` → dedup per path → `flat()` → `tap(log)` → process: "index" → readFile → extractKeywords → cleanUp → indexContent; "remove" → removeContent → `run()`; returns `{run, stop}`
|
|
88
|
-
|
|
89
|
-
### 4. Entry Points
|
|
90
|
-
- **run.watch.ts** — new entry point: default to cwd, SIGINT → `app.stop()`, log stats on exit
|
|
91
|
-
- **bin/xindex-watch** + package.json — bin shim + scripts entry
|
|
92
|
-
- **run.index.ts** — default to cwd instead of error-exit; IndexApp unchanged (one-time job)
|
|
93
|
-
|
|
94
|
-
## Edge Cases
|
|
95
|
-
|
|
96
|
-
- **Gitignore changes** — if `.gitignore` itself is modified, watcher should reload rules (or at minimum, not index newly-ignored files)
|
|
97
|
-
- **Binary files** — TODO: filter by extension or detect encoding; for now, readFile utf8 on everything (may produce garbage keywords for binaries)
|
|
98
|
-
- **Rapid renames** — editor save = delete old + create new; debounce window prevents double-processing
|
|
99
|
-
- **Symlinks** — `fs.watch` recursive doesn't follow symlinks; acceptable default
|
|
100
|
-
- **Index doesn't have the file** — "remove" for a file not in index should be a no-op (log warning, don't throw)
|
|
101
|
-
- **Watching individual files** — `fs.watch` works on single files too; not a primary use case but no issue supporting it
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
### 2026-04-10 — Task created
|
|
2
|
-
|
|
3
|
-
- User researched MCP SDK pattern (Server + StdioServerTransport + tool handlers)
|
|
4
|
-
- Scope: wrap existing xindex BuildComponents as 2 MCP tools (index + search)
|
|
5
|
-
- Integration: add to .claude/settings.json for Claude Code auto-discovery
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# Task: xindex-mcp — MCP Server for Semantic Code Search
|
|
2
|
-
|
|
3
|
-
## Context
|
|
4
|
-
|
|
5
|
-
xindex is a working local semantic code search tool (index files → query by meaning). Goal: wrap it as an MCP server so Claude Code can search the codebase directly.
|
|
6
|
-
|
|
7
|
-
**Existing xindex pipeline:**
|
|
8
|
-
- `extractKeywords` → `cleanUpKeywords` → `embed` (MiniLM-L6) → `vectra` (upsert/query)
|
|
9
|
-
- `BuildComponents()` wires everything
|
|
10
|
-
- Entry points: `apps/run.index.ts`, `apps/run.search.ts`
|
|
11
|
-
|
|
12
|
-
**MCP SDK (modern API — `registerTool` + Zod):**
|
|
13
|
-
```ts
|
|
14
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
15
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
-
import { z } from "zod";
|
|
17
|
-
|
|
18
|
-
const server = new McpServer({ name: "xindex", version: "0.1.0" });
|
|
19
|
-
|
|
20
|
-
server.registerTool("xindex_search", {
|
|
21
|
-
title: "Search codebase",
|
|
22
|
-
description: "Semantic search over indexed codebase files",
|
|
23
|
-
inputSchema: z.object({
|
|
24
|
-
query: z.string().describe("Natural language search query"),
|
|
25
|
-
limit: z.number().int().min(1).max(50).default(10).describe("Max results"),
|
|
26
|
-
}),
|
|
27
|
-
annotations: { readOnlyHint: true },
|
|
28
|
-
}, async ({ query, limit }) => {
|
|
29
|
-
// call searchContentIndex(query, limit)
|
|
30
|
-
return { content: [{ type: "text", text: JSON.stringify(results) }] };
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const transport = new StdioServerTransport();
|
|
34
|
-
await server.connect(transport);
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
**Integration:** `.mcp.json` in project root (project scope — shared via git).
|
|
38
|
-
|
|
39
|
-
**Decisions:**
|
|
40
|
-
- `BuildComponents()` inits once at startup — just factories, fast
|
|
41
|
-
- MCP exposes **search only** — indexing is a separate process (`bin/xindex-index`)
|
|
42
|
-
- Future: file watcher daemon to keep index up to date (out of scope for this task)
|
|
43
|
-
|
|
44
|
-
## Goal
|
|
45
|
-
|
|
46
|
-
Create an MCP server that exposes `xindex_search` as a tool, so Claude Code can search the codebase semantically. Indexing runs separately via CLI.
|
|
47
|
-
|
|
48
|
-
## Diagram
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
┌─────────────────────────┐
|
|
52
|
-
│ bin/xindex-index │
|
|
53
|
-
│ (separate process) │
|
|
54
|
-
│ indexes files → .xindex│
|
|
55
|
-
└──────────┬──────────────┘
|
|
56
|
-
│ writes
|
|
57
|
-
▼
|
|
58
|
-
.xindex/ (vectra)
|
|
59
|
-
▲
|
|
60
|
-
│ reads
|
|
61
|
-
┌──────────┐ stdio ┌───────┴──────────────┐
|
|
62
|
-
│Claude Code├──────────┤ xindex-mcp server │
|
|
63
|
-
└──────────┘ │ │
|
|
64
|
-
│ tool: xindex_search │
|
|
65
|
-
│ query → extract │
|
|
66
|
-
│ → cleanUp → embed │
|
|
67
|
-
│ → vectra query │
|
|
68
|
-
│ → IIndexRecord[] │
|
|
69
|
-
└───────────────────────┘
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Steps
|
|
73
|
-
|
|
74
|
-
### 1. MCP Server Setup
|
|
75
|
-
- Install `@modelcontextprotocol/sdk` and `zod` dependencies
|
|
76
|
-
- Create `apps/mcp-server.ts` — McpServer + StdioServerTransport
|
|
77
|
-
- `BuildComponents()` at top level (once), use `searchContentIndex` in tool handler
|
|
78
|
-
|
|
79
|
-
### 2. Tool: xindex_search
|
|
80
|
-
- Input schema: Zod `z.object({query: z.string(), limit: z.number().default(10)})` with `.describe()` on each field
|
|
81
|
-
- Handler: call `searchContentIndex(query, limit)` → return `IIndexRecord[]` as text content
|
|
82
|
-
- Use `annotations: { readOnlyHint: true }` — search has no side effects
|
|
83
|
-
|
|
84
|
-
### 3. Integration
|
|
85
|
-
- Add `bin/xindex-mcp` entry point (`#!/usr/bin/env tsx` + import)
|
|
86
|
-
- Add `.mcp.json` to project root: `{"mcpServers": {"xindex": {"command": "npx", "args": ["tsx", "apps/mcp-server.ts"]}}}`
|
|
87
|
-
- Test: index codebase via CLI, restart Claude Code, verify tool appears, search it
|
|
88
|
-
- Debug with: `npx @modelcontextprotocol/inspector npx tsx apps/mcp-server.ts`
|
|
89
|
-
|
|
90
|
-
## Future (out of scope)
|
|
91
|
-
- File watcher daemon — observe fs events, keep index up to date automatically
|
|
92
|
-
- `xindex_index` tool — allow Claude to trigger indexing directly
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# Research: How to Build an MCP Server (2026)
|
|
2
|
-
|
|
3
|
-
## Findings
|
|
4
|
-
|
|
5
|
-
### 1. SDK & API
|
|
6
|
-
|
|
7
|
-
**Package:** `@modelcontextprotocol/sdk` (latest ^1.0.0)
|
|
8
|
-
**Peer dep:** `zod` ^3.22.0
|
|
9
|
-
|
|
10
|
-
**Modern API** (use this — old `server.tool()` and `setRequestHandler` are deprecated):
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
-
import { z } from "zod";
|
|
16
|
-
|
|
17
|
-
const server = new McpServer({ name: "xindex", version: "0.1.0" });
|
|
18
|
-
|
|
19
|
-
server.registerTool(
|
|
20
|
-
"xindex_search",
|
|
21
|
-
{
|
|
22
|
-
title: "Search codebase",
|
|
23
|
-
description: "Semantic search over indexed codebase files",
|
|
24
|
-
inputSchema: z.object({
|
|
25
|
-
query: z.string().describe("Natural language search query"),
|
|
26
|
-
limit: z.number().int().min(1).max(50).default(10).describe("Max results"),
|
|
27
|
-
}),
|
|
28
|
-
annotations: { readOnlyHint: true },
|
|
29
|
-
},
|
|
30
|
-
async ({ query, limit }) => {
|
|
31
|
-
// call searchContentIndex(query, limit)
|
|
32
|
-
return {
|
|
33
|
-
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const transport = new StdioServerTransport();
|
|
39
|
-
await server.connect(transport);
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 2. Tool naming & annotations
|
|
43
|
-
|
|
44
|
-
- **snake_case** with service prefix: `xindex_search`, `xindex_index`
|
|
45
|
-
- **annotations**: `readOnlyHint: true` for search (signals no side effects), `destructiveHint: false` for index
|
|
46
|
-
- **inputSchema**: Zod objects with `.describe()` on each field — descriptions are shown to Claude
|
|
47
|
-
|
|
48
|
-
### 3. Transport
|
|
49
|
-
|
|
50
|
-
**Stdio** is correct for local tools. Server reads JSON-RPC from stdin, writes to stdout. Use `console.error()` for logging (stdout is the protocol channel).
|
|
51
|
-
|
|
52
|
-
**Gotcha with tsx**: Works fine as shebang (`#!/usr/bin/env tsx`). No build step needed.
|
|
53
|
-
|
|
54
|
-
### 4. Claude Code Integration
|
|
55
|
-
|
|
56
|
-
**Three scopes:**
|
|
57
|
-
|
|
58
|
-
| Scope | File | Shared? |
|
|
59
|
-
|-------|------|---------|
|
|
60
|
-
| Local (default) | `~/.claude.json` under project path | No |
|
|
61
|
-
| Project | `.mcp.json` in project root | Yes (git) |
|
|
62
|
-
| User | `~/.claude.json` global | No |
|
|
63
|
-
|
|
64
|
-
**For this project — use `.mcp.json` (project scope)** so it's checked in and works for anyone who clones.
|
|
65
|
-
|
|
66
|
-
```json
|
|
67
|
-
{
|
|
68
|
-
"mcpServers": {
|
|
69
|
-
"xindex": {
|
|
70
|
-
"command": "npx",
|
|
71
|
-
"args": ["tsx", "apps/mcp-server.ts"],
|
|
72
|
-
"env": {}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Or add via CLI:
|
|
79
|
-
```bash
|
|
80
|
-
claude mcp add --transport stdio --scope project xindex -- npx tsx apps/mcp-server.ts
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### 5. Package requirements
|
|
84
|
-
|
|
85
|
-
- `"type": "module"` in package.json — already have this
|
|
86
|
-
- tsconfig: `module` and `moduleResolution` should be `Node16` or `NodeNext` (SDK uses ES module exports with subpath imports)
|
|
87
|
-
- Current tsconfig has `"module": "Node16"` — compatible
|
|
88
|
-
|
|
89
|
-
### 6. Testing
|
|
90
|
-
|
|
91
|
-
Use MCP Inspector for debugging:
|
|
92
|
-
```bash
|
|
93
|
-
npx @modelcontextprotocol/inspector npx tsx apps/mcp-server.ts
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Recommendation
|
|
97
|
-
|
|
98
|
-
Minimal implementation — single file `apps/mcp-server.ts`:
|
|
99
|
-
1. `BuildComponents()` at top level
|
|
100
|
-
2. `server.registerTool("xindex_search", ...)` calling `searchContentIndex`
|
|
101
|
-
3. `StdioServerTransport` + connect
|
|
102
|
-
4. `.mcp.json` in project root for Claude Code discovery
|
|
103
|
-
5. `bin/xindex-mcp` shebang entry point
|
|
104
|
-
|
|
105
|
-
No build step needed (tsx). No express/HTTP needed (stdio only). ~40 lines of code.
|
|
106
|
-
|
|
107
|
-
## Sources
|
|
108
|
-
|
|
109
|
-
- [Official TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
|
|
110
|
-
- [SDK server docs](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/server.md)
|
|
111
|
-
- [Claude Code MCP docs](https://code.claude.com/docs/en/mcp)
|
|
112
|
-
- [Anthropic MCP server reference](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/node_mcp_server.md)
|
|
113
|
-
- [npm package](https://www.npmjs.com/package/@modelcontextprotocol/sdk)
|