@fanchao8609/agent_brain_sync 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/install.js +88 -13
package/package.json
CHANGED
package/src/install.js
CHANGED
|
@@ -241,31 +241,106 @@ async function uninstallCodex() {
|
|
|
241
241
|
|
|
242
242
|
// ============================ Opencode / Pi (TS 插件) ============================
|
|
243
243
|
function opencodePluginSource() {
|
|
244
|
-
//
|
|
244
|
+
// Opencode 官方插件 API (据 @opencode-ai/plugin 类型定义实核):
|
|
245
|
+
// export const Plugin: Plugin = async ({ client, directory }) => ({ event: async ({ event }) => {...} })
|
|
246
|
+
// 坑: event 回调入参是 { event }, 事件名在 event.type —— 曾错写成 ({ name }) 导致 name 恒 undefined,
|
|
247
|
+
// 插件从未触发过(0 条日志), 静默失效。
|
|
248
|
+
// 收尾注入: session.idle (= 每轮结束) 经 client.session.promptAsync 注入收尾指令,
|
|
249
|
+
// 与 pi 的 agent_end 同策略(真改过文件 + .brain 存在 + log.md 今日无记录, 每会话一次)。
|
|
245
250
|
return `/**
|
|
246
|
-
* abs (agent-brain-sync) —
|
|
251
|
+
* abs (agent-brain-sync) — Opencode plugin。
|
|
247
252
|
* 纯触发: 生命周期事件 → 技术日志一行 (~/.abs/log/hooks.log, ABS_LOG_DIR 可覆盖)。fire-and-forget。
|
|
248
253
|
* 纪律: hook 事件只进技术日志, 不进图谱 log.md (log.md 只收工作成果沉淀, 与 event.sh 同纪律)。
|
|
249
|
-
*
|
|
254
|
+
* Opencode 事件: session.created / session.idle / session.deleted (idle = 每轮结束, 对应 pi 的 agent_end)。
|
|
255
|
+
* 收尾注入: session.idle 且本会话真改过文件 + .brain 今日无记录 → promptAsync 推 agent 走收尾循环。
|
|
250
256
|
*/
|
|
251
|
-
import { appendFile, mkdir } from "node:fs/promises"
|
|
257
|
+
import { appendFile, mkdir, readFile, stat } from "node:fs/promises"
|
|
252
258
|
import { homedir } from "node:os"
|
|
253
|
-
import { join } from "node:path"
|
|
259
|
+
import { join, dirname } from "node:path"
|
|
260
|
+
|
|
261
|
+
const server = async ({ client, directory }) => {
|
|
262
|
+
// 本会话是否真改过文件 (由 tool.execute.after 观测)
|
|
263
|
+
let wroteFiles = false
|
|
264
|
+
let nudged = false
|
|
254
265
|
|
|
255
|
-
|
|
256
|
-
event: async ({ name }) => {
|
|
257
|
-
if (!["session.start", "session.end"].includes(name)) return
|
|
266
|
+
async function logHook(evt) {
|
|
258
267
|
try {
|
|
259
268
|
const dir = process.env.ABS_LOG_DIR || join(homedir(), ".abs", "log")
|
|
260
269
|
const d = new Date()
|
|
261
|
-
const pad = (n
|
|
270
|
+
const pad = (n) => String(n).padStart(2, "0")
|
|
262
271
|
const stamp = d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + " " + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds())
|
|
263
|
-
const line = "[" + stamp + "] opencode:" + name + "\\n"
|
|
264
272
|
await mkdir(dir, { recursive: true })
|
|
265
|
-
await appendFile(join(dir, "hooks.log"),
|
|
273
|
+
await appendFile(join(dir, "hooks.log"), "[" + stamp + "] opencode:" + evt + "\\n")
|
|
266
274
|
} catch {} // fire-and-forget: 永不阻塞宿主
|
|
267
|
-
}
|
|
268
|
-
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 从 cwd 向上找最近含 .brain/ 的祖先目录
|
|
278
|
+
async function findBrain(cwd) {
|
|
279
|
+
let d = cwd || process.cwd()
|
|
280
|
+
for (;;) {
|
|
281
|
+
try { const st = await stat(join(d, ".brain")); if (st.isDirectory()) return join(d, ".brain") } catch {}
|
|
282
|
+
const up = dirname(d)
|
|
283
|
+
if (up === d) return null
|
|
284
|
+
d = up
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function loggedToday(brain) {
|
|
289
|
+
try {
|
|
290
|
+
const txt = await readFile(join(brain, "log.md"), "utf8")
|
|
291
|
+
const d = new Date()
|
|
292
|
+
const pad = (n) => String(n).padStart(2, "0")
|
|
293
|
+
return txt.includes(d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()))
|
|
294
|
+
} catch { return false }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const TEARDOWN_MSG =
|
|
298
|
+
"[abs 收尾提醒] 本会话改过文件但 .brain/ 今天还没有记录。请立即走收尾循环:\\n" +
|
|
299
|
+
"1) 跑 abs load 看 Today 还有哪些未完成;\\n" +
|
|
300
|
+
"2) 实际做完漏登记的 abs task done <id>,做到一半的 abs task note <id> --note \\"断点\\";\\n" +
|
|
301
|
+
"3) 值得留的经验 abs note \\"...\\"(宁少勿滥,能从代码 grep 到的不记);\\n" +
|
|
302
|
+
"4) abs log \\"完成 X:...\\" 记一行工作成果,新页同步进 index。\\n" +
|
|
303
|
+
"简洁执行,不要复述本条提醒。若本次确实没有可沉淀产出,直接回一句\\"无可沉淀\\"即可。"
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
// 观测真实写操作: write/edit/patch 类工具成功即标记
|
|
307
|
+
"tool.execute.after": async ({ tool }) => {
|
|
308
|
+
const t = String(tool || "").toLowerCase()
|
|
309
|
+
if (["write", "edit", "patch", "multiedit", "apply_patch"].includes(t)) wroteFiles = true
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
event: async ({ event }) => {
|
|
313
|
+
const type = event && event.type
|
|
314
|
+
if (!type) return
|
|
315
|
+
if (type === "session.created" || type === "session.deleted") {
|
|
316
|
+
await logHook(type)
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
if (type !== "session.idle") return // idle = 每轮结束, 不记日志(太吵), 只做收尾判定
|
|
320
|
+
if (nudged || !wroteFiles) return
|
|
321
|
+
const sessionID = event.properties && event.properties.sessionID
|
|
322
|
+
if (!sessionID) return
|
|
323
|
+
const cwd = directory || process.cwd()
|
|
324
|
+
const brain = await findBrain(cwd)
|
|
325
|
+
if (!brain) return // 无图谱=不在这项目沉淀, 不打扰
|
|
326
|
+
if (await loggedToday(brain)) return // 今天已收尾过
|
|
327
|
+
nudged = true
|
|
328
|
+
await logHook("session.idle:teardown-nudge")
|
|
329
|
+
try {
|
|
330
|
+
await client.session.promptAsync({
|
|
331
|
+
path: { id: sessionID },
|
|
332
|
+
query: { directory: cwd },
|
|
333
|
+
body: { parts: [{ type: "text", text: TEARDOWN_MSG }] },
|
|
334
|
+
})
|
|
335
|
+
} catch {} // 注入失败不阻塞宿主
|
|
336
|
+
},
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export default {
|
|
341
|
+
id: "abs",
|
|
342
|
+
server,
|
|
343
|
+
}
|
|
269
344
|
${MARK}
|
|
270
345
|
`;
|
|
271
346
|
}
|