opencode-haimati 1.0.2 → 1.0.4
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 +46 -18
- package/dist/index.js +141 -133
- package/dist/package.json +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
- 🧠 类海马体记忆机制:模拟人脑记忆系统的分类存储
|
|
8
8
|
- 📁 树形索引结构:通过 `.haimati/索引.md` 维护记忆索引
|
|
9
9
|
- 📚 分组书页存储:记忆内容按序号分组存储于 `.haimati/书页/` 目录
|
|
10
|
-
- 🔍
|
|
11
|
-
- ✏️ 完整 CRUD
|
|
10
|
+
- 🔍 多模式搜索:支持序号精确查询和关键字模糊搜索
|
|
11
|
+
- ✏️ 完整 CRUD:读取、写入、更新(部分替换)、删除、移动记忆
|
|
12
|
+
- 🔄 版本并发控制:基于 version 的乐观锁,防止多会话并发冲突
|
|
12
13
|
- 🔄 自动规则注入:新会话自动注入海马体使用原则
|
|
13
14
|
- 📋 日志追踪:完整的操作日志记录
|
|
15
|
+
- ⚡ 纯异步架构:所有文件操作非阻塞执行
|
|
14
16
|
|
|
15
17
|
## 安装
|
|
16
18
|
|
|
@@ -35,8 +37,10 @@ bun add -g opencode-haimati
|
|
|
35
37
|
|
|
36
38
|
```
|
|
37
39
|
.haimati/
|
|
38
|
-
├──
|
|
39
|
-
|
|
40
|
+
├── .lock # 序号分配锁
|
|
41
|
+
├── 索引.md # 树形结构索引
|
|
42
|
+
├── next-id.txt # 下一个可用序号
|
|
43
|
+
└── 书页/ # 存储各记忆内容(按序号分组)
|
|
40
44
|
├── 001-050/
|
|
41
45
|
│ ├── 001.md
|
|
42
46
|
│ └── 002.md
|
|
@@ -46,13 +50,15 @@ bun add -g opencode-haimati
|
|
|
46
50
|
|
|
47
51
|
## 可用工具
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
| 工具 | 说明 |
|
|
54
|
+
|------|------|
|
|
55
|
+
| `haimati_read` | 读取记忆内容(只支持序号查询,支持分页,返回带行号和版本号) |
|
|
56
|
+
| `haimati_write` | 写入新记忆(覆盖已有内容时版本+1) |
|
|
57
|
+
| `haimati_search` | 搜索记忆内容(标题、分类、序号、书页内容) |
|
|
58
|
+
| `haimati_edit` | 修改记忆内容(部分替换,支持版本并发控制) |
|
|
59
|
+
| `haimati_move` | 修改记忆的分类路径 |
|
|
60
|
+
| `haimati_delete` | 删除记忆 |
|
|
61
|
+
| `haimati_list` | 列出记忆索引 |
|
|
56
62
|
|
|
57
63
|
## 海马体三大原则
|
|
58
64
|
|
|
@@ -65,24 +71,46 @@ bun add -g opencode-haimati
|
|
|
65
71
|
```typescript
|
|
66
72
|
// 写入新记忆
|
|
67
73
|
haimati_write({
|
|
68
|
-
category: "
|
|
74
|
+
category: "xxx项目/签章",
|
|
69
75
|
title: "签章服务WS通信机制",
|
|
70
76
|
content: "WebSocket通信采用base64编码..."
|
|
71
77
|
})
|
|
72
78
|
|
|
73
|
-
//
|
|
79
|
+
// 读取记忆(只支持序号查询)
|
|
74
80
|
haimati_read({ query: "086" })
|
|
75
|
-
// 或
|
|
76
|
-
haimati_read({ query: "标立方/签章/签章服务WS通信机制" })
|
|
77
81
|
|
|
78
82
|
// 搜索记忆
|
|
79
|
-
haimati_search({ keyword: "WebSocket", limit: 10 })
|
|
83
|
+
haimati_search({ keyword: "WebSocket", match: "or", limit: 10 })
|
|
84
|
+
|
|
85
|
+
// 部分修改记忆
|
|
86
|
+
haimati_edit({
|
|
87
|
+
query: "086",
|
|
88
|
+
offsetBegin: 3,
|
|
89
|
+
offsetEnd: 5,
|
|
90
|
+
content: "新的替换内容",
|
|
91
|
+
version: 1
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// 移动记忆到新分类
|
|
95
|
+
haimati_move({
|
|
96
|
+
query: "086",
|
|
97
|
+
newCategory: "xxx项目/客户端"
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// 删除记忆
|
|
101
|
+
haimati_delete({ query: "086" })
|
|
102
|
+
|
|
103
|
+
// 列出所有记忆
|
|
104
|
+
haimati_list({})
|
|
105
|
+
|
|
106
|
+
// 列出指定分类下的记忆
|
|
107
|
+
haimati_list({ category: "xxx项目", recursive: true })
|
|
80
108
|
```
|
|
81
109
|
|
|
82
110
|
## 索引格式示例
|
|
83
111
|
|
|
84
112
|
```
|
|
85
|
-
|
|
113
|
+
xxx项目/
|
|
86
114
|
├── 签章/
|
|
87
115
|
│ └── 签章服务WS通信机制 - 086
|
|
88
116
|
└── 通用/
|
|
@@ -97,4 +125,4 @@ haimati_search({ keyword: "WebSocket", limit: 10 })
|
|
|
97
125
|
|
|
98
126
|
## 许可证
|
|
99
127
|
|
|
100
|
-
MIT
|
|
128
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -3,24 +3,31 @@ import fs from "fs";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import os from "os";
|
|
5
5
|
import { tool } from "@opencode-ai/plugin";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
var fsReadFile = promisify(fs.readFile);
|
|
8
|
+
var fsWriteFile = promisify(fs.writeFile);
|
|
9
|
+
var fsUnlink = promisify(fs.unlink);
|
|
10
|
+
var fsMkdir = promisify(fs.mkdir);
|
|
11
|
+
var fsExists = promisify(fs.exists);
|
|
12
|
+
var fsStat = promisify(fs.stat);
|
|
6
13
|
var HAIMATI_DIR = ".haimati";
|
|
7
14
|
var INDEX_FILE = "\u7D22\u5F15.md";
|
|
8
15
|
var PAGES_DIR = "\u4E66\u9875";
|
|
9
16
|
var NEXT_ID_FILE = "next-id.txt";
|
|
10
17
|
var LOG_FILE = "haimati.log";
|
|
11
18
|
var LOCK_MAX_WAIT = 5e3;
|
|
12
|
-
var LOCK_RETRY_INTERVAL = 50;
|
|
13
19
|
var HAIMATI_CONFIG_FILE = "opencode-haimati.conf";
|
|
14
20
|
var HAIMATI_PATH_KEY = "haimati-path";
|
|
15
21
|
var VERSION_SEPARATOR = "\n---\nversion:";
|
|
16
22
|
var DEFAULT_VERSION = 1;
|
|
17
|
-
|
|
23
|
+
var AUTO_INJECT_CONTEXT = false;
|
|
24
|
+
async function getConfigHaimatiPath(directory) {
|
|
18
25
|
const configPath = path.join(directory, HAIMATI_CONFIG_FILE);
|
|
19
|
-
if (!
|
|
26
|
+
if (!await fsExists(configPath)) {
|
|
20
27
|
return null;
|
|
21
28
|
}
|
|
22
29
|
try {
|
|
23
|
-
const content =
|
|
30
|
+
const content = await fsReadFile(configPath, "utf-8");
|
|
24
31
|
const lines = content.split("\n");
|
|
25
32
|
for (const line of lines) {
|
|
26
33
|
const trimmed = line.trim();
|
|
@@ -41,35 +48,36 @@ function getConfigHaimatiPath(directory) {
|
|
|
41
48
|
}
|
|
42
49
|
return null;
|
|
43
50
|
}
|
|
44
|
-
function getHaimatiDir(directory) {
|
|
45
|
-
const configPath = getConfigHaimatiPath(directory);
|
|
51
|
+
async function getHaimatiDir(directory) {
|
|
52
|
+
const configPath = await getConfigHaimatiPath(directory);
|
|
46
53
|
if (configPath) {
|
|
47
54
|
return configPath;
|
|
48
55
|
}
|
|
49
56
|
return path.join(directory, HAIMATI_DIR);
|
|
50
57
|
}
|
|
51
|
-
function getIndexPath(directory) {
|
|
52
|
-
return path.join(getHaimatiDir(directory), INDEX_FILE);
|
|
58
|
+
async function getIndexPath(directory) {
|
|
59
|
+
return path.join(await getHaimatiDir(directory), INDEX_FILE);
|
|
53
60
|
}
|
|
54
|
-
function getNextIdPath(directory) {
|
|
55
|
-
return path.join(getHaimatiDir(directory), NEXT_ID_FILE);
|
|
61
|
+
async function getNextIdPath(directory) {
|
|
62
|
+
return path.join(await getHaimatiDir(directory), NEXT_ID_FILE);
|
|
56
63
|
}
|
|
57
|
-
function getLockPath(directory) {
|
|
58
|
-
return path.join(getHaimatiDir(directory), ".lock");
|
|
64
|
+
async function getLockPath(directory) {
|
|
65
|
+
return path.join(await getHaimatiDir(directory), ".lock");
|
|
59
66
|
}
|
|
60
|
-
function getPagesDir(directory) {
|
|
61
|
-
return path.join(getHaimatiDir(directory), PAGES_DIR);
|
|
67
|
+
async function getPagesDir(directory) {
|
|
68
|
+
return path.join(await getHaimatiDir(directory), PAGES_DIR);
|
|
62
69
|
}
|
|
63
|
-
function getPageGroupDir(directory, id) {
|
|
70
|
+
async function getPageGroupDir(directory, id) {
|
|
64
71
|
const n = parseInt(id, 10);
|
|
65
72
|
const groupStart = Math.floor((n - 1) / 50) * 50 + 1;
|
|
66
73
|
const groupEnd = groupStart + 49;
|
|
67
74
|
const groupName = `${String(groupStart).padStart(3, "0")}-${String(groupEnd).padStart(3, "0")}`;
|
|
68
|
-
return path.join(getPagesDir(directory), groupName);
|
|
75
|
+
return path.join(await getPagesDir(directory), groupName);
|
|
69
76
|
}
|
|
70
|
-
function getPagePath(directory, id) {
|
|
71
|
-
return path.join(getPageGroupDir(directory, id), `${id}.md`);
|
|
77
|
+
async function getPagePath(directory, id) {
|
|
78
|
+
return path.join(await getPageGroupDir(directory, id), `${id}.md`);
|
|
72
79
|
}
|
|
80
|
+
var fsAppendFile = promisify(fs.appendFile);
|
|
73
81
|
function getLogFilePath() {
|
|
74
82
|
const osTmpDir = os.tmpdir();
|
|
75
83
|
return path.join(osTmpDir, "haimati_logs", LOG_FILE);
|
|
@@ -81,12 +89,12 @@ async function writeLogToFile(message) {
|
|
|
81
89
|
const timeStr = now.toISOString().replace("T", " ").split(".")[0];
|
|
82
90
|
const logPath = getLogFilePath();
|
|
83
91
|
const logDir = path.dirname(logPath);
|
|
84
|
-
if (!
|
|
85
|
-
|
|
92
|
+
if (!await fsExists(logDir)) {
|
|
93
|
+
await fsMkdir(logDir, { recursive: true });
|
|
86
94
|
}
|
|
87
95
|
let shouldOverwrite = false;
|
|
88
|
-
if (
|
|
89
|
-
const mtime =
|
|
96
|
+
if (await fsExists(logPath)) {
|
|
97
|
+
const mtime = (await fsStat(logPath)).mtime;
|
|
90
98
|
const fileDate = mtime.toISOString().split("T")[0];
|
|
91
99
|
if (fileDate !== dateStr) {
|
|
92
100
|
shouldOverwrite = true;
|
|
@@ -94,9 +102,9 @@ async function writeLogToFile(message) {
|
|
|
94
102
|
}
|
|
95
103
|
const logLine = `[${timeStr}] ${message}`;
|
|
96
104
|
if (shouldOverwrite) {
|
|
97
|
-
|
|
105
|
+
await fsWriteFile(logPath, logLine + "\n", "utf-8");
|
|
98
106
|
} else {
|
|
99
|
-
|
|
107
|
+
await fsAppendFile(logPath, logLine + "\n", "utf-8");
|
|
100
108
|
}
|
|
101
109
|
} catch (error) {
|
|
102
110
|
console.error(`[haimati] \u5199\u5165\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:`, error);
|
|
@@ -181,7 +189,7 @@ function findEntryById(entries, id) {
|
|
|
181
189
|
function findEntryByPath(entries, categoryPath, title) {
|
|
182
190
|
return entries.find((e) => e.category === categoryPath && e.title === title) || null;
|
|
183
191
|
}
|
|
184
|
-
async function
|
|
192
|
+
async function searchEntries(entries, keyword, matchMode, directory, searchContent = false) {
|
|
185
193
|
const keywords = keyword.toLowerCase().split(/\s+/).filter((k) => k.length > 0);
|
|
186
194
|
if (keywords.length === 0) return [];
|
|
187
195
|
const matchedWithScore = [];
|
|
@@ -197,7 +205,7 @@ async function searchEntriesAsync(entries, keyword, matchMode, directory, search
|
|
|
197
205
|
}
|
|
198
206
|
}
|
|
199
207
|
if (searchContent && directory) {
|
|
200
|
-
const page = await
|
|
208
|
+
const page = await readPage(directory, e.id);
|
|
201
209
|
if (page && page.content) {
|
|
202
210
|
const contentLower = page.content.toLowerCase();
|
|
203
211
|
for (const kw of keywords) {
|
|
@@ -221,12 +229,12 @@ async function searchEntriesAsync(entries, keyword, matchMode, directory, search
|
|
|
221
229
|
matchedWithScore.sort((a, b) => b.score - a.score);
|
|
222
230
|
return matchedWithScore.map((item) => item.entry);
|
|
223
231
|
}
|
|
224
|
-
async function
|
|
225
|
-
const nextIdPath = getNextIdPath(directory);
|
|
226
|
-
if (!
|
|
232
|
+
async function readNextIdFromFile(directory) {
|
|
233
|
+
const nextIdPath = await getNextIdPath(directory);
|
|
234
|
+
if (!await fsExists(nextIdPath)) {
|
|
227
235
|
return 1;
|
|
228
236
|
}
|
|
229
|
-
const content =
|
|
237
|
+
const content = await fsReadFile(nextIdPath, "utf-8");
|
|
230
238
|
const trimmed = content.trim();
|
|
231
239
|
const parsed = parseInt(trimmed, 10);
|
|
232
240
|
if (!isNaN(parsed) && parsed > 0) {
|
|
@@ -234,58 +242,55 @@ async function readNextIdFromFileAsync(directory) {
|
|
|
234
242
|
}
|
|
235
243
|
return 1;
|
|
236
244
|
}
|
|
237
|
-
async function
|
|
238
|
-
const nextIdPath = getNextIdPath(directory);
|
|
239
|
-
if (
|
|
245
|
+
async function initializeNextIdIfNeeded(directory) {
|
|
246
|
+
const nextIdPath = await getNextIdPath(directory);
|
|
247
|
+
if (await fsExists(nextIdPath)) {
|
|
240
248
|
return;
|
|
241
249
|
}
|
|
242
|
-
const entries = await
|
|
250
|
+
const entries = await readIndex(directory);
|
|
243
251
|
let maxId = 0;
|
|
244
252
|
for (const e of entries) {
|
|
245
253
|
const n = parseInt(e.id, 10);
|
|
246
254
|
if (n > maxId) maxId = n;
|
|
247
255
|
}
|
|
248
|
-
await
|
|
256
|
+
await writeNextIdToFile(directory, maxId + 1);
|
|
249
257
|
}
|
|
250
|
-
async function
|
|
251
|
-
const haimatiDir = getHaimatiDir(directory);
|
|
252
|
-
if (!
|
|
253
|
-
|
|
258
|
+
async function writeNextIdToFile(directory, nextId) {
|
|
259
|
+
const haimatiDir = await getHaimatiDir(directory);
|
|
260
|
+
if (!await fsExists(haimatiDir)) {
|
|
261
|
+
await fsMkdir(haimatiDir, { recursive: true });
|
|
254
262
|
}
|
|
255
|
-
const nextIdPath = getNextIdPath(directory);
|
|
256
|
-
|
|
263
|
+
const nextIdPath = await getNextIdPath(directory);
|
|
264
|
+
await fsWriteFile(nextIdPath, String(nextId), "utf-8");
|
|
257
265
|
}
|
|
258
|
-
function acquireLock(directory) {
|
|
259
|
-
const haimatiDir = getHaimatiDir(directory);
|
|
260
|
-
if (!
|
|
261
|
-
|
|
266
|
+
async function acquireLock(directory) {
|
|
267
|
+
const haimatiDir = await getHaimatiDir(directory);
|
|
268
|
+
if (!await fsExists(haimatiDir)) {
|
|
269
|
+
await fsMkdir(haimatiDir, { recursive: true });
|
|
262
270
|
}
|
|
263
|
-
const lockPath = getLockPath(directory);
|
|
271
|
+
const lockPath = await getLockPath(directory);
|
|
264
272
|
const startTime = Date.now();
|
|
265
|
-
while (
|
|
273
|
+
while (await fsExists(lockPath)) {
|
|
266
274
|
if (Date.now() - startTime > LOCK_MAX_WAIT) {
|
|
267
275
|
return false;
|
|
268
276
|
}
|
|
269
|
-
|
|
270
|
-
const end = Date.now() + sleep;
|
|
271
|
-
while (Date.now() < end) {
|
|
272
|
-
}
|
|
277
|
+
await new Promise((resolve) => setTimeout(resolve, 50 + Math.random() * 20));
|
|
273
278
|
}
|
|
274
|
-
|
|
279
|
+
await fsWriteFile(lockPath, String(process.pid), "utf-8");
|
|
275
280
|
return true;
|
|
276
281
|
}
|
|
277
|
-
function releaseLock(directory) {
|
|
278
|
-
const lockPath = getLockPath(directory);
|
|
279
|
-
if (
|
|
280
|
-
|
|
282
|
+
async function releaseLock(directory) {
|
|
283
|
+
const lockPath = await getLockPath(directory);
|
|
284
|
+
if (await fsExists(lockPath)) {
|
|
285
|
+
await fsUnlink(lockPath);
|
|
281
286
|
}
|
|
282
287
|
}
|
|
283
|
-
async function
|
|
284
|
-
const pagePath = getPagePath(directory, id);
|
|
285
|
-
if (!
|
|
288
|
+
async function readPage(directory, id) {
|
|
289
|
+
const pagePath = await getPagePath(directory, id);
|
|
290
|
+
if (!await fsExists(pagePath)) {
|
|
286
291
|
return null;
|
|
287
292
|
}
|
|
288
|
-
const content =
|
|
293
|
+
const content = await fsReadFile(pagePath, "utf-8");
|
|
289
294
|
const lines = content.split("\n");
|
|
290
295
|
if (lines.length < 2) {
|
|
291
296
|
return null;
|
|
@@ -305,25 +310,25 @@ async function readPageAsync(directory, id) {
|
|
|
305
310
|
version
|
|
306
311
|
};
|
|
307
312
|
}
|
|
308
|
-
async function
|
|
309
|
-
const pagesDir = getPagesDir(directory);
|
|
310
|
-
if (!
|
|
311
|
-
|
|
313
|
+
async function writePage(directory, id, title, createdAt, content, version = 1) {
|
|
314
|
+
const pagesDir = await getPagesDir(directory);
|
|
315
|
+
if (!await fsExists(pagesDir)) {
|
|
316
|
+
await fsMkdir(pagesDir, { recursive: true });
|
|
312
317
|
}
|
|
313
|
-
const pageGroupDir = getPageGroupDir(directory, id);
|
|
314
|
-
if (!
|
|
315
|
-
|
|
318
|
+
const pageGroupDir = await getPageGroupDir(directory, id);
|
|
319
|
+
if (!await fsExists(pageGroupDir)) {
|
|
320
|
+
await fsMkdir(pageGroupDir, { recursive: true });
|
|
316
321
|
}
|
|
317
|
-
const pagePath = getPagePath(directory, id);
|
|
322
|
+
const pagePath = await getPagePath(directory, id);
|
|
318
323
|
const fileContent = `${title}
|
|
319
324
|
${createdAt}
|
|
320
325
|
${content}${VERSION_SEPARATOR}${version}`;
|
|
321
|
-
|
|
326
|
+
await fsWriteFile(pagePath, fileContent, "utf-8");
|
|
322
327
|
}
|
|
323
|
-
async function
|
|
324
|
-
const pagePath = getPagePath(directory, id);
|
|
325
|
-
if (
|
|
326
|
-
|
|
328
|
+
async function deletePage(directory, id) {
|
|
329
|
+
const pagePath = await getPagePath(directory, id);
|
|
330
|
+
if (await fsExists(pagePath)) {
|
|
331
|
+
await fsUnlink(pagePath);
|
|
327
332
|
return true;
|
|
328
333
|
}
|
|
329
334
|
return false;
|
|
@@ -386,57 +391,57 @@ function buildIndexTree(entries) {
|
|
|
386
391
|
function removeIndexEntry(entries, id) {
|
|
387
392
|
return entries.filter((e) => e.id !== id);
|
|
388
393
|
}
|
|
389
|
-
async function
|
|
390
|
-
const indexPath = getIndexPath(directory);
|
|
391
|
-
if (!
|
|
394
|
+
async function readIndex(directory) {
|
|
395
|
+
const indexPath = await getIndexPath(directory);
|
|
396
|
+
if (!await fsExists(indexPath)) {
|
|
392
397
|
return [];
|
|
393
398
|
}
|
|
394
|
-
const content =
|
|
399
|
+
const content = await fsReadFile(indexPath, "utf-8");
|
|
395
400
|
return parseIndex(content);
|
|
396
401
|
}
|
|
397
|
-
async function
|
|
398
|
-
const haimatiDir = getHaimatiDir(directory);
|
|
399
|
-
if (!
|
|
400
|
-
|
|
402
|
+
async function writeIndex(directory, entries) {
|
|
403
|
+
const haimatiDir = await getHaimatiDir(directory);
|
|
404
|
+
if (!await fsExists(haimatiDir)) {
|
|
405
|
+
await fsMkdir(haimatiDir, { recursive: true });
|
|
401
406
|
}
|
|
402
407
|
const tree = buildIndexTree(entries);
|
|
403
|
-
const indexPath = getIndexPath(directory);
|
|
404
|
-
|
|
408
|
+
const indexPath = await getIndexPath(directory);
|
|
409
|
+
await fsWriteFile(indexPath, tree, "utf-8");
|
|
405
410
|
}
|
|
406
411
|
async function allocateIdWithLock(directory, category, title, now, content) {
|
|
407
|
-
if (!acquireLock(directory)) {
|
|
412
|
+
if (!await acquireLock(directory)) {
|
|
408
413
|
return null;
|
|
409
414
|
}
|
|
410
415
|
try {
|
|
411
|
-
await
|
|
412
|
-
const entries = await
|
|
416
|
+
await initializeNextIdIfNeeded(directory);
|
|
417
|
+
const entries = await readIndex(directory);
|
|
413
418
|
const existingEntry = entries.find((e) => e.category === category && e.title === title);
|
|
414
419
|
if (existingEntry) {
|
|
415
|
-
const page = await
|
|
420
|
+
const page = await readPage(directory, existingEntry.id);
|
|
416
421
|
const createdAt = page ? page.createdAt : now;
|
|
417
422
|
const newVersion = page ? page.version + 1 : 1;
|
|
418
|
-
await
|
|
423
|
+
await writePage(directory, existingEntry.id, title, createdAt, content, newVersion);
|
|
419
424
|
await log("info", `[allocateIdWithLock] \u66F4\u65B0\u5DF2\u6709\u6761\u76EE: #${existingEntry.id} ${category}/${title}, \u65B0\u7248\u672C: ${newVersion}`);
|
|
420
425
|
return null;
|
|
421
426
|
}
|
|
422
|
-
const currentNext = await
|
|
427
|
+
const currentNext = await readNextIdFromFile(directory);
|
|
423
428
|
const newId = String(currentNext).padStart(3, "0");
|
|
424
|
-
await
|
|
429
|
+
await writePage(directory, newId, title, now, content, 1);
|
|
425
430
|
const newEntry = {
|
|
426
431
|
id: newId,
|
|
427
432
|
category,
|
|
428
433
|
title
|
|
429
434
|
};
|
|
430
435
|
entries.push(newEntry);
|
|
431
|
-
await
|
|
432
|
-
await
|
|
436
|
+
await writeIndex(directory, entries);
|
|
437
|
+
await writeNextIdToFile(directory, currentNext + 1);
|
|
433
438
|
return newId;
|
|
434
439
|
} catch (error) {
|
|
435
440
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
436
441
|
await log("error", `[allocateIdWithLock] \u5206\u914D\u5E8F\u53F7\u5931\u8D25: ${errMsg}`);
|
|
437
442
|
return null;
|
|
438
443
|
} finally {
|
|
439
|
-
releaseLock(directory);
|
|
444
|
+
await releaseLock(directory);
|
|
440
445
|
}
|
|
441
446
|
}
|
|
442
447
|
var HaimatiPlugin = async (ctx) => {
|
|
@@ -466,16 +471,16 @@ var HaimatiPlugin = async (ctx) => {
|
|
|
466
471
|
*/
|
|
467
472
|
async execute(args, context) {
|
|
468
473
|
const { directory } = context;
|
|
469
|
-
const indexPath = getIndexPath(directory);
|
|
474
|
+
const indexPath = await getIndexPath(directory);
|
|
470
475
|
await log("info", `[haimati_read] \u5165\u53C2: query="${args.query}", offset=${args.offset || 1}, limit=${args.limit || 2e3}`);
|
|
471
|
-
if (!
|
|
476
|
+
if (!await fsExists(indexPath)) {
|
|
472
477
|
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728\u4E8E ${indexPath}`;
|
|
473
478
|
await log("warn", `[haimati_read] \u51FA\u53C2:
|
|
474
479
|
${truncateForLog(result)}`);
|
|
475
480
|
return result;
|
|
476
481
|
}
|
|
477
482
|
try {
|
|
478
|
-
const entries = await
|
|
483
|
+
const entries = await readIndex(directory);
|
|
479
484
|
await log("debug", `[haimati_read] \u8BFB\u53D6\u7D22\u5F15: \u5171 ${entries.length} \u6761\u8BB0\u5F55`);
|
|
480
485
|
const query = args.query.trim();
|
|
481
486
|
if (!/^\d+$/.test(query)) {
|
|
@@ -492,14 +497,15 @@ ${truncateForLog(result2)}`);
|
|
|
492
497
|
${truncateForLog(result2)}`);
|
|
493
498
|
return result2;
|
|
494
499
|
}
|
|
495
|
-
const page = await
|
|
500
|
+
const page = await readPage(directory, entry.id);
|
|
496
501
|
if (!page) {
|
|
497
502
|
const result2 = `\u672A\u627E\u5230\u4E66\u9875: #${entry.id}`;
|
|
498
503
|
await log("error", `[haimati_read] \u51FA\u53C2:
|
|
499
504
|
${truncateForLog(result2)}`);
|
|
500
505
|
return result2;
|
|
501
506
|
}
|
|
502
|
-
|
|
507
|
+
const pageGroupDir = await getPageGroupDir(directory, entry.id);
|
|
508
|
+
await log("debug", `[haimati_read] \u8BFB\u53D6\u4E66\u9875: \u4E66\u9875/${pageGroupDir.split(/[\\/]/).pop()}/${entry.id}.md, version=${page.version}`);
|
|
503
509
|
const contentLines = page.content.split("\n");
|
|
504
510
|
const offset = args.offset || 1;
|
|
505
511
|
const limit = args.limit || 2e3;
|
|
@@ -530,9 +536,9 @@ ${truncateForLog(result)}`);
|
|
|
530
536
|
* 会自动分配新的序号并存储到文件
|
|
531
537
|
*/
|
|
532
538
|
haimati_write: tool({
|
|
533
|
-
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 '\
|
|
539
|
+
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",
|
|
534
540
|
args: {
|
|
535
|
-
category: tool.schema.string().describe("\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 '\
|
|
541
|
+
category: tool.schema.string().describe("\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 'xxx\u9879\u76EE/\u7B7E\u7AE0' \u6216 '\u901A\u7528'"),
|
|
536
542
|
title: tool.schema.string().describe("\u8BB0\u5FC6\u6807\u9898\uFF0C\u5982 '\u7B7E\u7AE0\u670D\u52A1WS\u901A\u4FE1\u673A\u5236'"),
|
|
537
543
|
content: tool.schema.string().describe("\u8981\u5199\u5165\u7684\u8BE6\u7EC6\u5185\u5BB9")
|
|
538
544
|
},
|
|
@@ -550,8 +556,9 @@ ${truncateForLog(args.content)}
|
|
|
550
556
|
await log("warn", `[haimati_write] \u9501\u83B7\u53D6\u5931\u8D25\uFF0C\u91CD\u8BD5\u7B2C ${retry + 1} \u6B21`);
|
|
551
557
|
}
|
|
552
558
|
if (newId) {
|
|
559
|
+
const pageGroupDir = await getPageGroupDir(directory, newId);
|
|
553
560
|
await log("debug", `[haimati_write] \u5206\u914D\u65B0\u5E8F\u53F7: #${newId}`);
|
|
554
|
-
await log("debug", `[haimati_write] \u4E66\u9875\u548C\u7D22\u5F15\u5DF2\u66F4\u65B0: \u4E66\u9875/${
|
|
561
|
+
await log("debug", `[haimati_write] \u4E66\u9875\u548C\u7D22\u5F15\u5DF2\u66F4\u65B0: \u4E66\u9875/${pageGroupDir.split(/[\\/]/).pop()}/${newId}.md`);
|
|
555
562
|
const result2 = `\u5DF2\u5199\u5165\u6D77\u9A6C\u4F53 #${newId}
|
|
556
563
|
\u8DEF\u5F84: ${args.category}/${args.title}
|
|
557
564
|
\u7248\u672C: 1`;
|
|
@@ -559,10 +566,10 @@ ${truncateForLog(args.content)}
|
|
|
559
566
|
${truncateForLog(result2)}`);
|
|
560
567
|
return result2;
|
|
561
568
|
}
|
|
562
|
-
const entries = await
|
|
569
|
+
const entries = await readIndex(directory);
|
|
563
570
|
const existingEntry = findEntryByPath(entries, args.category, args.title);
|
|
564
571
|
if (existingEntry) {
|
|
565
|
-
const page = await
|
|
572
|
+
const page = await readPage(directory, existingEntry.id);
|
|
566
573
|
const version = page ? page.version : 1;
|
|
567
574
|
const result2 = `\u5DF2\u66F4\u65B0\u6D77\u9A6C\u4F53 #${existingEntry.id}
|
|
568
575
|
\u8DEF\u5F84: ${args.category}/${args.title}
|
|
@@ -599,21 +606,21 @@ ${truncateForLog(result)}`);
|
|
|
599
606
|
},
|
|
600
607
|
async execute(args, context) {
|
|
601
608
|
const { directory } = context;
|
|
602
|
-
const indexPath = getIndexPath(directory);
|
|
609
|
+
const indexPath = await getIndexPath(directory);
|
|
603
610
|
await log("info", `[haimati_search] \u5165\u53C2: keyword="${args.keyword}", match="${args.match}", limit=${args.limit || 10}, offset=${args.offset || 0}`);
|
|
604
|
-
if (!
|
|
611
|
+
if (!await fsExists(indexPath)) {
|
|
605
612
|
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
606
613
|
await log("warn", `[haimati_search] \u51FA\u53C2:
|
|
607
614
|
${truncateForLog(result)}`);
|
|
608
615
|
return result;
|
|
609
616
|
}
|
|
610
617
|
try {
|
|
611
|
-
const entries = await
|
|
618
|
+
const entries = await readIndex(directory);
|
|
612
619
|
const keyword = args.keyword.trim();
|
|
613
620
|
const match = args.match;
|
|
614
621
|
const limit = args.limit || 10;
|
|
615
622
|
const offset = args.offset || 0;
|
|
616
|
-
const allMatched = await
|
|
623
|
+
const allMatched = await searchEntries(entries, keyword, match, directory, true);
|
|
617
624
|
const totalCount = allMatched.length;
|
|
618
625
|
const matched = allMatched.slice(offset, offset + limit);
|
|
619
626
|
if (matched.length === 0) {
|
|
@@ -632,7 +639,7 @@ ${truncateForLog(result2)}`);
|
|
|
632
639
|
`);
|
|
633
640
|
}
|
|
634
641
|
for (const entry of matched) {
|
|
635
|
-
const page = await
|
|
642
|
+
const page = await readPage(directory, entry.id);
|
|
636
643
|
const fullPath = `${entry.category}/${entry.title}`;
|
|
637
644
|
let excerpt = "";
|
|
638
645
|
if (page) {
|
|
@@ -683,18 +690,18 @@ ${truncateForLog(result)}`);
|
|
|
683
690
|
},
|
|
684
691
|
async execute(args, context) {
|
|
685
692
|
const { directory } = context;
|
|
686
|
-
const indexPath = getIndexPath(directory);
|
|
693
|
+
const indexPath = await getIndexPath(directory);
|
|
687
694
|
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=
|
|
688
695
|
${truncateForLog(args.content)}
|
|
689
696
|
`);
|
|
690
|
-
if (!
|
|
697
|
+
if (!await fsExists(indexPath)) {
|
|
691
698
|
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
692
699
|
await log("warn", `[haimati_edit] \u51FA\u53C2:
|
|
693
700
|
${truncateForLog(result)}`);
|
|
694
701
|
return result;
|
|
695
702
|
}
|
|
696
703
|
try {
|
|
697
|
-
const entries = await
|
|
704
|
+
const entries = await readIndex(directory);
|
|
698
705
|
const query = args.query.trim();
|
|
699
706
|
if (!/^\d+$/.test(query)) {
|
|
700
707
|
const result2 = `\u9519\u8BEF\uFF1Aquery \u5FC5\u987B\u662F\u5E8F\u53F7\uFF0C\u53EA\u652F\u6301\u5E8F\u53F7\u67E5\u8BE2`;
|
|
@@ -709,7 +716,7 @@ ${truncateForLog(result2)}`);
|
|
|
709
716
|
${truncateForLog(result2)}`);
|
|
710
717
|
return result2;
|
|
711
718
|
}
|
|
712
|
-
const page = await
|
|
719
|
+
const page = await readPage(directory, entry.id);
|
|
713
720
|
if (!page) {
|
|
714
721
|
const result2 = `\u672A\u627E\u5230\u4E66\u9875: #${entry.id}`;
|
|
715
722
|
await log("error", `[haimati_edit] \u51FA\u53C2:
|
|
@@ -733,15 +740,12 @@ ${truncateForLog(result2)}`);
|
|
|
733
740
|
const contentLines = page.content.split("\n");
|
|
734
741
|
const newContentLines = [
|
|
735
742
|
...contentLines.slice(0, offsetBegin - 1),
|
|
736
|
-
// [0, offsetBegin-1) 保留
|
|
737
743
|
...args.content.split("\n"),
|
|
738
|
-
// 插入新内容
|
|
739
744
|
...contentLines.slice(offsetEnd - 1)
|
|
740
|
-
// [offsetEnd-1, end) 保留
|
|
741
745
|
];
|
|
742
746
|
const newContent = newContentLines.join("\n");
|
|
743
747
|
const newVersion = page.version + 1;
|
|
744
|
-
await
|
|
748
|
+
await writePage(directory, entry.id, entry.title, page.createdAt, newContent, newVersion);
|
|
745
749
|
const fullPath = `${entry.category}/${entry.title}`;
|
|
746
750
|
const result = `\u5DF2\u66F4\u65B0\u6D77\u9A6C\u4F53 #${entry.id}
|
|
747
751
|
\u8DEF\u5F84: ${fullPath}
|
|
@@ -762,22 +766,22 @@ ${truncateForLog(result)}`);
|
|
|
762
766
|
* haimati_delete - 删除海马体中的记忆
|
|
763
767
|
*/
|
|
764
768
|
haimati_delete: tool({
|
|
765
|
-
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 '\
|
|
769
|
+
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",
|
|
766
770
|
args: {
|
|
767
771
|
query: tool.schema.string().describe("\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u5220\u9664\u7684\u8BB0\u5FC6")
|
|
768
772
|
},
|
|
769
773
|
async execute(args, context) {
|
|
770
774
|
const { directory } = context;
|
|
771
|
-
const indexPath = getIndexPath(directory);
|
|
775
|
+
const indexPath = await getIndexPath(directory);
|
|
772
776
|
await log("info", `[haimati_delete] \u5165\u53C2: query="${args.query}"`);
|
|
773
|
-
if (!
|
|
777
|
+
if (!await fsExists(indexPath)) {
|
|
774
778
|
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
775
779
|
await log("warn", `[haimati_delete] \u51FA\u53C2:
|
|
776
780
|
${truncateForLog(result)}`);
|
|
777
781
|
return result;
|
|
778
782
|
}
|
|
779
783
|
try {
|
|
780
|
-
const entries = await
|
|
784
|
+
const entries = await readIndex(directory);
|
|
781
785
|
const query = args.query.trim();
|
|
782
786
|
let entry = null;
|
|
783
787
|
if (/^\d+$/.test(query)) {
|
|
@@ -789,7 +793,7 @@ ${truncateForLog(result)}`);
|
|
|
789
793
|
const category = parts.join("/");
|
|
790
794
|
entry = findEntryByPath(entries, category, title);
|
|
791
795
|
} else {
|
|
792
|
-
const results = await
|
|
796
|
+
const results = await searchEntries(entries, query, "or", directory);
|
|
793
797
|
if (results.length === 1) {
|
|
794
798
|
entry = results[0];
|
|
795
799
|
} else if (results.length > 1) {
|
|
@@ -805,9 +809,9 @@ ${list}`;
|
|
|
805
809
|
${truncateForLog(result2)}`);
|
|
806
810
|
return result2;
|
|
807
811
|
}
|
|
808
|
-
await
|
|
812
|
+
await deletePage(directory, entry.id);
|
|
809
813
|
const newEntries = removeIndexEntry(entries, entry.id);
|
|
810
|
-
await
|
|
814
|
+
await writeIndex(directory, newEntries);
|
|
811
815
|
const fullPath = `${entry.category}/${entry.title}`;
|
|
812
816
|
const result = `\u5DF2\u5220\u9664\u6D77\u9A6C\u4F53 #${entry.id}
|
|
813
817
|
\u8DEF\u5F84: ${fullPath}`;
|
|
@@ -830,20 +834,20 @@ ${truncateForLog(result)}`);
|
|
|
830
834
|
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",
|
|
831
835
|
args: {
|
|
832
836
|
query: tool.schema.string().describe("\u5E8F\u53F7\u6216\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u4E8E\u5B9A\u4F4D\u8981\u79FB\u52A8\u7684\u8BB0\u5FC6"),
|
|
833
|
-
newCategory: tool.schema.string().describe("\u65B0\u7684\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 '\
|
|
837
|
+
newCategory: tool.schema.string().describe("\u65B0\u7684\u5206\u7C7B\u8DEF\u5F84\uFF0C\u5982 'xxx\u9879\u76EE/\u5BA2\u6237\u7AEF' \u6216 '\u901A\u7528'")
|
|
834
838
|
},
|
|
835
839
|
async execute(args, context) {
|
|
836
840
|
const { directory } = context;
|
|
837
|
-
const indexPath = getIndexPath(directory);
|
|
841
|
+
const indexPath = await getIndexPath(directory);
|
|
838
842
|
await log("info", `[haimati_move] \u5165\u53C2: query="${args.query}", newCategory="${args.newCategory}"`);
|
|
839
|
-
if (!
|
|
843
|
+
if (!await fsExists(indexPath)) {
|
|
840
844
|
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
841
845
|
await log("warn", `[haimati_move] \u51FA\u53C2:
|
|
842
846
|
${truncateForLog(result)}`);
|
|
843
847
|
return result;
|
|
844
848
|
}
|
|
845
849
|
try {
|
|
846
|
-
const entries = await
|
|
850
|
+
const entries = await readIndex(directory);
|
|
847
851
|
const query = args.query.trim();
|
|
848
852
|
let entry = null;
|
|
849
853
|
if (/^\d+$/.test(query)) {
|
|
@@ -855,7 +859,7 @@ ${truncateForLog(result)}`);
|
|
|
855
859
|
const category = parts.join("/");
|
|
856
860
|
entry = findEntryByPath(entries, category, title);
|
|
857
861
|
} else {
|
|
858
|
-
const results = await
|
|
862
|
+
const results = await searchEntries(entries, query, "or", directory);
|
|
859
863
|
if (results.length === 1) {
|
|
860
864
|
entry = results[0];
|
|
861
865
|
} else if (results.length > 1) {
|
|
@@ -881,7 +885,7 @@ ${truncateForLog(result2)}`);
|
|
|
881
885
|
}
|
|
882
886
|
return e;
|
|
883
887
|
});
|
|
884
|
-
await
|
|
888
|
+
await writeIndex(directory, newEntries);
|
|
885
889
|
const result = `\u5DF2\u79FB\u52A8\u6D77\u9A6C\u4F53 #${entry.id}
|
|
886
890
|
\u65E7\u8DEF\u5F84: ${oldPath}
|
|
887
891
|
\u65B0\u8DEF\u5F84: ${newPath}`;
|
|
@@ -908,18 +912,18 @@ ${truncateForLog(result)}`);
|
|
|
908
912
|
},
|
|
909
913
|
async execute(args, context) {
|
|
910
914
|
const { directory } = context;
|
|
911
|
-
const indexPath = getIndexPath(directory);
|
|
915
|
+
const indexPath = await getIndexPath(directory);
|
|
912
916
|
await log("info", `[haimati_list] \u5165\u53C2: category="${args.category || "(\u65E0)"}, recursive=${args.recursive}"`);
|
|
913
|
-
if (!
|
|
917
|
+
if (!await fsExists(indexPath)) {
|
|
914
918
|
const result = `\u9519\u8BEF\uFF1A\u6D77\u9A6C\u4F53\u7D22\u5F15\u4E0D\u5B58\u5728`;
|
|
915
919
|
await log("warn", `[haimati_list] \u51FA\u53C2:
|
|
916
920
|
${truncateForLog(result)}`);
|
|
917
921
|
return result;
|
|
918
922
|
}
|
|
919
923
|
try {
|
|
920
|
-
const entries = await
|
|
924
|
+
const entries = await readIndex(directory);
|
|
921
925
|
if (args.category) {
|
|
922
|
-
const indexContent =
|
|
926
|
+
const indexContent = await fsReadFile(await getIndexPath(directory), "utf-8");
|
|
923
927
|
const lines = indexContent.split("\n");
|
|
924
928
|
let targetDepth = 0;
|
|
925
929
|
let currentPath = [];
|
|
@@ -1041,6 +1045,10 @@ ${truncateForLog(result)}`);
|
|
|
1041
1045
|
await log("warn", "[session.created] \u65E0\u6CD5\u83B7\u53D6 sessionID\uFF0C\u8DF3\u8FC7\u6CE8\u5165");
|
|
1042
1046
|
return;
|
|
1043
1047
|
}
|
|
1048
|
+
if (!AUTO_INJECT_CONTEXT) {
|
|
1049
|
+
await log("info", "[session.created] \u81EA\u52A8\u6CE8\u5165\u5DF2\u5173\u95ED\uFF0C\u8DF3\u8FC7\u6CE8\u5165 (AUTO_INJECT_CONTEXT=false)");
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1044
1052
|
await ctx.client.session.prompt({
|
|
1045
1053
|
path: { id: sessionId },
|
|
1046
1054
|
body: {
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-haimati",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
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",
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-haimati",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
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
12
|
"build": "npx esbuild index.ts --bundle --platform=node --outdir=dist --format=esm --external:@opencode-ai/plugin && cp package.json dist/",
|
|
11
13
|
"prepublishOnly": "npm run build",
|
|
@@ -20,4 +22,4 @@
|
|
|
20
22
|
"@types/bun": "^1.3.1",
|
|
21
23
|
"typescript": "^5.9.3"
|
|
22
24
|
}
|
|
23
|
-
}
|
|
25
|
+
}
|