@fanchao8609/agent_brain_sync 1.31.0 → 1.31.2
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/hooks/abs-rounded-editor/format.ts +27 -13
- package/hooks/abs-rounded-editor/index.ts +31 -22
- package/hooks/abs.pi.ts +11 -4
- package/package.json +1 -1
- package/src/brain/load.js +1 -1
|
@@ -20,13 +20,36 @@ export function shortTokens(n: number | null | undefined): string {
|
|
|
20
20
|
return String(n)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/** 推理档位 → 一个大写字母(用户 2026-11-09 要求:右下角只用一个大写字母表示强度)。
|
|
24
|
+
*
|
|
25
|
+
* 为何不取首字母:minimal / medium / max 三个都是 M。这套是手选的无重写法:
|
|
26
|
+
* off=O、miNimal=N、Low=L、Medium=M、High=H、Xhigh=X、max=MX(用户指定,其余均单字母)。
|
|
27
|
+
* 改映射只改这里,并把测试里的表一起改。 */
|
|
28
|
+
export const LEVEL_LETTER: Record<string, string> = {
|
|
29
|
+
off: 'O',
|
|
30
|
+
minimal: 'N',
|
|
31
|
+
low: 'L',
|
|
32
|
+
medium: 'M',
|
|
33
|
+
high: 'H',
|
|
34
|
+
xhigh: 'X',
|
|
35
|
+
max: 'MX',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 档位 → 字母;空值或认不得的档位返回空串(那时不显示)。 */
|
|
39
|
+
export function levelLetter(level: string | null | undefined): string {
|
|
40
|
+
return LEVEL_LETTER[String(level || '').trim().toLowerCase()] || ''
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 拼右下角状态:`8.0%/1.0M 模型名 · H`。缺哪段就省哪段。
|
|
44
|
+
* 第三段是推理档位的一个大写字母(见 LEVEL_LETTER);模型不支持推理时不显。 */
|
|
45
|
+
export function buildStatus(usage: any, modelId: string, level = ''): string {
|
|
25
46
|
const parts: string[] = []
|
|
26
47
|
if (usage && usage.percent != null) parts.push(`${usage.percent.toFixed(1)}%`)
|
|
27
48
|
if (usage && usage.contextWindow) parts.push(`/${shortTokens(usage.contextWindow)}`)
|
|
28
49
|
const model = shortenModel(modelId)
|
|
29
|
-
|
|
50
|
+
const head = [parts.join(''), model].filter(Boolean).join(' ')
|
|
51
|
+
const letter = levelLetter(level)
|
|
52
|
+
return letter ? (head ? `${head} · ${letter}` : letter) : head
|
|
30
53
|
}
|
|
31
54
|
|
|
32
55
|
export function stripAnsi(s: string): string {
|
|
@@ -102,15 +125,6 @@ export function effortAnsi(level: string | null | undefined, colorMode = 'trueco
|
|
|
102
125
|
return colorMode === 'truecolor' ? `38;2;${g.rgb}` : `38;5;${g.ansi256}`
|
|
103
126
|
}
|
|
104
127
|
|
|
105
|
-
/** 「擱置」橙 #F59E0B —— 与 todo 面板的 `[擱置]`、面板大标题同色(abs.pi.ts 的 `C.hold`/`C.title`)。
|
|
106
|
-
* 用途(2026-11-09 用户要求):输入框**右下角的状态文字**(如 `30.0%/1.0M DeepSeek-V4.1-Flash`)。
|
|
107
|
-
* ★ 色值在 abs.pi.ts 里也有一份,不抽公共模块是故意的(见 EFFORT_COLOR 的注释)。 */
|
|
108
|
-
export const HOLD_COLOR = { rgb: '245;158;11', ansi256: 214 }
|
|
109
|
-
|
|
110
|
-
export function holdAnsi(colorMode = 'truecolor'): string {
|
|
111
|
-
return colorMode === 'truecolor' ? `38;2;${HOLD_COLOR.rgb}` : `38;5;${HOLD_COLOR.ansi256}`
|
|
112
|
-
}
|
|
113
|
-
|
|
114
128
|
/** 用 effortAnsi 的结果包一段文字;空串原样返回(off 时不着色)。 */
|
|
115
129
|
export function paintAnsi(ansi: string, text: string): string {
|
|
116
130
|
return ansi ? `\x1b[${ansi}m${text}\x1b[39m` : text
|
|
@@ -126,7 +140,7 @@ export function paintAnsi(ansi: string, text: string): string {
|
|
|
126
140
|
* (宁可没有,也不让边框溢出折行)。
|
|
127
141
|
*
|
|
128
142
|
* opts.labelFg:只给右边那个标签(右下角的状态文字)换个色,其余(圆角、虚线)仍用 fg。
|
|
129
|
-
* 2026-11-09
|
|
143
|
+
* 当前调用方不传(状态文字跟框线同色,2026-11-09 用户定);机制留着,要染色时传一个即可。
|
|
130
144
|
*
|
|
131
145
|
* 历史(别再重走):2026-11-08~09 曾有个 leadFg 开关,用来把 pi 嵌进上边框的
|
|
132
146
|
* `⠼ Working` 那段单独染色(橙)。现在 working 已还原成 pi 原版(不嵌边框),
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* ② 右下角显示「上下文百分比/上限 模型名」,如 `8.0%/1.0M deepseek-v4.1-flash`
|
|
7
7
|
* ③ 输入框下方原有 footer 两行全部隐藏
|
|
8
8
|
*
|
|
9
|
-
* 2026-11-09
|
|
10
|
-
* ① working
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* 2026-11-09 定案(用户来回试过好几版):
|
|
10
|
+
* ① working 状态交给 pi 嵌在上边框(`embedWorkingStatus: true`)—— 就是 pi 原本那个样子:
|
|
11
|
+
* 左上角 `⠇ Working` + 动画,颜色随整行走框线色。本扩展**不搬不改字**。
|
|
12
|
+
* 试过并作废的:搬到右下角居中、正文最右一列、右上角单字符 —— 用户看完都要回左上角。
|
|
13
|
+
* ② 边框**和框内文字**都用 pi 给的 `editor.borderColor`(即原版那七档 thinking 色)。
|
|
13
14
|
*
|
|
14
15
|
* 为什么要覆写 render():
|
|
15
16
|
* pi 的 Editor.render() 只画上下两条 `─`,每行是「左 padding + 文本 + 右 padding」,
|
|
@@ -22,15 +23,15 @@
|
|
|
22
23
|
* 注意:自动补全菜单行排在底边之下、且自带内容宽度,不套竖线(否则菜单被框住会错位)。
|
|
23
24
|
*/
|
|
24
25
|
import { CustomEditor } from '@earendil-works/pi-coding-agent'
|
|
25
|
-
import { buildStatus,
|
|
26
|
+
import { buildStatus, roundBorder, shortenModel } from './format.ts'
|
|
26
27
|
|
|
27
28
|
const SIDE = '│'
|
|
28
29
|
|
|
29
|
-
/** 右下角状态文本,如 `8.0%/1.0M deepseek-v4.1-flash`。render() 拿不到 ctx,故由事件回调维护。 */
|
|
30
|
+
/** 右下角状态文本,如 `8.0%/1.0M deepseek-v4.1-flash · H`。render() 拿不到 ctx,故由事件回调维护。 */
|
|
30
31
|
let statusLabel = ''
|
|
31
32
|
|
|
32
|
-
/**
|
|
33
|
-
let
|
|
33
|
+
/** 当前推理档位(off/minimal/…/max)。也只是为了拼状态文字 —— 边框颜色是 pi 自己上的。 */
|
|
34
|
+
let effortLevel = ''
|
|
34
35
|
|
|
35
36
|
export class RoundedEditor extends CustomEditor {
|
|
36
37
|
render(width: number): string[] {
|
|
@@ -43,28 +44,29 @@ export class RoundedEditor extends CustomEditor {
|
|
|
43
44
|
// pi 的 interactive-mode 在 updateEditorBorderColor() 里把 editor.borderColor 设成
|
|
44
45
|
// theme.getThinkingBorderColor(档位),bash 模式另给一套 —— 直接复用就等于原版那套
|
|
45
46
|
// (含 off 的暗灰),而且 /theme、切档、切 bash 模式时 pi 自己会换并请求重绘。
|
|
46
|
-
//
|
|
47
|
+
// 光标色 / 输入框背景 / 聚焦外发光是 pi 内部画的,接管不了。
|
|
47
48
|
const border = (this as any).borderColor
|
|
48
49
|
const wrap = typeof border === 'function' ? border.bind(this) : (s: string) => s
|
|
49
50
|
const out: string[] = []
|
|
50
51
|
|
|
51
|
-
// 上边框:原本是整行
|
|
52
|
+
// 上边框:原本是整行 `─`(含 pi 嵌进来的 working 状态与滚动提示),两端换圆角。
|
|
53
|
+
// working 就是 pi 嵌的那个样子(左上角 `⠇ Working` + 动画),不搬不改字(2026-11-09 定稿)。
|
|
54
|
+
// 整行会被 roundBorder 重上一次框线色,所以它跟边框同色。
|
|
52
55
|
out.push(roundBorder(lines[0], wrap, '╭', '╮'))
|
|
53
56
|
// 下边框在原结果的倒数第 (1 + 自动补全行数) 位 —— 补全菜单排在它后面。
|
|
54
57
|
const autocompleteHeight = (this as any).renderedAutocompleteHeight ?? 0
|
|
55
58
|
const bottomIdx = lines.length - 1 - autocompleteHeight
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
// 正文行:两端各包一根竖线,文字也上推理色(2026-11-09 用户要求「输入框文字颜色与推理一致」)。
|
|
61
|
+
// 先剥掉 pi 原生文字色再包 —— 否则内层色码会提前收色,看着像只有前半行有颜色。
|
|
62
|
+
for (const body of lines.slice(1, bottomIdx)) {
|
|
63
|
+
out.push(wrap(SIDE) + wrap(body.replace(/\x1b\[[0-9;]*m/g, '')) + wrap(SIDE))
|
|
60
64
|
}
|
|
61
65
|
// 下边框(2026-11-07 还原:左下角的 fanchao·昵称已去掉 —— 用户嫌难看,
|
|
62
66
|
// 回 todo 面板标题;roundBorder 的 leftLabel 参数保留备用)。
|
|
63
|
-
// 状态文字(`
|
|
64
|
-
//
|
|
65
|
-
out.push(roundBorder(lines[bottomIdx], wrap, '╰', '╯', statusLabel
|
|
66
|
-
labelFg: (s: string) => paintAnsi(holdAnsi(colorMode), s),
|
|
67
|
-
}))
|
|
67
|
+
// 状态文字(`31.8%/1.0M 模型名`)2026-11-09 从右上角搬到右下角,颜色跟框线一致
|
|
68
|
+
// (用户当日先要搁置橙、看完又要改回推理色 —— 以最新为准)。
|
|
69
|
+
out.push(roundBorder(lines[bottomIdx], wrap, '╰', '╯', statusLabel))
|
|
68
70
|
// 自动补全行原样透传(不套竖线)。
|
|
69
71
|
for (let i = bottomIdx + 1; i < lines.length; i++) out.push(lines[i])
|
|
70
72
|
return out
|
|
@@ -74,7 +76,12 @@ export class RoundedEditor extends CustomEditor {
|
|
|
74
76
|
/** 刷新右下角状态文本(每次渲染前由事件与定时器调用)。 */
|
|
75
77
|
function refreshStatus(ctx: any): void {
|
|
76
78
|
try {
|
|
77
|
-
|
|
79
|
+
effortLevel = String(ctx?.thinkingLevel || '')
|
|
80
|
+
} catch {
|
|
81
|
+
effortLevel = ''
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
statusLabel = buildStatus(ctx?.getContextUsage?.(), ctx?.model?.id ?? '', effortLevel)
|
|
78
85
|
} catch {
|
|
79
86
|
statusLabel = shortenModel(ctx?.model?.id ?? '')
|
|
80
87
|
}
|
|
@@ -83,8 +90,6 @@ function refreshStatus(ctx: any): void {
|
|
|
83
90
|
export default function piRoundedEditor(pi: any): void {
|
|
84
91
|
pi.on('session_start', (_event: unknown, ctx: any) => {
|
|
85
92
|
if (!ctx?.ui?.setEditorComponent) return
|
|
86
|
-
// 终端色模式(256 色档位用);取不到就当真彩。
|
|
87
|
-
try { colorMode = String(ctx.ui?.theme?.getColorMode?.() || 'truecolor') } catch { colorMode = 'truecolor' }
|
|
88
93
|
refreshStatus(ctx)
|
|
89
94
|
// 第四个参数 embedWorkingStatus: true —— working 状态进上边框,不单独占一行(2026-11-09 用户定案)。
|
|
90
95
|
// 颜色不用管:pi 嵌入时用 editor.borderColor,本扩展又把整行重上一次框线色,两边同为档位色。
|
|
@@ -104,7 +109,11 @@ export default function piRoundedEditor(pi: any): void {
|
|
|
104
109
|
ctx?.ui?.requestRender?.()
|
|
105
110
|
})
|
|
106
111
|
|
|
107
|
-
//
|
|
112
|
+
// 切推理强度:档位字母要跟着变(边框色是 pi 自己换并重绘的,不用管)。
|
|
113
|
+
pi.on('thinking_level_select', (_event: any, ctx: any) => {
|
|
114
|
+
refreshStatus(ctx)
|
|
115
|
+
ctx?.ui?.requestRender?.()
|
|
116
|
+
})
|
|
108
117
|
|
|
109
118
|
// 上下文占用随每轮对话变化 —— 轮次结束与消息结束时刷新。
|
|
110
119
|
for (const ev of ['turn_end', 'message_end', 'tool_execution_end']) {
|
package/hooks/abs.pi.ts
CHANGED
|
@@ -265,7 +265,14 @@ const TODO_GUIDELINES = [
|
|
|
265
265
|
// ⑧ 有疑惑先查记忆(用户 2026-11-03 定):主动搜索不是等报错才应激,
|
|
266
266
|
// 而是「心里形成关键词/有疑问的那一刻」自己查。上面七条只讲了登记,
|
|
267
267
|
// 没讲「记下来的东西怎么取」——实测一场会话从头到尾没查过一次记忆。
|
|
268
|
-
|
|
268
|
+
// ★ 2026-11-09 修:原来写的是 `abs recall`,那个命令 11-08 就删了 ——
|
|
269
|
+
// 规矩给成不存在的命令,模型只能白撞一次。
|
|
270
|
+
'When a doubt forms about something the project has likely dealt with before (a design choice, a past bug, a convention), run `abs query <关键词>` BEFORE acting on the assumption. Do not wait for an error or for the user to remind you — asking the memory is your own decision, made the moment the question arises.',
|
|
271
|
+
// ⑨ 参数规矩要说在**前面**(用户 2026-11-09 提:闸门拦得住,但得先告诉模型规矩)。
|
|
272
|
+
// 起因:另一个 pi 会话(pi-capsule-tools)连撞两次 —— ① 不传 cwd → 被 schema 拒;
|
|
273
|
+
// ② 把整句话当 id → 被「id 不能含空格」拒。两次都是「没提前说」而不是「不会做」。
|
|
274
|
+
// 放在常驻指引里而不是只靠工具 description:description 模型常不读。
|
|
275
|
+
'abs task tool parameters — wrong ones are rejected before anything is written, so get them right the first time: `cwd` = the project root that contains `.brain/` and is ALWAYS required; `id` = ONE word, no spaces / tabs / brackets (put the readable name in `note`); `start`/`add` require `note` (what to do, one sentence); `child_done` / `child_undone` / `state` take `n` = which child item (1-based, default = last); `done` takes the conclusion in `note` plus `as` (落地 / 否决 / 仅方案). A child row must keep `note` to ONE short sentence (≤200 字) — longer text goes to `abs log` (progress) or `abs note` (lessons), not the board.',
|
|
269
276
|
]
|
|
270
277
|
|
|
271
278
|
/** 当前项目是否有 .brain 图谱 —— 有才加指引(没图谱的项目里这指引是噪音)。
|
|
@@ -526,7 +533,7 @@ export function injectSessionGuideline(options: any, sid: string): boolean {
|
|
|
526
533
|
return true
|
|
527
534
|
}
|
|
528
535
|
|
|
529
|
-
/** 注入 todo 指引。静态
|
|
536
|
+
/** 注入 todo 指引。静态 9 条(行为要求 + 参数规矩) + 实时看板(对齐用)—— 两者职责不同:
|
|
530
537
|
* 静态条说「该怎么做」,看板说「现在是什么」。 */
|
|
531
538
|
export function injectTodoGuidelines(options: any, board?: string | null): boolean {
|
|
532
539
|
if (String(process.env.ABS_TODO_GUIDE || '') === '0') return false
|
|
@@ -556,8 +563,8 @@ const LOAD_MARK = '[开工] '
|
|
|
556
563
|
* 每次开会话都多塞 200 行,而其中大部分(图谱组织/容量纪律/tesardown 流程)
|
|
557
564
|
* 只在真正做 abs 维护时才用得上。
|
|
558
565
|
*
|
|
559
|
-
* 与【常驻
|
|
560
|
-
* -
|
|
566
|
+
* 与【常驻 9 条】TODO_GUIDELINES 的区别(两者职责不同,别合并):
|
|
567
|
+
* - 9 条常驻:行为要求与参数规矩,每轮都在(看板对齐用)。
|
|
561
568
|
* - 这 3 条:**只在开工第一轮出现一次** —— 因为它们是「开工那一刻」的触发器,
|
|
562
569
|
* 每次都说等于噪音(历史教训:静态指引有痕 414 次、行为 0 变化)。
|
|
563
570
|
*
|
package/package.json
CHANGED
package/src/brain/load.js
CHANGED
|
@@ -17,7 +17,7 @@ const SESSION_UNFILLED_SHOW = 5;
|
|
|
17
17
|
/** 开机屏的使用方法(2026-11-08 用户定 load 三件套之一)。一行一个动作,别写成手册。 */
|
|
18
18
|
const LOAD_USAGE = [
|
|
19
19
|
'--- 用法 ---',
|
|
20
|
-
'
|
|
20
|
+
'查经验 abs query <词> 记一句 abs note "…"',
|
|
21
21
|
'立概念 abs concept <id> --title "…" 登记活 abs todo start <id> --note "做什么"',
|
|
22
22
|
'收尾 abs todo done <id> --as 落地|否决|仅方案 "结论"',
|
|
23
23
|
'体检 abs lint 看全量 abs show index|log|status',
|