exomind 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,6 +26,7 @@ exomind whoami # 验证登录
26
26
  exomind ingest "Redis 持久化:RDB 快照 + AOF 日志,混合模式推荐" -t "Redis 持久化" --tag redis
27
27
  echo "管道内容" | exomind ingest -t "标题"
28
28
  exomind ingest --file ./notes.md -t "标题"
29
+ exomind ingest --dir ./notes --recursive # 目录批量(增量: SHA-256 跳过未变文件)
29
30
 
30
31
  # 查询与搜索
31
32
  exomind query "Redis RDB 和 AOF 的区别?"
package/dist/cli.js CHANGED
@@ -965,8 +965,8 @@ var require_command = __commonJS({
965
965
  "use strict";
966
966
  var EventEmitter = require("events").EventEmitter;
967
967
  var childProcess = require("child_process");
968
- var path4 = require("path");
969
- var fs5 = require("fs");
968
+ var path6 = require("path");
969
+ var fs7 = require("fs");
970
970
  var process2 = require("process");
971
971
  var { Argument: Argument2, humanReadableArgName } = require_argument();
972
972
  var { CommanderError: CommanderError2 } = require_error();
@@ -1898,11 +1898,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1898
1898
  let launchWithNode = false;
1899
1899
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1900
1900
  function findFile(baseDir, baseName) {
1901
- const localBin = path4.resolve(baseDir, baseName);
1902
- if (fs5.existsSync(localBin)) return localBin;
1903
- if (sourceExt.includes(path4.extname(baseName))) return void 0;
1901
+ const localBin = path6.resolve(baseDir, baseName);
1902
+ if (fs7.existsSync(localBin)) return localBin;
1903
+ if (sourceExt.includes(path6.extname(baseName))) return void 0;
1904
1904
  const foundExt = sourceExt.find(
1905
- (ext) => fs5.existsSync(`${localBin}${ext}`)
1905
+ (ext) => fs7.existsSync(`${localBin}${ext}`)
1906
1906
  );
1907
1907
  if (foundExt) return `${localBin}${foundExt}`;
1908
1908
  return void 0;
@@ -1914,21 +1914,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1914
1914
  if (this._scriptPath) {
1915
1915
  let resolvedScriptPath;
1916
1916
  try {
1917
- resolvedScriptPath = fs5.realpathSync(this._scriptPath);
1917
+ resolvedScriptPath = fs7.realpathSync(this._scriptPath);
1918
1918
  } catch (err) {
1919
1919
  resolvedScriptPath = this._scriptPath;
1920
1920
  }
1921
- executableDir = path4.resolve(
1922
- path4.dirname(resolvedScriptPath),
1921
+ executableDir = path6.resolve(
1922
+ path6.dirname(resolvedScriptPath),
1923
1923
  executableDir
1924
1924
  );
1925
1925
  }
1926
1926
  if (executableDir) {
1927
1927
  let localFile = findFile(executableDir, executableFile);
1928
1928
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1929
- const legacyName = path4.basename(
1929
+ const legacyName = path6.basename(
1930
1930
  this._scriptPath,
1931
- path4.extname(this._scriptPath)
1931
+ path6.extname(this._scriptPath)
1932
1932
  );
1933
1933
  if (legacyName !== this._name) {
1934
1934
  localFile = findFile(
@@ -1939,7 +1939,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1939
1939
  }
1940
1940
  executableFile = localFile || executableFile;
1941
1941
  }
1942
- launchWithNode = sourceExt.includes(path4.extname(executableFile));
1942
+ launchWithNode = sourceExt.includes(path6.extname(executableFile));
1943
1943
  let proc;
1944
1944
  if (process2.platform !== "win32") {
1945
1945
  if (launchWithNode) {
@@ -2779,7 +2779,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2779
2779
  * @return {Command}
2780
2780
  */
2781
2781
  nameFromFilename(filename) {
2782
- this._name = path4.basename(filename, path4.extname(filename));
2782
+ this._name = path6.basename(filename, path6.extname(filename));
2783
2783
  return this;
2784
2784
  }
2785
2785
  /**
@@ -2793,9 +2793,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2793
2793
  * @param {string} [path]
2794
2794
  * @return {(string|null|Command)}
2795
2795
  */
2796
- executableDir(path5) {
2797
- if (path5 === void 0) return this._executableDir;
2798
- this._executableDir = path5;
2796
+ executableDir(path7) {
2797
+ if (path7 === void 0) return this._executableDir;
2798
+ this._executableDir = path7;
2799
2799
  return this;
2800
2800
  }
2801
2801
  /**
@@ -3119,7 +3119,7 @@ var {
3119
3119
  // package.json
3120
3120
  var package_default = {
3121
3121
  name: "exomind",
3122
- version: "0.1.2",
3122
+ version: "0.2.0",
3123
3123
  description: "ExoMind \u8DE8\u5E73\u53F0\u547D\u4EE4\u884C\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4E0E ExoMind \u77E5\u8BC6\u5E93\u4EA4\u4E92(ingest/query/search/review),\u66FF\u4EE3 Windows \u4E0D\u53EF\u7528\u7684 MCP \u5BA2\u6237\u7AEF\u3002",
3124
3124
  bin: {
3125
3125
  exomind: "dist/cli.js"
@@ -3165,6 +3165,10 @@ var package_default = {
3165
3165
  };
3166
3166
 
3167
3167
  // src/api.ts
3168
+ function opTimeout(defaultMs) {
3169
+ const n = Number(process.env.EXOMIND_TIMEOUT_MS);
3170
+ return Number.isFinite(n) && n > 0 ? n : defaultMs;
3171
+ }
3168
3172
  var ApiError = class extends Error {
3169
3173
  status;
3170
3174
  detail;
@@ -3303,6 +3307,9 @@ function setJsonMode(v) {
3303
3307
  function isJsonMode() {
3304
3308
  return JSON_MODE;
3305
3309
  }
3310
+ function hint(msg) {
3311
+ if (!isJsonMode()) process.stderr.write(msg + "\n");
3312
+ }
3306
3313
  function output(data, pretty) {
3307
3314
  if (JSON_MODE) {
3308
3315
  console.log(JSON.stringify(data, null, 2));
@@ -3617,8 +3624,157 @@ async function runHook(client) {
3617
3624
  }
3618
3625
  }
3619
3626
 
3627
+ // src/ingest_dir.ts
3628
+ var fs5 = __toESM(require("fs"));
3629
+ var path4 = __toESM(require("path"));
3630
+
3631
+ // src/manifest.ts
3632
+ var fs4 = __toESM(require("fs"));
3633
+ var path3 = __toESM(require("path"));
3634
+ var crypto = __toESM(require("crypto"));
3635
+ var MANIFEST_FILE = path3.join(CONFIG_DIR, "manifest.json");
3636
+ function sha256(s) {
3637
+ return crypto.createHash("sha256").update(s, "utf-8").digest("hex");
3638
+ }
3639
+ function loadManifest() {
3640
+ try {
3641
+ const d = JSON.parse(fs4.readFileSync(MANIFEST_FILE, "utf-8"));
3642
+ return d && typeof d === "object" ? d : {};
3643
+ } catch {
3644
+ return {};
3645
+ }
3646
+ }
3647
+ function saveManifest(m) {
3648
+ try {
3649
+ fs4.mkdirSync(CONFIG_DIR, { recursive: true });
3650
+ fs4.writeFileSync(MANIFEST_FILE, JSON.stringify(m, null, 2));
3651
+ } catch {
3652
+ }
3653
+ }
3654
+ function cleanupStale(m, dir, currentFiles) {
3655
+ const prefix = path3.resolve(dir) + path3.sep;
3656
+ const current = new Set(currentFiles);
3657
+ for (const key of Object.keys(m)) {
3658
+ if (key.startsWith(prefix) && !current.has(key)) {
3659
+ delete m[key];
3660
+ }
3661
+ }
3662
+ }
3663
+
3664
+ // src/ingest_dir.ts
3665
+ function globToRegex(pattern) {
3666
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
3667
+ return new RegExp(`^${escaped}$`);
3668
+ }
3669
+ function walkDir(dir, recursive, pattern) {
3670
+ const rx = globToRegex(pattern);
3671
+ const out = [];
3672
+ const walk = (d) => {
3673
+ let entries;
3674
+ try {
3675
+ entries = fs5.readdirSync(d, { withFileTypes: true });
3676
+ } catch {
3677
+ return;
3678
+ }
3679
+ for (const e of entries) {
3680
+ if (e.name.startsWith(".")) continue;
3681
+ const full = path4.join(d, e.name);
3682
+ if (e.isDirectory()) {
3683
+ if (recursive) walk(full);
3684
+ } else if (e.isFile() && rx.test(e.name)) {
3685
+ out.push(full);
3686
+ }
3687
+ }
3688
+ };
3689
+ walk(path4.resolve(dir));
3690
+ return out.sort();
3691
+ }
3692
+ function deriveTitle(file, content) {
3693
+ for (const line of content.split("\n")) {
3694
+ const m = line.match(/^#\s+(.+?)\s*$/);
3695
+ if (m) return m[1].trim();
3696
+ }
3697
+ return path4.basename(file).replace(/\.[^.]+$/, "");
3698
+ }
3699
+ function planIngestest(files, manifest, force) {
3700
+ const toIngest = [];
3701
+ const toSkip = [];
3702
+ for (const f of files) {
3703
+ let content;
3704
+ try {
3705
+ content = readFileText(f);
3706
+ } catch {
3707
+ continue;
3708
+ }
3709
+ const hash = sha256(content);
3710
+ const prev = manifest[f];
3711
+ if (!force && prev && prev.hash === hash) {
3712
+ toSkip.push(f);
3713
+ } else {
3714
+ toIngest.push({ path: f, content, title: deriveTitle(f, content), hash });
3715
+ }
3716
+ }
3717
+ return { toIngest, toSkip };
3718
+ }
3719
+ async function runDirIngestest(client, opts, dir) {
3720
+ const files = walkDir(dir, !!opts.recursive, opts.pattern || "*.md");
3721
+ if (!files.length) {
3722
+ console.log(dim(`\u76EE\u5F55 ${dir} \u4E0B\u65E0\u5339\u914D\u6587\u4EF6\u3002`));
3723
+ return;
3724
+ }
3725
+ const manifest = loadManifest();
3726
+ const plan = planIngestest(files, manifest, !!opts.force);
3727
+ const total = files.length;
3728
+ process.stderr.write(
3729
+ `\u76EE\u5F55 ${dir}: ${total} \u6587\u4EF6 \u2192 \u65B0\u589E/\u66F4\u65B0 ${plan.toIngest.length},\u8DF3\u8FC7 ${plan.toSkip.length}
3730
+ `
3731
+ );
3732
+ let added = 0;
3733
+ let updated = 0;
3734
+ let failed = 0;
3735
+ for (let i = 0; i < plan.toIngest.length; i++) {
3736
+ const f = plan.toIngest[i];
3737
+ const prev = manifest[f.path];
3738
+ process.stderr.write(
3739
+ `\u23F3 [${i + 1}/${plan.toIngest.length}] ${path4.basename(f.path)} ${prev ? "(\u66F4\u65B0)" : "(\u65B0\u589E)"}\u2026
3740
+ `
3741
+ );
3742
+ try {
3743
+ await client.post(
3744
+ "/ingest",
3745
+ { content: f.content, title: f.title, tags: opts.tag },
3746
+ { timeoutMs: opTimeout(3e5) }
3747
+ );
3748
+ manifest[f.path] = {
3749
+ hash: f.hash,
3750
+ ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
3751
+ title: f.title,
3752
+ size: f.content.length
3753
+ };
3754
+ if (prev) updated++;
3755
+ else added++;
3756
+ saveManifest(manifest);
3757
+ } catch (e) {
3758
+ failed++;
3759
+ process.stderr.write(` ${red("\u2717")} ${path4.basename(f.path)}: ${e.message}
3760
+ `);
3761
+ }
3762
+ }
3763
+ cleanupStale(manifest, dir, files);
3764
+ saveManifest(manifest);
3765
+ output(
3766
+ { added, updated, skipped: plan.toSkip.length, failed, total, dir },
3767
+ () => console.log(
3768
+ green("\u2713 \u76EE\u5F55\u6444\u5165\u5B8C\u6210") + dim(
3769
+ `: \u65B0\u589E ${added} / \u66F4\u65B0 ${updated} / \u8DF3\u8FC7 ${plan.toSkip.length} / \u5931\u8D25 ${failed} (\u5171 ${total})`
3770
+ )
3771
+ )
3772
+ );
3773
+ }
3774
+
3620
3775
  // src/commands/ingest.ts
3621
3776
  async function ingest(client, opts, args) {
3777
+ if (opts.dir) return runDirIngestest(client, opts, opts.dir);
3622
3778
  let content = "";
3623
3779
  if (opts.file) {
3624
3780
  content = readFileText(opts.file);
@@ -3639,9 +3795,10 @@ async function ingest(client, opts, args) {
3639
3795
  const body = { content };
3640
3796
  if (opts.title) body.title = opts.title;
3641
3797
  if (opts.tag && opts.tag.length) body.tags = opts.tag;
3642
- const result = await client.post("/ingest", body, { timeoutMs: 12e4 });
3798
+ hint("\u23F3 \u6444\u5165\u4E2D: \u670D\u52A1\u5668\u7528 LLM \u62BD\u53D6\u5B9E\u4F53/\u5173\u7CFB,\u957F\u5185\u5BB9\u53EF\u80FD 1-3 \u5206\u949F\u2026");
3799
+ const result = await client.post("/ingest", body, { timeoutMs: opTimeout(3e5) });
3643
3800
  output(result, () => {
3644
- console.log(ok("\u5DF2\u5BFC\u5165\u77E5\u8BC6\u5E93"));
3801
+ console.log(ok("\u5DF2\u5BFC\u5165\u670D\u52A1\u5668\u77E5\u8BC6\u5E93"));
3645
3802
  if (opts.title) console.log(dim(` \u6807\u9898: ${opts.title}`));
3646
3803
  console.log(` ${green("\u5B9E\u4F53")}: ${result.entities ?? 0} ${green("\u6982\u5FF5")}: ${result.concepts ?? 0}`);
3647
3804
  if (result.summary) console.log(dim(` \u6458\u8981: ${truncate(result.summary, 120)}`));
@@ -3661,7 +3818,8 @@ async function query(client, opts, args) {
3661
3818
  const body = { question };
3662
3819
  if (opts.tag?.length) body.tags = opts.tag;
3663
3820
  if (opts.model) body.model = opts.model;
3664
- const result = await client.post("/query", body, { timeoutMs: 12e4 });
3821
+ hint("\u23F3 \u67E5\u8BE2\u4E2D: LLM \u68C0\u7D22 + \u751F\u6210,\u53EF\u80FD 1-2 \u5206\u949F\u2026");
3822
+ const result = await client.post("/query", body, { timeoutMs: opTimeout(18e4) });
3665
3823
  output(result, () => {
3666
3824
  console.log(result.answer || dim("(\u65E0\u56DE\u7B54)"));
3667
3825
  if (result.pages?.length) {
@@ -3793,7 +3951,8 @@ async function mark(client, opts, args) {
3793
3951
  async function synthesize(client, opts, args) {
3794
3952
  const topic = args.join(" ").trim();
3795
3953
  if (!topic) throw new Error('\u8BF7\u63D0\u4F9B\u4E3B\u9898: exomind synthesize "Redis \u6301\u4E45\u5316"');
3796
- const result = await client.post("/synthesize", { topic, depth: opts.depth ?? 2 }, { timeoutMs: 18e4 });
3954
+ hint("\u23F3 \u7EFC\u5408\u4E2D: \u591A\u6E90\u805A\u5408 + \u6D1E\u5BDF,\u53EF\u80FD 2-4 \u5206\u949F\u2026");
3955
+ const result = await client.post("/synthesize", { topic, depth: opts.depth ?? 2 }, { timeoutMs: opTimeout(3e5) });
3797
3956
  output(result, () => {
3798
3957
  console.log(bold(result.topic || topic));
3799
3958
  if (result.content) console.log(`
@@ -3901,10 +4060,10 @@ async function login(_client, opts) {
3901
4060
  )
3902
4061
  );
3903
4062
  }
3904
- const hint = token.length > 12 ? `${token.slice(0, 8)}\u2026${token.slice(-4)}` : token;
4063
+ const hint2 = token.length > 12 ? `${token.slice(0, 8)}\u2026${token.slice(-4)}` : token;
3905
4064
  console.log(ok("\u767B\u5F55\u6210\u529F"));
3906
4065
  console.log(dim(` \u670D\u52A1\u5668: ${baseUrl}`));
3907
- console.log(dim(` \u51ED\u8BC1: ${hint} (${token.startsWith("gh_") ? "GitHub token" : "API Key"})`));
4066
+ console.log(dim(` \u51ED\u8BC1: ${hint2} (${token.startsWith("gh_") ? "GitHub token" : "API Key"})`));
3908
4067
  }
3909
4068
 
3910
4069
  // src/commands/whoami.ts
@@ -3920,14 +4079,14 @@ async function whoami(client) {
3920
4079
  } catch {
3921
4080
  me = {};
3922
4081
  }
3923
- const hint = cfg.api_key.length > 12 ? `${cfg.api_key.slice(0, 8)}\u2026${cfg.api_key.slice(-4)}` : cfg.api_key;
4082
+ const hint2 = cfg.api_key.length > 12 ? `${cfg.api_key.slice(0, 8)}\u2026${cfg.api_key.slice(-4)}` : cfg.api_key;
3924
4083
  const kind = cfg.api_key.startsWith("gh_") ? "GitHub token" : "API Key";
3925
4084
  output(
3926
- { base_url: cfg.base_url, credential: hint, kind, ...me },
4085
+ { base_url: cfg.base_url, credential: hint2, kind, ...me },
3927
4086
  () => {
3928
4087
  console.log(ok("\u5DF2\u767B\u5F55"));
3929
4088
  console.log(dim(` \u670D\u52A1\u5668: ${cfg.base_url}`));
3930
- console.log(dim(` \u51ED\u8BC1: ${hint} (${kind})`));
4089
+ console.log(dim(` \u51ED\u8BC1: ${hint2} (${kind})`));
3931
4090
  if (me.authenticated) {
3932
4091
  console.log(dim(` \u7528\u6237: ${me.name || me.login || "-"} (tenant: ${me.tenant_id || "-"})`));
3933
4092
  }
@@ -3936,40 +4095,40 @@ async function whoami(client) {
3936
4095
  }
3937
4096
 
3938
4097
  // src/commands/install.ts
3939
- var fs4 = __toESM(require("fs"));
3940
- var path3 = __toESM(require("path"));
4098
+ var fs6 = __toESM(require("fs"));
4099
+ var path5 = __toESM(require("path"));
3941
4100
  var os2 = __toESM(require("os"));
3942
- var PKG_ROOT = path3.resolve(__dirname, "..");
3943
- var SKILL_SRC = path3.join(PKG_ROOT, "skill", "SKILL.md");
4101
+ var PKG_ROOT = path5.resolve(__dirname, "..");
4102
+ var SKILL_SRC = path5.join(PKG_ROOT, "skill", "SKILL.md");
3944
4103
  function readJson2(file) {
3945
4104
  try {
3946
- return JSON.parse(fs4.readFileSync(file, "utf-8"));
4105
+ return JSON.parse(fs6.readFileSync(file, "utf-8"));
3947
4106
  } catch {
3948
4107
  return null;
3949
4108
  }
3950
4109
  }
3951
4110
  function backup(file) {
3952
- if (!fs4.existsSync(file)) return;
4111
+ if (!fs6.existsSync(file)) return;
3953
4112
  const bak = `${file}.bak-${Date.now()}`;
3954
4113
  try {
3955
- fs4.copyFileSync(file, bak);
3956
- console.log(dim(` (\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${path3.basename(bak)})`));
4114
+ fs6.copyFileSync(file, bak);
4115
+ console.log(dim(` (\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${path5.basename(bak)})`));
3957
4116
  } catch {
3958
4117
  }
3959
4118
  }
3960
4119
  async function install(_client, opts) {
3961
- const claudeDir = path3.join(os2.homedir(), ".claude");
3962
- const skillDestDir = path3.join(claudeDir, "skills", "exomind");
3963
- if (!fs4.existsSync(SKILL_SRC)) {
4120
+ const claudeDir = path5.join(os2.homedir(), ".claude");
4121
+ const skillDestDir = path5.join(claudeDir, "skills", "exomind");
4122
+ if (!fs6.existsSync(SKILL_SRC)) {
3964
4123
  throw new Error(`skill \u6E90\u4E0D\u5B58\u5728: ${SKILL_SRC}(npm \u5305\u53EF\u80FD\u635F\u574F,\u6216\u5F00\u53D1\u6A21\u5F0F\u4E0B\u672A\u6784\u5EFA)`);
3965
4124
  }
3966
- fs4.mkdirSync(skillDestDir, { recursive: true });
3967
- fs4.copyFileSync(SKILL_SRC, path3.join(skillDestDir, "SKILL.md"));
4125
+ fs6.mkdirSync(skillDestDir, { recursive: true });
4126
+ fs6.copyFileSync(SKILL_SRC, path5.join(skillDestDir, "SKILL.md"));
3968
4127
  console.log(ok("\u5DF2\u5B89\u88C5 Claude Code skill"));
3969
4128
  console.log(dim(` \u2192 ${skillDestDir}/SKILL.md`));
3970
4129
  if (opts.withHook) {
3971
- const settingsFile = path3.join(claudeDir, "settings.json");
3972
- fs4.mkdirSync(claudeDir, { recursive: true });
4130
+ const settingsFile = path5.join(claudeDir, "settings.json");
4131
+ fs6.mkdirSync(claudeDir, { recursive: true });
3973
4132
  backup(settingsFile);
3974
4133
  const settings = readJson2(settingsFile) ?? {};
3975
4134
  const hooks = settings.hooks ?? {};
@@ -3982,7 +4141,7 @@ async function install(_client, opts) {
3982
4141
  });
3983
4142
  hooks.UserPromptSubmit = kept;
3984
4143
  settings.hooks = hooks;
3985
- fs4.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
4144
+ fs6.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
3986
4145
  console.log(ok("\u5DF2\u914D\u7F6E UserPromptSubmit hook \u2192 exomind hook"));
3987
4146
  console.log(dim(" \u91CD\u542F Claude Code \u751F\u6548\u3002hook \u51FA\u9519\u4E0D\u4F1A\u963B\u585E\u4F60\u7684\u8F93\u5165(\u9519\u8BEF\u53EA\u8FDB stderr)\u3002"));
3988
4147
  }
@@ -4033,7 +4192,7 @@ var program2 = new Command();
4033
4192
  program2.name("exomind").description("ExoMind \u8DE8\u5E73\u53F0\u77E5\u8BC6\u5E93\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4EA4\u4E92(\u66FF\u4EE3 Windows MCP \u5BA2\u6237\u7AEF)\u3002").version(VERSION).option("--json", "\u8F93\u51FA\u539F\u59CB JSON(\u673A\u5668\u53EF\u8BFB)").option("--base-url <url>", "\u8986\u76D6\u670D\u52A1\u5668\u5730\u5740").option("--api-key <key>", "\u8986\u76D6 API Key / \u51ED\u8BC1");
4034
4193
  program2.command("login").description("\u914D\u7F6E\u670D\u52A1\u5668\u5730\u5740\u4E0E\u51ED\u8BC1,\u5199\u5165 ~/.exomind/config.json").option("--base-url <url>", "\u670D\u52A1\u5668\u5730\u5740").option("--api-key <key>", "API Key \u6216\u767B\u5F55 token(\u4E0D\u586B\u5219\u4EA4\u4E92\u8F93\u5165)").action(run(login));
4035
4194
  program2.command("whoami").description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u6001\u4E0E\u670D\u52A1\u5668").action(run(whoami));
4036
- program2.command("ingest [content...]").description("\u5BFC\u5165\u77E5\u8BC6: \u53C2\u6570\u6587\u672C / --file / stdin (echo ... | exomind ingest)").option("-t, --title <title>", "\u6807\u9898").option("--tag <tag>", "\u6807\u7B7E(\u53EF\u91CD\u590D)", collect, []).option("--file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6\u5185\u5BB9").action(run(ingest));
4195
+ program2.command("ingest [content...]").description("\u5BFC\u5165\u77E5\u8BC6: \u53C2\u6570\u6587\u672C / --file / stdin / --dir \u76EE\u5F55\u6279\u91CF(\u589E\u91CF)").option("-t, --title <title>", "\u6807\u9898(\u5355\u6587\u4EF6\u6A21\u5F0F)").option("--tag <tag>", "\u6807\u7B7E(\u53EF\u91CD\u590D)", collect, []).option("--file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6\u5185\u5BB9").option("--dir <path>", "\u76EE\u5F55\u6279\u91CF\u6444\u5165(\u589E\u91CF: \u5185\u5BB9\u54C8\u5E0C\u8DF3\u8FC7\u672A\u53D8\u6587\u4EF6)").option("-r, --recursive", "\u9012\u5F52\u5B50\u76EE\u5F55(\u914D\u5408 --dir)").option("--pattern <glob>", "\u6587\u4EF6\u540D\u5339\u914D,\u9ED8\u8BA4 *.md", "*.md").option("--force", "\u5FFD\u7565 manifest,\u5F3A\u5236\u5168\u91CF\u91CD\u6444(\u914D\u5408 --dir)").action(run(ingest));
4037
4196
  program2.command("query [question...]").description("LLM \u95EE\u7B54").option("--tag <tag>", "\u6807\u7B7E\u8FC7\u6EE4(\u53EF\u91CD\u590D)", collect, []).option("--model <name>", "\u6A21\u578B").action(run(query));
4038
4197
  program2.command("search [keyword...]").description("\u5168\u6587/\u6DF7\u5408/\u7CBE\u6392\u641C\u7D22").option("-l, --limit <n>", "\u8FD4\u56DE\u6570\u91CF", "10").option("--rerank", "LLM \u7CBE\u6392(\u66F4\u51C6\u4F46\u66F4\u6162)").option("--hybrid", "\u6DF7\u5408\u641C\u7D22(BM25+\u8BED\u4E49)").action(run(search));
4039
4198
  program2.command("entity [name...]").description("\u5B9E\u4F53\u8BE6\u60C5 + \u5173\u7CFB").action(run(entity));