@hsingjui/contextweaver 0.0.2 → 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.
@@ -3,18 +3,20 @@ import {
3
3
  batchUpdateVectorIndexHash,
4
4
  batchUpsertChunkFts,
5
5
  clearVectorIndexHash,
6
- isChunksFtsInitialized,
6
+ isChunksFtsInitialized
7
+ } from "./chunk-VW5RACJC.js";
8
+ import {
7
9
  logger
8
- } from "./chunk-NHKBCYHN.js";
10
+ } from "./chunk-C7XDGBT5.js";
9
11
  import {
10
12
  getEmbeddingConfig
11
- } from "./chunk-2HG4HNNX.js";
13
+ } from "./chunk-SKBAE26T.js";
12
14
 
13
15
  // src/vectorStore/index.ts
14
- import * as lancedb from "@lancedb/lancedb";
15
- import path from "path";
16
- import os from "os";
17
16
  import fs from "fs";
17
+ import os from "os";
18
+ import path from "path";
19
+ import * as lancedb from "@lancedb/lancedb";
18
20
  var BASE_DIR = path.join(os.homedir(), ".contextweaver");
19
21
  var VectorStore = class {
20
22
  db = null;
@@ -49,11 +51,14 @@ var VectorStore = class {
49
51
  if (this.table) return;
50
52
  if (!this.db) throw new Error("VectorStore not initialized");
51
53
  if (records.length === 0) return;
52
- this.table = await this.db.createTable("chunks", records);
54
+ this.table = await this.db.createTable(
55
+ "chunks",
56
+ records
57
+ );
53
58
  }
54
59
  /**
55
60
  * 单调版本更新:先插入新版本,再删除旧版本
56
- *
61
+ *
57
62
  * 这保证了:
58
63
  * - 最坏情况(崩溃)是新旧版本共存(不缺失)
59
64
  * - 正常情况下旧版本被清理
@@ -70,18 +75,20 @@ var VectorStore = class {
70
75
  await this.table.add(records);
71
76
  }
72
77
  if (this.table) {
73
- await this.table.delete(`file_path = '${this.escapeString(filePath)}' AND file_hash != '${this.escapeString(newHash)}'`);
78
+ await this.table.delete(
79
+ `file_path = '${this.escapeString(filePath)}' AND file_hash != '${this.escapeString(newHash)}'`
80
+ );
74
81
  }
75
82
  }
76
83
  /**
77
84
  * 批量 upsert 多个文件(性能优化版,带分批机制)
78
- *
85
+ *
79
86
  * 流程:
80
87
  * 1. 将文件分成小批次(每批最多 BATCH_FILES 个文件或 BATCH_RECORDS 条记录)
81
88
  * 2. 每批执行:插入新 records → 删除旧版本
82
- *
89
+ *
83
90
  * 分批是必要的,因为 LanceDB native 模块在处理超大数据时可能崩溃
84
- *
91
+ *
85
92
  * @param files 文件列表,每个包含 path、hash 和 records
86
93
  */
87
94
  async batchUpsertFiles(files) {
@@ -122,7 +129,9 @@ var VectorStore = class {
122
129
  await this.table.add(batchRecords);
123
130
  }
124
131
  if (this.table && batch.length > 0) {
125
- const deleteConditions = batch.map((f) => `(file_path = '${this.escapeString(f.path)}' AND file_hash != '${this.escapeString(f.hash)}')`).join(" OR ");
132
+ const deleteConditions = batch.map(
133
+ (f) => `(file_path = '${this.escapeString(f.path)}' AND file_hash != '${this.escapeString(f.hash)}')`
134
+ ).join(" OR ");
126
135
  await this.table.delete(deleteConditions);
127
136
  }
128
137
  }
@@ -165,7 +174,7 @@ var VectorStore = class {
165
174
  }
166
175
  /**
167
176
  * 批量获取多个文件的 chunks(性能优化:单次查询替代 N 次循环)
168
- *
177
+ *
169
178
  * 适用于 GraphExpander 扩展、词法召回等需要批量获取的场景
170
179
  * @returns Map<filePath, ChunkRecord[]>,每个文件的 chunks 已按 chunk_index 排序
171
180
  */
@@ -242,9 +251,6 @@ async function closeAllVectorStores() {
242
251
  vectorStores.clear();
243
252
  }
244
253
 
245
- // src/indexer/index.ts
246
- import "better-sqlite3";
247
-
248
254
  // src/api/embedding.ts
249
255
  var ProgressTracker = class {
250
256
  completed = 0;
@@ -391,20 +397,17 @@ var RateLimitController = class {
391
397
  },
392
398
  "\u901F\u7387\u9650\u5236\uFF1A\u89E6\u53D1 429\uFF0C\u6682\u505C\u6240\u6709\u8BF7\u6C42"
393
399
  );
394
- let resumeResolve;
400
+ let resumeResolve = () => {
401
+ };
395
402
  this.pausePromise = new Promise((resolve) => {
396
403
  resumeResolve = resolve;
397
404
  });
398
405
  await sleep(this.backoffMs);
399
406
  this.backoffMs = Math.min(this.maxBackoffMs, this.backoffMs * 2);
400
407
  this.isPaused = false;
401
- const resolvedPromise = this.pausePromise;
402
408
  this.pausePromise = null;
403
409
  resumeResolve();
404
- logger.info(
405
- { waitMs: this.backoffMs },
406
- "\u901F\u7387\u9650\u5236\uFF1A\u6062\u590D\u8BF7\u6C42"
407
- );
410
+ logger.info({ waitMs: this.backoffMs }, "\u901F\u7387\u9650\u5236\uFF1A\u6062\u590D\u8BF7\u6C42");
408
411
  }
409
412
  /**
410
413
  * 获取当前状态(用于调试)
@@ -463,10 +466,13 @@ var EmbeddingClient = class {
463
466
  return batchResults.flat();
464
467
  }
465
468
  /**
466
- * 带速率限制的批次处理
469
+ * 带速率限制和网络错误重试的批次处理
467
470
  * 使用循环而非递归,避免栈溢出和槽位泄漏
468
471
  */
469
472
  async processWithRateLimit(texts, startIndex, progress) {
473
+ const MAX_NETWORK_RETRIES = 3;
474
+ const INITIAL_RETRY_DELAY_MS = 1e3;
475
+ let networkRetries = 0;
470
476
  while (true) {
471
477
  await this.rateLimiter.acquire();
472
478
  try {
@@ -474,17 +480,75 @@ var EmbeddingClient = class {
474
480
  this.rateLimiter.releaseSuccess();
475
481
  return result;
476
482
  } catch (err) {
477
- const isRateLimited = err.message?.includes("429") || err.message?.includes("rate");
483
+ const error = err;
484
+ const errorMessage = error.message || "";
485
+ const isRateLimited = errorMessage.includes("429") || errorMessage.includes("rate");
486
+ const isNetworkError = this.isNetworkError(err);
478
487
  if (isRateLimited) {
479
488
  this.rateLimiter.releaseForRetry();
480
489
  await this.rateLimiter.triggerRateLimit();
490
+ networkRetries = 0;
491
+ } else if (isNetworkError && networkRetries < MAX_NETWORK_RETRIES) {
492
+ networkRetries++;
493
+ const delayMs = INITIAL_RETRY_DELAY_MS * 2 ** (networkRetries - 1);
494
+ logger.warn(
495
+ {
496
+ error: errorMessage,
497
+ retry: networkRetries,
498
+ maxRetries: MAX_NETWORK_RETRIES,
499
+ delayMs
500
+ },
501
+ "\u7F51\u7EDC\u9519\u8BEF\uFF0C\u51C6\u5907\u91CD\u8BD5"
502
+ );
503
+ this.rateLimiter.releaseForRetry();
504
+ await sleep(delayMs);
481
505
  } else {
482
506
  this.rateLimiter.releaseFailure();
507
+ if (isNetworkError) {
508
+ logger.error({ error: errorMessage, retries: networkRetries }, "\u7F51\u7EDC\u9519\u8BEF\u91CD\u8BD5\u6B21\u6570\u8017\u5C3D");
509
+ }
483
510
  throw err;
484
511
  }
485
512
  }
486
513
  }
487
514
  }
515
+ /**
516
+ * 判断是否为网络错误
517
+ *
518
+ * 常见网络错误类型:
519
+ * - terminated: 连接被中断(TLS 断开)
520
+ * - ECONNRESET: 连接被远端重置
521
+ * - ETIMEDOUT: 连接超时
522
+ * - ENOTFOUND: DNS 解析失败
523
+ * - fetch failed: 通用 fetch 失败
524
+ * - socket hang up: 套接字意外关闭
525
+ */
526
+ isNetworkError(err) {
527
+ const error = err;
528
+ const message = (error.message || "").toLowerCase();
529
+ const code = error.code || "";
530
+ const networkErrorPatterns = [
531
+ "terminated",
532
+ "econnreset",
533
+ "etimedout",
534
+ "enotfound",
535
+ "econnrefused",
536
+ "fetch failed",
537
+ "socket hang up",
538
+ "network",
539
+ "aborted"
540
+ ];
541
+ for (const pattern of networkErrorPatterns) {
542
+ if (message.includes(pattern)) {
543
+ return true;
544
+ }
545
+ }
546
+ const networkErrorCodes = ["ECONNRESET", "ETIMEDOUT", "ENOTFOUND", "ECONNREFUSED", "EPIPE"];
547
+ if (networkErrorCodes.includes(code)) {
548
+ return true;
549
+ }
550
+ return false;
551
+ }
488
552
  /**
489
553
  * 处理单个批次(单次请求,不含重试逻辑)
490
554
  */
@@ -558,7 +622,7 @@ var Indexer = class {
558
622
  }
559
623
  /**
560
624
  * 处理扫描结果,更新向量索引
561
- *
625
+ *
562
626
  * @param db SQLite 数据库实例
563
627
  * @param results 文件处理结果
564
628
  */
@@ -611,14 +675,19 @@ var Indexer = class {
611
675
  stats.errors = indexResult.errors;
612
676
  }
613
677
  logger.info(
614
- { indexed: stats.indexed, deleted: stats.deleted, errors: stats.errors, skipped: stats.skipped },
678
+ {
679
+ indexed: stats.indexed,
680
+ deleted: stats.deleted,
681
+ errors: stats.errors,
682
+ skipped: stats.skipped
683
+ },
615
684
  "\u5411\u91CF\u7D22\u5F15\u5B8C\u6210"
616
685
  );
617
686
  return stats;
618
687
  }
619
688
  /**
620
689
  * 批量索引文件(性能优化版)
621
- *
690
+ *
622
691
  * 优化策略:
623
692
  * 1. Embedding 已批量化(原有)
624
693
  * 2. LanceDB 写入批量化:N 次 upsertFile → 1 次 batchUpsertFiles
@@ -649,8 +718,12 @@ var Indexer = class {
649
718
  const results = await this.embeddingClient.embedBatch(allTexts);
650
719
  embeddings = results.map((r) => r.embedding);
651
720
  } catch (err) {
652
- logger.error({ error: err.message, stack: err.stack }, "Embedding \u5931\u8D25");
653
- clearVectorIndexHash(db, files.map((f) => f.path));
721
+ const error = err;
722
+ logger.error({ error: error.message, stack: error.stack }, "Embedding \u5931\u8D25");
723
+ clearVectorIndexHash(
724
+ db,
725
+ files.map((f) => f.path)
726
+ );
654
727
  return { success: 0, errors: files.length };
655
728
  }
656
729
  const filesToUpsert = [];
@@ -690,23 +763,35 @@ var Indexer = class {
690
763
  filePath: record.file_path,
691
764
  chunkIndex: record.chunk_index,
692
765
  breadcrumb: record.breadcrumb,
693
- content: record.breadcrumb + "\n" + record.display_code
766
+ content: `${record.breadcrumb}
767
+ ${record.display_code}`
694
768
  });
695
769
  }
696
770
  filesToUpsert.push({ path: file.path, hash: file.hash, records });
697
771
  successFiles.push({ path: file.path, hash: file.hash });
698
772
  } catch (err) {
699
- logger.error({ path: file.path, error: err.message, stack: err.stack }, "\u7EC4\u88C5 ChunkRecord \u5931\u8D25");
773
+ const error = err;
774
+ logger.error(
775
+ { path: file.path, error: error.message, stack: error.stack },
776
+ "\u7EC4\u88C5 ChunkRecord \u5931\u8D25"
777
+ );
700
778
  errorFiles.push(file.path);
701
779
  }
702
780
  }
703
781
  if (filesToUpsert.length > 0) {
704
782
  try {
705
- await this.vectorStore.batchUpsertFiles(filesToUpsert);
706
- logger.info({ files: filesToUpsert.length, chunks: allFtsChunks.length }, "LanceDB \u6279\u91CF\u5199\u5165\u5B8C\u6210");
783
+ await this.vectorStore?.batchUpsertFiles(filesToUpsert);
784
+ logger.info(
785
+ { files: filesToUpsert.length, chunks: allFtsChunks.length },
786
+ "LanceDB \u6279\u91CF\u5199\u5165\u5B8C\u6210"
787
+ );
707
788
  } catch (err) {
708
- logger.error({ error: err.message, stack: err.stack }, "LanceDB \u6279\u91CF\u5199\u5165\u5931\u8D25");
709
- clearVectorIndexHash(db, files.map((f) => f.path));
789
+ const error = err;
790
+ logger.error({ error: error.message, stack: error.stack }, "LanceDB \u6279\u91CF\u5199\u5165\u5931\u8D25");
791
+ clearVectorIndexHash(
792
+ db,
793
+ files.map((f) => f.path)
794
+ );
710
795
  return { success: 0, errors: files.length };
711
796
  }
712
797
  }
@@ -715,18 +800,19 @@ var Indexer = class {
715
800
  const pathsToDelete = filesToUpsert.map((f) => f.path);
716
801
  batchDeleteFileChunksFts(db, pathsToDelete);
717
802
  batchUpsertChunkFts(db, allFtsChunks);
718
- logger.info({ files: pathsToDelete.length, chunks: allFtsChunks.length }, "FTS \u6279\u91CF\u66F4\u65B0\u5B8C\u6210");
803
+ logger.info(
804
+ { files: pathsToDelete.length, chunks: allFtsChunks.length },
805
+ "FTS \u6279\u91CF\u66F4\u65B0\u5B8C\u6210"
806
+ );
719
807
  } catch (err) {
720
- logger.warn({ error: err.message }, "FTS \u6279\u91CF\u66F4\u65B0\u5931\u8D25\uFF08\u5411\u91CF\u7D22\u5F15\u5DF2\u6210\u529F\uFF09");
808
+ const error = err;
809
+ logger.warn({ error: error.message }, "FTS \u6279\u91CF\u66F4\u65B0\u5931\u8D25\uFF08\u5411\u91CF\u7D22\u5F15\u5DF2\u6210\u529F\uFF09");
721
810
  }
722
811
  }
723
812
  if (successFiles.length > 0) {
724
813
  batchUpdateVectorIndexHash(db, successFiles);
725
814
  }
726
- logger.info(
727
- { success: successFiles.length, errors: errorFiles.length },
728
- "\u6279\u91CF\u7D22\u5F15\u5B8C\u6210"
729
- );
815
+ logger.info({ success: successFiles.length, errors: errorFiles.length }, "\u6279\u91CF\u7D22\u5F15\u5B8C\u6210");
730
816
  return { success: successFiles.length, errors: errorFiles.length };
731
817
  }
732
818
  /**
@@ -747,7 +833,7 @@ var Indexer = class {
747
833
  if (!this.vectorStore) {
748
834
  await this.init();
749
835
  }
750
- return this.vectorStore.search(queryVector, limit, filter);
836
+ return this.vectorStore?.search(queryVector, limit, filter);
751
837
  }
752
838
  /**
753
839
  * 文本搜索(先 embedding 再向量搜索)
@@ -763,7 +849,7 @@ var Indexer = class {
763
849
  if (!this.vectorStore) {
764
850
  await this.init();
765
851
  }
766
- await this.vectorStore.clear();
852
+ await this.vectorStore?.clear();
767
853
  }
768
854
  /**
769
855
  * 获取索引统计
@@ -772,7 +858,7 @@ var Indexer = class {
772
858
  if (!this.vectorStore) {
773
859
  await this.init();
774
860
  }
775
- const count = await this.vectorStore.count();
861
+ const count = await this.vectorStore?.count() ?? 0;
776
862
  return { totalChunks: count };
777
863
  }
778
864
  };
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  codebaseRetrievalSchema,
3
3
  handleCodebaseRetrieval
4
- } from "./chunk-C7PBP6HL.js";
5
- import "./chunk-NHKBCYHN.js";
6
- import "./chunk-2HG4HNNX.js";
4
+ } from "./chunk-O3HDM3CF.js";
5
+ import "./chunk-VW5RACJC.js";
6
+ import "./chunk-C7XDGBT5.js";
7
+ import "./chunk-SKBAE26T.js";
7
8
  export {
8
9
  codebaseRetrievalSchema,
9
10
  handleCodebaseRetrieval
@@ -1,18 +1,18 @@
1
1
  import {
2
- DEFAULT_EXCLUDE_PATTERNS,
3
2
  checkEmbeddingEnv,
4
3
  checkRerankerEnv,
5
4
  getEmbeddingConfig,
6
5
  getExcludePatterns,
7
6
  getRerankerConfig,
8
- isDev
9
- } from "./chunk-2HG4HNNX.js";
7
+ isDev,
8
+ isMcpMode
9
+ } from "./chunk-SKBAE26T.js";
10
10
  export {
11
- DEFAULT_EXCLUDE_PATTERNS,
12
11
  checkEmbeddingEnv,
13
12
  checkRerankerEnv,
14
13
  getEmbeddingConfig,
15
14
  getExcludePatterns,
16
15
  getRerankerConfig,
17
- isDev
16
+ isDev,
17
+ isMcpMode
18
18
  };
package/dist/index.js CHANGED
@@ -1,19 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  scan
4
- } from "./chunk-JNMCEE3A.js";
5
- import "./chunk-UWQSUTEL.js";
4
+ } from "./chunk-QWQ64TBE.js";
5
+ import "./chunk-WWYSLCNZ.js";
6
+ import {
7
+ generateProjectId
8
+ } from "./chunk-VW5RACJC.js";
6
9
  import {
7
- generateProjectId,
8
10
  logger
9
- } from "./chunk-NHKBCYHN.js";
10
- import "./chunk-2HG4HNNX.js";
11
+ } from "./chunk-C7XDGBT5.js";
12
+ import "./chunk-SKBAE26T.js";
11
13
 
12
14
  // src/index.ts
13
- import cac from "cac";
14
15
  import { promises as fs } from "fs";
15
- import path from "path";
16
16
  import os from "os";
17
+ import path from "path";
18
+ import cac from "cac";
17
19
  var cli = cac("contextweaver");
18
20
  cli.command("init", "\u521D\u59CB\u5316 ContextWeaver \u914D\u7F6E").action(async () => {
19
21
  const configDir = path.join(os.homedir(), ".contextweaver");
@@ -23,8 +25,9 @@ cli.command("init", "\u521D\u59CB\u5316 ContextWeaver \u914D\u7F6E").action(asyn
23
25
  await fs.mkdir(configDir, { recursive: true });
24
26
  logger.info(`\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55: ${configDir}`);
25
27
  } catch (err) {
26
- if (err.code !== "EEXIST") {
27
- logger.error({ err, stack: err.stack }, `\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55\u5931\u8D25: ${err.message}`);
28
+ const error = err;
29
+ if (error.code !== "EEXIST") {
30
+ logger.error({ err, stack: error.stack }, `\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55\u5931\u8D25: ${error.message}`);
28
31
  process.exit(1);
29
32
  }
30
33
  logger.info(`\u914D\u7F6E\u76EE\u5F55\u5DF2\u5B58\u5728: ${configDir}`);
@@ -58,7 +61,8 @@ RERANK_TOP_N=20
58
61
  await fs.writeFile(envFile, defaultEnvContent);
59
62
  logger.info(`\u521B\u5EFA .env \u6587\u4EF6: ${envFile}`);
60
63
  } catch (err) {
61
- logger.error({ err, stack: err.stack }, `\u521B\u5EFA .env \u6587\u4EF6\u5931\u8D25: ${err.message}`);
64
+ const error = err;
65
+ logger.error({ err, stack: error.stack }, `\u521B\u5EFA .env \u6587\u4EF6\u5931\u8D25: ${error.message}`);
62
66
  process.exit(1);
63
67
  }
64
68
  logger.info("\u4E0B\u4E00\u6B65\u64CD\u4F5C:");
@@ -80,7 +84,7 @@ cli.command("index [path]", "\u626B\u63CF\u4EE3\u7801\u5E93\u5E76\u5EFA\u7ACB\u7
80
84
  force: options.force,
81
85
  onProgress: (current, total) => {
82
86
  const percent = (current / total * 100).toFixed(1);
83
- process.stdout.write(`\r\u8FDB\u5EA6: ${current}/${total} (${percent}%)`);
87
+ logger.info({ current, total, percent: `${percent}%` }, "\u626B\u63CF\u8FDB\u5EA6");
84
88
  }
85
89
  });
86
90
  process.stdout.write("\n");
@@ -95,36 +99,47 @@ cli.command("index [path]", "\u626B\u63CF\u4EE3\u7801\u5E93\u5E76\u5EFA\u7ACB\u7
95
99
  logger.info(`\u8DF3\u8FC7: ${stats.skipped}`);
96
100
  logger.info(`\u9519\u8BEF: ${stats.errors}`);
97
101
  } catch (err) {
98
- logger.error({ err, stack: err.stack }, `\u626B\u63CF\u5931\u8D25: ${err.message}`);
102
+ const error = err;
103
+ logger.error({ err, stack: error.stack }, `\u626B\u63CF\u5931\u8D25: ${error.message}`);
99
104
  process.exit(1);
100
105
  }
101
106
  });
102
107
  cli.command("mcp", "\u542F\u52A8 MCP \u670D\u52A1\u5668").action(async () => {
103
- const { startMcpServer } = await import("./server-Y4GFTGLG.js");
108
+ const { startMcpServer } = await import("./server-7PYHHTOM.js");
104
109
  try {
105
110
  await startMcpServer();
106
- } catch (error) {
107
- logger.error({ error: error.message, stack: error.stack }, `MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${error.message}`);
111
+ } catch (err) {
112
+ const error = err;
113
+ logger.error(
114
+ { error: error.message, stack: error.stack },
115
+ `MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${error.message}`
116
+ );
108
117
  process.exit(1);
109
118
  }
110
119
  });
111
- cli.command("search", "\u672C\u5730\u68C0\u7D22\uFF08\u53C2\u6570\u5BF9\u9F50 MCP\uFF09").option("--repo-path <path>", "\u4EE3\u7801\u5E93\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09").option("--information-request <text>", "\u81EA\u7136\u8BED\u8A00\u95EE\u9898\u63CF\u8FF0\uFF08\u5FC5\u586B\uFF09").option("--technical-terms <terms>", "\u7CBE\u786E\u672F\u8BED\uFF08\u9017\u53F7\u5206\u9694\uFF09").option("--zen", "\u4F7F\u7528 MCP Zen \u914D\u7F6E\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF09").action(async (options) => {
112
- const repoPath = options.repoPath ? path.resolve(options.repoPath) : process.cwd();
113
- const informationRequest = options.informationRequest;
114
- if (!informationRequest) {
115
- logger.error("\u7F3A\u5C11 --information-request");
116
- process.exit(1);
120
+ cli.command("search", "\u672C\u5730\u68C0\u7D22\uFF08\u53C2\u6570\u5BF9\u9F50 MCP\uFF09").option("--repo-path <path>", "\u4EE3\u7801\u5E93\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09").option("--information-request <text>", "\u81EA\u7136\u8BED\u8A00\u95EE\u9898\u63CF\u8FF0\uFF08\u5FC5\u586B\uFF09").option("--technical-terms <terms>", "\u7CBE\u786E\u672F\u8BED\uFF08\u9017\u53F7\u5206\u9694\uFF09").option("--zen", "\u4F7F\u7528 MCP Zen \u914D\u7F6E\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF09").action(
121
+ async (options) => {
122
+ const repoPath = options.repoPath ? path.resolve(options.repoPath) : process.cwd();
123
+ const informationRequest = options.informationRequest;
124
+ if (!informationRequest) {
125
+ logger.error("\u7F3A\u5C11 --information-request");
126
+ process.exit(1);
127
+ }
128
+ const technicalTerms = (options.technicalTerms || "").split(",").map((t) => t.trim()).filter(Boolean);
129
+ const useZen = options.zen !== false;
130
+ const { handleCodebaseRetrieval } = await import("./codebaseRetrieval-AV4GK6FT.js");
131
+ const response = await handleCodebaseRetrieval(
132
+ {
133
+ repo_path: repoPath,
134
+ information_request: informationRequest,
135
+ technical_terms: technicalTerms.length > 0 ? technicalTerms : void 0
136
+ },
137
+ useZen ? void 0 : {}
138
+ );
139
+ const text = response.content.map((item) => item.text).join("\n");
140
+ process.stdout.write(`${text}
141
+ `);
117
142
  }
118
- const technicalTerms = (options.technicalTerms || "").split(",").map((t) => t.trim()).filter(Boolean);
119
- const useZen = options.zen !== false;
120
- const { handleCodebaseRetrieval } = await import("./codebaseRetrieval-GPSIAFSZ.js");
121
- const response = await handleCodebaseRetrieval({
122
- repo_path: repoPath,
123
- information_request: informationRequest,
124
- technical_terms: technicalTerms.length > 0 ? technicalTerms : void 0
125
- }, useZen ? void 0 : {});
126
- const text = response.content.map((item) => item.text).join("\n");
127
- process.stdout.write(text + "\n");
128
- });
143
+ );
129
144
  cli.help();
130
145
  cli.parse();
@@ -0,0 +1,106 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-C7XDGBT5.js";
4
+ import "./chunk-SKBAE26T.js";
5
+
6
+ // src/utils/lock.ts
7
+ import fs from "fs";
8
+ import os from "os";
9
+ import path from "path";
10
+ var BASE_DIR = path.join(os.homedir(), ".contextweaver");
11
+ var LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
12
+ var LOCK_CHECK_INTERVAL_MS = 100;
13
+ function getLockFilePath(projectId) {
14
+ return path.join(BASE_DIR, projectId, "index.lock");
15
+ }
16
+ function isLockValid(lockPath) {
17
+ try {
18
+ if (!fs.existsSync(lockPath)) {
19
+ return false;
20
+ }
21
+ const content = fs.readFileSync(lockPath, "utf-8");
22
+ const lockInfo = JSON.parse(content);
23
+ if (Date.now() - lockInfo.timestamp > LOCK_TIMEOUT_MS) {
24
+ logger.warn({ lockInfo, lockPath }, "\u9501\u5DF2\u8D85\u65F6");
25
+ return false;
26
+ }
27
+ try {
28
+ process.kill(lockInfo.pid, 0);
29
+ return true;
30
+ } catch {
31
+ logger.warn({ pid: lockInfo.pid }, "\u6301\u6709\u9501\u7684\u8FDB\u7A0B\u5DF2\u6B7B\u4EA1");
32
+ return false;
33
+ }
34
+ } catch (err) {
35
+ const error = err;
36
+ logger.debug({ error: error.message }, "\u8BFB\u53D6\u9501\u6587\u4EF6\u5931\u8D25");
37
+ return false;
38
+ }
39
+ }
40
+ async function acquireLock(projectId, operation, timeoutMs = 3e4) {
41
+ const lockPath = getLockFilePath(projectId);
42
+ const dir = path.dirname(lockPath);
43
+ if (!fs.existsSync(dir)) {
44
+ fs.mkdirSync(dir, { recursive: true });
45
+ }
46
+ const startTime = Date.now();
47
+ while (Date.now() - startTime < timeoutMs) {
48
+ if (!isLockValid(lockPath)) {
49
+ try {
50
+ const lockInfo = {
51
+ pid: process.pid,
52
+ timestamp: Date.now(),
53
+ operation
54
+ };
55
+ fs.writeFileSync(lockPath, JSON.stringify(lockInfo), { flag: "w" });
56
+ const verifyContent = fs.readFileSync(lockPath, "utf-8");
57
+ const verifyInfo = JSON.parse(verifyContent);
58
+ if (verifyInfo.pid === process.pid) {
59
+ logger.debug({ projectId: projectId.slice(0, 10), operation }, "\u83B7\u53D6\u9501\u6210\u529F");
60
+ return true;
61
+ }
62
+ } catch (err) {
63
+ const error = err;
64
+ logger.debug({ error: error.message }, "\u83B7\u53D6\u9501\u5931\u8D25\uFF0C\u91CD\u8BD5\u4E2D...");
65
+ }
66
+ } else {
67
+ logger.debug({ projectId: projectId.slice(0, 10) }, "\u7B49\u5F85\u9501\u91CA\u653E...");
68
+ }
69
+ await new Promise((resolve) => setTimeout(resolve, LOCK_CHECK_INTERVAL_MS));
70
+ }
71
+ logger.warn({ projectId: projectId.slice(0, 10), timeoutMs }, "\u83B7\u53D6\u9501\u8D85\u65F6");
72
+ return false;
73
+ }
74
+ function releaseLock(projectId) {
75
+ const lockPath = getLockFilePath(projectId);
76
+ try {
77
+ if (!fs.existsSync(lockPath)) {
78
+ return;
79
+ }
80
+ const content = fs.readFileSync(lockPath, "utf-8");
81
+ const lockInfo = JSON.parse(content);
82
+ if (lockInfo.pid === process.pid) {
83
+ fs.unlinkSync(lockPath);
84
+ logger.debug({ projectId: projectId.slice(0, 10) }, "\u91CA\u653E\u9501\u6210\u529F");
85
+ } else {
86
+ logger.warn({ ownPid: process.pid, lockPid: lockInfo.pid }, "\u5C1D\u8BD5\u91CA\u653E\u975E\u81EA\u5DF1\u6301\u6709\u7684\u9501");
87
+ }
88
+ } catch (err) {
89
+ const error = err;
90
+ logger.debug({ error: error.message }, "\u91CA\u653E\u9501\u65F6\u51FA\u9519");
91
+ }
92
+ }
93
+ async function withLock(projectId, operation, fn, timeoutMs = 3e4) {
94
+ const acquired = await acquireLock(projectId, operation, timeoutMs);
95
+ if (!acquired) {
96
+ throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u9879\u76EE\u9501 (${projectId.slice(0, 10)})\uFF0C\u5176\u4ED6\u8FDB\u7A0B\u6B63\u5728\u64CD\u4F5C\u7D22\u5F15`);
97
+ }
98
+ try {
99
+ return await fn();
100
+ } finally {
101
+ releaseLock(projectId);
102
+ }
103
+ }
104
+ export {
105
+ withLock
106
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ scan
3
+ } from "./chunk-QWQ64TBE.js";
4
+ import "./chunk-WWYSLCNZ.js";
5
+ import "./chunk-VW5RACJC.js";
6
+ import "./chunk-C7XDGBT5.js";
7
+ import "./chunk-SKBAE26T.js";
8
+ export {
9
+ scan
10
+ };
@@ -1,19 +1,17 @@
1
1
  import {
2
2
  codebaseRetrievalSchema,
3
3
  handleCodebaseRetrieval
4
- } from "./chunk-C7PBP6HL.js";
4
+ } from "./chunk-O3HDM3CF.js";
5
+ import "./chunk-VW5RACJC.js";
5
6
  import {
6
7
  logger
7
- } from "./chunk-NHKBCYHN.js";
8
- import "./chunk-2HG4HNNX.js";
8
+ } from "./chunk-C7XDGBT5.js";
9
+ import "./chunk-SKBAE26T.js";
9
10
 
10
11
  // src/mcp/server.ts
11
12
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
12
13
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
- import {
14
- CallToolRequestSchema,
15
- ListToolsRequestSchema
16
- } from "@modelcontextprotocol/sdk/types.js";
14
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
17
15
  var SERVER_NAME = "contextweaver";
18
16
  var TOOLS = [
19
17
  {
@@ -108,7 +106,8 @@ async function startMcpServer() {
108
106
  default:
109
107
  throw new Error(`Unknown tool: ${name}`);
110
108
  }
111
- } catch (error) {
109
+ } catch (err) {
110
+ const error = err;
112
111
  logger.error({ error: error.message, stack: error.stack, tool: name }, "\u5DE5\u5177\u8C03\u7528\u5931\u8D25");
113
112
  return {
114
113
  content: [