@bgicli/bgicli 2.5.0 → 2.7.0

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.
@@ -0,0 +1,73 @@
1
+ ---
2
+ id: web-search
3
+ name: Web Search (网络搜索)
4
+ category: utility
5
+ short-description: Search the internet using DuckDuckGo API and curl/Python for any query.
6
+ ---
7
+
8
+ # Web Search 网络搜索
9
+
10
+ 当用户需要搜索互联网、查找最新信息、核实事实、或查询非文献数据库内容时,使用以下方法通过 bash 工具执行网络搜索。
11
+
12
+ ## 规则
13
+ - 直接搜索,不要事先询问"是否需要搜索"
14
+ - 搜索到内容后,整合总结后再回复,不要直接粘贴原始 JSON
15
+ - 如果首次搜索结果不理想,换关键词重试一次
16
+ - 中文问题优先用中文关键词;专业术语用英文效果更好
17
+
18
+ ## 方法一:DuckDuckGo Instant Answer(无需 API Key,适合快速摘要)
19
+
20
+ ```bash
21
+ python3 - <<'EOF'
22
+ import urllib.request, urllib.parse, json, sys
23
+
24
+ query = "YOUR_QUERY_HERE"
25
+ url = "https://api.duckduckgo.com/?q=" + urllib.parse.quote(query) + "&format=json&no_html=1&skip_disambig=1"
26
+ try:
27
+ with urllib.request.urlopen(url, timeout=10) as r:
28
+ d = json.loads(r.read())
29
+ if d.get("AbstractText"):
30
+ print("摘要:", d["AbstractText"])
31
+ print("来源:", d.get("AbstractURL", ""))
32
+ topics = [t for t in d.get("RelatedTopics", []) if isinstance(t, dict) and t.get("Text")]
33
+ for t in topics[:6]:
34
+ print("•", t["Text"])
35
+ if t.get("FirstURL"):
36
+ print(" ", t["FirstURL"])
37
+ except Exception as e:
38
+ print("搜索失败:", e, file=sys.stderr)
39
+ EOF
40
+ ```
41
+
42
+ ## 方法二:获取并解析具体网页内容
43
+
44
+ ```bash
45
+ python3 - <<'EOF'
46
+ import urllib.request, re, sys
47
+
48
+ url = "https://TARGET_URL_HERE"
49
+ try:
50
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
51
+ with urllib.request.urlopen(req, timeout=15) as r:
52
+ html = r.read().decode("utf-8", errors="ignore")
53
+ text = re.sub(r"<script[^>]*>[\s\S]*?</script>", "", html, flags=re.I)
54
+ text = re.sub(r"<style[^>]*>[\s\S]*?</style>", "", text, flags=re.I)
55
+ text = re.sub(r"<[^>]+>", " ", text)
56
+ text = re.sub(r"\s{3,}", "\n\n", text)
57
+ print(text[:4000])
58
+ except Exception as e:
59
+ print("获取失败:", e, file=sys.stderr)
60
+ EOF
61
+ ```
62
+
63
+ ## 方法三:curl 快速搜索
64
+
65
+ ```bash
66
+ curl -sA "Mozilla/5.0" "https://api.duckduckgo.com/?q=QUERY&format=json&no_html=1" \
67
+ | python3 -c "
68
+ import json,sys
69
+ d=json.load(sys.stdin)
70
+ print(d.get('AbstractText',''))
71
+ [print('•',t['Text']) for t in d.get('RelatedTopics',[])[:5] if isinstance(t,dict) and t.get('Text')]
72
+ "
73
+ ```
package/dist/bgi.js CHANGED
@@ -13603,13 +13603,14 @@ var DEFAULT_PROVIDER = "bailian";
13603
13603
  // src/config.ts
13604
13604
  var BGI_DIR = (0, import_path2.join)((0, import_os.homedir)(), ".bgicli");
13605
13605
  var TOOLS_DIR = (0, import_path2.join)(BGI_DIR, "tools");
13606
- var SKILLS_DIR = (0, import_path2.join)(BGI_DIR, "skills");
13606
+ var BIO_SKILLS_DIR = (0, import_path2.join)(BGI_DIR, "bio-skills");
13607
+ var SKILLS_DIR = BIO_SKILLS_DIR;
13607
13608
  var USER_SKILLS_DIR = (0, import_path2.join)(BGI_DIR, "user-skills");
13608
13609
  var DATABASES_FILE = (0, import_path2.join)(BGI_DIR, "databases.json");
13609
13610
  var DATA_VERSION_FILE = (0, import_path2.join)(BGI_DIR, ".data-version");
13610
13611
  var CONFIG_FILE = (0, import_path2.join)(BGI_DIR, "config.json");
13611
13612
  function ensureDirs() {
13612
- for (const dir of [BGI_DIR, TOOLS_DIR, SKILLS_DIR, USER_SKILLS_DIR]) {
13613
+ for (const dir of [BGI_DIR, TOOLS_DIR, BIO_SKILLS_DIR, USER_SKILLS_DIR]) {
13613
13614
  if (!(0, import_fs2.existsSync)(dir)) (0, import_fs2.mkdirSync)(dir, { recursive: true });
13614
13615
  }
13615
13616
  }
@@ -13619,12 +13620,18 @@ function loadConfig() {
13619
13620
  const def = {
13620
13621
  provider: DEFAULT_PROVIDER,
13621
13622
  model: PROVIDERS[DEFAULT_PROVIDER].defaultModel,
13622
- apiKeys: {}
13623
+ apiKeys: {},
13624
+ permanentSkills: ["web-search"]
13623
13625
  };
13624
13626
  saveConfig(def);
13625
13627
  return def;
13626
13628
  }
13627
- return JSON.parse((0, import_fs2.readFileSync)(CONFIG_FILE, "utf8"));
13629
+ const cfg = JSON.parse((0, import_fs2.readFileSync)(CONFIG_FILE, "utf8"));
13630
+ if (!cfg.permanentSkills) {
13631
+ cfg.permanentSkills = ["web-search"];
13632
+ saveConfig(cfg);
13633
+ }
13634
+ return cfg;
13628
13635
  }
13629
13636
  function saveConfig(cfg) {
13630
13637
  ensureDirs();
@@ -17193,7 +17200,7 @@ function clearCheckpoints(sessionId) {
17193
17200
 
17194
17201
  // src/index.ts
17195
17202
  var import_fs7 = require("fs");
17196
- var VERSION2 = "2.5.0";
17203
+ var VERSION2 = "2.7.0";
17197
17204
  var BRAND2 = "bgi".toLowerCase();
17198
17205
  var SKILLHUB_HUBS = {
17199
17206
  bgi: { label: "BGI\u5185\u7F51", apiBase: "https://clawhub.ai", backend: "clawhub" },
@@ -17337,6 +17344,10 @@ var SESSION_CTX = {
17337
17344
  };
17338
17345
  var dbRegistry = { version: 1, lastScan: null, databases: {} };
17339
17346
  var systemPrompt = "";
17347
+ var debugMode = false;
17348
+ var debugFilePath = "";
17349
+ var debugRound = 0;
17350
+ var permanentSkillIds = /* @__PURE__ */ new Set();
17340
17351
  function installBundledData() {
17341
17352
  const bundledData = (0, import_path6.join)(__dirname, "..", "data");
17342
17353
  if (!(0, import_fs6.existsSync)(bundledData)) return;
@@ -17346,7 +17357,7 @@ function installBundledData() {
17346
17357
  try {
17347
17358
  for (const entry of (0, import_fs6.readdirSync)(legacyWorkflowsDir)) {
17348
17359
  const src = (0, import_path6.join)(legacyWorkflowsDir, entry);
17349
- const dest = (0, import_path6.join)(SKILLS_DIR, entry);
17360
+ const dest = (0, import_path6.join)(BIO_SKILLS_DIR, entry);
17350
17361
  if ((0, import_fs6.statSync)(src).isDirectory() && !(0, import_fs6.existsSync)(dest)) {
17351
17362
  (0, import_fs6.cpSync)(src, dest, { recursive: true });
17352
17363
  }
@@ -17355,10 +17366,18 @@ function installBundledData() {
17355
17366
  } catch {
17356
17367
  }
17357
17368
  }
17369
+ const legacySkillsDir = (0, import_path6.join)(BGI_DIR, "skills");
17370
+ if ((0, import_fs6.existsSync)(legacySkillsDir) && !(0, import_fs6.existsSync)(BIO_SKILLS_DIR)) {
17371
+ try {
17372
+ (0, import_fs6.cpSync)(legacySkillsDir, BIO_SKILLS_DIR, { recursive: true });
17373
+ (0, import_fs6.rmSync)(legacySkillsDir, { recursive: true, force: true });
17374
+ } catch {
17375
+ }
17376
+ }
17358
17377
  const installedDataVersion = (0, import_fs6.existsSync)(DATA_VERSION_FILE) ? (0, import_fs6.readFileSync)(DATA_VERSION_FILE, "utf8").trim() : "";
17359
17378
  const needsUpdate = installedDataVersion !== VERSION2;
17360
17379
  const targets = [
17361
- { src: (0, import_path6.join)(bundledData, "skills"), dest: SKILLS_DIR, name: "Skills" },
17380
+ { src: (0, import_path6.join)(bundledData, "skills"), dest: BIO_SKILLS_DIR, name: "\u751F\u7269\u4FE1\u606F Skills" },
17362
17381
  { src: (0, import_path6.join)(bundledData, "tools"), dest: TOOLS_DIR, name: "\u5DE5\u5177" }
17363
17382
  ];
17364
17383
  let installed = false;
@@ -17579,8 +17598,8 @@ function collectAllSkills() {
17579
17598
  }
17580
17599
  });
17581
17600
  };
17582
- addFrom(SKILLS_DIR, "skill");
17583
- addFrom(USER_SKILLS_DIR, "user");
17601
+ addFrom(BIO_SKILLS_DIR, "bio");
17602
+ addFrom(USER_SKILLS_DIR, "downloaded");
17584
17603
  return entries;
17585
17604
  }
17586
17605
  function listSkills(keyword) {
@@ -17668,6 +17687,14 @@ function parseSkillMeta(content) {
17668
17687
  };
17669
17688
  }
17670
17689
  async function injectSkill(id, history, injectedSkills, rl, skipConfirm = false) {
17690
+ if (injectedSkills.has(id)) {
17691
+ if (permanentSkillIds.has(id)) {
17692
+ console.log(source_default.dim(` [${id}] \u662F\u5E38\u9A7B\u6280\u80FD\uFF0C\u5DF2\u81EA\u52A8\u52A0\u8F7D\u5230\u7CFB\u7EDF\u63D0\u793A\u4E2D\uFF0C\u65E0\u9700\u624B\u52A8\u6FC0\u6D3B`));
17693
+ } else {
17694
+ console.log(source_default.dim(` [${id}] \u5DF2\u5728\u5F53\u524D\u4F1A\u8BDD\u4E2D\u52A0\u8F7D`));
17695
+ }
17696
+ return false;
17697
+ }
17671
17698
  const all = collectAllSkills();
17672
17699
  const match = all.find((e2) => e2.id === id) || all.find((e2) => e2.id.startsWith(id)) || all.find((e2) => e2.id.includes(id));
17673
17700
  if (!match) {
@@ -18480,7 +18507,7 @@ ${paramSummary}
18480
18507
  console.log("\u7528\u6CD5: /uninstall <skill-id>");
18481
18508
  break;
18482
18509
  }
18483
- const uninstallPath = (0, import_fs6.existsSync)((0, import_path6.join)(USER_SKILLS_DIR, arg)) ? (0, import_path6.join)(USER_SKILLS_DIR, arg) : (0, import_path6.join)(SKILLS_DIR, arg);
18510
+ const uninstallPath = (0, import_path6.join)(USER_SKILLS_DIR, arg);
18484
18511
  if (!(0, import_fs6.existsSync)(uninstallPath)) {
18485
18512
  console.log(source_default.red(`\u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684 Skill: ${arg}`));
18486
18513
  console.log(source_default.dim("\u6CE8\u610F: \u53EA\u80FD\u5378\u8F7D\u901A\u8FC7 /install \u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill"));
@@ -18572,7 +18599,7 @@ ${paramSummary}
18572
18599
  const label = parts.slice(3).join(" ") || `${type} (${genome})`;
18573
18600
  const entry = addDbEntry(dbRegistry, { label, type, genome, path: dbPath, source: "manual" });
18574
18601
  saveDbRegistry(dbRegistry);
18575
- systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
18602
+ rebuildSystemPrompt();
18576
18603
  console.log(source_default.green(`\u2713 \u5DF2\u6DFB\u52A0\u6570\u636E\u5E93: ${entry.label}`));
18577
18604
  console.log(` id: ${source_default.cyan(entry.id)}`);
18578
18605
  console.log(` \u8DEF\u5F84: ${source_default.dim(entry.path)}`);
@@ -18588,7 +18615,7 @@ ${paramSummary}
18588
18615
  const removed = removeDbEntry(dbRegistry, dbArg.trim());
18589
18616
  if (removed) {
18590
18617
  saveDbRegistry(dbRegistry);
18591
- systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
18618
+ rebuildSystemPrompt();
18592
18619
  console.log(source_default.green(`\u2713 \u5DF2\u79FB\u9664\u6570\u636E\u5E93: ${dbArg}`));
18593
18620
  } else {
18594
18621
  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`));
@@ -18623,7 +18650,7 @@ ${paramSummary}
18623
18650
  if (addedCount > 0) {
18624
18651
  dbRegistry.lastScan = (/* @__PURE__ */ new Date()).toISOString();
18625
18652
  saveDbRegistry(dbRegistry);
18626
- systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
18653
+ rebuildSystemPrompt();
18627
18654
  console.log(source_default.green(`
18628
18655
  \u2713 \u65B0\u589E ${addedCount} \u4E2A\u6570\u636E\u5E93\u5230\u6CE8\u518C\u8868`));
18629
18656
  } else {
@@ -18705,11 +18732,18 @@ ${summary}` },
18705
18732
  \u5F53\u524D\u5DF2\u52A0\u8F7D\u7684 Skills (${injectedSkills.size} \u4E2A):
18706
18733
  `));
18707
18734
  for (const [id, name] of injectedSkills) {
18708
- console.log(` ${source_default.green("\u25CF")} ${source_default.cyan(id)} ${source_default.dim("\u2014 " + name)}`);
18709
- console.log(source_default.dim(` /unload ${id} \u53EF\u5378\u8F7D\u6B64 Skill`));
18735
+ const isPerm = permanentSkillIds.has(id);
18736
+ const icon = isPerm ? source_default.yellow("\u26A1") : source_default.green("\u25CF");
18737
+ const tag = isPerm ? source_default.yellow(" [\u5E38\u9A7B]") : "";
18738
+ console.log(` ${icon} ${source_default.cyan(id)}${tag} ${source_default.dim("\u2014 " + name)}`);
18739
+ if (isPerm) {
18740
+ console.log(source_default.dim(` /unpin ${id} \u53EF\u53D6\u6D88\u5E38\u9A7B`));
18741
+ } else {
18742
+ console.log(source_default.dim(` /unload ${id} \u53EF\u5378\u8F7D\u6B64 Skill`));
18743
+ }
18710
18744
  }
18711
18745
  console.log();
18712
- console.log(source_default.dim("\u63D0\u793A: /clear \u6E05\u7A7A\u5168\u90E8\u5BF9\u8BDD\u548C Skills | /unload <id> \u5378\u8F7D\u5355\u4E2A"));
18746
+ console.log(source_default.dim("\u63D0\u793A: /clear \u6E05\u7A7A\u5168\u90E8\u5BF9\u8BDD\u548C Skills | /unload <id> \u5378\u8F7D | /unpin <id> \u53D6\u6D88\u5E38\u9A7B"));
18713
18747
  }
18714
18748
  break;
18715
18749
  }
@@ -18726,6 +18760,11 @@ ${summary}` },
18726
18760
  console.log(source_default.dim("\u4F7F\u7528 /skills \u67E5\u770B\u5F53\u524D\u5DF2\u52A0\u8F7D\u7684 Skills"));
18727
18761
  break;
18728
18762
  }
18763
+ if (permanentSkillIds.has(targetId)) {
18764
+ console.log(source_default.yellow(`"${targetId}" \u662F\u5E38\u9A7B\u6280\u80FD\uFF0C\u65E0\u6CD5\u901A\u8FC7 /unload \u5378\u8F7D`));
18765
+ console.log(source_default.dim(` \u4F7F\u7528 /unpin ${targetId} \u53EF\u5C06\u5176\u4ECE\u5E38\u9A7B\u5217\u8868\u4E2D\u79FB\u9664\uFF08\u4E0B\u6B21\u4F1A\u8BDD\u751F\u6548\uFF09`));
18766
+ break;
18767
+ }
18729
18768
  const SKILL_MARKER = `[Skill \u5DF2\u52A0\u8F7D: ${targetId}]`;
18730
18769
  let removed = 0;
18731
18770
  for (let i2 = history.length - 1; i2 >= 0; i2--) {
@@ -18806,29 +18845,106 @@ ${summary}` },
18806
18845
  }
18807
18846
  case "cat": {
18808
18847
  console.log(source_default.bold.cyan("\n\u2500\u2500\u2500 Skill \u5206\u7C7B\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"));
18809
- console.log(source_default.dim(" \u4F7F\u7528 /sk <id> \u6FC0\u6D3B\u6280\u80FD \xB7 \u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22\n"));
18848
+ console.log(source_default.dim(" \u4F7F\u7528 /sk <id> \u6FC0\u6D3B \xB7 /sk <\u5173\u952E\u8BCD> \u641C\u7D22 \xB7 /pin <id> \u8BBE\u4E3A\u5E38\u9A7B\n"));
18849
+ const allInstalled = collectAllSkills();
18850
+ const permIds = Array.from(permanentSkillIds);
18851
+ console.log(` \u26A1 ${source_default.bold.yellow("\u5E38\u9A7B Skills")} ${source_default.dim("(\u6BCF\u8F6E\u5BF9\u8BDD\u81EA\u52A8\u52A0\u5165\u4E0A\u4E0B\u6587)")}`);
18852
+ if (permIds.length === 0) {
18853
+ console.log(source_default.dim(" \u6682\u65E0\u5E38\u9A7B\u6280\u80FD\uFF0C\u4F7F\u7528 /pin <id> \u6DFB\u52A0"));
18854
+ } else {
18855
+ for (const id of permIds) {
18856
+ const entry = allInstalled.find((e2) => e2.id === id);
18857
+ const skillPath = entry ? (0, import_path6.join)(entry.dir, id, "SKILL.md") : "";
18858
+ const name = skillPath && (0, import_fs6.existsSync)(skillPath) ? parseSkillMeta((0, import_fs6.readFileSync)(skillPath, "utf8")).name || id : id;
18859
+ console.log(` ${source_default.yellow(id)} ${source_default.dim("\u2014 " + name)}`);
18860
+ }
18861
+ console.log(source_default.dim(" /unpin <id> \u53D6\u6D88\u5E38\u9A7B"));
18862
+ }
18863
+ console.log();
18864
+ const routedIds = new Set(SKILL_ROUTES.map((r2) => r2.id));
18810
18865
  const byCategory = {};
18811
18866
  for (const route of SKILL_ROUTES) {
18812
- (byCategory[route.category] ??= []).push({ id: route.id, dir: "", tag: route.tag });
18867
+ (byCategory[route.category] ??= []).push(route);
18813
18868
  }
18869
+ console.log(` \u{1F9EC} ${source_default.bold.cyan("\u751F\u7269\u4FE1\u606F Skills")} ${source_default.dim("(\u9700\u6FC0\u6D3B /sk <id>)")}`);
18814
18870
  for (const [catKey, meta] of Object.entries(SKILL_CATEGORIES)) {
18815
- const items = byCategory[catKey];
18816
- if (!items || items.length === 0) continue;
18817
- console.log(` ${meta.icon} ${source_default.bold(meta.label)}`);
18818
- for (const item of items) {
18819
- const route = SKILL_ROUTES.find((r2) => r2.id === item.id);
18820
- console.log(` ${source_default.cyan(item.id)} ${source_default.dim("\u2014 " + route.name)}`);
18871
+ const routes = byCategory[catKey];
18872
+ if (!routes || routes.length === 0) continue;
18873
+ console.log(` ${source_default.bold(meta.icon + " " + meta.label)}`);
18874
+ for (const route of routes) {
18875
+ console.log(` ${source_default.cyan(route.id)} ${source_default.dim("\u2014 " + route.name)}`);
18821
18876
  }
18822
- console.log();
18823
18877
  }
18824
- const routedIds = new Set(SKILL_ROUTES.map((r2) => r2.id));
18825
- const allInstalled = collectAllSkills();
18826
- const unrouted = allInstalled.filter((e2) => !routedIds.has(e2.id));
18827
- if (unrouted.length > 0) {
18828
- console.log(` \u{1F4E6} ${source_default.bold("\u66F4\u591A Skills")} ${source_default.dim(`(${unrouted.length} \u4E2A\uFF0C\u4F7F\u7528\u5173\u952E\u8BCD\u641C\u7D22)`)}`);
18829
- console.log(source_default.dim(" /sk <\u5173\u952E\u8BCD>\uFF0C\u4F8B: /sk ehr /sk clinical /sk imaging"));
18830
- console.log();
18878
+ const bioUnrouted = allInstalled.filter(
18879
+ (e2) => e2.tag === "bio" && !routedIds.has(e2.id) && !permanentSkillIds.has(e2.id)
18880
+ );
18881
+ if (bioUnrouted.length > 0) {
18882
+ console.log(source_default.dim(` \u2026 \u8FD8\u6709 ${bioUnrouted.length} \u4E2A\uFF0C\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22`));
18883
+ }
18884
+ console.log();
18885
+ const downloaded = allInstalled.filter((e2) => e2.tag === "downloaded");
18886
+ console.log(` \u{1F4E6} ${source_default.bold.green("\u5DF2\u5B89\u88C5 Skills")} ${source_default.dim(`(${downloaded.length} \u4E2A\uFF0C/install \u4E0B\u8F7D \u9700\u6FC0\u6D3B)`)}`);
18887
+ if (downloaded.length === 0) {
18888
+ console.log(source_default.dim(" \u6682\u65E0\uFF0C\u4F7F\u7528 /install <slug> \u4ECE clawhub \u5B89\u88C5"));
18889
+ } else {
18890
+ for (const e2 of downloaded.slice(0, 10)) {
18891
+ const skillPath = (0, import_path6.join)(e2.dir, e2.id, "SKILL.md");
18892
+ const name = (0, import_fs6.existsSync)(skillPath) ? parseSkillMeta((0, import_fs6.readFileSync)(skillPath, "utf8")).name || e2.id : e2.id;
18893
+ console.log(` ${source_default.green(e2.id)} ${source_default.dim("\u2014 " + name)}`);
18894
+ }
18895
+ if (downloaded.length > 10)
18896
+ console.log(source_default.dim(` \u2026 \u8FD8\u6709 ${downloaded.length - 10} \u4E2A\uFF0C\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22`));
18897
+ }
18898
+ console.log();
18899
+ break;
18900
+ }
18901
+ case "pin": {
18902
+ if (!arg) {
18903
+ console.log("\u7528\u6CD5: /pin <skill-id>");
18904
+ console.log(source_default.dim("\u5C06\u6307\u5B9A Skill \u8BBE\u4E3A\u5E38\u9A7B\uFF0C\u6BCF\u6B21\u5BF9\u8BDD\u81EA\u52A8\u52A0\u5165\u4E0A\u4E0B\u6587"));
18905
+ break;
18906
+ }
18907
+ const allForPin = collectAllSkills();
18908
+ const pinEntry = allForPin.find((e2) => e2.id === arg) || allForPin.find((e2) => e2.id.startsWith(arg)) || allForPin.find((e2) => e2.id.includes(arg));
18909
+ if (!pinEntry) {
18910
+ console.log(source_default.red(`\u627E\u4E0D\u5230 Skill: ${arg}\u3002\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22`));
18911
+ break;
18912
+ }
18913
+ const pinCfg = loadConfig();
18914
+ const perms = pinCfg.permanentSkills ?? [];
18915
+ if (perms.includes(pinEntry.id)) {
18916
+ console.log(source_default.yellow(`"${pinEntry.id}" \u5DF2\u662F\u5E38\u9A7B\u6280\u80FD`));
18917
+ break;
18918
+ }
18919
+ pinCfg.permanentSkills = [...perms, pinEntry.id];
18920
+ saveConfig(pinCfg);
18921
+ rebuildSystemPrompt();
18922
+ const pinPath = (0, import_path6.join)(pinEntry.dir, pinEntry.id, "SKILL.md");
18923
+ if ((0, import_fs6.existsSync)(pinPath)) {
18924
+ const { name } = parseSkillMeta((0, import_fs6.readFileSync)(pinPath, "utf8"));
18925
+ injectedSkills.set(pinEntry.id, name || pinEntry.id);
18926
+ } else {
18927
+ injectedSkills.set(pinEntry.id, pinEntry.id);
18928
+ }
18929
+ console.log(source_default.green(`\u2713 "${pinEntry.id}" \u5DF2\u8BBE\u4E3A\u5E38\u9A7B\u6280\u80FD\uFF0C\u7CFB\u7EDF\u63D0\u793A\u5DF2\u66F4\u65B0`));
18930
+ break;
18931
+ }
18932
+ case "unpin": {
18933
+ if (!arg) {
18934
+ console.log("\u7528\u6CD5: /unpin <skill-id>");
18935
+ console.log(source_default.dim("\u5C06\u6307\u5B9A Skill \u4ECE\u5E38\u9A7B\u5217\u8868\u79FB\u9664\uFF08\u5F53\u524D\u4F1A\u8BDD\u4ECD\u6709\u6548\uFF0C\u4E0B\u6B21\u4F1A\u8BDD\u8D77\u751F\u6548\uFF09"));
18936
+ break;
18937
+ }
18938
+ const unpinCfg = loadConfig();
18939
+ const unpinPerms = unpinCfg.permanentSkills ?? [];
18940
+ if (!unpinPerms.includes(arg)) {
18941
+ console.log(source_default.yellow(`"${arg}" \u4E0D\u5728\u5E38\u9A7B\u5217\u8868\u4E2D`));
18942
+ break;
18831
18943
  }
18944
+ unpinCfg.permanentSkills = unpinPerms.filter((id) => id !== arg);
18945
+ saveConfig(unpinCfg);
18946
+ rebuildSystemPrompt();
18947
+ console.log(source_default.green(`\u2713 "${arg}" \u5DF2\u4ECE\u5E38\u9A7B\u5217\u8868\u79FB\u9664\uFF08\u5F53\u524D\u4F1A\u8BDD\u4ECD\u6709\u6548\uFF0C\u4E0B\u6B21\u4F1A\u8BDD\u8D77\u751F\u6548\uFF09`));
18832
18948
  break;
18833
18949
  }
18834
18950
  case "cd": {
@@ -18867,7 +18983,73 @@ ${summary}` },
18867
18983
  }
18868
18984
  return {};
18869
18985
  }
18986
+ function rebuildSystemPrompt() {
18987
+ systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
18988
+ const cfg = loadConfig();
18989
+ const ids = cfg.permanentSkills ?? [];
18990
+ permanentSkillIds = new Set(ids);
18991
+ if (ids.length === 0) return;
18992
+ const all = collectAllSkills();
18993
+ const parts = [];
18994
+ for (const id of ids) {
18995
+ const entry = all.find((e2) => e2.id === id);
18996
+ if (!entry) continue;
18997
+ const skillPath = (0, import_path6.join)(entry.dir, entry.id, "SKILL.md");
18998
+ if (!(0, import_fs6.existsSync)(skillPath)) continue;
18999
+ const content = (0, import_fs6.readFileSync)(skillPath, "utf8");
19000
+ parts.push(`
19001
+ ### \u5E38\u9A7B\u6280\u80FD: ${id}
19002
+
19003
+ ${content}`);
19004
+ }
19005
+ if (parts.length > 0) {
19006
+ systemPrompt += "\n\n---\n\n# \u5E38\u9A7B\u6280\u80FD (Permanent Skills \u2014 \u5DF2\u81EA\u52A8\u6FC0\u6D3B\uFF0C\u6BCF\u8F6E\u5BF9\u8BDD\u5747\u6709\u6548)\n" + parts.join("\n");
19007
+ }
19008
+ }
19009
+ function appendDebugRound(sysPrompt, msgs, response, elapsedMs, tokensIn, tokensOut) {
19010
+ if (!debugFilePath) return;
19011
+ debugRound++;
19012
+ const ts = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
19013
+ const lines = [];
19014
+ lines.push(`## \u7B2C ${debugRound} \u8F6E (${ts})`);
19015
+ lines.push("");
19016
+ lines.push("### \u7CFB\u7EDF\u63D0\u793A (System Prompt)");
19017
+ lines.push("");
19018
+ lines.push("```text");
19019
+ lines.push(sysPrompt);
19020
+ lines.push("```");
19021
+ lines.push("");
19022
+ lines.push(`### \u5BF9\u8BDD\u8F93\u5165 (${msgs.length} \u6761\u6D88\u606F)`);
19023
+ lines.push("");
19024
+ msgs.forEach((m2, i2) => {
19025
+ const isLast = i2 === msgs.length - 1;
19026
+ const label = isLast ? `[${i2 + 1}] ${m2.role}\uFF08\u5F53\u524D\u8F93\u5165\uFF09` : `[${i2 + 1}] ${m2.role}`;
19027
+ lines.push(`#### ${label}`);
19028
+ lines.push("");
19029
+ const content = typeof m2.content === "string" ? m2.content : JSON.stringify(m2.content, null, 2);
19030
+ lines.push("```");
19031
+ lines.push(content);
19032
+ lines.push("```");
19033
+ lines.push("");
19034
+ });
19035
+ lines.push("### \u6A21\u578B\u8F93\u51FA");
19036
+ lines.push("");
19037
+ lines.push(response);
19038
+ lines.push("");
19039
+ lines.push("### \u7EDF\u8BA1");
19040
+ lines.push("");
19041
+ lines.push(`| \u6307\u6807 | \u503C |`);
19042
+ lines.push(`|------|-----|`);
19043
+ lines.push(`| \u8017\u65F6 | ${(elapsedMs / 1e3).toFixed(2)}s |`);
19044
+ lines.push(`| \u8F93\u5165 tokens | ${tokensIn} |`);
19045
+ lines.push(`| \u8F93\u51FA tokens | ${tokensOut} |`);
19046
+ lines.push("");
19047
+ lines.push("---");
19048
+ lines.push("");
19049
+ (0, import_fs6.appendFileSync)(debugFilePath, lines.join("\n"), "utf8");
19050
+ }
18870
19051
  async function main() {
19052
+ debugMode = process.argv.includes("--debug");
18871
19053
  installBundledData();
18872
19054
  printBanner();
18873
19055
  await checkAndAutoUpdate().catch(() => {
@@ -18893,8 +19075,15 @@ async function main() {
18893
19075
  console.log(` ${source_default.bold("\u670D\u52A1\u5546:")} ${prov?.name ?? cfg.provider}`);
18894
19076
  console.log(` ${source_default.bold("\u6A21\u578B:")} ${source_default.green(cfg.model)}`);
18895
19077
  console.log(` ${source_default.bold("Skills:")} ${skillsLabel} ${source_default.dim("(/sk \u641C\u7D22 /cat \u5206\u7C7B\u76EE\u5F55)")}`);
19078
+ if (permanentSkillIds.size > 0) {
19079
+ const permList = Array.from(permanentSkillIds).join(", ");
19080
+ console.log(` ${source_default.bold("\u5E38\u9A7B:")} ${source_default.yellow("\u26A1 " + permList)} ${source_default.dim("(/pin \u6DFB\u52A0 /unpin \u79FB\u9664)")}`);
19081
+ }
18896
19082
  console.log(` ${source_default.bold("\u5DE5\u5177:")} bash \xB7 read_file \xB7 write_file \xB7 list_dir \xB7 search_files`);
18897
19083
  console.log(` ${source_default.bold("\u65B0\u529F\u80FD:")} /sessions /resume /checkpoint /run /check-env /install /diff`);
19084
+ if (debugMode) {
19085
+ console.log(source_default.bold.yellow(" [DEBUG \u6A21\u5F0F] \u6BCF\u8F6E\u5C06\u6253\u5370\u5B8C\u6574 Prompt \u53CA Token \u7EDF\u8BA1"));
19086
+ }
18898
19087
  console.log(source_default.bold.cyan("\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
18899
19088
  const lastSess = getLastSession();
18900
19089
  if (lastSess) {
@@ -18923,10 +19112,43 @@ async function main() {
18923
19112
  }
18924
19113
  console.log();
18925
19114
  }
18926
- systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
19115
+ rebuildSystemPrompt();
19116
+ if (debugMode) {
19117
+ const debugDir = (0, import_path6.join)(BGI_DIR, "debug");
19118
+ (0, import_fs6.mkdirSync)(debugDir, { recursive: true });
19119
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
19120
+ debugFilePath = (0, import_path6.join)(debugDir, `debug-${ts}.md`);
19121
+ (0, import_fs6.writeFileSync)(
19122
+ debugFilePath,
19123
+ [
19124
+ "# BGI CLI Debug Log",
19125
+ "",
19126
+ `- \u4F1A\u8BDD\u5F00\u59CB: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}`,
19127
+ `- \u6A21\u578B: ${cfg.model}`,
19128
+ `- \u670D\u52A1\u5546: ${prov?.name ?? cfg.provider}`,
19129
+ "",
19130
+ "---",
19131
+ ""
19132
+ ].join("\n"),
19133
+ "utf8"
19134
+ );
19135
+ console.log(source_default.bold.yellow(` [DEBUG] \u8BB0\u5F55\u6587\u4EF6: ${debugFilePath}`));
19136
+ console.log();
19137
+ }
18927
19138
  let history = [];
18928
19139
  let thinkMode = false;
18929
19140
  const injectedSkills = /* @__PURE__ */ new Map();
19141
+ {
19142
+ const allForSeed = collectAllSkills();
19143
+ for (const id of Array.from(permanentSkillIds)) {
19144
+ const entry = allForSeed.find((e2) => e2.id === id);
19145
+ if (entry) {
19146
+ const sp = (0, import_path6.join)(entry.dir, entry.id, "SKILL.md");
19147
+ const { name } = (0, import_fs6.existsSync)(sp) ? parseSkillMeta((0, import_fs6.readFileSync)(sp, "utf8")) : { name: "" };
19148
+ injectedSkills.set(id, name || id);
19149
+ }
19150
+ }
19151
+ }
18930
19152
  const sessionId = newSessionId();
18931
19153
  const sessionCreatedAt = (/* @__PURE__ */ new Date()).toISOString();
18932
19154
  SESSION_CTX.id = sessionId;
@@ -18998,32 +19220,48 @@ async function main() {
18998
19220
  console.log(` \u6267\u884C\u547D\u4EE4: ${source_default.green("\u2713 " + sessionStats.successCmds + " \u6210\u529F")} ${sessionStats.failCmds > 0 ? source_default.yellow("\u2717 " + sessionStats.failCmds + " \u5931\u8D25") : source_default.dim("\u2717 0 \u5931\u8D25")}`);
18999
19221
  console.log(source_default.bold.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
19000
19222
  rl.close();
19001
- let ans = "n";
19002
- try {
19003
- const exitRl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
19004
- exitRl.on("SIGINT", () => {
19005
- exitRl.close();
19006
- process.exit(0);
19007
- });
19008
- process.once("SIGINT", () => {
19009
- exitRl.close();
19010
- process.exit(0);
19011
- });
19012
- ans = await Promise.race([
19013
- new Promise((res) => {
19014
- exitRl.question(source_default.cyan("\n \u662F\u5426\u4FDD\u5B58\u672C\u6B21\u4F1A\u8BDD\u8BB0\u5FC6\uFF1F[y/N] "), (a2) => {
19015
- exitRl.close();
19016
- res(a2.trim().toLowerCase());
19017
- });
19018
- }),
19019
- new Promise((res) => setTimeout(() => {
19020
- exitRl.close();
19021
- res("n");
19022
- }, 15e3))
19023
- ]);
19024
- } catch {
19223
+ async function askExitQuestion(prompt) {
19224
+ let answer = "n";
19225
+ try {
19226
+ const eRl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
19227
+ eRl.on("SIGINT", () => {
19228
+ eRl.close();
19229
+ process.exit(0);
19230
+ });
19231
+ process.once("SIGINT", () => {
19232
+ eRl.close();
19233
+ process.exit(0);
19234
+ });
19235
+ answer = await Promise.race([
19236
+ new Promise((res) => {
19237
+ eRl.question(prompt, (a2) => {
19238
+ eRl.close();
19239
+ res(a2.trim().toLowerCase());
19240
+ });
19241
+ }),
19242
+ new Promise((res) => setTimeout(() => {
19243
+ eRl.close();
19244
+ res("n");
19245
+ }, 15e3))
19246
+ ]);
19247
+ } catch {
19248
+ }
19249
+ return answer;
19250
+ }
19251
+ const memAns = await askExitQuestion(source_default.cyan("\n \u662F\u5426\u4FDD\u5B58\u672C\u6B21\u4F1A\u8BDD\u8BB0\u5FC6\uFF1F[y/N] "));
19252
+ if (memAns === "y") saveSessionMemory();
19253
+ if (debugMode && debugFilePath && (0, import_fs6.existsSync)(debugFilePath)) {
19254
+ console.log(source_default.dim(`
19255
+ Debug \u6587\u4EF6: ${debugFilePath}`));
19256
+ const delAns = await askExitQuestion(source_default.yellow(" \u662F\u5426\u5220\u9664 debug \u8BB0\u5F55\u6587\u4EF6\uFF1F[y/N] "));
19257
+ if (delAns === "y") {
19258
+ try {
19259
+ (0, import_fs6.rmSync)(debugFilePath);
19260
+ console.log(source_default.dim(" \u5DF2\u5220\u9664"));
19261
+ } catch {
19262
+ }
19263
+ }
19025
19264
  }
19026
- if (ans === "y") saveSessionMemory();
19027
19265
  console.log(source_default.dim("\n\u518D\u89C1\uFF01"));
19028
19266
  process.exit(0);
19029
19267
  }
@@ -19133,8 +19371,21 @@ ${expanded}` : expanded;
19133
19371
  try {
19134
19372
  const currentCfg = loadConfig();
19135
19373
  currentAbortController = new AbortController();
19374
+ const debugT0 = debugMode ? Date.now() : 0;
19375
+ const debugTokensBefore = debugMode ? { in: sessionStats.inputTokens, out: sessionStats.outputTokens } : null;
19136
19376
  const reply = await chat(history, currentCfg, systemPrompt, sessionStats, currentAbortController.signal);
19137
19377
  currentAbortController = null;
19378
+ if (debugMode && debugTokensBefore && reply) {
19379
+ const elapsedMs = Date.now() - debugT0;
19380
+ const dIn = sessionStats.inputTokens - debugTokensBefore.in;
19381
+ const dOut = sessionStats.outputTokens - debugTokensBefore.out;
19382
+ try {
19383
+ appendDebugRound(systemPrompt, history, reply, elapsedMs, dIn, dOut);
19384
+ } catch {
19385
+ }
19386
+ console.log(source_default.bold.yellow(`
19387
+ [DEBUG] \u5DF2\u8BB0\u5F55 \u2192 ${debugFilePath}`));
19388
+ }
19138
19389
  if (!reply && history[history.length - 1]?.role === "user") {
19139
19390
  history.pop();
19140
19391
  console.log();