@bgicli/bgicli 2.2.7 → 2.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/skills/anthropic-algorithmic-art/SKILL.md +405 -0
- package/data/skills/anthropic-canvas-design/SKILL.md +130 -0
- package/data/skills/anthropic-claude-api/SKILL.md +243 -0
- package/data/skills/anthropic-doc-coauthoring/SKILL.md +375 -0
- package/data/skills/anthropic-docx/SKILL.md +590 -0
- package/data/skills/anthropic-frontend-design/SKILL.md +42 -0
- package/data/skills/anthropic-internal-comms/SKILL.md +32 -0
- package/data/skills/anthropic-mcp-builder/SKILL.md +236 -0
- package/data/skills/anthropic-pdf/SKILL.md +314 -0
- package/data/skills/anthropic-pptx/SKILL.md +232 -0
- package/data/skills/anthropic-skill-creator/SKILL.md +485 -0
- package/data/skills/anthropic-webapp-testing/SKILL.md +96 -0
- package/data/skills/anthropic-xlsx/SKILL.md +292 -0
- package/data/skills/arxiv-database/SKILL.md +362 -0
- package/data/skills/astropy/SKILL.md +329 -0
- package/data/skills/ctx-advanced-evaluation/SKILL.md +402 -0
- package/data/skills/ctx-bdi-mental-states/SKILL.md +311 -0
- package/data/skills/ctx-context-compression/SKILL.md +272 -0
- package/data/skills/ctx-context-degradation/SKILL.md +206 -0
- package/data/skills/ctx-context-fundamentals/SKILL.md +201 -0
- package/data/skills/ctx-context-optimization/SKILL.md +195 -0
- package/data/skills/ctx-evaluation/SKILL.md +251 -0
- package/data/skills/ctx-filesystem-context/SKILL.md +287 -0
- package/data/skills/ctx-hosted-agents/SKILL.md +260 -0
- package/data/skills/ctx-memory-systems/SKILL.md +225 -0
- package/data/skills/ctx-multi-agent-patterns/SKILL.md +257 -0
- package/data/skills/ctx-project-development/SKILL.md +291 -0
- package/data/skills/ctx-tool-design/SKILL.md +271 -0
- package/data/skills/dhdna-profiler/SKILL.md +162 -0
- package/data/skills/generate-image/SKILL.md +183 -0
- package/data/skills/geomaster/SKILL.md +365 -0
- package/data/skills/get-available-resources/SKILL.md +275 -0
- package/data/skills/hamelsmu-build-review-interface/SKILL.md +96 -0
- package/data/skills/hamelsmu-error-analysis/SKILL.md +164 -0
- package/data/skills/hamelsmu-eval-audit/SKILL.md +183 -0
- package/data/skills/hamelsmu-evaluate-rag/SKILL.md +177 -0
- package/data/skills/hamelsmu-generate-synthetic-data/SKILL.md +131 -0
- package/data/skills/hamelsmu-validate-evaluator/SKILL.md +212 -0
- package/data/skills/hamelsmu-write-judge-prompt/SKILL.md +144 -0
- package/data/skills/hf-cli/SKILL.md +174 -0
- package/data/skills/hf-mcp/SKILL.md +178 -0
- package/data/skills/hugging-face-dataset-viewer/SKILL.md +121 -0
- package/data/skills/hugging-face-datasets/SKILL.md +542 -0
- package/data/skills/hugging-face-evaluation/SKILL.md +651 -0
- package/data/skills/hugging-face-jobs/SKILL.md +1042 -0
- package/data/skills/hugging-face-model-trainer/SKILL.md +717 -0
- package/data/skills/hugging-face-paper-pages/SKILL.md +239 -0
- package/data/skills/hugging-face-paper-publisher/SKILL.md +624 -0
- package/data/skills/hugging-face-tool-builder/SKILL.md +110 -0
- package/data/skills/hugging-face-trackio/SKILL.md +115 -0
- package/data/skills/hugging-face-vision-trainer/SKILL.md +593 -0
- package/data/skills/huggingface-gradio/SKILL.md +245 -0
- package/data/skills/matlab/SKILL.md +376 -0
- package/data/skills/modal/SKILL.md +381 -0
- package/data/skills/openai-cloudflare-deploy/SKILL.md +224 -0
- package/data/skills/openai-develop-web-game/SKILL.md +149 -0
- package/data/skills/openai-doc/SKILL.md +80 -0
- package/data/skills/openai-figma/SKILL.md +42 -0
- package/data/skills/openai-figma-implement-design/SKILL.md +264 -0
- package/data/skills/openai-gh-address-comments/SKILL.md +25 -0
- package/data/skills/openai-gh-fix-ci/SKILL.md +69 -0
- package/data/skills/openai-imagegen/SKILL.md +174 -0
- package/data/skills/openai-jupyter-notebook/SKILL.md +107 -0
- package/data/skills/openai-linear/SKILL.md +87 -0
- package/data/skills/openai-netlify-deploy/SKILL.md +247 -0
- package/data/skills/openai-notion-knowledge-capture/SKILL.md +56 -0
- package/data/skills/openai-notion-meeting-intelligence/SKILL.md +60 -0
- package/data/skills/openai-notion-research-documentation/SKILL.md +59 -0
- package/data/skills/openai-notion-spec-to-implementation/SKILL.md +58 -0
- package/data/skills/openai-openai-docs/SKILL.md +69 -0
- package/data/skills/openai-pdf/SKILL.md +67 -0
- package/data/skills/openai-playwright/SKILL.md +147 -0
- package/data/skills/openai-render-deploy/SKILL.md +479 -0
- package/data/skills/openai-screenshot/SKILL.md +267 -0
- package/data/skills/openai-security-best-practices/SKILL.md +86 -0
- package/data/skills/openai-security-ownership-map/SKILL.md +206 -0
- package/data/skills/openai-security-threat-model/SKILL.md +81 -0
- package/data/skills/openai-sentry/SKILL.md +123 -0
- package/data/skills/openai-sora/SKILL.md +178 -0
- package/data/skills/openai-speech/SKILL.md +144 -0
- package/data/skills/openai-spreadsheet/SKILL.md +145 -0
- package/data/skills/openai-transcribe/SKILL.md +81 -0
- package/data/skills/openai-vercel-deploy/SKILL.md +77 -0
- package/data/skills/openai-yeet/SKILL.md +28 -0
- package/data/skills/pennylane/SKILL.md +224 -0
- package/data/skills/polars-bio/SKILL.md +374 -0
- package/data/skills/primekg/SKILL.md +97 -0
- package/data/skills/pymatgen/SKILL.md +689 -0
- package/data/skills/qiskit/SKILL.md +273 -0
- package/data/skills/qutip/SKILL.md +316 -0
- package/data/skills/recursive-decomposition/SKILL.md +185 -0
- package/data/skills/rowan/SKILL.md +427 -0
- package/data/skills/scholar-evaluation/SKILL.md +298 -0
- package/data/skills/sentry-create-alert/SKILL.md +210 -0
- package/data/skills/sentry-fix-issues/SKILL.md +126 -0
- package/data/skills/sentry-pr-code-review/SKILL.md +105 -0
- package/data/skills/sentry-python-sdk/SKILL.md +317 -0
- package/data/skills/sentry-setup-ai-monitoring/SKILL.md +217 -0
- package/data/skills/stable-baselines3/SKILL.md +297 -0
- package/data/skills/sympy/SKILL.md +498 -0
- package/data/skills/trailofbits-ask-questions-if-underspecified/SKILL.md +85 -0
- package/data/skills/trailofbits-audit-context-building/SKILL.md +302 -0
- package/data/skills/trailofbits-differential-review/SKILL.md +220 -0
- package/data/skills/trailofbits-insecure-defaults/SKILL.md +117 -0
- package/data/skills/trailofbits-modern-python/SKILL.md +333 -0
- package/data/skills/trailofbits-property-based-testing/SKILL.md +123 -0
- package/data/skills/trailofbits-semgrep-rule-creator/SKILL.md +172 -0
- package/data/skills/trailofbits-sharp-edges/SKILL.md +292 -0
- package/data/skills/trailofbits-variant-analysis/SKILL.md +142 -0
- package/data/skills/transformers.js/SKILL.md +637 -0
- package/data/skills/writing/SKILL.md +419 -0
- package/data/workflows/survival-analysis-clinical/SKILL.md +348 -0
- package/data/workflows/survival-analysis-clinical/scripts/full_workflow.R +95 -0
- package/data/workflows/survival-analysis-clinical/scripts/load_example_data.R +65 -0
- package/data/workflows/survival-analysis-clinical/scripts/plot_forest.R +46 -0
- package/dist/bgi.js +1608 -233
- package/package.json +45 -45
package/dist/bgi.js
CHANGED
|
@@ -6368,18 +6368,18 @@ function createFileFromPath(path, { mtimeMs, size }, filenameOrOptions, options
|
|
|
6368
6368
|
});
|
|
6369
6369
|
}
|
|
6370
6370
|
function fileFromPathSync(path, filenameOrOptions, options = {}) {
|
|
6371
|
-
const stats = (0,
|
|
6371
|
+
const stats = (0, import_fs.statSync)(path);
|
|
6372
6372
|
return createFileFromPath(path, stats, filenameOrOptions, options);
|
|
6373
6373
|
}
|
|
6374
6374
|
async function fileFromPath2(path, filenameOrOptions, options) {
|
|
6375
|
-
const stats = await
|
|
6375
|
+
const stats = await import_fs.promises.stat(path);
|
|
6376
6376
|
return createFileFromPath(path, stats, filenameOrOptions, options);
|
|
6377
6377
|
}
|
|
6378
|
-
var
|
|
6378
|
+
var import_fs, import_path, import_node_domexception, __classPrivateFieldSet4, __classPrivateFieldGet5, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
|
|
6379
6379
|
var init_fileFromPath = __esm({
|
|
6380
6380
|
"node_modules/formdata-node/lib/esm/fileFromPath.js"() {
|
|
6381
|
-
|
|
6382
|
-
|
|
6381
|
+
import_fs = require("fs");
|
|
6382
|
+
import_path = require("path");
|
|
6383
6383
|
import_node_domexception = __toESM(require_node_domexception(), 1);
|
|
6384
6384
|
init_File();
|
|
6385
6385
|
init_isPlainObject();
|
|
@@ -6402,7 +6402,7 @@ var init_fileFromPath = __esm({
|
|
|
6402
6402
|
_FileFromPath_start.set(this, void 0);
|
|
6403
6403
|
__classPrivateFieldSet4(this, _FileFromPath_path, input.path, "f");
|
|
6404
6404
|
__classPrivateFieldSet4(this, _FileFromPath_start, input.start || 0, "f");
|
|
6405
|
-
this.name = (0,
|
|
6405
|
+
this.name = (0, import_path.basename)(__classPrivateFieldGet5(this, _FileFromPath_path, "f"));
|
|
6406
6406
|
this.size = input.size;
|
|
6407
6407
|
this.lastModified = input.lastModified;
|
|
6408
6408
|
}
|
|
@@ -6415,12 +6415,12 @@ var init_fileFromPath = __esm({
|
|
|
6415
6415
|
});
|
|
6416
6416
|
}
|
|
6417
6417
|
async *stream() {
|
|
6418
|
-
const { mtimeMs } = await
|
|
6418
|
+
const { mtimeMs } = await import_fs.promises.stat(__classPrivateFieldGet5(this, _FileFromPath_path, "f"));
|
|
6419
6419
|
if (mtimeMs > this.lastModified) {
|
|
6420
6420
|
throw new import_node_domexception.default(MESSAGE, "NotReadableError");
|
|
6421
6421
|
}
|
|
6422
6422
|
if (this.size) {
|
|
6423
|
-
yield* (0,
|
|
6423
|
+
yield* (0, import_fs.createReadStream)(__classPrivateFieldGet5(this, _FileFromPath_path, "f"), {
|
|
6424
6424
|
start: __classPrivateFieldGet5(this, _FileFromPath_start, "f"),
|
|
6425
6425
|
end: __classPrivateFieldGet5(this, _FileFromPath_start, "f") + this.size - 1
|
|
6426
6426
|
});
|
|
@@ -6932,105 +6932,10 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
6932
6932
|
var source_default = chalk;
|
|
6933
6933
|
|
|
6934
6934
|
// src/index.ts
|
|
6935
|
-
var
|
|
6936
|
-
var
|
|
6935
|
+
var import_fs5 = require("fs");
|
|
6936
|
+
var import_path5 = require("path");
|
|
6937
6937
|
var import_os3 = require("os");
|
|
6938
6938
|
|
|
6939
|
-
// src/config.ts
|
|
6940
|
-
var import_fs = require("fs");
|
|
6941
|
-
var import_os = require("os");
|
|
6942
|
-
var import_path = require("path");
|
|
6943
|
-
|
|
6944
|
-
// src/providers.ts
|
|
6945
|
-
var PROVIDERS = {
|
|
6946
|
-
bailian: {
|
|
6947
|
-
name: "\u767E\u70BC \xB7 \u963F\u91CC\u4E91 (DashScope)",
|
|
6948
|
-
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
6949
|
-
envKey: "DASHSCOPE_API_KEY",
|
|
6950
|
-
models: [
|
|
6951
|
-
// ── Qwen3.5 (最新旗舰) ────────────────────────────────
|
|
6952
|
-
"qwen3.5-397b-a17b",
|
|
6953
|
-
"qwen3.5-122b-a10b",
|
|
6954
|
-
"qwen3.5-plus",
|
|
6955
|
-
"qwen3.5-flash",
|
|
6956
|
-
// ── Qwen3 ─────────────────────────────────────────────
|
|
6957
|
-
"qwen3-235b-a22b",
|
|
6958
|
-
"qwen3-max",
|
|
6959
|
-
"qwen3-32b",
|
|
6960
|
-
"qwen3-14b",
|
|
6961
|
-
"qwen3-8b",
|
|
6962
|
-
// ── Qwen3 代码模型 ────────────────────────────────────
|
|
6963
|
-
"qwen3-coder-plus",
|
|
6964
|
-
"qwen3-coder-flash",
|
|
6965
|
-
"qwen3-coder-480b-a35b-instruct",
|
|
6966
|
-
// ── 经典 Qwen ─────────────────────────────────────────
|
|
6967
|
-
"qwen-max",
|
|
6968
|
-
"qwen-plus",
|
|
6969
|
-
"qwen-turbo",
|
|
6970
|
-
"qwen-long",
|
|
6971
|
-
"qwen-flash",
|
|
6972
|
-
// ── 推理模型 ──────────────────────────────────────────
|
|
6973
|
-
"qwq-plus",
|
|
6974
|
-
"deepseek-r1",
|
|
6975
|
-
"deepseek-v3",
|
|
6976
|
-
"deepseek-v3.2",
|
|
6977
|
-
// ── 第三方 (DashScope 聚合) ───────────────────────────
|
|
6978
|
-
"kimi-k2.5",
|
|
6979
|
-
"kimi-k2-thinking",
|
|
6980
|
-
"MiniMax-M2.5",
|
|
6981
|
-
"glm-5"
|
|
6982
|
-
],
|
|
6983
|
-
defaultModel: "qwen3.5-plus"
|
|
6984
|
-
},
|
|
6985
|
-
intranet: {
|
|
6986
|
-
name: "\u5185\u7F51 Qwen3-235B (172.16.224.137)",
|
|
6987
|
-
baseURL: "http://172.16.224.137:1024/v1",
|
|
6988
|
-
models: ["Qwen3-235B-A22B"],
|
|
6989
|
-
defaultModel: "Qwen3-235B-A22B",
|
|
6990
|
-
envKey: ""
|
|
6991
|
-
// no auth required
|
|
6992
|
-
},
|
|
6993
|
-
custom: {
|
|
6994
|
-
name: "\u81EA\u5B9A\u4E49 (Custom URL)",
|
|
6995
|
-
baseURL: "",
|
|
6996
|
-
// filled from config.customUrl at runtime
|
|
6997
|
-
models: [],
|
|
6998
|
-
// filled from config.customModel at runtime
|
|
6999
|
-
defaultModel: "",
|
|
7000
|
-
envKey: "CUSTOM_API_KEY"
|
|
7001
|
-
}
|
|
7002
|
-
};
|
|
7003
|
-
var DEFAULT_PROVIDER = "bailian";
|
|
7004
|
-
|
|
7005
|
-
// src/config.ts
|
|
7006
|
-
var BGI_DIR = (0, import_path.join)((0, import_os.homedir)(), ".bgicli");
|
|
7007
|
-
var WORKFLOWS_DIR = (0, import_path.join)(BGI_DIR, "workflows");
|
|
7008
|
-
var TOOLS_DIR = (0, import_path.join)(BGI_DIR, "tools");
|
|
7009
|
-
var SKILLS_DIR = (0, import_path.join)(BGI_DIR, "skills");
|
|
7010
|
-
var CONFIG_FILE = (0, import_path.join)(BGI_DIR, "config.json");
|
|
7011
|
-
function ensureDirs() {
|
|
7012
|
-
for (const dir of [BGI_DIR, WORKFLOWS_DIR, TOOLS_DIR, SKILLS_DIR]) {
|
|
7013
|
-
if (!(0, import_fs.existsSync)(dir)) (0, import_fs.mkdirSync)(dir, { recursive: true });
|
|
7014
|
-
}
|
|
7015
|
-
}
|
|
7016
|
-
function loadConfig() {
|
|
7017
|
-
ensureDirs();
|
|
7018
|
-
if (!(0, import_fs.existsSync)(CONFIG_FILE)) {
|
|
7019
|
-
const def = {
|
|
7020
|
-
provider: DEFAULT_PROVIDER,
|
|
7021
|
-
model: PROVIDERS[DEFAULT_PROVIDER].defaultModel,
|
|
7022
|
-
apiKeys: {}
|
|
7023
|
-
};
|
|
7024
|
-
saveConfig(def);
|
|
7025
|
-
return def;
|
|
7026
|
-
}
|
|
7027
|
-
return JSON.parse((0, import_fs.readFileSync)(CONFIG_FILE, "utf8"));
|
|
7028
|
-
}
|
|
7029
|
-
function saveConfig(cfg) {
|
|
7030
|
-
ensureDirs();
|
|
7031
|
-
(0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
7032
|
-
}
|
|
7033
|
-
|
|
7034
6939
|
// node_modules/openai/internal/qs/formats.mjs
|
|
7035
6940
|
var default_format = "RFC3986";
|
|
7036
6941
|
var formatters = {
|
|
@@ -13627,11 +13532,108 @@ OpenAI.Containers = Containers;
|
|
|
13627
13532
|
OpenAI.ContainerListResponsesPage = ContainerListResponsesPage;
|
|
13628
13533
|
var openai_default = OpenAI;
|
|
13629
13534
|
|
|
13535
|
+
// src/config.ts
|
|
13536
|
+
var import_fs2 = require("fs");
|
|
13537
|
+
var import_os = require("os");
|
|
13538
|
+
var import_path2 = require("path");
|
|
13539
|
+
|
|
13540
|
+
// src/providers.ts
|
|
13541
|
+
var PROVIDERS = {
|
|
13542
|
+
bailian: {
|
|
13543
|
+
name: "\u767E\u70BC \xB7 \u963F\u91CC\u4E91 (DashScope)",
|
|
13544
|
+
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
13545
|
+
envKey: "DASHSCOPE_API_KEY",
|
|
13546
|
+
models: [
|
|
13547
|
+
// ── Qwen3.5 (最新旗舰) ────────────────────────────────
|
|
13548
|
+
"qwen3.5-397b-a17b",
|
|
13549
|
+
"qwen3.5-122b-a10b",
|
|
13550
|
+
"qwen3.5-plus",
|
|
13551
|
+
"qwen3.5-flash",
|
|
13552
|
+
// ── Qwen3 ─────────────────────────────────────────────
|
|
13553
|
+
"qwen3-235b-a22b",
|
|
13554
|
+
"qwen3-max",
|
|
13555
|
+
"qwen3-32b",
|
|
13556
|
+
"qwen3-14b",
|
|
13557
|
+
"qwen3-8b",
|
|
13558
|
+
// ── Qwen3 代码模型 ────────────────────────────────────
|
|
13559
|
+
"qwen3-coder-plus",
|
|
13560
|
+
"qwen3-coder-flash",
|
|
13561
|
+
"qwen3-coder-480b-a35b-instruct",
|
|
13562
|
+
// ── 经典 Qwen ─────────────────────────────────────────
|
|
13563
|
+
"qwen-max",
|
|
13564
|
+
"qwen-plus",
|
|
13565
|
+
"qwen-turbo",
|
|
13566
|
+
"qwen-long",
|
|
13567
|
+
"qwen-flash",
|
|
13568
|
+
// ── 推理模型 ──────────────────────────────────────────
|
|
13569
|
+
"qwq-plus",
|
|
13570
|
+
"deepseek-r1",
|
|
13571
|
+
"deepseek-v3",
|
|
13572
|
+
"deepseek-v3.2",
|
|
13573
|
+
// ── 第三方 (DashScope 聚合) ───────────────────────────
|
|
13574
|
+
"kimi-k2.5",
|
|
13575
|
+
"kimi-k2-thinking",
|
|
13576
|
+
"MiniMax-M2.5",
|
|
13577
|
+
"glm-5"
|
|
13578
|
+
],
|
|
13579
|
+
defaultModel: "qwen3.5-plus"
|
|
13580
|
+
},
|
|
13581
|
+
intranet: {
|
|
13582
|
+
name: "\u5185\u7F51 Qwen3-235B (172.16.224.137)",
|
|
13583
|
+
baseURL: "http://172.16.224.137:1024/v1",
|
|
13584
|
+
models: ["Qwen3-235B-A22B"],
|
|
13585
|
+
defaultModel: "Qwen3-235B-A22B",
|
|
13586
|
+
envKey: ""
|
|
13587
|
+
// no auth required
|
|
13588
|
+
},
|
|
13589
|
+
custom: {
|
|
13590
|
+
name: "\u81EA\u5B9A\u4E49 (Custom URL)",
|
|
13591
|
+
baseURL: "",
|
|
13592
|
+
// filled from config.customUrl at runtime
|
|
13593
|
+
models: [],
|
|
13594
|
+
// filled from config.customModel at runtime
|
|
13595
|
+
defaultModel: "",
|
|
13596
|
+
envKey: "CUSTOM_API_KEY"
|
|
13597
|
+
}
|
|
13598
|
+
};
|
|
13599
|
+
var DEFAULT_PROVIDER = "bailian";
|
|
13600
|
+
|
|
13601
|
+
// src/config.ts
|
|
13602
|
+
var BGI_DIR = (0, import_path2.join)((0, import_os.homedir)(), ".bgicli");
|
|
13603
|
+
var WORKFLOWS_DIR = (0, import_path2.join)(BGI_DIR, "workflows");
|
|
13604
|
+
var TOOLS_DIR = (0, import_path2.join)(BGI_DIR, "tools");
|
|
13605
|
+
var SKILLS_DIR = (0, import_path2.join)(BGI_DIR, "skills");
|
|
13606
|
+
var CONFIG_FILE = (0, import_path2.join)(BGI_DIR, "config.json");
|
|
13607
|
+
function ensureDirs() {
|
|
13608
|
+
for (const dir of [BGI_DIR, WORKFLOWS_DIR, TOOLS_DIR, SKILLS_DIR]) {
|
|
13609
|
+
if (!(0, import_fs2.existsSync)(dir)) (0, import_fs2.mkdirSync)(dir, { recursive: true });
|
|
13610
|
+
}
|
|
13611
|
+
}
|
|
13612
|
+
function loadConfig() {
|
|
13613
|
+
ensureDirs();
|
|
13614
|
+
if (!(0, import_fs2.existsSync)(CONFIG_FILE)) {
|
|
13615
|
+
const def = {
|
|
13616
|
+
provider: DEFAULT_PROVIDER,
|
|
13617
|
+
model: PROVIDERS[DEFAULT_PROVIDER].defaultModel,
|
|
13618
|
+
apiKeys: {}
|
|
13619
|
+
};
|
|
13620
|
+
saveConfig(def);
|
|
13621
|
+
return def;
|
|
13622
|
+
}
|
|
13623
|
+
return JSON.parse((0, import_fs2.readFileSync)(CONFIG_FILE, "utf8"));
|
|
13624
|
+
}
|
|
13625
|
+
function saveConfig(cfg) {
|
|
13626
|
+
ensureDirs();
|
|
13627
|
+
(0, import_fs2.writeFileSync)(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
13628
|
+
}
|
|
13629
|
+
|
|
13630
13630
|
// src/tools.ts
|
|
13631
13631
|
var import_child_process = require("child_process");
|
|
13632
13632
|
var import_fs3 = require("fs");
|
|
13633
13633
|
var import_path3 = require("path");
|
|
13634
13634
|
var import_os2 = require("os");
|
|
13635
|
+
var import_https = require("https");
|
|
13636
|
+
var import_http = require("http");
|
|
13635
13637
|
var TOOL_DEFINITIONS = [
|
|
13636
13638
|
{
|
|
13637
13639
|
type: "function",
|
|
@@ -13651,7 +13653,7 @@ var TOOL_DEFINITIONS = [
|
|
|
13651
13653
|
},
|
|
13652
13654
|
timeout_ms: {
|
|
13653
13655
|
type: "number",
|
|
13654
|
-
description: "Timeout in milliseconds (default
|
|
13656
|
+
description: "Timeout in milliseconds (default 300000 / 5 min, max 1800000 / 30 min for long jobs like STAR alignment)"
|
|
13655
13657
|
}
|
|
13656
13658
|
},
|
|
13657
13659
|
required: ["command"]
|
|
@@ -13669,7 +13671,7 @@ var TOOL_DEFINITIONS = [
|
|
|
13669
13671
|
path: { type: "string", description: "Absolute or relative file path" },
|
|
13670
13672
|
max_lines: {
|
|
13671
13673
|
type: "number",
|
|
13672
|
-
description: "Maximum number of lines to return (default:
|
|
13674
|
+
description: "Maximum number of lines to return (default: 500)"
|
|
13673
13675
|
},
|
|
13674
13676
|
offset: {
|
|
13675
13677
|
type: "number",
|
|
@@ -13729,21 +13731,43 @@ var TOOL_DEFINITIONS = [
|
|
|
13729
13731
|
required: ["pattern"]
|
|
13730
13732
|
}
|
|
13731
13733
|
}
|
|
13734
|
+
},
|
|
13735
|
+
{
|
|
13736
|
+
type: "function",
|
|
13737
|
+
function: {
|
|
13738
|
+
name: "fetch_geo",
|
|
13739
|
+
description: "Query NCBI GEO database by accession number (GSE, GDS, GPL, GSM). Returns dataset metadata, sample info, organism, platform, and download links. Use this BEFORE asking the user to manually download data \u2014 always try fetch_geo first when a GEO accession is mentioned.",
|
|
13740
|
+
parameters: {
|
|
13741
|
+
type: "object",
|
|
13742
|
+
properties: {
|
|
13743
|
+
accession: {
|
|
13744
|
+
type: "string",
|
|
13745
|
+
description: 'GEO accession number, e.g. "GSE12345", "GDS1234", "GPL570"'
|
|
13746
|
+
},
|
|
13747
|
+
include_samples: {
|
|
13748
|
+
type: "boolean",
|
|
13749
|
+
description: "Whether to include individual sample (GSM) metadata (default: false, set true for small datasets)"
|
|
13750
|
+
}
|
|
13751
|
+
},
|
|
13752
|
+
required: ["accession"]
|
|
13753
|
+
}
|
|
13754
|
+
}
|
|
13732
13755
|
}
|
|
13733
13756
|
];
|
|
13734
|
-
async function executeTool(name, args) {
|
|
13757
|
+
async function executeTool(name, args, onStream) {
|
|
13735
13758
|
try {
|
|
13736
13759
|
switch (name) {
|
|
13737
13760
|
case "bash":
|
|
13738
13761
|
return await toolBash(
|
|
13739
13762
|
args["command"],
|
|
13740
13763
|
args["workdir"],
|
|
13741
|
-
args["timeout_ms"] ??
|
|
13764
|
+
args["timeout_ms"] ?? 3e5,
|
|
13765
|
+
onStream
|
|
13742
13766
|
);
|
|
13743
13767
|
case "read_file":
|
|
13744
13768
|
return toolReadFile(
|
|
13745
13769
|
args["path"],
|
|
13746
|
-
args["max_lines"] ??
|
|
13770
|
+
args["max_lines"] ?? 500,
|
|
13747
13771
|
args["offset"] ?? 0
|
|
13748
13772
|
);
|
|
13749
13773
|
case "write_file":
|
|
@@ -13755,6 +13779,11 @@ async function executeTool(name, args) {
|
|
|
13755
13779
|
args["pattern"],
|
|
13756
13780
|
args["path"] ?? process.cwd()
|
|
13757
13781
|
);
|
|
13782
|
+
case "fetch_geo":
|
|
13783
|
+
return await toolFetchGeo(
|
|
13784
|
+
args["accession"],
|
|
13785
|
+
args["include_samples"] ?? false
|
|
13786
|
+
);
|
|
13758
13787
|
default:
|
|
13759
13788
|
return { output: "", error: `Unknown tool: ${name}` };
|
|
13760
13789
|
}
|
|
@@ -13775,7 +13804,32 @@ function decodeBuffer(buf) {
|
|
|
13775
13804
|
}
|
|
13776
13805
|
}
|
|
13777
13806
|
}
|
|
13778
|
-
|
|
13807
|
+
var DANGEROUS_PATTERNS = [
|
|
13808
|
+
{ pattern: /rm\s+-rf\s+\/(?!\S)/, reason: "\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)" },
|
|
13809
|
+
{ pattern: /rm\s+-rf\s+~(?!\S)/, reason: "\u5220\u9664 home \u76EE\u5F55 (rm -rf ~)" },
|
|
13810
|
+
{ pattern: /rm\s+-rf\s+\$HOME(?!\S)/, reason: "\u5220\u9664 $HOME \u76EE\u5F55" },
|
|
13811
|
+
{ pattern: /dd\s+if=\/dev\/(?:zero|random|urandom)\s+of=\/dev\//, reason: "\u8986\u5199\u78C1\u76D8\u8BBE\u5907 (dd)" },
|
|
13812
|
+
{ pattern: /mkfs\b/, reason: "\u683C\u5F0F\u5316\u6587\u4EF6\u7CFB\u7EDF (mkfs)" },
|
|
13813
|
+
{ pattern: />\s*\/dev\/sd[a-z]/, reason: "\u76F4\u63A5\u5199\u5165\u78C1\u76D8\u8BBE\u5907" },
|
|
13814
|
+
{ pattern: /chmod\s+-R\s+777\s+\/(?!\S)/, reason: "\u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6743\u9650" },
|
|
13815
|
+
{ pattern: /:\(\)\s*\{.*\}.*:/, reason: "Fork bomb \u68C0\u6D4B" }
|
|
13816
|
+
];
|
|
13817
|
+
function checkDangerousCommand(command) {
|
|
13818
|
+
for (const { pattern, reason } of DANGEROUS_PATTERNS) {
|
|
13819
|
+
if (pattern.test(command)) return reason;
|
|
13820
|
+
}
|
|
13821
|
+
return null;
|
|
13822
|
+
}
|
|
13823
|
+
async function toolBash(command, workdir, timeoutMs = 3e5, onStream) {
|
|
13824
|
+
const danger = checkDangerousCommand(command);
|
|
13825
|
+
if (danger) {
|
|
13826
|
+
return {
|
|
13827
|
+
output: "",
|
|
13828
|
+
error: `\u26A0\uFE0F \u5B89\u5168\u62E6\u622A\uFF1A\u68C0\u6D4B\u5230\u5371\u9669\u547D\u4EE4\uFF08${danger}\uFF09\u3002
|
|
13829
|
+
\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62\uFF0C\u8BF7\u786E\u8BA4\u4F60\u7684\u610F\u56FE\u540E\u624B\u52A8\u6267\u884C\u3002
|
|
13830
|
+
\u88AB\u62E6\u622A\u7684\u547D\u4EE4: ${command}`
|
|
13831
|
+
};
|
|
13832
|
+
}
|
|
13779
13833
|
return new Promise((resolve3) => {
|
|
13780
13834
|
const isWin = process.platform === "win32";
|
|
13781
13835
|
const child = (0, import_child_process.spawn)(isWin ? "cmd" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
|
|
@@ -13788,10 +13842,16 @@ async function toolBash(command, workdir, timeoutMs = 3e4) {
|
|
|
13788
13842
|
const MAX = 10 * 1024 * 1024;
|
|
13789
13843
|
let total = 0;
|
|
13790
13844
|
child.stdout?.on("data", (c2) => {
|
|
13791
|
-
if ((total += c2.length) <= MAX)
|
|
13845
|
+
if ((total += c2.length) <= MAX) {
|
|
13846
|
+
outChunks.push(c2);
|
|
13847
|
+
if (onStream) onStream(decodeBuffer(c2));
|
|
13848
|
+
}
|
|
13792
13849
|
});
|
|
13793
13850
|
child.stderr?.on("data", (c2) => {
|
|
13794
|
-
if ((total += c2.length) <= MAX)
|
|
13851
|
+
if ((total += c2.length) <= MAX) {
|
|
13852
|
+
errChunks.push(c2);
|
|
13853
|
+
if (onStream) onStream(decodeBuffer(c2));
|
|
13854
|
+
}
|
|
13795
13855
|
});
|
|
13796
13856
|
let timedOut = false;
|
|
13797
13857
|
const timer = setTimeout(() => {
|
|
@@ -13848,6 +13908,121 @@ async function toolSearchFiles(pattern, rootPath) {
|
|
|
13848
13908
|
const command = isWin ? `dir /s /b "${resolved}\\${pattern}" 2>nul` : `find "${resolved}" -name ${pattern.includes("/") ? pattern : `"${pattern}"`} 2>/dev/null | head -50`;
|
|
13849
13909
|
return toolBash(command, resolved, 1e4);
|
|
13850
13910
|
}
|
|
13911
|
+
function httpFetch(url, timeoutMs = 15e3) {
|
|
13912
|
+
return new Promise((resolve3, reject) => {
|
|
13913
|
+
const getter = url.startsWith("https") ? import_https.get : import_http.get;
|
|
13914
|
+
const req = getter(url, { headers: { "User-Agent": "BGI-CLI/1.0" } }, (res) => {
|
|
13915
|
+
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
13916
|
+
httpFetch(res.headers.location, timeoutMs).then(resolve3).catch(reject);
|
|
13917
|
+
return;
|
|
13918
|
+
}
|
|
13919
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
13920
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
13921
|
+
return;
|
|
13922
|
+
}
|
|
13923
|
+
const chunks = [];
|
|
13924
|
+
res.on("data", (c2) => chunks.push(c2));
|
|
13925
|
+
res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
13926
|
+
res.on("error", reject);
|
|
13927
|
+
});
|
|
13928
|
+
req.setTimeout(timeoutMs, () => {
|
|
13929
|
+
req.destroy();
|
|
13930
|
+
reject(new Error("Request timed out"));
|
|
13931
|
+
});
|
|
13932
|
+
req.on("error", reject);
|
|
13933
|
+
});
|
|
13934
|
+
}
|
|
13935
|
+
async function toolFetchGeo(accession, includeSamples) {
|
|
13936
|
+
const acc = accession.trim().toUpperCase();
|
|
13937
|
+
if (!/^(GSE|GDS|GPL|GSM)\d+$/.test(acc)) {
|
|
13938
|
+
return {
|
|
13939
|
+
output: "",
|
|
13940
|
+
error: `\u65E0\u6548\u7684 GEO \u7F16\u53F7: "${acc}"\u3002\u652F\u6301\u683C\u5F0F: GSE12345, GDS1234, GPL570, GSM123456`
|
|
13941
|
+
};
|
|
13942
|
+
}
|
|
13943
|
+
const accType = acc.slice(0, 3);
|
|
13944
|
+
try {
|
|
13945
|
+
const searchUrl = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=gds&term=${acc}[Accession]&retmode=json&retmax=1`;
|
|
13946
|
+
const searchRaw = await httpFetch(searchUrl);
|
|
13947
|
+
const searchJson = JSON.parse(searchRaw);
|
|
13948
|
+
const idList = searchJson.esearchresult?.idlist ?? [];
|
|
13949
|
+
if (idList.length === 0) {
|
|
13950
|
+
return { output: "", error: `\u672A\u627E\u5230 GEO \u8BB0\u5F55: ${acc}\u3002\u8BF7\u786E\u8BA4\u7F16\u53F7\u662F\u5426\u6B63\u786E\u3002` };
|
|
13951
|
+
}
|
|
13952
|
+
const uid = idList[0];
|
|
13953
|
+
const summaryUrl = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=gds&id=${uid}&retmode=json`;
|
|
13954
|
+
const summaryRaw = await httpFetch(summaryUrl);
|
|
13955
|
+
const summaryJson = JSON.parse(summaryRaw);
|
|
13956
|
+
const rec = summaryJson.result?.[uid];
|
|
13957
|
+
if (!rec) {
|
|
13958
|
+
return { output: "", error: `\u65E0\u6CD5\u83B7\u53D6 ${acc} \u7684\u8BE6\u7EC6\u4FE1\u606F` };
|
|
13959
|
+
}
|
|
13960
|
+
const lines = [];
|
|
13961
|
+
lines.push(`=== GEO \u6570\u636E\u96C6: ${acc} ===`);
|
|
13962
|
+
lines.push("");
|
|
13963
|
+
if (rec.title) lines.push(`\u6807\u9898: ${rec.title}`);
|
|
13964
|
+
if (rec.taxon || rec.organism)
|
|
13965
|
+
lines.push(`\u7269\u79CD: ${rec.taxon ?? rec.organism}`);
|
|
13966
|
+
if (rec.gdstype || rec.ptechtype)
|
|
13967
|
+
lines.push(`\u7C7B\u578B: ${rec.gdstype ?? rec.ptechtype}`);
|
|
13968
|
+
if (rec.gpl) lines.push(`\u5E73\u53F0: GPL${rec.gpl}`);
|
|
13969
|
+
if (rec.n_samples) lines.push(`\u6837\u672C\u6570: ${rec.n_samples}`);
|
|
13970
|
+
if (rec.summary) {
|
|
13971
|
+
const shortSummary = rec.summary.length > 500 ? rec.summary.slice(0, 500) + "..." : rec.summary;
|
|
13972
|
+
lines.push("");
|
|
13973
|
+
lines.push(`\u6458\u8981:
|
|
13974
|
+
${shortSummary}`);
|
|
13975
|
+
}
|
|
13976
|
+
lines.push("");
|
|
13977
|
+
lines.push("=== \u4E0B\u8F7D\u94FE\u63A5 ===");
|
|
13978
|
+
lines.push(`GEO \u9875\u9762: https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=${acc}`);
|
|
13979
|
+
if (accType === "GSE") {
|
|
13980
|
+
lines.push(`\u77E9\u9635\u6587\u4EF6: https://ftp.ncbi.nlm.nih.gov/geo/series/${acc.slice(0, -3)}nnn/${acc}/matrix/`);
|
|
13981
|
+
lines.push(`\u539F\u59CB\u6570\u636E: https://ftp.ncbi.nlm.nih.gov/geo/series/${acc.slice(0, -3)}nnn/${acc}/suppl/`);
|
|
13982
|
+
lines.push("");
|
|
13983
|
+
lines.push("=== R \u4E0B\u8F7D\u4EE3\u7801 ===");
|
|
13984
|
+
lines.push("```r");
|
|
13985
|
+
lines.push("# \u65B9\u6CD51: GEOquery\uFF08\u63A8\u8350\uFF0C\u81EA\u52A8\u89E3\u6790\uFF09");
|
|
13986
|
+
lines.push('if (!require("GEOquery")) BiocManager::install("GEOquery")');
|
|
13987
|
+
lines.push(`gse <- getGEO("${acc}", GSEMatrix = TRUE, getGPL = FALSE)`);
|
|
13988
|
+
lines.push("expr_matrix <- exprs(gse[[1]]) # \u8868\u8FBE\u77E9\u9635");
|
|
13989
|
+
lines.push("pheno_data <- pData(gse[[1]]) # \u6837\u672C\u5143\u6570\u636E");
|
|
13990
|
+
lines.push("");
|
|
13991
|
+
lines.push("# \u65B9\u6CD52: \u76F4\u63A5\u4E0B\u8F7D\u77E9\u9635\u6587\u4EF6");
|
|
13992
|
+
lines.push(`url <- "https://ftp.ncbi.nlm.nih.gov/geo/series/${acc.slice(0, -3)}nnn/${acc}/matrix/${acc}_series_matrix.txt.gz"`);
|
|
13993
|
+
lines.push('download.file(url, destfile = "series_matrix.txt.gz")');
|
|
13994
|
+
lines.push("```");
|
|
13995
|
+
lines.push("");
|
|
13996
|
+
lines.push("=== Python \u4E0B\u8F7D\u4EE3\u7801 ===");
|
|
13997
|
+
lines.push("```python");
|
|
13998
|
+
lines.push("import GEOparse");
|
|
13999
|
+
lines.push(`gse = GEOparse.get_GEO(geo="${acc}", destdir="./data")`);
|
|
14000
|
+
lines.push("# gse.gsms \u2014 \u6837\u672C\u5B57\u5178");
|
|
14001
|
+
lines.push("# gse.gpls \u2014 \u5E73\u53F0\u4FE1\u606F");
|
|
14002
|
+
lines.push("```");
|
|
14003
|
+
}
|
|
14004
|
+
if (rec.suppfile) {
|
|
14005
|
+
lines.push("");
|
|
14006
|
+
lines.push(`\u8865\u5145\u6587\u4EF6: ${rec.suppfile}`);
|
|
14007
|
+
}
|
|
14008
|
+
if (includeSamples && rec.samples && rec.samples.length > 0) {
|
|
14009
|
+
lines.push("");
|
|
14010
|
+
lines.push(`=== \u6837\u672C\u5217\u8868 (${rec.samples.length} \u4E2A) ===`);
|
|
14011
|
+
const showSamples = rec.samples.slice(0, 20);
|
|
14012
|
+
showSamples.forEach((s2) => lines.push(` ${s2.accession}: ${s2.title}`));
|
|
14013
|
+
if (rec.samples.length > 20) {
|
|
14014
|
+
lines.push(` ... \u8FD8\u6709 ${rec.samples.length - 20} \u4E2A\u6837\u672C`);
|
|
14015
|
+
}
|
|
14016
|
+
}
|
|
14017
|
+
return { output: lines.join("\n") };
|
|
14018
|
+
} catch (err) {
|
|
14019
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14020
|
+
return {
|
|
14021
|
+
output: `GEO \u9875\u9762: https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=${acc}`,
|
|
14022
|
+
error: `\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25 (${msg})\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\uFF0C\u6216\u76F4\u63A5\u8BBF\u95EE\u4E0A\u65B9\u94FE\u63A5\u3002`
|
|
14023
|
+
};
|
|
14024
|
+
}
|
|
14025
|
+
}
|
|
13851
14026
|
|
|
13852
14027
|
// src/chat.ts
|
|
13853
14028
|
async function chat(messages, config, systemPrompt) {
|
|
@@ -13872,6 +14047,7 @@ async function chat(messages, config, systemPrompt) {
|
|
|
13872
14047
|
async function streamLoop(client, messages, model) {
|
|
13873
14048
|
let finalText = "";
|
|
13874
14049
|
for (let round = 0; round < 20; round++) {
|
|
14050
|
+
messages = deduplicateSkillInjections(trimToolOutputs(messages));
|
|
13875
14051
|
const { text, toolCalls, finishReason } = await streamOnce(client, messages, model);
|
|
13876
14052
|
if (text) finalText = text;
|
|
13877
14053
|
if (finishReason === "tool_calls" && toolCalls.length > 0) {
|
|
@@ -13888,28 +14064,63 @@ async function streamLoop(client, messages, model) {
|
|
|
13888
14064
|
const SPIN_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
13889
14065
|
for (const tc of toolCalls) {
|
|
13890
14066
|
const args = parseArgs(tc.args);
|
|
14067
|
+
const isBash = tc.name === "bash";
|
|
13891
14068
|
const label = source_default.dim(`[\u5DE5\u5177: ${tc.name}(${summarizeArgs(args)})]`);
|
|
13892
14069
|
const t0 = Date.now();
|
|
13893
|
-
let frame = 0;
|
|
13894
14070
|
process.stdout.write(`
|
|
13895
|
-
${label}
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
const
|
|
13903
|
-
|
|
14071
|
+
${label}
|
|
14072
|
+
`);
|
|
14073
|
+
let streamedLines = 0;
|
|
14074
|
+
let lastLineWasEmpty = false;
|
|
14075
|
+
const MAX_STREAM_LINES = 200;
|
|
14076
|
+
let spin = null;
|
|
14077
|
+
let frame = 0;
|
|
14078
|
+
const onStream = isBash ? (chunk) => {
|
|
14079
|
+
if (streamedLines >= MAX_STREAM_LINES) return;
|
|
14080
|
+
const lines = chunk.split("\n");
|
|
14081
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
14082
|
+
const line = lines[i2];
|
|
14083
|
+
if (line.trim() === "") {
|
|
14084
|
+
if (lastLineWasEmpty) continue;
|
|
14085
|
+
lastLineWasEmpty = true;
|
|
14086
|
+
} else {
|
|
14087
|
+
lastLineWasEmpty = false;
|
|
14088
|
+
}
|
|
14089
|
+
if (i2 < lines.length - 1 || line.length > 0) {
|
|
14090
|
+
process.stdout.write(source_default.dim(" \u2502 ") + line + (i2 < lines.length - 1 ? "\n" : ""));
|
|
14091
|
+
streamedLines++;
|
|
14092
|
+
if (streamedLines >= MAX_STREAM_LINES) {
|
|
14093
|
+
process.stdout.write(source_default.dim("\n \u2502 ... (\u8F93\u51FA\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD)\n"));
|
|
14094
|
+
break;
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14097
|
+
}
|
|
14098
|
+
} : void 0;
|
|
14099
|
+
if (!isBash) {
|
|
14100
|
+
spin = setInterval(() => {
|
|
14101
|
+
const secs = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
14102
|
+
process.stdout.write(
|
|
14103
|
+
`\r ${source_default.cyan(SPIN_FRAMES[frame++ % SPIN_FRAMES.length])} ${source_default.dim(secs + "s")}`
|
|
14104
|
+
);
|
|
14105
|
+
}, 80);
|
|
14106
|
+
}
|
|
14107
|
+
const result = await executeTool(tc.name, args, onStream);
|
|
14108
|
+
if (spin) {
|
|
14109
|
+
clearInterval(spin);
|
|
14110
|
+
process.stdout.write("\r\x1B[2K");
|
|
14111
|
+
}
|
|
13904
14112
|
const elapsed = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
13905
14113
|
const doneIcon = result.error ? source_default.yellow("\u2717") : source_default.green("\u2713");
|
|
13906
|
-
|
|
14114
|
+
if (isBash && streamedLines > 0) {
|
|
14115
|
+
process.stdout.write("\n");
|
|
14116
|
+
}
|
|
14117
|
+
process.stdout.write(` ${doneIcon} ${source_default.dim("\u5B8C\u6210 " + elapsed + "s")}
|
|
13907
14118
|
`);
|
|
13908
14119
|
if (result.error) {
|
|
13909
14120
|
process.stdout.write(source_default.yellow(` \u26A0 ${result.error}
|
|
13910
14121
|
`));
|
|
13911
14122
|
}
|
|
13912
|
-
if (result.output) {
|
|
14123
|
+
if (!isBash && result.output) {
|
|
13913
14124
|
const preview = result.output.split("\n").slice(0, 3).join("\n");
|
|
13914
14125
|
const more = result.output.split("\n").length > 3;
|
|
13915
14126
|
process.stdout.write(source_default.dim(` ${preview}${more ? "\n ..." : ""}
|
|
@@ -13974,6 +14185,52 @@ async function streamOnce(client, messages, model) {
|
|
|
13974
14185
|
finishReason
|
|
13975
14186
|
};
|
|
13976
14187
|
}
|
|
14188
|
+
function estimateTokens(messages) {
|
|
14189
|
+
const chars = messages.reduce((n2, m2) => {
|
|
14190
|
+
const content = m2.content;
|
|
14191
|
+
if (typeof content === "string") return n2 + content.length;
|
|
14192
|
+
if (Array.isArray(content)) {
|
|
14193
|
+
return n2 + content.reduce((s2, c2) => s2 + (typeof c2 === "object" && "text" in c2 ? c2.text.length : 0), 0);
|
|
14194
|
+
}
|
|
14195
|
+
return n2;
|
|
14196
|
+
}, 0);
|
|
14197
|
+
return Math.round(chars / 3.5);
|
|
14198
|
+
}
|
|
14199
|
+
var MAX_TOOL_OUTPUT_CHARS = 8e3;
|
|
14200
|
+
function trimToolOutputs(messages) {
|
|
14201
|
+
return messages.map((m2) => {
|
|
14202
|
+
if (m2.role !== "tool") return m2;
|
|
14203
|
+
const content = typeof m2.content === "string" ? m2.content : "";
|
|
14204
|
+
if (content.length <= MAX_TOOL_OUTPUT_CHARS) return m2;
|
|
14205
|
+
const head = content.slice(0, MAX_TOOL_OUTPUT_CHARS / 2);
|
|
14206
|
+
const tail = content.slice(-MAX_TOOL_OUTPUT_CHARS / 2);
|
|
14207
|
+
const trimmed = `${head}
|
|
14208
|
+
|
|
14209
|
+
... [\u8F93\u51FA\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD ${content.length - MAX_TOOL_OUTPUT_CHARS} \u5B57\u7B26] ...
|
|
14210
|
+
|
|
14211
|
+
${tail}`;
|
|
14212
|
+
return { ...m2, content: trimmed };
|
|
14213
|
+
});
|
|
14214
|
+
}
|
|
14215
|
+
function deduplicateSkillInjections(messages) {
|
|
14216
|
+
const SKILL_MARKER = "[Skill \u5DF2\u52A0\u8F7D:";
|
|
14217
|
+
const seenSkills = /* @__PURE__ */ new Set();
|
|
14218
|
+
const result = [];
|
|
14219
|
+
for (let i2 = messages.length - 1; i2 >= 0; i2--) {
|
|
14220
|
+
const m2 = messages[i2];
|
|
14221
|
+
const content = typeof m2.content === "string" ? m2.content : "";
|
|
14222
|
+
if (m2.role === "user" && content.startsWith(SKILL_MARKER)) {
|
|
14223
|
+
const idMatch = content.match(/\[Skill 已加载: ([^\]]+)\]/);
|
|
14224
|
+
const skillId = idMatch?.[1];
|
|
14225
|
+
if (skillId) {
|
|
14226
|
+
if (seenSkills.has(skillId)) continue;
|
|
14227
|
+
seenSkills.add(skillId);
|
|
14228
|
+
}
|
|
14229
|
+
}
|
|
14230
|
+
result.unshift(m2);
|
|
14231
|
+
}
|
|
14232
|
+
return result;
|
|
14233
|
+
}
|
|
13977
14234
|
async function compactMessages(messages, config) {
|
|
13978
14235
|
const prov = PROVIDERS[config.provider];
|
|
13979
14236
|
if (!prov) throw new Error(`Unknown provider: ${config.provider}`);
|
|
@@ -14040,6 +14297,7 @@ You have access to these tools:
|
|
|
14040
14297
|
- **write_file**: Create or update files
|
|
14041
14298
|
- **list_dir**: List directory contents
|
|
14042
14299
|
- **search_files**: Find files by pattern (glob)
|
|
14300
|
+
- **fetch_geo**: Query NCBI GEO database by accession (GSE/GDS/GPL/GSM). Returns metadata, sample info, organism, platform, and ready-to-use R/Python download code. **Always call this first when the user mentions a GEO accession number \u2014 never ask them to download manually.**
|
|
14043
14301
|
|
|
14044
14302
|
**MANDATORY WORKFLOW**: When the user gives you a bioinformatics task:
|
|
14045
14303
|
1. Check if a matching pre-built workflow exists (see Workflow Library below)
|
|
@@ -14092,6 +14350,7 @@ cat ${WORKFLOWS_DIR}/<workflow-id>/SKILL.md
|
|
|
14092
14350
|
|
|
14093
14351
|
| ID | Use When |
|
|
14094
14352
|
|----|----------|
|
|
14353
|
+
| \`survival-analysis-clinical\` | \u751F\u5B58\u5206\u6790\uFF1AKM \u66F2\u7EBF\u3001log-rank \u68C0\u9A8C\u3001Cox \u56DE\u5F52\u3001\u7ADE\u4E89\u98CE\u9669\uFF08OS/PFS/DFS\uFF09 |
|
|
14095
14354
|
| \`clinicaltrials-landscape\` | ClinicalTrials.gov \u6570\u636E\u5206\u6790 |
|
|
14096
14355
|
| \`literature-preclinical\` | \u4E34\u5E8A\u524D\u6587\u732E\u7CFB\u7EDF\u63D0\u53D6\u4E0E\u7EFC\u5408 |
|
|
14097
14356
|
| \`experimental-design-statistics\` | \u7EDF\u8BA1\u68C0\u9A8C\u9009\u62E9\u3001\u6837\u672C\u91CF\u8BA1\u7B97\u3001\u968F\u673A\u5316\u65B9\u6848 |
|
|
@@ -14175,6 +14434,62 @@ samtools --version 2>&1 | head -1
|
|
|
14175
14434
|
|
|
14176
14435
|
---
|
|
14177
14436
|
|
|
14437
|
+
## Data Integrity Rules\uFF08\u5206\u6790\u524D\u5FC5\u987B\u6267\u884C\uFF09
|
|
14438
|
+
|
|
14439
|
+
\u{1F52C} **\u5728\u4EFB\u4F55\u7EDF\u8BA1\u5206\u6790\u5F00\u59CB\u524D\uFF0C\u5FC5\u987B\u5B8C\u6210\u4EE5\u4E0B\u68C0\u67E5\uFF1A**
|
|
14440
|
+
|
|
14441
|
+
### 1. \u6837\u672C ID \u4E00\u81F4\u6027
|
|
14442
|
+
\`\`\`r
|
|
14443
|
+
# R: \u68C0\u67E5 count \u77E9\u9635\u5217\u540D\u4E0E metadata \u884C\u540D\u662F\u5426\u5B8C\u5168\u4E00\u81F4
|
|
14444
|
+
stopifnot(all(colnames(counts) == rownames(metadata)))
|
|
14445
|
+
# \u5982\u679C\u4E0D\u4E00\u81F4\uFF0C\u5148\u5BF9\u9F50\u518D\u5206\u6790\uFF1A
|
|
14446
|
+
metadata <- metadata[colnames(counts), , drop = FALSE]
|
|
14447
|
+
\`\`\`
|
|
14448
|
+
\`\`\`python
|
|
14449
|
+
# Python: \u68C0\u67E5 AnnData obs_names \u4E0E metadata index \u662F\u5426\u4E00\u81F4
|
|
14450
|
+
assert list(adata.obs_names) == list(metadata.index), "\u6837\u672C ID \u4E0D\u5339\u914D\uFF01"
|
|
14451
|
+
\`\`\`
|
|
14452
|
+
|
|
14453
|
+
### 2. \u91CD\u590D\u884C/\u5217\u540D\u68C0\u6D4B
|
|
14454
|
+
\`\`\`r
|
|
14455
|
+
# \u68C0\u67E5\u91CD\u590D\u57FA\u56E0\u540D\uFF08\u884C\u540D\uFF09
|
|
14456
|
+
if (any(duplicated(rownames(counts)))) {
|
|
14457
|
+
warning("\u53D1\u73B0\u91CD\u590D\u57FA\u56E0\u540D\uFF0C\u5C06\u805A\u5408\u91CD\u590D\u884C\uFF08\u53D6\u5747\u503C\uFF09")
|
|
14458
|
+
counts <- aggregate(counts, by = list(rownames(counts)), FUN = mean)
|
|
14459
|
+
}
|
|
14460
|
+
\`\`\`
|
|
14461
|
+
|
|
14462
|
+
### 3. \u7F3A\u5931\u503C\u62A5\u544A
|
|
14463
|
+
\`\`\`r
|
|
14464
|
+
na_pct <- sum(is.na(counts)) / prod(dim(counts)) * 100
|
|
14465
|
+
message(sprintf("\u7F3A\u5931\u503C\u6BD4\u4F8B: %.2f%%", na_pct))
|
|
14466
|
+
if (na_pct > 5) warning("\u7F3A\u5931\u503C\u8D85\u8FC7 5%\uFF0C\u8BF7\u68C0\u67E5\u6570\u636E\u8D28\u91CF")
|
|
14467
|
+
\`\`\`
|
|
14468
|
+
|
|
14469
|
+
### 4. \u5DEE\u5F02\u8868\u8FBE\u5206\u6790\uFF1A\u5FC5\u987B\u4F7F\u7528 padj\uFF0C\u7981\u6B62\u4F7F\u7528\u539F\u59CB pvalue
|
|
14470
|
+
\`\`\`r
|
|
14471
|
+
# \u2705 \u6B63\u786E\uFF1A\u4F7F\u7528 FDR \u6821\u6B63\u540E\u7684 p \u503C
|
|
14472
|
+
sig_genes <- res[!is.na(res$padj) & res$padj <= 0.05 & abs(res$log2FoldChange) >= 1, ]
|
|
14473
|
+
|
|
14474
|
+
# \u274C \u9519\u8BEF\uFF1A\u4E0D\u8981\u7528\u539F\u59CB pvalue \u7B5B\u9009 DEG
|
|
14475
|
+
# sig_genes <- res[res$pvalue < 0.05, ] # \u8FD9\u662F\u9519\u7684\uFF01
|
|
14476
|
+
\`\`\`
|
|
14477
|
+
|
|
14478
|
+
**\u6807\u51C6\u9608\u503C**\uFF08\u9664\u975E\u7528\u6237\u660E\u786E\u6307\u5B9A\u5176\u4ED6\u503C\uFF09\uFF1A
|
|
14479
|
+
- \u663E\u8457\u6027\uFF1A**padj \u2264 0.05**\uFF08FDR \u6821\u6B63\uFF0CBenjamini-Hochberg\uFF09
|
|
14480
|
+
- \u6548\u5E94\u91CF\uFF1A**|log2FoldChange| \u2265 1**\uFF08\u5373 2 \u500D\u5DEE\u5F02\uFF09
|
|
14481
|
+
|
|
14482
|
+
### 5. \u7ED3\u679C\u9A8C\u8BC1
|
|
14483
|
+
\u6BCF\u4E2A\u5206\u6790\u5B8C\u6210\u540E\uFF0C\u8F93\u51FA\u5173\u952E\u7EDF\u8BA1\u6458\u8981\uFF1A
|
|
14484
|
+
\`\`\`r
|
|
14485
|
+
message(sprintf("\u603B\u57FA\u56E0\u6570: %d | \u663E\u8457 DEG: %d (\u4E0A\u8C03: %d, \u4E0B\u8C03: %d)",
|
|
14486
|
+
nrow(res), nrow(sig_genes),
|
|
14487
|
+
sum(sig_genes$log2FoldChange > 0),
|
|
14488
|
+
sum(sig_genes$log2FoldChange < 0)))
|
|
14489
|
+
\`\`\`
|
|
14490
|
+
|
|
14491
|
+
---
|
|
14492
|
+
|
|
14178
14493
|
## Script Execution Rules
|
|
14179
14494
|
|
|
14180
14495
|
\u{1F6A8} **\u5173\u952E\u89C4\u5219\uFF1A**
|
|
@@ -14200,7 +14515,25 @@ samtools --version 2>&1 | head -1
|
|
|
14200
14515
|
\u4F7F\u7528 functional-enrichment-from-degs \u5DE5\u4F5C\u6D41
|
|
14201
14516
|
|
|
14202
14517
|
**\u7528\u6237\u8BF4 "\u8BBE\u8BA1 CRISPR guide RNA" \u2192**
|
|
14203
|
-
\u4F7F\u7528 molecular_biology.py \u7684 design_crispr_knockout_guides()
|
|
14518
|
+
\u4F7F\u7528 molecular_biology.py \u7684 design_crispr_knockout_guides()
|
|
14519
|
+
|
|
14520
|
+
**\u7528\u6237\u8BF4 "\u54EA\u4E9B\u57FA\u56E0\u5728\u80BF\u7624\u91CC\u8868\u8FBE\u91CF\u9AD8" / "\u627E\u4E0A\u8C03\u57FA\u56E0" \u2192**
|
|
14521
|
+
\u2192 \u5DEE\u5F02\u8868\u8FBE\u5206\u6790\uFF08DESeq2\uFF09\uFF0C\u4F7F\u7528 bulk-rnaseq-counts-to-de-deseq2 \u5DE5\u4F5C\u6D41
|
|
14522
|
+
|
|
14523
|
+
**\u7528\u6237\u8BF4 "\u5148\u505A\u5DEE\u5F02\u8868\u8FBE\uFF0C\u518D\u505A\u5BCC\u96C6\u5206\u6790" \u2192**
|
|
14524
|
+
\u2192 \u8BC6\u522B\u4E3A\u591A\u4EFB\u52A1\uFF1A\u4F9D\u6B21\u6267\u884C bulk-rnaseq-counts-to-de-deseq2 + functional-enrichment-from-degs
|
|
14525
|
+
|
|
14526
|
+
**\u7528\u6237\u8BF4 "\u8FD9\u4E9B\u57FA\u56E0\u53C2\u4E0E\u4EC0\u4E48\u901A\u8DEF" / "\u57FA\u56E0\u529F\u80FD\u662F\u4EC0\u4E48" \u2192**
|
|
14527
|
+
\u2192 \u529F\u80FD\u5BCC\u96C6\u5206\u6790\uFF0C\u4F7F\u7528 functional-enrichment-from-degs \u5DE5\u4F5C\u6D41
|
|
14528
|
+
|
|
14529
|
+
**\u7528\u6237\u8BF4 "\u5206\u6790\u5355\u7EC6\u80DE\u6570\u636E" / "10X\u6570\u636E\u5206\u6790" \u2192**
|
|
14530
|
+
\u5148\u95EE\uFF1APython \u8FD8\u662F R\uFF1F\u2192 Scanpy \u6216 Seurat
|
|
14531
|
+
|
|
14532
|
+
**\u7528\u6237\u8BF4 "\u753B\u751F\u5B58\u66F2\u7EBF" / "\u5206\u6790\u60A3\u8005\u9884\u540E" / "OS/PFS \u5206\u6790" \u2192**
|
|
14533
|
+
\u2192 \u751F\u5B58\u5206\u6790\uFF0C\u4F7F\u7528 survival-analysis-clinical \u5DE5\u4F5C\u6D41
|
|
14534
|
+
|
|
14535
|
+
**\u7528\u6237\u8BF4 "\u5E2E\u6211\u5206\u6790 GSE12345" / "\u4E0B\u8F7D GEO \u6570\u636E" \u2192**
|
|
14536
|
+
\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`;
|
|
14204
14537
|
}
|
|
14205
14538
|
|
|
14206
14539
|
// src/skillRouter.ts
|
|
@@ -14225,8 +14558,11 @@ var SKILL_ROUTES = [
|
|
|
14225
14558
|
category: "\u8F6C\u5F55\u7EC4",
|
|
14226
14559
|
tag: "workflow",
|
|
14227
14560
|
keywords: [
|
|
14561
|
+
// exact tool names
|
|
14228
14562
|
"deseq2",
|
|
14229
14563
|
"edger",
|
|
14564
|
+
"limma-voom",
|
|
14565
|
+
// explicit analysis terms
|
|
14230
14566
|
"rna-seq\u5DEE\u5F02",
|
|
14231
14567
|
"rnaseq\u5DEE\u5F02",
|
|
14232
14568
|
"\u5DEE\u5F02\u8868\u8FBE\u5206\u6790",
|
|
@@ -14234,7 +14570,21 @@ var SKILL_ROUTES = [
|
|
|
14234
14570
|
"count\u77E9\u9635",
|
|
14235
14571
|
"count matrix",
|
|
14236
14572
|
"\u539F\u59CBcounts",
|
|
14237
|
-
"raw counts"
|
|
14573
|
+
"raw counts",
|
|
14574
|
+
// natural-language synonyms (the main gap in the original)
|
|
14575
|
+
"\u54EA\u4E9B\u57FA\u56E0\u8868\u8FBE\u91CF\u9AD8",
|
|
14576
|
+
"\u54EA\u4E9B\u57FA\u56E0\u4E0A\u8C03",
|
|
14577
|
+
"\u54EA\u4E9B\u57FA\u56E0\u4E0B\u8C03",
|
|
14578
|
+
"\u57FA\u56E0\u8868\u8FBE\u5DEE\u5F02",
|
|
14579
|
+
"\u8F6C\u5F55\u7EC4\u5DEE\u5F02",
|
|
14580
|
+
"\u8868\u8FBE\u91CF\u5DEE\u5F02",
|
|
14581
|
+
"\u4E0A\u8C03\u57FA\u56E0",
|
|
14582
|
+
"\u4E0B\u8C03\u57FA\u56E0",
|
|
14583
|
+
"deg\u5206\u6790",
|
|
14584
|
+
"differentially expressed",
|
|
14585
|
+
"\u5DEE\u5F02\u5206\u6790",
|
|
14586
|
+
"\u6BD4\u8F83\u4E24\u7EC4\u57FA\u56E0\u8868\u8FBE",
|
|
14587
|
+
"\u80BF\u7624vs\u6B63\u5E38"
|
|
14238
14588
|
]
|
|
14239
14589
|
},
|
|
14240
14590
|
{
|
|
@@ -14250,7 +14600,16 @@ var SKILL_ROUTES = [
|
|
|
14250
14600
|
"hdbscan",
|
|
14251
14601
|
"\u6837\u672C\u805A\u7C7B",
|
|
14252
14602
|
"\u7279\u5F81\u805A\u7C7B",
|
|
14253
|
-
"omics clustering"
|
|
14603
|
+
"omics clustering",
|
|
14604
|
+
// natural-language synonyms
|
|
14605
|
+
"\u6837\u672C\u5206\u7EC4",
|
|
14606
|
+
"\u6837\u672C\u5206\u7C7B",
|
|
14607
|
+
"\u805A\u7C7B\u5206\u6790",
|
|
14608
|
+
"\u65E0\u76D1\u7763\u805A\u7C7B",
|
|
14609
|
+
"\u70ED\u56FE\u805A\u7C7B",
|
|
14610
|
+
"pca\u5206\u6790",
|
|
14611
|
+
"\u4E3B\u6210\u5206\u5206\u6790",
|
|
14612
|
+
"umap\u964D\u7EF4"
|
|
14254
14613
|
]
|
|
14255
14614
|
},
|
|
14256
14615
|
{
|
|
@@ -14266,7 +14625,16 @@ var SKILL_ROUTES = [
|
|
|
14266
14625
|
"10x chromium",
|
|
14267
14626
|
"leiden\u805A\u7C7B",
|
|
14268
14627
|
"python\u5355\u7EC6\u80DE",
|
|
14269
|
-
"anndata\u5206\u6790"
|
|
14628
|
+
"anndata\u5206\u6790",
|
|
14629
|
+
// natural-language synonyms
|
|
14630
|
+
"\u5355\u7EC6\u80DE\u6D4B\u5E8F",
|
|
14631
|
+
"\u5355\u7EC6\u80DE\u5206\u6790",
|
|
14632
|
+
"\u5355\u7EC6\u80DE\u6570\u636E",
|
|
14633
|
+
"10x\u6570\u636E",
|
|
14634
|
+
"\u7EC6\u80DE\u805A\u7C7B",
|
|
14635
|
+
"\u7EC6\u80DE\u7C7B\u578B\u9274\u5B9A",
|
|
14636
|
+
"scrna",
|
|
14637
|
+
"sc-rna"
|
|
14270
14638
|
]
|
|
14271
14639
|
},
|
|
14272
14640
|
{
|
|
@@ -14280,7 +14648,11 @@ var SKILL_ROUTES = [
|
|
|
14280
14648
|
"findclusters",
|
|
14281
14649
|
"findneighbors",
|
|
14282
14650
|
"sctransform",
|
|
14283
|
-
"r\u5355\u7EC6\u80DE\u5206\u6790"
|
|
14651
|
+
"r\u5355\u7EC6\u80DE\u5206\u6790",
|
|
14652
|
+
// natural-language synonyms
|
|
14653
|
+
"r\u505A\u5355\u7EC6\u80DE",
|
|
14654
|
+
"seurat\u5206\u6790",
|
|
14655
|
+
"\u7528r\u5206\u6790\u5355\u7EC6\u80DE"
|
|
14284
14656
|
]
|
|
14285
14657
|
},
|
|
14286
14658
|
{
|
|
@@ -14296,7 +14668,13 @@ var SKILL_ROUTES = [
|
|
|
14296
14668
|
"spatial deconvolution",
|
|
14297
14669
|
"\u914D\u4F53\u53D7\u4F53\u5206\u6790",
|
|
14298
14670
|
"\u7A7A\u95F4\u57FA\u56E0\u8868\u8FBE",
|
|
14299
|
-
"stereo-seq"
|
|
14671
|
+
"stereo-seq",
|
|
14672
|
+
// natural-language synonyms
|
|
14673
|
+
"\u7A7A\u95F4\u7EC4\u5B66",
|
|
14674
|
+
"\u7EC4\u7EC7\u5207\u7247\u6D4B\u5E8F",
|
|
14675
|
+
"\u7A7A\u95F4\u5206\u8FA8\u7387\u8F6C\u5F55\u7EC4",
|
|
14676
|
+
"\u7EC6\u80DE\u7A7A\u95F4\u5206\u5E03",
|
|
14677
|
+
"\u7A7A\u95F4\u5355\u7EC6\u80DE"
|
|
14300
14678
|
]
|
|
14301
14679
|
},
|
|
14302
14680
|
{
|
|
@@ -14310,7 +14688,12 @@ var SKILL_ROUTES = [
|
|
|
14310
14688
|
"coexpression network",
|
|
14311
14689
|
"\u57FA\u56E0\u5171\u8868\u8FBE\u6A21\u5757",
|
|
14312
14690
|
"weighted gene coexpression",
|
|
14313
|
-
"\u4E0E\u8868\u578B\u76F8\u5173\u7684\u57FA\u56E0\u6A21\u5757"
|
|
14691
|
+
"\u4E0E\u8868\u578B\u76F8\u5173\u7684\u57FA\u56E0\u6A21\u5757",
|
|
14692
|
+
// natural-language synonyms
|
|
14693
|
+
"\u57FA\u56E0\u6A21\u5757",
|
|
14694
|
+
"\u5171\u8868\u8FBE",
|
|
14695
|
+
"\u57FA\u56E0\u7F51\u7EDC",
|
|
14696
|
+
"\u57FA\u56E0\u76F8\u5173\u6027\u7F51\u7EDC"
|
|
14314
14697
|
]
|
|
14315
14698
|
},
|
|
14316
14699
|
{
|
|
@@ -14330,7 +14713,19 @@ var SKILL_ROUTES = [
|
|
|
14330
14713
|
"functional enrichment",
|
|
14331
14714
|
"\u529F\u80FD\u5BCC\u96C6",
|
|
14332
14715
|
"deg\u5BCC\u96C6",
|
|
14333
|
-
"\u5DEE\u5F02\u57FA\u56E0\u901A\u8DEF"
|
|
14716
|
+
"\u5DEE\u5F02\u57FA\u56E0\u901A\u8DEF",
|
|
14717
|
+
// natural-language synonyms
|
|
14718
|
+
"\u8FD9\u4E9B\u57FA\u56E0\u53C2\u4E0E\u4EC0\u4E48\u901A\u8DEF",
|
|
14719
|
+
"\u57FA\u56E0\u529F\u80FD\u6CE8\u91CA",
|
|
14720
|
+
"\u4FE1\u53F7\u901A\u8DEF",
|
|
14721
|
+
"\u751F\u7269\u5B66\u8FC7\u7A0B",
|
|
14722
|
+
"\u5206\u5B50\u529F\u80FD",
|
|
14723
|
+
"\u7EC6\u80DE\u7EC4\u5206",
|
|
14724
|
+
"go\u5BCC\u96C6",
|
|
14725
|
+
"\u901A\u8DEF\u5BCC\u96C6",
|
|
14726
|
+
"\u57FA\u56E0\u96C6\u5206\u6790",
|
|
14727
|
+
"ora\u5206\u6790",
|
|
14728
|
+
"gsea\u5206\u6790"
|
|
14334
14729
|
]
|
|
14335
14730
|
},
|
|
14336
14731
|
{
|
|
@@ -14345,7 +14740,12 @@ var SKILL_ROUTES = [
|
|
|
14345
14740
|
"gene regulatory network",
|
|
14346
14741
|
"\u8F6C\u5F55\u56E0\u5B50\u8C03\u63A7\u5B50",
|
|
14347
14742
|
"tf regulon",
|
|
14348
|
-
"grn\u63A8\u65AD"
|
|
14743
|
+
"grn\u63A8\u65AD",
|
|
14744
|
+
// natural-language synonyms
|
|
14745
|
+
"\u8F6C\u5F55\u56E0\u5B50\u5206\u6790",
|
|
14746
|
+
"\u8F6C\u5F55\u56E0\u5B50\u9776\u57FA\u56E0",
|
|
14747
|
+
"tf\u6D3B\u6027",
|
|
14748
|
+
"\u8C03\u63A7\u7F51\u7EDC\u63A8\u65AD"
|
|
14349
14749
|
]
|
|
14350
14750
|
},
|
|
14351
14751
|
// ── Genomics ──────────────────────────────────────────────────────────────────
|
|
@@ -14364,7 +14764,14 @@ var SKILL_ROUTES = [
|
|
|
14364
14764
|
"annovar",
|
|
14365
14765
|
"\u53D8\u5F02\u81F4\u75C5\u6027\u9884\u6D4B",
|
|
14366
14766
|
"\u53D8\u5F02\u529F\u80FD\u9884\u6D4B",
|
|
14367
|
-
"clinvar\u6CE8\u91CA"
|
|
14767
|
+
"clinvar\u6CE8\u91CA",
|
|
14768
|
+
// natural-language synonyms
|
|
14769
|
+
"\u7A81\u53D8\u6CE8\u91CA",
|
|
14770
|
+
"\u53D8\u5F02\u89E3\u8BFB",
|
|
14771
|
+
"vcf\u6587\u4EF6\u5206\u6790",
|
|
14772
|
+
"\u9057\u4F20\u53D8\u5F02\u89E3\u6790",
|
|
14773
|
+
"\u81F4\u75C5\u6027\u8BC4\u4F30",
|
|
14774
|
+
"\u53D8\u5F02\u5F71\u54CD\u9884\u6D4B"
|
|
14368
14775
|
]
|
|
14369
14776
|
},
|
|
14370
14777
|
{
|
|
@@ -14380,7 +14787,14 @@ var SKILL_ROUTES = [
|
|
|
14380
14787
|
"\u5168\u57FA\u56E0\u7EC4\u5173\u8054\u5206\u6790",
|
|
14381
14788
|
"genome-wide association",
|
|
14382
14789
|
"\u56E0\u679C\u57FA\u56E0\u9274\u5B9A",
|
|
14383
|
-
"qtl\u6574\u5408"
|
|
14790
|
+
"qtl\u6574\u5408",
|
|
14791
|
+
// natural-language synonyms
|
|
14792
|
+
"gwas\u7ED3\u679C\u89E3\u8BFB",
|
|
14793
|
+
"gwas\u529F\u80FD\u6CE8\u91CA",
|
|
14794
|
+
"\u5173\u8054\u4F4D\u70B9\u57FA\u56E0",
|
|
14795
|
+
"snp\u529F\u80FD",
|
|
14796
|
+
"gwas\u4FE1\u53F7",
|
|
14797
|
+
"\u9057\u4F20\u5173\u8054\u5206\u6790"
|
|
14384
14798
|
]
|
|
14385
14799
|
},
|
|
14386
14800
|
{
|
|
@@ -14396,7 +14810,12 @@ var SKILL_ROUTES = [
|
|
|
14396
14810
|
"ivw\u65B9\u6CD5",
|
|
14397
14811
|
"mr-egger",
|
|
14398
14812
|
"\u53CC\u6837\u672Cmr",
|
|
14399
|
-
"\u5DE5\u5177\u53D8\u91CFiv"
|
|
14813
|
+
"\u5DE5\u5177\u53D8\u91CFiv",
|
|
14814
|
+
// natural-language synonyms
|
|
14815
|
+
"\u56E0\u679C\u63A8\u65AD",
|
|
14816
|
+
"\u66B4\u9732\u4E0E\u7ED3\u5C40",
|
|
14817
|
+
"\u9057\u4F20\u5DE5\u5177\u53D8\u91CF",
|
|
14818
|
+
"mr\u5206\u6790"
|
|
14400
14819
|
]
|
|
14401
14820
|
},
|
|
14402
14821
|
{
|
|
@@ -14410,7 +14829,11 @@ var SKILL_ROUTES = [
|
|
|
14410
14829
|
"\u591A\u57FA\u56E0\u98CE\u9669\u8BC4\u5206",
|
|
14411
14830
|
"prs-cs",
|
|
14412
14831
|
"\u9057\u4F20\u98CE\u9669\u9884\u6D4B",
|
|
14413
|
-
"prs\u8BA1\u7B97"
|
|
14832
|
+
"prs\u8BA1\u7B97",
|
|
14833
|
+
// natural-language synonyms
|
|
14834
|
+
"\u9057\u4F20\u98CE\u9669\u8BC4\u5206",
|
|
14835
|
+
"\u591A\u57FA\u56E0\u8BC4\u5206",
|
|
14836
|
+
"\u75BE\u75C5\u9057\u4F20\u98CE\u9669"
|
|
14414
14837
|
]
|
|
14415
14838
|
},
|
|
14416
14839
|
{
|
|
@@ -14425,7 +14848,13 @@ var SKILL_ROUTES = [
|
|
|
14425
14848
|
"bagel2",
|
|
14426
14849
|
"sgrna\u7B5B\u9009",
|
|
14427
14850
|
"pooled crispr",
|
|
14428
|
-
"crispr hit\u8BC6\u522B"
|
|
14851
|
+
"crispr hit\u8BC6\u522B",
|
|
14852
|
+
// natural-language synonyms
|
|
14853
|
+
"crispr\u7B5B\u9009",
|
|
14854
|
+
"\u529F\u80FD\u57FA\u56E0\u7EC4\u7B5B\u9009",
|
|
14855
|
+
"\u5FC5\u9700\u57FA\u56E0\u7B5B\u9009",
|
|
14856
|
+
"crispr\u6572\u9664\u7B5B\u9009",
|
|
14857
|
+
"crispr\u6587\u5E93"
|
|
14429
14858
|
]
|
|
14430
14859
|
},
|
|
14431
14860
|
// ── Epigenomics ───────────────────────────────────────────────────────────────
|
|
@@ -14439,7 +14868,13 @@ var SKILL_ROUTES = [
|
|
|
14439
14868
|
"chip-seq\u5CF0\u503C\u5BCC\u96C6",
|
|
14440
14869
|
"peak enrichment chip",
|
|
14441
14870
|
"chip atlas\u6570\u636E\u5E93",
|
|
14442
|
-
"histone chip\u5206\u6790"
|
|
14871
|
+
"histone chip\u5206\u6790",
|
|
14872
|
+
// natural-language synonyms
|
|
14873
|
+
"chip-seq\u5206\u6790",
|
|
14874
|
+
"chip\u6570\u636E",
|
|
14875
|
+
"\u7EC4\u86CB\u767D\u4FEE\u9970",
|
|
14876
|
+
"h3k27ac",
|
|
14877
|
+
"h3k4me3"
|
|
14443
14878
|
]
|
|
14444
14879
|
},
|
|
14445
14880
|
{
|
|
@@ -14452,7 +14887,11 @@ var SKILL_ROUTES = [
|
|
|
14452
14887
|
"differential binding",
|
|
14453
14888
|
"\u5DEE\u5F02chip-seq",
|
|
14454
14889
|
"differential peak",
|
|
14455
|
-
"chip-seq\u6761\u4EF6\u6BD4\u8F83"
|
|
14890
|
+
"chip-seq\u6761\u4EF6\u6BD4\u8F83",
|
|
14891
|
+
// natural-language synonyms
|
|
14892
|
+
"\u5DEE\u5F02\u5CF0",
|
|
14893
|
+
"\u5DEE\u5F02\u7ED3\u5408\u4F4D\u70B9",
|
|
14894
|
+
"chip-seq\u5DEE\u5F02"
|
|
14456
14895
|
]
|
|
14457
14896
|
},
|
|
14458
14897
|
{
|
|
@@ -14466,7 +14905,12 @@ var SKILL_ROUTES = [
|
|
|
14466
14905
|
"\u8F6C\u5F55\u56E0\u5B50\u9776\u57FA\u56E0chip",
|
|
14467
14906
|
"tf\u9776\u57FA\u56E0",
|
|
14468
14907
|
"peak annotation\u9776\u57FA\u56E0",
|
|
14469
|
-
"chip-seq peak\u6CE8\u91CA"
|
|
14908
|
+
"chip-seq peak\u6CE8\u91CA",
|
|
14909
|
+
// natural-language synonyms
|
|
14910
|
+
"peak\u6CE8\u91CA",
|
|
14911
|
+
"\u5CF0\u503C\u6CE8\u91CA",
|
|
14912
|
+
"\u8F6C\u5F55\u56E0\u5B50\u7ED3\u5408\u4F4D\u70B9",
|
|
14913
|
+
"chip\u9776\u70B9"
|
|
14470
14914
|
]
|
|
14471
14915
|
},
|
|
14472
14916
|
// ── Clinical ──────────────────────────────────────────────────────────────────
|
|
@@ -14480,7 +14924,12 @@ var SKILL_ROUTES = [
|
|
|
14480
14924
|
"clinical trial landscape",
|
|
14481
14925
|
"ct.gov\u6570\u636E\u5206\u6790",
|
|
14482
14926
|
"\u4E34\u5E8A\u8BD5\u9A8C\u683C\u5C40",
|
|
14483
|
-
"\u4E34\u5E8A\u7814\u7A76\u5206\u6790"
|
|
14927
|
+
"\u4E34\u5E8A\u7814\u7A76\u5206\u6790",
|
|
14928
|
+
// natural-language synonyms
|
|
14929
|
+
"\u4E34\u5E8A\u8BD5\u9A8C",
|
|
14930
|
+
"\u5728\u7814\u836F\u7269",
|
|
14931
|
+
"\u4E34\u5E8A\u7BA1\u7EBF",
|
|
14932
|
+
"\u4E34\u5E8A\u7814\u7A76\u73B0\u72B6"
|
|
14484
14933
|
]
|
|
14485
14934
|
},
|
|
14486
14935
|
{
|
|
@@ -14493,7 +14942,12 @@ var SKILL_ROUTES = [
|
|
|
14493
14942
|
"preclinical literature",
|
|
14494
14943
|
"\u7CFB\u7EDF\u6587\u732E\u63D0\u53D6",
|
|
14495
14944
|
"literature extraction",
|
|
14496
|
-
"\u6587\u732E\u7CFB\u7EDF\u7EFC\u5408"
|
|
14945
|
+
"\u6587\u732E\u7CFB\u7EDF\u7EFC\u5408",
|
|
14946
|
+
// natural-language synonyms
|
|
14947
|
+
"\u6587\u732E\u7EFC\u8FF0",
|
|
14948
|
+
"\u7CFB\u7EDF\u7EFC\u8FF0",
|
|
14949
|
+
"\u6587\u732E\u6574\u7406",
|
|
14950
|
+
"\u6587\u732E\u6316\u6398"
|
|
14497
14951
|
]
|
|
14498
14952
|
},
|
|
14499
14953
|
{
|
|
@@ -14510,7 +14964,16 @@ var SKILL_ROUTES = [
|
|
|
14510
14964
|
"\u5B9E\u9A8C\u8BBE\u8BA1\u7EDF\u8BA1",
|
|
14511
14965
|
"\u5047\u8BBE\u68C0\u9A8C\u9009\u62E9",
|
|
14512
14966
|
"t\u68C0\u9A8C\u8FD8\u662F",
|
|
14513
|
-
"anova\u65B9\u5DEE\u5206\u6790"
|
|
14967
|
+
"anova\u65B9\u5DEE\u5206\u6790",
|
|
14968
|
+
// natural-language synonyms
|
|
14969
|
+
"\u7528\u4EC0\u4E48\u7EDF\u8BA1\u65B9\u6CD5",
|
|
14970
|
+
"\u7EDF\u8BA1\u663E\u8457\u6027",
|
|
14971
|
+
"\u68C0\u9A8C\u65B9\u6CD5",
|
|
14972
|
+
"\u9700\u8981\u591A\u5C11\u6837\u672C",
|
|
14973
|
+
"\u529F\u6548\u5206\u6790",
|
|
14974
|
+
"\u7EDF\u8BA1\u529F\u6548",
|
|
14975
|
+
"p\u503C",
|
|
14976
|
+
"\u663E\u8457\u6027\u68C0\u9A8C"
|
|
14514
14977
|
]
|
|
14515
14978
|
},
|
|
14516
14979
|
{
|
|
@@ -14524,7 +14987,14 @@ var SKILL_ROUTES = [
|
|
|
14524
14987
|
"biomarker panel\u7B5B\u9009",
|
|
14525
14988
|
"\u6700\u5C0F\u6807\u5FD7\u7269\u9762\u677F",
|
|
14526
14989
|
"feature selection lasso",
|
|
14527
|
-
"\u8BCA\u65AD\u6807\u5FD7\u7269\u7B5B\u9009"
|
|
14990
|
+
"\u8BCA\u65AD\u6807\u5FD7\u7269\u7B5B\u9009",
|
|
14991
|
+
// natural-language synonyms
|
|
14992
|
+
"\u751F\u7269\u6807\u5FD7\u7269\u7B5B\u9009",
|
|
14993
|
+
"\u6807\u5FD7\u7269\u7EC4\u5408",
|
|
14994
|
+
"\u8BCA\u65AD\u6A21\u578B",
|
|
14995
|
+
"\u9884\u6D4B\u6A21\u578B\u7279\u5F81\u7B5B\u9009",
|
|
14996
|
+
"biomarker",
|
|
14997
|
+
"\u7279\u5F81\u9009\u62E9"
|
|
14528
14998
|
]
|
|
14529
14999
|
},
|
|
14530
15000
|
{
|
|
@@ -14540,7 +15010,64 @@ var SKILL_ROUTES = [
|
|
|
14540
15010
|
"primer3",
|
|
14541
15011
|
"qrt-pcr\u8BBE\u8BA1",
|
|
14542
15012
|
"\u6269\u589E\u5B50\u8BBE\u8BA1",
|
|
14543
|
-
"\u5F15\u7269\u7279\u5F02\u6027\u9A8C\u8BC1"
|
|
15013
|
+
"\u5F15\u7269\u7279\u5F02\u6027\u9A8C\u8BC1",
|
|
15014
|
+
// natural-language synonyms
|
|
15015
|
+
"\u8BBE\u8BA1\u5F15\u7269",
|
|
15016
|
+
"\u6269\u589E\u5F15\u7269",
|
|
15017
|
+
"rt-pcr\u5F15\u7269",
|
|
15018
|
+
"\u5B9A\u91CFpcr"
|
|
15019
|
+
]
|
|
15020
|
+
},
|
|
15021
|
+
// ── Survival Analysis ─────────────────────────────────────────────────────────
|
|
15022
|
+
{
|
|
15023
|
+
id: "survival-analysis-clinical",
|
|
15024
|
+
name: "\u4E34\u5E8A\u751F\u5B58\u5206\u6790 (KM + Cox)",
|
|
15025
|
+
category: "\u4E34\u5E8A",
|
|
15026
|
+
tag: "workflow",
|
|
15027
|
+
keywords: [
|
|
15028
|
+
// exact method names
|
|
15029
|
+
"kaplan-meier",
|
|
15030
|
+
"kaplan meier",
|
|
15031
|
+
"km\u66F2\u7EBF",
|
|
15032
|
+
"cox\u56DE\u5F52",
|
|
15033
|
+
"cox regression",
|
|
15034
|
+
"\u751F\u5B58\u5206\u6790",
|
|
15035
|
+
"survival analysis",
|
|
15036
|
+
"\u751F\u5B58\u66F2\u7EBF",
|
|
15037
|
+
"log-rank",
|
|
15038
|
+
// outcome types
|
|
15039
|
+
"\u603B\u751F\u5B58\u671F",
|
|
15040
|
+
"overall survival",
|
|
15041
|
+
"os\u5206\u6790",
|
|
15042
|
+
"\u65E0\u8FDB\u5C55\u751F\u5B58",
|
|
15043
|
+
"progression-free survival",
|
|
15044
|
+
"pfs\u5206\u6790",
|
|
15045
|
+
"\u65E0\u75C5\u751F\u5B58",
|
|
15046
|
+
"disease-free survival",
|
|
15047
|
+
"dfs\u5206\u6790",
|
|
15048
|
+
"\u590D\u53D1\u751F\u5B58",
|
|
15049
|
+
"relapse-free survival",
|
|
15050
|
+
"rfs\u5206\u6790",
|
|
15051
|
+
// natural-language synonyms
|
|
15052
|
+
"\u60A3\u8005\u9884\u540E",
|
|
15053
|
+
"\u9884\u540E\u5206\u6790",
|
|
15054
|
+
"\u751F\u5B58\u9884\u540E",
|
|
15055
|
+
"\u4E34\u5E8A\u9884\u540E",
|
|
15056
|
+
"\u5220\u5931\u6570\u636E",
|
|
15057
|
+
"\u53F3\u5220\u5931",
|
|
15058
|
+
"censored data",
|
|
15059
|
+
"\u98CE\u9669\u6BD4",
|
|
15060
|
+
"hazard ratio",
|
|
15061
|
+
"hr\u503C",
|
|
15062
|
+
"\u7ADE\u4E89\u98CE\u9669",
|
|
15063
|
+
"competing risk",
|
|
15064
|
+
"fine-gray",
|
|
15065
|
+
"\u4E2D\u4F4D\u751F\u5B58\u65F6\u95F4",
|
|
15066
|
+
"5\u5E74\u751F\u5B58\u7387",
|
|
15067
|
+
"3\u5E74\u751F\u5B58\u7387",
|
|
15068
|
+
"\u9AD8\u8868\u8FBE\u9884\u540E\u5DEE",
|
|
15069
|
+
"\u57FA\u56E0\u8868\u8FBE\u4E0E\u9884\u540E",
|
|
15070
|
+
"\u7A81\u53D8\u4E0E\u9884\u540E"
|
|
14544
15071
|
]
|
|
14545
15072
|
},
|
|
14546
15073
|
// ── OpenClaw Key Skills ────────────────────────────────────────────────────────
|
|
@@ -14743,36 +15270,166 @@ function routeSkill(message) {
|
|
|
14743
15270
|
let score = 0;
|
|
14744
15271
|
for (const kw of route.keywords) {
|
|
14745
15272
|
if (lower.includes(kw.toLowerCase())) {
|
|
14746
|
-
score += kw.length;
|
|
15273
|
+
score += 1 + kw.length * 0.1;
|
|
14747
15274
|
}
|
|
14748
15275
|
}
|
|
14749
15276
|
if (score > 0) scores.set(route.id, { route, score });
|
|
14750
15277
|
}
|
|
14751
|
-
const sorted = Array.from(scores.values()).sort((a2, b2) => b2.score - a2.score).slice(0,
|
|
15278
|
+
const sorted = Array.from(scores.values()).sort((a2, b2) => b2.score - a2.score).slice(0, 5);
|
|
14752
15279
|
return {
|
|
14753
15280
|
routes: sorted.map((v2) => v2.route),
|
|
14754
15281
|
topScore: sorted[0]?.score ?? 0
|
|
14755
15282
|
};
|
|
14756
15283
|
}
|
|
14757
15284
|
|
|
15285
|
+
// src/sessions.ts
|
|
15286
|
+
var import_fs4 = require("fs");
|
|
15287
|
+
var import_path4 = require("path");
|
|
15288
|
+
var SESSIONS_DIR = (0, import_path4.join)(BGI_DIR, "sessions");
|
|
15289
|
+
var CHECKPOINTS_DIR = (0, import_path4.join)(BGI_DIR, "checkpoints");
|
|
15290
|
+
function ensureSessionDirs() {
|
|
15291
|
+
for (const d2 of [SESSIONS_DIR, CHECKPOINTS_DIR]) {
|
|
15292
|
+
if (!(0, import_fs4.existsSync)(d2)) (0, import_fs4.mkdirSync)(d2, { recursive: true });
|
|
15293
|
+
}
|
|
15294
|
+
}
|
|
15295
|
+
function sessionPath(id) {
|
|
15296
|
+
return (0, import_path4.join)(SESSIONS_DIR, `${id}.json`);
|
|
15297
|
+
}
|
|
15298
|
+
function newSessionId() {
|
|
15299
|
+
const now = /* @__PURE__ */ new Date();
|
|
15300
|
+
const date = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
15301
|
+
const rand = Math.random().toString(36).slice(2, 6);
|
|
15302
|
+
return `${date}-${rand}`;
|
|
15303
|
+
}
|
|
15304
|
+
function saveSession(id, name, messages, skills, createdAt) {
|
|
15305
|
+
ensureSessionDirs();
|
|
15306
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15307
|
+
const firstUser = messages.find((m2) => m2.role === "user");
|
|
15308
|
+
const rawPreview = typeof firstUser?.content === "string" ? firstUser.content : "";
|
|
15309
|
+
const preview = rawPreview.replace(/\[Skill 已加载[^\]]*\][\s\S]*/, "").trim().slice(0, 80);
|
|
15310
|
+
const session = {
|
|
15311
|
+
id,
|
|
15312
|
+
name,
|
|
15313
|
+
createdAt,
|
|
15314
|
+
updatedAt: now,
|
|
15315
|
+
messageCount: messages.length,
|
|
15316
|
+
skills,
|
|
15317
|
+
preview,
|
|
15318
|
+
messages
|
|
15319
|
+
};
|
|
15320
|
+
(0, import_fs4.writeFileSync)(sessionPath(id), JSON.stringify(session, null, 2), "utf8");
|
|
15321
|
+
}
|
|
15322
|
+
function loadSession(id) {
|
|
15323
|
+
ensureSessionDirs();
|
|
15324
|
+
const p2 = sessionPath(id);
|
|
15325
|
+
if (!(0, import_fs4.existsSync)(p2)) return null;
|
|
15326
|
+
try {
|
|
15327
|
+
return JSON.parse((0, import_fs4.readFileSync)(p2, "utf8"));
|
|
15328
|
+
} catch {
|
|
15329
|
+
return null;
|
|
15330
|
+
}
|
|
15331
|
+
}
|
|
15332
|
+
function listSessions() {
|
|
15333
|
+
ensureSessionDirs();
|
|
15334
|
+
const files = (0, import_fs4.readdirSync)(SESSIONS_DIR).filter((f2) => f2.endsWith(".json"));
|
|
15335
|
+
const metas = [];
|
|
15336
|
+
for (const f2 of files) {
|
|
15337
|
+
try {
|
|
15338
|
+
const raw = JSON.parse((0, import_fs4.readFileSync)((0, import_path4.join)(SESSIONS_DIR, f2), "utf8"));
|
|
15339
|
+
metas.push({
|
|
15340
|
+
id: raw.id,
|
|
15341
|
+
name: raw.name,
|
|
15342
|
+
createdAt: raw.createdAt,
|
|
15343
|
+
updatedAt: raw.updatedAt,
|
|
15344
|
+
messageCount: raw.messageCount,
|
|
15345
|
+
skills: raw.skills ?? [],
|
|
15346
|
+
preview: raw.preview ?? ""
|
|
15347
|
+
});
|
|
15348
|
+
} catch {
|
|
15349
|
+
}
|
|
15350
|
+
}
|
|
15351
|
+
return metas.sort((a2, b2) => b2.updatedAt.localeCompare(a2.updatedAt));
|
|
15352
|
+
}
|
|
15353
|
+
function deleteSession(id) {
|
|
15354
|
+
const p2 = sessionPath(id);
|
|
15355
|
+
if (!(0, import_fs4.existsSync)(p2)) return false;
|
|
15356
|
+
(0, import_fs4.unlinkSync)(p2);
|
|
15357
|
+
return true;
|
|
15358
|
+
}
|
|
15359
|
+
function getLastSession() {
|
|
15360
|
+
const all = listSessions();
|
|
15361
|
+
return all[0] ?? null;
|
|
15362
|
+
}
|
|
15363
|
+
function checkpointPath(id) {
|
|
15364
|
+
return (0, import_path4.join)(CHECKPOINTS_DIR, `${id}.json`);
|
|
15365
|
+
}
|
|
15366
|
+
function saveCheckpoint(sessionId, label, messages, skills) {
|
|
15367
|
+
ensureSessionDirs();
|
|
15368
|
+
const id = `${sessionId}-cp${Date.now()}`;
|
|
15369
|
+
const cp = {
|
|
15370
|
+
id,
|
|
15371
|
+
sessionId,
|
|
15372
|
+
label,
|
|
15373
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15374
|
+
messageCount: messages.length,
|
|
15375
|
+
messages,
|
|
15376
|
+
skills
|
|
15377
|
+
};
|
|
15378
|
+
(0, import_fs4.writeFileSync)(checkpointPath(id), JSON.stringify(cp, null, 2), "utf8");
|
|
15379
|
+
return id;
|
|
15380
|
+
}
|
|
15381
|
+
function listCheckpoints(sessionId) {
|
|
15382
|
+
ensureSessionDirs();
|
|
15383
|
+
const files = (0, import_fs4.readdirSync)(CHECKPOINTS_DIR).filter((f2) => f2.endsWith(".json"));
|
|
15384
|
+
const cps = [];
|
|
15385
|
+
for (const f2 of files) {
|
|
15386
|
+
try {
|
|
15387
|
+
const cp = JSON.parse((0, import_fs4.readFileSync)((0, import_path4.join)(CHECKPOINTS_DIR, f2), "utf8"));
|
|
15388
|
+
if (!sessionId || cp.sessionId === sessionId) cps.push(cp);
|
|
15389
|
+
} catch {
|
|
15390
|
+
}
|
|
15391
|
+
}
|
|
15392
|
+
return cps.sort((a2, b2) => b2.createdAt.localeCompare(a2.createdAt));
|
|
15393
|
+
}
|
|
15394
|
+
function deleteCheckpoint(id) {
|
|
15395
|
+
const p2 = checkpointPath(id);
|
|
15396
|
+
if (!(0, import_fs4.existsSync)(p2)) return false;
|
|
15397
|
+
(0, import_fs4.unlinkSync)(p2);
|
|
15398
|
+
return true;
|
|
15399
|
+
}
|
|
15400
|
+
function clearCheckpoints(sessionId) {
|
|
15401
|
+
const cps = listCheckpoints(sessionId);
|
|
15402
|
+
let count = 0;
|
|
15403
|
+
for (const cp of cps) {
|
|
15404
|
+
if (deleteCheckpoint(cp.id)) count++;
|
|
15405
|
+
}
|
|
15406
|
+
return count;
|
|
15407
|
+
}
|
|
15408
|
+
|
|
14758
15409
|
// src/index.ts
|
|
14759
|
-
var
|
|
15410
|
+
var import_fs6 = require("fs");
|
|
15411
|
+
var VERSION2 = "2.2.9";
|
|
15412
|
+
var SESSION_CTX = {
|
|
15413
|
+
id: "",
|
|
15414
|
+
createdAt: "",
|
|
15415
|
+
wdirSnapshot: null
|
|
15416
|
+
};
|
|
14760
15417
|
function installBundledData() {
|
|
14761
|
-
const bundledData = (0,
|
|
14762
|
-
if (!(0,
|
|
15418
|
+
const bundledData = (0, import_path5.join)(__dirname, "..", "data");
|
|
15419
|
+
if (!(0, import_fs5.existsSync)(bundledData)) return;
|
|
14763
15420
|
ensureDirs();
|
|
14764
15421
|
const targets = [
|
|
14765
|
-
{ src: (0,
|
|
14766
|
-
{ src: (0,
|
|
14767
|
-
{ src: (0,
|
|
15422
|
+
{ src: (0, import_path5.join)(bundledData, "workflows"), dest: WORKFLOWS_DIR, name: "Skills (\u751F\u4FE1\u5DE5\u4F5C\u6D41)" },
|
|
15423
|
+
{ src: (0, import_path5.join)(bundledData, "skills"), dest: SKILLS_DIR, name: "Skills (\u533B\u5B66\u4E13\u79D1)" },
|
|
15424
|
+
{ src: (0, import_path5.join)(bundledData, "tools"), dest: TOOLS_DIR, name: "\u5DE5\u5177" }
|
|
14768
15425
|
];
|
|
14769
15426
|
let installed = false;
|
|
14770
15427
|
for (const { src, dest, name } of targets) {
|
|
14771
|
-
if (!(0,
|
|
14772
|
-
const isEmpty = !(0,
|
|
15428
|
+
if (!(0, import_fs5.existsSync)(src)) continue;
|
|
15429
|
+
const isEmpty = !(0, import_fs5.existsSync)(dest) || (0, import_fs5.readdirSync)(dest).length === 0;
|
|
14773
15430
|
if (isEmpty) {
|
|
14774
|
-
(0,
|
|
14775
|
-
(0,
|
|
15431
|
+
(0, import_fs5.mkdirSync)(dest, { recursive: true });
|
|
15432
|
+
(0, import_fs5.cpSync)(src, dest, { recursive: true });
|
|
14776
15433
|
if (!installed) {
|
|
14777
15434
|
process.stdout.write(source_default.dim("\u6B63\u5728\u521D\u59CB\u5316\u5185\u7F6E\u6570\u636E...\n"));
|
|
14778
15435
|
installed = true;
|
|
@@ -14807,22 +15464,49 @@ function printHelp() {
|
|
|
14807
15464
|
console.log(` ${source_default.cyan("/clear")} \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2`);
|
|
14808
15465
|
console.log(` ${source_default.cyan("/history")} \u67E5\u770B\u5BF9\u8BDD\u7EDF\u8BA1\uFF08\u8F6E\u6B21 / Token \u4F30\u7B97\uFF09`);
|
|
14809
15466
|
console.log(` ${source_default.cyan("/compact")} \u7ACB\u5373\u538B\u7F29\u5BF9\u8BDD\u5386\u53F2\uFF08\u8D85 60k token \u81EA\u52A8\u89E6\u53D1\uFF09`);
|
|
14810
|
-
console.log(` ${source_default.cyan("/save")} [\
|
|
15467
|
+
console.log(` ${source_default.cyan("/save")} [\u540D\u79F0] \u4FDD\u5B58\u5BF9\u8BDD\u4E3A Markdown \u6587\u4EF6`);
|
|
14811
15468
|
console.log(` ${source_default.cyan("/think")} [on|off] \u5207\u6362\u601D\u8003\u6A21\u5F0F (Qwen3 /think \u524D\u7F00)`);
|
|
14812
15469
|
console.log();
|
|
15470
|
+
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u4F1A\u8BDD\u6301\u4E45\u5316 \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"));
|
|
15471
|
+
console.log(` ${source_default.cyan("/sessions")} \u5217\u51FA\u5386\u53F2\u4F1A\u8BDD`);
|
|
15472
|
+
console.log(` ${source_default.cyan("/resume")} [id] \u6062\u590D\u4E0A\u6B21\uFF08\u6216\u6307\u5B9A\uFF09\u4F1A\u8BDD`);
|
|
15473
|
+
console.log(` ${source_default.cyan("/session-save")} [\u540D\u79F0] \u624B\u52A8\u547D\u540D\u4FDD\u5B58\u5F53\u524D\u4F1A\u8BDD`);
|
|
15474
|
+
console.log(` ${source_default.cyan("/session-del")} <id> \u5220\u9664\u6307\u5B9A\u4F1A\u8BDD`);
|
|
15475
|
+
console.log();
|
|
15476
|
+
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u65AD\u70B9\u7EED\u4F20 \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"));
|
|
15477
|
+
console.log(` ${source_default.cyan("/checkpoint")} \u4FDD\u5B58\u5F53\u524D\u5BF9\u8BDD\u65AD\u70B9`);
|
|
15478
|
+
console.log(` ${source_default.cyan("/checkpoint list")} \u5217\u51FA\u5F53\u524D\u4F1A\u8BDD\u6240\u6709\u65AD\u70B9`);
|
|
15479
|
+
console.log(` ${source_default.cyan("/checkpoint restore")} <id> \u6062\u590D\u5230\u6307\u5B9A\u65AD\u70B9`);
|
|
15480
|
+
console.log(` ${source_default.cyan("/checkpoint clear")} \u6E05\u9664\u5F53\u524D\u4F1A\u8BDD\u6240\u6709\u65AD\u70B9`);
|
|
15481
|
+
console.log();
|
|
14813
15482
|
console.log(source_default.bold.cyan("\u2500\u2500\u2500 Skills \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"));
|
|
14814
15483
|
console.log(` ${source_default.cyan("/cat")} \u6309\u9886\u57DF\u6D4F\u89C8 Skills \u5206\u7C7B\u76EE\u5F55`);
|
|
14815
15484
|
console.log(` ${source_default.cyan("/sk")} \u5217\u51FA\u5168\u90E8 Skills`);
|
|
14816
15485
|
console.log(` ${source_default.cyan("/sk")} <\u5173\u952E\u8BCD> \u6A21\u7CCA\u641C\u7D22\uFF0C\u5339\u914D\u5219\u6CE8\u5165\uFF0C\u5426\u5219\u5217\u51FA\u5019\u9009`);
|
|
14817
15486
|
console.log(` ${source_default.cyan("/wf")} \u540C /sk\uFF0C\u522B\u540D`);
|
|
14818
|
-
console.log(source_default.
|
|
14819
|
-
console.log(source_default.
|
|
15487
|
+
console.log(` ${source_default.cyan("/skills")} \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u5DF2\u52A0\u8F7D\u7684 Skills`);
|
|
15488
|
+
console.log(` ${source_default.cyan("/unload")} <id> \u4ECE\u5F53\u524D\u4F1A\u8BDD\u5378\u8F7D\u6307\u5B9A Skill`);
|
|
15489
|
+
console.log(source_default.dim(" \u793A\u4F8B: /cat /sk deseq2 /skills /unload deseq2"));
|
|
15490
|
+
console.log(source_default.dim(" \u63D0\u793A: \u76F4\u63A5\u63CF\u8FF0\u4EFB\u52A1\uFF0CAI \u4F1A\u81EA\u52A8\u8BC6\u522B\u5E76\u6FC0\u6D3B\u5BF9\u5E94\u6280\u80FD\uFF08\u52A0\u8F7D\u524D\u4F1A\u8BE2\u95EE\u786E\u8BA4\uFF09"));
|
|
15491
|
+
console.log();
|
|
15492
|
+
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u76F4\u63A5\u6267\u884C \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"));
|
|
15493
|
+
console.log(` ${source_default.cyan("!<\u547D\u4EE4>")} \u7ED5\u8FC7 AI \u76F4\u63A5\u6267\u884C Shell \u547D\u4EE4\uFF08\u5B9E\u65F6\u8F93\u51FA\uFF09`);
|
|
15494
|
+
console.log(source_default.dim(" \u793A\u4F8B: !ls -la !Rscript analysis.R !python script.py !samtools view -h a.bam"));
|
|
15495
|
+
console.log();
|
|
15496
|
+
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u5DE5\u4F5C\u6D41\u5411\u5BFC \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"));
|
|
15497
|
+
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`);
|
|
15498
|
+
console.log(` ${source_default.cyan("/check-env")} [id] \u68C0\u6D4B Skill \u6240\u9700 R/Python \u5305\u662F\u5426\u5DF2\u5B89\u88C5`);
|
|
15499
|
+
console.log(` ${source_default.cyan("/install")} <url> \u4ECE GitHub \u5B89\u88C5\u7B2C\u4E09\u65B9 Skill`);
|
|
15500
|
+
console.log(` ${source_default.cyan("/uninstall")} <id> \u5378\u8F7D\u5DF2\u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill`);
|
|
14820
15501
|
console.log();
|
|
14821
15502
|
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"));
|
|
14822
15503
|
console.log(` ${source_default.cyan("/cd")} <\u8DEF\u5F84> \u66F4\u6539\u5DE5\u4F5C\u76EE\u5F55`);
|
|
14823
15504
|
console.log(` ${source_default.cyan("/cwd")} \u663E\u793A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55`);
|
|
15505
|
+
console.log(` ${source_default.cyan("/diff")} \u663E\u793A\u672C\u6B21\u4F1A\u8BDD\u65B0\u589E/\u4FEE\u6539\u7684\u6587\u4EF6`);
|
|
14824
15506
|
console.log(` ${source_default.cyan("/tools")} \u5217\u51FA AI \u53EF\u8C03\u7528\u7684\u5DE5\u5177`);
|
|
14825
15507
|
console.log(` ${source_default.cyan("@\u8DEF\u5F84")} \u6D88\u606F\u4E2D\u5185\u5D4C\u6587\u4EF6\u5185\u5BB9 (\u4F8B: @data.csv \u91CC\u6709\u4EC0\u4E48?)`);
|
|
15508
|
+
console.log(` ${source_default.cyan("@\u76EE\u5F55/")} \u5185\u5D4C\u76EE\u5F55\u4E0B\u6240\u6709\u6587\u4EF6\u6458\u8981 (\u4F8B: @results/)`);
|
|
15509
|
+
console.log(` ${source_default.cyan("@*.csv")} \u901A\u914D\u7B26\u5185\u5D4C\u591A\u4E2A\u6587\u4EF6 (\u4F8B: @*.csv @*.tsv)`);
|
|
14826
15510
|
console.log();
|
|
14827
15511
|
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u5176\u4ED6 \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"));
|
|
14828
15512
|
console.log(` ${source_default.cyan("/help")} \u663E\u793A\u672C\u5E2E\u52A9`);
|
|
@@ -14914,10 +15598,10 @@ async function firstRunIfNeeded(rl) {
|
|
|
14914
15598
|
function collectAllSkills() {
|
|
14915
15599
|
const entries = [];
|
|
14916
15600
|
const addFrom = (dir, tag) => {
|
|
14917
|
-
if (!(0,
|
|
14918
|
-
(0,
|
|
15601
|
+
if (!(0, import_fs5.existsSync)(dir)) return;
|
|
15602
|
+
(0, import_fs5.readdirSync)(dir).forEach((f2) => {
|
|
14919
15603
|
try {
|
|
14920
|
-
if ((0,
|
|
15604
|
+
if ((0, import_fs5.statSync)((0, import_path5.join)(dir, f2)).isDirectory()) entries.push({ id: f2, dir, tag });
|
|
14921
15605
|
} catch {
|
|
14922
15606
|
}
|
|
14923
15607
|
});
|
|
@@ -14945,7 +15629,72 @@ function listSkills(keyword) {
|
|
|
14945
15629
|
if (matched.length > 50) console.log(source_default.dim(` ... \u8FD8\u6709 ${matched.length - 50} \u4E2A\uFF0C\u8BF7\u7528\u5173\u952E\u8BCD\u7B5B\u9009`));
|
|
14946
15630
|
console.log();
|
|
14947
15631
|
}
|
|
14948
|
-
function
|
|
15632
|
+
async function llmRecommendSkills(userQuery) {
|
|
15633
|
+
const cfg = loadConfig();
|
|
15634
|
+
const prov = PROVIDERS[cfg.provider];
|
|
15635
|
+
if (!prov) return [];
|
|
15636
|
+
const apiKey = cfg.apiKeys[cfg.provider] ?? (prov.envKey ? process.env[prov.envKey] : void 0);
|
|
15637
|
+
const requiresKey = prov.envKey !== "";
|
|
15638
|
+
if (requiresKey && !apiKey) return [];
|
|
15639
|
+
const baseURL = cfg.provider === "custom" ? cfg.customUrl ?? prov.baseURL : prov.baseURL;
|
|
15640
|
+
const model = cfg.provider === "custom" ? cfg.customModel ?? cfg.model : cfg.model;
|
|
15641
|
+
const all = collectAllSkills();
|
|
15642
|
+
const catalogLines = [];
|
|
15643
|
+
for (const entry of all) {
|
|
15644
|
+
const skillPath = (0, import_path5.join)(entry.dir, entry.id, "SKILL.md");
|
|
15645
|
+
if (!(0, import_fs5.existsSync)(skillPath)) continue;
|
|
15646
|
+
const raw = (0, import_fs5.readFileSync)(skillPath, "utf8");
|
|
15647
|
+
const { name, shortDesc } = parseSkillMeta(raw);
|
|
15648
|
+
const displayName = name || entry.id;
|
|
15649
|
+
const desc = shortDesc ? ` \u2014 ${shortDesc}` : "";
|
|
15650
|
+
catalogLines.push(`${entry.id}|${displayName}${desc}`);
|
|
15651
|
+
}
|
|
15652
|
+
if (catalogLines.length === 0) return [];
|
|
15653
|
+
const catalog = catalogLines.join("\n");
|
|
15654
|
+
const systemMsg = `\u4F60\u662F\u4E00\u4E2A\u751F\u7269\u4FE1\u606F\u5B66 Skill \u63A8\u8350\u52A9\u624B\u3002
|
|
15655
|
+
\u7528\u6237\u4F1A\u63CF\u8FF0\u4ED6\u4EEC\u60F3\u505A\u7684\u5206\u6790\u4EFB\u52A1\uFF0C\u4F60\u9700\u8981\u4ECE\u4E0B\u9762\u7684 Skill \u76EE\u5F55\u4E2D\u63A8\u8350\u6700\u76F8\u5173\u7684 5 \u4E2A\uFF08\u6216\u66F4\u5C11\uFF09\u3002
|
|
15656
|
+
|
|
15657
|
+
Skill \u76EE\u5F55\uFF08\u683C\u5F0F: id|\u540D\u79F0 \u2014 \u7B80\u4ECB\uFF09\uFF1A
|
|
15658
|
+
${catalog}
|
|
15659
|
+
|
|
15660
|
+
\u8BF7\u4E25\u683C\u6309\u7167\u4EE5\u4E0B JSON \u683C\u5F0F\u56DE\u590D\uFF0C\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u5176\u4ED6\u5185\u5BB9\uFF1A
|
|
15661
|
+
[
|
|
15662
|
+
{"id": "skill-id", "name": "Skill \u540D\u79F0", "reason": "\u4E00\u53E5\u8BDD\u8BF4\u660E\u4E3A\u4EC0\u4E48\u63A8\u8350"},
|
|
15663
|
+
...
|
|
15664
|
+
]`;
|
|
15665
|
+
try {
|
|
15666
|
+
const client = new openai_default({ apiKey: apiKey || "none", baseURL });
|
|
15667
|
+
const resp = await client.chat.completions.create({
|
|
15668
|
+
model,
|
|
15669
|
+
messages: [
|
|
15670
|
+
{ role: "system", content: systemMsg },
|
|
15671
|
+
{ role: "user", content: userQuery }
|
|
15672
|
+
],
|
|
15673
|
+
temperature: 0.2,
|
|
15674
|
+
max_tokens: 800
|
|
15675
|
+
});
|
|
15676
|
+
const text = resp.choices[0]?.message?.content ?? "";
|
|
15677
|
+
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
15678
|
+
if (!jsonMatch) return [];
|
|
15679
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
15680
|
+
const validIds = new Set(all.map((e2) => e2.id));
|
|
15681
|
+
return parsed.filter((r2) => r2.id && validIds.has(r2.id)).slice(0, 5);
|
|
15682
|
+
} catch {
|
|
15683
|
+
return [];
|
|
15684
|
+
}
|
|
15685
|
+
}
|
|
15686
|
+
function parseSkillMeta(content) {
|
|
15687
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/) || content.match(/<!--[\s\S]*?-->\s*([\s\S]*?)(?=\n#|\n\n)/);
|
|
15688
|
+
if (!fmMatch) return { name: "", shortDesc: "" };
|
|
15689
|
+
const fm = fmMatch[1];
|
|
15690
|
+
const nameMatch = fm.match(/^name:\s*['"]?(.+?)['"]?\s*$/m);
|
|
15691
|
+
const descMatch = fm.match(/^short-description:\s*(.+)$/m);
|
|
15692
|
+
return {
|
|
15693
|
+
name: nameMatch?.[1]?.trim() ?? "",
|
|
15694
|
+
shortDesc: descMatch?.[1]?.trim() ?? ""
|
|
15695
|
+
};
|
|
15696
|
+
}
|
|
15697
|
+
async function injectSkill(id, history, injectedSkills, rl, skipConfirm = false) {
|
|
14949
15698
|
const all = collectAllSkills();
|
|
14950
15699
|
const match = all.find((e2) => e2.id === id) || all.find((e2) => e2.id.startsWith(id)) || all.find((e2) => e2.id.includes(id));
|
|
14951
15700
|
if (!match) {
|
|
@@ -14953,12 +15702,41 @@ function injectSkill(id, history) {
|
|
|
14953
15702
|
console.log(source_default.dim("\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22"));
|
|
14954
15703
|
return false;
|
|
14955
15704
|
}
|
|
14956
|
-
const skillPath = (0,
|
|
14957
|
-
if (!(0,
|
|
15705
|
+
const skillPath = (0, import_path5.join)(match.dir, match.id, "SKILL.md");
|
|
15706
|
+
if (!(0, import_fs5.existsSync)(skillPath)) {
|
|
14958
15707
|
console.log(source_default.red(`${match.id} \u7F3A\u5C11 SKILL.md`));
|
|
14959
15708
|
return false;
|
|
14960
15709
|
}
|
|
14961
|
-
const content = (0,
|
|
15710
|
+
const content = (0, import_fs5.readFileSync)(skillPath, "utf8");
|
|
15711
|
+
const { name, shortDesc } = parseSkillMeta(content);
|
|
15712
|
+
const displayName = name || match.id;
|
|
15713
|
+
if (!skipConfirm) {
|
|
15714
|
+
console.log();
|
|
15715
|
+
console.log(source_default.bold.cyan("\u250C\u2500 \u5373\u5C06\u52A0\u8F7D Skill \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"));
|
|
15716
|
+
console.log(`\u2502 ${source_default.bold("ID:")} ${source_default.cyan(match.id)}`);
|
|
15717
|
+
console.log(`\u2502 ${source_default.bold("\u540D\u79F0:")} ${displayName}`);
|
|
15718
|
+
if (shortDesc) {
|
|
15719
|
+
const words = shortDesc.split(" ");
|
|
15720
|
+
let line = "\u2502 \u529F\u80FD: ";
|
|
15721
|
+
for (const w2 of words) {
|
|
15722
|
+
if (line.length + w2.length > 70) {
|
|
15723
|
+
console.log(source_default.dim(line));
|
|
15724
|
+
line = "\u2502 " + w2 + " ";
|
|
15725
|
+
} else {
|
|
15726
|
+
line += w2 + " ";
|
|
15727
|
+
}
|
|
15728
|
+
}
|
|
15729
|
+
if (line.trim() !== "\u2502") console.log(source_default.dim(line));
|
|
15730
|
+
}
|
|
15731
|
+
console.log(source_default.bold.cyan("\u2514\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"));
|
|
15732
|
+
console.log();
|
|
15733
|
+
const ans = await question(rl, source_default.cyan(" \u786E\u8BA4\u52A0\u8F7D\u6B64 Skill\uFF1F[Y/n] \u203A "));
|
|
15734
|
+
const confirmed = ans.trim() === "" || ans.trim().toLowerCase() === "y";
|
|
15735
|
+
if (!confirmed) {
|
|
15736
|
+
console.log(source_default.dim(" \u5DF2\u53D6\u6D88"));
|
|
15737
|
+
return false;
|
|
15738
|
+
}
|
|
15739
|
+
}
|
|
14962
15740
|
history.push({
|
|
14963
15741
|
role: "user",
|
|
14964
15742
|
content: `[Skill \u5DF2\u52A0\u8F7D: ${match.id}]
|
|
@@ -14971,29 +15749,127 @@ ${content}`
|
|
|
14971
15749
|
role: "assistant",
|
|
14972
15750
|
content: `\u2713 Skill **${match.id}** \u5DF2\u52A0\u8F7D\u3002\u6211\u5DF2\u9605\u8BFB\u6307\u5357\uFF0C\u968F\u65F6\u53EF\u4EE5\u5F00\u59CB\u3002\u8BF7\u544A\u8BC9\u6211\u60A8\u7684\u5177\u4F53\u6570\u636E\u548C\u9700\u6C42\u3002`
|
|
14973
15751
|
});
|
|
14974
|
-
|
|
15752
|
+
injectedSkills.set(match.id, displayName);
|
|
15753
|
+
console.log(source_default.green(`\u2713 Skill "${match.id}" \u5DF2\u52A0\u8F7D\u5230\u5F53\u524D\u5BF9\u8BDD\u4E0A\u4E0B\u6587`));
|
|
14975
15754
|
return true;
|
|
14976
15755
|
}
|
|
14977
|
-
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
15756
|
+
var FILE_SIZE_LIMIT = 100 * 1024;
|
|
15757
|
+
var DIR_FILE_LIMIT = 20;
|
|
15758
|
+
function listDirFiles(dirPath) {
|
|
15759
|
+
try {
|
|
15760
|
+
return (0, import_fs6.readdirSync)(dirPath).map((f2) => (0, import_path5.join)(dirPath, f2)).filter((p2) => {
|
|
15761
|
+
try {
|
|
15762
|
+
return (0, import_fs6.statSync)(p2).isFile();
|
|
15763
|
+
} catch {
|
|
15764
|
+
return false;
|
|
15765
|
+
}
|
|
15766
|
+
});
|
|
15767
|
+
} catch {
|
|
15768
|
+
return [];
|
|
15769
|
+
}
|
|
15770
|
+
}
|
|
15771
|
+
function expandSingleFile(resolved) {
|
|
15772
|
+
try {
|
|
15773
|
+
const stat = (0, import_fs6.statSync)(resolved);
|
|
15774
|
+
if (!stat.isFile()) return `[${resolved}: \u4E0D\u662F\u6587\u4EF6]`;
|
|
15775
|
+
if (stat.size > FILE_SIZE_LIMIT) {
|
|
14987
15776
|
return `
|
|
14988
15777
|
\`\`\`
|
|
14989
15778
|
[\u6587\u4EF6: ${resolved}]
|
|
15779
|
+
(\u6587\u4EF6\u8FC7\u5927 ${Math.round(stat.size / 1024)}KB\uFF0C\u5DF2\u8DF3\u8FC7)
|
|
15780
|
+
\`\`\`
|
|
15781
|
+
`;
|
|
15782
|
+
}
|
|
15783
|
+
const content = (0, import_fs5.readFileSync)(resolved, "utf8");
|
|
15784
|
+
const lines = content.split("\n");
|
|
15785
|
+
const preview = lines.length > 150 ? lines.slice(0, 150).join("\n") + `
|
|
15786
|
+
... (\u5171 ${lines.length} \u884C\uFF0C\u5DF2\u622A\u65AD)` : content;
|
|
15787
|
+
return `
|
|
15788
|
+
\`\`\`
|
|
15789
|
+
[\u6587\u4EF6: ${resolved}]
|
|
14990
15790
|
${preview}
|
|
14991
15791
|
\`\`\`
|
|
14992
15792
|
`;
|
|
15793
|
+
} catch {
|
|
15794
|
+
return `[\u65E0\u6CD5\u8BFB\u53D6: ${resolved}]`;
|
|
15795
|
+
}
|
|
15796
|
+
}
|
|
15797
|
+
function expandFileRefs(input) {
|
|
15798
|
+
return input.replace(/@"([^"]+)"|@'([^']+)'|@([\/\w.*?~:-]+)/g, (match, q1, q2, q3) => {
|
|
15799
|
+
const rawPath = q1 ?? q2 ?? q3;
|
|
15800
|
+
const expanded = rawPath.replace(/^~/, (0, import_os3.homedir)());
|
|
15801
|
+
if (rawPath.endsWith("/") || rawPath.endsWith("\\")) {
|
|
15802
|
+
const dirResolved = (0, import_path5.resolve)(expanded);
|
|
15803
|
+
if (!(0, import_fs5.existsSync)(dirResolved)) return match;
|
|
15804
|
+
const files = listDirFiles(dirResolved).slice(0, DIR_FILE_LIMIT);
|
|
15805
|
+
if (files.length === 0) return `[\u76EE\u5F55 ${dirResolved} \u4E3A\u7A7A]`;
|
|
15806
|
+
const parts = [`
|
|
15807
|
+
[\u76EE\u5F55: ${dirResolved} \u5171 ${files.length} \u4E2A\u6587\u4EF6]`];
|
|
15808
|
+
for (const f2 of files) parts.push(expandSingleFile(f2));
|
|
15809
|
+
return parts.join("\n");
|
|
15810
|
+
}
|
|
15811
|
+
if (rawPath.includes("*") || rawPath.includes("?")) {
|
|
15812
|
+
const { globSync } = (() => {
|
|
15813
|
+
try {
|
|
15814
|
+
return require("glob");
|
|
15815
|
+
} catch {
|
|
15816
|
+
return { globSync: null };
|
|
15817
|
+
}
|
|
15818
|
+
})();
|
|
15819
|
+
let matched = [];
|
|
15820
|
+
if (globSync) {
|
|
15821
|
+
matched = globSync(expanded, { absolute: true }).slice(0, DIR_FILE_LIMIT);
|
|
15822
|
+
} else {
|
|
15823
|
+
const ext = rawPath.replace(/^.*\./, ".");
|
|
15824
|
+
matched = listDirFiles(process.cwd()).filter((f2) => f2.endsWith(ext)).slice(0, DIR_FILE_LIMIT);
|
|
15825
|
+
}
|
|
15826
|
+
if (matched.length === 0) return `[\u672A\u627E\u5230\u5339\u914D: ${rawPath}]`;
|
|
15827
|
+
const parts = [`
|
|
15828
|
+
[\u901A\u914D\u7B26\u5339\u914D: ${rawPath} \u5171 ${matched.length} \u4E2A\u6587\u4EF6]`];
|
|
15829
|
+
for (const f2 of matched) parts.push(expandSingleFile(f2));
|
|
15830
|
+
return parts.join("\n");
|
|
15831
|
+
}
|
|
15832
|
+
const resolved = (0, import_path5.resolve)(expanded);
|
|
15833
|
+
if (!(0, import_fs5.existsSync)(resolved)) return match;
|
|
15834
|
+
return expandSingleFile(resolved);
|
|
15835
|
+
});
|
|
15836
|
+
}
|
|
15837
|
+
function snapshotWorkdir(dir) {
|
|
15838
|
+
const snap = /* @__PURE__ */ new Map();
|
|
15839
|
+
function walk(d2, depth = 0) {
|
|
15840
|
+
if (depth > 3) return;
|
|
15841
|
+
try {
|
|
15842
|
+
for (const entry of (0, import_fs5.readdirSync)(d2)) {
|
|
15843
|
+
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
15844
|
+
const full = (0, import_path5.join)(d2, entry);
|
|
15845
|
+
try {
|
|
15846
|
+
const st2 = (0, import_fs5.statSync)(full);
|
|
15847
|
+
if (st2.isDirectory()) {
|
|
15848
|
+
walk(full, depth + 1);
|
|
15849
|
+
} else {
|
|
15850
|
+
snap.set(full, { path: full, mtime: st2.mtimeMs, size: st2.size });
|
|
15851
|
+
}
|
|
15852
|
+
} catch {
|
|
15853
|
+
}
|
|
15854
|
+
}
|
|
14993
15855
|
} catch {
|
|
14994
|
-
return _2;
|
|
14995
15856
|
}
|
|
14996
|
-
}
|
|
15857
|
+
}
|
|
15858
|
+
walk(dir);
|
|
15859
|
+
return snap;
|
|
15860
|
+
}
|
|
15861
|
+
function diffWorkdir(before, after) {
|
|
15862
|
+
const added = [];
|
|
15863
|
+
const modified = [];
|
|
15864
|
+
for (const [path, snap] of after) {
|
|
15865
|
+
const prev = before.get(path);
|
|
15866
|
+
if (!prev) {
|
|
15867
|
+
added.push(path);
|
|
15868
|
+
} else if (snap.mtime !== prev.mtime || snap.size !== prev.size) {
|
|
15869
|
+
modified.push(path);
|
|
15870
|
+
}
|
|
15871
|
+
}
|
|
15872
|
+
return { added, modified };
|
|
14997
15873
|
}
|
|
14998
15874
|
function saveConversation(history, filename) {
|
|
14999
15875
|
if (history.length === 0) {
|
|
@@ -15002,7 +15878,7 @@ function saveConversation(history, filename) {
|
|
|
15002
15878
|
}
|
|
15003
15879
|
const now = /* @__PURE__ */ new Date();
|
|
15004
15880
|
const stamp = now.toISOString().slice(0, 16).replace("T", "-").replace(":", "-");
|
|
15005
|
-
const outPath = (0,
|
|
15881
|
+
const outPath = (0, import_path5.resolve)(filename || `bgicli-chat-${stamp}.md`);
|
|
15006
15882
|
const lines = [`# BGI CLI \u5BF9\u8BDD\u8BB0\u5F55
|
|
15007
15883
|
`, `> \u5BFC\u51FA\u65F6\u95F4: ${now.toLocaleString("zh-CN")}
|
|
15008
15884
|
`];
|
|
@@ -15021,24 +15897,41 @@ ${msg.content}
|
|
|
15021
15897
|
`);
|
|
15022
15898
|
}
|
|
15023
15899
|
}
|
|
15024
|
-
(0,
|
|
15900
|
+
(0, import_fs5.writeFileSync)(outPath, lines.join("\n"), "utf8");
|
|
15025
15901
|
console.log(source_default.green(`\u2713 \u5BF9\u8BDD\u5DF2\u4FDD\u5B58: ${outPath}`));
|
|
15026
15902
|
}
|
|
15027
|
-
var COMPACT_TOKEN_THRESHOLD =
|
|
15903
|
+
var COMPACT_TOKEN_THRESHOLD = 4e4;
|
|
15028
15904
|
var COMPACT_KEEP_RECENT = 8;
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
return Math.round(chars / 3.5);
|
|
15032
|
-
}
|
|
15905
|
+
var WARN_TOKEN_THRESHOLD = 3e4;
|
|
15906
|
+
var estimateTokens2 = estimateTokens;
|
|
15033
15907
|
async function maybeCompact(history, cfg) {
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
const
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15908
|
+
let cleaned = deduplicateSkillInjections(trimToolOutputs(history));
|
|
15909
|
+
const tokensBefore = estimateTokens2(history);
|
|
15910
|
+
const tokensAfter = estimateTokens2(cleaned);
|
|
15911
|
+
if (tokensAfter < tokensBefore) {
|
|
15912
|
+
const saved = tokensBefore - tokensAfter;
|
|
15913
|
+
process.stdout.write(source_default.dim(
|
|
15914
|
+
`
|
|
15915
|
+
[\u4E0A\u4E0B\u6587\u4F18\u5316: \u53BB\u91CD/\u622A\u65AD\u8282\u7701 ~${Math.round(saved / 1e3)}k tokens]
|
|
15916
|
+
`
|
|
15917
|
+
));
|
|
15918
|
+
}
|
|
15919
|
+
if (tokensAfter >= WARN_TOKEN_THRESHOLD && tokensAfter < COMPACT_TOKEN_THRESHOLD) {
|
|
15920
|
+
process.stdout.write(source_default.dim(
|
|
15921
|
+
`[\u63D0\u793A: \u4E0A\u4E0B\u6587\u5DF2\u8FBE ~${Math.round(tokensAfter / 1e3)}k tokens\uFF0C\u63A5\u8FD1\u538B\u7F29\u9608\u503C ${Math.round(COMPACT_TOKEN_THRESHOLD / 1e3)}k]
|
|
15922
|
+
`
|
|
15923
|
+
));
|
|
15924
|
+
return cleaned;
|
|
15925
|
+
}
|
|
15926
|
+
if (tokensAfter < COMPACT_TOKEN_THRESHOLD) return cleaned;
|
|
15927
|
+
const recent = cleaned.slice(-COMPACT_KEEP_RECENT);
|
|
15928
|
+
const old = cleaned.slice(0, -COMPACT_KEEP_RECENT);
|
|
15929
|
+
if (old.length === 0) return cleaned;
|
|
15930
|
+
process.stdout.write(source_default.dim(
|
|
15931
|
+
`
|
|
15932
|
+
[\u4E0A\u4E0B\u6587\u5DF2\u8FBE ~${Math.round(tokensAfter / 1e3)}k tokens\uFF0C\u6B63\u5728\u81EA\u52A8\u538B\u7F29\u5386\u53F2...]
|
|
15933
|
+
`
|
|
15934
|
+
));
|
|
15042
15935
|
try {
|
|
15043
15936
|
const summary = await compactMessages(old, cfg);
|
|
15044
15937
|
const compacted = [
|
|
@@ -15054,16 +15947,31 @@ ${summary}`
|
|
|
15054
15947
|
},
|
|
15055
15948
|
...recent
|
|
15056
15949
|
];
|
|
15057
|
-
const
|
|
15058
|
-
|
|
15950
|
+
const finalTokens = estimateTokens2(compacted);
|
|
15951
|
+
const totalSaved = tokensBefore - finalTokens;
|
|
15952
|
+
process.stdout.write(source_default.dim(
|
|
15953
|
+
`[\u538B\u7F29\u5B8C\u6210: ~${Math.round(tokensBefore / 1e3)}k \u2192 ~${Math.round(finalTokens / 1e3)}k tokens\uFF0C\u8282\u7701 ~${Math.round(totalSaved / 1e3)}k]
|
|
15059
15954
|
|
|
15060
|
-
`
|
|
15955
|
+
`
|
|
15956
|
+
));
|
|
15061
15957
|
return compacted;
|
|
15062
15958
|
} catch {
|
|
15063
|
-
|
|
15064
|
-
|
|
15065
|
-
}
|
|
15066
|
-
|
|
15959
|
+
process.stdout.write(source_default.yellow("[\u538B\u7F29\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u622A\u65AD\u6A21\u5F0F]\n"));
|
|
15960
|
+
return cleaned.slice(-COMPACT_KEEP_RECENT * 2);
|
|
15961
|
+
}
|
|
15962
|
+
}
|
|
15963
|
+
function formatAge(isoDate) {
|
|
15964
|
+
const diff = Date.now() - new Date(isoDate).getTime();
|
|
15965
|
+
const mins = Math.floor(diff / 6e4);
|
|
15966
|
+
const hours = Math.floor(diff / 36e5);
|
|
15967
|
+
const days = Math.floor(diff / 864e5);
|
|
15968
|
+
if (mins < 1) return "\u521A\u521A";
|
|
15969
|
+
if (mins < 60) return `${mins} \u5206\u949F\u524D`;
|
|
15970
|
+
if (hours < 24) return `${hours} \u5C0F\u65F6\u524D`;
|
|
15971
|
+
if (days < 30) return `${days} \u5929\u524D`;
|
|
15972
|
+
return new Date(isoDate).toLocaleDateString("zh-CN");
|
|
15973
|
+
}
|
|
15974
|
+
async function handleCommand(input, rl, history, thinkMode, injectedSkills) {
|
|
15067
15975
|
const [cmd, ...rest] = input.slice(1).trim().split(/\s+/);
|
|
15068
15976
|
const arg = rest.join(" ");
|
|
15069
15977
|
const cfg = loadConfig();
|
|
@@ -15154,20 +16062,370 @@ async function handleCommand(input, rl, history, thinkMode) {
|
|
|
15154
16062
|
return { clearHistory: true };
|
|
15155
16063
|
case "history": {
|
|
15156
16064
|
const turns = Math.floor(history.length / 2);
|
|
15157
|
-
const
|
|
15158
|
-
const
|
|
16065
|
+
const estTokens = estimateTokens2(history);
|
|
16066
|
+
const pct = Math.round(estTokens / COMPACT_TOKEN_THRESHOLD * 100);
|
|
16067
|
+
const bar = "\u2588".repeat(Math.min(20, Math.round(pct / 5))) + "\u2591".repeat(Math.max(0, 20 - Math.round(pct / 5)));
|
|
15159
16068
|
console.log(source_default.bold("\u5BF9\u8BDD\u7EDF\u8BA1:"));
|
|
15160
16069
|
console.log(` \u8F6E\u6B21: ${turns}`);
|
|
15161
16070
|
console.log(` \u6D88\u606F\u603B\u6570: ${history.length}`);
|
|
15162
16071
|
console.log(` \u4F30\u7B97 Token: ~${estTokens.toLocaleString()}`);
|
|
16072
|
+
console.log(` \u4E0A\u4E0B\u6587\u7528\u91CF: [${pct >= 80 ? source_default.red(bar) : pct >= 50 ? source_default.yellow(bar) : source_default.green(bar)}] ${pct}%`);
|
|
16073
|
+
console.log(` \u538B\u7F29\u9608\u503C: ~${Math.round(COMPACT_TOKEN_THRESHOLD / 1e3)}k tokens`);
|
|
16074
|
+
if (pct >= 80) console.log(source_default.yellow(" \u26A0 \u63A5\u8FD1\u4E0A\u9650\uFF0C\u5EFA\u8BAE\u8FD0\u884C /compact"));
|
|
15163
16075
|
break;
|
|
15164
16076
|
}
|
|
15165
16077
|
case "save": {
|
|
15166
16078
|
saveConversation(history, arg || void 0);
|
|
15167
16079
|
break;
|
|
15168
16080
|
}
|
|
16081
|
+
// ── Session persistence ──────────────────────────────────────────────────
|
|
16082
|
+
case "sessions": {
|
|
16083
|
+
const sessions = listSessions();
|
|
16084
|
+
if (sessions.length === 0) {
|
|
16085
|
+
console.log(source_default.dim("\u6682\u65E0\u5386\u53F2\u4F1A\u8BDD\u3002\u5BF9\u8BDD\u7ED3\u675F\u540E\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002"));
|
|
16086
|
+
break;
|
|
16087
|
+
}
|
|
16088
|
+
console.log(source_default.bold(`
|
|
16089
|
+
\u5386\u53F2\u4F1A\u8BDD (${sessions.length} \u4E2A):
|
|
16090
|
+
`));
|
|
16091
|
+
sessions.slice(0, 20).forEach((s2, i2) => {
|
|
16092
|
+
const age = formatAge(s2.updatedAt);
|
|
16093
|
+
const skills = s2.skills.length > 0 ? source_default.dim(` [${s2.skills.join(", ")}]`) : "";
|
|
16094
|
+
const preview = s2.preview ? source_default.dim(` \u2014 "${s2.preview.slice(0, 40)}${s2.preview.length > 40 ? "\u2026" : ""}"`) : "";
|
|
16095
|
+
const marker = i2 === 0 ? source_default.green("\u25CF") : source_default.dim("\u25CB");
|
|
16096
|
+
console.log(` ${marker} ${source_default.cyan(s2.id)} ${source_default.dim(age)}${skills}`);
|
|
16097
|
+
if (preview) console.log(` ${preview}`);
|
|
16098
|
+
});
|
|
16099
|
+
console.log();
|
|
16100
|
+
console.log(source_default.dim("\u4F7F\u7528 /resume [id] \u6062\u590D\u4F1A\u8BDD /session-del <id> \u5220\u9664"));
|
|
16101
|
+
break;
|
|
16102
|
+
}
|
|
16103
|
+
case "resume": {
|
|
16104
|
+
let targetId = arg;
|
|
16105
|
+
if (!targetId) {
|
|
16106
|
+
const last = getLastSession();
|
|
16107
|
+
if (!last) {
|
|
16108
|
+
console.log(source_default.yellow("\u6682\u65E0\u5386\u53F2\u4F1A\u8BDD"));
|
|
16109
|
+
break;
|
|
16110
|
+
}
|
|
16111
|
+
targetId = last.id;
|
|
16112
|
+
console.log(source_default.dim(`\u6062\u590D\u6700\u8FD1\u4F1A\u8BDD: ${targetId}`));
|
|
16113
|
+
}
|
|
16114
|
+
const session = loadSession(targetId);
|
|
16115
|
+
if (!session) {
|
|
16116
|
+
const all = listSessions();
|
|
16117
|
+
const match = all.find((s3) => s3.id.includes(targetId));
|
|
16118
|
+
if (!match) {
|
|
16119
|
+
console.log(source_default.red(`\u672A\u627E\u5230\u4F1A\u8BDD: ${targetId}`));
|
|
16120
|
+
break;
|
|
16121
|
+
}
|
|
16122
|
+
const s2 = loadSession(match.id);
|
|
16123
|
+
if (!s2) {
|
|
16124
|
+
console.log(source_default.red("\u52A0\u8F7D\u5931\u8D25"));
|
|
16125
|
+
break;
|
|
16126
|
+
}
|
|
16127
|
+
console.log(source_default.green(`\u2713 \u5DF2\u6062\u590D\u4F1A\u8BDD ${s2.id} (${s2.messageCount} \u6761\u6D88\u606F)`));
|
|
16128
|
+
if (s2.skills.length > 0) console.log(source_default.dim(` \u5DF2\u52A0\u8F7D Skills: ${s2.skills.join(", ")}`));
|
|
16129
|
+
for (const sk of s2.skills) injectedSkills.set(sk, sk);
|
|
16130
|
+
return { injectHistory: s2.messages };
|
|
16131
|
+
}
|
|
16132
|
+
console.log(source_default.green(`\u2713 \u5DF2\u6062\u590D\u4F1A\u8BDD ${session.id} (${session.messageCount} \u6761\u6D88\u606F)`));
|
|
16133
|
+
if (session.skills.length > 0) console.log(source_default.dim(` \u5DF2\u52A0\u8F7D Skills: ${session.skills.join(", ")}`));
|
|
16134
|
+
for (const sk of session.skills) injectedSkills.set(sk, sk);
|
|
16135
|
+
return { injectHistory: session.messages };
|
|
16136
|
+
}
|
|
16137
|
+
case "session-save": {
|
|
16138
|
+
const sessionId = SESSION_CTX.id || void 0;
|
|
16139
|
+
if (!sessionId) {
|
|
16140
|
+
console.log(source_default.yellow("\u5F53\u524D\u4F1A\u8BDD\u5C1A\u672A\u521D\u59CB\u5316"));
|
|
16141
|
+
break;
|
|
16142
|
+
}
|
|
16143
|
+
const name = arg || (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
|
|
16144
|
+
saveSession(sessionId, name, history, Array.from(injectedSkills.keys()), SESSION_CTX.createdAt || (/* @__PURE__ */ new Date()).toISOString());
|
|
16145
|
+
console.log(source_default.green(`\u2713 \u4F1A\u8BDD\u5DF2\u4FDD\u5B58: ${sessionId} \u540D\u79F0: "${name}"`));
|
|
16146
|
+
break;
|
|
16147
|
+
}
|
|
16148
|
+
case "session-del": {
|
|
16149
|
+
if (!arg) {
|
|
16150
|
+
console.log("\u7528\u6CD5: /session-del <id>");
|
|
16151
|
+
break;
|
|
16152
|
+
}
|
|
16153
|
+
const ok = deleteSession(arg);
|
|
16154
|
+
console.log(ok ? source_default.green(`\u2713 \u5DF2\u5220\u9664\u4F1A\u8BDD: ${arg}`) : source_default.yellow(`\u672A\u627E\u5230\u4F1A\u8BDD: ${arg}`));
|
|
16155
|
+
break;
|
|
16156
|
+
}
|
|
16157
|
+
// ── Checkpoints ──────────────────────────────────────────────────────────
|
|
16158
|
+
case "checkpoint": {
|
|
16159
|
+
const sessionId = SESSION_CTX.id || "default";
|
|
16160
|
+
const sub = arg.split(/\s+/)[0]?.toLowerCase();
|
|
16161
|
+
const subArg = arg.split(/\s+/).slice(1).join(" ");
|
|
16162
|
+
if (!sub || sub === "save" || sub === "") {
|
|
16163
|
+
const label = subArg || `\u7B2C ${Math.floor(history.length / 2)} \u8F6E`;
|
|
16164
|
+
const cpId = saveCheckpoint(sessionId, label, history, Array.from(injectedSkills.keys()));
|
|
16165
|
+
console.log(source_default.green(`\u2713 \u65AD\u70B9\u5DF2\u4FDD\u5B58: ${cpId} \u6807\u7B7E: "${label}"`));
|
|
16166
|
+
} else if (sub === "list") {
|
|
16167
|
+
const cps = listCheckpoints(sessionId);
|
|
16168
|
+
if (cps.length === 0) {
|
|
16169
|
+
console.log(source_default.dim("\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u65AD\u70B9"));
|
|
16170
|
+
} else {
|
|
16171
|
+
console.log(source_default.bold(`
|
|
16172
|
+
\u5F53\u524D\u4F1A\u8BDD\u65AD\u70B9 (${cps.length} \u4E2A):
|
|
16173
|
+
`));
|
|
16174
|
+
cps.forEach((cp) => {
|
|
16175
|
+
const age = formatAge(cp.createdAt);
|
|
16176
|
+
console.log(` ${source_default.cyan(cp.id.split("-cp")[1] ?? cp.id)} ${source_default.dim(age)} "${cp.label}" (${cp.messageCount} \u6761\u6D88\u606F)`);
|
|
16177
|
+
console.log(source_default.dim(` /checkpoint restore ${cp.id}`));
|
|
16178
|
+
});
|
|
16179
|
+
}
|
|
16180
|
+
} else if (sub === "restore") {
|
|
16181
|
+
if (!subArg) {
|
|
16182
|
+
console.log("\u7528\u6CD5: /checkpoint restore <id>");
|
|
16183
|
+
break;
|
|
16184
|
+
}
|
|
16185
|
+
const cps = listCheckpoints(sessionId);
|
|
16186
|
+
const target = cps.find((cp) => cp.id === subArg || cp.id.endsWith(subArg));
|
|
16187
|
+
if (!target) {
|
|
16188
|
+
console.log(source_default.red(`\u672A\u627E\u5230\u65AD\u70B9: ${subArg}`));
|
|
16189
|
+
break;
|
|
16190
|
+
}
|
|
16191
|
+
console.log(source_default.green(`\u2713 \u5DF2\u6062\u590D\u5230\u65AD\u70B9: "${target.label}" (${target.messageCount} \u6761\u6D88\u606F)`));
|
|
16192
|
+
injectedSkills.clear();
|
|
16193
|
+
for (const sk of target.skills) injectedSkills.set(sk, sk);
|
|
16194
|
+
return { injectHistory: target.messages };
|
|
16195
|
+
} else if (sub === "clear") {
|
|
16196
|
+
const n2 = clearCheckpoints(sessionId);
|
|
16197
|
+
console.log(source_default.green(`\u2713 \u5DF2\u6E05\u9664 ${n2} \u4E2A\u65AD\u70B9`));
|
|
16198
|
+
} else {
|
|
16199
|
+
console.log("\u7528\u6CD5: /checkpoint [list|restore <id>|clear]");
|
|
16200
|
+
}
|
|
16201
|
+
break;
|
|
16202
|
+
}
|
|
16203
|
+
// ── Workdir diff ─────────────────────────────────────────────────────────
|
|
16204
|
+
case "diff": {
|
|
16205
|
+
const snap = SESSION_CTX.wdirSnapshot ?? void 0;
|
|
16206
|
+
if (!snap) {
|
|
16207
|
+
console.log(source_default.dim("\u5DE5\u4F5C\u76EE\u5F55\u5FEB\u7167\u5C1A\u672A\u5EFA\u7ACB\uFF08\u4F1A\u8BDD\u5F00\u59CB\u65F6\u81EA\u52A8\u521B\u5EFA\uFF09"));
|
|
16208
|
+
break;
|
|
16209
|
+
}
|
|
16210
|
+
const current = snapshotWorkdir(process.cwd());
|
|
16211
|
+
const { added, modified } = diffWorkdir(snap, current);
|
|
16212
|
+
if (added.length === 0 && modified.length === 0) {
|
|
16213
|
+
console.log(source_default.dim("\u672C\u6B21\u4F1A\u8BDD\u672A\u4EA7\u751F\u65B0\u6587\u4EF6\u6216\u4FEE\u6539"));
|
|
16214
|
+
} else {
|
|
16215
|
+
if (added.length > 0) {
|
|
16216
|
+
console.log(source_default.bold.green(`
|
|
16217
|
+
\u65B0\u589E\u6587\u4EF6 (${added.length} \u4E2A):`));
|
|
16218
|
+
added.forEach((f2) => console.log(` ${source_default.green("+")} ${f2}`));
|
|
16219
|
+
}
|
|
16220
|
+
if (modified.length > 0) {
|
|
16221
|
+
console.log(source_default.bold.yellow(`
|
|
16222
|
+
\u4FEE\u6539\u6587\u4EF6 (${modified.length} \u4E2A):`));
|
|
16223
|
+
modified.forEach((f2) => console.log(` ${source_default.yellow("~")} ${f2}`));
|
|
16224
|
+
}
|
|
16225
|
+
console.log();
|
|
16226
|
+
}
|
|
16227
|
+
break;
|
|
16228
|
+
}
|
|
16229
|
+
// ── /run workflow wizard ─────────────────────────────────────────────────
|
|
16230
|
+
case "run": {
|
|
16231
|
+
const targetId = arg;
|
|
16232
|
+
if (!targetId) {
|
|
16233
|
+
console.log("\u7528\u6CD5: /run <skill-id>");
|
|
16234
|
+
console.log(source_default.dim("\u793A\u4F8B: /run deseq2-analysis /run survival-analysis-clinical"));
|
|
16235
|
+
break;
|
|
16236
|
+
}
|
|
16237
|
+
const allSkillsRun = collectAllSkills();
|
|
16238
|
+
const runMatch = allSkillsRun.find((e2) => e2.id === targetId || e2.id.startsWith(targetId) || e2.id.includes(targetId));
|
|
16239
|
+
if (!runMatch) {
|
|
16240
|
+
console.log(source_default.red(`\u672A\u627E\u5230 Skill: ${targetId}`));
|
|
16241
|
+
break;
|
|
16242
|
+
}
|
|
16243
|
+
const skillPath = (0, import_path5.join)(runMatch.dir, runMatch.id, "SKILL.md");
|
|
16244
|
+
if (!(0, import_fs5.existsSync)(skillPath)) {
|
|
16245
|
+
console.log(source_default.red(`${runMatch.id} \u7F3A\u5C11 SKILL.md`));
|
|
16246
|
+
break;
|
|
16247
|
+
}
|
|
16248
|
+
const skillContent = (0, import_fs5.readFileSync)(skillPath, "utf8");
|
|
16249
|
+
const { name: skillName } = parseSkillMeta(skillContent);
|
|
16250
|
+
const paramsMatch = skillContent.match(/##\s*(?:必要参数|Required Parameters|参数)[\s\S]*?(?=\n##|$)/i);
|
|
16251
|
+
const paramsSection = paramsMatch?.[0] ?? "";
|
|
16252
|
+
console.log(source_default.bold.cyan(`
|
|
16253
|
+
\u2500\u2500\u2500 /run ${runMatch.id} \u5411\u5BFC \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`));
|
|
16254
|
+
console.log(source_default.dim(` Skill: ${skillName || runMatch.id}`));
|
|
16255
|
+
console.log(source_default.dim(" \u8BF7\u56DE\u7B54\u4EE5\u4E0B\u95EE\u9898\uFF0CAI \u5C06\u81EA\u52A8\u751F\u6210\u5E76\u6267\u884C\u5206\u6790\u811A\u672C\n"));
|
|
16256
|
+
const paramLines = paramsSection.split("\n").filter((l2) => /^[-*•]|^\d+\./.test(l2.trim())).map((l2) => l2.replace(/^[-*•\d.]+\s*/, "").trim()).filter(Boolean).slice(0, 8);
|
|
16257
|
+
const questions = paramLines.length > 0 ? paramLines : [
|
|
16258
|
+
"\u6570\u636E\u6587\u4EF6\u8DEF\u5F84\uFF08\u652F\u6301\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u5982 ./data/counts.csv\uFF09",
|
|
16259
|
+
"\u6837\u672C\u5206\u7EC4\u4FE1\u606F\uFF08\u5982 treatment,treatment,control,control\uFF09",
|
|
16260
|
+
"\u5BF9\u7167\u7EC4\u540D\u79F0",
|
|
16261
|
+
"\u5B9E\u9A8C\u7EC4\u540D\u79F0",
|
|
16262
|
+
"\u8F93\u51FA\u76EE\u5F55\uFF08\u9ED8\u8BA4: ./results\uFF09"
|
|
16263
|
+
];
|
|
16264
|
+
const answers = {};
|
|
16265
|
+
for (const q2 of questions) {
|
|
16266
|
+
const ans = await question(rl, source_default.blue(` ${q2}: `));
|
|
16267
|
+
if (ans.trim()) answers[q2] = ans.trim();
|
|
16268
|
+
}
|
|
16269
|
+
const paramSummary = Object.entries(answers).map(([k2, v2]) => `- ${k2}: ${v2}`).join("\n");
|
|
16270
|
+
const runPrompt = `\u8BF7\u6839\u636E\u4EE5\u4E0B\u53C2\u6570\uFF0C\u4F7F\u7528 ${skillName || runMatch.id} \u5DE5\u4F5C\u6D41\u751F\u6210\u5B8C\u6574\u7684\u5206\u6790\u811A\u672C\u5E76\u7ACB\u5373\u6267\u884C\uFF1A
|
|
16271
|
+
|
|
16272
|
+
\u7528\u6237\u63D0\u4F9B\u7684\u53C2\u6570\uFF1A
|
|
16273
|
+
${paramSummary}
|
|
16274
|
+
|
|
16275
|
+
\u8BF7\uFF1A
|
|
16276
|
+
1. \u751F\u6210\u5B8C\u6574\u53EF\u8FD0\u884C\u7684\u5206\u6790\u811A\u672C\uFF08R \u6216 Python\uFF09
|
|
16277
|
+
2. \u4F7F\u7528 bash \u5DE5\u5177\u6267\u884C\u811A\u672C
|
|
16278
|
+
3. \u5206\u6790\u5B8C\u6210\u540E\u7ED9\u51FA\u7ED3\u679C\u6458\u8981`;
|
|
16279
|
+
if (!injectedSkills.has(runMatch.id)) {
|
|
16280
|
+
await injectSkill(runMatch.id, history, injectedSkills, rl, true);
|
|
16281
|
+
}
|
|
16282
|
+
history.push({ role: "user", content: runPrompt });
|
|
16283
|
+
console.log(source_default.dim("\n \u6B63\u5728\u751F\u6210\u5E76\u6267\u884C\u5206\u6790\u811A\u672C...\n"));
|
|
16284
|
+
try {
|
|
16285
|
+
const runCfg = loadConfig();
|
|
16286
|
+
const reply = await chat(history, runCfg, buildSystemPrompt());
|
|
16287
|
+
history.push({ role: "assistant", content: reply });
|
|
16288
|
+
} catch (err) {
|
|
16289
|
+
console.error(source_default.red(`\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`));
|
|
16290
|
+
history.pop();
|
|
16291
|
+
}
|
|
16292
|
+
break;
|
|
16293
|
+
}
|
|
16294
|
+
// ── /check-env ───────────────────────────────────────────────────────────
|
|
16295
|
+
case "check-env": {
|
|
16296
|
+
const checkId = arg;
|
|
16297
|
+
const skillsToCheck = checkId ? (() => {
|
|
16298
|
+
const all = collectAllSkills();
|
|
16299
|
+
const m2 = all.find((e2) => e2.id === checkId || e2.id.startsWith(checkId) || e2.id.includes(checkId));
|
|
16300
|
+
return m2 ? [m2] : [];
|
|
16301
|
+
})() : collectAllSkills().filter((e2) => injectedSkills.has(e2.id));
|
|
16302
|
+
if (skillsToCheck.length === 0) {
|
|
16303
|
+
console.log(source_default.yellow(checkId ? `\u672A\u627E\u5230 Skill: ${checkId}` : "\u5F53\u524D\u4F1A\u8BDD\u672A\u52A0\u8F7D\u4EFB\u4F55 Skill"));
|
|
16304
|
+
break;
|
|
16305
|
+
}
|
|
16306
|
+
for (const entry of skillsToCheck) {
|
|
16307
|
+
const sp = (0, import_path5.join)(entry.dir, entry.id, "SKILL.md");
|
|
16308
|
+
if (!(0, import_fs5.existsSync)(sp)) continue;
|
|
16309
|
+
const content = (0, import_fs5.readFileSync)(sp, "utf8");
|
|
16310
|
+
const { name } = parseSkillMeta(content);
|
|
16311
|
+
console.log(source_default.bold(`
|
|
16312
|
+
\u68C0\u6D4B ${name || entry.id} \u7684\u4F9D\u8D56\u73AF\u5883:`));
|
|
16313
|
+
const rPkgs = [.../* @__PURE__ */ new Set([
|
|
16314
|
+
...(content.match(/library\(([\w.]+)\)/g) ?? []).map((m2) => m2.replace(/library\(|\)/g, "")),
|
|
16315
|
+
...(content.match(/require\(([\w.]+)\)/g) ?? []).map((m2) => m2.replace(/require\(|\)/g, "")),
|
|
16316
|
+
...(content.match(/BiocManager::install\("([^"]+)"\)/g) ?? []).map((m2) => m2.replace(/BiocManager::install\("|"\)/g, ""))
|
|
16317
|
+
])];
|
|
16318
|
+
const pyPkgs = [.../* @__PURE__ */ new Set([
|
|
16319
|
+
...(content.match(/import (\w+)/g) ?? []).map((m2) => m2.replace("import ", "")),
|
|
16320
|
+
...(content.match(/from (\w+) import/g) ?? []).map((m2) => m2.replace("from ", "").replace(" import", ""))
|
|
16321
|
+
])];
|
|
16322
|
+
if (rPkgs.length > 0) {
|
|
16323
|
+
console.log(source_default.dim(` R \u5305 (${rPkgs.length} \u4E2A): ${rPkgs.join(", ")}`));
|
|
16324
|
+
const checkScript = rPkgs.map(
|
|
16325
|
+
(p2) => `cat(sprintf(" %-30s %s\\n", "${p2}", if(requireNamespace("${p2}", quietly=TRUE)) "\u2713 \u5DF2\u5B89\u88C5" else "\u2717 \u672A\u5B89\u88C5"))`
|
|
16326
|
+
).join("\n");
|
|
16327
|
+
const result = await executeTool("bash", { command: `Rscript -e '${checkScript}'` });
|
|
16328
|
+
if (result.error) {
|
|
16329
|
+
console.log(source_default.dim(" (R \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u7528)"));
|
|
16330
|
+
} else {
|
|
16331
|
+
console.log(result.output);
|
|
16332
|
+
const missing = rPkgs.filter((p2) => result.output.includes(`${p2}`) && result.output.includes("\u2717"));
|
|
16333
|
+
if (missing.length > 0) {
|
|
16334
|
+
console.log(source_default.yellow(` \u7F3A\u5C11 ${missing.length} \u4E2A R \u5305\uFF0C\u5B89\u88C5\u547D\u4EE4:`));
|
|
16335
|
+
console.log(source_default.cyan(` !Rscript -e 'install.packages(c(${missing.map((p2) => `"${p2}"`).join(", ")}))'`));
|
|
16336
|
+
const biocPkgs = missing.filter((p2) => ["DESeq2", "edgeR", "limma", "clusterProfiler", "Seurat", "SingleCellExperiment", "BiocGenerics"].includes(p2));
|
|
16337
|
+
if (biocPkgs.length > 0) {
|
|
16338
|
+
console.log(source_default.cyan(` !Rscript -e 'BiocManager::install(c(${biocPkgs.map((p2) => `"${p2}"`).join(", ")}))'`));
|
|
16339
|
+
}
|
|
16340
|
+
}
|
|
16341
|
+
}
|
|
16342
|
+
}
|
|
16343
|
+
if (pyPkgs.length > 0) {
|
|
16344
|
+
const commonPy = ["numpy", "pandas", "scipy", "sklearn", "matplotlib", "seaborn", "scanpy", "anndata", "torch"];
|
|
16345
|
+
const relevantPy = pyPkgs.filter((p2) => commonPy.includes(p2));
|
|
16346
|
+
if (relevantPy.length > 0) {
|
|
16347
|
+
console.log(source_default.dim(` Python \u5305 (${relevantPy.length} \u4E2A): ${relevantPy.join(", ")}`));
|
|
16348
|
+
const checkPy = relevantPy.map(
|
|
16349
|
+
(p2) => `python3 -c "import ${p2}; print(' %-30s \u2713 \u5DF2\u5B89\u88C5' % '${p2}')" 2>/dev/null || echo " ${p2.padEnd(30)} \u2717 \u672A\u5B89\u88C5"`
|
|
16350
|
+
).join(" && ");
|
|
16351
|
+
const result = await executeTool("bash", { command: checkPy });
|
|
16352
|
+
if (!result.error) console.log(result.output);
|
|
16353
|
+
}
|
|
16354
|
+
}
|
|
16355
|
+
if (rPkgs.length === 0 && pyPkgs.length === 0) {
|
|
16356
|
+
console.log(source_default.dim(" \u672A\u68C0\u6D4B\u5230\u660E\u786E\u7684\u5305\u4F9D\u8D56\u58F0\u660E"));
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
16359
|
+
break;
|
|
16360
|
+
}
|
|
16361
|
+
// ── /install from GitHub ─────────────────────────────────────────────────
|
|
16362
|
+
case "install": {
|
|
16363
|
+
if (!arg) {
|
|
16364
|
+
console.log("\u7528\u6CD5: /install <github-url>");
|
|
16365
|
+
console.log(source_default.dim("\u793A\u4F8B: /install https://github.com/user/my-skill"));
|
|
16366
|
+
console.log(source_default.dim(" /install user/repo (GitHub \u7B80\u5199)"));
|
|
16367
|
+
break;
|
|
16368
|
+
}
|
|
16369
|
+
let repoUrl = arg;
|
|
16370
|
+
if (!repoUrl.startsWith("http")) {
|
|
16371
|
+
repoUrl = `https://github.com/${repoUrl}`;
|
|
16372
|
+
}
|
|
16373
|
+
const repoName = repoUrl.replace(/\.git$/, "").split("/").pop() ?? "unknown-skill";
|
|
16374
|
+
const installTarget = (0, import_path5.join)(SKILLS_DIR, repoName);
|
|
16375
|
+
if ((0, import_fs5.existsSync)(installTarget)) {
|
|
16376
|
+
console.log(source_default.yellow(`Skill "${repoName}" \u5DF2\u5B58\u5728\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u5148 /uninstall ${repoName}`));
|
|
16377
|
+
break;
|
|
16378
|
+
}
|
|
16379
|
+
console.log(source_default.dim(`\u6B63\u5728\u4ECE GitHub \u5B89\u88C5 Skill: ${repoName}...`));
|
|
16380
|
+
const cloneResult = await executeTool("bash", {
|
|
16381
|
+
command: `git clone --depth 1 "${repoUrl}" "${installTarget}" 2>&1`
|
|
16382
|
+
});
|
|
16383
|
+
if (cloneResult.error || cloneResult.output.includes("fatal:")) {
|
|
16384
|
+
console.log(source_default.red(`\u5B89\u88C5\u5931\u8D25: ${cloneResult.output || cloneResult.error}`));
|
|
16385
|
+
break;
|
|
16386
|
+
}
|
|
16387
|
+
const skillMdPath = (0, import_path5.join)(installTarget, "SKILL.md");
|
|
16388
|
+
if (!(0, import_fs5.existsSync)(skillMdPath)) {
|
|
16389
|
+
console.log(source_default.red(`\u5B89\u88C5\u5931\u8D25: ${repoName} \u7F3A\u5C11 SKILL.md \u6587\u4EF6`));
|
|
16390
|
+
await executeTool("bash", { command: `rm -rf "${installTarget}"` });
|
|
16391
|
+
break;
|
|
16392
|
+
}
|
|
16393
|
+
const content = (0, import_fs5.readFileSync)(skillMdPath, "utf8");
|
|
16394
|
+
const { name, shortDesc } = parseSkillMeta(content);
|
|
16395
|
+
console.log(source_default.green(`\u2713 Skill \u5B89\u88C5\u6210\u529F!`));
|
|
16396
|
+
console.log(` ID: ${source_default.cyan(repoName)}`);
|
|
16397
|
+
console.log(` \u540D\u79F0: ${name || repoName}`);
|
|
16398
|
+
if (shortDesc) console.log(` \u529F\u80FD: ${source_default.dim(shortDesc)}`);
|
|
16399
|
+
console.log(source_default.dim(` \u4F7F\u7528 /sk ${repoName} \u52A0\u8F7D`));
|
|
16400
|
+
break;
|
|
16401
|
+
}
|
|
16402
|
+
case "uninstall": {
|
|
16403
|
+
if (!arg) {
|
|
16404
|
+
console.log("\u7528\u6CD5: /uninstall <skill-id>");
|
|
16405
|
+
break;
|
|
16406
|
+
}
|
|
16407
|
+
const uninstallPath = (0, import_path5.join)(SKILLS_DIR, arg);
|
|
16408
|
+
if (!(0, import_fs5.existsSync)(uninstallPath)) {
|
|
16409
|
+
console.log(source_default.red(`\u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684 Skill: ${arg}`));
|
|
16410
|
+
console.log(source_default.dim("\u6CE8\u610F: \u53EA\u80FD\u5378\u8F7D\u901A\u8FC7 /install \u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill"));
|
|
16411
|
+
break;
|
|
16412
|
+
}
|
|
16413
|
+
const ans = await question(rl, source_default.yellow(` \u786E\u8BA4\u5378\u8F7D Skill "${arg}"\uFF1F[y/N] \u203A `));
|
|
16414
|
+
if (ans.trim().toLowerCase() !== "y") {
|
|
16415
|
+
console.log(source_default.dim(" \u5DF2\u53D6\u6D88"));
|
|
16416
|
+
break;
|
|
16417
|
+
}
|
|
16418
|
+
const rmResult = await executeTool("bash", { command: `rm -rf "${uninstallPath}"` });
|
|
16419
|
+
if (rmResult.error) {
|
|
16420
|
+
console.log(source_default.red(`\u5378\u8F7D\u5931\u8D25: ${rmResult.error}`));
|
|
16421
|
+
} else {
|
|
16422
|
+
injectedSkills.delete(arg);
|
|
16423
|
+
console.log(source_default.green(`\u2713 Skill "${arg}" \u5DF2\u5378\u8F7D`));
|
|
16424
|
+
}
|
|
16425
|
+
break;
|
|
16426
|
+
}
|
|
15169
16427
|
case "compact": {
|
|
15170
|
-
const tokens =
|
|
16428
|
+
const tokens = estimateTokens2(history);
|
|
15171
16429
|
if (history.length < 4) {
|
|
15172
16430
|
console.log(source_default.dim("\u5BF9\u8BDD\u592A\u77ED\uFF0C\u65E0\u9700\u538B\u7F29"));
|
|
15173
16431
|
break;
|
|
@@ -15189,7 +16447,7 @@ ${summary}` },
|
|
|
15189
16447
|
{ role: "assistant", content: "\u2713 \u5DF2\u7406\u89E3\u4E4B\u524D\u7684\u5BF9\u8BDD\u6458\u8981\uFF0C\u8BF7\u7EE7\u7EED\u3002" },
|
|
15190
16448
|
...recent
|
|
15191
16449
|
];
|
|
15192
|
-
const after =
|
|
16450
|
+
const after = estimateTokens2(newHistory);
|
|
15193
16451
|
console.log(source_default.green(`\u2713 \u538B\u7F29\u5B8C\u6210: ${history.length} \u6761\u6D88\u606F \u2192 ${newHistory.length} \u6761\uFF0C~${Math.round(after / 1e3)}k tokens`));
|
|
15194
16452
|
return { injectHistory: newHistory };
|
|
15195
16453
|
} catch (err) {
|
|
@@ -15197,6 +16455,52 @@ ${summary}` },
|
|
|
15197
16455
|
}
|
|
15198
16456
|
break;
|
|
15199
16457
|
}
|
|
16458
|
+
case "skills": {
|
|
16459
|
+
if (injectedSkills.size === 0) {
|
|
16460
|
+
console.log(source_default.dim("\u5F53\u524D\u4F1A\u8BDD\u672A\u52A0\u8F7D\u4EFB\u4F55 Skill"));
|
|
16461
|
+
console.log(source_default.dim("\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u52A0\u8F7D\uFF0C\u6216\u76F4\u63A5\u63CF\u8FF0\u4EFB\u52A1\u81EA\u52A8\u6FC0\u6D3B"));
|
|
16462
|
+
} else {
|
|
16463
|
+
console.log(source_default.bold(`
|
|
16464
|
+
\u5F53\u524D\u5DF2\u52A0\u8F7D\u7684 Skills (${injectedSkills.size} \u4E2A):
|
|
16465
|
+
`));
|
|
16466
|
+
for (const [id, name] of injectedSkills) {
|
|
16467
|
+
console.log(` ${source_default.green("\u25CF")} ${source_default.cyan(id)} ${source_default.dim("\u2014 " + name)}`);
|
|
16468
|
+
console.log(source_default.dim(` /unload ${id} \u53EF\u5378\u8F7D\u6B64 Skill`));
|
|
16469
|
+
}
|
|
16470
|
+
console.log();
|
|
16471
|
+
console.log(source_default.dim("\u63D0\u793A: /clear \u6E05\u7A7A\u5168\u90E8\u5BF9\u8BDD\u548C Skills | /unload <id> \u5378\u8F7D\u5355\u4E2A"));
|
|
16472
|
+
}
|
|
16473
|
+
break;
|
|
16474
|
+
}
|
|
16475
|
+
case "unload": {
|
|
16476
|
+
if (!arg) {
|
|
16477
|
+
console.log("\u7528\u6CD5: /unload <skill-id>");
|
|
16478
|
+
console.log(source_default.dim("\u4F7F\u7528 /skills \u67E5\u770B\u5F53\u524D\u5DF2\u52A0\u8F7D\u7684 Skills"));
|
|
16479
|
+
break;
|
|
16480
|
+
}
|
|
16481
|
+
const loadedIds = Array.from(injectedSkills.keys());
|
|
16482
|
+
const targetId = loadedIds.find((id) => id === arg || id.includes(arg));
|
|
16483
|
+
if (!targetId) {
|
|
16484
|
+
console.log(source_default.yellow(`\u672A\u627E\u5230\u5DF2\u52A0\u8F7D\u7684 Skill: "${arg}"`));
|
|
16485
|
+
console.log(source_default.dim("\u4F7F\u7528 /skills \u67E5\u770B\u5F53\u524D\u5DF2\u52A0\u8F7D\u7684 Skills"));
|
|
16486
|
+
break;
|
|
16487
|
+
}
|
|
16488
|
+
const SKILL_MARKER = `[Skill \u5DF2\u52A0\u8F7D: ${targetId}]`;
|
|
16489
|
+
let removed = 0;
|
|
16490
|
+
for (let i2 = history.length - 1; i2 >= 0; i2--) {
|
|
16491
|
+
const content = typeof history[i2].content === "string" ? history[i2].content : "";
|
|
16492
|
+
if (history[i2].role === "user" && content.startsWith(SKILL_MARKER)) {
|
|
16493
|
+
const toRemove = i2 + 1 < history.length && history[i2 + 1].role === "assistant" ? 2 : 1;
|
|
16494
|
+
history.splice(i2, toRemove);
|
|
16495
|
+
removed += toRemove;
|
|
16496
|
+
break;
|
|
16497
|
+
}
|
|
16498
|
+
}
|
|
16499
|
+
injectedSkills.delete(targetId);
|
|
16500
|
+
console.log(source_default.green(`\u2713 Skill "${targetId}" \u5DF2\u5378\u8F7D`));
|
|
16501
|
+
if (removed > 0) console.log(source_default.dim(` \u5DF2\u4ECE\u5BF9\u8BDD\u5386\u53F2\u4E2D\u79FB\u9664 ${removed} \u6761\u6CE8\u5165\u6D88\u606F`));
|
|
16502
|
+
break;
|
|
16503
|
+
}
|
|
15200
16504
|
case "think": {
|
|
15201
16505
|
const val = arg.toLowerCase();
|
|
15202
16506
|
if (val === "on" || val === "1" || val === "true") {
|
|
@@ -15221,29 +16525,41 @@ ${summary}` },
|
|
|
15221
16525
|
const allSkills = collectAllSkills();
|
|
15222
16526
|
const idMatch = allSkills.find((e2) => e2.id === arg || e2.id.startsWith(arg) || e2.id.includes(arg));
|
|
15223
16527
|
if (idMatch) {
|
|
15224
|
-
injectSkill(
|
|
16528
|
+
await injectSkill(idMatch.id, history, injectedSkills, rl);
|
|
15225
16529
|
break;
|
|
15226
16530
|
}
|
|
15227
|
-
const
|
|
15228
|
-
|
|
16531
|
+
const spinner = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
16532
|
+
let si = 0;
|
|
16533
|
+
const spinTimer = setInterval(() => {
|
|
16534
|
+
process.stdout.write(`\r ${source_default.cyan(spinner[si++ % spinner.length])} \u6B63\u5728\u7528 AI \u641C\u7D22\u6700\u5339\u914D\u7684 Skills...`);
|
|
16535
|
+
}, 80);
|
|
16536
|
+
const llmRecs = await llmRecommendSkills(arg);
|
|
16537
|
+
clearInterval(spinTimer);
|
|
16538
|
+
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
16539
|
+
if (llmRecs.length === 0) {
|
|
16540
|
+
console.log(source_default.yellow(" AI \u63A8\u8350\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u663E\u793A\u5173\u952E\u8BCD\u5339\u914D\u7ED3\u679C:"));
|
|
15229
16541
|
listSkills(arg);
|
|
15230
16542
|
break;
|
|
15231
16543
|
}
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
injectSkill(routes[0].id, history);
|
|
15235
|
-
} else {
|
|
15236
|
-
console.log(source_default.bold(`
|
|
15237
|
-
\u6839\u636E\u63CF\u8FF0\u63A8\u8350\u4EE5\u4E0B Skills:
|
|
16544
|
+
console.log(source_default.bold(`
|
|
16545
|
+
AI \u4E3A\u60A8\u63A8\u8350\u4EE5\u4E0B Skills (\u8F93\u5165\u5E8F\u53F7\u76F4\u63A5\u52A0\u8F7D):
|
|
15238
16546
|
`));
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
16547
|
+
llmRecs.forEach((r2, i2) => {
|
|
16548
|
+
const num = source_default.bold.cyan(`[${i2 + 1}]`);
|
|
16549
|
+
console.log(` ${num} ${source_default.cyan(r2.id)}`);
|
|
16550
|
+
console.log(` ${source_default.dim(r2.reason)}`);
|
|
16551
|
+
});
|
|
16552
|
+
console.log();
|
|
16553
|
+
console.log(source_default.dim(" \u8F93\u5165\u5E8F\u53F7 1-" + llmRecs.length + " \u52A0\u8F7D\uFF0C\u76F4\u63A5\u56DE\u8F66\u53D6\u6D88\uFF0C\u6216\u8F93\u5165 /sk <id> \u52A0\u8F7D\u5176\u4ED6"));
|
|
16554
|
+
console.log();
|
|
16555
|
+
const choice = await question(rl, source_default.blue(" \u9009\u62E9 \u203A "));
|
|
16556
|
+
const choiceNum = parseInt(choice.trim(), 10);
|
|
16557
|
+
if (!isNaN(choiceNum) && choiceNum >= 1 && choiceNum <= llmRecs.length) {
|
|
16558
|
+
await injectSkill(llmRecs[choiceNum - 1].id, history, injectedSkills, rl);
|
|
16559
|
+
} else if (choice.trim() === "") {
|
|
16560
|
+
console.log(source_default.dim(" \u5DF2\u53D6\u6D88"));
|
|
16561
|
+
} else {
|
|
16562
|
+
console.log(source_default.dim(` \u63D0\u793A: \u4F7F\u7528 /sk ${choice.trim()} \u52A0\u8F7D\u6307\u5B9A Skill`));
|
|
15247
16563
|
}
|
|
15248
16564
|
break;
|
|
15249
16565
|
}
|
|
@@ -15279,8 +16595,8 @@ ${summary}` },
|
|
|
15279
16595
|
console.log("\u7528\u6CD5: /cd <\u8DEF\u5F84>");
|
|
15280
16596
|
break;
|
|
15281
16597
|
}
|
|
15282
|
-
const target = (0,
|
|
15283
|
-
if (!(0,
|
|
16598
|
+
const target = (0, import_path5.resolve)(arg.replace(/^~/, (0, import_os3.homedir)()));
|
|
16599
|
+
if (!(0, import_fs5.existsSync)(target)) {
|
|
15284
16600
|
console.log(source_default.red(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`));
|
|
15285
16601
|
break;
|
|
15286
16602
|
}
|
|
@@ -15293,11 +16609,12 @@ ${summary}` },
|
|
|
15293
16609
|
break;
|
|
15294
16610
|
case "tools":
|
|
15295
16611
|
console.log(source_default.bold("AI \u53EF\u8C03\u7528\u7684\u5DE5\u5177:"));
|
|
15296
|
-
console.log(` ${source_default.cyan("bash")} \u6267\u884C Shell \u547D\u4EE4 (R/Python/bash \u811A\u672C\u3001\u751F\u4FE1\u5DE5\u5177)`);
|
|
15297
|
-
console.log(` ${source_default.cyan("read_file")} \u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9 (\u652F\u6301 ~/ \u8DEF\u5F84)`);
|
|
16612
|
+
console.log(` ${source_default.cyan("bash")} \u6267\u884C Shell \u547D\u4EE4 (R/Python/bash \u811A\u672C\u3001\u751F\u4FE1\u5DE5\u5177) \u2014 \u5B9E\u65F6\u6D41\u5F0F\u8F93\u51FA`);
|
|
16613
|
+
console.log(` ${source_default.cyan("read_file")} \u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9 (\u652F\u6301 ~/ \u8DEF\u5F84\uFF0C\u9ED8\u8BA4 500 \u884C)`);
|
|
15298
16614
|
console.log(` ${source_default.cyan("write_file")} \u521B\u5EFA\u6216\u8986\u5199\u6587\u4EF6`);
|
|
15299
16615
|
console.log(` ${source_default.cyan("list_dir")} \u5217\u51FA\u76EE\u5F55\u5185\u5BB9`);
|
|
15300
16616
|
console.log(` ${source_default.cyan("search_files")} glob \u641C\u7D22\u6587\u4EF6 (\u5982 *.R, *.csv)`);
|
|
16617
|
+
console.log(` ${source_default.cyan("fetch_geo")} \u67E5\u8BE2 NCBI GEO \u6570\u636E\u5E93 (GSE/GDS/GPL/GSM \u7F16\u53F7)`);
|
|
15301
16618
|
console.log();
|
|
15302
16619
|
console.log(source_default.dim("\u63D0\u793A: \u76F4\u63A5\u63CF\u8FF0\u4EFB\u52A1\uFF0CAI \u4F1A\u81EA\u52A8\u51B3\u5B9A\u8C03\u7528\u54EA\u4E2A\u5DE5\u5177"));
|
|
15303
16620
|
break;
|
|
@@ -15332,13 +16649,39 @@ async function main() {
|
|
|
15332
16649
|
console.log(` ${source_default.bold("\u6A21\u578B:")} ${source_default.green(cfg.model)}`);
|
|
15333
16650
|
console.log(` ${source_default.bold("Skills:")} ${totalSkills > 0 ? source_default.green(`${totalSkills} \u4E2A`) : source_default.yellow("\u672A\u5B89\u88C5")} ${source_default.dim("(/sk \u641C\u7D22 /cat \u5206\u7C7B\u76EE\u5F55)")}`);
|
|
15334
16651
|
console.log(` ${source_default.bold("\u5DE5\u5177:")} bash \xB7 read_file \xB7 write_file \xB7 list_dir \xB7 search_files`);
|
|
16652
|
+
console.log(` ${source_default.bold("\u65B0\u529F\u80FD:")} /sessions /resume /checkpoint /run /check-env /install /diff`);
|
|
15335
16653
|
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"));
|
|
16654
|
+
const lastSess = getLastSession();
|
|
16655
|
+
if (lastSess) {
|
|
16656
|
+
const age = (() => {
|
|
16657
|
+
const diff = Date.now() - new Date(lastSess.updatedAt).getTime();
|
|
16658
|
+
const h2 = Math.floor(diff / 36e5);
|
|
16659
|
+
const d2 = Math.floor(diff / 864e5);
|
|
16660
|
+
return d2 > 0 ? `${d2}\u5929\u524D` : h2 > 0 ? `${h2}\u5C0F\u65F6\u524D` : "\u521A\u521A";
|
|
16661
|
+
})();
|
|
16662
|
+
console.log(source_default.dim(` \u4E0A\u6B21\u4F1A\u8BDD: ${lastSess.id} ${age} /resume \u6062\u590D`));
|
|
16663
|
+
}
|
|
15336
16664
|
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"));
|
|
15337
16665
|
console.log();
|
|
15338
16666
|
const systemPrompt = buildSystemPrompt();
|
|
15339
16667
|
let history = [];
|
|
15340
16668
|
let thinkMode = false;
|
|
15341
|
-
const injectedSkills = /* @__PURE__ */ new
|
|
16669
|
+
const injectedSkills = /* @__PURE__ */ new Map();
|
|
16670
|
+
const sessionId = newSessionId();
|
|
16671
|
+
const sessionCreatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
16672
|
+
SESSION_CTX.id = sessionId;
|
|
16673
|
+
SESSION_CTX.createdAt = sessionCreatedAt;
|
|
16674
|
+
const wdirSnapshot = snapshotWorkdir(process.cwd());
|
|
16675
|
+
SESSION_CTX.wdirSnapshot = wdirSnapshot;
|
|
16676
|
+
function autoSaveSession() {
|
|
16677
|
+
if (history.length === 0) return;
|
|
16678
|
+
try {
|
|
16679
|
+
saveSession(sessionId, sessionCreatedAt, history, Array.from(injectedSkills.keys()), sessionCreatedAt);
|
|
16680
|
+
} catch {
|
|
16681
|
+
}
|
|
16682
|
+
}
|
|
16683
|
+
let lastCheckpointMsgCount = 0;
|
|
16684
|
+
const CHECKPOINT_INTERVAL = 6;
|
|
15342
16685
|
while (true) {
|
|
15343
16686
|
let input;
|
|
15344
16687
|
const thinkIndicator = thinkMode ? source_default.yellow("[\u601D\u8003]") + " " : "";
|
|
@@ -15355,7 +16698,7 @@ async function main() {
|
|
|
15355
16698
|
break;
|
|
15356
16699
|
}
|
|
15357
16700
|
if (trimmed.startsWith("/")) {
|
|
15358
|
-
const result = await handleCommand(trimmed, rl, history, thinkMode);
|
|
16701
|
+
const result = await handleCommand(trimmed, rl, history, thinkMode, injectedSkills);
|
|
15359
16702
|
if (result.exit) break;
|
|
15360
16703
|
if (result.clearHistory) {
|
|
15361
16704
|
history = [];
|
|
@@ -15365,13 +16708,34 @@ async function main() {
|
|
|
15365
16708
|
if (result.thinkMode !== void 0) thinkMode = result.thinkMode;
|
|
15366
16709
|
continue;
|
|
15367
16710
|
}
|
|
16711
|
+
if (trimmed.startsWith("!")) {
|
|
16712
|
+
const cmd = trimmed.slice(1).trim();
|
|
16713
|
+
if (!cmd) {
|
|
16714
|
+
console.log(source_default.yellow("\u7528\u6CD5: !<\u547D\u4EE4> \u4F8B: !ls -la !Rscript analysis.R !python script.py"));
|
|
16715
|
+
continue;
|
|
16716
|
+
}
|
|
16717
|
+
console.log(source_default.dim(`[\u76F4\u63A5\u6267\u884C] ${cmd}`));
|
|
16718
|
+
const t0 = Date.now();
|
|
16719
|
+
const result = await executeTool("bash", { command: cmd }, (chunk) => {
|
|
16720
|
+
process.stdout.write(source_default.dim(" \u2502 ") + chunk);
|
|
16721
|
+
});
|
|
16722
|
+
const elapsed = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
16723
|
+
if (result.error) {
|
|
16724
|
+
console.log(source_default.yellow(`
|
|
16725
|
+
\u2717 ${result.error} ${source_default.dim("(" + elapsed + "s)")}`));
|
|
16726
|
+
} else {
|
|
16727
|
+
console.log(source_default.green(`
|
|
16728
|
+
\u2713 \u5B8C\u6210 ${source_default.dim("(" + elapsed + "s)")}`));
|
|
16729
|
+
}
|
|
16730
|
+
console.log();
|
|
16731
|
+
continue;
|
|
16732
|
+
}
|
|
15368
16733
|
const { routes: suggestedRoutes, topScore } = routeSkill(trimmed);
|
|
15369
16734
|
const newRoutes = suggestedRoutes.filter((r2) => !injectedSkills.has(r2.id));
|
|
15370
16735
|
if (newRoutes.length === 1 && topScore >= 8) {
|
|
15371
16736
|
const r2 = newRoutes[0];
|
|
15372
|
-
const ok = injectSkill(r2.id, history);
|
|
16737
|
+
const ok = await injectSkill(r2.id, history, injectedSkills, rl, true);
|
|
15373
16738
|
if (ok) {
|
|
15374
|
-
injectedSkills.add(r2.id);
|
|
15375
16739
|
console.log(source_default.dim(" (\u63D0\u793A: /clear \u53EF\u6E05\u9664\u4E0A\u4E0B\u6587\u540E\u5207\u6362 Skill)"));
|
|
15376
16740
|
}
|
|
15377
16741
|
} else if (newRoutes.length >= 2 && topScore >= 4) {
|
|
@@ -15390,6 +16754,17 @@ ${expanded}` : expanded;
|
|
|
15390
16754
|
const reply = await chat(history, currentCfg, systemPrompt);
|
|
15391
16755
|
history.push({ role: "assistant", content: reply });
|
|
15392
16756
|
history = await maybeCompact(history, currentCfg);
|
|
16757
|
+
autoSaveSession();
|
|
16758
|
+
if (history.length - lastCheckpointMsgCount >= CHECKPOINT_INTERVAL) {
|
|
16759
|
+
const label = `\u7B2C ${Math.floor(history.length / 2)} \u8F6E`;
|
|
16760
|
+
saveCheckpoint(sessionId, label, history, Array.from(injectedSkills.keys()));
|
|
16761
|
+
lastCheckpointMsgCount = history.length;
|
|
16762
|
+
}
|
|
16763
|
+
if (injectedSkills.size > 0) {
|
|
16764
|
+
const ids = Array.from(injectedSkills.keys()).join(" \xB7 ");
|
|
16765
|
+
console.log(source_default.dim(`
|
|
16766
|
+
[\u6FC0\u6D3B Skill: ${ids}]`));
|
|
16767
|
+
}
|
|
15393
16768
|
} catch (err) {
|
|
15394
16769
|
const msg = err instanceof Error ? err.message : String(err);
|
|
15395
16770
|
console.error(source_default.red(`
|