agent-wiki 0.1.0 → 0.1.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.
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ import { handleCompile } from "./tools/compile.js";
20
20
  import { handleSearch } from "./tools/search.js";
21
21
  import { handleArticle } from "./tools/article.js";
22
22
  import { handleLint } from "./tools/lint.js";
23
+ import { handleFeedback } from "./tools/feedback.js";
23
24
  const server = new McpServer({
24
25
  name: "agent-wiki",
25
26
  version: "0.1.0",
@@ -42,11 +43,11 @@ server.tool("wiki_init", "初始化知识库。创建目录结构和 SCHEMA.md
42
43
  interests: z.array(z.string()).describe("兴趣方向列表,如 ['LLM', '创业', 'RAG']"),
43
44
  }, async (params) => handleInit(params));
44
45
  /**
45
- * wiki_ingest — 摄入 MD 文件到知识库
46
- * 从指定路径复制 MD 文件到 raw/(增量)
47
- * 返回每个文件的前 500 字预览供 Agent 用 LLM 处理
46
+ * wiki_ingest — 摄入文件到知识库(支持多格式)
47
+ * md/txt/html/csv 零依赖转换为 md
48
+ * pdf/docx/xlsx/图片原样存储,Agent 多模态读取
48
49
  */
49
- server.tool("wiki_ingest", "摄入 MD 文件到知识库。从指定路径复制 .md 文件到 raw/ 目录(增量,已摄入的跳过)。返回新文件内容预览,供你用 LLM 提取标签和摘要。", {
50
+ server.tool("wiki_ingest", "摄入文件到知识库。支持 md/txt/html/csv(自动转 markdown)、pdf/docx/xlsx/图片(原样存储)。从指定路径复制到 raw/ 目录(增量,已摄入的跳过)。返回新文件内容预览,供你用 LLM 提取标签和摘要。", {
50
51
  source: z.string().describe("源文件路径或目录路径"),
51
52
  force: z.boolean().optional().describe("强制重新摄入已存在的文件,默认 false"),
52
53
  }, async (params) => handleIngest(params));
@@ -99,6 +100,19 @@ server.tool("wiki_article", "保存原创文章到 outputs/ 目录。保存前
99
100
  server.tool("wiki_lint", "自检知识库健康状态。检测死链接、缺失摘要、内容过短、缺少标签等问题,返回问题列表。", {
100
101
  check_only: z.boolean().optional().describe("仅检测不修复,默认 true"),
101
102
  }, async (params) => handleLint(params));
103
+ /**
104
+ * wiki_feedback — 保存问答分析到知识库(用户确认版)
105
+ * 用户问问题 → Agent 回答 → 如果回答有综合分析价值 → 用户说"存" → 调用此工具
106
+ * 标记 type: query-derived,区分原始材料编译 vs 问答生成
107
+ * 防止幻觉复利:不自动存,必须用户确认
108
+ */
109
+ server.tool("wiki_feedback", "保存问答分析到知识库。仅当用户明确说'存'或'保存'时才调用。将问答中的综合分析、对比、洞察保存为新的 Wiki 页面,标记为 query-derived 类型(区分原始材料编译 vs 问答生成)。", {
110
+ question: z.string().describe("用户的原始问题"),
111
+ answer: z.string().describe("Agent 的回答内容(有价值综合分析部分)"),
112
+ sources: z.array(z.string()).describe("参考了哪些 Wiki 页面(文件名列表)"),
113
+ tags: z.array(z.string()).describe("标签列表,如 ['RAG', '对比分析']"),
114
+ category: z.string().describe("分类名,如 'AI工程化'"),
115
+ }, async (params) => handleFeedback(params));
102
116
  // ─── 启动 MCP Server ───
103
117
  async function main() {
104
118
  const transport = new StdioServerTransport();
@@ -0,0 +1,20 @@
1
+ /**
2
+ * wiki_feedback — 保存问答分析到知识库(用户确认版)
3
+ *
4
+ * 核心理念(来自 Karpathy):
5
+ * "good answers can be filed back into the wiki as new pages"
6
+ * 用户的提问和 Agent 的回答,不应该消失在聊天记录里,应该沉淀回知识库
7
+ *
8
+ * 防止幻觉复利的关键设计:
9
+ * ❌ 不自动存:必须用户说"存"才调用
10
+ * ✅ 标记来源:type: query-derived,区分原始材料 vs 问答生成
11
+ * ✅ 搜索排序:compiled 类型排在 query-derived 前面
12
+ */
13
+ import { ToolResult } from "../types.js";
14
+ export declare function handleFeedback(params: {
15
+ question: string;
16
+ answer: string;
17
+ sources: string[];
18
+ tags: string[];
19
+ category: string;
20
+ }): Promise<ToolResult>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * wiki_feedback — 保存问答分析到知识库(用户确认版)
3
+ *
4
+ * 核心理念(来自 Karpathy):
5
+ * "good answers can be filed back into the wiki as new pages"
6
+ * 用户的提问和 Agent 的回答,不应该消失在聊天记录里,应该沉淀回知识库
7
+ *
8
+ * 防止幻觉复利的关键设计:
9
+ * ❌ 不自动存:必须用户说"存"才调用
10
+ * ✅ 标记来源:type: query-derived,区分原始材料 vs 问答生成
11
+ * ✅ 搜索排序:compiled 类型排在 query-derived 前面
12
+ */
13
+ import * as path from "path";
14
+ import * as fs from "fs";
15
+ import { getWikiDir, getWikiPagesDir } from "../state.js";
16
+ import { safeFilename, todayStr, nowStr } from "../utils.js";
17
+ import { textResult } from "../types.js";
18
+ export async function handleFeedback(params) {
19
+ const wikiDir = getWikiDir();
20
+ if (!wikiDir) {
21
+ return textResult(JSON.stringify({ error: "知识库未初始化" }));
22
+ }
23
+ const { question, answer, sources, tags, category } = params;
24
+ const wikiPagesDir = getWikiPagesDir(wikiDir);
25
+ // 从问题生成标题(截取前 30 字符)
26
+ const title = `Q&A: ${question.substring(0, 30)}${question.length > 30 ? "..." : ""}`;
27
+ const fileName = `${safeFilename(title)}.md`;
28
+ const filePath = path.join(wikiPagesDir, fileName);
29
+ // 构建 sources 的 wikilink 格式
30
+ const sourceLinks = sources.map((s) => `- "[[${s}]]"`).join("\n");
31
+ // 生成 Wiki 页面内容
32
+ const content = `---
33
+ title: "${title.replace(/"/g, '\\"')}"
34
+ date: ${todayStr()}
35
+ tags: [${tags.join(", ")}]
36
+ category: ${category}
37
+ type: query-derived
38
+ sources:
39
+ ${sourceLinks}
40
+ ---
41
+
42
+ # ${title}
43
+
44
+ ## 问题
45
+ ${question}
46
+
47
+ ## 回答
48
+ ${answer}
49
+
50
+ ## 参考来源
51
+ ${sources.map((s) => `- [[${s.replace(".md", "")}]]`).join("\n")}
52
+
53
+ ---
54
+ *来源: 用户问答反馈 · 生成时间: ${nowStr()}*
55
+ `;
56
+ // 写入 Wiki 页面
57
+ fs.writeFileSync(filePath, content, "utf-8");
58
+ // 更新 INDEX.md
59
+ const indexPath = path.join(wikiPagesDir, "INDEX.md");
60
+ if (fs.existsSync(indexPath)) {
61
+ const indexContent = fs.readFileSync(indexPath, "utf-8");
62
+ // 解析已有分类
63
+ const sections = {};
64
+ const lines = indexContent.split("\n");
65
+ let currentCategory = "";
66
+ for (const line of lines) {
67
+ if (line.startsWith("## ") && !line.includes("知识库索引")) {
68
+ currentCategory = line.replace("## ", "").trim();
69
+ if (!sections[currentCategory])
70
+ sections[currentCategory] = [];
71
+ }
72
+ else if (line.startsWith("- [[") && currentCategory) {
73
+ const linkMatch = line.match(/- \[\[(.+?)\]\]\s*\((.+?)\)/);
74
+ if (linkMatch && !sections[currentCategory]) {
75
+ sections[currentCategory] = [];
76
+ }
77
+ }
78
+ }
79
+ if (!sections[category])
80
+ sections[category] = [];
81
+ sections[category].push({ title, tags: tags.slice(0, 3) });
82
+ // 重新生成 INDEX.md
83
+ const totalEntries = Object.values(sections).reduce((s, e) => s + e.length, 0);
84
+ const newLines = [
85
+ "---",
86
+ `title: 知识库索引`,
87
+ `date: ${todayStr()}`,
88
+ "---",
89
+ "",
90
+ "# 知识库索引",
91
+ "",
92
+ ];
93
+ for (const [cat, entries] of Object.entries(sections)) {
94
+ if (entries.length === 0)
95
+ continue;
96
+ newLines.push(`## ${cat}`);
97
+ for (const entry of entries) {
98
+ newLines.push(`- [[${entry.title}]] (${entry.tags.join(", ")})`);
99
+ }
100
+ newLines.push("");
101
+ }
102
+ newLines.push(`---\n*共 ${totalEntries} 篇 · 更新于 ${todayStr()}*`);
103
+ fs.writeFileSync(indexPath, newLines.join("\n"), "utf-8");
104
+ }
105
+ // 追加 LOG.md
106
+ const logPath = path.join(wikiPagesDir, "LOG.md");
107
+ const logEntry = `\n## [${nowStr()}] feedback | ${title}\n来源: 用户问答 | 分类: ${category} | 标签: ${tags.join(", ")} | 参考: ${sources.join(", ")}\n`;
108
+ if (fs.existsSync(logPath)) {
109
+ fs.appendFileSync(logPath, logEntry, "utf-8");
110
+ }
111
+ return textResult(JSON.stringify({
112
+ status: "ok",
113
+ file: fileName,
114
+ title,
115
+ type: "query-derived",
116
+ note: "已保存为 Wiki 页面。搜索时 compiled 类型结果会排在 query-derived 前面。",
117
+ }));
118
+ }
@@ -1,9 +1,10 @@
1
1
  /**
2
- * wiki_ingest — 从指定路径复制 MD 文件到 raw/(增量)
2
+ * wiki_ingest — 从指定路径摄入文件到 raw/(增量,支持多格式)
3
3
  *
4
- * source 可以是单个文件或目录
4
+ * 零依赖转换:.md(直接复制)、.txt(改后缀)、.html(去标签)、.csv(转表格)
5
+ * 原样存储:.pdf、.docx、.xlsx、.xls、图片 → Agent 多模态读取
5
6
  * 已摄入的文件跳过(除非 force=true)
6
- * 返回每个文件的名称和前 500 字预览(供 Agent 用 LLM 处理)
7
+ * 返回每个文件的名称、类型和前 500 字预览(供 Agent 用 LLM 处理)
7
8
  */
8
9
  import { ToolResult } from "../types.js";
9
10
  export declare function handleIngest(params: {
@@ -1,65 +1,256 @@
1
1
  /**
2
- * wiki_ingest — 从指定路径复制 MD 文件到 raw/(增量)
2
+ * wiki_ingest — 从指定路径摄入文件到 raw/(增量,支持多格式)
3
3
  *
4
- * source 可以是单个文件或目录
4
+ * 零依赖转换:.md(直接复制)、.txt(改后缀)、.html(去标签)、.csv(转表格)
5
+ * 原样存储:.pdf、.docx、.xlsx、.xls、图片 → Agent 多模态读取
5
6
  * 已摄入的文件跳过(除非 force=true)
6
- * 返回每个文件的名称和前 500 字预览(供 Agent 用 LLM 处理)
7
+ * 返回每个文件的名称、类型和前 500 字预览(供 Agent 用 LLM 处理)
7
8
  */
8
9
  import * as path from "path";
9
10
  import * as fs from "fs";
10
11
  import { getWikiDir, getRawDir, getState, setState } from "../state.js";
11
12
  import { readFile_safe, ensureDir } from "../utils.js";
12
13
  import { textResult } from "../types.js";
14
+ // ─── 支持的文件类型 ───
15
+ /** 零依赖可转为 .md 的格式 */
16
+ const CONVERTIBLE_EXTENSIONS = new Set([".md", ".txt", ".html", ".csv"]);
17
+ /** 原样存储的二进制格式(Agent 多模态读取) */
18
+ const BINARY_EXTENSIONS = new Set([".pdf", ".docx", ".xlsx", ".xls"]);
19
+ /** 图片格式 */
20
+ const IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"]);
21
+ /** 所有支持的格式 */
22
+ const SUPPORTED_EXTENSIONS = new Set([
23
+ ...CONVERTIBLE_EXTENSIONS,
24
+ ...BINARY_EXTENSIONS,
25
+ ...IMAGE_EXTENSIONS,
26
+ ]);
27
+ // ─── 格式转换函数 ───
28
+ /**
29
+ * .txt → .md(直接改后缀,内容不变)
30
+ */
31
+ function convertTxt(content) {
32
+ return content;
33
+ }
34
+ /**
35
+ * .html → .md(去除 HTML 标签,保留纯文本)
36
+ * 零依赖,用正则处理。不追求完美还原,只提取可读文本
37
+ */
38
+ function convertHtml(html) {
39
+ let text = html;
40
+ // 去除 script、style 标签及其内容
41
+ text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
42
+ text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
43
+ // 处理常见块级标签,转为换行
44
+ text = text.replace(/<\/?(p|div|br|h[1-6]|li|tr|blockquote)[^>]*>/gi, "\n");
45
+ // 处理标题标签,转为 markdown 标题
46
+ text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "# $1\n");
47
+ text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "## $1\n");
48
+ text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "### $1\n");
49
+ text = text.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "#### $1\n");
50
+ // 处理列表项
51
+ text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "- $1\n");
52
+ // 处理加粗和斜体
53
+ text = text.replace(/<(strong|b)[^>]*>([\s\S]*?)<\/(strong|b)>/gi, "**$2**");
54
+ text = text.replace(/<(em|i)[^>]*>([\s\S]*?)<\/(em|i)>/gi, "*$2*");
55
+ // 处理链接
56
+ text = text.replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, "[$2]($1)");
57
+ // 去除所有剩余 HTML 标签
58
+ text = text.replace(/<[^>]+>/g, "");
59
+ // 解码常见 HTML 实体
60
+ text = text.replace(/&amp;/g, "&");
61
+ text = text.replace(/&lt;/g, "<");
62
+ text = text.replace(/&gt;/g, ">");
63
+ text = text.replace(/&quot;/g, '"');
64
+ text = text.replace(/&#39;/g, "'");
65
+ text = text.replace(/&nbsp;/g, " ");
66
+ // 清理多余空行(超过 2 个连续换行压缩为 2 个)
67
+ text = text.replace(/\n{3,}/g, "\n\n");
68
+ return text.trim();
69
+ }
70
+ /**
71
+ * .csv → markdown 表格
72
+ * 零依赖,手动解析。处理简单 CSV(带引号的字段、逗号在引号内)
73
+ */
74
+ function convertCsv(csv) {
75
+ // 简单 CSV 解析(处理引号内的逗号)
76
+ function parseLine(line) {
77
+ const fields = [];
78
+ let current = "";
79
+ let inQuotes = false;
80
+ for (let i = 0; i < line.length; i++) {
81
+ const ch = line[i];
82
+ if (ch === '"') {
83
+ if (inQuotes && i + 1 < line.length && line[i + 1] === '"') {
84
+ // 转义引号 ""
85
+ current += '"';
86
+ i++;
87
+ }
88
+ else {
89
+ inQuotes = !inQuotes;
90
+ }
91
+ }
92
+ else if (ch === "," && !inQuotes) {
93
+ fields.push(current.trim());
94
+ current = "";
95
+ }
96
+ else {
97
+ current += ch;
98
+ }
99
+ }
100
+ fields.push(current.trim());
101
+ return fields;
102
+ }
103
+ const lines = csv.split(/\r?\n/).filter((line) => line.trim());
104
+ if (lines.length === 0)
105
+ return "";
106
+ // 第一行是表头
107
+ const headers = parseLine(lines[0]);
108
+ const colCount = headers.length;
109
+ // 构建 markdown 表格
110
+ const mdLines = [];
111
+ // 表头行
112
+ mdLines.push("| " + headers.join(" | ") + " |");
113
+ // 分隔行
114
+ mdLines.push("| " + headers.map(() => "---").join(" | ") + " |");
115
+ // 数据行
116
+ for (let i = 1; i < lines.length; i++) {
117
+ const fields = parseLine(lines[i]);
118
+ // 补齐或截断到与表头同列数
119
+ while (fields.length < colCount)
120
+ fields.push("");
121
+ mdLines.push("| " + fields.slice(0, colCount).join(" | ") + " |");
122
+ }
123
+ return mdLines.join("\n");
124
+ }
125
+ /**
126
+ * 根据文件扩展名,将文件内容转为 .md 格式
127
+ * 返回 null 表示无法转换(二进制文件)
128
+ */
129
+ function convertToMarkdown(filePath) {
130
+ const ext = path.extname(filePath).toLowerCase();
131
+ const content = readFile_safe(filePath);
132
+ if (content === null)
133
+ return null;
134
+ switch (ext) {
135
+ case ".md":
136
+ return content;
137
+ case ".txt":
138
+ return convertTxt(content);
139
+ case ".html":
140
+ return convertHtml(content);
141
+ case ".csv":
142
+ return convertCsv(content);
143
+ default:
144
+ return null;
145
+ }
146
+ }
147
+ // ─── 文件类型判断 ───
148
+ function getFileType(filePath) {
149
+ const ext = path.extname(filePath).toLowerCase();
150
+ if (CONVERTIBLE_EXTENSIONS.has(ext))
151
+ return "convertible";
152
+ if (BINARY_EXTENSIONS.has(ext))
153
+ return "binary";
154
+ if (IMAGE_EXTENSIONS.has(ext))
155
+ return "image";
156
+ return "unsupported";
157
+ }
158
+ // ─── 主处理函数 ───
13
159
  export async function handleIngest(params) {
14
160
  const wikiDir = getWikiDir();
15
161
  if (!wikiDir) {
16
162
  return textResult(JSON.stringify({ error: "知识库未初始化,请先调用 wiki_init" }));
17
163
  }
18
164
  const rawDir = getRawDir(wikiDir);
165
+ const assetsDir = path.join(rawDir, "assets");
19
166
  ensureDir(rawDir);
167
+ ensureDir(assetsDir);
20
168
  const state = getState(wikiDir);
21
169
  const source = params.source.replace(/^~/, process.env.HOME || "~");
22
170
  const force = params.force ?? false;
23
171
  // 收集要摄入的文件列表
24
172
  let filesToIngest = [];
25
173
  if (fs.statSync(source).isDirectory()) {
174
+ // 目录:扫描所有支持的文件类型
26
175
  filesToIngest = fs
27
176
  .readdirSync(source)
28
- .filter((f) => f.endsWith(".md"))
177
+ .filter((f) => SUPPORTED_EXTENSIONS.has(path.extname(f).toLowerCase()))
29
178
  .map((f) => path.join(source, f));
30
179
  }
31
- else if (source.endsWith(".md")) {
32
- filesToIngest = [source];
33
- }
34
180
  else {
35
- return textResult(JSON.stringify({ error: "source 必须是 .md 文件或包含 .md 文件的目录" }));
181
+ // 单文件:检查是否支持
182
+ const ext = path.extname(source).toLowerCase();
183
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
184
+ filesToIngest = [source];
185
+ }
186
+ else {
187
+ return textResult(JSON.stringify({
188
+ error: `不支持的文件格式: ${ext}。支持的格式: md, txt, html, csv, pdf, docx, xlsx, xls, png, jpg, jpeg, gif, webp`,
189
+ }));
190
+ }
36
191
  }
37
- // 增量过滤
192
+ // 增量过滤 + 处理
38
193
  const results = [];
39
194
  let ingested = 0;
40
195
  for (const filePath of filesToIngest) {
41
196
  const fileName = path.basename(filePath);
42
- const destPath = path.join(rawDir, fileName);
197
+ const fileType = getFileType(filePath);
198
+ // 生成 raw/ 中的目标文件名
199
+ let destFileName = fileName;
200
+ if (fileType === "convertible" && !fileName.endsWith(".md")) {
201
+ // 可转换格式:目标文件名改为 .md
202
+ destFileName = path.basename(fileName, path.extname(fileName)) + ".md";
203
+ }
43
204
  // 已摄入则跳过(除非 force)
44
- if (!force && state.ingested.includes(fileName)) {
45
- results.push({ name: fileName, content_preview: "(已摄入,跳过)", skipped: true });
205
+ if (!force && state.ingested.includes(destFileName)) {
206
+ results.push({ name: fileName, type: fileType, content_preview: "(已摄入,跳过)", skipped: true });
46
207
  continue;
47
208
  }
48
- // 复制到 raw/
49
- const content = readFile_safe(filePath);
50
- if (!content) {
51
- results.push({ name: fileName, content_preview: "(无法读取文件)", skipped: true });
52
- continue;
209
+ if (fileType === "convertible") {
210
+ // ── 零依赖转换 ──
211
+ const mdContent = convertToMarkdown(filePath);
212
+ if (!mdContent) {
213
+ results.push({ name: fileName, type: fileType, content_preview: "(无法读取文件)", skipped: true });
214
+ continue;
215
+ }
216
+ const destPath = path.join(rawDir, destFileName);
217
+ fs.writeFileSync(destPath, mdContent, "utf-8");
218
+ if (!state.ingested.includes(destFileName)) {
219
+ state.ingested.push(destFileName);
220
+ }
221
+ results.push({
222
+ name: fileName,
223
+ type: "converted",
224
+ content_preview: mdContent.substring(0, 500),
225
+ note: `已转为 ${destFileName}`,
226
+ });
227
+ }
228
+ else if (fileType === "binary") {
229
+ // ── 原样存储(PDF/DOCX/XLSX) ──
230
+ const destPath = path.join(rawDir, fileName);
231
+ fs.copyFileSync(filePath, destPath);
232
+ if (!state.ingested.includes(fileName)) {
233
+ state.ingested.push(fileName);
234
+ }
235
+ results.push({
236
+ name: fileName,
237
+ type: "binary",
238
+ note: "原文件已存储,请直接读取 raw/ 下的文件",
239
+ });
53
240
  }
54
- fs.writeFileSync(destPath, content, "utf-8");
55
- // 更新状态
56
- if (!state.ingested.includes(fileName)) {
57
- state.ingested.push(fileName);
241
+ else if (fileType === "image") {
242
+ // ── 图片存储到 raw/assets/ ──
243
+ const destPath = path.join(assetsDir, fileName);
244
+ fs.copyFileSync(filePath, destPath);
245
+ if (!state.ingested.includes(fileName)) {
246
+ state.ingested.push(fileName);
247
+ }
248
+ results.push({
249
+ name: fileName,
250
+ type: "image",
251
+ note: "已存储到 raw/assets/,可直接查看",
252
+ });
58
253
  }
59
- results.push({
60
- name: fileName,
61
- content_preview: content.substring(0, 500),
62
- });
63
254
  ingested++;
64
255
  }
65
256
  // 保存状态
@@ -80,12 +80,15 @@ export async function handleSearch(params) {
80
80
  }
81
81
  // 至少有一点匹配才加入结果
82
82
  if (score > 0) {
83
+ // 类型权重:compiled(原始材料编译)排在 query-derived(问答生成)前面
84
+ const typeBoost = String(meta.type) === "query-derived" ? 0.9 : 1.0;
83
85
  results.push({
84
86
  title,
85
87
  file: fileName,
86
88
  tags: fileTags,
87
89
  summary,
88
- score: Math.round(score * 100) / 100,
90
+ score: Math.round(score * typeBoost * 100) / 100,
91
+ type: String(meta.type || "compiled"),
89
92
  });
90
93
  }
91
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wiki",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "卡帕西智能知识库 MCP 插件 — 一行安装,纯对话交互,Agent 自动帮你管理知识库、写有人味儿的原创文章",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,13 @@
12
12
  "start": "node dist/index.js",
13
13
  "dev": "tsc --watch"
14
14
  },
15
- "keywords": ["mcp", "wiki", "knowledge-base", "karpathy", "obsidian"],
15
+ "keywords": [
16
+ "mcp",
17
+ "wiki",
18
+ "knowledge-base",
19
+ "karpathy",
20
+ "obsidian"
21
+ ],
16
22
  "author": "",
17
23
  "license": "MIT",
18
24
  "dependencies": {