opencode-haimati 1.0.3 → 1.0.5
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 +5 -5
- package/dist/index.js +89 -1067
- package/dist/package.json +8 -4
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ bun add -g opencode-haimati
|
|
|
53
53
|
| 工具 | 说明 |
|
|
54
54
|
|------|------|
|
|
55
55
|
| `haimati_read` | 读取记忆内容(只支持序号查询,支持分页,返回带行号和版本号) |
|
|
56
|
-
| `haimati_write` |
|
|
56
|
+
| `haimati_write` | 写入新记忆(如果已存在相同分类和标题的记忆则报错) |
|
|
57
57
|
| `haimati_search` | 搜索记忆内容(标题、分类、序号、书页内容) |
|
|
58
58
|
| `haimati_edit` | 修改记忆内容(部分替换,支持版本并发控制) |
|
|
59
59
|
| `haimati_move` | 修改记忆的分类路径 |
|
|
@@ -71,7 +71,7 @@ bun add -g opencode-haimati
|
|
|
71
71
|
```typescript
|
|
72
72
|
// 写入新记忆
|
|
73
73
|
haimati_write({
|
|
74
|
-
category: "
|
|
74
|
+
category: "xxx项目/签章",
|
|
75
75
|
title: "签章服务WS通信机制",
|
|
76
76
|
content: "WebSocket通信采用base64编码..."
|
|
77
77
|
})
|
|
@@ -94,7 +94,7 @@ haimati_edit({
|
|
|
94
94
|
// 移动记忆到新分类
|
|
95
95
|
haimati_move({
|
|
96
96
|
query: "086",
|
|
97
|
-
newCategory: "
|
|
97
|
+
newCategory: "xxx项目/客户端"
|
|
98
98
|
})
|
|
99
99
|
|
|
100
100
|
// 删除记忆
|
|
@@ -104,13 +104,13 @@ haimati_delete({ query: "086" })
|
|
|
104
104
|
haimati_list({})
|
|
105
105
|
|
|
106
106
|
// 列出指定分类下的记忆
|
|
107
|
-
haimati_list({ category: "
|
|
107
|
+
haimati_list({ category: "xxx项目", recursive: true })
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
## 索引格式示例
|
|
111
111
|
|
|
112
112
|
```
|
|
113
|
-
|
|
113
|
+
xxx项目/
|
|
114
114
|
├── 签章/
|
|
115
115
|
│ └── 签章服务WS通信机制 - 086
|
|
116
116
|
└── 通用/
|
package/dist/index.js
CHANGED
|
@@ -1,1056 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import path from "
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const groupName = `${String(groupStart).padStart(3, "0")}-${String(groupEnd).padStart(3, "0")}`;
|
|
74
|
-
return path.join(await getPagesDir(directory), groupName);
|
|
75
|
-
}
|
|
76
|
-
async function getPagePath(directory, id) {
|
|
77
|
-
return path.join(await getPageGroupDir(directory, id), `${id}.md`);
|
|
78
|
-
}
|
|
79
|
-
var fsAppendFile = promisify(fs.appendFile);
|
|
80
|
-
function getLogFilePath() {
|
|
81
|
-
const osTmpDir = os.tmpdir();
|
|
82
|
-
return path.join(osTmpDir, "haimati_logs", LOG_FILE);
|
|
83
|
-
}
|
|
84
|
-
async function writeLogToFile(message) {
|
|
85
|
-
try {
|
|
86
|
-
const now = /* @__PURE__ */ new Date();
|
|
87
|
-
const dateStr = now.toISOString().split("T")[0];
|
|
88
|
-
const timeStr = now.toISOString().replace("T", " ").split(".")[0];
|
|
89
|
-
const logPath = getLogFilePath();
|
|
90
|
-
const logDir = path.dirname(logPath);
|
|
91
|
-
if (!await fsExists(logDir)) {
|
|
92
|
-
await fsMkdir(logDir, { recursive: true });
|
|
93
|
-
}
|
|
94
|
-
let shouldOverwrite = false;
|
|
95
|
-
if (await fsExists(logPath)) {
|
|
96
|
-
const mtime = (await fsStat(logPath)).mtime;
|
|
97
|
-
const fileDate = mtime.toISOString().split("T")[0];
|
|
98
|
-
if (fileDate !== dateStr) {
|
|
99
|
-
shouldOverwrite = true;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const logLine = `[${timeStr}] ${message}`;
|
|
103
|
-
if (shouldOverwrite) {
|
|
104
|
-
await fsWriteFile(logPath, logLine + "\n", "utf-8");
|
|
105
|
-
} else {
|
|
106
|
-
await fsAppendFile(logPath, logLine + "\n", "utf-8");
|
|
107
|
-
}
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error(`[haimati] \u5199\u5165\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:`, error);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function truncateForLog(content, maxLen = 500) {
|
|
113
|
-
if (content.length <= maxLen) return content;
|
|
114
|
-
const half = Math.floor((maxLen - 20) / 2);
|
|
115
|
-
return content.substring(0, half) + "...(\u7701\u7565" + (content.length - maxLen) + "\u5B57\u7B26)..." + content.substring(content.length - half);
|
|
116
|
-
}
|
|
117
|
-
async function log(level, message) {
|
|
118
|
-
await writeLogToFile(`[${level.toUpperCase()}] ${message}`);
|
|
119
|
-
}
|
|
120
|
-
function getTreeDepth(line) {
|
|
121
|
-
let depth = 0;
|
|
122
|
-
let i = 0;
|
|
123
|
-
const len = line.length;
|
|
124
|
-
while (i < len) {
|
|
125
|
-
if (line[i] === "\u2502" && i + 3 < len && line.slice(i + 1, i + 4) === " ") {
|
|
126
|
-
depth++;
|
|
127
|
-
i += 4;
|
|
128
|
-
} else if (line.slice(i, i + 4) === " ") {
|
|
129
|
-
depth++;
|
|
130
|
-
i += 4;
|
|
131
|
-
} else if ((line[i] === "\u251C" || line[i] === "\u2514") && i + 3 < len && line.slice(i + 1, i + 4) === "\u2500\u2500 ") {
|
|
132
|
-
break;
|
|
133
|
-
} else {
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return depth;
|
|
138
|
-
}
|
|
139
|
-
function extractCategoryName(line) {
|
|
140
|
-
const trimmed = line.trim();
|
|
141
|
-
let name = trimmed.replace(/^[│├└─\s]+/, "").trim();
|
|
142
|
-
if (name === "/") {
|
|
143
|
-
return "/";
|
|
144
|
-
}
|
|
145
|
-
if (!name.endsWith("/")) {
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
name = name.slice(0, -1);
|
|
149
|
-
if (!name) return null;
|
|
150
|
-
return name;
|
|
151
|
-
}
|
|
152
|
-
function parseIndex(content) {
|
|
153
|
-
const entries = [];
|
|
154
|
-
const lines = content.split("\n");
|
|
155
|
-
const categoryPath = [];
|
|
156
|
-
for (const line of lines) {
|
|
157
|
-
if (!line.trim()) continue;
|
|
158
|
-
const depth = getTreeDepth(line);
|
|
159
|
-
const entryMatch = line.match(/^\s*[│├└─\s]*(.+?)\s*-\s*(\d+)\s*$/);
|
|
160
|
-
if (entryMatch) {
|
|
161
|
-
const title = entryMatch[1].trim();
|
|
162
|
-
const id = entryMatch[2];
|
|
163
|
-
const entryCategoryParts = categoryPath.slice(0, depth).filter((p) => p !== "");
|
|
164
|
-
const category = entryCategoryParts.join("/");
|
|
165
|
-
entries.push({ id, category, title });
|
|
166
|
-
} else {
|
|
167
|
-
const name = extractCategoryName(line);
|
|
168
|
-
if (name) {
|
|
169
|
-
if (name === "/") {
|
|
170
|
-
categoryPath[depth] = "";
|
|
171
|
-
for (let j = depth + 1; j < categoryPath.length; j++) {
|
|
172
|
-
delete categoryPath[j];
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
categoryPath[depth] = name;
|
|
176
|
-
for (let j = depth + 1; j < categoryPath.length; j++) {
|
|
177
|
-
delete categoryPath[j];
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return entries;
|
|
184
|
-
}
|
|
185
|
-
function findEntryById(entries, id) {
|
|
186
|
-
return entries.find((e) => e.id === id) || null;
|
|
187
|
-
}
|
|
188
|
-
function findEntryByPath(entries, categoryPath, title) {
|
|
189
|
-
return entries.find((e) => e.category === categoryPath && e.title === title) || null;
|
|
190
|
-
}
|
|
191
|
-
async function searchEntries(entries, keyword, matchMode, directory, searchContent = false) {
|
|
192
|
-
const keywords = keyword.toLowerCase().split(/\s+/).filter((k) => k.length > 0);
|
|
193
|
-
if (keywords.length === 0) return [];
|
|
194
|
-
const matchedWithScore = [];
|
|
195
|
-
for (const e of entries) {
|
|
196
|
-
const titleLower = e.title.toLowerCase();
|
|
197
|
-
const categoryLower = e.category.toLowerCase();
|
|
198
|
-
const idLower = e.id.toLowerCase();
|
|
199
|
-
let metaKeywordsCount = 0;
|
|
200
|
-
let contentKeywordsCount = 0;
|
|
201
|
-
for (const kw of keywords) {
|
|
202
|
-
if (titleLower.includes(kw) || categoryLower.includes(kw) || idLower.includes(kw)) {
|
|
203
|
-
metaKeywordsCount++;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (searchContent && directory) {
|
|
207
|
-
const page = await readPage(directory, e.id);
|
|
208
|
-
if (page && page.content) {
|
|
209
|
-
const contentLower = page.content.toLowerCase();
|
|
210
|
-
for (const kw of keywords) {
|
|
211
|
-
if (contentLower.includes(kw)) {
|
|
212
|
-
contentKeywordsCount++;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const totalMatchCount = metaKeywordsCount + contentKeywordsCount;
|
|
218
|
-
if (matchMode === "and") {
|
|
219
|
-
if (totalMatchCount >= keywords.length) {
|
|
220
|
-
matchedWithScore.push({ entry: e, score: totalMatchCount });
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
if (metaKeywordsCount > 0 || searchContent && contentKeywordsCount > 0) {
|
|
224
|
-
matchedWithScore.push({ entry: e, score: totalMatchCount });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
matchedWithScore.sort((a, b) => b.score - a.score);
|
|
229
|
-
return matchedWithScore.map((item) => item.entry);
|
|
230
|
-
}
|
|
231
|
-
async function readNextIdFromFile(directory) {
|
|
232
|
-
const nextIdPath = await getNextIdPath(directory);
|
|
233
|
-
if (!await fsExists(nextIdPath)) {
|
|
234
|
-
return 1;
|
|
235
|
-
}
|
|
236
|
-
const content = await fsReadFile(nextIdPath, "utf-8");
|
|
237
|
-
const trimmed = content.trim();
|
|
238
|
-
const parsed = parseInt(trimmed, 10);
|
|
239
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
240
|
-
return parsed;
|
|
241
|
-
}
|
|
242
|
-
return 1;
|
|
243
|
-
}
|
|
244
|
-
async function initializeNextIdIfNeeded(directory) {
|
|
245
|
-
const nextIdPath = await getNextIdPath(directory);
|
|
246
|
-
if (await fsExists(nextIdPath)) {
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const entries = await readIndex(directory);
|
|
250
|
-
let maxId = 0;
|
|
251
|
-
for (const e of entries) {
|
|
252
|
-
const n = parseInt(e.id, 10);
|
|
253
|
-
if (n > maxId) maxId = n;
|
|
254
|
-
}
|
|
255
|
-
await writeNextIdToFile(directory, maxId + 1);
|
|
256
|
-
}
|
|
257
|
-
async function writeNextIdToFile(directory, nextId) {
|
|
258
|
-
const haimatiDir = await getHaimatiDir(directory);
|
|
259
|
-
if (!await fsExists(haimatiDir)) {
|
|
260
|
-
await fsMkdir(haimatiDir, { recursive: true });
|
|
261
|
-
}
|
|
262
|
-
const nextIdPath = await getNextIdPath(directory);
|
|
263
|
-
await fsWriteFile(nextIdPath, String(nextId), "utf-8");
|
|
264
|
-
}
|
|
265
|
-
async function acquireLock(directory) {
|
|
266
|
-
const haimatiDir = await getHaimatiDir(directory);
|
|
267
|
-
if (!await fsExists(haimatiDir)) {
|
|
268
|
-
await fsMkdir(haimatiDir, { recursive: true });
|
|
269
|
-
}
|
|
270
|
-
const lockPath = await getLockPath(directory);
|
|
271
|
-
const startTime = Date.now();
|
|
272
|
-
while (await fsExists(lockPath)) {
|
|
273
|
-
if (Date.now() - startTime > LOCK_MAX_WAIT) {
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
await new Promise((resolve) => setTimeout(resolve, 50 + Math.random() * 20));
|
|
277
|
-
}
|
|
278
|
-
await fsWriteFile(lockPath, String(process.pid), "utf-8");
|
|
279
|
-
return true;
|
|
280
|
-
}
|
|
281
|
-
async function releaseLock(directory) {
|
|
282
|
-
const lockPath = await getLockPath(directory);
|
|
283
|
-
if (await fsExists(lockPath)) {
|
|
284
|
-
await fsUnlink(lockPath);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
async function readPage(directory, id) {
|
|
288
|
-
const pagePath = await getPagePath(directory, id);
|
|
289
|
-
if (!await fsExists(pagePath)) {
|
|
290
|
-
return null;
|
|
291
|
-
}
|
|
292
|
-
const content = await fsReadFile(pagePath, "utf-8");
|
|
293
|
-
const lines = content.split("\n");
|
|
294
|
-
if (lines.length < 2) {
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
let version = 1;
|
|
298
|
-
let actualContentLines = lines.slice(2);
|
|
299
|
-
const versionMatchIndex = content.lastIndexOf(VERSION_SEPARATOR);
|
|
300
|
-
if (versionMatchIndex !== -1) {
|
|
301
|
-
const versionStr = content.substring(versionMatchIndex + VERSION_SEPARATOR.length);
|
|
302
|
-
version = parseInt(versionStr.trim(), 10) || DEFAULT_VERSION;
|
|
303
|
-
actualContentLines = lines.slice(2, lines.length - 2);
|
|
304
|
-
}
|
|
305
|
-
return {
|
|
306
|
-
title: lines[0],
|
|
307
|
-
createdAt: lines[1],
|
|
308
|
-
content: actualContentLines.join("\n"),
|
|
309
|
-
version
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
async function writePage(directory, id, title, createdAt, content, version = 1) {
|
|
313
|
-
const pagesDir = await getPagesDir(directory);
|
|
314
|
-
if (!await fsExists(pagesDir)) {
|
|
315
|
-
await fsMkdir(pagesDir, { recursive: true });
|
|
316
|
-
}
|
|
317
|
-
const pageGroupDir = await getPageGroupDir(directory, id);
|
|
318
|
-
if (!await fsExists(pageGroupDir)) {
|
|
319
|
-
await fsMkdir(pageGroupDir, { recursive: true });
|
|
320
|
-
}
|
|
321
|
-
const pagePath = await getPagePath(directory, id);
|
|
322
|
-
const fileContent = `${title}
|
|
323
|
-
${createdAt}
|
|
324
|
-
${content}${VERSION_SEPARATOR}${version}`;
|
|
325
|
-
await fsWriteFile(pagePath, fileContent, "utf-8");
|
|
326
|
-
}
|
|
327
|
-
async function deletePage(directory, id) {
|
|
328
|
-
const pagePath = await getPagePath(directory, id);
|
|
329
|
-
if (await fsExists(pagePath)) {
|
|
330
|
-
await fsUnlink(pagePath);
|
|
331
|
-
return true;
|
|
332
|
-
}
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
function buildIndexTree(entries) {
|
|
336
|
-
if (entries.length === 0) {
|
|
337
|
-
return "";
|
|
338
|
-
}
|
|
339
|
-
const root = { name: "/", children: [], entry: null };
|
|
340
|
-
for (const entry of entries) {
|
|
341
|
-
const parts = entry.category.split("/");
|
|
342
|
-
let current = root;
|
|
343
|
-
for (let i = 0; i < parts.length; i++) {
|
|
344
|
-
const part = parts[i];
|
|
345
|
-
if (!part) continue;
|
|
346
|
-
let child = current.children.find((c) => c.name === part);
|
|
347
|
-
if (!child) {
|
|
348
|
-
child = { name: part, children: [], entry: null };
|
|
349
|
-
current.children.push(child);
|
|
350
|
-
}
|
|
351
|
-
current = child;
|
|
352
|
-
}
|
|
353
|
-
const entryNode = {
|
|
354
|
-
name: entry.id,
|
|
355
|
-
children: [],
|
|
356
|
-
entry
|
|
357
|
-
};
|
|
358
|
-
current.children.push(entryNode);
|
|
359
|
-
}
|
|
360
|
-
function sortNode(node) {
|
|
361
|
-
node.children.sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }));
|
|
362
|
-
for (const child of node.children) {
|
|
363
|
-
sortNode(child);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
sortNode(root);
|
|
367
|
-
function nodeToLines(node, prefix, isLast) {
|
|
368
|
-
const lines = [];
|
|
369
|
-
const displayPrefix = prefix + (isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ");
|
|
370
|
-
const nodeName = node.name === "/" ? "/" : node.name + "/";
|
|
371
|
-
lines.push(`${displayPrefix}${nodeName}`);
|
|
372
|
-
prefix = prefix + (isLast ? " " : "\u2502 ");
|
|
373
|
-
const categories = node.children.filter((c) => c.entry === null);
|
|
374
|
-
const items = node.children.filter((c) => c.entry !== null);
|
|
375
|
-
const allChildren = [...categories, ...items];
|
|
376
|
-
for (let i = 0; i < allChildren.length; i++) {
|
|
377
|
-
const child = allChildren[i];
|
|
378
|
-
const childIsLast = i === allChildren.length - 1;
|
|
379
|
-
if (child.entry) {
|
|
380
|
-
const itemPrefix = prefix + (childIsLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ");
|
|
381
|
-
lines.push(`${itemPrefix}${child.entry.title} - ${child.entry.id}`);
|
|
382
|
-
} else {
|
|
383
|
-
lines.push(...nodeToLines(child, prefix, childIsLast));
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return lines;
|
|
387
|
-
}
|
|
388
|
-
return nodeToLines(root, "", true).join("\n");
|
|
389
|
-
}
|
|
390
|
-
function removeIndexEntry(entries, id) {
|
|
391
|
-
return entries.filter((e) => e.id !== id);
|
|
392
|
-
}
|
|
393
|
-
async function readIndex(directory) {
|
|
394
|
-
const indexPath = await getIndexPath(directory);
|
|
395
|
-
if (!await fsExists(indexPath)) {
|
|
396
|
-
return [];
|
|
397
|
-
}
|
|
398
|
-
const content = await fsReadFile(indexPath, "utf-8");
|
|
399
|
-
return parseIndex(content);
|
|
400
|
-
}
|
|
401
|
-
async function writeIndex(directory, entries) {
|
|
402
|
-
const haimatiDir = await getHaimatiDir(directory);
|
|
403
|
-
if (!await fsExists(haimatiDir)) {
|
|
404
|
-
await fsMkdir(haimatiDir, { recursive: true });
|
|
405
|
-
}
|
|
406
|
-
const tree = buildIndexTree(entries);
|
|
407
|
-
const indexPath = await getIndexPath(directory);
|
|
408
|
-
await fsWriteFile(indexPath, tree, "utf-8");
|
|
409
|
-
}
|
|
410
|
-
async function allocateIdWithLock(directory, category, title, now, content) {
|
|
411
|
-
if (!await acquireLock(directory)) {
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
try {
|
|
415
|
-
await initializeNextIdIfNeeded(directory);
|
|
416
|
-
const entries = await readIndex(directory);
|
|
417
|
-
const existingEntry = entries.find((e) => e.category === category && e.title === title);
|
|
418
|
-
if (existingEntry) {
|
|
419
|
-
const page = await readPage(directory, existingEntry.id);
|
|
420
|
-
const createdAt = page ? page.createdAt : now;
|
|
421
|
-
const newVersion = page ? page.version + 1 : 1;
|
|
422
|
-
await writePage(directory, existingEntry.id, title, createdAt, content, newVersion);
|
|
423
|
-
await log("info", `[allocateIdWithLock] \u66F4\u65B0\u5DF2\u6709\u6761\u76EE: #${existingEntry.id} ${category}/${title}, \u65B0\u7248\u672C: ${newVersion}`);
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
const currentNext = await readNextIdFromFile(directory);
|
|
427
|
-
const newId = String(currentNext).padStart(3, "0");
|
|
428
|
-
await writePage(directory, newId, title, now, content, 1);
|
|
429
|
-
const newEntry = {
|
|
430
|
-
id: newId,
|
|
431
|
-
category,
|
|
432
|
-
title
|
|
433
|
-
};
|
|
434
|
-
entries.push(newEntry);
|
|
435
|
-
await writeIndex(directory, entries);
|
|
436
|
-
await writeNextIdToFile(directory, currentNext + 1);
|
|
437
|
-
return newId;
|
|
438
|
-
} catch (error) {
|
|
439
|
-
const errMsg = error instanceof Error ? error.message : String(error);
|
|
440
|
-
await log("error", `[allocateIdWithLock] \u5206\u914D\u5E8F\u53F7\u5931\u8D25: ${errMsg}`);
|
|
441
|
-
return null;
|
|
442
|
-
} finally {
|
|
443
|
-
await releaseLock(directory);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
var HaimatiPlugin = async (ctx) => {
|
|
447
|
-
await log("info", `\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u63D2\u4EF6(\u6587\u4EF6\u7248)\u5DF2\u52A0\u8F7D`);
|
|
448
|
-
return {
|
|
449
|
-
tool: {
|
|
450
|
-
/**
|
|
451
|
-
* haimati_read - 从海马体读取记忆
|
|
452
|
-
*
|
|
453
|
-
* 只支持序号查询:如 "086"
|
|
454
|
-
*/
|
|
455
|
-
haimati_read: tool({
|
|
456
|
-
description: "\u4ECE\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u8BFB\u53D6\u5185\u5BB9\u3002\u901A\u8FC7\u5E8F\u53F7\uFF08\u5982 '086'\uFF09\u6765\u5B9A\u4F4D\u5E76\u8BFB\u53D6\u8BB0\u5FC6\u5185\u5BB9\u3002\u652F\u6301\u5206\u9875\u8BFB\u53D6\uFF08offset/limit\uFF09\uFF0C\u8FD4\u56DE\u5185\u5BB9\u5E26\u884C\u53F7\u548C\u7248\u672C\u53F7\u3002",
|
|
457
|
-
args: {
|
|
458
|
-
query: tool.schema.string().describe("\u5E8F\u53F7\u3002\u4F8B\u5982\uFF1A'086'"),
|
|
459
|
-
offset: tool.schema.number().optional().default(1).describe("\u8D77\u59CB\u884C\u53F7\uFF08\u9ED8\u8BA4 1\uFF09"),
|
|
460
|
-
limit: tool.schema.number().optional().default(2e3).describe("\u6700\u5927\u8BFB\u53D6\u884C\u6570\uFF08\u9ED8\u8BA4 2000\uFF09")
|
|
461
|
-
},
|
|
462
|
-
/**
|
|
463
|
-
* 返回格式:
|
|
464
|
-
* version:1
|
|
465
|
-
* 1|第一行内容
|
|
466
|
-
* 2|第二行内容
|
|
467
|
-
* ...
|
|
468
|
-
*
|
|
469
|
-
* version 用于后续 haimati_edit 的并发控制,请妥善保存
|
|
470
|
-
*/
|
|
471
|
-
async execute(args, context) {
|
|
472
|
-
const { directory } = context;
|
|
473
|
-
const indexPath = await getIndexPath(directory);
|
|
474
|
-
await log("info", `[haimati_read] \u5165\u53C2: query="${args.query}", offset=${args.offset || 1}, limit=${args.limit || 2e3}`);
|
|
475
|
-
if (!await fsExists(indexPath)) {
|
|
476
|
-
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728\u4E8E ${indexPath}`;
|
|
477
|
-
await log("warn", `[haimati_read] \u51FA\u53C2:
|
|
478
|
-
${truncateForLog(result)}`);
|
|
479
|
-
return result;
|
|
480
|
-
}
|
|
481
|
-
try {
|
|
482
|
-
const entries = await readIndex(directory);
|
|
483
|
-
await log("debug", `[haimati_read] \u8BFB\u53D6\u7D22\u5F15: \u5171 ${entries.length} \u6761\u8BB0\u5F55`);
|
|
484
|
-
const query = args.query.trim();
|
|
485
|
-
if (!/^\d+$/.test(query)) {
|
|
486
|
-
const result2 = `\u9519\u8BEF\uFF1Aquery \u5FC5\u987B\u662F\u5E8F\u53F7\uFF0C\u53EA\u652F\u6301\u5E8F\u53F7\u67E5\u8BE2`;
|
|
487
|
-
await log("warn", `[haimati_read] \u51FA\u53C2:
|
|
488
|
-
${truncateForLog(result2)}`);
|
|
489
|
-
return result2;
|
|
490
|
-
}
|
|
491
|
-
const entry = findEntryById(entries, query);
|
|
492
|
-
await log("debug", `[haimati_read] \u5E8F\u53F7\u67E5\u8BE2 #${query}: ${entry ? "\u627E\u5230" : "\u672A\u627E\u5230"}`);
|
|
493
|
-
if (!entry) {
|
|
494
|
-
const result2 = `\u672A\u627E\u5230\u8BB0\u5FC6: ${args.query}`;
|
|
495
|
-
await log("info", `[haimati_read] \u51FA\u53C2:
|
|
496
|
-
${truncateForLog(result2)}`);
|
|
497
|
-
return result2;
|
|
498
|
-
}
|
|
499
|
-
const page = await readPage(directory, entry.id);
|
|
500
|
-
if (!page) {
|
|
501
|
-
const result2 = `\u672A\u627E\u5230\u4E66\u9875: #${entry.id}`;
|
|
502
|
-
await log("error", `[haimati_read] \u51FA\u53C2:
|
|
503
|
-
${truncateForLog(result2)}`);
|
|
504
|
-
return result2;
|
|
505
|
-
}
|
|
506
|
-
const pageGroupDir = await getPageGroupDir(directory, entry.id);
|
|
507
|
-
await log("debug", `[haimati_read] \u8BFB\u53D6\u4E66\u9875: \u4E66\u9875/${pageGroupDir.split(/[\\/]/).pop()}/${entry.id}.md, version=${page.version}`);
|
|
508
|
-
const contentLines = page.content.split("\n");
|
|
509
|
-
const offset = args.offset || 1;
|
|
510
|
-
const limit = args.limit || 2e3;
|
|
511
|
-
const totalLines = contentLines.length;
|
|
512
|
-
const startIndex = Math.max(0, offset - 1);
|
|
513
|
-
const endIndex = Math.min(totalLines, startIndex + limit);
|
|
514
|
-
const linesOutput = [];
|
|
515
|
-
linesOutput.push(`version:${page.version}`);
|
|
516
|
-
for (let i = startIndex; i < endIndex; i++) {
|
|
517
|
-
linesOutput.push(`${i + 1}|${contentLines[i]}`);
|
|
518
|
-
}
|
|
519
|
-
const result = linesOutput.join("\n");
|
|
520
|
-
await log("info", `[haimati_read] \u51FA\u53C2:
|
|
521
|
-
${truncateForLog(result)}`);
|
|
522
|
-
return result;
|
|
523
|
-
} catch (error) {
|
|
524
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
525
|
-
const stack = error instanceof Error ? error.stack : "";
|
|
526
|
-
await log("error", `[haimati_read] \u51FA\u53C2: \u9519\u8BEF: ${errorMsg}
|
|
527
|
-
\u5806\u6808: ${stack}`);
|
|
528
|
-
return `\u9519\u8BEF: ${errorMsg}`;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}),
|
|
532
|
-
/**
|
|
533
|
-
* haimati_write - 将新信息写入海马体记忆系统
|
|
534
|
-
*
|
|
535
|
-
* 会自动分配新的序号并存储到文件
|
|
536
|
-
*/
|
|
537
|
-
haimati_write: tool({
|
|
538
|
-
description: "\u5C06\u65B0\u4FE1\u606F\u5199\u5165\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u3002\u9700\u8981\u63D0\u4F9B\u5206\u7C7B\u8DEF\u5F84\uFF08\u5982 '\u6807\u7ACB\u65B9/\u7B7E\u7AE0'\uFF09\u3001\u6807\u9898\u548C\u5185\u5BB9\u3002",
|
|
539
|
-
args: {
|
|
540
|
-
category: tool.schema.string().describe("\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 '\u6807\u7ACB\u65B9/\u7B7E\u7AE0' \u6216 '\u901A\u7528'"),
|
|
541
|
-
title: tool.schema.string().describe("\u8BB0\u5FC6\u6807\u9898\uFF0C\u5982 '\u7B7E\u7AE0\u670D\u52A1WS\u901A\u4FE1\u673A\u5236'"),
|
|
542
|
-
content: tool.schema.string().describe("\u8981\u5199\u5165\u7684\u8BE6\u7EC6\u5185\u5BB9")
|
|
543
|
-
},
|
|
544
|
-
async execute(args, context) {
|
|
545
|
-
const { directory } = context;
|
|
546
|
-
await log("info", `[haimati_write] \u5165\u53C2: category="${args.category}", title="${args.title}", content\u957F\u5EA6=${args.content.length}\u5B57\u7B26, content=
|
|
547
|
-
${truncateForLog(args.content)}
|
|
548
|
-
`);
|
|
549
|
-
try {
|
|
550
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
551
|
-
let newId = null;
|
|
552
|
-
for (let retry = 0; retry < 3; retry++) {
|
|
553
|
-
newId = await allocateIdWithLock(directory, args.category, args.title, now, args.content);
|
|
554
|
-
if (newId) break;
|
|
555
|
-
await log("warn", `[haimati_write] \u9501\u83B7\u53D6\u5931\u8D25\uFF0C\u91CD\u8BD5\u7B2C ${retry + 1} \u6B21`);
|
|
556
|
-
}
|
|
557
|
-
if (newId) {
|
|
558
|
-
const pageGroupDir = await getPageGroupDir(directory, newId);
|
|
559
|
-
await log("debug", `[haimati_write] \u5206\u914D\u65B0\u5E8F\u53F7: #${newId}`);
|
|
560
|
-
await log("debug", `[haimati_write] \u4E66\u9875\u548C\u7D22\u5F15\u5DF2\u66F4\u65B0: \u4E66\u9875/${pageGroupDir.split(/[\\/]/).pop()}/${newId}.md`);
|
|
561
|
-
const result2 = `\u5DF2\u5199\u5165\u6D77\u9A6C\u4F53 #${newId}
|
|
562
|
-
\u8DEF\u5F84: ${args.category}/${args.title}
|
|
563
|
-
\u7248\u672C: 1`;
|
|
564
|
-
await log("info", `[haimati_write] \u51FA\u53C2:
|
|
565
|
-
${truncateForLog(result2)}`);
|
|
566
|
-
return result2;
|
|
567
|
-
}
|
|
568
|
-
const entries = await readIndex(directory);
|
|
569
|
-
const existingEntry = findEntryByPath(entries, args.category, args.title);
|
|
570
|
-
if (existingEntry) {
|
|
571
|
-
const page = await readPage(directory, existingEntry.id);
|
|
572
|
-
const version = page ? page.version : 1;
|
|
573
|
-
const result2 = `\u5DF2\u66F4\u65B0\u6D77\u9A6C\u4F53 #${existingEntry.id}
|
|
574
|
-
\u8DEF\u5F84: ${args.category}/${args.title}
|
|
575
|
-
\u7248\u672C: ${version}`;
|
|
576
|
-
await log("info", `[haimati_write] \u51FA\u53C2:
|
|
577
|
-
${truncateForLog(result2)}`);
|
|
578
|
-
return result2;
|
|
579
|
-
}
|
|
580
|
-
const result = `\u9519\u8BEF\uFF1A\u65E0\u6CD5\u5206\u914D\u5E8F\u53F7\uFF08\u9501\u7B49\u5F85\u8D85\u65F6\uFF09`;
|
|
581
|
-
await log("error", `[haimati_write] \u51FA\u53C2:
|
|
582
|
-
${truncateForLog(result)}`);
|
|
583
|
-
return result;
|
|
584
|
-
} catch (error) {
|
|
585
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
586
|
-
const stack = error instanceof Error ? error.stack : "";
|
|
587
|
-
await log("error", `[haimati_write] \u51FA\u53C2: \u9519\u8BEF: ${errorMsg}
|
|
588
|
-
\u5806\u6808: ${stack}`);
|
|
589
|
-
return `\u9519\u8BEF: ${errorMsg}`;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}),
|
|
593
|
-
/**
|
|
594
|
-
* haimati_search - 搜索海马体记忆系统
|
|
595
|
-
*
|
|
596
|
-
* 在标题、分类、序号和书页内容中进行搜索
|
|
597
|
-
*/
|
|
598
|
-
haimati_search: tool({
|
|
599
|
-
description: "\u641C\u7D22\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\uFF08\u652F\u6301\u591A\u5173\u952E\u5B57\uFF0C\u7528\u7A7A\u683C\u5206\u9694\uFF09\u3002\u641C\u7D22\u8303\u56F4\u5305\u62EC\uFF1A\u6807\u9898\u3001\u5206\u7C7B\u3001\u5E8F\u53F7\u3001\u4E66\u9875\u5185\u5BB9\u3002\u8F93\u5165\u5173\u952E\u8BCD\uFF0C\u8FD4\u56DE\u5339\u914D\u7684\u6761\u76EE\u5217\u8868\u548C\u5185\u5BB9\u7247\u6BB5\uFF0C\u652F\u6301\u5206\u9875\uFF08limit/offset\uFF09\u3002",
|
|
600
|
-
args: {
|
|
601
|
-
keyword: tool.schema.string().describe("\u641C\u7D22\u5173\u952E\u8BCD\uFF08\u652F\u6301\u591A\u5173\u952E\u5B57\uFF0C\u7528\u7A7A\u683C\u5206\u9694\uFF09"),
|
|
602
|
-
match: tool.schema.string().describe("\u5339\u914D\u6A21\u5F0F\uFF1Aand=\u6240\u6709\u5173\u952E\u5B57\u90FD\u5339\u914D\uFF0Cor=\u4EFB\u610F\u5173\u952E\u5B57\u5339\u914D"),
|
|
603
|
-
limit: tool.schema.number().default(10).describe("\u8FD4\u56DE\u7ED3\u679C\u6570\u91CF\u9650\u5236"),
|
|
604
|
-
offset: tool.schema.number().default(0).describe("\u5206\u9875\u504F\u79FB\u91CF\uFF0C\u7528\u4E8E\u83B7\u53D6\u540E\u7EED\u9875\u9762")
|
|
605
|
-
},
|
|
606
|
-
async execute(args, context) {
|
|
607
|
-
const { directory } = context;
|
|
608
|
-
const indexPath = await getIndexPath(directory);
|
|
609
|
-
await log("info", `[haimati_search] \u5165\u53C2: keyword="${args.keyword}", match="${args.match}", limit=${args.limit || 10}, offset=${args.offset || 0}`);
|
|
610
|
-
if (!await fsExists(indexPath)) {
|
|
611
|
-
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
612
|
-
await log("warn", `[haimati_search] \u51FA\u53C2:
|
|
613
|
-
${truncateForLog(result)}`);
|
|
614
|
-
return result;
|
|
615
|
-
}
|
|
616
|
-
try {
|
|
617
|
-
const entries = await readIndex(directory);
|
|
618
|
-
const keyword = args.keyword.trim();
|
|
619
|
-
const match = args.match;
|
|
620
|
-
const limit = args.limit || 10;
|
|
621
|
-
const offset = args.offset || 0;
|
|
622
|
-
const allMatched = await searchEntries(entries, keyword, match, directory, true);
|
|
623
|
-
const totalCount = allMatched.length;
|
|
624
|
-
const matched = allMatched.slice(offset, offset + limit);
|
|
625
|
-
if (matched.length === 0) {
|
|
626
|
-
const result2 = `\u672A\u627E\u5230\u5305\u542B '${keyword}' \u7684\u8BB0\u5FC6`;
|
|
627
|
-
await log("info", `[haimati_search] \u51FA\u53C2:
|
|
628
|
-
${truncateForLog(result2)}`);
|
|
629
|
-
return result2;
|
|
630
|
-
}
|
|
631
|
-
const results = [];
|
|
632
|
-
const hasMore = offset + matched.length < totalCount;
|
|
633
|
-
const displayCount = hasMore ? `${offset + 1}-${offset + matched.length}/${totalCount}` : `${offset + 1}-${offset + matched.length}/${totalCount}`;
|
|
634
|
-
results.push(`\u627E\u5230 ${displayCount} \u6761\u5339\u914D\uFF1A
|
|
635
|
-
`);
|
|
636
|
-
if (hasMore) {
|
|
637
|
-
results.push(`\uFF08\u5982\u9700\u83B7\u53D6\u66F4\u591A\uFF0C\u8BF7\u4F7F\u7528 offset=${offset + limit} \u7EE7\u7EED\u7FFB\u9875\uFF09
|
|
638
|
-
`);
|
|
639
|
-
}
|
|
640
|
-
for (const entry of matched) {
|
|
641
|
-
const page = await readPage(directory, entry.id);
|
|
642
|
-
const fullPath = `${entry.category}/${entry.title}`;
|
|
643
|
-
let excerpt = "";
|
|
644
|
-
if (page) {
|
|
645
|
-
const lines = page.content.split("\n").filter((l) => l.trim());
|
|
646
|
-
excerpt = lines.slice(0, 3).join(" | ").substring(0, 150);
|
|
647
|
-
if (excerpt.length === 150) excerpt += "...";
|
|
648
|
-
}
|
|
649
|
-
results.push(`## ${fullPath} [${entry.id}]`);
|
|
650
|
-
if (excerpt) {
|
|
651
|
-
results.push(`> ${excerpt}`);
|
|
652
|
-
}
|
|
653
|
-
results.push("");
|
|
654
|
-
}
|
|
655
|
-
const result = results.join("\n");
|
|
656
|
-
await log("info", `[haimati_search] \u51FA\u53C2:
|
|
657
|
-
${truncateForLog(result)}`);
|
|
658
|
-
return result;
|
|
659
|
-
} catch (error) {
|
|
660
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
661
|
-
const stack = error instanceof Error ? error.stack : "";
|
|
662
|
-
await log("error", `[haimati_search] \u51FA\u53C2: \u9519\u8BEF: ${errorMsg}
|
|
663
|
-
\u5806\u6808: ${stack}`);
|
|
664
|
-
return `\u9519\u8BEF: ${errorMsg}`;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}),
|
|
668
|
-
/**
|
|
669
|
-
* haimati_edit - 修改海马体中的记忆内容(部分替换)
|
|
670
|
-
*
|
|
671
|
-
* 使用流程:
|
|
672
|
-
* 1. 先调用 haimati_read 获取当前内容的行号和 version
|
|
673
|
-
* 2. 传入 offsetBegin/offsetEnd 指定替换范围,传入 content 替换内容
|
|
674
|
-
* 3. 传入 read 返回的 version 进行并发控制
|
|
675
|
-
*
|
|
676
|
-
* 并发控制原理:
|
|
677
|
-
* - 如果在你读取后、其他会话修改了该记忆,version会变化
|
|
678
|
-
* - 写入时检查 version 是否匹配,不匹配则拒绝写入
|
|
679
|
-
* - 写入成功后 version 自动+1
|
|
680
|
-
*/
|
|
681
|
-
haimati_edit: tool({
|
|
682
|
-
description: "\u4FEE\u6539\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7684\u90E8\u5206\u5185\u5BB9\u3002\u5C06\u6307\u5B9A\u884C\u53F7\u8303\u56F4 [offsetBegin, offsetEnd) \u7684\u5185\u5BB9\u66FF\u6362\u4E3A\u65B0 content\u3002\u9700\u8981\u63D0\u4F9B read \u65F6\u8FD4\u56DE\u7684 version \u8FDB\u884C\u5E76\u53D1\u63A7\u5236\u3002",
|
|
683
|
-
args: {
|
|
684
|
-
query: tool.schema.string().describe("\u5E8F\u53F7\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u66F4\u65B0\u7684\u8BB0\u5FC6"),
|
|
685
|
-
offsetBegin: tool.schema.number().describe("\u8D77\u59CB\u884C\u53F7\uFF08\u5305\u62EC\uFF09"),
|
|
686
|
-
offsetEnd: tool.schema.number().describe("\u7ED3\u675F\u884C\u53F7\uFF08\u4E0D\u5305\u62EC\uFF09"),
|
|
687
|
-
content: tool.schema.string().describe("\u66FF\u6362\u5185\u5BB9"),
|
|
688
|
-
version: tool.schema.number().describe("read \u65F6\u8FD4\u56DE\u7684 version\uFF0C\u7528\u4E8E\u5E76\u53D1\u63A7\u5236")
|
|
689
|
-
},
|
|
690
|
-
async execute(args, context) {
|
|
691
|
-
const { directory } = context;
|
|
692
|
-
const indexPath = await getIndexPath(directory);
|
|
693
|
-
await log("info", `[haimati_edit] \u5165\u53C2: query="${args.query}", offsetBegin=${args.offsetBegin}, offsetEnd=${args.offsetEnd}, content\u957F\u5EA6=${args.content.length}\u5B57\u7B26, version=${args.version}, content=
|
|
694
|
-
${truncateForLog(args.content)}
|
|
695
|
-
`);
|
|
696
|
-
if (!await fsExists(indexPath)) {
|
|
697
|
-
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
698
|
-
await log("warn", `[haimati_edit] \u51FA\u53C2:
|
|
699
|
-
${truncateForLog(result)}`);
|
|
700
|
-
return result;
|
|
701
|
-
}
|
|
702
|
-
try {
|
|
703
|
-
const entries = await readIndex(directory);
|
|
704
|
-
const query = args.query.trim();
|
|
705
|
-
if (!/^\d+$/.test(query)) {
|
|
706
|
-
const result2 = `\u9519\u8BEF\uFF1Aquery \u5FC5\u987B\u662F\u5E8F\u53F7\uFF0C\u53EA\u652F\u6301\u5E8F\u53F7\u67E5\u8BE2`;
|
|
707
|
-
await log("warn", `[haimati_edit] \u51FA\u53C2:
|
|
708
|
-
${truncateForLog(result2)}`);
|
|
709
|
-
return result2;
|
|
710
|
-
}
|
|
711
|
-
const entry = findEntryById(entries, query);
|
|
712
|
-
if (!entry) {
|
|
713
|
-
const result2 = `\u672A\u627E\u5230\u4E0E '${query}' \u76F8\u5173\u7684\u8BB0\u5FC6`;
|
|
714
|
-
await log("info", `[haimati_edit] \u51FA\u53C2:
|
|
715
|
-
${truncateForLog(result2)}`);
|
|
716
|
-
return result2;
|
|
717
|
-
}
|
|
718
|
-
const page = await readPage(directory, entry.id);
|
|
719
|
-
if (!page) {
|
|
720
|
-
const result2 = `\u672A\u627E\u5230\u4E66\u9875: #${entry.id}`;
|
|
721
|
-
await log("error", `[haimati_edit] \u51FA\u53C2:
|
|
722
|
-
${truncateForLog(result2)}`);
|
|
723
|
-
return result2;
|
|
724
|
-
}
|
|
725
|
-
const offsetBegin = args.offsetBegin;
|
|
726
|
-
const offsetEnd = args.offsetEnd;
|
|
727
|
-
if (offsetBegin > page.content.split("\n").length || offsetBegin >= offsetEnd) {
|
|
728
|
-
const result2 = `\u9519\u8BEF\uFF1Aoffset \u975E\u6CD5\uFF0CoffsetBegin=${offsetBegin}, offsetEnd=${offsetEnd}, \u6587\u4EF6\u603B\u884C\u6570=${page.content.split("\n").length}`;
|
|
729
|
-
await log("warn", `[haimati_edit] \u51FA\u53C2:
|
|
730
|
-
${truncateForLog(result2)}`);
|
|
731
|
-
return result2;
|
|
732
|
-
}
|
|
733
|
-
if (page.version !== args.version) {
|
|
734
|
-
const result2 = `\u9519\u8BEF\uFF1A\u7248\u672C\u51B2\u7A81\uFF0C\u5F53\u524D version=${page.version}\uFF0C\u4F20\u5165 version=${args.version}\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u518D\u64CD\u4F5C`;
|
|
735
|
-
await log("warn", `[haimati_edit] \u51FA\u53C2:
|
|
736
|
-
${truncateForLog(result2)}`);
|
|
737
|
-
return result2;
|
|
738
|
-
}
|
|
739
|
-
const contentLines = page.content.split("\n");
|
|
740
|
-
const newContentLines = [
|
|
741
|
-
...contentLines.slice(0, offsetBegin - 1),
|
|
742
|
-
...args.content.split("\n"),
|
|
743
|
-
...contentLines.slice(offsetEnd - 1)
|
|
744
|
-
];
|
|
745
|
-
const newContent = newContentLines.join("\n");
|
|
746
|
-
const newVersion = page.version + 1;
|
|
747
|
-
await writePage(directory, entry.id, entry.title, page.createdAt, newContent, newVersion);
|
|
748
|
-
const fullPath = `${entry.category}/${entry.title}`;
|
|
749
|
-
const result = `\u5DF2\u66F4\u65B0\u6D77\u9A6C\u4F53 #${entry.id}
|
|
750
|
-
\u8DEF\u5F84: ${fullPath}
|
|
751
|
-
\u65B0\u7248\u672C: ${newVersion}`;
|
|
752
|
-
await log("info", `[haimati_edit] \u51FA\u53C2:
|
|
753
|
-
${truncateForLog(result)}`);
|
|
754
|
-
return result;
|
|
755
|
-
} catch (error) {
|
|
756
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
757
|
-
const stack = error instanceof Error ? error.stack : "";
|
|
758
|
-
await log("error", `[haimati_edit] \u51FA\u53C2: \u9519\u8BEF: ${errorMsg}
|
|
759
|
-
\u5806\u6808: ${stack}`);
|
|
760
|
-
return `\u9519\u8BEF: ${errorMsg}`;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
}),
|
|
764
|
-
/**
|
|
765
|
-
* haimati_delete - 删除海马体中的记忆
|
|
766
|
-
*/
|
|
767
|
-
haimati_delete: tool({
|
|
768
|
-
description: "\u5220\u9664\u6D77\u9A6C\u4F53\u4E2D\u7684\u8BB0\u5FC6\u3002\u901A\u8FC7\u5E8F\u53F7\uFF08\u5982 '086'\uFF09\u6216\u5B8C\u6574\u8DEF\u5F84\uFF08\u5982 '\u6807\u7ACB\u65B9/\u7B7E\u7AE0/\u7B7E\u7AE0\u670D\u52A1WS\u901A\u4FE1\u673A\u5236'\uFF09\u6765\u5B9A\u4F4D\u8981\u5220\u9664\u7684\u8BB0\u5FC6\u3002",
|
|
769
|
-
args: {
|
|
770
|
-
query: tool.schema.string().describe("\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u5220\u9664\u7684\u8BB0\u5FC6")
|
|
771
|
-
},
|
|
772
|
-
async execute(args, context) {
|
|
773
|
-
const { directory } = context;
|
|
774
|
-
const indexPath = await getIndexPath(directory);
|
|
775
|
-
await log("info", `[haimati_delete] \u5165\u53C2: query="${args.query}"`);
|
|
776
|
-
if (!await fsExists(indexPath)) {
|
|
777
|
-
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
778
|
-
await log("warn", `[haimati_delete] \u51FA\u53C2:
|
|
779
|
-
${truncateForLog(result)}`);
|
|
780
|
-
return result;
|
|
781
|
-
}
|
|
782
|
-
try {
|
|
783
|
-
const entries = await readIndex(directory);
|
|
784
|
-
const query = args.query.trim();
|
|
785
|
-
let entry = null;
|
|
786
|
-
if (/^\d+$/.test(query)) {
|
|
787
|
-
entry = findEntryById(entries, query);
|
|
788
|
-
} else {
|
|
789
|
-
const parts = query.split("/").filter(Boolean);
|
|
790
|
-
if (parts.length >= 2) {
|
|
791
|
-
const title = parts.pop();
|
|
792
|
-
const category = parts.join("/");
|
|
793
|
-
entry = findEntryByPath(entries, category, title);
|
|
794
|
-
} else {
|
|
795
|
-
const results = await searchEntries(entries, query, "or", directory);
|
|
796
|
-
if (results.length === 1) {
|
|
797
|
-
entry = results[0];
|
|
798
|
-
} else if (results.length > 1) {
|
|
799
|
-
const list = results.slice(0, 10).map((e) => ` - ${e.category}/${e.title} [${e.id}]`).join("\n");
|
|
800
|
-
return `\u627E\u5230\u591A\u6761\u5339\u914D\uFF0C\u8BF7\u4F7F\u7528\u5E8F\u53F7\u7CBE\u786E\u6307\u5B9A\uFF1A
|
|
801
|
-
${list}`;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
if (!entry) {
|
|
806
|
-
const result2 = `\u672A\u627E\u5230\u4E0E '${query}' \u76F8\u5173\u7684\u8BB0\u5FC6`;
|
|
807
|
-
await log("info", `[haimati_delete] \u51FA\u53C2:
|
|
808
|
-
${truncateForLog(result2)}`);
|
|
809
|
-
return result2;
|
|
810
|
-
}
|
|
811
|
-
await deletePage(directory, entry.id);
|
|
812
|
-
const newEntries = removeIndexEntry(entries, entry.id);
|
|
813
|
-
await writeIndex(directory, newEntries);
|
|
814
|
-
const fullPath = `${entry.category}/${entry.title}`;
|
|
815
|
-
const result = `\u5DF2\u5220\u9664\u6D77\u9A6C\u4F53 #${entry.id}
|
|
816
|
-
\u8DEF\u5F84: ${fullPath}`;
|
|
817
|
-
await log("info", `[haimati_delete] \u51FA\u53C2:
|
|
818
|
-
${truncateForLog(result)}`);
|
|
819
|
-
return result;
|
|
820
|
-
} catch (error) {
|
|
821
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
822
|
-
const stack = error instanceof Error ? error.stack : "";
|
|
823
|
-
await log("error", `[haimati_delete] \u51FA\u53C2: \u9519\u8BEF: ${errorMsg}
|
|
824
|
-
\u5806\u6808: ${stack}`);
|
|
825
|
-
return `\u9519\u8BEF: ${errorMsg}`;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
}),
|
|
829
|
-
/**
|
|
830
|
-
* haimati_move - 修改记忆的分类路径
|
|
831
|
-
*/
|
|
832
|
-
haimati_move: tool({
|
|
833
|
-
description: "\u4FEE\u6539\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7684\u5206\u7C7B\u8DEF\u5F84\u3002\u901A\u8FC7\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\u5B9A\u4F4D\u8BB0\u5FC6\uFF0C\u7136\u540E\u5C06\u5176\u79FB\u52A8\u5230\u65B0\u7684\u5206\u7C7B\u3002",
|
|
834
|
-
args: {
|
|
835
|
-
query: tool.schema.string().describe("\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u79FB\u52A8\u7684\u8BB0\u5FC6"),
|
|
836
|
-
newCategory: tool.schema.string().describe("\u65B0\u7684\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 '\u6807\u7ACB\u65B9/\u5BA2\u6237\u7AEF' \u6216 '\u901A\u7528'")
|
|
837
|
-
},
|
|
838
|
-
async execute(args, context) {
|
|
839
|
-
const { directory } = context;
|
|
840
|
-
const indexPath = await getIndexPath(directory);
|
|
841
|
-
await log("info", `[haimati_move] \u5165\u53C2: query="${args.query}", newCategory="${args.newCategory}"`);
|
|
842
|
-
if (!await fsExists(indexPath)) {
|
|
843
|
-
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
844
|
-
await log("warn", `[haimati_move] \u51FA\u53C2:
|
|
845
|
-
${truncateForLog(result)}`);
|
|
846
|
-
return result;
|
|
847
|
-
}
|
|
848
|
-
try {
|
|
849
|
-
const entries = await readIndex(directory);
|
|
850
|
-
const query = args.query.trim();
|
|
851
|
-
let entry = null;
|
|
852
|
-
if (/^\d+$/.test(query)) {
|
|
853
|
-
entry = findEntryById(entries, query);
|
|
854
|
-
} else {
|
|
855
|
-
const parts = query.split("/").filter(Boolean);
|
|
856
|
-
if (parts.length >= 2) {
|
|
857
|
-
const title = parts.pop();
|
|
858
|
-
const category = parts.join("/");
|
|
859
|
-
entry = findEntryByPath(entries, category, title);
|
|
860
|
-
} else {
|
|
861
|
-
const results = await searchEntries(entries, query, "or", directory);
|
|
862
|
-
if (results.length === 1) {
|
|
863
|
-
entry = results[0];
|
|
864
|
-
} else if (results.length > 1) {
|
|
865
|
-
const list = results.slice(0, 10).map((e) => ` - ${e.category}/${e.title} [${e.id}]`).join("\n");
|
|
866
|
-
return `\u627E\u5230\u591A\u6761\u5339\u914D\uFF0C\u8BF7\u4F7F\u7528\u5E8F\u53F7\u7CBE\u786E\u6307\u5B9A\uFF1A
|
|
867
|
-
${list}`;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
if (!entry) {
|
|
872
|
-
const result2 = `\u672A\u627E\u5230\u4E0E '${query}' \u76F8\u5173\u7684\u8BB0\u5FC6`;
|
|
873
|
-
await log("info", `[haimati_move] \u51FA\u53C2:
|
|
874
|
-
${truncateForLog(result2)}`);
|
|
875
|
-
return result2;
|
|
876
|
-
}
|
|
877
|
-
const oldCategory = entry.category;
|
|
878
|
-
const oldPath = `${oldCategory}/${entry.title}`;
|
|
879
|
-
const newCategory = args.newCategory;
|
|
880
|
-
const newPath = `${newCategory}/${entry.title}`;
|
|
881
|
-
const newEntries = entries.map((e) => {
|
|
882
|
-
if (e.id === entry.id) {
|
|
883
|
-
return { ...e, category: newCategory };
|
|
884
|
-
}
|
|
885
|
-
return e;
|
|
886
|
-
});
|
|
887
|
-
await writeIndex(directory, newEntries);
|
|
888
|
-
const result = `\u5DF2\u79FB\u52A8\u6D77\u9A6C\u4F53 #${entry.id}
|
|
889
|
-
\u65E7\u8DEF\u5F84: ${oldPath}
|
|
890
|
-
\u65B0\u8DEF\u5F84: ${newPath}`;
|
|
891
|
-
await log("info", `[haimati_move] \u51FA\u53C2:
|
|
892
|
-
${truncateForLog(result)}`);
|
|
893
|
-
return result;
|
|
894
|
-
} catch (error) {
|
|
895
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
896
|
-
const stack = error instanceof Error ? error.stack : "";
|
|
897
|
-
await log("error", `[haimati_move] \u51FA\u53C2: \u9519\u8BEF: ${errorMsg}
|
|
898
|
-
\u5806\u6808: ${stack}`);
|
|
899
|
-
return `\u9519\u8BEF: ${errorMsg}`;
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
}),
|
|
903
|
-
/**
|
|
904
|
-
* haimati_list - 列出海马体索引结构
|
|
905
|
-
*/
|
|
906
|
-
haimati_list: tool({
|
|
907
|
-
description: "\u5217\u51FA\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u7684\u5B8C\u6574\u7D22\u5F15\u7ED3\u6784\u3002",
|
|
908
|
-
args: {
|
|
909
|
-
category: tool.schema.string().optional().describe("\u53EF\u9009\u7684\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5217\u51FA\u8BE5\u5206\u7C7B\u4E0B\u7684\u6761\u76EE"),
|
|
910
|
-
recursive: tool.schema.boolean().optional().default(false).describe("\u662F\u5426\u9012\u5F52\u904D\u5386\u5B50\u5206\u7C7B\uFF0C\u9ED8\u8BA4false")
|
|
911
|
-
},
|
|
912
|
-
async execute(args, context) {
|
|
913
|
-
const { directory } = context;
|
|
914
|
-
const indexPath = await getIndexPath(directory);
|
|
915
|
-
await log("info", `[haimati_list] \u5165\u53C2: category="${args.category || "(\u65E0)"}, recursive=${args.recursive}"`);
|
|
916
|
-
if (!await fsExists(indexPath)) {
|
|
917
|
-
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
918
|
-
await log("warn", `[haimati_list] \u51FA\u53C2:
|
|
919
|
-
${truncateForLog(result)}`);
|
|
920
|
-
return result;
|
|
921
|
-
}
|
|
922
|
-
try {
|
|
923
|
-
const entries = await readIndex(directory);
|
|
924
|
-
if (args.category) {
|
|
925
|
-
const indexContent = await fsReadFile(await getIndexPath(directory), "utf-8");
|
|
926
|
-
const lines = indexContent.split("\n");
|
|
927
|
-
let targetDepth = 0;
|
|
928
|
-
let currentPath = [];
|
|
929
|
-
for (const line of lines) {
|
|
930
|
-
if (!line.trim()) continue;
|
|
931
|
-
const depth = getTreeDepth(line);
|
|
932
|
-
const entryMatch = line.match(/^\s*[│├└─\s]*(.+?)\s*-\s*(\d+)\s*$/);
|
|
933
|
-
if (entryMatch) continue;
|
|
934
|
-
const name = extractCategoryName(line);
|
|
935
|
-
if (name) {
|
|
936
|
-
if (name === "/") {
|
|
937
|
-
currentPath[depth] = "";
|
|
938
|
-
} else {
|
|
939
|
-
currentPath[depth] = name;
|
|
940
|
-
}
|
|
941
|
-
currentPath = currentPath.slice(0, depth + 1);
|
|
942
|
-
const fullPath = currentPath.filter(Boolean).join("/");
|
|
943
|
-
if (fullPath === args.category) {
|
|
944
|
-
targetDepth = depth;
|
|
945
|
-
break;
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
const subCategories = [];
|
|
950
|
-
const directEntries = [];
|
|
951
|
-
currentPath = [];
|
|
952
|
-
for (const line of lines) {
|
|
953
|
-
if (!line.trim()) continue;
|
|
954
|
-
const depth = getTreeDepth(line);
|
|
955
|
-
const entryMatch = line.match(/^\s*[│├└─\s]*(.+?)\s*-\s*(\d+)\s*$/);
|
|
956
|
-
if (entryMatch) {
|
|
957
|
-
const title = entryMatch[1].trim();
|
|
958
|
-
const id = entryMatch[2];
|
|
959
|
-
if (depth === targetDepth + 1) {
|
|
960
|
-
const entryCategory = currentPath.slice(1, depth).filter(Boolean).join("/");
|
|
961
|
-
if (entryCategory === args.category) {
|
|
962
|
-
directEntries.push({ category: entryCategory, title, id });
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
continue;
|
|
966
|
-
}
|
|
967
|
-
const name = extractCategoryName(line);
|
|
968
|
-
if (name) {
|
|
969
|
-
if (name === "/") {
|
|
970
|
-
currentPath[depth] = "";
|
|
971
|
-
} else {
|
|
972
|
-
currentPath[depth] = name;
|
|
973
|
-
}
|
|
974
|
-
currentPath = currentPath.slice(0, depth + 1);
|
|
975
|
-
if (depth === targetDepth + 1) {
|
|
976
|
-
const fullPath = currentPath.filter(Boolean).join("/");
|
|
977
|
-
if (fullPath === args.category) {
|
|
978
|
-
subCategories.push(name);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
let result2 = `## ${args.category}
|
|
1
|
+
var Y=Object.defineProperty;var Nt=Object.getOwnPropertyDescriptor;var qt=Object.getOwnPropertyNames;var jt=Object.prototype.hasOwnProperty;var pt=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(i,n)=>(typeof require<"u"?require:i)[n]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var Z=(t,i)=>()=>(t&&(i=t(t=0)),i);var yt=(t,i)=>{for(var n in i)Y(t,n,{get:i[n],enumerable:!0})},Ot=(t,i,n,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let e of qt(i))!jt.call(t,e)&&e!==n&&Y(t,e,{get:()=>i[e],enumerable:!(s=Nt(i,e))||s.enumerable});return t};var z=t=>Ot(Y({},"__esModule",{value:!0}),t);var wt={};yt(wt,{AUTO_INJECT_CONTEXT:()=>at,DEFAULT_VERSION:()=>W,HAIMATI_CONFIG_FILE:()=>ot,HAIMATI_DIR:()=>Q,HAIMATI_PATH_KEY:()=>st,INDEX_FILE:()=>tt,LOCK_MAX_WAIT:()=>rt,LOG_FILE:()=>it,NEXT_ID_FILE:()=>nt,PAGES_DIR:()=>et,PAGE_GROUP_SIZE:()=>N,VERSION_SEPARATOR:()=>A});var Q,tt,et,nt,it,rt,ot,st,A,W,at,N,C=Z(()=>{"use strict";Q=".haimati",tt="\u7D22\u5F15.md",et="\u4E66\u9875",nt="next-id.txt",it="haimati.log",rt=5e3,ot="opencode-haimati.conf",st="haimati-path",A=`
|
|
2
|
+
---
|
|
3
|
+
version:`,W=1,at=!1,N=50});import b from"path";import Rt from"os";import B from"fs";function Wt(t){let i=b.join(t,ot);if(!B.existsSync(i))return null;try{let s=B.readFileSync(i,"utf-8").split(`
|
|
4
|
+
`);for(let e of s){let o=e.trim();if(!o||o.startsWith("#"))continue;let r=o.match(/^([^:]+):\s*(.+)$/);if(r){let a=r[1].trim(),c=r[2].trim();if(a===st&&c)return b.resolve(t,c)}}}catch(n){console.error("[haimati] \u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25:",n)}return null}function T(t){let i=Wt(t);return i||b.join(t,Q)}function I(t){return b.join(T(t),tt)}function G(t){return b.join(T(t),nt)}function ct(t){return b.join(T(t),".lock")}function lt(t){return b.join(T(t),"\u4E66\u9875")}function L(t,i){let n=parseInt(i,10),s=Math.floor((n-1)/50)*50+1,e=s+50-1,o=`${String(s).padStart(3,"0")}-${String(e).padStart(3,"0")}`;return b.join(lt(t),o)}function H(t,i){return b.join(L(t,i),`${i}.md`)}function $t(){let t=Rt.tmpdir();return b.join(t,"haimati_logs",it)}function v(t){return B.promises.mkdir(t,{recursive:!0}).then(()=>{})}function x(t){return B.promises.access(t).then(()=>!0).catch(()=>!1)}var k=Z(()=>{"use strict";C()});var dt={};yt(dt,{buildIndexTree:()=>U,extractCategoryName:()=>j,findEntryById:()=>S,findEntryByPath:()=>O,getTreeDepth:()=>q,parseIndex:()=>Pt,readIndex:()=>P,readIndexContent:()=>mt,removeIndexEntry:()=>ft,writeIndex:()=>ut});import xt from"fs";import{promisify as _t}from"util";function q(t){let i=0,n=0,s=t.length;for(;n<s;)if(t[n]==="\u2502"&&n+3<s&&t.slice(n+1,n+4)===" ")i++,n+=4;else if(t.slice(n,n+4)===" ")i++,n+=4;else{if((t[n]==="\u251C"||t[n]==="\u2514")&&n+3<s&&t.slice(n+1,n+4)==="\u2500\u2500 ")break;break}return i}function j(t){let n=t.trim().replace(/^[│├└─\s]+/,"").trim();return n==="/"?"/":!n.endsWith("/")||(n=n.slice(0,-1),!n)?null:n}function Pt(t){let i=[],n=t.split(`
|
|
5
|
+
`),s=[];for(let e of n){if(!e.trim())continue;let o=q(e),r=e.match(/^\s*[│├└─\s]*(.+?)\s*-\s*(\d+)\s*$/);if(r){let a=r[1].trim(),c=r[2],m=s.slice(0,o).filter(h=>h!=="").join("/");i.push({id:c,category:m,title:a})}else{let a=j(e);if(a)if(a==="/"){s[o]="";for(let c=o+1;c<s.length;c++)delete s[c]}else{s[o]=a;for(let c=o+1;c<s.length;c++)delete s[c]}}}return i}function S(t,i){return t.find(n=>n.id===i)||null}function O(t,i,n){return t.find(s=>s.category===i&&s.title===n)||null}function ft(t,i){return t.filter(n=>n.id!==i)}function U(t){if(t.length===0)return"";let i={name:"/",children:[],entry:null};for(let e of t){let o=e.category.split("/"),r=i;for(let c=0;c<o.length;c++){let g=o[c];if(!g)continue;let m=r.children.find(h=>h.name===g);m||(m={name:g,children:[],entry:null},r.children.push(m)),r=m}let a={name:e.id,children:[],entry:e};r.children.push(a)}function n(e){e.children.sort((o,r)=>o.name.localeCompare(r.name,void 0,{sensitivity:"base"}));for(let o of e.children)n(o)}n(i);function s(e,o,r){let a=[],c=o+(r?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 "),g=e.name==="/"?"/":e.name+"/";a.push(`${c}${g}`),o=o+(r?" ":"\u2502 ");let m=e.children.filter(f=>f.entry===null),h=e.children.filter(f=>f.entry!==null),p=[...m,...h];for(let f=0;f<p.length;f++){let d=p[f],y=f===p.length-1;if(d.entry){let u=o+(y?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ");a.push(`${u}${d.entry.title} - ${d.entry.id}`)}else a.push(...s(d,o,y))}return a}return s(i,"",!0).join(`
|
|
6
|
+
`)}async function P(t){let i=I(t);if(!await x(i))return[];let n=await Et(i,"utf-8");return Pt(n)}async function ut(t,i){let n=pt("path").join(t,(C(),z(wt)).HAIMATI_DIR);await x(n)||await v(n);let s=U(i),e=I(t);await Bt(e,s,"utf-8")}async function mt(t){let i=I(t);return await x(i)?Et(i,"utf-8"):""}var Et,Bt,F=Z(()=>{"use strict";k();Et=_t(xt.readFile),Bt=_t(xt.writeFile)});k();F();import{tool as _}from"@opencode-ai/plugin";k();C();import X from"fs";import{promisify as K}from"util";var Gt=K(X.readFile),Ht=K(X.writeFile),Ut=K(X.unlink),It=K(X.mkdir);async function M(t,i){let n=H(t,i);if(!await x(n))return null;let s=await Gt(n,"utf-8"),e=s.indexOf(A);if(e===-1){let p=s.split(`
|
|
7
|
+
`);return p.length<2?null:{title:p[0],createdAt:p[1],content:p.slice(2).join(`
|
|
8
|
+
`),version:1}}let o=s.substring(0,e).split(`
|
|
9
|
+
`)[0],a=s.substring(0,e).split(`
|
|
10
|
+
`)[1]||"",c=s.substring(e+A.length),g=parseInt(c.trim(),10)||1,h=s.substring(0,e).substring(o.length+1+a.length+1);return{title:o,createdAt:a,content:h,version:g}}async function R(t,i,n,s,e,o=1){let r=lt(t);await x(r)||await It(r,{recursive:!0});let a=L(t,i);await x(a)||await It(a,{recursive:!0});let c=H(t,i),g=`${n}
|
|
11
|
+
${s}
|
|
12
|
+
${e}${A}${o}`;await Ht(c,g,"utf-8")}async function bt(t,i){let n=H(t,i);return await x(n)?(await Ut(n),!0):!1}async function V(t,i,n,s,e=!1){let o=i.toLowerCase().split(/\s+/).filter(a=>a.length>0);if(o.length===0)return[];let r=[];for(let a of t){let c=a.title.toLowerCase(),g=a.category.toLowerCase(),m=a.id.toLowerCase(),h=0,p=0;for(let d of o)(c.includes(d)||g.includes(d)||m.includes(d))&&h++;if(e&&s){let d=await M(s,a.id);if(d&&d.content){let y=d.content.toLowerCase();for(let u of o)y.includes(u)&&p++}}let f=h+p;n==="and"?f>=o.length&&r.push({entry:a,score:f}):(h>0||e&&p>0)&&r.push({entry:a,score:f})}return r.sort((a,c)=>c.score-a.score),r.map(a=>a.entry)}F();k();F();import kt from"fs";import{promisify as Ct}from"util";k();C();import Tt from"fs";import{promisify as vt}from"util";var Xt=vt(Tt.writeFile),Kt=vt(Tt.unlink);async function Mt(t){let i=T(t);await x(i)||await v(i);let n=ct(t),s=Date.now(),e=50+Math.random()*20;for(;await x(n);){if(Date.now()-s>5e3)return!1;await new Promise(o=>setTimeout(o,e))}return await Xt(n,String(process.pid),"utf-8"),!0}async function Dt(t){let i=ct(t);await x(i)&&await Kt(i)}k();import ht from"fs";import{promisify as gt}from"util";var Vt=gt(ht.appendFile),Jt=gt(ht.writeFile),Yt=gt(ht.stat);function w(t,i=500){if(t.length<=i)return t;let n=Math.floor((i-20)/2);return t.substring(0,n)+"...(\u7701\u7565"+(t.length-i)+"\u5B57\u7B26)..."+t.substring(t.length-n)}async function Zt(t){try{let i=new Date,n=i.toISOString().split("T")[0],s=i.toISOString().replace("T"," ").split(".")[0],e=$t(),o=pt("path").dirname(e);await x(o)||await v(o);let r=!1;await x(e)&&(await Yt(e)).mtime.toISOString().split("T")[0]!==n&&(r=!0);let a=`[${s}] ${t}`;r?await Jt(e,a+`
|
|
13
|
+
`,"utf-8"):await Vt(e,a+`
|
|
14
|
+
`,"utf-8")}catch(i){console.error("[haimati] \u5199\u5165\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:",i)}}async function l(t,i){await Zt(`[${t.toUpperCase()}] ${i}`)}var zt=Ct(kt.readFile),Qt=Ct(kt.writeFile);async function te(t){let i=G(t);if(!await x(i))return 1;let s=(await zt(i,"utf-8")).trim(),e=parseInt(s,10);return!isNaN(e)&&e>0?e:1}async function ee(t){let i=G(t);if(await x(i))return;let n=await P(t),s=0;for(let e of n){let o=parseInt(e.id,10);o>s&&(s=o)}await Lt(t,s+1)}async function Lt(t,i){let n=T(t);await x(n)||await v(n);let s=G(t);await Qt(s,String(i),"utf-8")}async function St(t,i,n,s,e,o=!1){if(!await Mt(t))return null;try{await ee(t);let r=await P(t),a=r.find(h=>h.category===i&&h.title===n);if(a){if(!o)throw new Error(`\u8BB0\u5FC6\u5DF2\u5B58\u5728: ${i}/${n}\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u4F7F\u7528 haimati_edit \u5DE5\u5177`);let h=await M(t,a.id),p=h?h.createdAt:s,f=h?h.version+1:1;return await R(t,a.id,n,p,e,f),await l("info",`[allocateIdWithLock] \u66F4\u65B0\u5DF2\u6709\u6761\u76EE: #${a.id} ${i}/${n}, \u65B0\u7248\u672C: ${f}`),null}let c=await te(t),g=String(c).padStart(3,"0");await R(t,g,n,s,e,1);let m={id:g,category:i,title:n};return r.push(m),await ut(t,r),await Lt(t,c+1),g}catch(r){let a=r instanceof Error?r.message:String(r);return await l("error",`[allocateIdWithLock] \u5206\u914D\u5E8F\u53F7\u5931\u8D25: ${a}`),null}finally{await Dt(t)}}function Ft(){return{haimati_read:_({description:"\u4ECE\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u8BFB\u53D6\u5185\u5BB9\u3002\u901A\u8FC7\u5E8F\u53F7\uFF08\u5982 '086'\uFF09\u6765\u5B9A\u4F4D\u5E76\u8BFB\u53D6\u8BB0\u5FC6\u5185\u5BB9\u3002\u652F\u6301\u5206\u9875\u8BFB\u53D6\uFF08offset/limit\uFF09\uFF0C\u8FD4\u56DE\u5185\u5BB9\u5E26\u884C\u53F7\u548C\u7248\u672C\u53F7\u3002",args:{query:_.schema.string().describe("\u5E8F\u53F7\u3002\u4F8B\u5982\uFF1A'086'"),offset:_.schema.number().optional().default(1).describe("\u8D77\u59CB\u884C\u53F7\uFF08\u9ED8\u8BA4 1\uFF09"),limit:_.schema.number().optional().default(2e3).describe("\u6700\u5927\u8BFB\u53D6\u884C\u6570\uFF08\u9ED8\u8BA4 2000\uFF09")},async execute(t,i){let{directory:n}=i,s=I(n);if(await l("info",`[haimati_read] \u5165\u53C2: query="${t.query}", offset=${t.offset||1}, limit=${t.limit||2e3}`),!await x(s)){let e=`\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728\u4E8E ${s}`;return await l("warn",`[haimati_read] \u51FA\u53C2:
|
|
15
|
+
${w(e)}`),e}try{let e=await P(n);await l("debug",`[haimati_read] \u8BFB\u53D6\u7D22\u5F15: \u5171 ${e.length} \u6761\u8BB0\u5F55`);let o=t.query.trim();if(!/^\d+$/.test(o)){let $="\u9519\u8BEF\uFF1Aquery \u5FC5\u987B\u662F\u5E8F\u53F7\uFF0C\u53EA\u652F\u6301\u5E8F\u53F7\u67E5\u8BE2";return await l("warn",`[haimati_read] \u51FA\u53C2:
|
|
16
|
+
${w($)}`),$}let r=S(e,o);if(await l("debug",`[haimati_read] \u5E8F\u53F7\u67E5\u8BE2 #${o}: ${r?"\u627E\u5230":"\u672A\u627E\u5230"}`),!r){let $=`\u672A\u627E\u5230\u8BB0\u5FC6: ${t.query}`;return await l("info",`[haimati_read] \u51FA\u53C2:
|
|
17
|
+
${w($)}`),$}let a=await M(n,r.id);if(!a){let $=`\u672A\u627E\u5230\u4E66\u9875: #${r.id}`;return await l("error",`[haimati_read] \u51FA\u53C2:
|
|
18
|
+
${w($)}`),$}let c=L(n,r.id);await l("debug",`[haimati_read] \u8BFB\u53D6\u4E66\u9875: \u4E66\u9875/${c.split(/[\\/]/).pop()}/${r.id}.md, version=${a.version}`);let g=a.content.split(`
|
|
19
|
+
`),m=t.offset||1,h=t.limit||2e3,p=g.length,f=Math.max(0,m-1),d=Math.min(p,f+h),y=[];y.push(`version:${a.version}`);for(let $=f;$<d;$++)y.push(`${$+1}|${g[$]}`);let u=y.join(`
|
|
20
|
+
`);return await l("info",`[haimati_read] \u51FA\u53C2:
|
|
21
|
+
${w(u)}`),u}catch(e){let o=e instanceof Error?e.message:String(e),r=e instanceof Error?e.stack:"";return await l("error",`[haimati_read] \u51FA\u53C2: \u9519\u8BEF: ${o}
|
|
22
|
+
\u5806\u6808: ${r}`),`\u9519\u8BEF: ${o}`}}}),haimati_write:_({description:"\u5C06\u65B0\u4FE1\u606F\u5199\u5165\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u3002\u9700\u8981\u63D0\u4F9B\u5206\u7C7B\u8DEF\u5F84\uFF08\u5982 'xxx\u9879\u76EE/\u7B7E\u7AE0'\uFF09\u3001\u6807\u9898\u548C\u5185\u5BB9\u3002\u5982\u679C\u76F8\u540C\u5206\u7C7B\u548C\u6807\u9898\u7684\u8BB0\u5FC6\u5DF2\u5B58\u5728\uFF0C\u5219\u4F1A\u62A5\u9519\u3002",args:{category:_.schema.string().describe("\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 'xxx\u9879\u76EE/\u7B7E\u7AE0' \u6216 '\u901A\u7528'"),title:_.schema.string().describe("\u8BB0\u5FC6\u6807\u9898\uFF0C\u5982 '\u7B7E\u7AE0\u670D\u52A1WS\u901A\u4FE1\u673A\u5236'"),content:_.schema.string().describe("\u8981\u5199\u5165\u7684\u8BE6\u7EC6\u5185\u5BB9")},async execute(t,i){let{directory:n}=i;await l("info",`[haimati_write] \u5165\u53C2: category="${t.category}", title="${t.title}", content\u957F\u5EA6=${t.content.length}\u5B57\u7B26, content=
|
|
23
|
+
${w(t.content)}
|
|
24
|
+
`);try{let s=new Date().toISOString(),e=null;for(let c=0;c<3&&(e=await St(n,t.category,t.title,s,t.content,!1),!e);c++)await l("warn",`[haimati_write] \u9501\u83B7\u53D6\u5931\u8D25\uFF0C\u91CD\u8BD5\u7B2C ${c+1} \u6B21`);if(e){let c=L(n,e);await l("debug",`[haimati_write] \u5206\u914D\u65B0\u5E8F\u53F7: #${e}`),await l("debug",`[haimati_write] \u4E66\u9875\u548C\u7D22\u5F15\u5DF2\u66F4\u65B0: \u4E66\u9875/${c.split(/[\\/]/).pop()}/${e}.md`);let g=`\u5DF2\u5199\u5165\u6D77\u9A6C\u4F53 #${e}
|
|
25
|
+
\u8DEF\u5F84: ${t.category}/${t.title}
|
|
26
|
+
\u7248\u672C: 1`;return await l("info",`[haimati_write] \u51FA\u53C2:
|
|
27
|
+
${w(g)}`),g}let o=await P(n),r=O(o,t.category,t.title);if(r){let c=`\u9519\u8BEF\uFF1A\u8BB0\u5FC6\u5DF2\u5B58\u5728 #${r.id} (${t.category}/${t.title})\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u4F7F\u7528 haimati_edit \u5DE5\u5177`;return await l("error",`[haimati_write] \u51FA\u53C2:
|
|
28
|
+
${w(c)}`),c}let a="\u9519\u8BEF\uFF1A\u65E0\u6CD5\u5206\u914D\u5E8F\u53F7\uFF08\u9501\u7B49\u5F85\u8D85\u65F6\uFF09";return await l("error",`[haimati_write] \u51FA\u53C2:
|
|
29
|
+
${w(a)}`),a}catch(s){let e=s instanceof Error?s.message:String(s),o=s instanceof Error?s.stack:"";return await l("error",`[haimati_write] \u51FA\u53C2: \u9519\u8BEF: ${e}
|
|
30
|
+
\u5806\u6808: ${o}`),`\u9519\u8BEF: ${e}`}}}),haimati_search:_({description:"\u641C\u7D22\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\uFF08\u652F\u6301\u591A\u5173\u952E\u5B57\uFF0C\u7528\u7A7A\u683C\u5206\u9694\uFF09\u3002\u641C\u7D22\u8303\u56F4\u5305\u62EC\uFF1A\u6807\u9898\u3001\u5206\u7C7B\u3001\u5E8F\u53F7\u3001\u4E66\u9875\u5185\u5BB9\u3002\u8F93\u5165\u5173\u952E\u8BCD\uFF0C\u8FD4\u56DE\u5339\u914D\u7684\u6761\u76EE\u5217\u8868\u548C\u5185\u5BB9\u7247\u6BB5\uFF0C\u652F\u6301\u5206\u9875\uFF08limit/offset\uFF09\u3002",args:{keyword:_.schema.string().describe("\u641C\u7D22\u5173\u952E\u8BCD\uFF08\u652F\u6301\u591A\u5173\u952E\u5B57\uFF0C\u7528\u7A7A\u683C\u5206\u9694\uFF09"),match:_.schema.string().describe("\u5339\u914D\u6A21\u5F0F\uFF1Aand=\u6240\u6709\u5173\u952E\u5B57\u90FD\u5339\u914D\uFF0Cor=\u4EFB\u610F\u5173\u952E\u5B57\u5339\u914D"),limit:_.schema.number().default(10).describe("\u8FD4\u56DE\u7ED3\u679C\u6570\u91CF\u9650\u5236"),offset:_.schema.number().default(0).describe("\u5206\u9875\u504F\u79FB\u91CF\uFF0C\u7528\u4E8E\u83B7\u53D6\u540E\u7EED\u9875\u9762")},async execute(t,i){let{directory:n}=i,s=I(n);if(await l("info",`[haimati_search] \u5165\u53C2: keyword="${t.keyword}", match="${t.match}", limit=${t.limit||10}, offset=${t.offset||0}`),!await x(s)){let e="\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728";return await l("warn",`[haimati_search] \u51FA\u53C2:
|
|
31
|
+
${w(e)}`),e}try{let e=await P(n),o=t.keyword.trim(),r=t.match,a=t.limit||10,c=t.offset||0,g=await V(e,o,r,n,!0),m=g.length,h=g.slice(c,c+a);if(h.length===0){let u=`\u672A\u627E\u5230\u5305\u542B '${o}' \u7684\u8BB0\u5FC6`;return await l("info",`[haimati_search] \u51FA\u53C2:
|
|
32
|
+
${w(u)}`),u}let p=[],f=c+h.length<m,d=f?`${c+1}-${c+h.length}/${m}`:`${c+1}-${c+h.length}/${m}`;p.push(`\u627E\u5230 ${d} \u6761\u5339\u914D\uFF1A
|
|
33
|
+
`),f&&p.push(`\uFF08\u5982\u9700\u83B7\u53D6\u66F4\u591A\uFF0C\u8BF7\u4F7F\u7528 offset=${c+a} \u7EE7\u7EED\u7FFB\u9875\uFF09
|
|
34
|
+
`);for(let u of h){let $=await M(n,u.id),E=`${u.category}/${u.title}`,D="";$&&(D=$.content.split(`
|
|
35
|
+
`).filter(At=>At.trim()).slice(0,3).join(" | ").substring(0,150),D.length===150&&(D+="...")),p.push(`## ${E} [${u.id}]`),D&&p.push(`> ${D}`),p.push("")}let y=p.join(`
|
|
36
|
+
`);return await l("info",`[haimati_search] \u51FA\u53C2:
|
|
37
|
+
${w(y)}`),y}catch(e){let o=e instanceof Error?e.message:String(e),r=e instanceof Error?e.stack:"";return await l("error",`[haimati_search] \u51FA\u53C2: \u9519\u8BEF: ${o}
|
|
38
|
+
\u5806\u6808: ${r}`),`\u9519\u8BEF: ${o}`}}}),haimati_edit:_({description:"\u4FEE\u6539\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7684\u90E8\u5206\u5185\u5BB9\u3002\u5C06\u6307\u5B9A\u884C\u53F7\u8303\u56F4 [offsetBegin, offsetEnd) \u7684\u5185\u5BB9\u66FF\u6362\u4E3A\u65B0 content\u3002\u9700\u8981\u63D0\u4F9B read \u65F6\u8FD4\u56DE\u7684 version \u8FDB\u884C\u5E76\u53D1\u63A7\u5236\u3002",args:{query:_.schema.string().describe("\u5E8F\u53F7\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u66F4\u65B0\u7684\u8BB0\u5FC6"),offsetBegin:_.schema.number().describe("\u8D77\u59CB\u884C\u53F7\uFF08\u5305\u62EC\uFF09"),offsetEnd:_.schema.number().describe("\u7ED3\u675F\u884C\u53F7\uFF08\u4E0D\u5305\u62EC\uFF09"),content:_.schema.string().describe("\u66FF\u6362\u5185\u5BB9"),version:_.schema.number().describe("read \u65F6\u8FD4\u56DE\u7684 version\uFF0C\u7528\u4E8E\u5E76\u53D1\u63A7\u5236")},async execute(t,i){let{directory:n}=i,s=I(n);if(await l("info",`[haimati_edit] \u5165\u53C2: query="${t.query}", offsetBegin=${t.offsetBegin}, offsetEnd=${t.offsetEnd}, content\u957F\u5EA6=${t.content.length}\u5B57\u7B26, version=${t.version}, content=
|
|
39
|
+
${w(t.content)}
|
|
40
|
+
`),!await x(s)){let e="\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728";return await l("warn",`[haimati_edit] \u51FA\u53C2:
|
|
41
|
+
${w(e)}`),e}try{let e=await P(n),o=t.query.trim();if(!/^\d+$/.test(o)){let u="\u9519\u8BEF\uFF1Aquery \u5FC5\u987B\u662F\u5E8F\u53F7\uFF0C\u53EA\u652F\u6301\u5E8F\u53F7\u67E5\u8BE2";return await l("warn",`[haimati_edit] \u51FA\u53C2:
|
|
42
|
+
${w(u)}`),u}let r=S(e,o);if(!r){let u=`\u672A\u627E\u5230\u4E0E '${o}' \u76F8\u5173\u7684\u8BB0\u5FC6`;return await l("info",`[haimati_edit] \u51FA\u53C2:
|
|
43
|
+
${w(u)}`),u}let a=await M(n,r.id);if(!a){let u=`\u672A\u627E\u5230\u4E66\u9875: #${r.id}`;return await l("error",`[haimati_edit] \u51FA\u53C2:
|
|
44
|
+
${w(u)}`),u}let c=t.offsetBegin,g=t.offsetEnd;if(c>a.content.split(`
|
|
45
|
+
`).length||c>=g){let u=`\u9519\u8BEF\uFF1Aoffset \u975E\u6CD5\uFF0CoffsetBegin=${c}, offsetEnd=${g}, \u6587\u4EF6\u603B\u884C\u6570=${a.content.split(`
|
|
46
|
+
`).length}`;return await l("warn",`[haimati_edit] \u51FA\u53C2:
|
|
47
|
+
${w(u)}`),u}if(a.version!==t.version){let u=`\u9519\u8BEF\uFF1A\u7248\u672C\u51B2\u7A81\uFF0C\u5F53\u524D version=${a.version}\uFF0C\u4F20\u5165 version=${t.version}\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u518D\u64CD\u4F5C`;return await l("warn",`[haimati_edit] \u51FA\u53C2:
|
|
48
|
+
${w(u)}`),u}let m=a.content.split(`
|
|
49
|
+
`),p=[...m.slice(0,c-1),...t.content.split(`
|
|
50
|
+
`),...m.slice(g-1)].join(`
|
|
51
|
+
`),f=a.version+1;await R(n,r.id,r.title,a.createdAt,p,f);let d=`${r.category}/${r.title}`,y=`\u5DF2\u66F4\u65B0\u6D77\u9A6C\u4F53 #${r.id}
|
|
52
|
+
\u8DEF\u5F84: ${d}
|
|
53
|
+
\u65B0\u7248\u672C: ${f}`;return await l("info",`[haimati_edit] \u51FA\u53C2:
|
|
54
|
+
${w(y)}`),y}catch(e){let o=e instanceof Error?e.message:String(e),r=e instanceof Error?e.stack:"";return await l("error",`[haimati_edit] \u51FA\u53C2: \u9519\u8BEF: ${o}
|
|
55
|
+
\u5806\u6808: ${r}`),`\u9519\u8BEF: ${o}`}}}),haimati_delete:_({description:"\u5220\u9664\u6D77\u9A6C\u4F53\u4E2D\u7684\u8BB0\u5FC6\u3002\u901A\u8FC7\u5E8F\u53F7\uFF08\u5982 '086'\uFF09\u6216\u5B8C\u6574\u8DEF\u5F84\uFF08\u5982 'xxx\u9879\u76EE/\u7B7E\u7AE0/\u7B7E\u7AE0\u670D\u52A1WS\u901A\u4FE1\u673A\u5236'\uFF09\u6765\u5B9A\u4F4D\u8981\u5220\u9664\u7684\u8BB0\u5FC6\u3002",args:{query:_.schema.string().describe("\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u5220\u9664\u7684\u8BB0\u5FC6")},async execute(t,i){let{directory:n}=i,s=I(n);if(await l("info",`[haimati_delete] \u5165\u53C2: query="${t.query}"`),!await x(s)){let e="\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728";return await l("warn",`[haimati_delete] \u51FA\u53C2:
|
|
56
|
+
${w(e)}`),e}try{let e=await P(n),o=t.query.trim(),r=null;if(/^\d+$/.test(o))r=S(e,o);else{let m=o.split("/").filter(Boolean);if(m.length>=2){let h=m.pop(),p=m.join("/");r=O(e,p,h)}else{let h=await V(e,o,"or",n);if(h.length===1)r=h[0];else if(h.length>1)return`\u627E\u5230\u591A\u6761\u5339\u914D\uFF0C\u8BF7\u4F7F\u7528\u5E8F\u53F7\u7CBE\u786E\u6307\u5B9A\uFF1A
|
|
57
|
+
${h.slice(0,10).map(f=>` - ${f.category}/${f.title} [${f.id}]`).join(`
|
|
58
|
+
`)}`}}if(!r){let m=`\u672A\u627E\u5230\u4E0E '${o}' \u76F8\u5173\u7684\u8BB0\u5FC6`;return await l("info",`[haimati_delete] \u51FA\u53C2:
|
|
59
|
+
${w(m)}`),m}await bt(n,r.id);let a=ft(e,r.id);await(F(),z(dt)).writeIndex(n,a);let c=`${r.category}/${r.title}`,g=`\u5DF2\u5220\u9664\u6D77\u9A6C\u4F53 #${r.id}
|
|
60
|
+
\u8DEF\u5F84: ${c}`;return await l("info",`[haimati_delete] \u51FA\u53C2:
|
|
61
|
+
${w(g)}`),g}catch(e){let o=e instanceof Error?e.message:String(e),r=e instanceof Error?e.stack:"";return await l("error",`[haimati_delete] \u51FA\u53C2: \u9519\u8BEF: ${o}
|
|
62
|
+
\u5806\u6808: ${r}`),`\u9519\u8BEF: ${o}`}}}),haimati_move:_({description:"\u4FEE\u6539\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7684\u5206\u7C7B\u8DEF\u5F84\u3002\u901A\u8FC7\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\u5B9A\u4F4D\u8BB0\u5FC6\uFF0C\u7136\u540E\u5C06\u5176\u79FB\u52A8\u5230\u65B0\u7684\u5206\u7C7B\u3002",args:{query:_.schema.string().describe("\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u79FB\u52A8\u7684\u8BB0\u5FC6"),newCategory:_.schema.string().describe("\u65B0\u7684\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 'xxx\u9879\u76EE/\u5BA2\u6237\u7AEF' \u6216 '\u901A\u7528'")},async execute(t,i){let{directory:n}=i,s=I(n);if(await l("info",`[haimati_move] \u5165\u53C2: query="${t.query}", newCategory="${t.newCategory}"`),!await x(s)){let e="\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728";return await l("warn",`[haimati_move] \u51FA\u53C2:
|
|
63
|
+
${w(e)}`),e}try{let e=await P(n),o=t.query.trim(),r=null;if(/^\d+$/.test(o))r=S(e,o);else{let f=o.split("/").filter(Boolean);if(f.length>=2){let d=f.pop(),y=f.join("/");r=O(e,y,d)}else{let d=await V(e,o,"or",n);if(d.length===1)r=d[0];else if(d.length>1)return`\u627E\u5230\u591A\u6761\u5339\u914D\uFF0C\u8BF7\u4F7F\u7528\u5E8F\u53F7\u7CBE\u786E\u6307\u5B9A\uFF1A
|
|
64
|
+
${d.slice(0,10).map(u=>` - ${u.category}/${u.title} [${u.id}]`).join(`
|
|
65
|
+
`)}`}}if(!r){let f=`\u672A\u627E\u5230\u4E0E '${o}' \u76F8\u5173\u7684\u8BB0\u5FC6`;return await l("info",`[haimati_move] \u51FA\u53C2:
|
|
66
|
+
${w(f)}`),f}let c=`${r.category}/${r.title}`,g=t.newCategory,m=`${g}/${r.title}`,h=e.map(f=>f.id===r.id?{...f,category:g}:f);await(F(),z(dt)).writeIndex(n,h);let p=`\u5DF2\u79FB\u52A8\u6D77\u9A6C\u4F53 #${r.id}
|
|
67
|
+
\u65E7\u8DEF\u5F84: ${c}
|
|
68
|
+
\u65B0\u8DEF\u5F84: ${m}`;return await l("info",`[haimati_move] \u51FA\u53C2:
|
|
69
|
+
${w(p)}`),p}catch(e){let o=e instanceof Error?e.message:String(e),r=e instanceof Error?e.stack:"";return await l("error",`[haimati_move] \u51FA\u53C2: \u9519\u8BEF: ${o}
|
|
70
|
+
\u5806\u6808: ${r}`),`\u9519\u8BEF: ${o}`}}}),haimati_list:_({description:"\u5217\u51FA\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u7684\u5B8C\u6574\u7D22\u5F15\u7ED3\u6784\u3002",args:{category:_.schema.string().optional().describe("\u53EF\u9009\u7684\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5217\u51FA\u8BE5\u5206\u7C7B\u4E0B\u7684\u6761\u76EE"),recursive:_.schema.boolean().optional().default(!1).describe("\u662F\u5426\u9012\u5F52\u904D\u5386\u5B50\u5206\u7C7B\uFF0C\u9ED8\u8BA4false")},async execute(t,i){let{directory:n}=i,s=I(n);if(await l("info",`[haimati_list] \u5165\u53C2: category="${t.category||"(\u65E0)"}, recursive=${t.recursive}"`),!await x(s)){let e="\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728";return await l("warn",`[haimati_list] \u51FA\u53C2:
|
|
71
|
+
${w(e)}`),e}try{let e=await P(n);if(t.category){let c=(await mt(n)).split(`
|
|
72
|
+
`),g=0,m=[];for(let d of c){if(!d.trim())continue;let y=q(d);if(d.match(/^\s*[│├└─\s]*(.+?)\s*-\s*(\d+)\s*$/))continue;let $=j(d);if($&&($==="/"?m[y]="":m[y]=$,m=m.slice(0,y+1),m.filter(Boolean).join("/")===t.category)){g=y;break}}let h=[],p=[];m=[];for(let d of c){if(!d.trim())continue;let y=q(d),u=d.match(/^\s*[│├└─\s]*(.+?)\s*-\s*(\d+)\s*$/);if(u){let E=u[1].trim(),D=u[2];if(y===g+1){let J=m.slice(1,y).filter(Boolean).join("/");J===t.category&&p.push({category:J,title:E,id:D})}continue}let $=j(d);$&&($==="/"?m[y]="":m[y]=$,m=m.slice(0,y+1),y===g+1&&m.filter(Boolean).join("/")===t.category&&h.push($))}let f=`## ${t.category}
|
|
984
73
|
|
|
985
|
-
`;
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
(e) => e.category === args.category || e.category.startsWith(args.category + "/")
|
|
989
|
-
);
|
|
990
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
991
|
-
for (const entry of allDescendants) {
|
|
992
|
-
const rel = entry.category.slice(args.category.length + 1);
|
|
993
|
-
const firstPart = rel.split("/")[0] || "(\u76F4\u63A5)";
|
|
994
|
-
if (!grouped.has(firstPart)) {
|
|
995
|
-
grouped.set(firstPart, []);
|
|
996
|
-
}
|
|
997
|
-
grouped.get(firstPart).push(entry);
|
|
998
|
-
}
|
|
999
|
-
for (const [group, groupEntries] of grouped) {
|
|
1000
|
-
result2 += `### ${group}/
|
|
1001
|
-
`;
|
|
1002
|
-
result2 += groupEntries.map((e) => `- ${e.title} - ${e.id}`).join("\n") + "\n\n";
|
|
1003
|
-
}
|
|
1004
|
-
} else {
|
|
1005
|
-
if (subCategories.length > 0) {
|
|
1006
|
-
result2 += "**\u5B50\u5206\u7C7B\uFF1A**\n" + subCategories.map((c) => `- ${c}/`).join("\n") + "\n\n";
|
|
1007
|
-
}
|
|
1008
|
-
if (directEntries.length > 0) {
|
|
1009
|
-
result2 += "**\u76F4\u63A5\u6761\u76EE\uFF1A**\n" + directEntries.map((e) => `- ${e.title} - ${e.id}`).join("\n");
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
await log("info", `[haimati_list] \u51FA\u53C2:
|
|
1013
|
-
${truncateForLog(result2)}`);
|
|
1014
|
-
return result2;
|
|
1015
|
-
}
|
|
1016
|
-
const tree = buildIndexTree(entries);
|
|
1017
|
-
const result = `## \u6D77\u9A6C\u4F53\u7D22\u5F15
|
|
74
|
+
`;if(t.recursive){let d=e.filter(u=>u.category===t.category||u.category.startsWith(t.category+"/")),y=new Map;for(let u of d){let E=u.category.slice(t.category.length+1).split("/")[0]||"(\u76F4\u63A5)";y.has(E)||y.set(E,[]),y.get(E).push(u)}for(let[u,$]of y)f+=`### ${u}/
|
|
75
|
+
`,f+=$.map(E=>`- ${E.title} - ${E.id}`).join(`
|
|
76
|
+
`)+`
|
|
1018
77
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
})
|
|
1032
|
-
},
|
|
1033
|
-
/**
|
|
1034
|
-
* session.created - 会话创建时注入海马体规则
|
|
1035
|
-
* 在每次新会话开始时,自动将海马体三大原则注入到上下文中
|
|
1036
|
-
*/
|
|
1037
|
-
event: async ({ event: evt }) => {
|
|
1038
|
-
if (evt.type !== "session.created") {
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
try {
|
|
1042
|
-
const sessionId = evt.properties?.info?.id;
|
|
1043
|
-
if (!sessionId) {
|
|
1044
|
-
await log("warn", "[session.created] \u65E0\u6CD5\u83B7\u53D6 sessionID\uFF0C\u8DF3\u8FC7\u6CE8\u5165");
|
|
1045
|
-
return;
|
|
1046
|
-
}
|
|
1047
|
-
await ctx.client.session.prompt({
|
|
1048
|
-
path: { id: sessionId },
|
|
1049
|
-
body: {
|
|
1050
|
-
noReply: true,
|
|
1051
|
-
parts: [{
|
|
1052
|
-
type: "text",
|
|
1053
|
-
text: `## \u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u4F7F\u7528\u89C4\u5219
|
|
78
|
+
`}else h.length>0&&(f+=`**\u5B50\u5206\u7C7B\uFF1A**
|
|
79
|
+
`+h.map(d=>`- ${d}/`).join(`
|
|
80
|
+
`)+`
|
|
81
|
+
|
|
82
|
+
`),p.length>0&&(f+=`**\u76F4\u63A5\u6761\u76EE\uFF1A**
|
|
83
|
+
`+p.map(d=>`- ${d.title} - ${d.id}`).join(`
|
|
84
|
+
`));return await l("info",`[haimati_list] \u51FA\u53C2:
|
|
85
|
+
${w(f)}`),f}let r=`## \u6D77\u9A6C\u4F53\u7D22\u5F15
|
|
86
|
+
|
|
87
|
+
${U(e)||"(\u7A7A)"}`;return await l("info",`[haimati_list] \u51FA\u53C2:
|
|
88
|
+
${w(r)}`),r}catch(e){let o=e instanceof Error?e.message:String(e),r=e instanceof Error?e.stack:"";return await l("error",`[haimati_list] \u51FA\u53C2: \u9519\u8BEF: ${o}
|
|
89
|
+
\u5806\u6808: ${r}`),`\u9519\u8BEF: ${o}`}}})}}C();var Je=async t=>(await l("info","\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u63D2\u4EF6(\u6587\u4EF6\u7248)\u5DF2\u52A0\u8F7D"),{tool:Ft(),event:async({event:n})=>{if(n.type==="session.created")try{let s=n.properties?.info?.id;if(!s){await l("warn","[session.created] \u65E0\u6CD5\u83B7\u53D6 sessionID\uFF0C\u8DF3\u8FC7\u6CE8\u5165");return}if(!!1){await l("info","[session.created] \u81EA\u52A8\u6CE8\u5165\u5DF2\u5173\u95ED\uFF0C\u8DF3\u8FC7\u6CE8\u5165 (AUTO_INJECT_CONTEXT=false)");return}await t.client.session.prompt({path:{id:s},body:{noReply:!0,parts:[{type:"text",text:`## \u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u4F7F\u7528\u89C4\u5219
|
|
1054
90
|
|
|
1055
91
|
\u4F60\u6709\u4E00\u4E2A\u6D77\u9A6C\u4F53\u8BB0\u5FC6\u7CFB\u7EDF\u53EF\u7528\uFF0C\u7528\u4E8E\u5B58\u50A8\u548C\u68C0\u7D22\u8DE8\u4F1A\u8BDD\u7684\u77E5\u8BC6\u3002
|
|
1056
92
|
|
|
@@ -1073,7 +109,7 @@ ${truncateForLog(result)}`);
|
|
|
1073
109
|
**\u6CE8\u610F**\uFF1A\u4EE5\u4E0B\u5DE5\u5177\u5217\u8868\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u4F7F\u7528\u5E8F\u53F7\u67E5\u8BE2\u4EE3\u66FF\u8DEF\u5F84\u67E5\u8BE2\u3002haimati_read/haimati_edit \u8FD4\u56DE\u7684 version \u7528\u4E8E\u5E76\u53D1\u63A7\u5236\u3002
|
|
1074
110
|
|
|
1075
111
|
- \`haimati_read\` - \u8BFB\u53D6\u8BB0\u5FC6\u5185\u5BB9\uFF08\u53EA\u652F\u6301\u5E8F\u53F7\u67E5\u8BE2\uFF0C\u652F\u6301\u5206\u9875\uFF0C\u8FD4\u56DE\u5E26\u884C\u53F7\u548C\u7248\u672C\u53F7\uFF09
|
|
1076
|
-
- \`haimati_write\` - \u5199\u5165\u65B0\u8BB0\u5FC6\uFF08\
|
|
112
|
+
- \`haimati_write\` - \u5199\u5165\u65B0\u8BB0\u5FC6\uFF08\u5982\u679C\u5DF2\u5B58\u5728\u76F8\u540C\u5206\u7C7B\u548C\u6807\u9898\u7684\u8BB0\u5FC6\u5219\u62A5\u9519\uFF0C\u4E0D\u5141\u8BB8\u8986\u76D6\uFF09
|
|
1077
113
|
- \`haimati_search\` - \u641C\u7D22\u8BB0\u5FC6\u5185\u5BB9\uFF08\u641C\u7D22\u8303\u56F4\uFF1A\u6807\u9898\u3001\u5206\u7C7B\u3001\u5E8F\u53F7\u3001\u4E66\u9875\u5185\u5BB9\u3002**\u6CE8\u610F\uFF1Amatch\u53C2\u6570\u5FC5\u586B\uFF0Cand=\u6240\u6709\u5173\u952E\u5B57\u90FD\u5339\u914D\uFF0Cor=\u4EFB\u610F\u5173\u952E\u5B57\u5339\u914D**\uFF09
|
|
1078
114
|
- \`haimati_edit\` - \u4FEE\u6539\u8BB0\u5FC6\u5185\u5BB9\uFF08\u90E8\u5206\u66FF\u6362\uFF0C\u901A\u8FC7\u884C\u53F7\u8303\u56F4\u66FF\u6362\u5185\u5BB9\uFF0C\u652F\u6301\u7248\u672C\u5E76\u53D1\u63A7\u5236\uFF09
|
|
1079
115
|
- \`haimati_move\` - \u4FEE\u6539\u8BB0\u5FC6\u7684\u5206\u7C7B\u8DEF\u5F84
|
|
@@ -1084,18 +120,4 @@ ${truncateForLog(result)}`);
|
|
|
1084
120
|
|
|
1085
121
|
**\u4F1A\u8BDD\u5F00\u59CB\u524D**: \u4F7F\u7528 haimati_list \u6216 haimati_search \u8BFB\u53D6\u4E0E\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u8BB0\u5FC6\u5185\u5BB9\uFF0C\u518D\u5F00\u59CB\u5DE5\u4F5C\u3002
|
|
1086
122
|
|
|
1087
|
-
**\u4F1A\u8BDD\u7ED3\u675F\u540E**: \u4F7F\u7528 haimati_write \u5C06\u65B0\u83B7\u5F97\u7684\u77E5\u8BC6\u3001\u7ECF\u9A8C\u3001\u51B3\u7B56\u5199\u5165\u6D77\u9A6C\u4F53\uFF0C\u4FDD\u6301\u4FE1\u606F\u7684\u6301\u7EED\u79EF\u7D2F\u3002`
|
|
1088
|
-
}]
|
|
1089
|
-
}
|
|
1090
|
-
});
|
|
1091
|
-
await log("info", `[session.created] \u6D77\u9A6C\u4F53\u89C4\u5219\u5DF2\u6CE8\u5165\u65B0\u4F1A\u8BDD (sessionID: ${sessionId})`);
|
|
1092
|
-
} catch (error) {
|
|
1093
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1094
|
-
await log("error", `[session.created] \u6CE8\u5165\u6D77\u9A6C\u4F53\u89C4\u5219\u5931\u8D25: ${errorMsg}`);
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1098
|
-
};
|
|
1099
|
-
export {
|
|
1100
|
-
HaimatiPlugin
|
|
1101
|
-
};
|
|
123
|
+
**\u4F1A\u8BDD\u7ED3\u675F\u540E**: \u4F7F\u7528 haimati_write \u5C06\u65B0\u83B7\u5F97\u7684\u77E5\u8BC6\u3001\u7ECF\u9A8C\u3001\u51B3\u7B56\u5199\u5165\u6D77\u9A6C\u4F53\uFF0C\u4FDD\u6301\u4FE1\u606F\u7684\u6301\u7EED\u79EF\u7D2F\u3002`}]}}),await l("info",`[session.created] \u6D77\u9A6C\u4F53\u89C4\u5219\u5DF2\u6CE8\u5165\u65B0\u4F1A\u8BDD (sessionID: ${s})`)}catch(s){let e=s instanceof Error?s.message:String(s);await l("error",`[session.created] \u6CE8\u5165\u6D77\u9A6C\u4F53\u89C4\u5219\u5931\u8D25: ${e}`)}}});export{Je as HaimatiPlugin};
|
package/dist/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-haimati",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "OpenCode plugin for a file-based memory system inspired by the hippocampus, providing long-term memory storage and retrieval across sessions.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"files": [
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
9
11
|
"scripts": {
|
|
10
|
-
"build": "npx esbuild index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin && cp package.json dist/",
|
|
12
|
+
"build": "rm -rf dist && npx esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin --minify && cp package.json dist/",
|
|
13
|
+
"build:uncompressed": "rm -rf dist && npx esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin && cp package.json dist/",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
11
15
|
"prepublishOnly": "npm run build",
|
|
12
16
|
"test": "npx tsx test/index.ts"
|
|
13
17
|
},
|
|
@@ -20,4 +24,4 @@
|
|
|
20
24
|
"@types/bun": "^1.3.1",
|
|
21
25
|
"typescript": "^5.9.3"
|
|
22
26
|
}
|
|
23
|
-
}
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-haimati",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "OpenCode plugin for a file-based memory system inspired by the hippocampus, providing long-term memory storage and retrieval across sessions.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"files": [
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
9
11
|
"scripts": {
|
|
10
|
-
"build": "npx esbuild index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin && cp package.json dist/",
|
|
12
|
+
"build": "rm -rf dist && npx esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin --minify && cp package.json dist/",
|
|
13
|
+
"build:uncompressed": "rm -rf dist && npx esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin && cp package.json dist/",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
11
15
|
"prepublishOnly": "npm run build",
|
|
12
16
|
"test": "npx tsx test/index.ts"
|
|
13
17
|
},
|
|
@@ -20,4 +24,4 @@
|
|
|
20
24
|
"@types/bun": "^1.3.1",
|
|
21
25
|
"typescript": "^5.9.3"
|
|
22
26
|
}
|
|
23
|
-
}
|
|
27
|
+
}
|