@tai-io/codesearch 2026.313.1614 → 2026.313.1649

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 ADDED
@@ -0,0 +1,115 @@
1
+ # @tai-io/codesearch
2
+
3
+ Semantic code search MCP server for [Claude Code](https://claude.ai/code). Index any codebase and search it by meaning, not just keywords.
4
+
5
+ ## How it works
6
+
7
+ 1. **Index** a codebase — files are parsed with tree-sitter (AST-aware chunking for 10 languages), embedded via OpenAI, and stored in a local SQLite database
8
+ 2. **Search** by natural language — queries are embedded and matched using hybrid search (dense vectors + full-text, RRF fusion)
9
+ 3. Results return in a compact table (~20 tokens/result) so Claude can efficiently decide what to read in full
10
+
11
+ All data is stored locally under `~/.codesearch/`. No external servers required.
12
+
13
+ ## Quick start
14
+
15
+ ### Prerequisites
16
+
17
+ - Node.js >= 20
18
+ - An OpenAI API key (for embeddings), or Ollama running locally
19
+
20
+ ### Install as a Claude Code MCP server
21
+
22
+ Add to your Claude Code config (`.claude.json` or via CLI):
23
+
24
+ ```bash
25
+ claude mcp add -s user -e OPENAI_API_KEY=sk-... codesearch -- npx @tai-io/codesearch
26
+ ```
27
+
28
+ Or add the MCP config directly:
29
+
30
+ ```json
31
+ {
32
+ "codesearch": {
33
+ "command": "npx",
34
+ "args": ["@tai-io/codesearch"],
35
+ "env": {
36
+ "OPENAI_API_KEY": "sk-..."
37
+ }
38
+ }
39
+ }
40
+ ```
41
+
42
+ ### Use it
43
+
44
+ Once configured, Claude Code has 8 new tools:
45
+
46
+ | Tool | Example | What it does |
47
+ |------|---------|--------------|
48
+ | `index` | `index(path="/my/project")` | Index a codebase (~30s one-time) |
49
+ | `search` | `search(query="how does auth work")` | Semantic search |
50
+ | `list` | `list()` | See indexed codebases |
51
+ | `browse` | `browse(path="/my/project")` | Structural map of classes/functions |
52
+ | `clear` | `clear(path="/my/project")` | Remove index |
53
+ | `cleanup` | `cleanup(path="/my/project")` | Remove vectors for deleted files |
54
+ | `ingest` | `ingest(content="...", library="react", ...)` | Cache external docs |
55
+ | `lookup` | `lookup(query="react hooks")` | Search cached docs |
56
+
57
+ ## Tools
58
+
59
+ | Tool | Description |
60
+ |------|-------------|
61
+ | `index` | Index a codebase for semantic search. Incremental — only re-embeds changed files. |
62
+ | `search` | Search indexed code by natural language. Returns compact results (~20 tokens each). |
63
+ | `list` | List all indexed codebases with status and file/chunk counts. |
64
+ | `browse` | Structural map — classes, functions, methods with signatures, grouped by file. |
65
+ | `clear` | Remove the search index for a codebase. |
66
+ | `cleanup` | Remove orphaned vectors for deleted files. No embedding cost. |
67
+ | `ingest` | Cache external documentation for cheap semantic search later. |
68
+ | `lookup` | Search cached documentation (~20 tokens/result vs ~5K for re-fetching). |
69
+
70
+ ## Supported languages
71
+
72
+ AST-aware chunking (via tree-sitter): TypeScript, JavaScript, Python, Go, Java, Rust, C++, C, C#, TSX.
73
+
74
+ Line-based fallback for all other text files.
75
+
76
+ ## Configuration
77
+
78
+ All configuration is via environment variables:
79
+
80
+ | Variable | Default | Description |
81
+ |----------|---------|-------------|
82
+ | `OPENAI_API_KEY` | *required* | OpenAI API key for embeddings |
83
+ | `EMBEDDING_PROVIDER` | `openai` | `openai`, `ollama`, or `local` |
84
+ | `EMBEDDING_MODEL` | `text-embedding-3-small` | Embedding model name |
85
+ | `OPENAI_BASE_URL` | — | Override base URL (for proxies or compatible APIs) |
86
+ | `OLLAMA_BASE_URL` | `http://localhost:11434/v1` | Ollama server URL |
87
+ | `EMBEDDING_BATCH_SIZE` | `100` | Vectors per API call (1–2048) |
88
+ | `INDEXING_CONCURRENCY` | `8` | Parallel file processing (1–32) |
89
+ | `CODESEARCH_DATA_DIR` | `~/.codesearch` | Data directory for indexes and state |
90
+ | `CUSTOM_EXTENSIONS` | `[]` | Additional file extensions as JSON array |
91
+ | `CUSTOM_IGNORE_PATTERNS` | `[]` | Additional glob ignore patterns as JSON array |
92
+
93
+ ### Using Ollama (free, local)
94
+
95
+ ```bash
96
+ # Install and start Ollama with an embedding model
97
+ ollama pull nomic-embed-text
98
+
99
+ # Configure
100
+ export EMBEDDING_PROVIDER=ollama
101
+ ```
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ git clone https://github.com/tai-io/codesearch.git
107
+ cd codesearch
108
+ npm install
109
+ npm run build
110
+ npm test
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT
@@ -1,3 +1,3 @@
1
- export declare const BUILD_VERSION = "2026.313.1614";
2
- export declare const BUILD_TIMESTAMP = "2026-03-13T16:14:48.402Z";
1
+ export declare const BUILD_VERSION = "2026.313.1649";
2
+ export declare const BUILD_TIMESTAMP = "2026-03-13T16:49:55.333Z";
3
3
  //# sourceMappingURL=build-info.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Auto-generated by scripts/generate-build-info.ts — do not edit
2
- export const BUILD_VERSION = '2026.313.1614';
3
- export const BUILD_TIMESTAMP = '2026-03-13T16:14:48.402Z';
2
+ export const BUILD_VERSION = '2026.313.1649';
3
+ export const BUILD_TIMESTAMP = '2026-03-13T16:49:55.333Z';
4
4
  //# sourceMappingURL=build-info.js.map
package/dist/config.d.ts CHANGED
@@ -7,7 +7,7 @@ declare const configSchema: z.ZodEffects<z.ZodObject<{
7
7
  embeddingModel: z.ZodOptional<z.ZodString>;
8
8
  embeddingBatchSize: z.ZodDefault<z.ZodNumber>;
9
9
  indexingConcurrency: z.ZodDefault<z.ZodNumber>;
10
- eideticDataDir: z.ZodDefault<z.ZodString>;
10
+ dataDir: z.ZodDefault<z.ZodString>;
11
11
  customExtensions: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], unknown>;
12
12
  customIgnorePatterns: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], unknown>;
13
13
  }, "strip", z.ZodTypeAny, {
@@ -16,7 +16,7 @@ declare const configSchema: z.ZodEffects<z.ZodObject<{
16
16
  ollamaBaseUrl: string;
17
17
  embeddingBatchSize: number;
18
18
  indexingConcurrency: number;
19
- eideticDataDir: string;
19
+ dataDir: string;
20
20
  customExtensions: string[];
21
21
  customIgnorePatterns: string[];
22
22
  openaiBaseUrl?: string | undefined;
@@ -29,7 +29,7 @@ declare const configSchema: z.ZodEffects<z.ZodObject<{
29
29
  embeddingModel?: string | undefined;
30
30
  embeddingBatchSize?: number | undefined;
31
31
  indexingConcurrency?: number | undefined;
32
- eideticDataDir?: string | undefined;
32
+ dataDir?: string | undefined;
33
33
  customExtensions?: unknown;
34
34
  customIgnorePatterns?: unknown;
35
35
  }>, {
@@ -39,7 +39,7 @@ declare const configSchema: z.ZodEffects<z.ZodObject<{
39
39
  ollamaBaseUrl: string;
40
40
  embeddingBatchSize: number;
41
41
  indexingConcurrency: number;
42
- eideticDataDir: string;
42
+ dataDir: string;
43
43
  customExtensions: string[];
44
44
  customIgnorePatterns: string[];
45
45
  openaiBaseUrl?: string | undefined;
@@ -51,7 +51,7 @@ declare const configSchema: z.ZodEffects<z.ZodObject<{
51
51
  embeddingModel?: string | undefined;
52
52
  embeddingBatchSize?: number | undefined;
53
53
  indexingConcurrency?: number | undefined;
54
- eideticDataDir?: string | undefined;
54
+ dataDir?: string | undefined;
55
55
  customExtensions?: unknown;
56
56
  customIgnorePatterns?: unknown;
57
57
  }>;
package/dist/config.js CHANGED
@@ -11,7 +11,7 @@ const configSchema = z
11
11
  embeddingModel: z.string().optional(),
12
12
  embeddingBatchSize: z.coerce.number().int().min(1).max(2048).default(100),
13
13
  indexingConcurrency: z.coerce.number().int().min(1).max(32).default(8),
14
- eideticDataDir: z.string().default(path.join(os.homedir(), '.eidetic')),
14
+ dataDir: z.string().default(path.join(os.homedir(), '.codesearch')),
15
15
  customExtensions: z.preprocess((val) => (typeof val === 'string' ? JSON.parse(val) : val), z.array(z.string()).default([])),
16
16
  customIgnorePatterns: z.preprocess((val) => (typeof val === 'string' ? JSON.parse(val) : val), z.array(z.string()).default([])),
17
17
  })
@@ -30,7 +30,7 @@ export function loadConfig() {
30
30
  embeddingModel: process.env.EMBEDDING_MODEL?.trim() ?? undefined,
31
31
  embeddingBatchSize: process.env.EMBEDDING_BATCH_SIZE,
32
32
  indexingConcurrency: process.env.INDEXING_CONCURRENCY,
33
- eideticDataDir: process.env.EIDETIC_DATA_DIR,
33
+ dataDir: process.env.CODESEARCH_DATA_DIR,
34
34
  customExtensions: process.env.CUSTOM_EXTENSIONS,
35
35
  customIgnorePatterns: process.env.CUSTOM_IGNORE_PATTERNS,
36
36
  };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * CLI subcommand router for hook events.
3
3
  *
4
- * Plugin bash hooks call `npx eidetic-codesearch hook <event>` which routes here.
4
+ * Plugin bash hooks call `npx codesearch hook <event>` which routes here.
5
5
  */
6
6
  export declare function runHook(event: string | undefined): Promise<void>;
7
7
  //# sourceMappingURL=cli-router.d.ts.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * CLI subcommand router for hook events.
3
3
  *
4
- * Plugin bash hooks call `npx eidetic-codesearch hook <event>` which routes here.
4
+ * Plugin bash hooks call `npx codesearch hook <event>` which routes here.
5
5
  */
6
6
  export async function runHook(event) {
7
7
  switch (event) {
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import { ToolHandlers } from './tools.js';
20
20
  import { TOOL_DEFINITIONS } from './tool-schemas.js';
21
21
  import { getSetupErrorMessage } from './setup-message.js';
22
22
  import { BUILD_VERSION, BUILD_TIMESTAMP } from './build-info.js';
23
- const SERVER_INSTRUCTIONS = `# Eidetic Codesearch — Workflow
23
+ const SERVER_INSTRUCTIONS = `# Codesearch — Workflow
24
24
 
25
25
  **Before searching:** Ensure the codebase is indexed.
26
26
  - \`list\` → see what's already indexed
@@ -45,7 +45,7 @@ const SERVER_INSTRUCTIONS = `# Eidetic Codesearch — Workflow
45
45
  - Next time you need the same docs: \`lookup(query="...", library="<name>")\`
46
46
  - Stale docs (past TTL) still return results but are flagged \`[STALE]\``;
47
47
  async function main() {
48
- // CLI subcommand routing — hooks call `npx eidetic-codesearch hook <event>`
48
+ // CLI subcommand routing — hooks call `npx codesearch hook <event>`
49
49
  if (process.argv[2] === 'hook') {
50
50
  const { runHook } = await import('./hooks/cli-router.js');
51
51
  await runHook(process.argv[3]);
@@ -121,7 +121,7 @@ async function main() {
121
121
  });
122
122
  const transport = new StdioServerTransport();
123
123
  await server.connect(transport);
124
- console.log(`Eidetic Codesearch MCP server v${BUILD_VERSION} (built ${BUILD_TIMESTAMP}) started on stdio.`);
124
+ console.log(`Codesearch MCP server v${BUILD_VERSION} (built ${BUILD_TIMESTAMP}) started on stdio.`);
125
125
  }
126
126
  process.on('SIGINT', () => {
127
127
  console.error('Received SIGINT, shutting down...');
package/dist/paths.js CHANGED
@@ -14,7 +14,7 @@ export function normalizePath(inputPath) {
14
14
  return resolved;
15
15
  }
16
16
  export function getDataDir() {
17
- return normalizePath(getConfig().eideticDataDir);
17
+ return normalizePath(getConfig().dataDir);
18
18
  }
19
19
  export function getSnapshotDir() {
20
20
  return `${getDataDir()}/snapshots`;
@@ -32,7 +32,7 @@ export function pathToCollectionName(absolutePath) {
32
32
  .replace(/[^a-z0-9]/g, '_')
33
33
  .replace(/_+/g, '_')
34
34
  .replace(/^_|_$/g, '');
35
- return `eidetic_${safe}`;
35
+ return `cs_${safe}`;
36
36
  }
37
37
  export function getCodesearchDbPath() {
38
38
  return `${getDataDir()}/codesearch.db`;
@@ -73,7 +73,7 @@ export async function cleanupOrphanedSnapshots(vectordb) {
73
73
  const collections = listSnapshotCollections();
74
74
  if (collections.length === 0)
75
75
  return 0;
76
- const probeResult = await vectordb.hasCollection('__eidetic_connectivity_probe__');
76
+ const probeResult = await vectordb.hasCollection('__codesearch_connectivity_probe__');
77
77
  if (probeResult) {
78
78
  console.warn('Orphan cleanup skipped: connectivity probe returned unexpected result.');
79
79
  return 0;
@@ -246,14 +246,16 @@ export class SqliteVectorDB {
246
246
  symbol_signature, parent_symbol, indexed_at, file_modified_at)
247
247
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
248
248
  `);
249
+ const deleteVec = this.db.prepare(`DELETE FROM "${safe}_vec" WHERE id = ?`);
249
250
  const insertVec = this.db.prepare(`
250
- INSERT OR REPLACE INTO "${safe}_vec" (id, vector) VALUES (?, ?)
251
+ INSERT INTO "${safe}_vec" (id, vector) VALUES (?, ?)
251
252
  `);
252
253
  for (let i = 0; i < documents.length; i += INSERT_BATCH_SIZE) {
253
254
  const batch = documents.slice(i, i + INSERT_BATCH_SIZE);
254
255
  this.db.transaction(() => {
255
256
  for (const doc of batch) {
256
257
  insertChunk.run(doc.id, safe, doc.content, doc.relativePath, doc.startLine, doc.endLine, doc.fileExtension, doc.language, doc.fileCategory ?? null, doc.symbolName ?? null, doc.symbolKind ?? null, doc.symbolSignature ?? null, doc.parentSymbol ?? null, doc.fileModifiedAt ?? null);
258
+ deleteVec.run(doc.id);
257
259
  insertVec.run(doc.id, vecToBuffer(doc.vector));
258
260
  }
259
261
  })();
@@ -308,7 +310,7 @@ export class SqliteVectorDB {
308
310
  WHERE "${safe}_fts" MATCH ?
309
311
  ${extClause}
310
312
  LIMIT ?`)
311
- .all(safe, ftsQuery, fetchLimit, ...extParams);
313
+ .all(safe, ftsQuery, ...extParams, fetchLimit);
312
314
  const pointsForRank = ftsRows.map((r) => ({
313
315
  id: r.id,
314
316
  payload: { content: r.content },
@@ -437,8 +439,9 @@ export class SqliteVectorDB {
437
439
  symbol_signature, parent_symbol, indexed_at, file_modified_at)
438
440
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)`)
439
441
  .run(id, safe, String(payload.content ?? ''), String(payload.relativePath ?? ''), Number(payload.startLine ?? 0), Number(payload.endLine ?? 0), String(payload.fileExtension ?? ''), String(payload.language ?? ''), String(payload.fileCategory ?? ''), String(payload.symbolName ?? ''), String(payload.symbolKind ?? ''), String(payload.symbolSignature ?? ''), String(payload.parentSymbol ?? ''), payload.fileModifiedAt != null ? String(payload.fileModifiedAt) : null);
442
+ this.db.prepare(`DELETE FROM "${safe}_vec" WHERE id = ?`).run(id);
440
443
  this.db
441
- .prepare(`INSERT OR REPLACE INTO "${safe}_vec" (id, vector) VALUES (?, ?)`)
444
+ .prepare(`INSERT INTO "${safe}_vec" (id, vector) VALUES (?, ?)`)
442
445
  .run(id, vecToBuffer(vector));
443
446
  })();
444
447
  }
package/messages.yaml CHANGED
@@ -1,34 +1,36 @@
1
- # Eidetic user-facing messages — edit here, everything else reads from this file.
1
+ # Codesearch user-facing messages — edit here, everything else reads from this file.
2
2
 
3
3
  welcome:
4
4
  ascii_art: |
5
5
  ┌─────────────────────────────────────────────┐
6
- ╔═╗╦╔╦╗╔═╗╔╦╗╦╔═╗
7
- ║╣ ║ ║║║╣ ║║ semantic code search
8
- ╚═╝╩═╩╝╚═╝ ╩╚═╝ for Claude Code
6
+ ╔═╗╔═╗╔╦╗╔═╗╔═╗╔═╗╔═╗╦═╗╔═╗╦ ╦
7
+ ║ ║ ║ ║║║╣ ╚═╗║╣ ╠═╣╠╦╝║ ╠═╣
8
+ ╚═╝╚═╝═╩╝╚═╝╚═╝╚═╝╩ ╩╩╚═╚═╝╩
9
+ │ semantic code search │
10
+ │ for Claude Code │
9
11
  └─────────────────────────────────────────────┘
10
12
  first_run: |
11
- Eidetic is ready! Quick start:
13
+ Codesearch is ready! Quick start:
12
14
  1. `/index` — index this codebase
13
15
  2. `search("how does X work")` — search by meaning
14
16
  3. That's it. Everything else is optional.
15
17
 
16
18
  setup:
17
19
  missing:
18
- header: "Eidetic setup required: No API key configured."
20
+ header: "Codesearch setup required: No API key configured."
19
21
  diagnosis: >
20
22
  `OPENAI_API_KEY` is not set and no alternative provider is configured.
21
23
  step1: "**Get an API key**: https://platform.openai.com/api-keys"
22
24
 
23
25
  invalid:
24
- header: "Eidetic setup failed: {error}"
26
+ header: "Codesearch setup failed: {error}"
25
27
  diagnosis: >
26
28
  Key/provider is configured but initialization failed.
27
29
  The key may be invalid, expired, or the provider may be unreachable.
28
30
  step1: "**Verify your key** is valid and has embedding permissions"
29
31
 
30
32
  unknown:
31
- header: "Eidetic setup failed: {error}"
33
+ header: "Codesearch setup failed: {error}"
32
34
  diagnosis: ""
33
35
  step1: "**Get an API key**: https://platform.openai.com/api-keys"
34
36
 
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@tai-io/codesearch",
3
- "version": "2026.313.1614",
3
+ "version": "2026.313.1649",
4
4
  "description": "Semantic code search MCP server for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "eidetic-codesearch": "dist/index.js"
8
+ "codesearch": "dist/index.js"
9
9
  },
10
10
  "files": [
11
11
  "dist/**/*.js",
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "repository": {
75
75
  "type": "git",
76
- "url": "https://github.com/eidetics/claude-codesearch.git"
76
+ "url": "https://github.com/tai-io/codesearch.git"
77
77
  },
78
78
  "license": "MIT"
79
79
  }