claude-eidetic 0.1.1 → 0.1.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.
Files changed (96) hide show
  1. package/README.md +333 -0
  2. package/dist/config.d.ts +25 -0
  3. package/dist/config.js +29 -10
  4. package/dist/core/cleanup.d.ts +8 -0
  5. package/dist/core/cleanup.js +41 -0
  6. package/dist/core/doc-indexer.d.ts +13 -0
  7. package/dist/core/doc-indexer.js +76 -0
  8. package/dist/core/doc-searcher.d.ts +13 -0
  9. package/dist/core/doc-searcher.js +65 -0
  10. package/dist/core/file-category.d.ts +7 -0
  11. package/dist/core/file-category.js +75 -0
  12. package/dist/core/indexer.js +12 -4
  13. package/dist/core/preview.d.ts +1 -2
  14. package/dist/core/preview.js +2 -5
  15. package/dist/core/repo-map.d.ts +33 -0
  16. package/dist/core/repo-map.js +144 -0
  17. package/dist/core/searcher.d.ts +1 -13
  18. package/dist/core/searcher.js +20 -24
  19. package/dist/core/snapshot-io.js +2 -2
  20. package/dist/core/sync.d.ts +5 -25
  21. package/dist/core/sync.js +90 -65
  22. package/dist/core/targeted-indexer.d.ts +19 -0
  23. package/dist/core/targeted-indexer.js +127 -0
  24. package/dist/embedding/factory.d.ts +0 -13
  25. package/dist/embedding/factory.js +0 -17
  26. package/dist/embedding/openai.d.ts +2 -14
  27. package/dist/embedding/openai.js +7 -20
  28. package/dist/errors.d.ts +2 -0
  29. package/dist/errors.js +2 -0
  30. package/dist/format.d.ts +12 -0
  31. package/dist/format.js +160 -31
  32. package/dist/hooks/post-tool-use.d.ts +13 -0
  33. package/dist/hooks/post-tool-use.js +113 -0
  34. package/dist/hooks/stop-hook.d.ts +11 -0
  35. package/dist/hooks/stop-hook.js +121 -0
  36. package/dist/hooks/targeted-runner.d.ts +11 -0
  37. package/dist/hooks/targeted-runner.js +66 -0
  38. package/dist/index.js +68 -9
  39. package/dist/infra/qdrant-bootstrap.js +14 -12
  40. package/dist/memory/history.d.ts +19 -0
  41. package/dist/memory/history.js +40 -0
  42. package/dist/memory/llm.d.ts +2 -0
  43. package/dist/memory/llm.js +56 -0
  44. package/dist/memory/prompts.d.ts +5 -0
  45. package/dist/memory/prompts.js +36 -0
  46. package/dist/memory/reconciler.d.ts +12 -0
  47. package/dist/memory/reconciler.js +36 -0
  48. package/dist/memory/store.d.ts +20 -0
  49. package/dist/memory/store.js +206 -0
  50. package/dist/memory/types.d.ts +28 -0
  51. package/dist/memory/types.js +2 -0
  52. package/dist/paths.d.ts +3 -4
  53. package/dist/paths.js +14 -4
  54. package/dist/precompact/hook.d.ts +9 -0
  55. package/dist/precompact/hook.js +170 -0
  56. package/dist/precompact/index-runner.d.ts +9 -0
  57. package/dist/precompact/index-runner.js +52 -0
  58. package/dist/precompact/note-writer.d.ts +15 -0
  59. package/dist/precompact/note-writer.js +109 -0
  60. package/dist/precompact/session-indexer.d.ts +13 -0
  61. package/dist/precompact/session-indexer.js +31 -0
  62. package/dist/precompact/tier0-inject.d.ts +16 -0
  63. package/dist/precompact/tier0-inject.js +88 -0
  64. package/dist/precompact/tier0-writer.d.ts +16 -0
  65. package/dist/precompact/tier0-writer.js +74 -0
  66. package/dist/precompact/transcript-parser.d.ts +10 -0
  67. package/dist/precompact/transcript-parser.js +148 -0
  68. package/dist/precompact/types.d.ts +93 -0
  69. package/dist/precompact/types.js +5 -0
  70. package/dist/precompact/utils.d.ts +29 -0
  71. package/dist/precompact/utils.js +95 -0
  72. package/dist/setup-message.d.ts +2 -2
  73. package/dist/setup-message.js +39 -20
  74. package/dist/splitter/ast.js +84 -22
  75. package/dist/splitter/line.d.ts +0 -4
  76. package/dist/splitter/line.js +1 -7
  77. package/dist/splitter/symbol-extract.d.ts +16 -0
  78. package/dist/splitter/symbol-extract.js +61 -0
  79. package/dist/splitter/types.d.ts +5 -0
  80. package/dist/splitter/types.js +1 -1
  81. package/dist/state/doc-metadata.d.ts +18 -0
  82. package/dist/state/doc-metadata.js +59 -0
  83. package/dist/state/registry.d.ts +1 -3
  84. package/dist/state/snapshot.d.ts +0 -1
  85. package/dist/state/snapshot.js +3 -19
  86. package/dist/tool-schemas.d.ts +251 -1
  87. package/dist/tool-schemas.js +307 -0
  88. package/dist/tools.d.ts +69 -0
  89. package/dist/tools.js +286 -17
  90. package/dist/vectordb/milvus.d.ts +7 -5
  91. package/dist/vectordb/milvus.js +116 -19
  92. package/dist/vectordb/qdrant.d.ts +8 -10
  93. package/dist/vectordb/qdrant.js +105 -33
  94. package/dist/vectordb/types.d.ts +20 -0
  95. package/messages.yaml +50 -0
  96. package/package.json +31 -6
@@ -1,4 +1,4 @@
1
- import type { VectorDB, CodeDocument, HybridSearchParams, SearchResult } from './types.js';
1
+ import type { VectorDB, CodeDocument, HybridSearchParams, SearchResult, SymbolEntry } from './types.js';
2
2
  export declare const RRF_K = 5;
3
3
  export declare const RRF_ALPHA = 0.7;
4
4
  export declare class QdrantVectorDB implements VectorDB {
@@ -9,22 +9,19 @@ export declare class QdrantVectorDB implements VectorDB {
9
9
  dropCollection(name: string): Promise<void>;
10
10
  insert(name: string, documents: CodeDocument[]): Promise<void>;
11
11
  search(name: string, params: HybridSearchParams): Promise<SearchResult[]>;
12
+ getById(name: string, id: string): Promise<{
13
+ payload: Record<string, unknown>;
14
+ vector: number[];
15
+ } | null>;
16
+ updatePoint(name: string, id: string, vector: number[], payload: Record<string, unknown>): Promise<void>;
12
17
  deleteByPath(name: string, relativePath: string): Promise<void>;
18
+ listSymbols(name: string): Promise<SymbolEntry[]>;
13
19
  }
14
20
  interface RankedPoint {
15
21
  id: string | number;
16
22
  payload?: Record<string, unknown> | null;
17
23
  rawScore: number;
18
24
  }
19
- /**
20
- * Rank text-match results by normalized term frequency.
21
- *
22
- * Qdrant's scroll API returns text-filtered points in storage order (not by
23
- * relevance). Before feeding these into RRF, we score each result by how many
24
- * query terms appear in its content, normalized by word count to avoid bias
25
- * toward longer chunks. Returns points sorted best-first with rawScore attached
26
- * so RRF can blend rank position with content-based signal.
27
- */
28
25
  export declare function rankByTermFrequency(points: {
29
26
  id: string | number;
30
27
  payload?: Record<string, unknown> | null;
@@ -37,6 +34,7 @@ interface ScoredPayload {
37
34
  endLine: number;
38
35
  fileExtension: string;
39
36
  language: string;
37
+ fileCategory: string;
40
38
  }
41
39
  export declare function extractPayload(point: {
42
40
  id: string | number;
@@ -10,7 +10,7 @@ export class QdrantVectorDB {
10
10
  const config = getConfig();
11
11
  this.client = new QdrantClient({
12
12
  url: url ?? config.qdrantUrl,
13
- ...(apiKey ?? config.qdrantApiKey ? { apiKey: apiKey ?? config.qdrantApiKey } : {}),
13
+ ...((apiKey ?? config.qdrantApiKey) ? { apiKey: apiKey ?? config.qdrantApiKey } : {}),
14
14
  });
15
15
  }
16
16
  async createCollection(name, dimension) {
@@ -20,7 +20,6 @@ export class QdrantVectorDB {
20
20
  dense: { size: dimension, distance: 'Cosine' },
21
21
  },
22
22
  });
23
- // Create payload indexes for filtering and full-text search
24
23
  await Promise.all([
25
24
  this.client.createPayloadIndex(name, {
26
25
  field_name: 'content',
@@ -37,6 +36,11 @@ export class QdrantVectorDB {
37
36
  field_schema: 'keyword',
38
37
  wait: true,
39
38
  }),
39
+ this.client.createPayloadIndex(name, {
40
+ field_name: 'fileCategory',
41
+ field_schema: 'keyword',
42
+ wait: true,
43
+ }),
40
44
  ]);
41
45
  }
42
46
  catch (err) {
@@ -71,7 +75,7 @@ export class QdrantVectorDB {
71
75
  const batch = documents.slice(i, i + batchSize);
72
76
  await this.client.upsert(name, {
73
77
  wait: true,
74
- points: batch.map(doc => ({
78
+ points: batch.map((doc) => ({
75
79
  id: doc.id ?? randomUUID(),
76
80
  vector: { dense: doc.vector },
77
81
  payload: {
@@ -81,6 +85,11 @@ export class QdrantVectorDB {
81
85
  endLine: doc.endLine,
82
86
  fileExtension: doc.fileExtension,
83
87
  language: doc.language,
88
+ fileCategory: doc.fileCategory ?? 'source',
89
+ symbolName: doc.symbolName ?? '',
90
+ symbolKind: doc.symbolKind ?? '',
91
+ symbolSignature: doc.symbolSignature ?? '',
92
+ parentSymbol: doc.parentSymbol ?? '',
84
93
  },
85
94
  })),
86
95
  });
@@ -95,7 +104,7 @@ export class QdrantVectorDB {
95
104
  const fetchLimit = params.limit * 2;
96
105
  const extensionFilter = params.extensionFilter?.length
97
106
  ? {
98
- should: params.extensionFilter.map(ext => ({
107
+ should: params.extensionFilter.map((ext) => ({
99
108
  key: 'fileExtension',
100
109
  match: { value: ext },
101
110
  })),
@@ -118,15 +127,51 @@ export class QdrantVectorDB {
118
127
  limit: fetchLimit,
119
128
  with_payload: true,
120
129
  });
121
- // Rank text results by query term frequency so RRF receives a meaningful ordering
122
130
  const rankedTextResults = rankByTermFrequency(textResponse.points, params.queryText);
123
- // 3. Client-side RRF fusion blending rank position with raw similarity scores
124
131
  return reciprocalRankFusion(denseResults, rankedTextResults, params.limit);
125
132
  }
126
133
  catch (err) {
127
134
  throw new VectorDBError(`Search failed in collection "${name}"`, err);
128
135
  }
129
136
  }
137
+ async getById(name, id) {
138
+ try {
139
+ const results = await this.client.retrieve(name, {
140
+ ids: [id],
141
+ with_payload: true,
142
+ with_vector: true,
143
+ });
144
+ if (results.length === 0)
145
+ return null;
146
+ const point = results[0];
147
+ const vectors = point.vector;
148
+ const vector = Array.isArray(vectors) ? vectors : (vectors?.dense ?? []);
149
+ return {
150
+ payload: point.payload ?? {},
151
+ vector,
152
+ };
153
+ }
154
+ catch (err) {
155
+ throw new VectorDBError(`Failed to retrieve point "${id}" from "${name}"`, err);
156
+ }
157
+ }
158
+ async updatePoint(name, id, vector, payload) {
159
+ try {
160
+ await this.client.upsert(name, {
161
+ wait: true,
162
+ points: [
163
+ {
164
+ id,
165
+ vector: { dense: vector },
166
+ payload,
167
+ },
168
+ ],
169
+ });
170
+ }
171
+ catch (err) {
172
+ throw new VectorDBError(`Failed to update point "${id}" in "${name}"`, err);
173
+ }
174
+ }
130
175
  async deleteByPath(name, relativePath) {
131
176
  try {
132
177
  await this.client.delete(name, {
@@ -140,43 +185,74 @@ export class QdrantVectorDB {
140
185
  throw new VectorDBError(`Failed to delete documents for path "${relativePath}" from "${name}"`, err);
141
186
  }
142
187
  }
188
+ async listSymbols(name) {
189
+ try {
190
+ const results = [];
191
+ let offset = undefined;
192
+ const pageSize = 1000;
193
+ while (true) {
194
+ const response = await this.client.scroll(name, {
195
+ filter: {
196
+ must_not: [{ is_empty: { key: 'symbolName' } }],
197
+ },
198
+ limit: pageSize,
199
+ with_payload: true,
200
+ with_vector: false,
201
+ ...(offset !== undefined ? { offset } : {}),
202
+ });
203
+ for (const point of response.points) {
204
+ const p = point.payload ?? {};
205
+ const symName = String(p.symbolName ?? '');
206
+ if (!symName)
207
+ continue;
208
+ results.push({
209
+ name: symName,
210
+ kind: String(p.symbolKind ?? ''),
211
+ relativePath: String(p.relativePath ?? ''),
212
+ startLine: Number(p.startLine ?? 0),
213
+ ...(p.symbolSignature ? { signature: String(p.symbolSignature) } : {}),
214
+ ...(p.parentSymbol ? { parentName: String(p.parentSymbol) } : {}),
215
+ });
216
+ }
217
+ if (response.next_page_offset == null || response.points.length < pageSize)
218
+ break;
219
+ offset = response.next_page_offset;
220
+ }
221
+ return results;
222
+ }
223
+ catch (err) {
224
+ throw new VectorDBError(`Failed to list symbols from "${name}"`, err);
225
+ }
226
+ }
143
227
  }
144
- /**
145
- * Rank text-match results by normalized term frequency.
146
- *
147
- * Qdrant's scroll API returns text-filtered points in storage order (not by
148
- * relevance). Before feeding these into RRF, we score each result by how many
149
- * query terms appear in its content, normalized by word count to avoid bias
150
- * toward longer chunks. Returns points sorted best-first with rawScore attached
151
- * so RRF can blend rank position with content-based signal.
152
- */
228
+ // Rank text-match results by normalized term frequency so RRF receives a meaningful ordering.
153
229
  export function rankByTermFrequency(points, queryText) {
154
230
  if (points.length === 0)
155
231
  return [];
156
- // Split query into individual terms, deduplicate, and build escaped regex patterns
157
- const terms = [...new Set(queryText.toLowerCase().split(/\s+/).filter(t => t.length > 0))];
232
+ const terms = [
233
+ ...new Set(queryText
234
+ .toLowerCase()
235
+ .split(/\s+/)
236
+ .filter((t) => t.length > 0)),
237
+ ];
158
238
  if (terms.length === 0)
159
- return points.map(p => ({ ...p, rawScore: 0 }));
160
- const termPatterns = terms.map(t => new RegExp(t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'));
161
- const scored = points.map(point => {
239
+ return points.map((p) => ({ ...p, rawScore: 0 }));
240
+ const termPatterns = terms.map((t) => new RegExp(t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'));
241
+ const scored = points.map((point) => {
162
242
  const content = point.payload?.content ?? '';
163
243
  const wordCount = Math.max(1, content.split(/\s+/).length);
164
- // Count total term occurrences across all query terms
165
244
  let hits = 0;
166
245
  for (const pattern of termPatterns) {
167
- pattern.lastIndex = 0; // reset stateful regex
246
+ pattern.lastIndex = 0;
168
247
  const matches = content.match(pattern);
169
248
  hits += matches ? matches.length : 0;
170
249
  }
171
- // Normalize by word count (TF = hits / total words)
172
250
  const tf = hits / wordCount;
173
251
  return { point, tf };
174
252
  });
175
- // Sort by TF descending (best matches first)
176
253
  scored.sort((a, b) => b.tf - a.tf);
177
- // Normalize TF to 0-1 range for blending with cosine similarity
178
254
  const maxTf = scored[0].tf;
179
- return scored.map(s => ({
255
+ return scored.map((s) => ({
180
256
  ...s.point,
181
257
  rawScore: maxTf > 0 ? s.tf / maxTf : 0,
182
258
  }));
@@ -191,19 +267,15 @@ export function extractPayload(point) {
191
267
  endLine: Number(p.endLine ?? 0),
192
268
  fileExtension: String(p.fileExtension ?? ''),
193
269
  language: String(p.language ?? ''),
270
+ fileCategory: String(p.fileCategory ?? ''),
194
271
  };
195
272
  }
196
273
  export function reciprocalRankFusion(denseResults, textResults, limit) {
197
274
  const scoreMap = new Map();
198
- // Hybrid RRF blended with raw similarity:
199
- // score = alpha * (1/(k + rank + 1)) + (1-alpha) * rawSimilarity
200
- // alpha=0.7 preserves rank-fusion stability while 0.3 raw similarity
201
- // injects query-specific signal so identical ranks get different scores.
202
275
  const blendedScore = (rank, rawSimilarity) => RRF_ALPHA * (1 / (RRF_K + rank + 1)) + (1 - RRF_ALPHA) * rawSimilarity;
203
- // Score from dense results (cosine similarity from Qdrant)
204
276
  for (let rank = 0; rank < denseResults.length; rank++) {
205
277
  const point = denseResults[rank];
206
- const rawSim = point.score ?? 0; // cosine similarity (0-1)
278
+ const rawSim = point.score ?? 0;
207
279
  const score = blendedScore(rank, rawSim);
208
280
  const existing = scoreMap.get(point.id);
209
281
  const payload = extractPayload(point);
@@ -214,7 +286,6 @@ export function reciprocalRankFusion(denseResults, textResults, limit) {
214
286
  scoreMap.set(point.id, { score, payload });
215
287
  }
216
288
  }
217
- // Score from text results (normalized TF from rankByTermFrequency)
218
289
  for (let rank = 0; rank < textResults.length; rank++) {
219
290
  const point = textResults[rank];
220
291
  const score = blendedScore(rank, point.rawScore);
@@ -236,6 +307,7 @@ export function reciprocalRankFusion(denseResults, textResults, limit) {
236
307
  fileExtension: payload.fileExtension,
237
308
  language: payload.language,
238
309
  score,
310
+ fileCategory: payload.fileCategory,
239
311
  }));
240
312
  }
241
313
  //# sourceMappingURL=qdrant.js.map
@@ -8,6 +8,19 @@ export interface CodeDocument {
8
8
  endLine: number;
9
9
  fileExtension: string;
10
10
  language: string;
11
+ fileCategory?: string;
12
+ symbolName?: string;
13
+ symbolKind?: string;
14
+ symbolSignature?: string;
15
+ parentSymbol?: string;
16
+ }
17
+ export interface SymbolEntry {
18
+ name: string;
19
+ kind: string;
20
+ relativePath: string;
21
+ startLine: number;
22
+ signature?: string;
23
+ parentName?: string;
11
24
  }
12
25
  export interface HybridSearchParams {
13
26
  queryVector: EmbeddingVector;
@@ -23,6 +36,7 @@ export interface SearchResult {
23
36
  fileExtension: string;
24
37
  language: string;
25
38
  score: number;
39
+ fileCategory?: string;
26
40
  }
27
41
  export interface VectorDB {
28
42
  createCollection(name: string, dimension: number): Promise<void>;
@@ -31,5 +45,11 @@ export interface VectorDB {
31
45
  insert(name: string, documents: CodeDocument[]): Promise<void>;
32
46
  search(name: string, params: HybridSearchParams): Promise<SearchResult[]>;
33
47
  deleteByPath(name: string, relativePath: string): Promise<void>;
48
+ getById(name: string, id: string): Promise<{
49
+ payload: Record<string, unknown>;
50
+ vector: number[];
51
+ } | null>;
52
+ updatePoint(name: string, id: string, vector: number[], payload: Record<string, unknown>): Promise<void>;
53
+ listSymbols(name: string): Promise<SymbolEntry[]>;
34
54
  }
35
55
  //# sourceMappingURL=types.d.ts.map
package/messages.yaml ADDED
@@ -0,0 +1,50 @@
1
+ # Eidetic user-facing messages — edit here, everything else reads from this file.
2
+
3
+ setup:
4
+ missing:
5
+ header: "Eidetic setup required: No API key configured."
6
+ diagnosis: >
7
+ `OPENAI_API_KEY` is not set and no alternative provider is configured.
8
+ step1: "**Get an API key**: https://platform.openai.com/api-keys"
9
+
10
+ invalid:
11
+ header: "Eidetic setup failed: {error}"
12
+ diagnosis: >
13
+ Key/provider is configured but initialization failed.
14
+ The key may be invalid, expired, or the provider may be unreachable.
15
+ step1: "**Verify your key** is valid and has embedding permissions"
16
+
17
+ unknown:
18
+ header: "Eidetic setup failed: {error}"
19
+ diagnosis: ""
20
+ step1: "**Get an API key**: https://platform.openai.com/api-keys"
21
+
22
+ # Shared across all contexts
23
+ config_instructions: |
24
+ ### Windows
25
+ ```
26
+ setx OPENAI_API_KEY sk-your-key-here
27
+ ```
28
+
29
+ ### macOS / Linux
30
+ Add to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.):
31
+ ```
32
+ export OPENAI_API_KEY="sk-your-key-here"
33
+ ```
34
+
35
+ ### Manual
36
+
37
+ Edit `~/.claude/plugins/claude-eidetic/config.json`:
38
+ ```json
39
+ {
40
+ "env": {
41
+ "OPENAI_API_KEY": "sk-your-key-here"
42
+ }
43
+ }
44
+ ```
45
+
46
+ ### Using Ollama instead (free, local)
47
+
48
+ Set `EMBEDDING_PROVIDER=ollama` in the same env block (requires Ollama running locally with `nomic-embed-text`).
49
+
50
+ footer: "**Restart Claude Code** for changes to take effect."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-eidetic",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Semantic code search MCP server — lean, correct, fast",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,27 +11,37 @@
11
11
  "dist/**/*.js",
12
12
  "dist/**/*.d.ts",
13
13
  "!dist/**/*.test.*",
14
- "!dist/__test__/**",
15
- "!dist/e2e/**"
14
+ "!dist/__tests__/**",
15
+ "!dist/**/tests/**",
16
+ "!dist/e2e/**",
17
+ "messages.yaml"
16
18
  ],
17
19
  "scripts": {
18
20
  "build": "tsc",
19
21
  "dev": "tsx --watch src/index.ts",
20
22
  "start": "node dist/index.js",
21
23
  "clean": "rimraf dist",
24
+ "lint": "eslint src/",
25
+ "lint:fix": "eslint src/ --fix",
26
+ "format": "prettier --write \"src/**/*.ts\"",
27
+ "format:check": "prettier --check \"src/**/*.ts\"",
22
28
  "typecheck": "tsc --noEmit",
23
29
  "test": "vitest run",
24
30
  "test:watch": "vitest",
25
31
  "test:coverage": "vitest run --coverage",
26
32
  "test:integration": "vitest run --config vitest.integration.config.ts",
27
33
  "test:all": "vitest run && vitest run --config vitest.integration.config.ts",
28
- "test:audit": "tsx scripts/audit/lonely-source.ts; tsx scripts/audit/mock-hygiene.ts; tsx scripts/audit/bare-hands.ts; tsx scripts/audit/timer-drift.ts",
29
- "prepublishOnly": "npm run clean && npm run build"
34
+ "test:eval": "tsx scripts/eval/recall-at-k.ts && tsx scripts/eval/fusion-lift.ts && tsx scripts/eval/ndcg.ts",
35
+ "test:eval:generate": "tsx scripts/eval/generate-queries.ts",
36
+ "prepublishOnly": "npm run clean && npm run build",
37
+ "release": "bash scripts/release.sh"
30
38
  },
31
39
  "dependencies": {
40
+ "@anthropic-ai/sdk": "^0.78.0",
32
41
  "@modelcontextprotocol/sdk": "^1.12.1",
33
42
  "@qdrant/js-client-rest": "^1.13.0",
34
- "glob": "^10.0.0",
43
+ "better-sqlite3": "^12.6.2",
44
+ "glob": "^11.0.0",
35
45
  "openai": "^5.1.1",
36
46
  "tree-sitter": "^0.21.1",
37
47
  "tree-sitter-c-sharp": "^0.21.0",
@@ -42,21 +52,36 @@
42
52
  "tree-sitter-python": "^0.21.0",
43
53
  "tree-sitter-rust": "^0.21.0",
44
54
  "tree-sitter-typescript": "^0.21.0",
55
+ "yaml": "^2.8.2",
45
56
  "zod": "^3.25.0"
46
57
  },
47
58
  "optionalDependencies": {
48
59
  "@zilliz/milvus2-sdk-node": "^2.5.10"
49
60
  },
50
61
  "devDependencies": {
62
+ "@eslint/js": "^9.39.3",
63
+ "@types/better-sqlite3": "^7.6.13",
51
64
  "@types/node": "^20.0.0",
52
65
  "@vitest/coverage-v8": "^4.0.18",
66
+ "eslint": "^9.39.3",
67
+ "eslint-config-prettier": "^10.1.8",
68
+ "prettier": "^3.8.1",
53
69
  "rimraf": "^6.0.1",
54
70
  "tsx": "^4.19.4",
55
71
  "typescript": "^5.8.3",
72
+ "typescript-eslint": "^8.56.0",
56
73
  "vitest": "^4.0.18"
57
74
  },
75
+ "overrides": {
76
+ "minimatch": "^10.2.1",
77
+ "@grpc/grpc-js": "^1.8.22"
78
+ },
58
79
  "engines": {
59
80
  "node": ">=20.0.0"
60
81
  },
82
+ "repository": {
83
+ "type": "git",
84
+ "url": "https://github.com/eidetics/claude-eidetic"
85
+ },
61
86
  "license": "MIT"
62
87
  }