@lih-x-x/kmr 1.0.60 → 1.0.62

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
@@ -5,12 +5,13 @@
5
5
  ## 功能
6
6
 
7
7
  - **会议纪要提取**:发送飞书云文档链接给机器人,自动提取会议摘要、待办事项、风险项、关键共识、可复用知识
8
- - **飞书任务创建**:提取待办后自动发送确认消息,回复 `/confirm` 创建飞书任务,`/reject` 取消,3 分钟超时自动取消。自动通过群成员列表解析负责人并指派任务
9
- - **任务状态同步**:每小时自动从飞书拉取任务最新状态,查询时也会实时刷新。通过 `/task` 指令或 `kmr task` 命令查看所有任务详情
10
- - **首次启动自动引导**:无配置文件时自动初始化并打开 Web 设置页引导填写飞书凭证,配置完成后自动继续启动,无需手动 `kmr init`
8
+ - **任务闭环**:提取待办后自动发送确认消息,回复 `/confirm` 创建飞书任务,`/reject` 取消,3 分钟超时自动取消。自动通过群成员列表解析负责人并指派任务,同步回写会议记录
9
+ - **本地 AI · 零成本接入**:基于 [acpx](https://www.npmjs.com/package/acpx) 复用本地 Claude / Codex,接入飞书机器人实现私有化部署,数据不出本机,无额外 API 费用
10
+ - **历史信息搜索**:会议记录和任务均可通过 AI 对话查询;`/find` 指令与自然语言双支持,返回最相关的 3 条结果
11
+ - **指令 / 自然语言双支持**:`/list`、`/show`、`/del`、`/find`、`/task` 等精确指令,与自由对话文本均可使用,AI 理解意图自动路由到对应操作
11
12
  - **AI 自由对话**:发送非指令文本即可与 AI 自由对话,基于 acpx 持久化 session,按用户隔离,支持 claude 和 codex 两种 agent。AI 可直接调用 `kmr` 命令创建任务、查询记录,回复通过飞书机器人发送,处理中显示表情回应
12
13
  - **独立任务创建**:通过 `kmr create-task` 命令(或 AI 对话)直接创建飞书任务,支持关联到指定会议记录(`--meeting <id>`),无关联时存入独立任务记录
13
- - **历史会议查询**:通过 `/find` 指令用自然语言搜索历史会议记录
14
+ - **首次启动自动引导**:无配置文件时自动初始化并打开 Web 设置页引导填写飞书凭证,配置完成后自动继续启动,无需手动 `kmr init`
14
15
  - **记录管理**:`/list` 列出所有记录,`/show <id>` 查看详情,`/del <id>` 删除记录,`/task` 查看所有任务
15
16
  - **CLI 命令**:`kmr list`、`kmr show`、`kmr task`、`kmr del`、`kmr find` 等子命令可在终端直接操作,无需启动服务
16
17
  - **群聊智能识别**:群聊中非 @消息仅自动识别会议纪要文档链接(通过文档标题判断),其他操作需 @机器人
@@ -21,7 +22,7 @@
21
22
 
22
23
  ## 核心理念:不是记录员,而是分析师
23
24
 
24
- KMR 不做流水账式的会议记录搬运。每条提取结果都围绕两个核心目标:
25
+ KMR 不做流水账式的会议记录搬运。每条提取结果都围绕三个核心目标:
25
26
 
26
27
  ### 1. 溯源追责 — 关键共识与承诺
27
28
 
@@ -38,7 +39,13 @@ KMR 不做流水账式的会议记录搬运。每条提取结果都围绕两个
38
39
  }
39
40
  ```
40
41
 
41
- ### 2. 知识复用可提效的工具与方法
42
+ ### 2. 任务闭环会议待办变真实任务
43
+
44
+ > 会议待办不再悬空,从纪要到飞书任务全程自动。
45
+
46
+ 会议结束不是终点。KMR 提取待办后自动发起确认消息,回复 `/confirm` 即可在飞书创建任务并自动解析负责人,任务状态同步回写到会议记录——会开了,事就该定下来。
47
+
48
+ ### 3. 知识复用 — 可提效的工具与方法
42
49
 
43
50
  > 会上提到的工具用法、方法论、经验教训,下次用得上吗?
44
51
 
@@ -47,6 +47,15 @@ var UserResolver = class {
47
47
  console.error(`[user-resolver] \u52A0\u8F7D\u7FA4\u6210\u5458\u5931\u8D25:`, err.message);
48
48
  }
49
49
  }
50
+ /**
51
+ * 根据 open_id 反查姓名
52
+ */
53
+ resolveName(openId) {
54
+ for (const [name, id] of this.nameMap) {
55
+ if (id === openId) return name;
56
+ }
57
+ return void 0;
58
+ }
50
59
  /**
51
60
  * 获取当前所有已缓存的映射
52
61
  */
package/dist/cli.js CHANGED
@@ -6,9 +6,9 @@ async function checkUpdate() {
6
6
  try {
7
7
  const res = await fetch(`https://registry.npmjs.org/${"@lih-x-x/kmr"}/latest`, { signal: AbortSignal.timeout(3e3) });
8
8
  const data = await res.json();
9
- if (data.version && data.version !== "1.0.60") {
9
+ if (data.version && data.version !== "1.0.62") {
10
10
  console.log(`
11
- \u2B06\uFE0F \u65B0\u7248\u672C\u53EF\u7528: ${"1.0.60"} \u2192 ${data.version}`);
11
+ \u2B06\uFE0F \u65B0\u7248\u672C\u53EF\u7528: ${"1.0.62"} \u2192 ${data.version}`);
12
12
  console.log(` \u8FD0\u884C npm install -g ${"@lih-x-x/kmr"} \u66F4\u65B0
13
13
  `);
14
14
  }
@@ -56,7 +56,7 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
56
56
  kmr --help \u663E\u793A\u5E2E\u52A9
57
57
  `);
58
58
  } else if (command === "--version" || command === "-v") {
59
- console.log("1.0.60");
59
+ console.log("1.0.62");
60
60
  } else if (command === "list") {
61
61
  const { loadConfig } = await import("./config-WIQGW5OC.js");
62
62
  const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
@@ -163,7 +163,7 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
163
163
  const { loadConfig } = await import("./config-WIQGW5OC.js");
164
164
  const { createLarkClient } = await import("./client-DFBBDD77.js");
165
165
  const { TaskCreator } = await import("./taskCreator-TCI3VB5D.js");
166
- const { UserResolver } = await import("./userResolver-DTWOTVP6.js");
166
+ const { UserResolver } = await import("./userResolver-SDRJIVT2.js");
167
167
  const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
168
168
  const config = loadConfig();
169
169
  const client = createLarkClient(config);
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  } from "./chunk-XQVLJTP4.js";
29
29
  import {
30
30
  UserResolver
31
- } from "./chunk-2IG4NXRZ.js";
31
+ } from "./chunk-23UVH7BV.js";
32
32
 
33
33
  // src/index.ts
34
34
  import fs3 from "fs";
@@ -590,6 +590,7 @@ var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A
590
590
  - \u4F60\u8FD0\u884C\u5728\u98DE\u4E66\u673A\u5668\u4EBA\u4E2D\uFF0C\u4F60\u7684\u56DE\u590D\u4F1A\u76F4\u63A5\u4F5C\u4E3A\u98DE\u4E66\u6D88\u606F\u53D1\u9001\u5230\u7528\u6237\u6240\u5728\u7684\u5BF9\u8BDD\u4E2D
591
591
  - \u4E0D\u8981\u8BF4"\u6211\u6CA1\u6709\u53D1\u6D88\u606F/\u521B\u5EFA\u4EFB\u52A1\u7684\u80FD\u529B"\u2014\u2014\u4F60\u53EF\u4EE5\u901A\u8FC7 kmr \u547D\u4EE4\u6765\u5B8C\u6210\u8FD9\u4E9B\u64CD\u4F5C
592
592
  - \u4F60\u6709\u80FD\u529B\u901A\u8FC7 kmr \u547D\u4EE4\u67E5\u8BE2\u6570\u636E\u3001\u521B\u5EFA\u4EFB\u52A1\u3001\u7BA1\u7406\u8BB0\u5F55
593
+ - \u652F\u6301\u7684 agent provider\uFF1Aclaude\u3001codex\u3001cursor\u3001kiro\u3001openclaw\u3001opencode\u3001gemini\uFF08\u7531\u914D\u7F6E\u51B3\u5B9A\uFF0C\u8FD0\u884C\u65F6\u4E0D\u53EF\u5207\u6362\uFF09
593
594
 
594
595
  \u53EF\u7528\u7684 kmr \u547D\u4EE4\uFF08\u901A\u8FC7 bash \u6267\u884C\uFF09\uFF1A
595
596
  - kmr list \u2014 \u5217\u51FA\u6240\u6709\u4F1A\u8BAE\u8BB0\u5F55
@@ -597,11 +598,12 @@ var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A
597
598
  - kmr find <query> \u2014 \u641C\u7D22\u4F1A\u8BAE\u8BB0\u5F55
598
599
  - kmr task \u2014 \u67E5\u770B\u6240\u6709\u4EFB\u52A1
599
600
  - kmr del <id> \u2014 \u5220\u9664\u8BB0\u5F55
600
- - kmr create-task <\u4EFB\u52A1\u6807\u9898> [--due YYYY-MM-DD] [--assignee \u59D3\u540D] [--desc \u63CF\u8FF0] [--meeting <id>] \u2014 \u521B\u5EFA\u98DE\u4E66\u4EFB\u52A1\u5E76\u4FDD\u5B58\u5230\u672C\u5730\uFF08--meeting \u53EF\u5173\u8054\u5230\u6307\u5B9A\u4F1A\u8BAE\u8BB0\u5F55\uFF09
601
+ - kmr create-task <\u4EFB\u52A1\u6807\u9898> [--due YYYY-MM-DD] [--assignee \u59D3\u540D] [--desc \u63CF\u8FF0] [--meeting <id>] \u2014 \u521B\u5EFA\u98DE\u4E66\u4EFB\u52A1\u5E76\u4FDD\u5B58\u5230\u672C\u5730\uFF08--meeting \u53EF\u5173\u8054\u5230\u6307\u5B9A\u4F1A\u8BAE\u8BB0\u5F55\uFF0C\u82E5\u6709\uFF0C\u53EF\u9009\uFF09
601
602
 
602
603
  \u64CD\u4F5C\u6307\u5357\uFF1A
603
604
  - \u5F53\u7528\u6237\u8981\u6C42\u521B\u5EFA\u4EFB\u52A1\u65F6\uFF0C\u76F4\u63A5\u6267\u884C kmr create-task \u547D\u4EE4\uFF0C\u4F8B\u5982\uFF1Akmr create-task "\u5B8C\u6210\u65B9\u6848\u8BBE\u8BA1" --due 2026-05-15 --assignee \u5F20\u4E09\uFF1B\u5982\u679C\u80FD\u5173\u8054\u5230\u67D0\u6761\u4F1A\u8BAE\u8BB0\u5F55\uFF0C\u52A0\u4E0A --meeting <id>
604
605
  - \u5F53\u7528\u6237\u95EE\u5230\u4F1A\u8BAE/\u4EFB\u52A1\u76F8\u5173\u7684\u95EE\u9898\u65F6\uFF0C\u5148\u6267\u884C kmr \u547D\u4EE4\u83B7\u53D6\u6570\u636E\u518D\u56DE\u7B54
606
+ - \u5F53\u7528\u6237\u95EE\u597D\u6216\u8005\u8BF7\u4F60\u4ECB\u7ECD\u65F6\uFF0C\u8981\u8F83\u4E3A\u8BE6\u7EC6\u5730\u4ECB\u7ECD\u81EA\u5DF1\u548C\u529F\u80FD\uFF0C\u5E2E\u52A9\u7528\u6237\u7406\u89E3\u4F60\u80FD\u505A\u4EC0\u4E48\u4EE5\u53CA\u4ED6\u80FD\u600E\u4E48\u4F7F\u7528
605
607
  - \u6BCF\u6761\u6D88\u606F\u5F00\u5934\u4F1A\u6709\u3010\u5F53\u524D\u73AF\u5883\u3011\u63D0\u793A\uFF0C\u544A\u8BC9\u4F60\u662F\u7FA4\u804A\u8FD8\u662F\u79C1\u804A\uFF1A
606
608
  - \u7FA4\u804A\uFF1A\u4F60\u7684\u56DE\u590D\u76F4\u63A5\u53D1\u5230\u7FA4\u91CC\uFF0C\u5982\u679C\u9700\u8981\u6307\u5B9A\u5BF9\u8C61\u5BF9\u8BDD\uFF0C\u53EF\u4EE5\u7528 @\u59D3\u540D \u6765\u63D0\u9192\u7FA4\u5185\u6210\u5458\uFF0C\u4E0D\u9700\u8981\u8BA9\u7528\u6237\u8F6C\u53D1
607
609
  - \u79C1\u804A\uFF1A\u65E0\u6CD5 @\u4ED6\u4EBA\uFF0C\u5982\u9700\u901A\u77E5\u4ED6\u4EBA\u8BF7\u62DF\u5236\u6D88\u606F\u5185\u5BB9\u5EFA\u8BAE\u7528\u6237\u8F6C\u53D1
@@ -630,10 +632,10 @@ var SessionManager = class {
630
632
  sessionDir;
631
633
  async handleMessage(userId, text, context) {
632
634
  const sessionName = await this.ensureSession(userId);
633
- console.log(`[session] \u53D1\u9001\u6D88\u606F\u5230 session: ${sessionName}`);
634
635
  let fullText = text;
635
636
  if (context) {
636
- const env = context.isGroup ? "\u3010\u5F53\u524D\u73AF\u5883\uFF1A\u7FA4\u804A\u3002\u4F60\u7684\u56DE\u590D\u4F1A\u53D1\u9001\u5230\u7FA4\u91CC\uFF0C\u5982\u679C\u9700\u8981\u6307\u5B9A\u5BF9\u8C61\u5BF9\u8BDD\uFF0C\u53EF\u4EE5\u76F4\u63A5\u7528 @\u59D3\u540D \u6765\u63D0\u9192\u7FA4\u5185\u6210\u5458\u3002\u3011" : "\u3010\u5F53\u524D\u73AF\u5883\uFF1A\u79C1\u804A\u3002\u65E0\u6CD5\u76F4\u63A5 @\u4ED6\u4EBA\uFF0C\u5982\u9700\u901A\u77E5\u4ED6\u4EBA\u8BF7\u62DF\u5236\u6D88\u606F\u5185\u5BB9\u8BA9\u7528\u6237\u8F6C\u53D1\u3002\u3011";
637
+ const senderHint = context.senderName ? `\u53D1\u9001\u8005\uFF1A${context.senderName}\uFF08open_id: ${userId}\uFF09` : `\u53D1\u9001\u8005 open_id: ${userId}`;
638
+ const env = context.isGroup ? `\u3010\u5F53\u524D\u73AF\u5883\uFF1A\u7FA4\u804A\u3002${senderHint}\u3002\u4F60\u7684\u56DE\u590D\u4F1A\u53D1\u9001\u5230\u7FA4\u91CC\uFF0C\u5982\u679C\u9700\u8981\u6307\u5B9A\u5BF9\u8C61\u5BF9\u8BDD\uFF0C\u53EF\u4EE5\u76F4\u63A5\u7528 @\u59D3\u540D \u6765\u63D0\u9192\u7FA4\u5185\u6210\u5458\u3002\u3011` : `\u3010\u5F53\u524D\u73AF\u5883\uFF1A\u79C1\u804A\u3002${senderHint}\u3002\u65E0\u6CD5\u76F4\u63A5 @\u4ED6\u4EBA\uFF0C\u5982\u9700\u901A\u77E5\u4ED6\u4EBA\u8BF7\u62DF\u5236\u6D88\u606F\u5185\u5BB9\u8BA9\u7528\u6237\u8F6C\u53D1\u3002\u3011`;
637
639
  fullText = `${env}
638
640
  ${text}`;
639
641
  }
@@ -643,7 +645,8 @@ ${text}`;
643
645
  }
644
646
  getAgentCmd() {
645
647
  const provider = getAgentProvider();
646
- return provider === "codex" ? "codex" : provider;
648
+ const supported = ["claude", "codex", "cursor", "kiro", "openclaw", "opencode", "gemini"];
649
+ return supported.includes(provider) ? provider : "claude";
647
650
  }
648
651
  /**
649
652
  * 获取当前所有 claude-agent-acp 进程的 PID
@@ -686,7 +689,6 @@ ${text}`;
686
689
  (line) => !line.includes("[closed]") && line.split(" ")[1]?.trim() === sessionName
687
690
  );
688
691
  if (hasExisting) {
689
- console.log(`[session] \u53D1\u73B0\u6B8B\u7559 session: ${sessionName}\uFF0C\u5148\u5173\u95ED`);
690
692
  try {
691
693
  await execFileAsync("acpx", [agentCmd, "sessions", "close", sessionName], {
692
694
  timeout: 1e4,
@@ -806,11 +808,9 @@ ${text}`;
806
808
  async closeAll() {
807
809
  const userIds = [...this.activeSessions.keys()];
808
810
  if (userIds.length > 0) {
809
- console.log(`[session] \u6B63\u5728\u5173\u95ED ${userIds.length} \u4E2A\u6D3B\u8DC3 session...`);
810
811
  await Promise.all(userIds.map((uid) => this.closeSession(uid)));
811
812
  }
812
813
  if (this.ownedPids.size > 0) {
813
- console.log(`[session] \u6B63\u5728\u6E05\u7406 ${this.ownedPids.size} \u4E2A agent \u8FDB\u7A0B...`);
814
814
  for (const pid of this.ownedPids) {
815
815
  try {
816
816
  process.kill(pid, "SIGTERM");
@@ -874,7 +874,7 @@ async function main() {
874
874
  const botOpenId = await getBotOpenId(client);
875
875
  const dispatcher = createEventDispatcher(botOpenId, async (messageId, text, chatId, senderId, meta) => {
876
876
  const parsed = parseMessage(text);
877
- console.log(`[route] type=${parsed.type}, isGroup=${meta.isGroup}, isMentioned=${meta.isMentioned}`);
877
+ console.log(`[message] \u6536\u5230\u6D88\u606F: "${text}" (chatId=${chatId}, senderId=${senderId})`);
878
878
  if (meta.isGroup && !meta.isMentioned && parsed.type === "document_link" /* DOCUMENT_LINK */) {
879
879
  const title = await docReader.getDocumentTitle(parsed.documentId);
880
880
  const isMeetingNote = /纪要|会议记录|meeting/i.test(title) || /from=vc_assistant/.test(text);
@@ -1060,7 +1060,8 @@ async function main() {
1060
1060
  }
1061
1061
  console.log(`[process] \u81EA\u7531\u5BF9\u8BDD, userId=${senderId}`);
1062
1062
  const reactionId = await messenger.addReaction(messageId, "OnIt");
1063
- const reply = await sessionManager.handleMessage(senderId, text, { isGroup: meta.isGroup });
1063
+ const senderName = userResolver.resolveName(senderId);
1064
+ const reply = await sessionManager.handleMessage(senderId, text, { isGroup: meta.isGroup, senderName });
1064
1065
  await messenger.removeReaction(messageId, reactionId);
1065
1066
  await messenger.replyText(messageId, reply);
1066
1067
  break;
@@ -290,8 +290,9 @@ async function renderSettings() {
290
290
  '<div class="form-field">' +
291
291
  '<label class="form-label">Provider</label>' +
292
292
  '<select class="form-select" id="cfg-provider">' +
293
- '<option value="claude"' + (config.agent.provider === 'claude' ? ' selected' : '') + '>claude</option>' +
294
- '<option value="codex"' + (config.agent.provider === 'codex' ? ' selected' : '') + '>codex</option>' +
293
+ ['claude', 'codex', 'cursor', 'kiro', 'openclaw', 'opencode', 'gemini'].map(function(p) {
294
+ return '<option value="' + p + '"' + (config.agent.provider === p ? ' selected' : '') + '>' + p + '</option>';
295
+ }).join('') +
295
296
  '</select>' +
296
297
  '</div>' +
297
298
  '<div class="form-field">' +
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  UserResolver
3
- } from "./chunk-2IG4NXRZ.js";
3
+ } from "./chunk-23UVH7BV.js";
4
4
  export {
5
5
  UserResolver
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lih-x-x/kmr",
3
- "version": "1.0.60",
3
+ "version": "1.0.62",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {