@tai-io/codesearch 2026.313.1614

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.
Files changed (78) hide show
  1. package/dist/build-info.d.ts +3 -0
  2. package/dist/build-info.js +4 -0
  3. package/dist/config.d.ts +62 -0
  4. package/dist/config.js +52 -0
  5. package/dist/core/cleanup.d.ts +8 -0
  6. package/dist/core/cleanup.js +41 -0
  7. package/dist/core/doc-indexer.d.ts +13 -0
  8. package/dist/core/doc-indexer.js +76 -0
  9. package/dist/core/doc-searcher.d.ts +13 -0
  10. package/dist/core/doc-searcher.js +65 -0
  11. package/dist/core/file-category.d.ts +7 -0
  12. package/dist/core/file-category.js +75 -0
  13. package/dist/core/indexer.d.ts +18 -0
  14. package/dist/core/indexer.js +177 -0
  15. package/dist/core/preview.d.ts +13 -0
  16. package/dist/core/preview.js +58 -0
  17. package/dist/core/repo-map.d.ts +33 -0
  18. package/dist/core/repo-map.js +144 -0
  19. package/dist/core/searcher.d.ts +12 -0
  20. package/dist/core/searcher.js +97 -0
  21. package/dist/core/sync.d.ts +15 -0
  22. package/dist/core/sync.js +212 -0
  23. package/dist/core/targeted-indexer.d.ts +19 -0
  24. package/dist/core/targeted-indexer.js +127 -0
  25. package/dist/embedding/factory.d.ts +4 -0
  26. package/dist/embedding/factory.js +24 -0
  27. package/dist/embedding/openai.d.ts +33 -0
  28. package/dist/embedding/openai.js +234 -0
  29. package/dist/embedding/truncate.d.ts +6 -0
  30. package/dist/embedding/truncate.js +14 -0
  31. package/dist/embedding/types.d.ts +18 -0
  32. package/dist/embedding/types.js +2 -0
  33. package/dist/errors.d.ts +17 -0
  34. package/dist/errors.js +21 -0
  35. package/dist/format.d.ts +18 -0
  36. package/dist/format.js +151 -0
  37. package/dist/hooks/cli-router.d.ts +7 -0
  38. package/dist/hooks/cli-router.js +47 -0
  39. package/dist/hooks/hook-output.d.ts +56 -0
  40. package/dist/hooks/hook-output.js +21 -0
  41. package/dist/hooks/post-tool-use.d.ts +13 -0
  42. package/dist/hooks/post-tool-use.js +123 -0
  43. package/dist/hooks/stop-hook.d.ts +11 -0
  44. package/dist/hooks/stop-hook.js +137 -0
  45. package/dist/hooks/targeted-runner.d.ts +11 -0
  46. package/dist/hooks/targeted-runner.js +58 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.js +138 -0
  49. package/dist/paths.d.ts +11 -0
  50. package/dist/paths.js +54 -0
  51. package/dist/setup-message.d.ts +4 -0
  52. package/dist/setup-message.js +48 -0
  53. package/dist/splitter/ast.d.ts +13 -0
  54. package/dist/splitter/ast.js +231 -0
  55. package/dist/splitter/line.d.ts +10 -0
  56. package/dist/splitter/line.js +103 -0
  57. package/dist/splitter/symbol-extract.d.ts +16 -0
  58. package/dist/splitter/symbol-extract.js +61 -0
  59. package/dist/splitter/types.d.ts +16 -0
  60. package/dist/splitter/types.js +2 -0
  61. package/dist/state/doc-metadata.d.ts +18 -0
  62. package/dist/state/doc-metadata.js +59 -0
  63. package/dist/state/registry.d.ts +7 -0
  64. package/dist/state/registry.js +46 -0
  65. package/dist/state/snapshot.d.ts +26 -0
  66. package/dist/state/snapshot.js +100 -0
  67. package/dist/tool-schemas.d.ts +215 -0
  68. package/dist/tool-schemas.js +269 -0
  69. package/dist/tools.d.ts +58 -0
  70. package/dist/tools.js +245 -0
  71. package/dist/vectordb/rrf.d.ts +32 -0
  72. package/dist/vectordb/rrf.js +88 -0
  73. package/dist/vectordb/sqlite.d.ts +34 -0
  74. package/dist/vectordb/sqlite.js +624 -0
  75. package/dist/vectordb/types.d.ts +63 -0
  76. package/dist/vectordb/types.js +2 -0
  77. package/messages.yaml +69 -0
  78. package/package.json +79 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CLI subcommand router for hook events.
3
+ *
4
+ * Plugin bash hooks call `npx eidetic-codesearch hook <event>` which routes here.
5
+ */
6
+ export declare function runHook(event: string | undefined): Promise<void>;
7
+ //# sourceMappingURL=cli-router.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * CLI subcommand router for hook events.
3
+ *
4
+ * Plugin bash hooks call `npx eidetic-codesearch hook <event>` which routes here.
5
+ */
6
+ export async function runHook(event) {
7
+ switch (event) {
8
+ case 'stop': {
9
+ const { run } = await import('./stop-hook.js');
10
+ await run();
11
+ break;
12
+ }
13
+ case 'post-tool-use': {
14
+ const { run } = await import('./post-tool-use.js');
15
+ await run();
16
+ break;
17
+ }
18
+ case 'setup-message': {
19
+ const mode = process.argv[4] ?? 'welcome';
20
+ const detail = process.argv[5];
21
+ const { getSetupErrorMessage, getWelcomeMessage } = await import('../setup-message.js');
22
+ if (mode === 'welcome') {
23
+ const output = {
24
+ hookSpecificOutput: {
25
+ hookEventName: 'SessionStart',
26
+ additionalContext: getWelcomeMessage(),
27
+ },
28
+ };
29
+ process.stdout.write(JSON.stringify(output));
30
+ }
31
+ else {
32
+ const output = {
33
+ hookSpecificOutput: {
34
+ hookEventName: 'SessionStart',
35
+ additionalContext: getSetupErrorMessage(detail ?? 'OPENAI_API_KEY is not set.', mode),
36
+ },
37
+ };
38
+ process.stdout.write(JSON.stringify(output));
39
+ }
40
+ break;
41
+ }
42
+ default:
43
+ process.stderr.write(`Unknown hook event: ${event ?? '(none)'}\n`);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ //# sourceMappingURL=cli-router.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Strongly typed Claude Code hook output schemas.
3
+ *
4
+ * Every hook must output JSON to stdout matching these types.
5
+ * See: https://docs.anthropic.com/en/docs/claude-code/hooks
6
+ *
7
+ * Base fields (available for ALL hooks):
8
+ * continue, suppressOutput, stopReason, decision, reason, systemMessage
9
+ *
10
+ * hookSpecificOutput varies by event:
11
+ * PreToolUse → permissionDecision, permissionDecisionReason, updatedInput
12
+ * UserPromptSubmit → additionalContext (required)
13
+ * PostToolUse → additionalContext (optional)
14
+ * SessionStart → additionalContext (optional)
15
+ * PreCompact / SessionEnd / Stop → no hookSpecificOutput defined
16
+ */
17
+ interface HookOutputBase {
18
+ continue?: boolean;
19
+ suppressOutput?: boolean;
20
+ stopReason?: string;
21
+ decision?: 'approve' | 'block';
22
+ reason?: string;
23
+ systemMessage?: string;
24
+ }
25
+ export interface PreToolUseOutput extends HookOutputBase {
26
+ hookSpecificOutput?: {
27
+ hookEventName: 'PreToolUse';
28
+ permissionDecision?: 'allow' | 'deny' | 'ask';
29
+ permissionDecisionReason?: string;
30
+ updatedInput?: Record<string, unknown>;
31
+ };
32
+ }
33
+ export interface UserPromptSubmitOutput extends HookOutputBase {
34
+ hookSpecificOutput: {
35
+ hookEventName: 'UserPromptSubmit';
36
+ additionalContext: string;
37
+ };
38
+ }
39
+ export interface PostToolUseOutput extends HookOutputBase {
40
+ hookSpecificOutput?: {
41
+ hookEventName: 'PostToolUse';
42
+ additionalContext?: string;
43
+ };
44
+ }
45
+ export interface SessionStartOutput extends HookOutputBase {
46
+ hookSpecificOutput?: {
47
+ hookEventName: 'SessionStart';
48
+ additionalContext?: string;
49
+ };
50
+ }
51
+ /** PreCompact, SessionEnd, Stop — no hookSpecificOutput defined. */
52
+ export type SimpleHookOutput = HookOutputBase;
53
+ export type HookOutput = PreToolUseOutput | UserPromptSubmitOutput | PostToolUseOutput | SessionStartOutput | SimpleHookOutput;
54
+ export declare function writeHookOutput(output: HookOutput): void;
55
+ export {};
56
+ //# sourceMappingURL=hook-output.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Strongly typed Claude Code hook output schemas.
3
+ *
4
+ * Every hook must output JSON to stdout matching these types.
5
+ * See: https://docs.anthropic.com/en/docs/claude-code/hooks
6
+ *
7
+ * Base fields (available for ALL hooks):
8
+ * continue, suppressOutput, stopReason, decision, reason, systemMessage
9
+ *
10
+ * hookSpecificOutput varies by event:
11
+ * PreToolUse → permissionDecision, permissionDecisionReason, updatedInput
12
+ * UserPromptSubmit → additionalContext (required)
13
+ * PostToolUse → additionalContext (optional)
14
+ * SessionStart → additionalContext (optional)
15
+ * PreCompact / SessionEnd / Stop → no hookSpecificOutput defined
16
+ */
17
+ // ── Helper to write output to stdout ──────────────────────────────────
18
+ export function writeHookOutput(output) {
19
+ process.stdout.write(JSON.stringify(output));
20
+ }
21
+ //# sourceMappingURL=hook-output.js.map
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PostToolUse hook entry point.
4
+ *
5
+ * Receives hook data via stdin when Write or Edit tools are used.
6
+ * Maintains a shadow git index per session to track which files were
7
+ * modified, without touching HEAD or the working index.
8
+ *
9
+ * Shadow index: <git-dir>/claude/indexes/<session-id>/index
10
+ * Base commit: <git-dir>/claude/indexes/<session-id>/base_commit
11
+ */
12
+ export declare function run(): Promise<void>;
13
+ //# sourceMappingURL=post-tool-use.d.ts.map
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PostToolUse hook entry point.
4
+ *
5
+ * Receives hook data via stdin when Write or Edit tools are used.
6
+ * Maintains a shadow git index per session to track which files were
7
+ * modified, without touching HEAD or the working index.
8
+ *
9
+ * Shadow index: <git-dir>/claude/indexes/<session-id>/index
10
+ * Base commit: <git-dir>/claude/indexes/<session-id>/base_commit
11
+ */
12
+ import { z } from 'zod';
13
+ import path from 'node:path';
14
+ import fs from 'node:fs';
15
+ import { execFileSync } from 'node:child_process';
16
+ import { fileURLToPath } from 'node:url';
17
+ const PostToolUseInputSchema = z.object({
18
+ session_id: z.string(),
19
+ cwd: z.string(),
20
+ hook_event_name: z.literal('PostToolUse'),
21
+ tool_name: z.string(),
22
+ tool_input: z
23
+ .object({
24
+ file_path: z.string().optional(),
25
+ })
26
+ .passthrough(),
27
+ });
28
+ async function readStdin() {
29
+ const chunks = [];
30
+ for await (const chunk of process.stdin) {
31
+ chunks.push(chunk);
32
+ }
33
+ return Buffer.concat(chunks).toString('utf-8');
34
+ }
35
+ function outputSuccess() {
36
+ const output = {
37
+ hookSpecificOutput: { hookEventName: 'PostToolUse' },
38
+ };
39
+ process.stdout.write(JSON.stringify(output));
40
+ }
41
+ function outputError(message) {
42
+ process.stderr.write(`[codesearch:post-tool-use] ${message}\n`);
43
+ const output = {
44
+ hookSpecificOutput: { hookEventName: 'PostToolUse' },
45
+ };
46
+ process.stdout.write(JSON.stringify(output));
47
+ }
48
+ export async function run() {
49
+ try {
50
+ const input = await readStdin();
51
+ const parseResult = PostToolUseInputSchema.safeParse(JSON.parse(input));
52
+ if (!parseResult.success) {
53
+ outputError(`Invalid hook input: ${parseResult.error.message}`);
54
+ return;
55
+ }
56
+ const { session_id, cwd, tool_name, tool_input } = parseResult.data;
57
+ // Safety check beyond matcher — only process Write and Edit
58
+ if (tool_name !== 'Write' && tool_name !== 'Edit') {
59
+ outputSuccess();
60
+ return;
61
+ }
62
+ const filePath = tool_input.file_path;
63
+ if (!filePath) {
64
+ outputSuccess();
65
+ return;
66
+ }
67
+ // Resolve git dir
68
+ let gitDir;
69
+ try {
70
+ gitDir = execFileSync('git', ['-C', cwd, 'rev-parse', '--git-dir'], {
71
+ encoding: 'utf-8',
72
+ timeout: 5000,
73
+ }).trim();
74
+ }
75
+ catch {
76
+ // Not a git repo — silently skip
77
+ outputSuccess();
78
+ return;
79
+ }
80
+ if (!path.isAbsolute(gitDir)) {
81
+ gitDir = path.resolve(cwd, gitDir);
82
+ }
83
+ const shadowDir = path.join(gitDir, 'claude', 'indexes', session_id);
84
+ const shadowIndex = path.join(shadowDir, 'index');
85
+ const baseCommitFile = path.join(shadowDir, 'base_commit');
86
+ // First call for this session: seed shadow index from HEAD
87
+ if (!fs.existsSync(shadowIndex)) {
88
+ fs.mkdirSync(shadowDir, { recursive: true });
89
+ let headSha;
90
+ try {
91
+ headSha = execFileSync('git', ['-C', cwd, 'rev-parse', 'HEAD'], {
92
+ encoding: 'utf-8',
93
+ timeout: 5000,
94
+ }).trim();
95
+ }
96
+ catch {
97
+ // Empty repo / no commits — cannot seed from HEAD
98
+ outputSuccess();
99
+ return;
100
+ }
101
+ // Seed shadow index from HEAD tree
102
+ execFileSync('git', ['-C', cwd, 'read-tree', `--index-output=${shadowIndex}`, 'HEAD'], {
103
+ timeout: 5000,
104
+ });
105
+ fs.writeFileSync(baseCommitFile, headSha, 'utf-8');
106
+ }
107
+ // Stage the file into the shadow index
108
+ const absoluteFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
109
+ execFileSync('git', ['-C', cwd, 'add', absoluteFilePath], {
110
+ env: { ...process.env, GIT_INDEX_FILE: shadowIndex },
111
+ timeout: 5000,
112
+ });
113
+ outputSuccess();
114
+ }
115
+ catch (err) {
116
+ outputError(err instanceof Error ? err.message : String(err));
117
+ }
118
+ }
119
+ // CLI router calls run() directly; self-execute when run as standalone script
120
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
121
+ void run();
122
+ }
123
+ //# sourceMappingURL=post-tool-use.js.map
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Stop hook entry point.
4
+ *
5
+ * Receives hook data via stdin when a Claude session ends.
6
+ * Commits the session's shadow git index to refs/heads/claude/<session-id>,
7
+ * diffs against base commit to find modified files, then spawns a
8
+ * detached background targeted re-indexer.
9
+ */
10
+ export declare function run(): Promise<void>;
11
+ //# sourceMappingURL=stop-hook.d.ts.map
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Stop hook entry point.
4
+ *
5
+ * Receives hook data via stdin when a Claude session ends.
6
+ * Commits the session's shadow git index to refs/heads/claude/<session-id>,
7
+ * diffs against base commit to find modified files, then spawns a
8
+ * detached background targeted re-indexer.
9
+ */
10
+ import { z } from 'zod';
11
+ import path from 'node:path';
12
+ import fs from 'node:fs';
13
+ import os from 'node:os';
14
+ import { execFileSync, spawn } from 'node:child_process';
15
+ import { fileURLToPath } from 'node:url';
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+ const StopInputSchema = z.object({
19
+ session_id: z.string(),
20
+ cwd: z.string(),
21
+ hook_event_name: z.literal('Stop'),
22
+ stop_hook_active: z.boolean().optional(),
23
+ });
24
+ async function readStdin() {
25
+ const chunks = [];
26
+ for await (const chunk of process.stdin) {
27
+ chunks.push(chunk);
28
+ }
29
+ return Buffer.concat(chunks).toString('utf-8');
30
+ }
31
+ function outputSuccess() {
32
+ const output = {};
33
+ process.stdout.write(JSON.stringify(output));
34
+ }
35
+ function outputError(message) {
36
+ process.stderr.write(`[codesearch:stop-hook] ${message}\n`);
37
+ const output = {};
38
+ process.stdout.write(JSON.stringify(output));
39
+ }
40
+ export async function run() {
41
+ try {
42
+ const input = await readStdin();
43
+ const parseResult = StopInputSchema.safeParse(JSON.parse(input));
44
+ if (!parseResult.success) {
45
+ outputError(`Invalid hook input: ${parseResult.error.message}`);
46
+ return;
47
+ }
48
+ const { session_id, cwd } = parseResult.data;
49
+ // Resolve git dir
50
+ let gitDir;
51
+ try {
52
+ gitDir = execFileSync('git', ['-C', cwd, 'rev-parse', '--git-dir'], {
53
+ encoding: 'utf-8',
54
+ timeout: 5000,
55
+ }).trim();
56
+ }
57
+ catch {
58
+ // Not a git repo — nothing to do
59
+ outputSuccess();
60
+ return;
61
+ }
62
+ if (!path.isAbsolute(gitDir)) {
63
+ gitDir = path.resolve(cwd, gitDir);
64
+ }
65
+ const shadowDir = path.join(gitDir, 'claude', 'indexes', session_id);
66
+ const shadowIndex = path.join(shadowDir, 'index');
67
+ const baseCommitFile = path.join(shadowDir, 'base_commit');
68
+ // No shadow index means no edits happened this session
69
+ if (!fs.existsSync(shadowIndex)) {
70
+ outputSuccess();
71
+ return;
72
+ }
73
+ if (!fs.existsSync(baseCommitFile)) {
74
+ // Missing base commit file — clean up and bail
75
+ try {
76
+ fs.rmSync(shadowDir, { recursive: true, force: true });
77
+ }
78
+ catch {
79
+ // Cleanup is best-effort
80
+ }
81
+ outputSuccess();
82
+ return;
83
+ }
84
+ const baseCommit = fs.readFileSync(baseCommitFile, 'utf-8').trim();
85
+ // Write tree from shadow index
86
+ const treeSha = execFileSync('git', ['-C', cwd, 'write-tree'], {
87
+ env: { ...process.env, GIT_INDEX_FILE: shadowIndex },
88
+ encoding: 'utf-8',
89
+ timeout: 10000,
90
+ }).trim();
91
+ // Create a commit object pointing to that tree
92
+ const commitSha = execFileSync('git', ['-C', cwd, 'commit-tree', treeSha, '-p', baseCommit, '-m', `codesearch: session ${session_id}`], { encoding: 'utf-8', timeout: 10000 }).trim();
93
+ // Store under refs/heads/claude/<session-id> for history
94
+ execFileSync('git', ['-C', cwd, 'update-ref', `refs/heads/claude/${session_id}`, commitSha], {
95
+ timeout: 5000,
96
+ });
97
+ // Find files that changed between base and new commit
98
+ const diffOutput = execFileSync('git', ['-C', cwd, 'diff-tree', '--no-commit-id', '--name-only', '-r', baseCommit, commitSha], { encoding: 'utf-8', timeout: 5000 }).trim();
99
+ const modifiedFiles = diffOutput
100
+ .split('\n')
101
+ .map((f) => f.trim())
102
+ .filter((f) => f.length > 0);
103
+ // Clean up shadow index directory
104
+ try {
105
+ fs.rmSync(shadowDir, { recursive: true, force: true });
106
+ }
107
+ catch (err) {
108
+ process.stderr.write(`[codesearch:stop-hook] Failed to clean shadow index: ${String(err)}\n`);
109
+ }
110
+ if (modifiedFiles.length === 0) {
111
+ outputSuccess();
112
+ return;
113
+ }
114
+ // Write manifest for targeted runner
115
+ const manifest = { projectPath: cwd, modifiedFiles };
116
+ const manifestFile = path.join(os.tmpdir(), `codesearch-reindex-${session_id}.json`);
117
+ fs.writeFileSync(manifestFile, JSON.stringify(manifest), 'utf-8');
118
+ // Spawn detached background targeted runner
119
+ const runnerPath = path.join(__dirname, 'targeted-runner.js');
120
+ const child = spawn(process.execPath, [runnerPath, manifestFile], {
121
+ detached: true,
122
+ stdio: 'ignore',
123
+ env: process.env,
124
+ windowsHide: true,
125
+ });
126
+ child.unref();
127
+ outputSuccess();
128
+ }
129
+ catch (err) {
130
+ outputError(err instanceof Error ? err.message : String(err));
131
+ }
132
+ }
133
+ // CLI router calls run() directly; self-execute when run as standalone script
134
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
135
+ void run();
136
+ }
137
+ //# sourceMappingURL=stop-hook.js.map
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Standalone CLI for background targeted re-indexing.
4
+ * Spawned as a detached child process by stop-hook.ts.
5
+ *
6
+ * Usage: node targeted-runner.js <manifest-json-path>
7
+ *
8
+ * Manifest JSON: { projectPath: string, modifiedFiles: string[] }
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=targeted-runner.d.ts.map
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Standalone CLI for background targeted re-indexing.
4
+ * Spawned as a detached child process by stop-hook.ts.
5
+ *
6
+ * Usage: node targeted-runner.js <manifest-json-path>
7
+ *
8
+ * Manifest JSON: { projectPath: string, modifiedFiles: string[] }
9
+ */
10
+ import fs from 'node:fs';
11
+ import { indexFiles } from '../core/targeted-indexer.js';
12
+ import { createEmbedding } from '../embedding/factory.js';
13
+ import { SqliteVectorDB } from '../vectordb/sqlite.js';
14
+ import { getCodesearchDbPath } from '../paths.js';
15
+ import { loadConfig } from '../config.js';
16
+ async function main() {
17
+ const manifestPath = process.argv[2];
18
+ if (!manifestPath) {
19
+ process.stderr.write('Usage: targeted-runner.js <manifest-json-path>\n');
20
+ process.exit(1);
21
+ }
22
+ let manifest;
23
+ try {
24
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
25
+ }
26
+ catch (err) {
27
+ process.stderr.write(`[targeted-runner] Failed to read manifest: ${String(err)}\n`);
28
+ process.exit(1);
29
+ }
30
+ // Clean up manifest file
31
+ try {
32
+ fs.unlinkSync(manifestPath);
33
+ }
34
+ catch {
35
+ // Ignore — best effort
36
+ }
37
+ const { projectPath, modifiedFiles } = manifest;
38
+ if (!projectPath || !Array.isArray(modifiedFiles) || modifiedFiles.length === 0) {
39
+ process.stderr.write('[targeted-runner] Empty or invalid manifest, nothing to do.\n');
40
+ process.exit(0);
41
+ }
42
+ try {
43
+ const config = loadConfig();
44
+ const embedding = createEmbedding(config);
45
+ await embedding.initialize();
46
+ const vectordb = new SqliteVectorDB(getCodesearchDbPath());
47
+ const result = await indexFiles(projectPath, modifiedFiles, embedding, vectordb);
48
+ process.stderr.write(`[targeted-runner] Re-indexed ${result.processedFiles} files ` +
49
+ `(${result.totalChunks} chunks, ${result.skippedFiles} deleted) ` +
50
+ `in ${result.durationMs}ms\n`);
51
+ }
52
+ catch (err) {
53
+ process.stderr.write(`[targeted-runner] Failed: ${String(err)}\n`);
54
+ process.exit(1);
55
+ }
56
+ }
57
+ void main();
58
+ //# sourceMappingURL=targeted-runner.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ // CRITICAL: Redirect console outputs to stderr BEFORE any imports
3
+ // Only MCP protocol messages should go to stdout
4
+ console.log = (...args) => {
5
+ process.stderr.write('[LOG] ' + args.join(' ') + '\n');
6
+ };
7
+ console.warn = (...args) => {
8
+ process.stderr.write('[WARN] ' + args.join(' ') + '\n');
9
+ };
10
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
12
+ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
13
+ import { loadConfig } from './config.js';
14
+ import { createEmbedding } from './embedding/factory.js';
15
+ import { SqliteVectorDB } from './vectordb/sqlite.js';
16
+ import { getCodesearchDbPath } from './paths.js';
17
+ import { StateManager, cleanupOrphanedSnapshots } from './state/snapshot.js';
18
+ import { listProjects } from './state/registry.js';
19
+ import { ToolHandlers } from './tools.js';
20
+ import { TOOL_DEFINITIONS } from './tool-schemas.js';
21
+ import { getSetupErrorMessage } from './setup-message.js';
22
+ import { BUILD_VERSION, BUILD_TIMESTAMP } from './build-info.js';
23
+ const SERVER_INSTRUCTIONS = `# Eidetic Codesearch — Workflow
24
+
25
+ **Before searching:** Ensure the codebase is indexed.
26
+ - \`list\` → see what's already indexed
27
+ - \`index(path="...", dryRun=true)\` → preview before indexing
28
+ - \`index(path="...")\` → index (incremental, only re-embeds changed files)
29
+
30
+ **Searching efficiently:**
31
+ - \`search(query="...")\` → returns compact table by default (~20 tokens/result)
32
+ - Review the table, then use Read tool to fetch full code for interesting results
33
+ - Add \`compact=false\` only when you need all code snippets immediately
34
+ - Use \`extensionFilter\` to narrow by file type
35
+ - Use \`project\` param instead of \`path\` for convenience
36
+ - Start with specific queries, broaden if no results
37
+
38
+ **After first index:**
39
+ - Re-indexing is incremental (only changed files re-embedded)
40
+ - Use \`project\` param instead of \`path\` for convenience
41
+ - Use \`cleanup(path="...", dryRun=true)\` to preview stale vectors
42
+
43
+ **Documentation caching (saves ~5K tokens per repeated doc fetch):**
44
+ - After fetching docs via query-docs or WebFetch, cache them: \`ingest(content="...", source="<url>", library="<name>", topic="<topic>")\`
45
+ - Next time you need the same docs: \`lookup(query="...", library="<name>")\`
46
+ - Stale docs (past TTL) still return results but are flagged \`[STALE]\``;
47
+ async function main() {
48
+ // CLI subcommand routing — hooks call `npx eidetic-codesearch hook <event>`
49
+ if (process.argv[2] === 'hook') {
50
+ const { runHook } = await import('./hooks/cli-router.js');
51
+ await runHook(process.argv[3]);
52
+ process.exit(0);
53
+ }
54
+ const config = loadConfig();
55
+ console.log(`Config loaded. Model: ${config.embeddingModel}`);
56
+ let handlers = null;
57
+ let setupError = null;
58
+ try {
59
+ const embedding = createEmbedding(config);
60
+ await embedding.initialize();
61
+ const vectordb = new SqliteVectorDB(getCodesearchDbPath());
62
+ console.log('Using SQLite vector database.');
63
+ const cleaned = await cleanupOrphanedSnapshots(vectordb);
64
+ if (cleaned > 0) {
65
+ console.log(`Cleaned ${cleaned} orphaned snapshot(s).`);
66
+ }
67
+ const state = new StateManager();
68
+ const hydrated = await state.hydrate(listProjects(), vectordb);
69
+ if (hydrated > 0) {
70
+ console.log(`Hydrated ${hydrated} project(s) from registry.`);
71
+ }
72
+ handlers = new ToolHandlers(embedding, vectordb, state);
73
+ }
74
+ catch (err) {
75
+ setupError = err instanceof Error ? err.message : String(err);
76
+ console.warn(`Codesearch initialization failed: ${setupError}`);
77
+ console.warn('Server will start in setup-required mode. All tool calls will return setup instructions.');
78
+ }
79
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
80
+ const server = new Server({ name: '@tai-io/codesearch', version: BUILD_VERSION }, { capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS });
81
+ // eslint-disable-next-line @typescript-eslint/require-await
82
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
83
+ tools: [...TOOL_DEFINITIONS],
84
+ }));
85
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
86
+ const { name, arguments: args } = request.params;
87
+ if (!handlers) {
88
+ return {
89
+ content: [
90
+ {
91
+ type: 'text',
92
+ text: getSetupErrorMessage(setupError ?? 'Unknown error'),
93
+ },
94
+ ],
95
+ isError: true,
96
+ };
97
+ }
98
+ switch (name) {
99
+ case 'index':
100
+ return handlers.handleIndex(args ?? {});
101
+ case 'search':
102
+ return handlers.handleSearch(args ?? {});
103
+ case 'clear':
104
+ return handlers.handleClear(args ?? {});
105
+ case 'list':
106
+ return handlers.handleList();
107
+ case 'ingest':
108
+ return handlers.handleIngest(args ?? {});
109
+ case 'lookup':
110
+ return handlers.handleLookup(args ?? {});
111
+ case 'cleanup':
112
+ return handlers.handleCleanup(args ?? {});
113
+ case 'browse':
114
+ return handlers.handleBrowse(args ?? {});
115
+ default:
116
+ return {
117
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
118
+ isError: true,
119
+ };
120
+ }
121
+ });
122
+ const transport = new StdioServerTransport();
123
+ await server.connect(transport);
124
+ console.log(`Eidetic Codesearch MCP server v${BUILD_VERSION} (built ${BUILD_TIMESTAMP}) started on stdio.`);
125
+ }
126
+ process.on('SIGINT', () => {
127
+ console.error('Received SIGINT, shutting down...');
128
+ process.exit(0);
129
+ });
130
+ process.on('SIGTERM', () => {
131
+ console.error('Received SIGTERM, shutting down...');
132
+ process.exit(0);
133
+ });
134
+ main().catch((err) => {
135
+ console.error('Fatal error:', err);
136
+ process.exit(1);
137
+ });
138
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ export declare function normalizePath(inputPath: string): string;
2
+ export declare function getDataDir(): string;
3
+ export declare function getSnapshotDir(): string;
4
+ export declare function getCacheDir(): string;
5
+ export declare function getRegistryPath(): string;
6
+ export declare function pathToCollectionName(absolutePath: string): string;
7
+ export declare function getCodesearchDbPath(): string;
8
+ export declare function getSnapshotDbPath(): string;
9
+ export declare function getDocMetadataPath(): string;
10
+ export declare function docCollectionName(library: string): string;
11
+ //# sourceMappingURL=paths.d.ts.map