@lih-x-x/kmr 1.0.23 → 1.0.25
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/chunk-KJPAKNVA.js +171 -0
- package/dist/chunk-SGTENG37.js +38 -0
- package/dist/chunk-TUCCS6QJ.js +50 -0
- package/dist/claudeCode-PPRQZG5K.js +7 -0
- package/dist/cli.js +61 -1
- package/dist/handler-46CQQIA2.js +6 -0
- package/dist/index.js +71 -304
- package/dist/jsonStore-AL73KEUG.js +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAgentProvider
|
|
3
|
+
} from "./chunk-ZXGVA5QX.js";
|
|
4
|
+
|
|
5
|
+
// src/agent/claudeCode.ts
|
|
6
|
+
import { execFile } from "child_process";
|
|
7
|
+
import { promisify } from "util";
|
|
8
|
+
|
|
9
|
+
// src/agent/prompt.ts
|
|
10
|
+
var EXTRACT_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u9AD8\u7EA7\u4F1A\u8BAE\u5206\u6790\u5E08\uFF0C\u4E0D\u662F\u8BB0\u5F55\u5458\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u4ECE\u4F1A\u8BAE\u7EAA\u8981\u4E2D\u63D0\u70BC\u771F\u6B63\u6709\u4EF7\u503C\u7684\u4FE1\u606F\uFF0C\u800C\u4E0D\u662F\u6D41\u6C34\u8D26\u5F0F\u590D\u8FF0\u3002
|
|
11
|
+
|
|
12
|
+
\u4F60\u9700\u8981\u5E26\u7740\u4E24\u4E2A\u6838\u5FC3\u76EE\u6807\u5206\u6790\u4F1A\u8BAE\u5185\u5BB9\uFF1A
|
|
13
|
+
1. **\u6EAF\u6E90\u8FFD\u8D23**\uFF1A\u63D0\u53D6\u6240\u6709\u660E\u786E\u7684\u627F\u8BFA\u3001\u7EA6\u5B9A\u3001\u51B3\u8BAE\u2014\u2014\u8C01\u7B54\u5E94\u4E86\u4EC0\u4E48\u3001\u4EC0\u4E48\u65F6\u5019\u5B8C\u6210\u3001\u8FBE\u6210\u4E86\u4EC0\u4E48\u5171\u8BC6\u3002\u5177\u4F53\u7684\u4EBA\u548C\u5177\u4F53\u7684\u65F6\u95F4\u662F\u6838\u5FC3\u951A\u70B9\uFF0C\u8FD9\u4E9B\u4FE1\u606F\u7528\u4E8E\u4E8B\u540E\u5FEB\u901F\u6EAF\u6E90\u3002
|
|
14
|
+
2. **\u77E5\u8BC6\u590D\u7528**\uFF1A\u63D0\u53D6\u4F1A\u8BAE\u4E2D\u63D0\u5230\u7684\u5DE5\u5177\u7528\u6CD5\u3001\u65B9\u6CD5\u8BBA\u3001\u7ECF\u9A8C\u6559\u8BAD\u3001\u6700\u4F73\u5B9E\u8DF5\u2014\u2014\u8FD9\u4E9B\u53EF\u4EE5\u63D0\u5347\u5DE5\u4F5C\u6548\u7387\u7684\u53EF\u590D\u7528\u77E5\u8BC6\u3002
|
|
15
|
+
|
|
16
|
+
\u5206\u6790\u8981\u6C42\uFF1A
|
|
17
|
+
- participants\uFF1A\u5FC5\u987B\u5217\u51FA\u7EAA\u8981\u6587\u6863\u4E2D\u8BB0\u5F55\u7684\u53C2\u4E0E\u8BA8\u8BBA\u548C\u51B3\u7B56\u6216\u8005\u88AB\u63D0\u53CA\u7684\u6838\u5FC3\u6210\u5458\u540D\u5B57
|
|
18
|
+
- keyPoints\uFF1A\u53EA\u4FDD\u7559\u6700\u5173\u952E\u7684 3-8 \u4E2A\u51B3\u7B56\u6027\u8981\u70B9\uFF0C\u7F57\u5217\u7B80\u8981\u8BA8\u8BBA\u8FC7\u7A0B
|
|
19
|
+
- todos\uFF1A\u53EA\u63D0\u53D6\u6709\u660E\u786E\u8D1F\u8D23\u4EBA\u548C\u53EF\u6267\u884C\u5185\u5BB9\u7684\u5F85\u529E\uFF0C\u4E0D\u8981\u6A21\u7CCA\u7684"\u540E\u7EED\u8DDF\u8FDB"
|
|
20
|
+
- risks\uFF1A\u53EA\u63D0\u53D6\u771F\u6B63\u7684\u98CE\u9669\u548C\u672A\u51B3\u4E8B\u9879\uFF0C\u8981\u6709\u5177\u4F53\u7684\u7F13\u89E3\u65B9\u6848
|
|
21
|
+
- commitments\u3010\u6700\u91CD\u8981\u3011\uFF1A\u63D0\u53D6\u6240\u6709\u627F\u8BFA\u3001\u7EA6\u5B9A\u3001\u51B3\u8BAE\u3002context \u5B57\u6BB5\u5FC5\u987B\u5199\u4F60\u7684\u5206\u6790\u2014\u2014\u4E3A\u4EC0\u4E48\u4F1A\u8FBE\u6210\u8FD9\u4E2A\u5171\u8BC6\uFF0C\u80CC\u666F\u662F\u4EC0\u4E48\uFF0C\u8C01\u8FBE\u6210\u7684\uFF0C\u4E0D\u80FD\u53EA\u642C\u8FD0\u539F\u6587
|
|
22
|
+
- reusableInsights\u3010\u6700\u91CD\u8981\u3011\uFF1A\u63D0\u53D6\u53EF\u590D\u7528\u7684\u77E5\u8BC6\u3002scenario \u5B57\u6BB5\u5FC5\u987B\u5199\u8FD9\u4E2A\u77E5\u8BC6\u5728\u4EC0\u4E48\u573A\u666F\u4E0B\u6709\u7528\uFF0C\u4E0D\u80FD\u6CDB\u6CDB\u800C\u8C08\uFF0C\u8981\u6709\u5177\u4F53\u64CD\u4F5C\u5B9E\u8DF5\u6B65\u9AA4\u6307\u5BFC
|
|
23
|
+
|
|
24
|
+
\u3010\u91CD\u8981\u3011\u76F4\u63A5\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u89E3\u91CA\u6216 markdown \u4EE3\u7801\u5757\u3002\u4EC5\u8F93\u51FA\u4EE5\u4E0B\u683C\u5F0F\u7684 JSON\uFF1A
|
|
25
|
+
{"summary":{"title":"\u4F1A\u8BAE\u6807\u9898","date":"YYYY-MM-DD","participants":["\u53C2\u4E0E\u4EBA1"],"keyPoints":["\u51B3\u7B56\u6027\u8981\u70B9\uFF0C\u4E0D\u662F\u6D41\u6C34\u8D26"]},"todos":[{"content":"\u5177\u4F53\u53EF\u6267\u884C\u5185\u5BB9","owner":"\u8D1F\u8D23\u4EBA","deadline":"YYYY-MM-DD","status":"pending"}],"risks":[{"description":"\u5177\u4F53\u98CE\u9669","severity":"high|medium|low","mitigation":"\u5177\u4F53\u7F13\u89E3\u65B9\u6848"}],"projectRelations":[{"project":"\u9879\u76EE\u540D","relation":"\u5173\u8054\u63CF\u8FF0"}],"commitments":[{"content":"\u627F\u8BFA/\u7EA6\u5B9A/\u51B3\u8BAE\u7684\u5177\u4F53\u5185\u5BB9","participants":["\u76F8\u5173\u65B91","\u76F8\u5173\u65B92"],"deadline":"YYYY-MM-DD","context":"\u4F60\u7684\u5206\u6790\uFF1A\u4E3A\u4EC0\u4E48\u8FBE\u6210\u8FD9\u4E2A\u5171\u8BC6\uFF0C\u80CC\u666F\u548C\u5F71\u54CD\u662F\u4EC0\u4E48"}],"reusableInsights":[{"category":"tool|methodology|lesson|best-practice","content":"\u77E5\u8BC6\u70B9\u7684\u5177\u4F53\u5185\u5BB9","scenario":"\u5728\u4EC0\u4E48\u573A\u666F\u4E0B\u53EF\u4EE5\u590D\u7528\u8FD9\u4E2A\u77E5\u8BC6","source":"\u8C01\u63D0\u51FA\u7684"}]}
|
|
26
|
+
|
|
27
|
+
\u4F1A\u8BAE\u7EAA\u8981\u5185\u5BB9\uFF1A
|
|
28
|
+
`;
|
|
29
|
+
var SEARCH_KEYWORDS_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u641C\u7D22\u52A9\u624B\u3002\u7528\u6237\u60F3\u67E5\u627E\u5386\u53F2\u4F1A\u8BAE\u8BB0\u5F55\uFF0C\u8BF7\u5C06\u7528\u6237\u7684\u81EA\u7136\u8BED\u8A00\u67E5\u8BE2\u8F6C\u6362\u4E3A\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868\u3002
|
|
30
|
+
|
|
31
|
+
\u8981\u6C42\uFF1A
|
|
32
|
+
- \u8FD4\u56DE 3-5 \u4E2A\u6700\u76F8\u5173\u7684\u5173\u952E\u8BCD
|
|
33
|
+
- \u4EC5\u8FD4\u56DE JSON \u6570\u7EC4\u683C\u5F0F\uFF0C\u4E0D\u8981\u5176\u4ED6\u5185\u5BB9
|
|
34
|
+
- \u793A\u4F8B\uFF1A["\u5173\u952E\u8BCD1", "\u5173\u952E\u8BCD2", "\u5173\u952E\u8BCD3"]
|
|
35
|
+
|
|
36
|
+
\u7528\u6237\u67E5\u8BE2\uFF1A`;
|
|
37
|
+
var RANK_RESULTS_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u641C\u7D22\u6392\u5E8F\u52A9\u624B\u3002\u7528\u6237\u67E5\u8BE2\u548C\u5019\u9009\u4F1A\u8BAE\u8BB0\u5F55\u5982\u4E0B\uFF0C\u8BF7\u6309\u76F8\u5173\u6027\u4ECE\u9AD8\u5230\u4F4E\u6392\u5E8F\u3002
|
|
38
|
+
|
|
39
|
+
\u4EC5\u8FD4\u56DE\u6392\u5E8F\u540E\u7684\u4F1A\u8BAE ID JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u5176\u4ED6\u5185\u5BB9\u3002
|
|
40
|
+
\u793A\u4F8B\uFF1A["meeting_003", "meeting_001"]
|
|
41
|
+
|
|
42
|
+
\u7528\u6237\u67E5\u8BE2\uFF1A`;
|
|
43
|
+
|
|
44
|
+
// src/utils/claudeEnv.ts
|
|
45
|
+
import fs from "fs";
|
|
46
|
+
import path from "path";
|
|
47
|
+
import os from "os";
|
|
48
|
+
var cachedEnv = null;
|
|
49
|
+
function getClaudeEnv() {
|
|
50
|
+
if (cachedEnv) return cachedEnv;
|
|
51
|
+
const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
52
|
+
let settingsEnv = {};
|
|
53
|
+
try {
|
|
54
|
+
const content = fs.readFileSync(settingsPath, "utf-8");
|
|
55
|
+
const settings = JSON.parse(content);
|
|
56
|
+
if (settings.env && typeof settings.env === "object") {
|
|
57
|
+
settingsEnv = settings.env;
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
cachedEnv = { ...settingsEnv };
|
|
62
|
+
for (const key of Object.keys(settingsEnv)) {
|
|
63
|
+
if (process.env[key]) {
|
|
64
|
+
cachedEnv[key] = process.env[key];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return cachedEnv;
|
|
68
|
+
}
|
|
69
|
+
function getExecEnv() {
|
|
70
|
+
const claudeEnv = getClaudeEnv();
|
|
71
|
+
return { ...process.env, ...claudeEnv };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/agent/claudeCode.ts
|
|
75
|
+
var execFileAsync = promisify(execFile);
|
|
76
|
+
var ClaudeCodeProvider = class {
|
|
77
|
+
constructor(timeout = 12e4) {
|
|
78
|
+
this.timeout = timeout;
|
|
79
|
+
}
|
|
80
|
+
timeout;
|
|
81
|
+
name = "claude";
|
|
82
|
+
async extract(content) {
|
|
83
|
+
const prompt = EXTRACT_PROMPT + content;
|
|
84
|
+
console.log(`[ClaudeCodeProvider] \u63D0\u53D6\u4FE1\u606F\u7684 prompt:
|
|
85
|
+
${prompt}
|
|
86
|
+
--- End of Prompt ---`);
|
|
87
|
+
const output = await this.callAcpx(prompt);
|
|
88
|
+
const parsed = JSON.parse(output);
|
|
89
|
+
return {
|
|
90
|
+
id: `meeting_${Date.now()}`,
|
|
91
|
+
documentUrl: "",
|
|
92
|
+
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93
|
+
summary: parsed.summary,
|
|
94
|
+
todos: (parsed.todos || []).map((t) => ({ ...t, status: "invalid_cause_no_task" })),
|
|
95
|
+
risks: parsed.risks || [],
|
|
96
|
+
projectRelations: parsed.projectRelations || [],
|
|
97
|
+
commitments: parsed.commitments || [],
|
|
98
|
+
reusableInsights: parsed.reusableInsights || [],
|
|
99
|
+
rawContent: content
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async searchKeywords(query) {
|
|
103
|
+
const prompt = SEARCH_KEYWORDS_PROMPT + query;
|
|
104
|
+
const output = await this.callAcpx(prompt);
|
|
105
|
+
return JSON.parse(output);
|
|
106
|
+
}
|
|
107
|
+
async rankResults(query, candidates) {
|
|
108
|
+
if (candidates.length <= 1) return candidates;
|
|
109
|
+
const summaries = candidates.map((c) => ({
|
|
110
|
+
id: c.id,
|
|
111
|
+
title: c.summary.title,
|
|
112
|
+
keyPoints: c.summary.keyPoints
|
|
113
|
+
}));
|
|
114
|
+
const prompt = RANK_RESULTS_PROMPT + query + "\n\n\u5019\u9009\u4F1A\u8BAE\uFF1A\n" + JSON.stringify(summaries, null, 2);
|
|
115
|
+
const output = await this.callAcpx(prompt);
|
|
116
|
+
const rankedIds = JSON.parse(output);
|
|
117
|
+
const indexed = new Map(candidates.map((c) => [c.id, c]));
|
|
118
|
+
return rankedIds.map((id) => indexed.get(id)).filter(Boolean);
|
|
119
|
+
}
|
|
120
|
+
async callAcpx(prompt) {
|
|
121
|
+
const provider = getAgentProvider();
|
|
122
|
+
const agentCmd = provider === "codex" ? "codex" : provider;
|
|
123
|
+
console.log(`[agent] \u4F7F\u7528 ${agentCmd} \u6267\u884C\u63D0\u53D6`);
|
|
124
|
+
try {
|
|
125
|
+
const { stdout } = await execFileAsync("acpx", ["--allowed-tools", "", agentCmd, "exec", prompt], {
|
|
126
|
+
timeout: this.timeout,
|
|
127
|
+
maxBuffer: 1024 * 1024,
|
|
128
|
+
env: getExecEnv()
|
|
129
|
+
});
|
|
130
|
+
return this.extractJson(stdout);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err.killed) {
|
|
133
|
+
throw new Error(`acpx \u8C03\u7528\u8D85\u65F6 (${this.timeout}ms)`);
|
|
134
|
+
}
|
|
135
|
+
throw new Error(`acpx \u8C03\u7528\u5931\u8D25: ${err.message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
extractJson(output) {
|
|
139
|
+
const lines = output.split("\n");
|
|
140
|
+
let jsonStart = -1;
|
|
141
|
+
let jsonEnd = -1;
|
|
142
|
+
for (let i = 0; i < lines.length; i++) {
|
|
143
|
+
const trimmed = lines[i].trim();
|
|
144
|
+
if (jsonStart === -1 && (trimmed.startsWith("{") || trimmed.startsWith("["))) {
|
|
145
|
+
jsonStart = i;
|
|
146
|
+
}
|
|
147
|
+
if (trimmed.endsWith("}") || trimmed.endsWith("]")) {
|
|
148
|
+
jsonEnd = i;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (jsonStart !== -1 && jsonEnd >= jsonStart) {
|
|
152
|
+
const candidate = lines.slice(jsonStart, jsonEnd + 1).join("\n").trim();
|
|
153
|
+
try {
|
|
154
|
+
JSON.parse(candidate);
|
|
155
|
+
return candidate;
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const filtered = lines.filter((line) => {
|
|
160
|
+
const t = line.trim();
|
|
161
|
+
return t.length > 0 && !t.startsWith("[client]") && !t.startsWith("[tool]") && !t.startsWith("[thinking]") && !t.startsWith("[done]") && !t.startsWith("[error]") && !t.startsWith("[warn]") && !t.startsWith("[info]");
|
|
162
|
+
}).join("\n").trim();
|
|
163
|
+
if (filtered.length > 0) return filtered;
|
|
164
|
+
return output.trim();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
getExecEnv,
|
|
170
|
+
ClaudeCodeProvider
|
|
171
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/query/finder.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
async function grepMeetings(dataDir, keywords) {
|
|
5
|
+
if (!fs.existsSync(dataDir)) return [];
|
|
6
|
+
const files = fs.readdirSync(dataDir).filter((f) => f.endsWith(".json"));
|
|
7
|
+
const matches = [];
|
|
8
|
+
for (const file of files) {
|
|
9
|
+
const filePath = path.join(dataDir, file);
|
|
10
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
11
|
+
const hasMatch = keywords.some((kw) => raw.includes(kw));
|
|
12
|
+
if (hasMatch) {
|
|
13
|
+
matches.push(JSON.parse(raw));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return matches;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/query/handler.ts
|
|
20
|
+
var QueryHandler = class {
|
|
21
|
+
constructor(agent, dataDir) {
|
|
22
|
+
this.agent = agent;
|
|
23
|
+
this.dataDir = dataDir;
|
|
24
|
+
}
|
|
25
|
+
agent;
|
|
26
|
+
dataDir;
|
|
27
|
+
async find(query) {
|
|
28
|
+
const keywords = await this.agent.searchKeywords(query);
|
|
29
|
+
const candidates = await grepMeetings(this.dataDir, keywords);
|
|
30
|
+
if (candidates.length === 0) return [];
|
|
31
|
+
const ranked = await this.agent.rankResults(query, candidates);
|
|
32
|
+
return ranked.slice(0, 3);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
QueryHandler
|
|
38
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/storage/jsonStore.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var JsonStore = class {
|
|
5
|
+
constructor(dataDir) {
|
|
6
|
+
this.dataDir = dataDir;
|
|
7
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
dataDir;
|
|
10
|
+
async save(record) {
|
|
11
|
+
const date = record.summary.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
12
|
+
const fileName = `${date}_${record.id}.json`;
|
|
13
|
+
const filePath = path.join(this.dataDir, fileName);
|
|
14
|
+
fs.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
15
|
+
return filePath;
|
|
16
|
+
}
|
|
17
|
+
async load(id) {
|
|
18
|
+
const files = this.getFiles();
|
|
19
|
+
const target = files.find((f) => f.includes(id));
|
|
20
|
+
if (!target) return null;
|
|
21
|
+
const raw = fs.readFileSync(target, "utf-8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
async list() {
|
|
25
|
+
const files = this.getFiles();
|
|
26
|
+
return files.map((f) => JSON.parse(fs.readFileSync(f, "utf-8")));
|
|
27
|
+
}
|
|
28
|
+
async search(keywords) {
|
|
29
|
+
const all = await this.list();
|
|
30
|
+
return all.filter((record) => {
|
|
31
|
+
const text = record.rawContent + " " + record.summary.title + " " + record.summary.keyPoints.join(" ");
|
|
32
|
+
return keywords.some((kw) => text.includes(kw));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async delete(id) {
|
|
36
|
+
const files = this.getFiles();
|
|
37
|
+
const target = files.find((f) => f.includes(id));
|
|
38
|
+
if (!target) return false;
|
|
39
|
+
fs.unlinkSync(target);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
getFiles() {
|
|
43
|
+
if (!fs.existsSync(this.dataDir)) return [];
|
|
44
|
+
return fs.readdirSync(this.dataDir).filter((f) => f.endsWith(".json")).map((f) => path.join(this.dataDir, f));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
JsonStore
|
|
50
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -49,14 +49,74 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
49
49
|
|
|
50
50
|
\u7528\u6CD5:
|
|
51
51
|
kmr init \u521D\u59CB\u5316\u914D\u7F6E\u76EE\u5F55 ~/.kmr/
|
|
52
|
-
kmr --help \u663E\u793A\u5E2E\u52A9
|
|
53
52
|
kmr \u542F\u52A8\u670D\u52A1\uFF08\u98DE\u4E66\u673A\u5668\u4EBA + Web \u7BA1\u7406\u754C\u9762\uFF09
|
|
53
|
+
kmr list \u5217\u51FA\u6240\u6709\u4F1A\u8BAE\u8BB0\u5F55
|
|
54
|
+
kmr show <id> \u67E5\u770B\u8BB0\u5F55\u8BE6\u60C5
|
|
55
|
+
kmr del <id> \u5220\u9664\u8BB0\u5F55
|
|
56
|
+
kmr find <q> \u641C\u7D22\u4F1A\u8BAE\u8BB0\u5F55
|
|
57
|
+
kmr --help \u663E\u793A\u5E2E\u52A9
|
|
54
58
|
`);
|
|
55
59
|
} else if (command === "--version" || command === "-v") {
|
|
56
60
|
const { createRequire } = await import("module");
|
|
57
61
|
const require2 = createRequire(import.meta.url);
|
|
58
62
|
const pkg = require2("../package.json");
|
|
59
63
|
console.log(pkg.version);
|
|
64
|
+
} else if (command === "list") {
|
|
65
|
+
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
66
|
+
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
67
|
+
const config = loadConfig();
|
|
68
|
+
const store = new JsonStore(config.storage.dataDir);
|
|
69
|
+
const all = await store.list();
|
|
70
|
+
all.sort((a, b) => b.extractedAt.localeCompare(a.extractedAt));
|
|
71
|
+
if (all.length === 0) {
|
|
72
|
+
console.log("\u6682\u65E0\u4F1A\u8BAE\u8BB0\u5F55");
|
|
73
|
+
} else {
|
|
74
|
+
for (const m of all) console.log(`${m.id} ${m.summary.title} ${m.summary.date}`);
|
|
75
|
+
}
|
|
76
|
+
} else if (command === "show") {
|
|
77
|
+
const id = argv[3];
|
|
78
|
+
if (!id) {
|
|
79
|
+
console.error("\u7528\u6CD5: kmr show <id>");
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
83
|
+
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
84
|
+
const store = new JsonStore(loadConfig().storage.dataDir);
|
|
85
|
+
const record = await store.load(id);
|
|
86
|
+
if (!record) {
|
|
87
|
+
console.error(`\u672A\u627E\u5230\u8BB0\u5F55: ${id}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
console.log(JSON.stringify(record, null, 2));
|
|
91
|
+
} else if (command === "del") {
|
|
92
|
+
const id = argv[3];
|
|
93
|
+
if (!id) {
|
|
94
|
+
console.error("\u7528\u6CD5: kmr del <id>");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
98
|
+
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
99
|
+
const store = new JsonStore(loadConfig().storage.dataDir);
|
|
100
|
+
const ok = await store.delete(id);
|
|
101
|
+
console.log(ok ? `\u2705 \u5DF2\u5220\u9664: ${id}` : `\u274C \u672A\u627E\u5230: ${id}`);
|
|
102
|
+
} else if (command === "find") {
|
|
103
|
+
const query = argv.slice(3).join(" ");
|
|
104
|
+
if (!query) {
|
|
105
|
+
console.error("\u7528\u6CD5: kmr find <query>");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
109
|
+
const { ClaudeCodeProvider } = await import("./claudeCode-PPRQZG5K.js");
|
|
110
|
+
const { QueryHandler } = await import("./handler-46CQQIA2.js");
|
|
111
|
+
const config = loadConfig();
|
|
112
|
+
const agent = new ClaudeCodeProvider(config.agent.timeout);
|
|
113
|
+
const handler = new QueryHandler(agent, config.storage.dataDir);
|
|
114
|
+
const results = await handler.find(query);
|
|
115
|
+
if (results.length === 0) {
|
|
116
|
+
console.log("\u672A\u627E\u5230\u5339\u914D\u7684\u4F1A\u8BAE\u8BB0\u5F55");
|
|
117
|
+
} else {
|
|
118
|
+
for (const r of results) console.log(`${r.id} ${r.summary.title} ${r.documentUrl || ""}`);
|
|
119
|
+
}
|
|
60
120
|
} else {
|
|
61
121
|
checkUpdate();
|
|
62
122
|
await import("./index.js");
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JsonStore
|
|
3
|
+
} from "./chunk-TUCCS6QJ.js";
|
|
4
|
+
import {
|
|
5
|
+
ClaudeCodeProvider,
|
|
6
|
+
getExecEnv
|
|
7
|
+
} from "./chunk-KJPAKNVA.js";
|
|
1
8
|
import {
|
|
2
9
|
KMR_DIR,
|
|
3
10
|
getAgentProvider,
|
|
4
11
|
loadConfig
|
|
5
12
|
} from "./chunk-ZXGVA5QX.js";
|
|
13
|
+
import {
|
|
14
|
+
QueryHandler
|
|
15
|
+
} from "./chunk-SGTENG37.js";
|
|
6
16
|
|
|
7
17
|
// src/lark/client.ts
|
|
8
18
|
import * as lark from "@larksuiteoapi/node-sdk";
|
|
@@ -408,258 +418,13 @@ function parseMessage(text) {
|
|
|
408
418
|
return { type: "unknown" /* UNKNOWN */, raw: text };
|
|
409
419
|
}
|
|
410
420
|
|
|
411
|
-
// src/agent/claudeCode.ts
|
|
412
|
-
import { execFile } from "child_process";
|
|
413
|
-
import { promisify } from "util";
|
|
414
|
-
|
|
415
|
-
// src/agent/prompt.ts
|
|
416
|
-
var EXTRACT_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u9AD8\u7EA7\u4F1A\u8BAE\u5206\u6790\u5E08\uFF0C\u4E0D\u662F\u8BB0\u5F55\u5458\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u4ECE\u4F1A\u8BAE\u7EAA\u8981\u4E2D\u63D0\u70BC\u771F\u6B63\u6709\u4EF7\u503C\u7684\u4FE1\u606F\uFF0C\u800C\u4E0D\u662F\u6D41\u6C34\u8D26\u5F0F\u590D\u8FF0\u3002
|
|
417
|
-
|
|
418
|
-
\u4F60\u9700\u8981\u5E26\u7740\u4E24\u4E2A\u6838\u5FC3\u76EE\u6807\u5206\u6790\u4F1A\u8BAE\u5185\u5BB9\uFF1A
|
|
419
|
-
1. **\u6EAF\u6E90\u8FFD\u8D23**\uFF1A\u63D0\u53D6\u6240\u6709\u660E\u786E\u7684\u627F\u8BFA\u3001\u7EA6\u5B9A\u3001\u51B3\u8BAE\u2014\u2014\u8C01\u7B54\u5E94\u4E86\u4EC0\u4E48\u3001\u4EC0\u4E48\u65F6\u5019\u5B8C\u6210\u3001\u8FBE\u6210\u4E86\u4EC0\u4E48\u5171\u8BC6\u3002\u5177\u4F53\u7684\u4EBA\u548C\u5177\u4F53\u7684\u65F6\u95F4\u662F\u6838\u5FC3\u951A\u70B9\uFF0C\u8FD9\u4E9B\u4FE1\u606F\u7528\u4E8E\u4E8B\u540E\u5FEB\u901F\u6EAF\u6E90\u3002
|
|
420
|
-
2. **\u77E5\u8BC6\u590D\u7528**\uFF1A\u63D0\u53D6\u4F1A\u8BAE\u4E2D\u63D0\u5230\u7684\u5DE5\u5177\u7528\u6CD5\u3001\u65B9\u6CD5\u8BBA\u3001\u7ECF\u9A8C\u6559\u8BAD\u3001\u6700\u4F73\u5B9E\u8DF5\u2014\u2014\u8FD9\u4E9B\u53EF\u4EE5\u63D0\u5347\u5DE5\u4F5C\u6548\u7387\u7684\u53EF\u590D\u7528\u77E5\u8BC6\u3002
|
|
421
|
-
|
|
422
|
-
\u5206\u6790\u8981\u6C42\uFF1A
|
|
423
|
-
- participants\uFF1A\u5FC5\u987B\u5217\u51FA\u7EAA\u8981\u6587\u6863\u4E2D\u8BB0\u5F55\u7684\u53C2\u4E0E\u8BA8\u8BBA\u548C\u51B3\u7B56\u6216\u8005\u88AB\u63D0\u53CA\u7684\u6838\u5FC3\u6210\u5458\u540D\u5B57
|
|
424
|
-
- keyPoints\uFF1A\u53EA\u4FDD\u7559\u6700\u5173\u952E\u7684 3-8 \u4E2A\u51B3\u7B56\u6027\u8981\u70B9\uFF0C\u7F57\u5217\u7B80\u8981\u8BA8\u8BBA\u8FC7\u7A0B
|
|
425
|
-
- todos\uFF1A\u53EA\u63D0\u53D6\u6709\u660E\u786E\u8D1F\u8D23\u4EBA\u548C\u53EF\u6267\u884C\u5185\u5BB9\u7684\u5F85\u529E\uFF0C\u4E0D\u8981\u6A21\u7CCA\u7684"\u540E\u7EED\u8DDF\u8FDB"
|
|
426
|
-
- risks\uFF1A\u53EA\u63D0\u53D6\u771F\u6B63\u7684\u98CE\u9669\u548C\u672A\u51B3\u4E8B\u9879\uFF0C\u8981\u6709\u5177\u4F53\u7684\u7F13\u89E3\u65B9\u6848
|
|
427
|
-
- commitments\u3010\u6700\u91CD\u8981\u3011\uFF1A\u63D0\u53D6\u6240\u6709\u627F\u8BFA\u3001\u7EA6\u5B9A\u3001\u51B3\u8BAE\u3002context \u5B57\u6BB5\u5FC5\u987B\u5199\u4F60\u7684\u5206\u6790\u2014\u2014\u4E3A\u4EC0\u4E48\u4F1A\u8FBE\u6210\u8FD9\u4E2A\u5171\u8BC6\uFF0C\u80CC\u666F\u662F\u4EC0\u4E48\uFF0C\u8C01\u8FBE\u6210\u7684\uFF0C\u4E0D\u80FD\u53EA\u642C\u8FD0\u539F\u6587
|
|
428
|
-
- reusableInsights\u3010\u6700\u91CD\u8981\u3011\uFF1A\u63D0\u53D6\u53EF\u590D\u7528\u7684\u77E5\u8BC6\u3002scenario \u5B57\u6BB5\u5FC5\u987B\u5199\u8FD9\u4E2A\u77E5\u8BC6\u5728\u4EC0\u4E48\u573A\u666F\u4E0B\u6709\u7528\uFF0C\u4E0D\u80FD\u6CDB\u6CDB\u800C\u8C08\uFF0C\u8981\u6709\u5177\u4F53\u64CD\u4F5C\u5B9E\u8DF5\u6B65\u9AA4\u6307\u5BFC
|
|
429
|
-
|
|
430
|
-
\u3010\u91CD\u8981\u3011\u76F4\u63A5\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u89E3\u91CA\u6216 markdown \u4EE3\u7801\u5757\u3002\u4EC5\u8F93\u51FA\u4EE5\u4E0B\u683C\u5F0F\u7684 JSON\uFF1A
|
|
431
|
-
{"summary":{"title":"\u4F1A\u8BAE\u6807\u9898","date":"YYYY-MM-DD","participants":["\u53C2\u4E0E\u4EBA1"],"keyPoints":["\u51B3\u7B56\u6027\u8981\u70B9\uFF0C\u4E0D\u662F\u6D41\u6C34\u8D26"]},"todos":[{"content":"\u5177\u4F53\u53EF\u6267\u884C\u5185\u5BB9","owner":"\u8D1F\u8D23\u4EBA","deadline":"YYYY-MM-DD","status":"pending"}],"risks":[{"description":"\u5177\u4F53\u98CE\u9669","severity":"high|medium|low","mitigation":"\u5177\u4F53\u7F13\u89E3\u65B9\u6848"}],"projectRelations":[{"project":"\u9879\u76EE\u540D","relation":"\u5173\u8054\u63CF\u8FF0"}],"commitments":[{"content":"\u627F\u8BFA/\u7EA6\u5B9A/\u51B3\u8BAE\u7684\u5177\u4F53\u5185\u5BB9","participants":["\u76F8\u5173\u65B91","\u76F8\u5173\u65B92"],"deadline":"YYYY-MM-DD","context":"\u4F60\u7684\u5206\u6790\uFF1A\u4E3A\u4EC0\u4E48\u8FBE\u6210\u8FD9\u4E2A\u5171\u8BC6\uFF0C\u80CC\u666F\u548C\u5F71\u54CD\u662F\u4EC0\u4E48"}],"reusableInsights":[{"category":"tool|methodology|lesson|best-practice","content":"\u77E5\u8BC6\u70B9\u7684\u5177\u4F53\u5185\u5BB9","scenario":"\u5728\u4EC0\u4E48\u573A\u666F\u4E0B\u53EF\u4EE5\u590D\u7528\u8FD9\u4E2A\u77E5\u8BC6","source":"\u8C01\u63D0\u51FA\u7684"}]}
|
|
432
|
-
|
|
433
|
-
\u4F1A\u8BAE\u7EAA\u8981\u5185\u5BB9\uFF1A
|
|
434
|
-
`;
|
|
435
|
-
var SEARCH_KEYWORDS_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u641C\u7D22\u52A9\u624B\u3002\u7528\u6237\u60F3\u67E5\u627E\u5386\u53F2\u4F1A\u8BAE\u8BB0\u5F55\uFF0C\u8BF7\u5C06\u7528\u6237\u7684\u81EA\u7136\u8BED\u8A00\u67E5\u8BE2\u8F6C\u6362\u4E3A\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868\u3002
|
|
436
|
-
|
|
437
|
-
\u8981\u6C42\uFF1A
|
|
438
|
-
- \u8FD4\u56DE 3-5 \u4E2A\u6700\u76F8\u5173\u7684\u5173\u952E\u8BCD
|
|
439
|
-
- \u4EC5\u8FD4\u56DE JSON \u6570\u7EC4\u683C\u5F0F\uFF0C\u4E0D\u8981\u5176\u4ED6\u5185\u5BB9
|
|
440
|
-
- \u793A\u4F8B\uFF1A["\u5173\u952E\u8BCD1", "\u5173\u952E\u8BCD2", "\u5173\u952E\u8BCD3"]
|
|
441
|
-
|
|
442
|
-
\u7528\u6237\u67E5\u8BE2\uFF1A`;
|
|
443
|
-
var RANK_RESULTS_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u641C\u7D22\u6392\u5E8F\u52A9\u624B\u3002\u7528\u6237\u67E5\u8BE2\u548C\u5019\u9009\u4F1A\u8BAE\u8BB0\u5F55\u5982\u4E0B\uFF0C\u8BF7\u6309\u76F8\u5173\u6027\u4ECE\u9AD8\u5230\u4F4E\u6392\u5E8F\u3002
|
|
444
|
-
|
|
445
|
-
\u4EC5\u8FD4\u56DE\u6392\u5E8F\u540E\u7684\u4F1A\u8BAE ID JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u5176\u4ED6\u5185\u5BB9\u3002
|
|
446
|
-
\u793A\u4F8B\uFF1A["meeting_003", "meeting_001"]
|
|
447
|
-
|
|
448
|
-
\u7528\u6237\u67E5\u8BE2\uFF1A`;
|
|
449
|
-
|
|
450
|
-
// src/utils/claudeEnv.ts
|
|
451
|
-
import fs from "fs";
|
|
452
|
-
import path from "path";
|
|
453
|
-
import os from "os";
|
|
454
|
-
var cachedEnv = null;
|
|
455
|
-
function getClaudeEnv() {
|
|
456
|
-
if (cachedEnv) return cachedEnv;
|
|
457
|
-
const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
458
|
-
let settingsEnv = {};
|
|
459
|
-
try {
|
|
460
|
-
const content = fs.readFileSync(settingsPath, "utf-8");
|
|
461
|
-
const settings = JSON.parse(content);
|
|
462
|
-
if (settings.env && typeof settings.env === "object") {
|
|
463
|
-
settingsEnv = settings.env;
|
|
464
|
-
}
|
|
465
|
-
} catch {
|
|
466
|
-
}
|
|
467
|
-
cachedEnv = { ...settingsEnv };
|
|
468
|
-
for (const key of Object.keys(settingsEnv)) {
|
|
469
|
-
if (process.env[key]) {
|
|
470
|
-
cachedEnv[key] = process.env[key];
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
return cachedEnv;
|
|
474
|
-
}
|
|
475
|
-
function getExecEnv() {
|
|
476
|
-
const claudeEnv = getClaudeEnv();
|
|
477
|
-
return { ...process.env, ...claudeEnv };
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// src/agent/claudeCode.ts
|
|
481
|
-
var execFileAsync = promisify(execFile);
|
|
482
|
-
var ClaudeCodeProvider = class {
|
|
483
|
-
constructor(timeout = 12e4) {
|
|
484
|
-
this.timeout = timeout;
|
|
485
|
-
}
|
|
486
|
-
timeout;
|
|
487
|
-
name = "claude";
|
|
488
|
-
async extract(content) {
|
|
489
|
-
const prompt = EXTRACT_PROMPT + content;
|
|
490
|
-
console.log(`[ClaudeCodeProvider] \u63D0\u53D6\u4FE1\u606F\u7684 prompt:
|
|
491
|
-
${prompt}
|
|
492
|
-
--- End of Prompt ---`);
|
|
493
|
-
const output = await this.callAcpx(prompt);
|
|
494
|
-
const parsed = JSON.parse(output);
|
|
495
|
-
return {
|
|
496
|
-
id: `meeting_${Date.now()}`,
|
|
497
|
-
documentUrl: "",
|
|
498
|
-
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
499
|
-
summary: parsed.summary,
|
|
500
|
-
todos: (parsed.todos || []).map((t) => ({ ...t, status: "invalid_cause_no_task" })),
|
|
501
|
-
risks: parsed.risks || [],
|
|
502
|
-
projectRelations: parsed.projectRelations || [],
|
|
503
|
-
commitments: parsed.commitments || [],
|
|
504
|
-
reusableInsights: parsed.reusableInsights || [],
|
|
505
|
-
rawContent: content
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
async searchKeywords(query) {
|
|
509
|
-
const prompt = SEARCH_KEYWORDS_PROMPT + query;
|
|
510
|
-
const output = await this.callAcpx(prompt);
|
|
511
|
-
return JSON.parse(output);
|
|
512
|
-
}
|
|
513
|
-
async rankResults(query, candidates) {
|
|
514
|
-
if (candidates.length <= 1) return candidates;
|
|
515
|
-
const summaries = candidates.map((c) => ({
|
|
516
|
-
id: c.id,
|
|
517
|
-
title: c.summary.title,
|
|
518
|
-
keyPoints: c.summary.keyPoints
|
|
519
|
-
}));
|
|
520
|
-
const prompt = RANK_RESULTS_PROMPT + query + "\n\n\u5019\u9009\u4F1A\u8BAE\uFF1A\n" + JSON.stringify(summaries, null, 2);
|
|
521
|
-
const output = await this.callAcpx(prompt);
|
|
522
|
-
const rankedIds = JSON.parse(output);
|
|
523
|
-
const indexed = new Map(candidates.map((c) => [c.id, c]));
|
|
524
|
-
return rankedIds.map((id) => indexed.get(id)).filter(Boolean);
|
|
525
|
-
}
|
|
526
|
-
async callAcpx(prompt) {
|
|
527
|
-
const provider = getAgentProvider();
|
|
528
|
-
const agentCmd = provider === "codex" ? "codex" : provider;
|
|
529
|
-
console.log(`[agent] \u4F7F\u7528 ${agentCmd} \u6267\u884C\u63D0\u53D6`);
|
|
530
|
-
try {
|
|
531
|
-
const { stdout } = await execFileAsync("acpx", ["--allowed-tools", "", agentCmd, "exec", prompt], {
|
|
532
|
-
timeout: this.timeout,
|
|
533
|
-
maxBuffer: 1024 * 1024,
|
|
534
|
-
env: getExecEnv()
|
|
535
|
-
});
|
|
536
|
-
return this.extractJson(stdout);
|
|
537
|
-
} catch (err) {
|
|
538
|
-
if (err.killed) {
|
|
539
|
-
throw new Error(`acpx \u8C03\u7528\u8D85\u65F6 (${this.timeout}ms)`);
|
|
540
|
-
}
|
|
541
|
-
throw new Error(`acpx \u8C03\u7528\u5931\u8D25: ${err.message}`);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
extractJson(output) {
|
|
545
|
-
const lines = output.split("\n");
|
|
546
|
-
let jsonStart = -1;
|
|
547
|
-
let jsonEnd = -1;
|
|
548
|
-
for (let i = 0; i < lines.length; i++) {
|
|
549
|
-
const trimmed = lines[i].trim();
|
|
550
|
-
if (jsonStart === -1 && (trimmed.startsWith("{") || trimmed.startsWith("["))) {
|
|
551
|
-
jsonStart = i;
|
|
552
|
-
}
|
|
553
|
-
if (trimmed.endsWith("}") || trimmed.endsWith("]")) {
|
|
554
|
-
jsonEnd = i;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
if (jsonStart !== -1 && jsonEnd >= jsonStart) {
|
|
558
|
-
const candidate = lines.slice(jsonStart, jsonEnd + 1).join("\n").trim();
|
|
559
|
-
try {
|
|
560
|
-
JSON.parse(candidate);
|
|
561
|
-
return candidate;
|
|
562
|
-
} catch {
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
const filtered = lines.filter((line) => {
|
|
566
|
-
const t = line.trim();
|
|
567
|
-
return t.length > 0 && !t.startsWith("[client]") && !t.startsWith("[tool]") && !t.startsWith("[thinking]") && !t.startsWith("[done]") && !t.startsWith("[error]") && !t.startsWith("[warn]") && !t.startsWith("[info]");
|
|
568
|
-
}).join("\n").trim();
|
|
569
|
-
if (filtered.length > 0) return filtered;
|
|
570
|
-
return output.trim();
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
// src/storage/jsonStore.ts
|
|
575
|
-
import fs2 from "fs";
|
|
576
|
-
import path2 from "path";
|
|
577
|
-
var JsonStore = class {
|
|
578
|
-
constructor(dataDir) {
|
|
579
|
-
this.dataDir = dataDir;
|
|
580
|
-
fs2.mkdirSync(dataDir, { recursive: true });
|
|
581
|
-
}
|
|
582
|
-
dataDir;
|
|
583
|
-
async save(record) {
|
|
584
|
-
const date = record.summary.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
585
|
-
const fileName = `${date}_${record.id}.json`;
|
|
586
|
-
const filePath = path2.join(this.dataDir, fileName);
|
|
587
|
-
fs2.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
588
|
-
return filePath;
|
|
589
|
-
}
|
|
590
|
-
async load(id) {
|
|
591
|
-
const files = this.getFiles();
|
|
592
|
-
const target = files.find((f) => f.includes(id));
|
|
593
|
-
if (!target) return null;
|
|
594
|
-
const raw = fs2.readFileSync(target, "utf-8");
|
|
595
|
-
return JSON.parse(raw);
|
|
596
|
-
}
|
|
597
|
-
async list() {
|
|
598
|
-
const files = this.getFiles();
|
|
599
|
-
return files.map((f) => JSON.parse(fs2.readFileSync(f, "utf-8")));
|
|
600
|
-
}
|
|
601
|
-
async search(keywords) {
|
|
602
|
-
const all = await this.list();
|
|
603
|
-
return all.filter((record) => {
|
|
604
|
-
const text = record.rawContent + " " + record.summary.title + " " + record.summary.keyPoints.join(" ");
|
|
605
|
-
return keywords.some((kw) => text.includes(kw));
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
async delete(id) {
|
|
609
|
-
const files = this.getFiles();
|
|
610
|
-
const target = files.find((f) => f.includes(id));
|
|
611
|
-
if (!target) return false;
|
|
612
|
-
fs2.unlinkSync(target);
|
|
613
|
-
return true;
|
|
614
|
-
}
|
|
615
|
-
getFiles() {
|
|
616
|
-
if (!fs2.existsSync(this.dataDir)) return [];
|
|
617
|
-
return fs2.readdirSync(this.dataDir).filter((f) => f.endsWith(".json")).map((f) => path2.join(this.dataDir, f));
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
|
|
621
|
-
// src/query/finder.ts
|
|
622
|
-
import fs3 from "fs";
|
|
623
|
-
import path3 from "path";
|
|
624
|
-
async function grepMeetings(dataDir, keywords) {
|
|
625
|
-
if (!fs3.existsSync(dataDir)) return [];
|
|
626
|
-
const files = fs3.readdirSync(dataDir).filter((f) => f.endsWith(".json"));
|
|
627
|
-
const matches = [];
|
|
628
|
-
for (const file of files) {
|
|
629
|
-
const filePath = path3.join(dataDir, file);
|
|
630
|
-
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
631
|
-
const hasMatch = keywords.some((kw) => raw.includes(kw));
|
|
632
|
-
if (hasMatch) {
|
|
633
|
-
matches.push(JSON.parse(raw));
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return matches;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// src/query/handler.ts
|
|
640
|
-
var QueryHandler = class {
|
|
641
|
-
constructor(agent, dataDir) {
|
|
642
|
-
this.agent = agent;
|
|
643
|
-
this.dataDir = dataDir;
|
|
644
|
-
}
|
|
645
|
-
agent;
|
|
646
|
-
dataDir;
|
|
647
|
-
async find(query) {
|
|
648
|
-
const keywords = await this.agent.searchKeywords(query);
|
|
649
|
-
const candidates = await grepMeetings(this.dataDir, keywords);
|
|
650
|
-
if (candidates.length === 0) return [];
|
|
651
|
-
const ranked = await this.agent.rankResults(query, candidates);
|
|
652
|
-
return ranked.slice(0, 3);
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
|
|
656
421
|
// src/web/server.ts
|
|
657
422
|
import http from "http";
|
|
658
|
-
import
|
|
659
|
-
import
|
|
423
|
+
import fs from "fs";
|
|
424
|
+
import path from "path";
|
|
660
425
|
import { fileURLToPath } from "url";
|
|
661
|
-
var __dirname =
|
|
662
|
-
var PUBLIC_DIR =
|
|
426
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
427
|
+
var PUBLIC_DIR = path.join(__dirname, "public");
|
|
663
428
|
var MIME_TYPES = {
|
|
664
429
|
".html": "text/html; charset=utf-8",
|
|
665
430
|
".css": "text/css; charset=utf-8",
|
|
@@ -669,30 +434,30 @@ var MIME_TYPES = {
|
|
|
669
434
|
".svg": "image/svg+xml"
|
|
670
435
|
};
|
|
671
436
|
function serveStatic(res, urlPath) {
|
|
672
|
-
const safePath =
|
|
673
|
-
let filePath =
|
|
674
|
-
if (!
|
|
675
|
-
filePath =
|
|
437
|
+
const safePath = path.normalize(urlPath).replace(/^(\.\.[/\\])+/, "");
|
|
438
|
+
let filePath = path.join(PUBLIC_DIR, safePath === "/" ? "index.html" : safePath);
|
|
439
|
+
if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
|
|
440
|
+
filePath = path.join(PUBLIC_DIR, "index.html");
|
|
676
441
|
}
|
|
677
|
-
const ext =
|
|
442
|
+
const ext = path.extname(filePath);
|
|
678
443
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
679
|
-
const content =
|
|
444
|
+
const content = fs.readFileSync(filePath);
|
|
680
445
|
res.writeHead(200, { "Content-Type": contentType });
|
|
681
446
|
res.end(content);
|
|
682
447
|
}
|
|
683
448
|
function listSessions(kmrDir) {
|
|
684
|
-
const sessionsDir =
|
|
685
|
-
if (!
|
|
686
|
-
return
|
|
687
|
-
const full =
|
|
688
|
-
return
|
|
449
|
+
const sessionsDir = path.join(kmrDir, "sessions");
|
|
450
|
+
if (!fs.existsSync(sessionsDir)) return [];
|
|
451
|
+
return fs.readdirSync(sessionsDir).filter((d) => {
|
|
452
|
+
const full = path.join(sessionsDir, d);
|
|
453
|
+
return fs.statSync(full).isDirectory();
|
|
689
454
|
}).map((userId) => {
|
|
690
|
-
const summaryPath =
|
|
455
|
+
const summaryPath = path.join(sessionsDir, userId, "summary.md");
|
|
691
456
|
let summaryPreview = "";
|
|
692
457
|
let messageCount = 0;
|
|
693
458
|
let lastActivity = "";
|
|
694
|
-
if (
|
|
695
|
-
const content =
|
|
459
|
+
if (fs.existsSync(summaryPath)) {
|
|
460
|
+
const content = fs.readFileSync(summaryPath, "utf-8");
|
|
696
461
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
697
462
|
messageCount = lines.length;
|
|
698
463
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
@@ -704,26 +469,26 @@ function listSessions(kmrDir) {
|
|
|
704
469
|
const lastLine = lines[lines.length - 1] || "";
|
|
705
470
|
const timeMatch = lastLine.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]/);
|
|
706
471
|
if (timeMatch) lastActivity = timeMatch[1];
|
|
707
|
-
const stat =
|
|
472
|
+
const stat = fs.statSync(summaryPath);
|
|
708
473
|
if (!lastActivity) lastActivity = stat.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
709
474
|
} else {
|
|
710
|
-
const userDir =
|
|
711
|
-
const stat =
|
|
475
|
+
const userDir = path.join(sessionsDir, userId);
|
|
476
|
+
const stat = fs.statSync(userDir);
|
|
712
477
|
lastActivity = stat.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
713
478
|
}
|
|
714
479
|
return { userId, summaryPreview, messageCount, lastActivity };
|
|
715
480
|
}).sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
|
|
716
481
|
}
|
|
717
482
|
function deleteSession(kmrDir, userId) {
|
|
718
|
-
const sessionDir =
|
|
719
|
-
if (!
|
|
720
|
-
|
|
483
|
+
const sessionDir = path.join(kmrDir, "sessions", userId);
|
|
484
|
+
if (!fs.existsSync(sessionDir)) return false;
|
|
485
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
721
486
|
return true;
|
|
722
487
|
}
|
|
723
488
|
function getSessionDetail(kmrDir, userId) {
|
|
724
|
-
const summaryPath =
|
|
725
|
-
if (!
|
|
726
|
-
return
|
|
489
|
+
const summaryPath = path.join(kmrDir, "sessions", userId, "summary.md");
|
|
490
|
+
if (!fs.existsSync(summaryPath)) return null;
|
|
491
|
+
return fs.readFileSync(summaryPath, "utf-8");
|
|
727
492
|
}
|
|
728
493
|
function startWebServer(store, kmrDir, port = 3e3) {
|
|
729
494
|
return new Promise((resolve, reject) => {
|
|
@@ -793,8 +558,8 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
793
558
|
return;
|
|
794
559
|
}
|
|
795
560
|
if (pathname === "/api/config" && method === "GET") {
|
|
796
|
-
const configPath =
|
|
797
|
-
const config = JSON.parse(
|
|
561
|
+
const configPath = path.join(kmrDir, "config.json");
|
|
562
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
798
563
|
jsonResponse(res, 200, { ok: true, data: config });
|
|
799
564
|
return;
|
|
800
565
|
}
|
|
@@ -807,8 +572,8 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
807
572
|
jsonResponse(res, 400, { ok: false, error: "\u65E0\u6548\u7684 JSON \u683C\u5F0F" });
|
|
808
573
|
return;
|
|
809
574
|
}
|
|
810
|
-
const configPath =
|
|
811
|
-
|
|
575
|
+
const configPath = path.join(kmrDir, "config.json");
|
|
576
|
+
fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), "utf-8");
|
|
812
577
|
jsonResponse(res, 200, { ok: true, data: parsed });
|
|
813
578
|
return;
|
|
814
579
|
}
|
|
@@ -843,27 +608,27 @@ async function handleApi(req, res, url, store, kmrDir) {
|
|
|
843
608
|
}
|
|
844
609
|
|
|
845
610
|
// src/web/openBrowser.ts
|
|
846
|
-
import { execFile
|
|
611
|
+
import { execFile } from "child_process";
|
|
847
612
|
function openBrowser(url) {
|
|
848
613
|
const platform = process.platform;
|
|
849
614
|
switch (platform) {
|
|
850
615
|
case "darwin":
|
|
851
|
-
|
|
616
|
+
execFile("open", [url]);
|
|
852
617
|
break;
|
|
853
618
|
case "win32":
|
|
854
|
-
|
|
619
|
+
execFile("cmd", ["/c", "start", url]);
|
|
855
620
|
break;
|
|
856
621
|
default:
|
|
857
|
-
|
|
622
|
+
execFile("xdg-open", [url]);
|
|
858
623
|
break;
|
|
859
624
|
}
|
|
860
625
|
}
|
|
861
626
|
|
|
862
627
|
// src/session/manager.ts
|
|
863
|
-
import { execFile as
|
|
864
|
-
import { promisify
|
|
865
|
-
import
|
|
866
|
-
import
|
|
628
|
+
import { execFile as execFile2 } from "child_process";
|
|
629
|
+
import { promisify } from "util";
|
|
630
|
+
import fs2 from "fs";
|
|
631
|
+
import path2 from "path";
|
|
867
632
|
|
|
868
633
|
// src/session/skill.ts
|
|
869
634
|
var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A\u80FD\u52A9\u624B\u3002
|
|
@@ -875,30 +640,30 @@ var SESSION_SKILL = `\u4F60\u662F KMR\uFF08Key Meetings Record\uFF09\u7684\u667A
|
|
|
875
640
|
4. \u63D0\u4F9B\u5DE5\u4F5C\u5EFA\u8BAE\u548C\u77E5\u8BC6\u68C0\u7D22
|
|
876
641
|
|
|
877
642
|
\u80CC\u666F\u4FE1\u606F\uFF1A
|
|
878
|
-
- KMR \u662F\u4E00\u4E2A\u4ECE\u98DE\u4E66\u4F1A\u8BAE\u7EAA\u8981\u4E2D\u63D0\u53D6\u5173\u952E\u4FE1\u606F\u7684\u670D\u52A1
|
|
879
|
-
- \u6838\u5FC3\u63D0\u53D6\u7EF4\u5EA6\uFF1A\u5173\u952E\u5171\u8BC6\u4E0E\u627F\u8BFA\uFF08\u6EAF\u6E90\u8FFD\u8D23\uFF09\u3001\u53EF\u590D\u7528\u77E5\u8BC6\uFF08\u5DE5\u5177/\u65B9\u6CD5/\u7ECF\u9A8C\uFF09
|
|
643
|
+
- KMR \u662F\u4E00\u4E2A\u4ECE\u98DE\u4E66\u4F1A\u8BAE\u7EAA\u8981\u4E2D\u63D0\u53D6\u5173\u952E\u4FE1\u606F\u5E76\u6316\u6398\u51FA\u5171\u8BC6\u3001\u627F\u8BFA\u3001\u98CE\u9669\u3001\u5F85\u529E\u3001\u77E5\u8BC6\u7684\u670D\u52A1
|
|
644
|
+
- \u6838\u5FC3\u63D0\u53D6\u7EF4\u5EA6\uFF1A\u5173\u952E\u5171\u8BC6\u4E0E\u627F\u8BFA\uFF08\u6EAF\u6E90\u8FFD\u8D23\uFF09\u3001\u53EF\u590D\u7528\u77E5\u8BC6\uFF08\u5DE5\u5177/\u65B9\u6CD5/\u7ECF\u9A8C\uFF09\u3001\u98CE\u9669\u4E0E\u5F85\u529E\uFF08\u98CE\u9669\u9884\u8B66/\u4EFB\u52A1\u8DDF\u8E2A\uFF09
|
|
880
645
|
|
|
881
646
|
\u6570\u636E\u8BBF\u95EE\uFF1A
|
|
882
647
|
- \u4F1A\u8BAE\u8BB0\u5F55\u5B58\u50A8\u5728 ~/.kmr/data/meetings/ \u76EE\u5F55\u4E0B\uFF0C\u6BCF\u4E2A\u4F1A\u8BAE\u662F\u4E00\u4E2A JSON \u6587\u4EF6
|
|
883
|
-
- \u4F60\u53EF\u4EE5\u76F4\u63A5\u7528 ls\u3001cat\u3001grep \u7B49\u547D\u4EE4\u8BFB\u53D6\u8FD9\u4E9B\u6587\u4EF6\u6765\u56DE\u7B54\u7528\u6237\u7684\u95EE\u9898
|
|
884
648
|
- \u6BCF\u4E2A JSON \u6587\u4EF6\u5305\u542B\uFF1Asummary\uFF08\u6807\u9898/\u65E5\u671F/\u53C2\u4E0E\u4EBA/\u8981\u70B9\uFF09\u3001todos\u3001risks\u3001commitments\u3001reusableInsights\u3001rawContent
|
|
885
|
-
- \
|
|
886
|
-
- \u7528\u6237\
|
|
649
|
+
- \u4F60\u53EF\u4EE5\u901A\u8FC7\u4EE5\u4E0B\u547D\u4EE4\u67E5\u8BE2\u4F1A\u8BAE\u6570\u636E\uFF1Akmr list\uFF08\u5217\u51FA\u5168\u90E8\uFF09\u3001kmr show <id>\uFF08\u67E5\u770B\u8BE6\u60C5\uFF09\u3001kmr find <query>\uFF08\u641C\u7D22\uFF09
|
|
650
|
+
- \u5F53\u7528\u6237\u95EE\u5230\u4F1A\u8BAE/\u4EFB\u52A1\u76F8\u5173\u7684\u95EE\u9898\u65F6\uFF0C\u76F4\u63A5\u6267\u884C kmr \u547D\u4EE4\u83B7\u53D6\u6570\u636E\u5E76\u56DE\u7B54
|
|
651
|
+
- \u63D0\u793A\u7528\u6237\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u53EF\u4EE5\u4F7F\u7528\uFF1A/find \u641C\u7D22\u3001/listall \u5217\u51FA\u5168\u90E8\u3001/show <id> \u67E5\u770B\u8BE6\u60C5\u3001/del <id> \u5220\u9664; \u6216\u8005\u5728\u7EC8\u7AEF\u4F7F\u7528\u4E0A\u8FF0kmr\u547D\u4EE4\u8FDB\u884C\u64CD\u4F5C
|
|
887
652
|
|
|
888
653
|
\u5BF9\u8BDD\u8981\u6C42\uFF1A
|
|
889
654
|
- \u7B80\u6D01\u3001\u4E13\u4E1A\u3001\u6709\u5E2E\u52A9
|
|
890
|
-
- \u4F18\u5148\
|
|
655
|
+
- \u4F18\u5148\u901A\u8FC7 kmr \u547D\u4EE4\u83B7\u53D6\u6570\u636E\u56DE\u7B54\u95EE\u9898\uFF1B\u5982\u679C\u7528\u6237\u660E\u786E\u60F3\u81EA\u5DF1\u64CD\u4F5C\uFF0C\u4E5F\u53EF\u4EE5\u544A\u77E5\u5BF9\u5E94\u547D\u4EE4
|
|
891
656
|
- \u6BCF\u6B21\u56DE\u590D\u63A7\u5236\u5728\u5408\u7406\u957F\u5EA6\uFF0C\u9002\u5408\u98DE\u4E66\u6D88\u606F\u9605\u8BFB
|
|
892
657
|
|
|
893
658
|
\u3010\u91CD\u8981\u3011\u76F4\u63A5\u8F93\u51FA\u7EAF\u6587\u672C\u56DE\u590D\uFF0C\u4E0D\u8981\u8F93\u51FA JSON\u3001markdown \u4EE3\u7801\u5757\u7B49\u683C\u5F0F\u3002`;
|
|
894
659
|
|
|
895
660
|
// src/session/manager.ts
|
|
896
|
-
var
|
|
661
|
+
var execFileAsync = promisify(execFile2);
|
|
897
662
|
var SessionManager = class {
|
|
898
663
|
constructor(kmrDir, timeout = 6e4) {
|
|
899
664
|
this.timeout = timeout;
|
|
900
|
-
this.sessionDir =
|
|
901
|
-
|
|
665
|
+
this.sessionDir = path2.join(kmrDir, "sessions");
|
|
666
|
+
fs2.mkdirSync(this.sessionDir, { recursive: true });
|
|
902
667
|
}
|
|
903
668
|
timeout;
|
|
904
669
|
activeSessions = /* @__PURE__ */ new Map();
|
|
@@ -921,14 +686,14 @@ var SessionManager = class {
|
|
|
921
686
|
return existing.name;
|
|
922
687
|
}
|
|
923
688
|
const sessionName = `kmr-${userId}`;
|
|
924
|
-
const userDir =
|
|
925
|
-
|
|
926
|
-
const skillPath =
|
|
927
|
-
|
|
689
|
+
const userDir = path2.join(this.sessionDir, userId);
|
|
690
|
+
fs2.mkdirSync(userDir, { recursive: true });
|
|
691
|
+
const skillPath = path2.join(userDir, "skill.md");
|
|
692
|
+
fs2.writeFileSync(skillPath, SESSION_SKILL, "utf-8");
|
|
928
693
|
console.log(`[session] \u521B\u5EFA\u65B0 session: ${sessionName}`);
|
|
929
694
|
const agentCmd = this.getAgentCmd();
|
|
930
695
|
try {
|
|
931
|
-
await
|
|
696
|
+
await execFileAsync("acpx", [agentCmd, "sessions", "ensure", "--name", sessionName], {
|
|
932
697
|
timeout: this.timeout,
|
|
933
698
|
maxBuffer: 1024 * 1024,
|
|
934
699
|
env: getExecEnv()
|
|
@@ -939,7 +704,7 @@ var SessionManager = class {
|
|
|
939
704
|
}
|
|
940
705
|
console.log(`[session] \u53D1\u9001\u89D2\u8272\u8BBE\u5B9A...`);
|
|
941
706
|
try {
|
|
942
|
-
await
|
|
707
|
+
await execFileAsync(
|
|
943
708
|
"acpx",
|
|
944
709
|
["--approve-all", agentCmd, "-s", sessionName, SESSION_SKILL],
|
|
945
710
|
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
@@ -956,11 +721,13 @@ var SessionManager = class {
|
|
|
956
721
|
async sendToSession(sessionName, text) {
|
|
957
722
|
const agentCmd = this.getAgentCmd();
|
|
958
723
|
try {
|
|
959
|
-
const { stdout } = await
|
|
724
|
+
const { stdout } = await execFileAsync(
|
|
960
725
|
"acpx",
|
|
961
726
|
["--approve-all", agentCmd, "-s", sessionName, text],
|
|
962
727
|
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
963
728
|
);
|
|
729
|
+
console.log(`[session] agent \u5B8C\u6574\u8F93\u51FA:
|
|
730
|
+
${stdout}`);
|
|
964
731
|
return this.extractReply(stdout);
|
|
965
732
|
} catch (err) {
|
|
966
733
|
if (err.killed) {
|
|
@@ -999,21 +766,21 @@ var SessionManager = class {
|
|
|
999
766
|
return output.trim();
|
|
1000
767
|
}
|
|
1001
768
|
async appendSummary(userId, userText, aiReply) {
|
|
1002
|
-
const userDir =
|
|
1003
|
-
const summaryPath =
|
|
769
|
+
const userDir = path2.join(this.sessionDir, userId);
|
|
770
|
+
const summaryPath = path2.join(userDir, "summary.md");
|
|
1004
771
|
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
1005
772
|
const entry = `
|
|
1006
773
|
[${now}] \u7528\u6237: ${userText}
|
|
1007
774
|
[${now}] AI: ${aiReply}
|
|
1008
775
|
`;
|
|
1009
|
-
|
|
776
|
+
fs2.appendFileSync(summaryPath, entry, "utf-8");
|
|
1010
777
|
}
|
|
1011
778
|
async closeSession(userId) {
|
|
1012
779
|
const info = this.activeSessions.get(userId);
|
|
1013
780
|
if (!info) return;
|
|
1014
781
|
try {
|
|
1015
782
|
const agentCmd = this.getAgentCmd();
|
|
1016
|
-
await
|
|
783
|
+
await execFileAsync("acpx", [agentCmd, "sessions", "close", info.name], {
|
|
1017
784
|
timeout: 1e4,
|
|
1018
785
|
env: getExecEnv()
|
|
1019
786
|
});
|