@grec0/memory-bank-mcp 0.0.3 → 0.0.4
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 +494 -420
- package/dist/common/chunker.js +166 -534
- package/dist/common/embeddingService.js +39 -51
- package/dist/common/fileScanner.js +123 -58
- package/dist/common/indexManager.js +135 -102
- package/dist/common/projectKnowledgeService.js +627 -0
- package/dist/common/setup.js +49 -0
- package/dist/common/utils.js +215 -0
- package/dist/common/vectorStore.js +80 -67
- package/dist/index.js +77 -9
- package/dist/operations/boardMemberships.js +186 -0
- package/dist/operations/boards.js +268 -0
- package/dist/operations/cards.js +426 -0
- package/dist/operations/comments.js +249 -0
- package/dist/operations/labels.js +258 -0
- package/dist/operations/lists.js +157 -0
- package/dist/operations/projects.js +102 -0
- package/dist/operations/tasks.js +238 -0
- package/dist/tools/analyzeCoverage.js +46 -66
- package/dist/tools/board-summary.js +151 -0
- package/dist/tools/card-details.js +106 -0
- package/dist/tools/create-card-with-tasks.js +81 -0
- package/dist/tools/generateProjectDocs.js +133 -0
- package/dist/tools/getProjectDocs.js +126 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/indexCode.js +0 -1
- package/dist/tools/searchMemory.js +2 -2
- package/dist/tools/workflow-actions.js +145 -0
- package/package.json +2 -2
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* @fileoverview Embedding service for Memory Bank using OpenAI
|
|
3
3
|
* Generates vector embeddings for code chunks
|
|
4
4
|
*/
|
|
5
|
-
import { logger } from "./logger.js";
|
|
6
5
|
import OpenAI from "openai";
|
|
7
6
|
import * as crypto from "crypto";
|
|
8
7
|
import * as fs from "fs";
|
|
@@ -50,7 +49,7 @@ export class EmbeddingService {
|
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
/**
|
|
53
|
-
* Saves embedding cache to disk
|
|
52
|
+
* Saves embedding cache to disk
|
|
54
53
|
*/
|
|
55
54
|
saveCache() {
|
|
56
55
|
try {
|
|
@@ -59,16 +58,8 @@ export class EmbeddingService {
|
|
|
59
58
|
fs.mkdirSync(dir, { recursive: true });
|
|
60
59
|
}
|
|
61
60
|
const entries = Array.from(this.cache.values());
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
entries.forEach((entry, index) => {
|
|
65
|
-
const isLast = index === entries.length - 1;
|
|
66
|
-
const line = JSON.stringify(entry) + (isLast ? '' : ',\n');
|
|
67
|
-
stream.write(line);
|
|
68
|
-
});
|
|
69
|
-
stream.write('\n]');
|
|
70
|
-
stream.end();
|
|
71
|
-
console.error(`Updated embedding cache (Total: ${entries.length} entries)`);
|
|
61
|
+
fs.writeFileSync(this.options.cachePath, JSON.stringify(entries, null, 2));
|
|
62
|
+
console.error(`Saved ${entries.length} embeddings to cache`);
|
|
72
63
|
}
|
|
73
64
|
catch (error) {
|
|
74
65
|
console.error(`Warning: Could not save embedding cache: ${error}`);
|
|
@@ -145,14 +136,14 @@ export class EmbeddingService {
|
|
|
145
136
|
// Check if it's a rate limit error
|
|
146
137
|
if (error?.status === 429 || error?.code === "rate_limit_exceeded") {
|
|
147
138
|
const backoffMs = Math.pow(2, attempt) * 1000; // Exponential backoff
|
|
148
|
-
|
|
139
|
+
console.error(`Rate limit hit, retrying in ${backoffMs}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
149
140
|
await this.sleep(backoffMs);
|
|
150
141
|
continue;
|
|
151
142
|
}
|
|
152
143
|
// Check if it's a temporary error
|
|
153
144
|
if (error?.status >= 500 && error?.status < 600) {
|
|
154
145
|
const backoffMs = Math.pow(2, attempt) * 1000;
|
|
155
|
-
|
|
146
|
+
console.error(`Server error ${error.status}, retrying in ${backoffMs}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
156
147
|
await this.sleep(backoffMs);
|
|
157
148
|
continue;
|
|
158
149
|
}
|
|
@@ -194,10 +185,9 @@ export class EmbeddingService {
|
|
|
194
185
|
/**
|
|
195
186
|
* Generates embeddings for multiple chunks in batches
|
|
196
187
|
*/
|
|
197
|
-
async generateBatchEmbeddings(chunks
|
|
188
|
+
async generateBatchEmbeddings(chunks) {
|
|
198
189
|
const results = [];
|
|
199
190
|
const toGenerate = [];
|
|
200
|
-
const shouldSave = options.autoSave !== undefined ? options.autoSave : true;
|
|
201
191
|
// Check cache and collect chunks that need generation
|
|
202
192
|
for (let i = 0; i < chunks.length; i++) {
|
|
203
193
|
const chunk = chunks[i];
|
|
@@ -214,43 +204,41 @@ export class EmbeddingService {
|
|
|
214
204
|
toGenerate.push({ ...chunk, index: i });
|
|
215
205
|
}
|
|
216
206
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
logger.error(`Error generating batch embeddings:`, error);
|
|
244
|
-
throw error;
|
|
245
|
-
}
|
|
246
|
-
// Small delay between batches to avoid rate limits
|
|
247
|
-
if (i + this.options.batchSize < toGenerate.length) {
|
|
248
|
-
await this.sleep(100);
|
|
207
|
+
console.error(`Generating embeddings: ${toGenerate.length} new, ${chunks.length - toGenerate.length} cached`);
|
|
208
|
+
// Process in batches
|
|
209
|
+
for (let i = 0; i < toGenerate.length; i += this.options.batchSize) {
|
|
210
|
+
const batch = toGenerate.slice(i, i + this.options.batchSize);
|
|
211
|
+
const batchTexts = batch.map((item) => item.content);
|
|
212
|
+
console.error(`Processing batch ${Math.floor(i / this.options.batchSize) + 1}/${Math.ceil(toGenerate.length / this.options.batchSize)}`);
|
|
213
|
+
try {
|
|
214
|
+
const vectors = await this.generateBatchWithRetry(batchTexts);
|
|
215
|
+
// Store results and cache
|
|
216
|
+
for (let j = 0; j < batch.length; j++) {
|
|
217
|
+
const item = batch[j];
|
|
218
|
+
const vector = vectors[j];
|
|
219
|
+
// Cache the result
|
|
220
|
+
this.cacheEmbedding(item.id, item.content, vector);
|
|
221
|
+
// Estimate tokens
|
|
222
|
+
const tokens = Math.ceil(item.content.length / 4);
|
|
223
|
+
results[item.index] = {
|
|
224
|
+
chunkId: item.id,
|
|
225
|
+
vector,
|
|
226
|
+
model: this.options.model,
|
|
227
|
+
tokens,
|
|
228
|
+
};
|
|
249
229
|
}
|
|
250
230
|
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.error(`Error generating batch embeddings: ${error}`);
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
// Small delay between batches to avoid rate limits
|
|
236
|
+
if (i + this.options.batchSize < toGenerate.length) {
|
|
237
|
+
await this.sleep(100);
|
|
238
|
+
}
|
|
251
239
|
}
|
|
252
240
|
// Save cache after batch processing
|
|
253
|
-
if (
|
|
241
|
+
if (this.options.enableCache && toGenerate.length > 0) {
|
|
254
242
|
this.saveCache();
|
|
255
243
|
}
|
|
256
244
|
return results;
|
|
@@ -268,7 +256,7 @@ export class EmbeddingService {
|
|
|
268
256
|
return response.data[0].embedding;
|
|
269
257
|
}
|
|
270
258
|
catch (error) {
|
|
271
|
-
|
|
259
|
+
console.error(`Error generating query embedding: ${error}`);
|
|
272
260
|
throw error;
|
|
273
261
|
}
|
|
274
262
|
}
|
|
@@ -280,7 +268,7 @@ export class EmbeddingService {
|
|
|
280
268
|
if (fs.existsSync(this.options.cachePath)) {
|
|
281
269
|
fs.unlinkSync(this.options.cachePath);
|
|
282
270
|
}
|
|
283
|
-
|
|
271
|
+
console.error("Embedding cache cleared");
|
|
284
272
|
}
|
|
285
273
|
/**
|
|
286
274
|
* Gets cache statistics
|
|
@@ -6,53 +6,120 @@ import * as fs from "fs";
|
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import * as crypto from "crypto";
|
|
8
8
|
import ignoreLib from "ignore";
|
|
9
|
-
import { logger } from "./logger.js";
|
|
10
9
|
// Handle ignore library export
|
|
11
10
|
const ignore = typeof ignoreLib === 'function' ? ignoreLib : ignoreLib.default;
|
|
12
11
|
// Language detection by file extension
|
|
13
12
|
const LANGUAGE_MAP = {
|
|
13
|
+
// TypeScript/JavaScript
|
|
14
14
|
".ts": "typescript",
|
|
15
15
|
".tsx": "typescript",
|
|
16
16
|
".js": "javascript",
|
|
17
17
|
".jsx": "javascript",
|
|
18
18
|
".mjs": "javascript",
|
|
19
19
|
".cjs": "javascript",
|
|
20
|
+
// Python
|
|
20
21
|
".py": "python",
|
|
22
|
+
".pyi": "python",
|
|
23
|
+
".pyw": "python",
|
|
24
|
+
// JVM Languages
|
|
21
25
|
".java": "java",
|
|
26
|
+
".kt": "kotlin",
|
|
27
|
+
".kts": "kotlin",
|
|
28
|
+
".scala": "scala",
|
|
29
|
+
".groovy": "groovy",
|
|
30
|
+
".gradle": "groovy",
|
|
31
|
+
// C/C++
|
|
22
32
|
".c": "c",
|
|
23
33
|
".cpp": "cpp",
|
|
24
34
|
".cc": "cpp",
|
|
25
35
|
".cxx": "cpp",
|
|
26
36
|
".h": "c",
|
|
27
37
|
".hpp": "cpp",
|
|
38
|
+
".hxx": "cpp",
|
|
39
|
+
// .NET
|
|
28
40
|
".cs": "csharp",
|
|
41
|
+
".fs": "fsharp",
|
|
42
|
+
".vb": "vb",
|
|
43
|
+
// Systems Languages
|
|
29
44
|
".go": "go",
|
|
30
45
|
".rs": "rust",
|
|
46
|
+
// Scripting Languages
|
|
31
47
|
".rb": "ruby",
|
|
32
48
|
".php": "php",
|
|
33
|
-
".
|
|
34
|
-
".
|
|
35
|
-
".
|
|
36
|
-
".scala": "scala",
|
|
49
|
+
".pl": "perl",
|
|
50
|
+
".pm": "perl",
|
|
51
|
+
".lua": "lua",
|
|
37
52
|
".r": "r",
|
|
38
53
|
".R": "r",
|
|
39
|
-
|
|
54
|
+
// Mobile
|
|
55
|
+
".swift": "swift",
|
|
56
|
+
".m": "objectivec",
|
|
57
|
+
".mm": "objectivec",
|
|
58
|
+
// Shell
|
|
40
59
|
".sh": "shell",
|
|
41
60
|
".bash": "shell",
|
|
42
61
|
".zsh": "shell",
|
|
43
62
|
".fish": "shell",
|
|
44
|
-
".
|
|
45
|
-
".
|
|
46
|
-
".
|
|
47
|
-
".
|
|
48
|
-
|
|
63
|
+
".ps1": "powershell",
|
|
64
|
+
".psm1": "powershell",
|
|
65
|
+
".bat": "batch",
|
|
66
|
+
".cmd": "batch",
|
|
67
|
+
// Web
|
|
49
68
|
".html": "html",
|
|
50
69
|
".htm": "html",
|
|
51
70
|
".css": "css",
|
|
52
71
|
".scss": "scss",
|
|
53
72
|
".sass": "sass",
|
|
73
|
+
".less": "less",
|
|
54
74
|
".vue": "vue",
|
|
55
75
|
".svelte": "svelte",
|
|
76
|
+
".astro": "astro",
|
|
77
|
+
// Data/Config
|
|
78
|
+
".json": "json",
|
|
79
|
+
".jsonc": "json",
|
|
80
|
+
".json5": "json",
|
|
81
|
+
".yaml": "yaml",
|
|
82
|
+
".yml": "yaml",
|
|
83
|
+
".toml": "toml",
|
|
84
|
+
".xml": "xml",
|
|
85
|
+
".ini": "ini",
|
|
86
|
+
".cfg": "ini",
|
|
87
|
+
".conf": "ini",
|
|
88
|
+
".properties": "properties",
|
|
89
|
+
".env": "dotenv",
|
|
90
|
+
".env.local": "dotenv",
|
|
91
|
+
".env.example": "dotenv",
|
|
92
|
+
// Documentation
|
|
93
|
+
".md": "markdown",
|
|
94
|
+
".mdx": "markdown",
|
|
95
|
+
".rst": "rst",
|
|
96
|
+
".txt": "text",
|
|
97
|
+
// Database
|
|
98
|
+
".sql": "sql",
|
|
99
|
+
".prisma": "prisma",
|
|
100
|
+
".graphql": "graphql",
|
|
101
|
+
".gql": "graphql",
|
|
102
|
+
// Other
|
|
103
|
+
".dockerfile": "dockerfile",
|
|
104
|
+
".tf": "terraform",
|
|
105
|
+
".hcl": "hcl",
|
|
106
|
+
".proto": "protobuf",
|
|
107
|
+
".sol": "solidity",
|
|
108
|
+
".zig": "zig",
|
|
109
|
+
".nim": "nim",
|
|
110
|
+
".ex": "elixir",
|
|
111
|
+
".exs": "elixir",
|
|
112
|
+
".erl": "erlang",
|
|
113
|
+
".hrl": "erlang",
|
|
114
|
+
".clj": "clojure",
|
|
115
|
+
".cljs": "clojure",
|
|
116
|
+
".cljc": "clojure",
|
|
117
|
+
".hs": "haskell",
|
|
118
|
+
".elm": "elm",
|
|
119
|
+
".dart": "dart",
|
|
120
|
+
".v": "v",
|
|
121
|
+
".asm": "assembly",
|
|
122
|
+
".s": "assembly",
|
|
56
123
|
};
|
|
57
124
|
// Binary file extensions to skip
|
|
58
125
|
const BINARY_EXTENSIONS = new Set([
|
|
@@ -77,10 +144,10 @@ export function loadIgnorePatterns(rootPath) {
|
|
|
77
144
|
try {
|
|
78
145
|
const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
|
|
79
146
|
ig.add(gitignoreContent);
|
|
80
|
-
|
|
147
|
+
console.error(`Loaded .gitignore patterns from ${gitignorePath}`);
|
|
81
148
|
}
|
|
82
149
|
catch (error) {
|
|
83
|
-
|
|
150
|
+
console.error(`Warning: Could not read .gitignore: ${error}`);
|
|
84
151
|
}
|
|
85
152
|
}
|
|
86
153
|
// Load .memoryignore if exists
|
|
@@ -89,19 +156,19 @@ export function loadIgnorePatterns(rootPath) {
|
|
|
89
156
|
try {
|
|
90
157
|
const memoryignoreContent = fs.readFileSync(memoryignorePath, "utf-8");
|
|
91
158
|
ig.add(memoryignoreContent);
|
|
92
|
-
|
|
159
|
+
console.error(`Loaded .memoryignore patterns from ${memoryignorePath}`);
|
|
93
160
|
}
|
|
94
161
|
catch (error) {
|
|
95
|
-
|
|
162
|
+
console.error(`Warning: Could not read .memoryignore: ${error}`);
|
|
96
163
|
}
|
|
97
164
|
}
|
|
98
165
|
return ig;
|
|
99
166
|
}
|
|
100
167
|
/**
|
|
101
|
-
* Calculates SHA-256 hash of file content
|
|
168
|
+
* Calculates SHA-256 hash of file content
|
|
102
169
|
*/
|
|
103
|
-
export
|
|
104
|
-
const content =
|
|
170
|
+
export function calculateFileHash(filePath) {
|
|
171
|
+
const content = fs.readFileSync(filePath);
|
|
105
172
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
106
173
|
}
|
|
107
174
|
/**
|
|
@@ -133,26 +200,39 @@ export function isCodeFile(filePath) {
|
|
|
133
200
|
// Additional checks for files without extension or special cases
|
|
134
201
|
const basename = path.basename(filePath);
|
|
135
202
|
const codeFileNames = new Set([
|
|
203
|
+
// Build/DevOps
|
|
136
204
|
"Makefile", "Dockerfile", "Jenkinsfile", "Vagrantfile",
|
|
137
205
|
"Rakefile", "Gemfile", "Podfile", "Fastfile",
|
|
206
|
+
"CMakeLists.txt", "meson.build", "BUILD", "WORKSPACE",
|
|
207
|
+
// Config files
|
|
208
|
+
".gitignore", ".gitattributes", ".dockerignore",
|
|
209
|
+
".editorconfig", ".prettierrc", ".eslintrc",
|
|
210
|
+
".babelrc", ".browserslistrc",
|
|
211
|
+
"tsconfig.json", "jsconfig.json", "package.json",
|
|
212
|
+
"angular.json", "nest-cli.json", "nx.json",
|
|
213
|
+
"webpack.config.js", "vite.config.js", "rollup.config.js",
|
|
214
|
+
// CI/CD
|
|
215
|
+
".gitlab-ci.yml", ".travis.yml", "azure-pipelines.yml",
|
|
216
|
+
"bitbucket-pipelines.yml", "cloudbuild.yaml",
|
|
217
|
+
// K8s/Helm
|
|
218
|
+
"Chart.yaml", "values.yaml", "kustomization.yaml",
|
|
219
|
+
// Lock files (optional - might want to skip these)
|
|
220
|
+
// "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
|
|
138
221
|
]);
|
|
139
222
|
return codeFileNames.has(basename);
|
|
140
223
|
}
|
|
141
224
|
/**
|
|
142
|
-
* Recursively scans directory for code files
|
|
225
|
+
* Recursively scans directory for code files
|
|
143
226
|
*/
|
|
144
|
-
|
|
227
|
+
function scanDirectoryRecursive(dirPath, rootPath, ig, options, results) {
|
|
145
228
|
let entries;
|
|
146
229
|
try {
|
|
147
|
-
entries =
|
|
230
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
148
231
|
}
|
|
149
232
|
catch (error) {
|
|
150
|
-
|
|
233
|
+
console.error(`Warning: Could not read directory ${dirPath}: ${error}`);
|
|
151
234
|
return;
|
|
152
235
|
}
|
|
153
|
-
// Process subdirectories sequentially to avoid recursion explosion
|
|
154
|
-
// but process files in current directory in parallel
|
|
155
|
-
const filesToProcess = [];
|
|
156
236
|
for (const entry of entries) {
|
|
157
237
|
const fullPath = path.join(dirPath, entry.name);
|
|
158
238
|
const relativePath = path.relative(rootPath, fullPath);
|
|
@@ -167,35 +247,23 @@ async function scanDirectoryRecursive(dirPath, rootPath, ig, options, results, c
|
|
|
167
247
|
}
|
|
168
248
|
if (entry.isDirectory()) {
|
|
169
249
|
if (options.recursive) {
|
|
170
|
-
|
|
250
|
+
scanDirectoryRecursive(fullPath, rootPath, ig, options, results);
|
|
171
251
|
}
|
|
172
252
|
}
|
|
173
253
|
else if (entry.isFile()) {
|
|
174
|
-
filesToProcess.push(entry);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Process files in batches
|
|
178
|
-
for (let i = 0; i < filesToProcess.length; i += concurrencyLimit) {
|
|
179
|
-
const batch = filesToProcess.slice(i, i + concurrencyLimit);
|
|
180
|
-
await Promise.all(batch.map(async (entry) => {
|
|
181
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
182
|
-
// Calculate relative path from PROJECT root (not just scan root)
|
|
183
|
-
// And strictly normalize to forward slashes for consistency
|
|
184
|
-
const relativePathRaw = path.relative(options.projectRoot, fullPath);
|
|
185
|
-
const relativePath = relativePathRaw.split(path.sep).join("/");
|
|
186
254
|
try {
|
|
187
255
|
// Check if it's a code file
|
|
188
256
|
if (!isCodeFile(fullPath)) {
|
|
189
|
-
|
|
257
|
+
continue;
|
|
190
258
|
}
|
|
191
|
-
const stats =
|
|
259
|
+
const stats = fs.statSync(fullPath);
|
|
192
260
|
// Check file size limit
|
|
193
261
|
if (stats.size > options.maxFileSize) {
|
|
194
|
-
|
|
195
|
-
|
|
262
|
+
console.error(`Skipping large file (${stats.size} bytes): ${relativePath}`);
|
|
263
|
+
continue;
|
|
196
264
|
}
|
|
197
265
|
// Calculate hash and collect metadata
|
|
198
|
-
const hash =
|
|
266
|
+
const hash = calculateFileHash(fullPath);
|
|
199
267
|
const language = detectLanguage(fullPath);
|
|
200
268
|
const extension = path.extname(fullPath);
|
|
201
269
|
results.push({
|
|
@@ -209,18 +277,17 @@ async function scanDirectoryRecursive(dirPath, rootPath, ig, options, results, c
|
|
|
209
277
|
});
|
|
210
278
|
}
|
|
211
279
|
catch (error) {
|
|
212
|
-
|
|
280
|
+
console.error(`Warning: Could not process file ${fullPath}: ${error}`);
|
|
213
281
|
}
|
|
214
|
-
}
|
|
282
|
+
}
|
|
215
283
|
}
|
|
216
284
|
}
|
|
217
285
|
/**
|
|
218
|
-
* Scans workspace for code files
|
|
286
|
+
* Scans workspace for code files
|
|
219
287
|
*/
|
|
220
|
-
export
|
|
288
|
+
export function scanFiles(options) {
|
|
221
289
|
const fullOptions = {
|
|
222
290
|
rootPath: options.rootPath,
|
|
223
|
-
projectRoot: options.projectRoot || options.rootPath,
|
|
224
291
|
recursive: options.recursive !== undefined ? options.recursive : true,
|
|
225
292
|
includeHidden: options.includeHidden !== undefined ? options.includeHidden : false,
|
|
226
293
|
maxFileSize: options.maxFileSize || 10 * 1024 * 1024, // 10MB default
|
|
@@ -229,23 +296,23 @@ export async function scanFiles(options) {
|
|
|
229
296
|
if (!fs.existsSync(fullOptions.rootPath)) {
|
|
230
297
|
throw new Error(`Root path does not exist: ${fullOptions.rootPath}`);
|
|
231
298
|
}
|
|
232
|
-
const stats =
|
|
299
|
+
const stats = fs.statSync(fullOptions.rootPath);
|
|
233
300
|
if (!stats.isDirectory()) {
|
|
234
301
|
throw new Error(`Root path is not a directory: ${fullOptions.rootPath}`);
|
|
235
302
|
}
|
|
236
|
-
|
|
303
|
+
console.error(`Scanning files in: ${fullOptions.rootPath}`);
|
|
237
304
|
// Load ignore patterns
|
|
238
305
|
const ig = loadIgnorePatterns(fullOptions.rootPath);
|
|
239
306
|
// Scan files
|
|
240
307
|
const results = [];
|
|
241
|
-
|
|
242
|
-
|
|
308
|
+
scanDirectoryRecursive(fullOptions.rootPath, fullOptions.rootPath, ig, fullOptions, results);
|
|
309
|
+
console.error(`Found ${results.length} code files to index`);
|
|
243
310
|
return results;
|
|
244
311
|
}
|
|
245
312
|
/**
|
|
246
313
|
* Scans a single file and returns its metadata
|
|
247
314
|
*/
|
|
248
|
-
export
|
|
315
|
+
export function scanSingleFile(filePath, rootPath) {
|
|
249
316
|
try {
|
|
250
317
|
if (!fs.existsSync(filePath)) {
|
|
251
318
|
throw new Error(`File does not exist: ${filePath}`);
|
|
@@ -257,12 +324,10 @@ export async function scanSingleFile(filePath, rootPath, projectRoot) {
|
|
|
257
324
|
if (!isCodeFile(filePath)) {
|
|
258
325
|
return null;
|
|
259
326
|
}
|
|
260
|
-
const hash =
|
|
327
|
+
const hash = calculateFileHash(filePath);
|
|
261
328
|
const language = detectLanguage(filePath);
|
|
262
329
|
const extension = path.extname(filePath);
|
|
263
|
-
|
|
264
|
-
const relativePathRaw = path.relative(projectRoot || rootPath, filePath);
|
|
265
|
-
const relativePath = relativePathRaw.split(path.sep).join("/");
|
|
330
|
+
const relativePath = path.relative(rootPath, filePath);
|
|
266
331
|
return {
|
|
267
332
|
path: relativePath,
|
|
268
333
|
absolutePath: filePath,
|
|
@@ -274,7 +339,7 @@ export async function scanSingleFile(filePath, rootPath, projectRoot) {
|
|
|
274
339
|
};
|
|
275
340
|
}
|
|
276
341
|
catch (error) {
|
|
277
|
-
|
|
342
|
+
console.error(`Error scanning file ${filePath}: ${error}`);
|
|
278
343
|
return null;
|
|
279
344
|
}
|
|
280
345
|
}
|