@claudeink/mcp-server 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +728 -940
  2. package/dist/index.js +523 -745
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4320,11 +4320,11 @@ var require_core = __commonJS({
4320
4320
  Ajv2.ValidationError = validation_error_1.default;
4321
4321
  Ajv2.MissingRefError = ref_error_1.default;
4322
4322
  exports2.default = Ajv2;
4323
- function checkOptions(checkOpts, options2, msg, log = "error") {
4323
+ function checkOptions(checkOpts, options2, msg, log2 = "error") {
4324
4324
  for (const key in checkOpts) {
4325
4325
  const opt = key;
4326
4326
  if (opt in options2)
4327
- this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`);
4327
+ this.logger[log2](`${msg}: option ${key}. ${checkOpts[opt]}`);
4328
4328
  }
4329
4329
  }
4330
4330
  function getSchEnv(keyRef) {
@@ -23142,8 +23142,8 @@ var Server = class extends Protocol {
23142
23142
  this._loggingLevels = /* @__PURE__ */ new Map();
23143
23143
  this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
23144
23144
  this.isMessageIgnored = (level, sessionId) => {
23145
- const currentLevel = this._loggingLevels.get(sessionId);
23146
- return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
23145
+ const currentLevel2 = this._loggingLevels.get(sessionId);
23146
+ return currentLevel2 ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel2) : false;
23147
23147
  };
23148
23148
  this._capabilities = options2?.capabilities ?? {};
23149
23149
  this._instructions = options2?.instructions;
@@ -24476,12 +24476,9 @@ var StdioServerTransport = class {
24476
24476
  }
24477
24477
  };
24478
24478
 
24479
- // src/tools/source.ts
24480
- var import_gray_matter2 = __toESM(require_gray_matter(), 1);
24481
- import { readFile as readFile3 } from "fs/promises";
24479
+ // src/tools/workflow.ts
24480
+ import { mkdir as mkdir3, access, unlink } from "fs/promises";
24482
24481
  import { join as join3 } from "path";
24483
- import { readdirSync } from "fs";
24484
- import { execSync } from "child_process";
24485
24482
 
24486
24483
  // src/lib/state.ts
24487
24484
  import { readFile, writeFile, mkdir, chmod } from "fs/promises";
@@ -24503,7 +24500,8 @@ var DEFAULT_STATE = {
24503
24500
  credentials: null,
24504
24501
  config: {
24505
24502
  apiBaseUrl: "https://app.claudeink.com",
24506
- workflowDir: ""
24503
+ workflowDir: "",
24504
+ logLevel: "INFO"
24507
24505
  },
24508
24506
  tagQueue: {
24509
24507
  queue: [],
@@ -24578,10 +24576,160 @@ async function addToTagQueue(item) {
24578
24576
  await writeState(state);
24579
24577
  }
24580
24578
 
24579
+ // src/lib/logger.ts
24580
+ import { appendFile, mkdir as mkdir2 } from "fs/promises";
24581
+ import { join as join2 } from "path";
24582
+ var currentLevel = "INFO";
24583
+ var LEVELS = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 };
24584
+ function setLogLevel(level) {
24585
+ currentLevel = level;
24586
+ }
24587
+ async function log(level, scope, message) {
24588
+ if (LEVELS[level] > LEVELS[currentLevel]) return;
24589
+ const dir = join2(getClaudeinkDir(), "logs");
24590
+ await mkdir2(dir, { recursive: true });
24591
+ const date3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
24592
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${scope} | ${message}
24593
+ `;
24594
+ await appendFile(join2(dir, `mcp-${date3}.log`), line);
24595
+ console.error(`[ClaudeInk] ${line.trim()}`);
24596
+ }
24597
+
24598
+ // src/tools/sync.ts
24599
+ var syncSchema = external_exports.object({
24600
+ workDir: external_exports.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
24601
+ });
24602
+ async function sync(input) {
24603
+ if (input.workDir) setWorkDir(input.workDir);
24604
+ const creds = await getCredentials();
24605
+ if (!creds?.token) {
24606
+ return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
24607
+ }
24608
+ return doPull(creds.token);
24609
+ }
24610
+ async function doPull(token) {
24611
+ const config2 = await getConfig();
24612
+ try {
24613
+ const res = await fetch(`${config2.apiBaseUrl}/api/sync/pull`, {
24614
+ headers: { Authorization: `Bearer ${token}` }
24615
+ });
24616
+ if (!res.ok) {
24617
+ if (res.status === 404) {
24618
+ return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
24619
+ }
24620
+ await log("ERROR", "ink.sync", `pull failed: HTTP ${res.status}`);
24621
+ return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
24622
+ }
24623
+ await updateLastSyncAt();
24624
+ await log("INFO", "ink.sync", "pull completed");
24625
+ return {
24626
+ success: true,
24627
+ message: "\u2705 \u540C\u6B65\u5B8C\u6210"
24628
+ };
24629
+ } catch (err) {
24630
+ return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
24631
+ }
24632
+ }
24633
+ async function syncPull(input) {
24634
+ if (input.workDir) setWorkDir(input.workDir);
24635
+ const creds = await getCredentials();
24636
+ if (!creds?.token) {
24637
+ return { success: false, message: "\u672A\u6FC0\u6D3B" };
24638
+ }
24639
+ return doPull(creds.token);
24640
+ }
24641
+
24642
+ // src/tools/workflow.ts
24643
+ var DEFAULT_API_BASE_URL = "https://app.claudeink.com";
24644
+ var workflowInitSchema = external_exports.object({
24645
+ workDir: external_exports.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
24646
+ licenseKey: external_exports.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
24647
+ });
24648
+ async function workflowInit(input) {
24649
+ const cwd = input.workDir;
24650
+ const results = [];
24651
+ try {
24652
+ setWorkDir(cwd);
24653
+ const claudeinkDir = join3(cwd, ".claudeink");
24654
+ await mkdir3(join3(claudeinkDir, "knowledge"), { recursive: true });
24655
+ results.push("\u2705 \u77E5\u8BC6\u5E93\u76EE\u5F55\u5DF2\u521B\u5EFA");
24656
+ const state = await readState();
24657
+ state.config.workflowDir = cwd;
24658
+ const oldCredsPath = join3(claudeinkDir, "credentials.json");
24659
+ try {
24660
+ await access(oldCredsPath);
24661
+ await unlink(oldCredsPath);
24662
+ results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
24663
+ } catch {
24664
+ }
24665
+ let activated = false;
24666
+ if (input.licenseKey) {
24667
+ try {
24668
+ const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
24669
+ method: "POST",
24670
+ headers: { "Content-Type": "application/json" },
24671
+ body: JSON.stringify({ key: input.licenseKey })
24672
+ });
24673
+ const data = await res.json();
24674
+ if (data.userId) {
24675
+ state.credentials = {
24676
+ licenseKey: input.licenseKey,
24677
+ token: data.token,
24678
+ userId: data.userId,
24679
+ plan: data.plan,
24680
+ expiresAt: data.expiresAt
24681
+ };
24682
+ results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
24683
+ activated = true;
24684
+ } else {
24685
+ results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
24686
+ }
24687
+ } catch (err) {
24688
+ results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
24689
+ }
24690
+ }
24691
+ await writeState(state);
24692
+ results.push("\u2705 state.json");
24693
+ if (activated) {
24694
+ try {
24695
+ const pullResult = await syncPull({ workDir: cwd });
24696
+ if (pullResult.success) {
24697
+ results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
24698
+ } else {
24699
+ results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E");
24700
+ }
24701
+ } catch {
24702
+ results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
24703
+ }
24704
+ }
24705
+ return {
24706
+ success: true,
24707
+ message: [
24708
+ "\u{1F389} ClaudeInk \u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
24709
+ "",
24710
+ ...results,
24711
+ "",
24712
+ "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
24713
+ "1. \u5728\u5BF9\u8BDD\u4E2D\u8BF4\u300C\u5E2E\u6211\u5B58\u4E00\u4E0B\u300D\u4FDD\u5B58\u6709\u4EF7\u503C\u5185\u5BB9",
24714
+ "2. \u7528\u300C\u627E\u4E00\u4E0B\u5173\u4E8E XX \u7684\u300D\u68C0\u7D22\u77E5\u8BC6\u5E93"
24715
+ ].join("\n")
24716
+ };
24717
+ } catch (err) {
24718
+ return {
24719
+ success: false,
24720
+ message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
24721
+ };
24722
+ }
24723
+ }
24724
+
24725
+ // src/tools/ink.ts
24726
+ import { readFile as readFile3 } from "fs/promises";
24727
+ import { extname } from "path";
24728
+
24581
24729
  // src/lib/knowledge.ts
24582
24730
  var import_gray_matter = __toESM(require_gray_matter(), 1);
24583
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
24584
- import { join as join2 } from "path";
24731
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir4, unlink as unlink2 } from "fs/promises";
24732
+ import { join as join4 } from "path";
24585
24733
  var _lockPromise = Promise.resolve();
24586
24734
  function withIndexLock(fn) {
24587
24735
  const prev = _lockPromise;
@@ -24592,10 +24740,10 @@ function withIndexLock(fn) {
24592
24740
  return prev.then(fn).finally(() => resolve());
24593
24741
  }
24594
24742
  function getKnowledgeDir() {
24595
- return join2(getWorkDir(), ".claudeink", "knowledge");
24743
+ return join4(getWorkDir(), ".claudeink", "knowledge");
24596
24744
  }
24597
24745
  function getIndexPath() {
24598
- return join2(getWorkDir(), ".claudeink", "knowledge", "index.json");
24746
+ return join4(getWorkDir(), ".claudeink", "knowledge", "index.json");
24599
24747
  }
24600
24748
  async function generateIdInternal(index) {
24601
24749
  const now = /* @__PURE__ */ new Date();
@@ -24628,17 +24776,17 @@ async function readIndex() {
24628
24776
  async function saveIndex(index) {
24629
24777
  index.updated_at = (/* @__PURE__ */ new Date()).toISOString();
24630
24778
  const dir = getKnowledgeDir();
24631
- await mkdir2(dir, { recursive: true });
24779
+ await mkdir4(dir, { recursive: true });
24632
24780
  await writeFile2(getIndexPath(), JSON.stringify(index, null, 2), "utf-8");
24633
24781
  }
24634
24782
  async function writeKnowledgeFile(meta, content) {
24635
24783
  const now = /* @__PURE__ */ new Date();
24636
24784
  const monthDir = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
24637
- const dir = join2(getKnowledgeDir(), monthDir);
24638
- await mkdir2(dir, { recursive: true });
24785
+ const dir = join4(getKnowledgeDir(), monthDir);
24786
+ await mkdir4(dir, { recursive: true });
24639
24787
  const slug = toSlug(meta.title) || meta.id;
24640
24788
  const filename = `${now.toISOString().slice(0, 10)}-${slug}.md`;
24641
- const filePath = join2(dir, filename);
24789
+ const filePath = join4(dir, filename);
24642
24790
  const frontmatter = {
24643
24791
  id: meta.id,
24644
24792
  title: meta.title,
@@ -24651,13 +24799,13 @@ async function writeKnowledgeFile(meta, content) {
24651
24799
  if (meta.url) frontmatter.url = meta.url;
24652
24800
  const output = import_gray_matter.default.stringify(content, frontmatter);
24653
24801
  await writeFile2(filePath, output, "utf-8");
24654
- return join2(monthDir, filename);
24802
+ return join4(monthDir, filename);
24655
24803
  }
24656
24804
  async function readKnowledgeFile(id) {
24657
24805
  const index = await readIndex();
24658
24806
  const entry = index.entries[id];
24659
24807
  if (!entry) return null;
24660
- const filePath = join2(getKnowledgeDir(), entry.file_path);
24808
+ const filePath = join4(getKnowledgeDir(), entry.file_path);
24661
24809
  try {
24662
24810
  const raw = await readFile2(filePath, "utf-8");
24663
24811
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -24742,9 +24890,9 @@ async function deleteKnowledgeFile(id) {
24742
24890
  const index = await readIndex();
24743
24891
  const entry = index.entries[id];
24744
24892
  if (!entry) return false;
24745
- const filePath = join2(getKnowledgeDir(), entry.file_path);
24893
+ const filePath = join4(getKnowledgeDir(), entry.file_path);
24746
24894
  try {
24747
- await unlink(filePath);
24895
+ await unlink2(filePath);
24748
24896
  } catch {
24749
24897
  }
24750
24898
  delete index.entries[id];
@@ -24752,55 +24900,6 @@ async function deleteKnowledgeFile(id) {
24752
24900
  return true;
24753
24901
  });
24754
24902
  }
24755
- function searchKnowledge(index, filters) {
24756
- let entries = Object.values(index.entries);
24757
- if (filters.tags && filters.tags.length > 0) {
24758
- const filterTags = filters.tags.map((t) => t.toLowerCase());
24759
- entries = entries.filter(
24760
- (e) => e.tags.some((t) => filterTags.includes(t.toLowerCase()))
24761
- );
24762
- }
24763
- if (filters.category) {
24764
- entries = entries.filter((e) => e.category === filters.category);
24765
- }
24766
- if (filters.date_from) {
24767
- entries = entries.filter((e) => e.created_at >= filters.date_from);
24768
- }
24769
- if (filters.date_to) {
24770
- entries = entries.filter((e) => e.created_at <= filters.date_to);
24771
- }
24772
- if (filters.query) {
24773
- const q = filters.query.toLowerCase();
24774
- entries = entries.map((e) => {
24775
- let score = 0;
24776
- if (e.title.toLowerCase().includes(q)) score += 10;
24777
- if (e.preview.toLowerCase().includes(q)) score += 5;
24778
- if (e.tags.some((t) => t.toLowerCase().includes(q))) score += 3;
24779
- return { ...e, _score: score };
24780
- }).filter((e) => e._score > 0).sort((a, b) => b._score - a._score);
24781
- } else {
24782
- entries.sort((a, b) => b.created_at.localeCompare(a.created_at));
24783
- }
24784
- const total = entries.length;
24785
- const limit = filters.limit || 10;
24786
- const results = entries.slice(0, limit).map((e) => ({
24787
- id: e.id,
24788
- title: e.title,
24789
- tags: e.tags,
24790
- category: e.category,
24791
- created_at: e.created_at,
24792
- preview: e.preview
24793
- }));
24794
- return { results, total };
24795
- }
24796
- async function readMultipleKnowledgeFiles(ids) {
24797
- const results = [];
24798
- for (const id of ids) {
24799
- const file = await readKnowledgeFile(id);
24800
- if (file) results.push(file);
24801
- }
24802
- return results;
24803
- }
24804
24903
  function getAllTags(index) {
24805
24904
  const tagCounts = {};
24806
24905
  for (const entry of Object.values(index.entries)) {
@@ -24819,7 +24918,7 @@ async function renameTag(oldTag, newTag) {
24819
24918
  if (idx !== -1) {
24820
24919
  entry.tags[idx] = newTag;
24821
24920
  count++;
24822
- const filePath = join2(getKnowledgeDir(), entry.file_path);
24921
+ const filePath = join4(getKnowledgeDir(), entry.file_path);
24823
24922
  try {
24824
24923
  const raw = await readFile2(filePath, "utf-8");
24825
24924
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -24854,7 +24953,7 @@ async function mergeTags(sourceTags, targetTag) {
24854
24953
  }
24855
24954
  entry.tags = newTags;
24856
24955
  count++;
24857
- const filePath = join2(getKnowledgeDir(), entry.file_path);
24956
+ const filePath = join4(getKnowledgeDir(), entry.file_path);
24858
24957
  try {
24859
24958
  const raw = await readFile2(filePath, "utf-8");
24860
24959
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -24885,6 +24984,7 @@ async function doPush(partial2) {
24885
24984
  knowledge: partial2.knowledge || [],
24886
24985
  crawlerSources: partial2.crawlerSources || []
24887
24986
  };
24987
+ await log("DEBUG", "push", `payload: ${partial2.knowledge?.length || 0} knowledge, ${partial2.crawlerSources?.length || 0} sources`);
24888
24988
  try {
24889
24989
  await fetch(`${config2.apiBaseUrl}/api/sync/batch`, {
24890
24990
  method: "POST",
@@ -24895,57 +24995,349 @@ async function doPush(partial2) {
24895
24995
  body: JSON.stringify(payload)
24896
24996
  });
24897
24997
  } catch (err) {
24898
- console.error("[ClaudeInk] auto-push failed:", err instanceof Error ? err.message : err);
24998
+ await log("WARN", "push", `failed: ${err instanceof Error ? err.message : err}`);
24899
24999
  }
24900
25000
  }
24901
25001
 
24902
- // src/tools/source.ts
24903
- var sourceCrawlSchema = external_exports.object({
24904
- sourceId: external_exports.string().optional().describe("\u6307\u5B9A\u722C\u866B\u6E90\u540D\u79F0\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF")
25002
+ // src/tools/ink.ts
25003
+ var inkSaveSchema = external_exports.object({
25004
+ id: external_exports.string().optional().describe("\u77E5\u8BC6\u7247\u6BB5 ID\uFF08\u63D0\u4F9B\u5219\u66F4\u65B0\uFF0C\u4E0D\u63D0\u4F9B\u5219\u65B0\u5EFA\uFF09"),
25005
+ content: external_exports.string().describe("\u8981\u4FDD\u5B58\u7684\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09"),
25006
+ title: external_exports.string().describe("\u6807\u9898\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF09"),
25007
+ tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
25008
+ category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
25009
+ source_type: external_exports.enum(["conversation", "url", "manual"]).optional().default("conversation").describe("\u6765\u6E90\u7C7B\u578B"),
25010
+ url: external_exports.string().optional().describe("\u6765\u6E90 URL")
24905
25011
  });
24906
- async function sourceCrawl(input) {
24907
- const config2 = await getConfig();
24908
- const crawlerDir = join3(config2.workflowDir || process.cwd(), "tools", "crawler");
24909
- const configPath = join3(crawlerDir, "config.json");
24910
- let crawlerConfig;
25012
+ async function inkSave(input) {
24911
25013
  try {
24912
- crawlerConfig = JSON.parse(await readFile3(configPath, "utf-8"));
24913
- } catch {
24914
- crawlerConfig = { sources: [] };
24915
- }
24916
- if (crawlerConfig.sources.length === 0) {
24917
- return { success: false, message: "\u6682\u65E0\u914D\u7F6E\u722C\u866B\u6E90\u3002\u8BF7\u5148\u7528 source.subscribe \u6DFB\u52A0\u3002" };
24918
- }
24919
- const targets = input.sourceId ? crawlerConfig.sources.filter((s) => s.id === input.sourceId) : crawlerConfig.sources.filter((s) => s.enabled !== false);
24920
- if (targets.length === 0) {
25014
+ if (input.id) {
25015
+ const result = await updateKnowledgeFile(input.id, {
25016
+ title: input.title,
25017
+ content: input.content,
25018
+ tags: input.tags,
25019
+ category: input.category
25020
+ });
25021
+ if (!result) {
25022
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
25023
+ }
25024
+ fireAndForgetPush({
25025
+ knowledge: [{
25026
+ id: result.meta.id,
25027
+ title: result.meta.title,
25028
+ tags: result.meta.tags,
25029
+ category: result.meta.category,
25030
+ source_type: result.meta.source_type,
25031
+ url: result.meta.url,
25032
+ created_at: result.meta.created_at,
25033
+ updated_at: result.meta.updated_at
25034
+ }]
25035
+ });
25036
+ await log("INFO", "ink.save", `updated id=${input.id} title="${input.title}"`);
25037
+ return {
25038
+ success: true,
25039
+ message: `\u5DF2\u66F4\u65B0\u300C${result.meta.title}\u300D`,
25040
+ data: { id: result.meta.id }
25041
+ };
25042
+ }
25043
+ const { id } = await saveKnowledge({
25044
+ content: input.content,
25045
+ title: input.title,
25046
+ tags: input.tags,
25047
+ category: input.category,
25048
+ source_type: input.source_type,
25049
+ url: input.url
25050
+ });
25051
+ fireAndForgetPush({
25052
+ knowledge: [{
25053
+ id,
25054
+ title: input.title,
25055
+ tags: input.tags,
25056
+ category: input.category,
25057
+ source_type: input.source_type,
25058
+ url: input.url,
25059
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
25060
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
25061
+ }]
25062
+ });
25063
+ await log("INFO", "ink.save", `created id=${id} title="${input.title}" tags=${JSON.stringify(input.tags)}`);
24921
25064
  return {
24922
- success: false,
24923
- message: input.sourceId ? `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}` : "\u6CA1\u6709\u5DF2\u542F\u7528\u7684\u722C\u866B\u6E90"
25065
+ success: true,
25066
+ message: `\u5DF2\u4FDD\u5B58\u300C${input.title}\u300D`,
25067
+ data: { id }
24924
25068
  };
25069
+ } catch (err) {
25070
+ await log("ERROR", "ink.save", `failed: ${err.message}`);
25071
+ return { success: false, message: `\u4FDD\u5B58\u5931\u8D25\uFF1A${err.message}` };
24925
25072
  }
24926
- const crawlScript = join3(crawlerDir, "crawl.mjs");
24927
- const args = input.sourceId ? `--source ${input.sourceId}` : "";
25073
+ }
25074
+ var inkDeleteSchema = external_exports.object({
25075
+ id: external_exports.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
25076
+ confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
25077
+ });
25078
+ async function inkDelete(input) {
24928
25079
  try {
24929
- execSync(`node "${crawlScript}" ${args}`, {
24930
- cwd: crawlerDir,
24931
- encoding: "utf-8",
24932
- timeout: 5 * 60 * 1e3,
24933
- stdio: ["pipe", "pipe", "pipe"]
25080
+ if (!input.confirm) {
25081
+ const index = await readIndex();
25082
+ const entry = index.entries[input.id];
25083
+ if (!entry) {
25084
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
25085
+ }
25086
+ return {
25087
+ success: false,
25088
+ message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
25089
+ data: { id: entry.id, title: entry.title, category: entry.category, tags: entry.tags }
25090
+ };
25091
+ }
25092
+ const deleted = await deleteKnowledgeFile(input.id);
25093
+ if (!deleted) {
25094
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
25095
+ }
25096
+ fireAndForgetPush({
25097
+ knowledge: [{
25098
+ id: input.id,
25099
+ title: "",
25100
+ tags: [],
25101
+ category: "reference",
25102
+ source_type: "conversation",
25103
+ created_at: "",
25104
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
25105
+ }]
24934
25106
  });
24935
- let saved = 0;
24936
- let queued = 0;
24937
- for (const target of targets) {
24938
- const sourceDir = join3(config2.workflowDir, "sources", "articles", target.id);
24939
- let files;
24940
- try {
24941
- files = readdirSync(sourceDir).filter((f) => f.endsWith(".md"));
24942
- } catch {
24943
- continue;
25107
+ await log("INFO", "ink.delete", `deleted id=${input.id}`);
25108
+ return { success: true, message: `\u5DF2\u5220\u9664\u77E5\u8BC6\u7247\u6BB5 ${input.id}`, data: { id: input.id } };
25109
+ } catch (err) {
25110
+ await log("ERROR", "ink.delete", `failed: ${err.message}`);
25111
+ return { success: false, message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}` };
25112
+ }
25113
+ }
25114
+ var inkWriteSchema = external_exports.object({
25115
+ title: external_exports.string().describe("\u6587\u7AE0\u6807\u9898"),
25116
+ content: external_exports.string().describe("Markdown \u5168\u6587"),
25117
+ cover: external_exports.string().optional().describe("\u5C01\u9762\u56FE\uFF08\u672C\u5730\u8DEF\u5F84\u6216 URL\uFF0C\u81EA\u52A8\u4E0A\u4F20\uFF09"),
25118
+ tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6587\u7AE0\u6807\u7B7E"),
25119
+ category: external_exports.string().optional().describe("\u6587\u7AE0\u5206\u7C7B"),
25120
+ excerpt: external_exports.string().optional().describe("\u6458\u8981\uFF08\u4E0D\u586B\u81EA\u52A8\u622A\u53D6\u524D 200 \u5B57\uFF09"),
25121
+ source_ids: external_exports.array(external_exports.string()).optional().describe("\u5173\u8054\u7684\u7D20\u6750 ink ID\uFF08\u6EAF\u6E90\uFF09")
25122
+ });
25123
+ async function inkWrite(input) {
25124
+ const creds = await getCredentials();
25125
+ if (!creds?.token) return { success: false, message: "\u672A\u8BA4\u8BC1\uFF0C\u8BF7\u5148\u8FD0\u884C ink.init" };
25126
+ const config2 = await getConfig();
25127
+ try {
25128
+ let coverUrl = input.cover || null;
25129
+ if (coverUrl) {
25130
+ if (coverUrl.startsWith("http://") || coverUrl.startsWith("https://")) {
25131
+ const uploadResult = await uploadImageFromUrl(coverUrl, "cover", creds.token, config2.apiBaseUrl);
25132
+ if (uploadResult.success) coverUrl = uploadResult.url;
25133
+ } else {
25134
+ const uploadResult = await uploadImage(coverUrl, "cover", creds.token, config2.apiBaseUrl);
25135
+ if (uploadResult.success) {
25136
+ coverUrl = uploadResult.url;
25137
+ } else {
25138
+ await log("WARN", "ink.write", `cover upload failed: ${uploadResult.error}`);
25139
+ coverUrl = null;
25140
+ }
24944
25141
  }
24945
- for (const f of files) {
24946
- const filePath = join3(sourceDir, f);
24947
- try {
24948
- const raw = await readFile3(filePath, "utf-8");
25142
+ }
25143
+ const slug = `${Date.now().toString(36)}-${input.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-|-$/g, "").slice(0, 60)}`;
25144
+ const excerpt = input.excerpt || input.content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, 200);
25145
+ const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
25146
+ method: "POST",
25147
+ headers: {
25148
+ "Content-Type": "application/json",
25149
+ Authorization: `Bearer ${creds.token}`
25150
+ },
25151
+ body: JSON.stringify({
25152
+ title: input.title,
25153
+ slug,
25154
+ content: input.content,
25155
+ cover_url: coverUrl,
25156
+ tags: input.tags,
25157
+ category: input.category,
25158
+ excerpt,
25159
+ source_ids: input.source_ids
25160
+ })
25161
+ });
25162
+ if (!res.ok) {
25163
+ const err = await res.text();
25164
+ await log("ERROR", "ink.write", `save failed: ${err}`);
25165
+ return { success: false, message: `\u4FDD\u5B58\u5931\u8D25: ${err}` };
25166
+ }
25167
+ const data = await res.json();
25168
+ await log("INFO", "ink.write", `saved article="${input.title}" id=${data.id} slug=${data.slug}`);
25169
+ return {
25170
+ success: true,
25171
+ message: `\u6587\u7AE0\u300C${input.title}\u300D\u5DF2\u4FDD\u5B58\u4E3A\u8349\u7A3F\uFF0C\u8BF7\u5230 Dashboard \u53D1\u5E03`,
25172
+ data: { id: data.id, slug: data.slug, status: data.status }
25173
+ };
25174
+ } catch (err) {
25175
+ await log("ERROR", "ink.write", `failed: ${err.message}`);
25176
+ return { success: false, message: `\u5199\u6587\u7AE0\u5931\u8D25: ${err.message}` };
25177
+ }
25178
+ }
25179
+ var inkTagsSchema = external_exports.object({
25180
+ action: external_exports.enum(["list", "rename", "merge"]).describe("\u64CD\u4F5C\uFF1Alist\uFF08\u5217\u51FA\u6240\u6709\u6807\u7B7E\uFF09/ rename\uFF08\u91CD\u547D\u540D\uFF09/ merge\uFF08\u5408\u5E76\uFF09"),
25181
+ tag: external_exports.string().optional().describe("\u76EE\u6807\u6807\u7B7E\uFF08rename/merge \u65F6\u4F7F\u7528\uFF09"),
25182
+ new_tag: external_exports.string().optional().describe("\u65B0\u6807\u7B7E\u540D\uFF08rename \u65F6\u4F7F\u7528\uFF09"),
25183
+ source_tags: external_exports.array(external_exports.string()).optional().describe("\u8981\u5408\u5E76\u7684\u6E90\u6807\u7B7E\u5217\u8868\uFF08merge \u65F6\u4F7F\u7528\uFF09")
25184
+ });
25185
+ async function inkTags(input) {
25186
+ try {
25187
+ const index = await readIndex();
25188
+ switch (input.action) {
25189
+ case "list": {
25190
+ const tags = getAllTags(index);
25191
+ return {
25192
+ success: true,
25193
+ message: tags.length > 0 ? `\u5171 ${tags.length} \u4E2A\u6807\u7B7E` : "\u6682\u65E0\u6807\u7B7E",
25194
+ data: { tags }
25195
+ };
25196
+ }
25197
+ case "rename": {
25198
+ if (!input.tag || !input.new_tag) {
25199
+ return { success: false, message: "rename \u9700\u8981 tag\uFF08\u539F\u6807\u7B7E\uFF09\u548C new_tag\uFF08\u65B0\u6807\u7B7E\u540D\uFF09" };
25200
+ }
25201
+ const count = await renameTag(input.tag, input.new_tag);
25202
+ if (count === 0) {
25203
+ return { success: false, message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u77E5\u8BC6\u7247\u6BB5` };
25204
+ }
25205
+ await log("INFO", "ink.tags", `renamed "${input.tag}" \u2192 "${input.new_tag}", affected=${count}`);
25206
+ return {
25207
+ success: true,
25208
+ message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u300C${input.tag}\u300D\u91CD\u547D\u540D\u4E3A\u300C${input.new_tag}\u300D`,
25209
+ data: { old_tag: input.tag, new_tag: input.new_tag, affected: count }
25210
+ };
25211
+ }
25212
+ case "merge": {
25213
+ if (!input.source_tags || input.source_tags.length === 0 || !input.new_tag) {
25214
+ return { success: false, message: "merge \u9700\u8981 source_tags\uFF08\u6E90\u6807\u7B7E\u5217\u8868\uFF09\u548C new_tag\uFF08\u76EE\u6807\u6807\u7B7E\uFF09" };
25215
+ }
25216
+ const count = await mergeTags(input.source_tags, input.new_tag);
25217
+ if (count === 0) {
25218
+ return { success: false, message: "\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u77E5\u8BC6\u7247\u6BB5" };
25219
+ }
25220
+ await log("INFO", "ink.tags", `merged [${input.source_tags.join(",")}] \u2192 "${input.new_tag}", affected=${count}`);
25221
+ return {
25222
+ success: true,
25223
+ message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
25224
+ data: { source_tags: input.source_tags, target_tag: input.new_tag, affected: count }
25225
+ };
25226
+ }
25227
+ }
25228
+ } catch (err) {
25229
+ await log("ERROR", "ink.tags", `failed: ${err.message}`);
25230
+ return { success: false, message: `\u6807\u7B7E\u64CD\u4F5C\u5931\u8D25\uFF1A${err.message}` };
25231
+ }
25232
+ }
25233
+ async function uploadImage(localPath, type, token, apiBase) {
25234
+ try {
25235
+ const fileBuffer = await readFile3(localPath);
25236
+ const ext = extname(localPath) || ".jpg";
25237
+ const extMap = {
25238
+ ".jpg": "image/jpeg",
25239
+ ".jpeg": "image/jpeg",
25240
+ ".png": "image/png",
25241
+ ".webp": "image/webp",
25242
+ ".gif": "image/gif"
25243
+ };
25244
+ const contentType = extMap[ext.toLowerCase()] || "application/octet-stream";
25245
+ const filename = `${type}-${Date.now()}${ext}`;
25246
+ const res = await fetch(`${apiBase}/api/blog/upload-image`, {
25247
+ method: "POST",
25248
+ headers: {
25249
+ Authorization: `Bearer ${token}`,
25250
+ "Content-Type": contentType,
25251
+ "X-Filename": filename,
25252
+ "X-Image-Type": type
25253
+ },
25254
+ body: fileBuffer
25255
+ });
25256
+ if (!res.ok) return { success: false, error: await res.text() };
25257
+ const { url } = await res.json();
25258
+ return { success: true, url };
25259
+ } catch (err) {
25260
+ return { success: false, error: err.message };
25261
+ }
25262
+ }
25263
+ async function uploadImageFromUrl(imageUrl, type, token, apiBase) {
25264
+ try {
25265
+ const res = await fetch(imageUrl);
25266
+ if (!res.ok) return { success: false, error: `download failed: HTTP ${res.status}` };
25267
+ const fileBuffer = Buffer.from(await res.arrayBuffer());
25268
+ const ext = extname(new URL(imageUrl).pathname) || ".jpg";
25269
+ const contentType = res.headers.get("content-type") || "image/jpeg";
25270
+ const filename = `${type}-${Date.now()}${ext}`;
25271
+ const uploadRes = await fetch(`${apiBase}/api/blog/upload-image`, {
25272
+ method: "POST",
25273
+ headers: {
25274
+ Authorization: `Bearer ${token}`,
25275
+ "Content-Type": contentType,
25276
+ "X-Filename": filename,
25277
+ "X-Image-Type": type
25278
+ },
25279
+ body: fileBuffer
25280
+ });
25281
+ if (!uploadRes.ok) return { success: false, error: await uploadRes.text() };
25282
+ const { url } = await uploadRes.json();
25283
+ return { success: true, url };
25284
+ } catch (err) {
25285
+ return { success: false, error: err.message };
25286
+ }
25287
+ }
25288
+
25289
+ // src/tools/source.ts
25290
+ var import_gray_matter2 = __toESM(require_gray_matter(), 1);
25291
+ import { readFile as readFile4 } from "fs/promises";
25292
+ import { join as join5 } from "path";
25293
+ import { readdirSync } from "fs";
25294
+ import { execSync } from "child_process";
25295
+ var sourceCrawlSchema = external_exports.object({
25296
+ sourceId: external_exports.string().optional().describe("\u6307\u5B9A\u722C\u866B\u6E90\u540D\u79F0\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF")
25297
+ });
25298
+ async function sourceCrawl(input) {
25299
+ const config2 = await getConfig();
25300
+ const crawlerDir = join5(config2.workflowDir || process.cwd(), "tools", "crawler");
25301
+ const configPath = join5(crawlerDir, "config.json");
25302
+ let crawlerConfig;
25303
+ try {
25304
+ crawlerConfig = JSON.parse(await readFile4(configPath, "utf-8"));
25305
+ } catch {
25306
+ crawlerConfig = { sources: [] };
25307
+ }
25308
+ if (crawlerConfig.sources.length === 0) {
25309
+ return { success: false, message: "\u6682\u65E0\u914D\u7F6E\u722C\u866B\u6E90\u3002\u8BF7\u5148\u7528 source.subscribe \u6DFB\u52A0\u3002" };
25310
+ }
25311
+ const targets = input.sourceId ? crawlerConfig.sources.filter((s) => s.id === input.sourceId) : crawlerConfig.sources.filter((s) => s.enabled !== false);
25312
+ if (targets.length === 0) {
25313
+ return {
25314
+ success: false,
25315
+ message: input.sourceId ? `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}` : "\u6CA1\u6709\u5DF2\u542F\u7528\u7684\u722C\u866B\u6E90"
25316
+ };
25317
+ }
25318
+ const crawlScript = join5(crawlerDir, "crawl.mjs");
25319
+ const args = input.sourceId ? `--source ${input.sourceId}` : "";
25320
+ try {
25321
+ execSync(`node "${crawlScript}" ${args}`, {
25322
+ cwd: crawlerDir,
25323
+ encoding: "utf-8",
25324
+ timeout: 5 * 60 * 1e3,
25325
+ stdio: ["pipe", "pipe", "pipe"]
25326
+ });
25327
+ let saved = 0;
25328
+ let queued = 0;
25329
+ for (const target of targets) {
25330
+ const sourceDir = join5(config2.workflowDir, "sources", "articles", target.id);
25331
+ let files;
25332
+ try {
25333
+ files = readdirSync(sourceDir).filter((f) => f.endsWith(".md"));
25334
+ } catch {
25335
+ continue;
25336
+ }
25337
+ for (const f of files) {
25338
+ const filePath = join5(sourceDir, f);
25339
+ try {
25340
+ const raw = await readFile4(filePath, "utf-8");
24949
25341
  const { data, content } = (0, import_gray_matter2.default)(raw);
24950
25342
  const title = data.title || f.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/\.md$/, "").replace(/-/g, " ");
24951
25343
  const url = data.url || "";
@@ -25004,8 +25396,8 @@ async function sourceCrawl(input) {
25004
25396
  }
25005
25397
 
25006
25398
  // src/tools/subscribe.ts
25007
- import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
25008
- import { join as join4 } from "path";
25399
+ import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir5 } from "fs/promises";
25400
+ import { join as join6 } from "path";
25009
25401
  var sourceSubscribeSchema = external_exports.object({
25010
25402
  action: external_exports.enum(["add", "remove", "list"]).describe("\u64CD\u4F5C\u7C7B\u578B"),
25011
25403
  id: external_exports.string().optional().describe("\u8BA2\u9605\u6E90 ID"),
@@ -25027,16 +25419,16 @@ var sourceSubscribeSchema = external_exports.object({
25027
25419
  });
25028
25420
  async function readCrawlerConfig(configPath) {
25029
25421
  try {
25030
- return JSON.parse(await readFile4(configPath, "utf-8"));
25422
+ return JSON.parse(await readFile5(configPath, "utf-8"));
25031
25423
  } catch {
25032
25424
  return { sources: [] };
25033
25425
  }
25034
25426
  }
25035
25427
  async function sourceSubscribe(input) {
25036
25428
  const config2 = await getConfig();
25037
- const crawlerDir = join4(config2.workflowDir || process.cwd(), "tools", "crawler");
25038
- await mkdir3(crawlerDir, { recursive: true });
25039
- const configPath = join4(crawlerDir, "config.json");
25429
+ const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
25430
+ await mkdir5(crawlerDir, { recursive: true });
25431
+ const configPath = join6(crawlerDir, "config.json");
25040
25432
  const crawlerConfig = await readCrawlerConfig(configPath);
25041
25433
  if (input.action === "list") {
25042
25434
  return {
@@ -25102,654 +25494,40 @@ async function sourceSubscribe(input) {
25102
25494
  return { success: false, message: "\u672A\u77E5\u64CD\u4F5C" };
25103
25495
  }
25104
25496
 
25105
- // src/tools/sync.ts
25106
- var syncSchema = external_exports.object({
25107
- workDir: external_exports.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
25108
- });
25109
- async function sync(input) {
25110
- if (input.workDir) setWorkDir(input.workDir);
25111
- const creds = await getCredentials();
25112
- if (!creds?.token) {
25113
- return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
25114
- }
25115
- return doPull(creds.token);
25116
- }
25117
- async function doPull(token) {
25118
- const config2 = await getConfig();
25119
- try {
25120
- const res = await fetch(`${config2.apiBaseUrl}/api/sync/pull`, {
25121
- headers: { Authorization: `Bearer ${token}` }
25122
- });
25123
- if (!res.ok) {
25124
- if (res.status === 404) {
25125
- return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
25126
- }
25127
- return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
25128
- }
25129
- await updateLastSyncAt();
25130
- return {
25131
- success: true,
25132
- message: "\u2705 \u540C\u6B65\u5B8C\u6210"
25133
- };
25134
- } catch (err) {
25135
- return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
25136
- }
25137
- }
25138
- async function syncPull(input) {
25139
- if (input.workDir) setWorkDir(input.workDir);
25140
- const creds = await getCredentials();
25141
- if (!creds?.token) {
25142
- return { success: false, message: "\u672A\u6FC0\u6D3B" };
25143
- }
25144
- return doPull(creds.token);
25145
- }
25146
-
25147
- // src/tools/workflow.ts
25148
- import { mkdir as mkdir4, access, unlink as unlink2 } from "fs/promises";
25149
- import { join as join5 } from "path";
25150
- var DEFAULT_API_BASE_URL = "https://app.claudeink.com";
25151
- var workflowInitSchema = external_exports.object({
25152
- workDir: external_exports.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
25153
- licenseKey: external_exports.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
25154
- });
25155
- async function workflowInit(input) {
25156
- const cwd = input.workDir;
25157
- const results = [];
25158
- try {
25159
- setWorkDir(cwd);
25160
- const claudeinkDir = join5(cwd, ".claudeink");
25161
- await mkdir4(join5(claudeinkDir, "knowledge"), { recursive: true });
25162
- results.push("\u2705 \u77E5\u8BC6\u5E93\u76EE\u5F55\u5DF2\u521B\u5EFA");
25163
- const state = await readState();
25164
- state.config.workflowDir = cwd;
25165
- const oldCredsPath = join5(claudeinkDir, "credentials.json");
25166
- try {
25167
- await access(oldCredsPath);
25168
- await unlink2(oldCredsPath);
25169
- results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
25170
- } catch {
25171
- }
25172
- let activated = false;
25173
- if (input.licenseKey) {
25174
- try {
25175
- const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
25176
- method: "POST",
25177
- headers: { "Content-Type": "application/json" },
25178
- body: JSON.stringify({ key: input.licenseKey })
25179
- });
25180
- const data = await res.json();
25181
- if (data.userId) {
25182
- state.credentials = {
25183
- licenseKey: input.licenseKey,
25184
- token: data.token,
25185
- userId: data.userId,
25186
- plan: data.plan,
25187
- expiresAt: data.expiresAt
25188
- };
25189
- results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
25190
- activated = true;
25191
- } else {
25192
- results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
25193
- }
25194
- } catch (err) {
25195
- results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
25196
- }
25197
- }
25198
- await writeState(state);
25199
- results.push("\u2705 state.json");
25200
- if (activated) {
25201
- try {
25202
- const pullResult = await syncPull({ workDir: cwd });
25203
- if (pullResult.success) {
25204
- results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
25205
- } else {
25206
- results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E");
25207
- }
25208
- } catch {
25209
- results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
25210
- }
25211
- }
25212
- return {
25213
- success: true,
25214
- message: [
25215
- "\u{1F389} ClaudeInk \u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
25216
- "",
25217
- ...results,
25218
- "",
25219
- "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
25220
- "1. \u5728\u5BF9\u8BDD\u4E2D\u8BF4\u300C\u5E2E\u6211\u5B58\u4E00\u4E0B\u300D\u4FDD\u5B58\u6709\u4EF7\u503C\u5185\u5BB9",
25221
- "2. \u7528\u300C\u627E\u4E00\u4E0B\u5173\u4E8E XX \u7684\u300D\u68C0\u7D22\u77E5\u8BC6\u5E93"
25222
- ].join("\n")
25223
- };
25224
- } catch (err) {
25225
- return {
25226
- success: false,
25227
- message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
25228
- };
25229
- }
25230
- }
25231
-
25232
- // src/tools/ink.ts
25233
- var inkSaveSchema = external_exports.object({
25234
- content: external_exports.string().describe("\u8981\u4FDD\u5B58\u7684\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09"),
25235
- title: external_exports.string().describe("\u6807\u9898\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF09"),
25236
- tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF0C\u7528\u6237\u53EF\u6307\u5B9A\uFF09"),
25237
- category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B\uFF1Ainsight / decision / analysis / idea / reference / action"),
25238
- source_type: external_exports.enum(["conversation", "url", "manual"]).optional().default("conversation").describe("\u6765\u6E90\u7C7B\u578B\uFF1Aconversation / url / manual"),
25239
- url: external_exports.string().optional().describe("\u6765\u6E90 URL\uFF08\u5982\u679C\u662F\u5916\u90E8\u7D20\u6750\uFF09")
25240
- });
25241
- async function inkSave(input) {
25242
- try {
25243
- const { id } = await saveKnowledge({
25244
- content: input.content,
25245
- title: input.title,
25246
- tags: input.tags,
25247
- category: input.category,
25248
- source_type: input.source_type,
25249
- url: input.url
25250
- });
25251
- const categoryNames = {
25252
- insight: "\u6D1E\u5BDF",
25253
- decision: "\u51B3\u7B56",
25254
- analysis: "\u5206\u6790",
25255
- idea: "\u60F3\u6CD5",
25256
- reference: "\u53C2\u8003",
25257
- action: "\u884C\u52A8"
25258
- };
25259
- const catName = categoryNames[input.category] || input.category;
25260
- const tagStr = input.tags.length > 0 ? input.tags.join("\u3001") : "\u65E0\u6807\u7B7E";
25261
- fireAndForgetPush({
25262
- knowledge: [{
25263
- id,
25264
- title: input.title,
25265
- tags: input.tags,
25266
- category: input.category,
25267
- source_type: input.source_type,
25268
- url: input.url,
25269
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
25270
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
25271
- }]
25272
- });
25273
- return {
25274
- success: true,
25275
- message: `\u5DF2\u4FDD\u5B58\u5230\u300C${catName}\u300D\uFF0C\u6807\u7B7E\uFF1A${tagStr}`,
25276
- data: { id }
25277
- };
25278
- } catch (err) {
25279
- return {
25280
- success: false,
25281
- message: `\u4FDD\u5B58\u5931\u8D25\uFF1A${err.message}`
25282
- };
25283
- }
25284
- }
25285
- var inkSearchSchema = external_exports.object({
25286
- query: external_exports.string().optional().describe("\u641C\u7D22\u5173\u952E\u8BCD"),
25287
- tags: external_exports.array(external_exports.string()).optional().describe("\u6309\u6807\u7B7E\u8FC7\u6EE4"),
25288
- category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().describe("\u6309\u5206\u7C7B\u8FC7\u6EE4"),
25289
- date_from: external_exports.string().optional().describe("\u8D77\u59CB\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF09"),
25290
- date_to: external_exports.string().optional().describe("\u622A\u6B62\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF09"),
25291
- limit: external_exports.number().optional().default(10).describe("\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 10")
25292
- });
25293
- async function inkSearch(input) {
25294
- try {
25295
- const index = await readIndex();
25296
- const { results, total } = searchKnowledge(index, {
25297
- query: input.query,
25298
- tags: input.tags,
25299
- category: input.category,
25300
- date_from: input.date_from,
25301
- date_to: input.date_to,
25302
- limit: input.limit
25303
- });
25304
- if (total === 0) {
25305
- return {
25306
- success: true,
25307
- message: "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u77E5\u8BC6\u7247\u6BB5",
25308
- data: { results: [], total: 0 }
25309
- };
25310
- }
25311
- return {
25312
- success: true,
25313
- message: `\u627E\u5230 ${total} \u6761\u76F8\u5173\u8BB0\u5F55${total > results.length ? `\uFF0C\u663E\u793A\u524D ${results.length} \u6761` : ""}`,
25314
- data: { results, total }
25315
- };
25316
- } catch (err) {
25317
- return {
25318
- success: false,
25319
- message: `\u641C\u7D22\u5931\u8D25\uFF1A${err.message}`
25320
- };
25321
- }
25322
- }
25323
- var inkGetSchema = external_exports.object({
25324
- id: external_exports.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID")
25325
- });
25326
- async function inkGet(input) {
25327
- try {
25328
- const file = await readKnowledgeFile(input.id);
25329
- if (!file) {
25330
- return {
25331
- success: false,
25332
- message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5`
25333
- };
25334
- }
25335
- return {
25336
- success: true,
25337
- message: file.meta.title,
25338
- data: {
25339
- id: file.meta.id,
25340
- title: file.meta.title,
25341
- content: file.content,
25342
- tags: file.meta.tags,
25343
- category: file.meta.category,
25344
- source_type: file.meta.source_type,
25345
- url: file.meta.url,
25346
- created_at: file.meta.created_at,
25347
- updated_at: file.meta.updated_at
25348
- }
25349
- };
25350
- } catch (err) {
25351
- return {
25352
- success: false,
25353
- message: `\u83B7\u53D6\u5931\u8D25\uFF1A${err.message}`
25354
- };
25355
- }
25356
- }
25357
- var inkReviewSchema = external_exports.object({
25358
- period: external_exports.enum(["today", "week", "month", "custom"]).optional().default("week").describe("\u56DE\u987E\u65F6\u95F4\u6BB5\uFF1Atoday / week / month / custom"),
25359
- date_from: external_exports.string().optional().describe("\u81EA\u5B9A\u4E49\u8D77\u59CB\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF0Cperiod=custom \u65F6\u4F7F\u7528\uFF09"),
25360
- date_to: external_exports.string().optional().describe("\u81EA\u5B9A\u4E49\u622A\u6B62\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF0Cperiod=custom \u65F6\u4F7F\u7528\uFF09"),
25361
- category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().describe("\u6309\u5206\u7C7B\u8FC7\u6EE4")
25362
- });
25363
- async function inkReview(input) {
25364
- try {
25365
- const index = await readIndex();
25366
- const entries = Object.values(index.entries);
25367
- const now = /* @__PURE__ */ new Date();
25368
- let dateFrom;
25369
- let dateTo = now.toISOString();
25370
- switch (input.period) {
25371
- case "today":
25372
- dateFrom = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();
25373
- break;
25374
- case "week": {
25375
- const weekAgo = new Date(now);
25376
- weekAgo.setDate(weekAgo.getDate() - 7);
25377
- dateFrom = weekAgo.toISOString();
25378
- break;
25379
- }
25380
- case "month": {
25381
- const monthAgo = new Date(now);
25382
- monthAgo.setMonth(monthAgo.getMonth() - 1);
25383
- dateFrom = monthAgo.toISOString();
25384
- break;
25385
- }
25386
- case "custom":
25387
- dateFrom = input.date_from || (/* @__PURE__ */ new Date(0)).toISOString();
25388
- dateTo = input.date_to || now.toISOString();
25389
- break;
25390
- }
25391
- let filtered = entries.filter(
25392
- (e) => e.created_at >= dateFrom && e.created_at <= dateTo
25393
- );
25394
- if (input.category) {
25395
- filtered = filtered.filter((e) => e.category === input.category);
25396
- }
25397
- filtered.sort((a, b) => b.created_at.localeCompare(a.created_at));
25398
- const byCategory = {};
25399
- for (const e of filtered) {
25400
- byCategory[e.category] = (byCategory[e.category] || 0) + 1;
25401
- }
25402
- const tagCounts = {};
25403
- for (const e of filtered) {
25404
- for (const t of e.tags) {
25405
- tagCounts[t] = (tagCounts[t] || 0) + 1;
25406
- }
25407
- }
25408
- const topTags = Object.entries(tagCounts).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([tag, count]) => ({ tag, count }));
25409
- const categoryNames = {
25410
- insight: "\u6D1E\u5BDF",
25411
- decision: "\u51B3\u7B56",
25412
- analysis: "\u5206\u6790",
25413
- idea: "\u60F3\u6CD5",
25414
- reference: "\u53C2\u8003",
25415
- action: "\u884C\u52A8"
25416
- };
25417
- const periodNames = {
25418
- today: "\u4ECA\u65E5",
25419
- week: "\u672C\u5468",
25420
- month: "\u672C\u6708",
25421
- custom: "\u81EA\u5B9A\u4E49\u65F6\u6BB5"
25422
- };
25423
- return {
25424
- success: true,
25425
- message: `${periodNames[input.period]}\u77E5\u8BC6\u56DE\u987E\uFF1A\u5171 ${filtered.length} \u6761\u8BB0\u5F55`,
25426
- data: {
25427
- period: input.period,
25428
- date_from: dateFrom,
25429
- date_to: dateTo,
25430
- total: filtered.length,
25431
- by_category: Object.entries(byCategory).map(([cat, count]) => ({
25432
- category: cat,
25433
- label: categoryNames[cat] || cat,
25434
- count
25435
- })),
25436
- top_tags: topTags,
25437
- items: filtered.map((e) => ({
25438
- id: e.id,
25439
- title: e.title,
25440
- category: e.category,
25441
- tags: e.tags,
25442
- created_at: e.created_at,
25443
- preview: e.preview
25444
- }))
25445
- }
25446
- };
25447
- } catch (err) {
25448
- return {
25449
- success: false,
25450
- message: `\u56DE\u987E\u5931\u8D25\uFF1A${err.message}`
25451
- };
25452
- }
25453
- }
25454
- var inkUpdateSchema = external_exports.object({
25455
- id: external_exports.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
25456
- title: external_exports.string().optional().describe("\u65B0\u6807\u9898"),
25457
- content: external_exports.string().optional().describe("\u65B0\u5185\u5BB9"),
25458
- tags: external_exports.array(external_exports.string()).optional().describe("\u65B0\u6807\u7B7E\uFF08\u5B8C\u6574\u66FF\u6362\uFF09"),
25459
- category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().describe("\u65B0\u5206\u7C7B")
25460
- });
25461
- async function inkUpdate(input) {
25462
- try {
25463
- const { id, ...updates } = input;
25464
- if (!updates.title && !updates.content && !updates.tags && !updates.category) {
25465
- return {
25466
- success: false,
25467
- message: "\u8BF7\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u8981\u66F4\u65B0\u7684\u5B57\u6BB5\uFF08title / content / tags / category\uFF09"
25468
- };
25469
- }
25470
- const result = await updateKnowledgeFile(id, updates);
25471
- if (!result) {
25472
- return {
25473
- success: false,
25474
- message: `\u672A\u627E\u5230 ID \u4E3A ${id} \u7684\u77E5\u8BC6\u7247\u6BB5`
25475
- };
25476
- }
25477
- const changed = Object.keys(updates).filter(
25478
- (k) => updates[k] !== void 0
25479
- );
25480
- fireAndForgetPush({
25481
- knowledge: [{
25482
- id: result.meta.id,
25483
- title: result.meta.title,
25484
- tags: result.meta.tags,
25485
- category: result.meta.category,
25486
- source_type: result.meta.source_type,
25487
- url: result.meta.url,
25488
- created_at: result.meta.created_at,
25489
- updated_at: result.meta.updated_at
25490
- }]
25491
- });
25492
- return {
25493
- success: true,
25494
- message: `\u5DF2\u66F4\u65B0\u300C${result.meta.title}\u300D\u7684 ${changed.join("\u3001")}`,
25495
- data: {
25496
- id: result.meta.id,
25497
- title: result.meta.title,
25498
- tags: result.meta.tags,
25499
- category: result.meta.category,
25500
- updated_at: result.meta.updated_at
25501
- }
25502
- };
25503
- } catch (err) {
25504
- return {
25505
- success: false,
25506
- message: `\u66F4\u65B0\u5931\u8D25\uFF1A${err.message}`
25507
- };
25508
- }
25509
- }
25510
- var inkDeleteSchema = external_exports.object({
25511
- id: external_exports.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
25512
- confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
25513
- });
25514
- async function inkDelete(input) {
25515
- try {
25516
- if (!input.confirm) {
25517
- const index = await readIndex();
25518
- const entry = index.entries[input.id];
25519
- if (!entry) {
25520
- return {
25521
- success: false,
25522
- message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5`
25523
- };
25524
- }
25525
- return {
25526
- success: false,
25527
- message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
25528
- data: {
25529
- id: entry.id,
25530
- title: entry.title,
25531
- category: entry.category,
25532
- tags: entry.tags,
25533
- created_at: entry.created_at
25534
- }
25535
- };
25536
- }
25537
- const deleted = await deleteKnowledgeFile(input.id);
25538
- if (!deleted) {
25539
- return {
25540
- success: false,
25541
- message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5`
25542
- };
25543
- }
25544
- fireAndForgetPush({
25545
- knowledge: [{
25546
- id: input.id,
25547
- title: "",
25548
- tags: [],
25549
- category: "reference",
25550
- source_type: "conversation",
25551
- created_at: "",
25552
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
25553
- }]
25554
- });
25555
- return {
25556
- success: true,
25557
- message: `\u5DF2\u5220\u9664\u77E5\u8BC6\u7247\u6BB5 ${input.id}`,
25558
- data: { id: input.id }
25559
- };
25560
- } catch (err) {
25561
- return {
25562
- success: false,
25563
- message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}`
25564
- };
25565
- }
25566
- }
25567
- var inkCompileSchema = external_exports.object({
25568
- ids: external_exports.array(external_exports.string()).optional().describe("\u6307\u5B9A\u8981\u6574\u5408\u7684\u77E5\u8BC6\u7247\u6BB5 ID \u5217\u8868"),
25569
- query: external_exports.string().optional().describe("\u641C\u7D22\u6761\u4EF6\uFF08\u4E0E ids \u4E8C\u9009\u4E00\uFF09"),
25570
- tags: external_exports.array(external_exports.string()).optional().describe("\u6309\u6807\u7B7E\u7B5B\u9009\uFF08\u4E0E query \u642D\u914D\uFF09"),
25571
- format: external_exports.enum(["outline", "article", "summary"]).optional().default("summary").describe("\u8F93\u51FA\u683C\u5F0F\uFF1Aoutline\uFF08\u5927\u7EB2\uFF09/ article\uFF08\u6587\u7AE0\uFF09/ summary\uFF08\u6458\u8981\uFF09"),
25572
- instruction: external_exports.string().optional().describe("\u989D\u5916\u6574\u5408\u6307\u4EE4")
25573
- });
25574
- async function inkCompile(input) {
25575
- try {
25576
- let ids = [];
25577
- if (input.ids && input.ids.length > 0) {
25578
- ids = input.ids;
25579
- } else if (input.query || input.tags && input.tags.length > 0) {
25580
- const index = await readIndex();
25581
- const { results } = searchKnowledge(index, {
25582
- query: input.query,
25583
- tags: input.tags,
25584
- limit: 20
25585
- });
25586
- ids = results.map((r) => r.id);
25587
- }
25588
- if (ids.length === 0) {
25589
- return {
25590
- success: false,
25591
- message: "\u6CA1\u6709\u627E\u5230\u8981\u6574\u5408\u7684\u77E5\u8BC6\u7247\u6BB5\uFF0C\u8BF7\u6307\u5B9A ids \u6216\u63D0\u4F9B\u641C\u7D22\u6761\u4EF6"
25592
- };
25593
- }
25594
- const files = await readMultipleKnowledgeFiles(ids);
25595
- if (files.length === 0) {
25596
- return {
25597
- success: false,
25598
- message: "\u672A\u80FD\u8BFB\u53D6\u4EFB\u4F55\u77E5\u8BC6\u7247\u6BB5\u5185\u5BB9"
25599
- };
25600
- }
25601
- const sections = files.map((f, i) => {
25602
- const tagStr = f.meta.tags.length > 0 ? ` [${f.meta.tags.join(", ")}]` : "";
25603
- return `### ${i + 1}. ${f.meta.title}${tagStr}
25604
- > ${f.meta.category} | ${f.meta.created_at.slice(0, 10)}
25605
-
25606
- ${f.content}`;
25607
- });
25608
- const formatLabels = {
25609
- outline: "\u5927\u7EB2",
25610
- article: "\u6587\u7AE0",
25611
- summary: "\u6458\u8981"
25612
- };
25613
- const compiled = sections.join("\n\n---\n\n");
25614
- return {
25615
- success: true,
25616
- message: `\u5DF2\u6574\u5408 ${files.length} \u6761\u77E5\u8BC6\u7247\u6BB5\uFF0C\u683C\u5F0F\uFF1A${formatLabels[input.format]}`,
25617
- data: {
25618
- source_count: files.length,
25619
- source_ids: files.map((f) => f.meta.id),
25620
- format: input.format,
25621
- instruction: input.instruction || null,
25622
- compiled_content: compiled
25623
- }
25624
- };
25625
- } catch (err) {
25626
- return {
25627
- success: false,
25628
- message: `\u6574\u5408\u5931\u8D25\uFF1A${err.message}`
25629
- };
25630
- }
25631
- }
25632
- var inkTagsSchema = external_exports.object({
25633
- action: external_exports.enum(["list", "rename", "merge"]).describe("\u64CD\u4F5C\uFF1Alist\uFF08\u5217\u51FA\u6240\u6709\u6807\u7B7E\uFF09/ rename\uFF08\u91CD\u547D\u540D\uFF09/ merge\uFF08\u5408\u5E76\uFF09"),
25634
- tag: external_exports.string().optional().describe("\u76EE\u6807\u6807\u7B7E\uFF08rename/merge \u65F6\u4F7F\u7528\uFF09"),
25635
- new_tag: external_exports.string().optional().describe("\u65B0\u6807\u7B7E\u540D\uFF08rename \u65F6\u4F7F\u7528\uFF09"),
25636
- source_tags: external_exports.array(external_exports.string()).optional().describe("\u8981\u5408\u5E76\u7684\u6E90\u6807\u7B7E\u5217\u8868\uFF08merge \u65F6\u4F7F\u7528\uFF09")
25637
- });
25638
- async function inkTags(input) {
25639
- try {
25640
- const index = await readIndex();
25641
- switch (input.action) {
25642
- case "list": {
25643
- const tags = getAllTags(index);
25644
- return {
25645
- success: true,
25646
- message: tags.length > 0 ? `\u5171 ${tags.length} \u4E2A\u6807\u7B7E` : "\u6682\u65E0\u6807\u7B7E",
25647
- data: { tags }
25648
- };
25649
- }
25650
- case "rename": {
25651
- if (!input.tag || !input.new_tag) {
25652
- return {
25653
- success: false,
25654
- message: "rename \u64CD\u4F5C\u9700\u8981\u63D0\u4F9B tag\uFF08\u539F\u6807\u7B7E\uFF09\u548C new_tag\uFF08\u65B0\u6807\u7B7E\u540D\uFF09"
25655
- };
25656
- }
25657
- const count = await renameTag(input.tag, input.new_tag);
25658
- if (count === 0) {
25659
- return {
25660
- success: false,
25661
- message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u77E5\u8BC6\u7247\u6BB5`
25662
- };
25663
- }
25664
- return {
25665
- success: true,
25666
- message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u300C${input.tag}\u300D\u91CD\u547D\u540D\u4E3A\u300C${input.new_tag}\u300D`,
25667
- data: { old_tag: input.tag, new_tag: input.new_tag, affected: count }
25668
- };
25669
- }
25670
- case "merge": {
25671
- if (!input.source_tags || input.source_tags.length === 0 || !input.new_tag) {
25672
- return {
25673
- success: false,
25674
- message: "merge \u64CD\u4F5C\u9700\u8981\u63D0\u4F9B source_tags\uFF08\u6E90\u6807\u7B7E\u5217\u8868\uFF09\u548C new_tag\uFF08\u76EE\u6807\u6807\u7B7E\uFF09"
25675
- };
25676
- }
25677
- const count = await mergeTags(input.source_tags, input.new_tag);
25678
- if (count === 0) {
25679
- return {
25680
- success: false,
25681
- message: `\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u77E5\u8BC6\u7247\u6BB5`
25682
- };
25683
- }
25684
- return {
25685
- success: true,
25686
- message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
25687
- data: {
25688
- source_tags: input.source_tags,
25689
- target_tag: input.new_tag,
25690
- affected: count
25691
- }
25692
- };
25693
- }
25694
- }
25695
- } catch (err) {
25696
- return {
25697
- success: false,
25698
- message: `\u6807\u7B7E\u64CD\u4F5C\u5931\u8D25\uFF1A${err.message}`
25699
- };
25700
- }
25701
- }
25702
-
25703
25497
  // src/index.ts
25704
25498
  var server = new McpServer({
25705
25499
  name: "ClaudeInk",
25706
- version: "1.0.0"
25500
+ version: "2.0.1"
25707
25501
  });
25708
- server.tool("workflow.init", "\u521D\u59CB\u5316\u77E5\u8BC6\u5E93\uFF08\u521B\u5EFA\u76EE\u5F55 + \u6FC0\u6D3B License + \u540C\u6B65\u4E91\u7AEF\uFF09", workflowInitSchema.shape, async (input) => {
25502
+ server.tool("ink.init", "\u521D\u59CB\u5316\u77E5\u8BC6\u5E93\uFF08\u521B\u5EFA\u76EE\u5F55 + \u6FC0\u6D3B License + \u540C\u6B65\u4E91\u7AEF\uFF09", workflowInitSchema.shape, async (input) => {
25709
25503
  const result = await workflowInit(input);
25710
25504
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25711
25505
  });
25712
- server.tool("sync", "\u4ECE\u4E91\u7AEF\u540C\u6B65\u77E5\u8BC6\u5143\u6570\u636E", syncSchema.shape, async (input) => {
25506
+ server.tool("ink.sync", "\u4ECE\u4E91\u7AEF\u540C\u6B65\u77E5\u8BC6\u5143\u6570\u636E", syncSchema.shape, async (input) => {
25713
25507
  const result = await sync(input);
25714
25508
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25715
25509
  });
25716
- server.tool("ink.save", "\u4FDD\u5B58\u77E5\u8BC6\u7247\u6BB5\u5230\u672C\u5730\u77E5\u8BC6\u5E93\uFF08\u5BF9\u8BDD\u6D1E\u5BDF\u3001\u51B3\u7B56\u3001\u5206\u6790\u7B49\uFF09", inkSaveSchema.shape, async (input) => {
25510
+ server.tool("ink.save", "\u4FDD\u5B58\u6216\u66F4\u65B0\u77E5\u8BC6\u7D20\u6750\uFF08\u6709 id \u66F4\u65B0\uFF0C\u65E0 id \u65B0\u5EFA\uFF09", inkSaveSchema.shape, async (input) => {
25717
25511
  const result = await inkSave(input);
25718
25512
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25719
25513
  });
25720
- server.tool("ink.search", "\u641C\u7D22\u77E5\u8BC6\u5E93\uFF08\u652F\u6301\u5173\u952E\u8BCD\u3001\u6807\u7B7E\u3001\u5206\u7C7B\u3001\u65F6\u95F4\u8303\u56F4\u8FC7\u6EE4\uFF09", inkSearchSchema.shape, async (input) => {
25721
- const result = await inkSearch(input);
25722
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25723
- });
25724
- server.tool("ink.get", "\u83B7\u53D6\u77E5\u8BC6\u7247\u6BB5\u5B8C\u6574\u5185\u5BB9", inkGetSchema.shape, async (input) => {
25725
- const result = await inkGet(input);
25726
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25727
- });
25728
- server.tool("ink.review", "\u6309\u65F6\u95F4\u6BB5\u56DE\u987E\u77E5\u8BC6\u5E93\uFF08\u7EDF\u8BA1 + \u5217\u8868\uFF09", inkReviewSchema.shape, async (input) => {
25729
- const result = await inkReview(input);
25730
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25731
- });
25732
- server.tool("ink.update", "\u66F4\u65B0\u5DF2\u4FDD\u5B58\u7684\u77E5\u8BC6\u7247\u6BB5\uFF08\u6807\u9898/\u5185\u5BB9/\u6807\u7B7E/\u5206\u7C7B\uFF09", inkUpdateSchema.shape, async (input) => {
25733
- const result = await inkUpdate(input);
25734
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25735
- });
25736
25514
  server.tool("ink.delete", "\u5220\u9664\u77E5\u8BC6\u7247\u6BB5\uFF08\u9700\u786E\u8BA4\uFF09", inkDeleteSchema.shape, async (input) => {
25737
25515
  const result = await inkDelete(input);
25738
25516
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25739
25517
  });
25740
- server.tool("ink.compile", "\u6574\u5408\u591A\u6761\u77E5\u8BC6\u7247\u6BB5\u4E3A\u7ED3\u6784\u5316\u5185\u5BB9\uFF08\u6309 ID \u6216\u641C\u7D22\u6761\u4EF6\uFF09", inkCompileSchema.shape, async (input) => {
25741
- const result = await inkCompile(input);
25518
+ server.tool("ink.write", "\u5199\u6587\u7AE0\u4FDD\u5B58\u4E3A\u8349\u7A3F\u3002Claude \u5199\u5B8C\u535A\u5BA2\u6587\u7AE0\u540E\u81EA\u52A8\u8C03\u7528\uFF0C\u63D0\u4F9B\u6807\u9898\u3001Markdown \u5185\u5BB9\u3001\u5C01\u9762\u56FE\u548C\u6807\u7B7E\u3002", inkWriteSchema.shape, async (input) => {
25519
+ const result = await inkWrite(input);
25742
25520
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25743
25521
  });
25744
25522
  server.tool("ink.tags", "\u6807\u7B7E\u7BA1\u7406\uFF08\u5217\u51FA / \u91CD\u547D\u540D / \u5408\u5E76\u6807\u7B7E\uFF09", inkTagsSchema.shape, async (input) => {
25745
25523
  const result = await inkTags(input);
25746
25524
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25747
25525
  });
25748
- server.tool("source.crawl", "\u89E6\u53D1\u722C\u866B\u6293\u53D6\uFF08\u5185\u5BB9\u81EA\u52A8\u5B58\u5165\u77E5\u8BC6\u5E93\uFF09", sourceCrawlSchema.shape, async (input) => {
25526
+ server.tool("ink.crawl", "\u89E6\u53D1\u722C\u866B\u6293\u53D6\uFF08\u5185\u5BB9\u81EA\u52A8\u5B58\u5165\u77E5\u8BC6\u5E93\uFF09", sourceCrawlSchema.shape, async (input) => {
25749
25527
  const result = await sourceCrawl(input);
25750
25528
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25751
25529
  });
25752
- server.tool("source.subscribe", "\u7BA1\u7406\u8BA2\u9605\u6E90\uFF08\u6DFB\u52A0/\u5220\u9664/\u5217\u51FA\u722C\u866B\u6E90\uFF09", sourceSubscribeSchema.shape, async (input) => {
25530
+ server.tool("ink.subscribe", "\u7BA1\u7406\u8BA2\u9605\u6E90\uFF08\u6DFB\u52A0/\u5220\u9664/\u5217\u51FA\u722C\u866B\u6E90\uFF09", sourceSubscribeSchema.shape, async (input) => {
25753
25531
  const result = await sourceSubscribe(input);
25754
25532
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25755
25533
  });
@@ -25758,16 +25536,16 @@ async function main() {
25758
25536
  const state = await readState();
25759
25537
  if (state.config.workflowDir) {
25760
25538
  setWorkDir(state.config.workflowDir);
25761
- console.error(`[ClaudeInk MCP] workDir restored: ${state.config.workflowDir}`);
25762
25539
  }
25540
+ setLogLevel(state.config.logLevel || "INFO");
25763
25541
  } catch {
25764
25542
  }
25765
25543
  const transport = new StdioServerTransport();
25766
25544
  await server.connect(transport);
25767
- console.error("[ClaudeInk MCP] Server started on stdio (12 tools)");
25545
+ await log("INFO", "server", "started (8 tools)");
25768
25546
  }
25769
- main().catch((err) => {
25770
- console.error("[ClaudeInk MCP] Fatal error:", err);
25547
+ main().catch(async (err) => {
25548
+ await log("ERROR", "server", `fatal: ${err.message || err}`);
25771
25549
  process.exit(1);
25772
25550
  });
25773
25551
  /*! Bundled license information: