@fanchao8609/agent_brain_sync 1.5.4 → 1.5.6
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 +2 -2
- package/bin/abs.js +64 -6
- package/bin/mcp.js +12 -12
- package/hooks/abs.opencode.ts +113 -0
- package/hooks/abs.pi.ts +130 -0
- package/package.json +1 -1
- package/skill/SKILL.md +10 -5
- package/src/index.js +15 -15
- package/src/install.js +317 -302
- package/src/store.js +27 -6
- package/src/todo.js +22 -0
package/src/store.js
CHANGED
|
@@ -4,7 +4,7 @@ import { promises as fs } from 'node:fs';
|
|
|
4
4
|
import { join, resolve, dirname } from 'node:path';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { requireBrain, findBrainRoot, brainPath, BRAIN_DIR } from './index.js';
|
|
7
|
-
import { addTask, upsertTask, boardText, readTodo, ensureTodo, todoTemplate, today, localStamp, setBreakpoint, moveBlocked, insertDoneGrouped, idOfTaskLine, archiveDoneInText, renderArchivePage, renderArchiveBody } from './todo.js';
|
|
7
|
+
import { addTask, upsertTask, boardText, readTodo, ensureTodo, todoTemplate, today, localStamp, setBreakpoint, moveBlocked, insertDoneGrouped, idOfTaskLine, archiveDoneInText, renderArchivePage, renderArchiveBody, DONE_KINDS, withDoneKind, doneKindOf } from './todo.js';
|
|
8
8
|
import { editFile, SKIP } from './lock.js';
|
|
9
9
|
import { appendWrapup, strandedFor, wrapupLogPath } from './wrapup.js';
|
|
10
10
|
|
|
@@ -402,7 +402,7 @@ export async function cmdLog({ dir, title, kind = 'dev' }) {
|
|
|
402
402
|
// ---------- task: 登记/推进(幂等键 = 行首 id;hook 也调这个) ----------
|
|
403
403
|
// 纪律: task 过程动作(start/done/note/blocked)只改 todo.md, 不写 log.md。
|
|
404
404
|
// log.md 是「工作成果沉淀摘要」(用户/AI 主动 abs log "..." 记), 不收工具动作流水。
|
|
405
|
-
export async function cmdTask({ dir, action, id, section, note }) {
|
|
405
|
+
export async function cmdTask({ dir, action, id, section, note, as }) {
|
|
406
406
|
const root = await requireBrain(dir || process.cwd());
|
|
407
407
|
if (action === 'start') {
|
|
408
408
|
const text = `${id}${note ? ' — ' + note : ''}`;
|
|
@@ -423,13 +423,17 @@ export async function cmdTask({ dir, action, id, section, note }) {
|
|
|
423
423
|
}
|
|
424
424
|
if (action === 'done') {
|
|
425
425
|
// 找到匹配 id 的行,勾选并归位 Done(简化:若行在某 section 则标记完成)
|
|
426
|
-
|
|
426
|
+
// --as 结语:落地(默认) / 否决 / 仅方案 —— 让 [x] 可被信任(见 todo.js DONE_KINDS)
|
|
427
|
+
if (as && !DONE_KINDS.includes(as)) {
|
|
428
|
+
throw new Error(`✗ --as 只接受: ${DONE_KINDS.join(' | ')}(收到 "${as}")`);
|
|
429
|
+
}
|
|
430
|
+
const res = await markDone(brainPath(root, 'todo.md'), id, as || '落地');
|
|
427
431
|
return res;
|
|
428
432
|
}
|
|
429
433
|
throw new Error(`unknown task action: ${action}`);
|
|
430
434
|
}
|
|
431
435
|
|
|
432
|
-
async function markDone(file, id) {
|
|
436
|
+
async function markDone(file, id, kind = '落地') {
|
|
433
437
|
const res = await editFile(file, (text) => {
|
|
434
438
|
const lines = text.split('\n');
|
|
435
439
|
let changed = false;
|
|
@@ -440,7 +444,10 @@ async function markDone(file, id) {
|
|
|
440
444
|
const l = lines[i];
|
|
441
445
|
if (!moved && idOfTaskLine(l) === wantId) {
|
|
442
446
|
changed = true;
|
|
443
|
-
const head =
|
|
447
|
+
const head = withDoneKind(
|
|
448
|
+
l.replace('- [ ]', '- [x]').replace(/\(认领[^)]*\)/, '') + ` (完成 ${today()})`,
|
|
449
|
+
kind,
|
|
450
|
+
);
|
|
444
451
|
const bp = [];
|
|
445
452
|
while (i + 1 < lines.length && lines[i + 1].trimStart().startsWith('↳')) bp.push(lines[++i]);
|
|
446
453
|
moved = [head, ...bp];
|
|
@@ -454,7 +461,7 @@ async function markDone(file, id) {
|
|
|
454
461
|
});
|
|
455
462
|
return res === SKIP
|
|
456
463
|
? `(未找到含 "${id}" 的未完成任务行)`
|
|
457
|
-
: `✓ 已完成并归位 Done: ${id}
|
|
464
|
+
: `✓ 已完成并归位 Done: ${id} 【${kind}】`;
|
|
458
465
|
}
|
|
459
466
|
|
|
460
467
|
// ---------- show: 查看 index/todo/log(只读面) ----------
|
|
@@ -669,6 +676,20 @@ export async function cmdLint({ dir }) {
|
|
|
669
676
|
if (doneLines > DONE_MAX) {
|
|
670
677
|
issues.push(`DONE-PILED-UP: Done 区 ${doneLines} 行 > ${DONE_MAX}; 跑 \`abs todo archive\` 迁出旧日期组`);
|
|
671
678
|
}
|
|
679
|
+
|
|
680
|
+
// 结语契约:Done 的 [x] 必须带【落地/否决/仅方案】。
|
|
681
|
+
// 为什么钉死: 无结语的 [x] 同时意味着"真做完了"和"只想过",读的人无法区分。
|
|
682
|
+
// 实测翻车: 把"跑通后又被撤销"的 daemon 条当成已落地 → 得出错误结论。
|
|
683
|
+
// 只查 Done 区(含历史归档前的旧条目也算),不做自动改写(改记录属内容决策,不该由 lint 代劳)。
|
|
684
|
+
const doneBody = todoTxt.split('\n').slice(di + 1);
|
|
685
|
+
const noKind = doneBody.filter((l) => /^\s*- \[x\]/.test(l) && !doneKindOf(l));
|
|
686
|
+
if (noKind.length) {
|
|
687
|
+
const sample = (noKind[0].match(/- \[x\] (\S+)/) || [, '?'])[1];
|
|
688
|
+
issues.push(
|
|
689
|
+
`DONE-NO-KIND: Done 区 ${noKind.length} 条缺结语(如 ${sample})。` +
|
|
690
|
+
`逐条补 \`--as 落地|否决|仅方案\`(新条目:abs todo done <id> --as …)`,
|
|
691
|
+
);
|
|
692
|
+
}
|
|
672
693
|
}
|
|
673
694
|
|
|
674
695
|
const n = issues.length;
|
package/src/todo.js
CHANGED
|
@@ -93,6 +93,28 @@ export function doneDateOf(line) {
|
|
|
93
93
|
return m ? m[1] : '';
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// ---------- Done 结语契约 ----------
|
|
97
|
+
// 为什么需要: `[x]` 原本同时表示「真落地」「评估后不做」「仅设计过」三种完全不同的状态,
|
|
98
|
+
// 读的人(下一个会话/未来的自己)无法区分。实测翻车: 读 daemon 条的 [x] 当成已落地,
|
|
99
|
+
// 实际它跑通后被撤销、代码全删 —— 基于假记录得出「daemon 是过配项」的错误结论。
|
|
100
|
+
// 结论: 图谱的价值取决于「可被信任」,一条状态失真的记录比一百条冗长记录的危害大一个量级。
|
|
101
|
+
export const DONE_KINDS = ['落地', '否决', '仅方案'];
|
|
102
|
+
|
|
103
|
+
/** 从 Done 任务行提取结语标记;无标记返回 ''。 */
|
|
104
|
+
export function doneKindOf(line) {
|
|
105
|
+
const m = String(line).match(/【(落地|否决|仅方案)】/);
|
|
106
|
+
return m ? m[1] : '';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 追写结语到任务行:插在 `(完成 …)` 之前,幂等(已有则原位替换)。 */
|
|
110
|
+
export function withDoneKind(line, kind) {
|
|
111
|
+
if (!kind) return line;
|
|
112
|
+
const base = String(line).replace(/【(落地|否决|仅方案)】/g, '').replace(/\s{2,}/g, ' ').trimEnd();
|
|
113
|
+
const i = base.lastIndexOf(' (完成 ');
|
|
114
|
+
if (i === -1) return `${base} 【${kind}】`;
|
|
115
|
+
return `${base.slice(0, i)} 【${kind}】${base.slice(i)}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
/** 判断一行是否为任务行(- [x] / - [ ],允许缩进)。 */
|
|
97
119
|
function isTaskLine(l) {
|
|
98
120
|
return /^\s*- \[[ x]\]/.test(l);
|