pi-cc-extensions 0.8.67 → 0.8.69
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.en.md +6 -4
- package/README.md +6 -4
- package/extensions/config/config.ts +30 -38
- package/extensions/config/panel.ts +62 -13
- package/extensions/feature/agent-summary/core.ts +5 -26
- package/extensions/feature/agent-summary/index.ts +2 -2
- package/extensions/feature/compact-thinking.ts +0 -3
- package/extensions/feature/context.ts +1 -0
- package/extensions/feature/reference/index.ts +7 -53
- package/extensions/feature/reference/session.ts +2 -14
- package/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/extensions/feature/shell/startup-header.ts +1 -6
- package/extensions/index.ts +2 -0
- package/extensions/renderer/compact-mode.ts +8 -23
- package/extensions/renderer/default-mode.ts +26 -64
- package/extensions/renderer/index.ts +0 -13
- package/extensions/renderer/mouse/hover.ts +0 -5
- package/extensions/renderer/mouse/interaction.ts +57 -27
- package/extensions/renderer/mouse/packets.ts +1 -0
- package/extensions/renderer/mouse/scroll.ts +7 -14
- package/extensions/renderer/tool/diff/diff-limits.ts +2 -10
- package/extensions/renderer/tool/diff/diff-parse.ts +2 -9
- package/extensions/renderer/tool/names.ts +1 -1
- package/extensions/renderer/tool/result.ts +45 -31
- package/extensions/utils/patch-keys.ts +5 -10
- package/extensions/utils/sgr-mouse.ts +6 -0
- package/package.json +3 -3
package/README.en.md
CHANGED
|
@@ -61,16 +61,18 @@ Run `/reload` after installation.
|
|
|
61
61
|
"editDiffCollapsedLines": 24, // Edit collapse lines; beyond that shows the expand hint
|
|
62
62
|
"writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
|
|
63
63
|
"diffWordWrap": true, // wrap long diff lines
|
|
64
|
-
"expandedPreviewMaxLines": 40, // max lines for expanded bodies
|
|
65
|
-
"toolInputNameLength": 100, // tool summary path/command clip length
|
|
66
64
|
|
|
67
65
|
// thinking
|
|
68
66
|
"useSummaryTitlesAsThinkingTitle": true, // use latest summary as thinking title
|
|
69
67
|
"previewLines": 3, // preview lines; 0 hides
|
|
70
68
|
"animationIntervalMs": 90, // title animation interval (ms)
|
|
71
|
-
"dimThinkingText": false,
|
|
69
|
+
"dimThinkingText": false, // dim thinking body text
|
|
72
70
|
|
|
73
71
|
// ui
|
|
72
|
+
"expandedInputMaxLines": 5, // expanded tool Input lines; overflow shows a footer hint
|
|
73
|
+
"expandedOutputMaxLines": 10, // expanded tool Output lines; overflow shows a footer hint
|
|
74
|
+
"expandedPreviewMaxLines": 40, // max lines for expanded diff/TaskList bodies
|
|
75
|
+
"inputClip": 100, // tool summary path/command clip length
|
|
74
76
|
"showStartupHeader": true, // startup header (logo + tips) toggle
|
|
75
77
|
"scrollStepLines": 3, // fullscreen wheel scroll step
|
|
76
78
|
|
|
@@ -84,7 +86,7 @@ Run `/reload` after installation.
|
|
|
84
86
|
}
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
> **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. Double-click an expanded panel to collapse it.
|
|
89
|
+
> **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. When expanded Input/Output exceeds the line cap, the footer `… +N more lines • click to show more` opens a full preview. Double-click an expanded panel to collapse it.
|
|
88
90
|
>
|
|
89
91
|
> **Tip**: set `markdown.mermaid` to `final` via `~/.pi/agent/settings.json` or the Mermaid diagrams option in `/settings`. Default `streaming` redraws per frame; `final` renders once at completion.
|
|
90
92
|
|
package/README.md
CHANGED
|
@@ -61,16 +61,18 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
61
61
|
"editDiffCollapsedLines": 24, // Edit 折叠行数,超出显示展开提示
|
|
62
62
|
"writeDiffCollapsedLines": 0, // write 折叠行数,0 仅显示创建摘要
|
|
63
63
|
"diffWordWrap": true, // 长 diff 行换行
|
|
64
|
-
"expandedPreviewMaxLines": 40, // 展开正文最大行数
|
|
65
|
-
"toolInputNameLength": 100, // 工具摘要 path/command 折叠字符数
|
|
66
64
|
|
|
67
65
|
// thinking
|
|
68
66
|
"useSummaryTitlesAsThinkingTitle": true, // 用最新摘要作思考标题
|
|
69
67
|
"previewLines": 3, // 预览行数,0 隐藏
|
|
70
68
|
"animationIntervalMs": 90, // 标题动画间隔(毫秒)
|
|
71
|
-
"dimThinkingText": false,
|
|
69
|
+
"dimThinkingText": false, // thinking 正文用 dim 色
|
|
72
70
|
|
|
73
71
|
// ui
|
|
72
|
+
"expandedInputMaxLines": 5, // 展开工具卡 Input 可见行数,超出在末行显示展开提示
|
|
73
|
+
"expandedOutputMaxLines": 10, // 展开工具卡 Output 可见行数,超出在末行显示展开提示
|
|
74
|
+
"expandedPreviewMaxLines": 40, // 展开 diff/TaskList 正文最大行数
|
|
75
|
+
"inputClip": 100, // 工具摘要 path/command 折叠字符数
|
|
74
76
|
"showStartupHeader": true, // 启动头(logo + tips)开关
|
|
75
77
|
"scrollStepLines": 3, // fullscreen 滚轮步进
|
|
76
78
|
|
|
@@ -84,7 +86,7 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
84
86
|
}
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
> **Fullscreen**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact
|
|
89
|
+
> **Fullscreen**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要;展开后 Input/Output 超行时,末行 `… +N more lines • click to show more` 打开全量预览。双击展开面板收起。
|
|
88
90
|
>
|
|
89
91
|
> **建议**:`markdown.mermaid` 设为 `final`(`~/.pi/agent/settings.json` 或 `/settings` 面板的 Mermaid diagrams 选项)。默认 `streaming` 逐帧重绘,`final` 渲染最终版更稳定。
|
|
90
92
|
|
|
@@ -5,9 +5,6 @@ import { join } from "node:path";
|
|
|
5
5
|
|
|
6
6
|
export type CompactStyleMode = "on" | "compact" | "off";
|
|
7
7
|
|
|
8
|
-
// ── diff 显示配置:作为配置 schema 的一部分由 config 层拥有 ──
|
|
9
|
-
// renderer(diff)从 config 导入并 re-export,避免 config → renderer 的反向依赖。
|
|
10
|
-
|
|
11
8
|
export type DiffViewMode = "auto" | "split" | "unified";
|
|
12
9
|
export type DiffIndicatorMode = "bars" | "classic" | "none";
|
|
13
10
|
|
|
@@ -52,7 +49,9 @@ export type Config = {
|
|
|
52
49
|
writeDiffCollapsedLines: number;
|
|
53
50
|
diffWordWrap: boolean;
|
|
54
51
|
expandedPreviewMaxLines: number;
|
|
55
|
-
|
|
52
|
+
expandedInputMaxLines: number;
|
|
53
|
+
expandedOutputMaxLines: number;
|
|
54
|
+
inputClip: number;
|
|
56
55
|
useSummaryTitlesAsThinkingTitle: boolean;
|
|
57
56
|
previewLines: number;
|
|
58
57
|
animationIntervalMs: number;
|
|
@@ -78,8 +77,12 @@ export const DIFF_COLLAPSED_LINES_VALUES = ["12", "24", "36", "48", "80", "120"]
|
|
|
78
77
|
export const WRITE_DIFF_COLLAPSED_LINES_VALUES = ["0", "4", "8", "12", "24", "36"];
|
|
79
78
|
/** Presets for expanded body height — keep low options first so cycling stays TUI-friendly. */
|
|
80
79
|
export const EXPANDED_PREVIEW_MAX_LINES_VALUES = ["40", "60", "80", "120", "200", "500", "2000"];
|
|
80
|
+
/** 展开工具卡 Input 可见行数预设。 */
|
|
81
|
+
export const EXPANDED_INPUT_MAX_LINES_VALUES = ["5", "10", "20", "40", "80"];
|
|
82
|
+
/** 展开工具卡 Output 可见行数预设。 */
|
|
83
|
+
export const EXPANDED_OUTPUT_MAX_LINES_VALUES = ["10", "20", "40", "80", "120"];
|
|
81
84
|
/** 工具摘要里 path/command 等输入的折叠字符数。 */
|
|
82
|
-
export const
|
|
85
|
+
export const INPUT_CLIP_VALUES = ["40", "60", "80", "100", "120", "160"];
|
|
83
86
|
export const THINKING_PREVIEW_LINES_VALUES = ["0", "1", "3", "5", "10"];
|
|
84
87
|
export const THINKING_ANIMATION_INTERVAL_VALUES = ["40", "60", "90", "120", "180"];
|
|
85
88
|
/** fullscreen 滚轮步进行数预设。 */
|
|
@@ -107,7 +110,9 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
107
110
|
writeDiffCollapsedLines: DEFAULT_TOOL_DISPLAY_CONFIG.writeDiffCollapsedLines,
|
|
108
111
|
diffWordWrap: DEFAULT_TOOL_DISPLAY_CONFIG.diffWordWrap,
|
|
109
112
|
expandedPreviewMaxLines: DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
110
|
-
|
|
113
|
+
expandedInputMaxLines: 5,
|
|
114
|
+
expandedOutputMaxLines: 10,
|
|
115
|
+
inputClip: 100,
|
|
111
116
|
useSummaryTitlesAsThinkingTitle: true,
|
|
112
117
|
previewLines: 3,
|
|
113
118
|
animationIntervalMs: 90,
|
|
@@ -141,16 +146,7 @@ export function pickPositiveNumber(value: unknown, fallback: number, min = 1): n
|
|
|
141
146
|
|
|
142
147
|
export function normalizeConfig(input: unknown): Config {
|
|
143
148
|
const source = (input && typeof input === "object" ? input : {}) as Record<string, unknown>;
|
|
144
|
-
const mode = source.mode;
|
|
145
|
-
// 旧 `enabled: boolean` 配置迁移;compact 已恢复为受支持模式,不再回退 on。
|
|
146
|
-
const migratedMode: CompactStyleMode =
|
|
147
|
-
mode === "on" || mode === "compact" || mode === "off"
|
|
148
|
-
? mode
|
|
149
|
-
: typeof source.enabled === "boolean"
|
|
150
|
-
? source.enabled
|
|
151
|
-
? "on"
|
|
152
|
-
: "off"
|
|
153
|
-
: "on";
|
|
149
|
+
const mode = pickEnum(source.mode, ["on", "compact", "off"], DEFAULT_CONFIG.mode);
|
|
154
150
|
const excludeRenderers = Array.isArray(source.excludeRenderers)
|
|
155
151
|
? [
|
|
156
152
|
...new Set(
|
|
@@ -161,7 +157,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
161
157
|
]
|
|
162
158
|
: [];
|
|
163
159
|
return {
|
|
164
|
-
mode
|
|
160
|
+
mode,
|
|
165
161
|
excludeRenderers,
|
|
166
162
|
diffViewMode: pickEnum(source.diffViewMode, DIFF_VIEW_MODES, DEFAULT_CONFIG.diffViewMode),
|
|
167
163
|
diffIndicatorMode: pickEnum(
|
|
@@ -176,7 +172,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
176
172
|
300,
|
|
177
173
|
),
|
|
178
174
|
editDiffCollapsedLines: pickPositiveInt(
|
|
179
|
-
source.editDiffCollapsedLines
|
|
175
|
+
source.editDiffCollapsedLines,
|
|
180
176
|
DEFAULT_CONFIG.editDiffCollapsedLines,
|
|
181
177
|
1,
|
|
182
178
|
500,
|
|
@@ -194,12 +190,19 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
194
190
|
10,
|
|
195
191
|
50_000,
|
|
196
192
|
),
|
|
197
|
-
|
|
198
|
-
source.
|
|
199
|
-
DEFAULT_CONFIG.
|
|
200
|
-
|
|
201
|
-
|
|
193
|
+
expandedInputMaxLines: pickPositiveInt(
|
|
194
|
+
source.expandedInputMaxLines,
|
|
195
|
+
DEFAULT_CONFIG.expandedInputMaxLines,
|
|
196
|
+
1,
|
|
197
|
+
5_000,
|
|
198
|
+
),
|
|
199
|
+
expandedOutputMaxLines: pickPositiveInt(
|
|
200
|
+
source.expandedOutputMaxLines,
|
|
201
|
+
DEFAULT_CONFIG.expandedOutputMaxLines,
|
|
202
|
+
1,
|
|
203
|
+
5_000,
|
|
202
204
|
),
|
|
205
|
+
inputClip: pickPositiveInt(source.inputClip, DEFAULT_CONFIG.inputClip, 8, 500),
|
|
203
206
|
useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle !== false,
|
|
204
207
|
previewLines: pickPositiveInt(
|
|
205
208
|
source.previewLines,
|
|
@@ -258,7 +261,9 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
258
261
|
`writeCollapsed=${source.writeDiffCollapsedLines}`,
|
|
259
262
|
`diffWordWrap=${source.diffWordWrap ? "on" : "off"}`,
|
|
260
263
|
`expandedMax=${source.expandedPreviewMaxLines}`,
|
|
261
|
-
`
|
|
264
|
+
`expandedInput=${source.expandedInputMaxLines}`,
|
|
265
|
+
`expandedOutput=${source.expandedOutputMaxLines}`,
|
|
266
|
+
`inputClip=${source.inputClip}`,
|
|
262
267
|
`thinkingTitle=${source.useSummaryTitlesAsThinkingTitle ? "summary" : "default"}`,
|
|
263
268
|
`thinkingPreview=${source.previewLines}`,
|
|
264
269
|
`thinkingAnimation=${source.animationIntervalMs}ms`,
|
|
@@ -282,20 +287,7 @@ function loadConfig(): Config {
|
|
|
282
287
|
const source = existsSync(CONFIG_PATH)
|
|
283
288
|
? (JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as Record<string, unknown>)
|
|
284
289
|
: {};
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
typeof source.enabled === "boolean" &&
|
|
288
|
-
source.mode !== "on" &&
|
|
289
|
-
source.mode !== "compact" &&
|
|
290
|
-
source.mode !== "off"
|
|
291
|
-
) {
|
|
292
|
-
try {
|
|
293
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(normalized, null, 2));
|
|
294
|
-
} catch {
|
|
295
|
-
// A read-only config still uses the migrated in-memory value.
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
return normalized;
|
|
290
|
+
return normalizeConfig(source);
|
|
299
291
|
} catch {
|
|
300
292
|
// Ignore bad config and fall back to defaults.
|
|
301
293
|
}
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
DIFF_SPLIT_MIN_WIDTH_VALUES,
|
|
18
18
|
DIFF_VIEW_MODES,
|
|
19
19
|
EXCLUDE_RENDERER_CANDIDATES,
|
|
20
|
+
EXPANDED_INPUT_MAX_LINES_VALUES,
|
|
21
|
+
EXPANDED_OUTPUT_MAX_LINES_VALUES,
|
|
20
22
|
EXPANDED_PREVIEW_MAX_LINES_VALUES,
|
|
21
23
|
formatExcludeRenderers,
|
|
22
24
|
getCompactThinkingConfig,
|
|
@@ -25,7 +27,7 @@ import {
|
|
|
25
27
|
SCROLL_STEP_LINES_VALUES,
|
|
26
28
|
THINKING_ANIMATION_INTERVAL_VALUES,
|
|
27
29
|
THINKING_PREVIEW_LINES_VALUES,
|
|
28
|
-
|
|
30
|
+
INPUT_CLIP_VALUES,
|
|
29
31
|
WRITE_DIFF_COLLAPSED_LINES_VALUES,
|
|
30
32
|
updateConfig,
|
|
31
33
|
type CompactStyleMode,
|
|
@@ -264,15 +266,15 @@ export async function showCcstylePanel(
|
|
|
264
266
|
);
|
|
265
267
|
},
|
|
266
268
|
};
|
|
267
|
-
const
|
|
268
|
-
id: "
|
|
269
|
+
const inputClipSetting = {
|
|
270
|
+
id: "inputClip",
|
|
269
271
|
label: "Input clip",
|
|
270
272
|
description:
|
|
271
273
|
"Max characters for path/command/name in single and grouped tool summaries. Enter to type a custom value.",
|
|
272
|
-
currentValue: String(config.
|
|
273
|
-
values: [...
|
|
274
|
+
currentValue: String(config.inputClip),
|
|
275
|
+
values: [...INPUT_CLIP_VALUES],
|
|
274
276
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
275
|
-
buildNumberInputSubmenu(theme,
|
|
277
|
+
buildNumberInputSubmenu(theme, inputClipSetting, closeSubmenu),
|
|
276
278
|
};
|
|
277
279
|
const diffViewSetting = {
|
|
278
280
|
id: "diffViewMode",
|
|
@@ -327,11 +329,31 @@ export async function showCcstylePanel(
|
|
|
327
329
|
currentValue: config.diffWordWrap ? "on" : "off",
|
|
328
330
|
values: ["on", "off"],
|
|
329
331
|
};
|
|
332
|
+
const expandedInputSetting = {
|
|
333
|
+
id: "expandedInputMaxLines",
|
|
334
|
+
label: "Expanded input lines",
|
|
335
|
+
description:
|
|
336
|
+
"Max Input section lines in an expanded tool card. Overflow shows click to show more. Default 5.",
|
|
337
|
+
currentValue: String(config.expandedInputMaxLines),
|
|
338
|
+
values: [...EXPANDED_INPUT_MAX_LINES_VALUES],
|
|
339
|
+
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
340
|
+
buildNumberInputSubmenu(theme, expandedInputSetting, closeSubmenu),
|
|
341
|
+
};
|
|
342
|
+
const expandedOutputSetting = {
|
|
343
|
+
id: "expandedOutputMaxLines",
|
|
344
|
+
label: "Expanded output lines",
|
|
345
|
+
description:
|
|
346
|
+
"Max Output section lines in an expanded tool card. Overflow shows click to show more. Default 10.",
|
|
347
|
+
currentValue: String(config.expandedOutputMaxLines),
|
|
348
|
+
values: [...EXPANDED_OUTPUT_MAX_LINES_VALUES],
|
|
349
|
+
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
350
|
+
buildNumberInputSubmenu(theme, expandedOutputSetting, closeSubmenu),
|
|
351
|
+
};
|
|
330
352
|
const expandedMaxSetting = {
|
|
331
353
|
id: "expandedPreviewMaxLines",
|
|
332
354
|
label: "Expanded max lines",
|
|
333
355
|
description:
|
|
334
|
-
"Max
|
|
356
|
+
"Max diff/TaskList body lines when expanded. Tool Input/Output use the two settings above.",
|
|
335
357
|
currentValue: String(config.expandedPreviewMaxLines),
|
|
336
358
|
values: [...EXPANDED_PREVIEW_MAX_LINES_VALUES],
|
|
337
359
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
@@ -452,11 +474,11 @@ export async function showCcstylePanel(
|
|
|
452
474
|
return;
|
|
453
475
|
}
|
|
454
476
|
switch (id) {
|
|
455
|
-
case "
|
|
477
|
+
case "inputClip":
|
|
456
478
|
updateConfig({
|
|
457
|
-
|
|
479
|
+
inputClip: pickPositiveInt(value, DEFAULT_CONFIG.inputClip, 8, 500),
|
|
458
480
|
});
|
|
459
|
-
|
|
481
|
+
inputClipSetting.currentValue = String(config.inputClip);
|
|
460
482
|
break;
|
|
461
483
|
case "mode": {
|
|
462
484
|
// 选项值带 Experimental 标记,选择后还原为真实 mode 值。
|
|
@@ -512,6 +534,28 @@ export async function showCcstylePanel(
|
|
|
512
534
|
? "Long diff lines wrap within the panel width."
|
|
513
535
|
: "Long diff lines are truncated to the panel width.";
|
|
514
536
|
break;
|
|
537
|
+
case "expandedInputMaxLines":
|
|
538
|
+
updateConfig({
|
|
539
|
+
expandedInputMaxLines: pickPositiveInt(
|
|
540
|
+
value,
|
|
541
|
+
DEFAULT_CONFIG.expandedInputMaxLines,
|
|
542
|
+
1,
|
|
543
|
+
5_000,
|
|
544
|
+
),
|
|
545
|
+
});
|
|
546
|
+
expandedInputSetting.currentValue = String(config.expandedInputMaxLines);
|
|
547
|
+
break;
|
|
548
|
+
case "expandedOutputMaxLines":
|
|
549
|
+
updateConfig({
|
|
550
|
+
expandedOutputMaxLines: pickPositiveInt(
|
|
551
|
+
value,
|
|
552
|
+
DEFAULT_CONFIG.expandedOutputMaxLines,
|
|
553
|
+
1,
|
|
554
|
+
5_000,
|
|
555
|
+
),
|
|
556
|
+
});
|
|
557
|
+
expandedOutputSetting.currentValue = String(config.expandedOutputMaxLines);
|
|
558
|
+
break;
|
|
515
559
|
case "expandedPreviewMaxLines":
|
|
516
560
|
updateConfig({
|
|
517
561
|
expandedPreviewMaxLines: pickPositiveInt(
|
|
@@ -580,8 +624,6 @@ export async function showCcstylePanel(
|
|
|
580
624
|
diffCollapsedSetting,
|
|
581
625
|
writeDiffCollapsedSetting,
|
|
582
626
|
diffWordWrapSetting,
|
|
583
|
-
expandedMaxSetting,
|
|
584
|
-
toolInputNameSetting,
|
|
585
627
|
],
|
|
586
628
|
},
|
|
587
629
|
{
|
|
@@ -597,7 +639,14 @@ export async function showCcstylePanel(
|
|
|
597
639
|
{
|
|
598
640
|
id: "ui",
|
|
599
641
|
label: "UI",
|
|
600
|
-
items: [
|
|
642
|
+
items: [
|
|
643
|
+
expandedInputSetting,
|
|
644
|
+
expandedOutputSetting,
|
|
645
|
+
expandedMaxSetting,
|
|
646
|
+
inputClipSetting,
|
|
647
|
+
startupHeaderSetting,
|
|
648
|
+
scrollStepSetting,
|
|
649
|
+
],
|
|
601
650
|
},
|
|
602
651
|
{
|
|
603
652
|
id: "feature",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Agent 回合摘要:统计一次 agent 运行的工具使用并格式化成摘要文本。
|
|
3
3
|
*
|
|
4
4
|
* 分类:bash / read / edit / write / other(精确工具名;MCP 风格名归 other)。
|
|
5
|
-
* 计数:bash 按调用次数;read/edit/write 按非空 path/file_path 去重;other 按调用次数。
|
|
5
|
+
* 计数:bash/powershell 按调用次数;read/edit/write 按非空 path/file_path 去重;other 按调用次数。
|
|
6
6
|
* 失败单独累计;另记回合耗时。
|
|
7
7
|
*
|
|
8
8
|
* 呈现:`summaryLine` 纯文本,`summaryMarkdown` Markdown(可 box 引用块)。
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { formatDuration } from "../../utils/format.ts";
|
|
13
|
-
export { formatDuration };
|
|
14
13
|
|
|
15
14
|
/** 工具分类。 */
|
|
16
15
|
export type AgentToolCategory = "bash" | "read" | "edit" | "write" | "other";
|
|
@@ -28,7 +27,7 @@ export type AgentSummaryData = {
|
|
|
28
27
|
|
|
29
28
|
export function classifyTool(toolName: string): AgentToolCategory {
|
|
30
29
|
const base = toolName.split(".").pop() ?? toolName;
|
|
31
|
-
if (base === "bash") return "bash";
|
|
30
|
+
if (base === "bash" || base === "powershell") return "bash";
|
|
32
31
|
if (base === "read") return "read";
|
|
33
32
|
if (base === "edit") return "edit";
|
|
34
33
|
if (base === "write") return "write";
|
|
@@ -114,24 +113,8 @@ function summaryParts(data: AgentSummaryData): string[] {
|
|
|
114
113
|
return parts;
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
/** 纯文本摘要行:句首大写 + 可选耗时。 */
|
|
118
|
-
export function summaryLine(data: AgentSummaryData): string {
|
|
119
|
-
const parts = summaryParts(data);
|
|
120
|
-
if (parts.length === 0) return "";
|
|
121
|
-
const text = parts.join(", ");
|
|
122
|
-
const capitalized = text[0].toUpperCase() + text.slice(1);
|
|
123
|
-
const duration = formatDuration(data.durationMs);
|
|
124
|
-
return duration ? `${capitalized} · ${duration}` : capitalized;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Markdown 摘要行。
|
|
129
|
-
* `box` 为 true:引用块 `> *斜体*`;false:整体加粗。
|
|
130
|
-
* `colors`:仅数字染色(success / failed)。
|
|
131
|
-
*/
|
|
132
116
|
export function summaryMarkdown(
|
|
133
117
|
data: AgentSummaryData,
|
|
134
|
-
box = false,
|
|
135
118
|
colors: { success: string; failed: string } = { success: "", failed: "" },
|
|
136
119
|
): string {
|
|
137
120
|
const parts = summaryParts(data);
|
|
@@ -148,7 +131,7 @@ export function summaryMarkdown(
|
|
|
148
131
|
.join(", ");
|
|
149
132
|
const duration = formatDuration(data.durationMs);
|
|
150
133
|
const line = duration ? `${text} · ${duration}` : text;
|
|
151
|
-
return
|
|
134
|
+
return `> *${line}*`;
|
|
152
135
|
}
|
|
153
136
|
|
|
154
137
|
/**
|
|
@@ -160,8 +143,7 @@ export function summaryMarkdown(
|
|
|
160
143
|
export function bindAgentSummary(
|
|
161
144
|
pi: ExtensionAPI,
|
|
162
145
|
onSummary: (data: AgentSummaryData) => void,
|
|
163
|
-
|
|
164
|
-
): () => void {
|
|
146
|
+
): void {
|
|
165
147
|
let summary = new AgentRunSummary();
|
|
166
148
|
pi.on("agent_start", async () => {
|
|
167
149
|
summary = new AgentRunSummary();
|
|
@@ -173,9 +155,6 @@ export function bindAgentSummary(
|
|
|
173
155
|
summary.recordToolResult(event.isError === true);
|
|
174
156
|
});
|
|
175
157
|
pi.on("agent_end", async () => {
|
|
176
|
-
if (summary.toolCount >=
|
|
158
|
+
if (summary.toolCount >= 2) onSummary(summary.snapshot());
|
|
177
159
|
});
|
|
178
|
-
return () => {
|
|
179
|
-
summary = new AgentRunSummary();
|
|
180
|
-
};
|
|
181
160
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Agent 回合摘要展示:agent_end 时把本回合工具统计写入会话条目,
|
|
3
3
|
* 由 entry renderer 渲染为 markdown 引用块 `> [!TIP] *斜体内容*`。
|
|
4
4
|
*
|
|
5
|
-
* 统计复用 ./core.ts(bash/read/edit/write/other)。
|
|
5
|
+
* 统计复用 ./core.ts(bash|powershell/read/edit/write/other)。
|
|
6
6
|
* appendEntry 不进 LLM 上下文,只显示在聊天区。
|
|
7
7
|
* 引用块文字色取主题 mdQuote(cc 主题下为 muted 灰),内容用 *斜体* 语法。
|
|
8
8
|
*/
|
|
@@ -16,7 +16,7 @@ export const AGENT_SUMMARY_ENTRY_TYPE = "agent-summary";
|
|
|
16
16
|
|
|
17
17
|
export default function (pi: ExtensionAPI): void {
|
|
18
18
|
pi.registerEntryRenderer(AGENT_SUMMARY_ENTRY_TYPE, (entry, _options, theme) => {
|
|
19
|
-
const line = summaryMarkdown(entry.data as AgentSummaryData,
|
|
19
|
+
const line = summaryMarkdown(entry.data as AgentSummaryData, {
|
|
20
20
|
success: theme.getFgAnsi("success"),
|
|
21
21
|
failed: theme.getFgAnsi("error"),
|
|
22
22
|
});
|
|
@@ -31,9 +31,6 @@ import {
|
|
|
31
31
|
patchRegistry,
|
|
32
32
|
PROTOTYPE_ORIGINAL_KEY,
|
|
33
33
|
} from "../utils/patch-keys.ts";
|
|
34
|
-
// 保持导出兼容:渲染函数已并入 renderer/compact-mode.ts,这里 re-export。
|
|
35
|
-
export { animateCompactThinkingText, formatThoughtDuration, styleCompactThinkingText };
|
|
36
|
-
|
|
37
34
|
// pi-tui 类型声明中 TUI 的 re-export 解析失败,本地用最小结构化类型(只用到 requestRender)。
|
|
38
35
|
type RenderTui = { requestRender(force?: boolean): void };
|
|
39
36
|
|
|
@@ -26,8 +26,6 @@ const MAX_SESSION_SUGGESTIONS = 3;
|
|
|
26
26
|
const MAX_FILE_SUGGESTIONS = 7;
|
|
27
27
|
const MAX_REFERENCED_SESSIONS = 5;
|
|
28
28
|
const MENTION_PATTERN = /(?:^|[\t ])@([^\s@]*)$/;
|
|
29
|
-
const SUBAGENT_MANAGER_KEY = Symbol.for("pi-subagents:manager");
|
|
30
|
-
|
|
31
29
|
// ── In-process subagent record tracker ──────────────────────────────
|
|
32
30
|
// pi-subagents does not expose a global manager; we track records
|
|
33
31
|
// ourselves by listening to the events it emits.
|
|
@@ -53,17 +51,6 @@ type SessionReference = {
|
|
|
53
51
|
messages?: unknown[];
|
|
54
52
|
};
|
|
55
53
|
|
|
56
|
-
type SubagentRecord = {
|
|
57
|
-
id: string;
|
|
58
|
-
description?: string;
|
|
59
|
-
startedAt?: number;
|
|
60
|
-
completedAt?: number;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
type SubagentManager = {
|
|
64
|
-
getRecord(id: string): SubagentRecord | undefined;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
54
|
// Local subagent record tracking (pi-subagents does not expose a global manager).
|
|
68
55
|
const liveSubagentRecords = new Map<string, SubagentLiveRecord>();
|
|
69
56
|
/** Cap retained completed/live records so long-lived pi processes cannot grow unbounded. */
|
|
@@ -120,28 +107,6 @@ function trackSubagentFromEvent(data: unknown): void {
|
|
|
120
107
|
pruneLiveSubagentRecords();
|
|
121
108
|
}
|
|
122
109
|
|
|
123
|
-
function getSubagentManager(): SubagentManager | undefined {
|
|
124
|
-
// Try the global manager first (future-proof), fall back to local records.
|
|
125
|
-
const manager = (globalThis as Record<PropertyKey, unknown>)[SUBAGENT_MANAGER_KEY] as
|
|
126
|
-
| SubagentManager
|
|
127
|
-
| undefined;
|
|
128
|
-
if (manager && typeof manager.getRecord === "function") return manager;
|
|
129
|
-
// If no global manager, use our local tracking.
|
|
130
|
-
if (liveSubagentRecords.size === 0) return undefined;
|
|
131
|
-
return {
|
|
132
|
-
getRecord(id: string): SubagentRecord | undefined {
|
|
133
|
-
const record = liveSubagentRecords.get(id);
|
|
134
|
-
if (!record) return undefined;
|
|
135
|
-
return {
|
|
136
|
-
id: record.runId,
|
|
137
|
-
description: record.agent || undefined,
|
|
138
|
-
startedAt: record.startedAt,
|
|
139
|
-
completedAt: record.completedAt,
|
|
140
|
-
};
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
110
|
function extractMentionQuery(textBeforeCursor: string): string | undefined {
|
|
146
111
|
return textBeforeCursor.match(MENTION_PATTERN)?.[1];
|
|
147
112
|
}
|
|
@@ -281,31 +246,20 @@ function liveSubagentReferences(
|
|
|
281
246
|
agentIds: Set<string>,
|
|
282
247
|
currentSessionId: string,
|
|
283
248
|
): SessionReference[] {
|
|
284
|
-
const manager = getSubagentManager();
|
|
285
|
-
if (!manager) return [];
|
|
286
|
-
|
|
287
249
|
const references: SessionReference[] = [];
|
|
288
250
|
for (const agentId of agentIds) {
|
|
289
|
-
const record =
|
|
290
|
-
if (!record) continue;
|
|
291
|
-
|
|
292
|
-
// Try the global manager's live session first (future-proof).
|
|
293
|
-
const liveRecord = liveSubagentRecords.get(agentId);
|
|
294
|
-
const sessionId = liveRecord?.sessionId ?? record.id;
|
|
295
|
-
if (!sessionId || sessionId === currentSessionId) continue;
|
|
296
|
-
|
|
297
|
-
const name = record.description?.trim() || liveRecord?.agent || undefined;
|
|
298
|
-
const modifiedAt = record.completedAt ?? record.startedAt ?? Date.now();
|
|
251
|
+
const record = liveSubagentRecords.get(agentId);
|
|
252
|
+
if (!record || record.sessionId === currentSessionId) continue;
|
|
299
253
|
references.push({
|
|
300
254
|
kind: "subagent",
|
|
301
|
-
referenceIds: [sessionId, agentId],
|
|
255
|
+
referenceIds: [record.sessionId, agentId],
|
|
302
256
|
info: {
|
|
303
|
-
id: sessionId,
|
|
304
|
-
name,
|
|
305
|
-
cwd:
|
|
257
|
+
id: record.sessionId,
|
|
258
|
+
name: record.agent || undefined,
|
|
259
|
+
cwd: record.cwd,
|
|
306
260
|
firstMessage: "",
|
|
307
261
|
messageCount: 0,
|
|
308
|
-
modified: new Date(
|
|
262
|
+
modified: new Date(record.completedAt ?? record.startedAt),
|
|
309
263
|
},
|
|
310
264
|
});
|
|
311
265
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export const SESSION_REFERENCE_CUSTOM_TYPE = "session-reference";
|
|
2
2
|
export const SESSION_REFERENCE_PREFIX = "@session:";
|
|
3
3
|
|
|
4
|
-
const SESSION_REFERENCE_PATTERN =
|
|
5
|
-
/(?:^|\s)@session:(?:\[([^\]]+)\]|([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?))(?![\w.\]-])/g;
|
|
4
|
+
const SESSION_REFERENCE_PATTERN = /(?:^|\s)@session:\[([^\]]+)](?![\w-])/g;
|
|
6
5
|
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
|
|
7
6
|
|
|
8
7
|
export interface ReferenceSessionInfo {
|
|
@@ -31,13 +30,12 @@ export const DEFAULT_REFERENCE_LIMITS: ReferenceLimits = {
|
|
|
31
30
|
maxTotalBytes: 48_000,
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
/** 提取 @session:[name](组 1)或旧格式 @session:id(组 2),返回 name 或 id。 */
|
|
35
33
|
export function extractSessionReferenceIds(text: string): string[] {
|
|
36
34
|
const ids: string[] = [];
|
|
37
35
|
const seen = new Set<string>();
|
|
38
36
|
|
|
39
37
|
for (const match of text.matchAll(SESSION_REFERENCE_PATTERN)) {
|
|
40
|
-
const id =
|
|
38
|
+
const id = match[1]?.trim();
|
|
41
39
|
if (id && !seen.has(id)) {
|
|
42
40
|
seen.add(id);
|
|
43
41
|
ids.push(id);
|
|
@@ -250,13 +248,3 @@ export function buildReferenceContentFromSections(
|
|
|
250
248
|
].join("\n");
|
|
251
249
|
return truncateUtf8([header, ...sections].join("\n\n"), maxTotalBytes);
|
|
252
250
|
}
|
|
253
|
-
|
|
254
|
-
export function buildReferenceContent(
|
|
255
|
-
sources: ReferenceSource[],
|
|
256
|
-
limits: ReferenceLimits = DEFAULT_REFERENCE_LIMITS,
|
|
257
|
-
): string {
|
|
258
|
-
return buildReferenceContentFromSections(
|
|
259
|
-
sources.map((source) => formatReferenceSession(source, limits)),
|
|
260
|
-
limits.maxTotalBytes,
|
|
261
|
-
);
|
|
262
|
-
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fullscreen 下 ! / !! 在 agent 仍 streaming 时挂到 dock(pendingMessages)。
|
|
3
|
+
* 官方只在下次普通 submit 才 flushPendingBashComponents,命令结束后卡钉在输入框上方。
|
|
4
|
+
* 在 handleBashCommand 收尾补一次 flush。
|
|
5
|
+
*/
|
|
6
|
+
import { InteractiveMode } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { FLUSH_DOCKED_BASH_PATCH, patchRegistry } from "../../utils/patch-keys.ts";
|
|
8
|
+
|
|
9
|
+
type Patch = {
|
|
10
|
+
active: boolean;
|
|
11
|
+
prototype: any;
|
|
12
|
+
original: (...args: any[]) => unknown;
|
|
13
|
+
installed: (...args: any[]) => unknown;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function installFlushDockedBash(): void {
|
|
17
|
+
const prototype = InteractiveMode.prototype as any;
|
|
18
|
+
const previous = patchRegistry.get<Patch>(FLUSH_DOCKED_BASH_PATCH);
|
|
19
|
+
if (previous) previous.active = false;
|
|
20
|
+
const original =
|
|
21
|
+
previous && prototype.handleBashCommand === previous.installed
|
|
22
|
+
? previous.original
|
|
23
|
+
: prototype.handleBashCommand;
|
|
24
|
+
const patch: Patch = {
|
|
25
|
+
active: true,
|
|
26
|
+
prototype,
|
|
27
|
+
original,
|
|
28
|
+
installed: async function (this: any, ...args: any[]) {
|
|
29
|
+
const result = await original.apply(this, args);
|
|
30
|
+
if (patch.active) this.flushPendingBashComponents();
|
|
31
|
+
return result;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
prototype.handleBashCommand = patch.installed;
|
|
35
|
+
patchRegistry.install(FLUSH_DOCKED_BASH_PATCH, patch);
|
|
36
|
+
}
|
|
@@ -3,6 +3,7 @@ import { getKeybindings } from "@earendil-works/pi-tui";
|
|
|
3
3
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { config } from "../../config/config.ts";
|
|
5
5
|
import { ansi16ToRgb, ansi256ToRgb } from "../../utils/ansi-color.ts";
|
|
6
|
+
import { stripAnsi } from "../../utils/ansi-text.ts";
|
|
6
7
|
type Rgb = [number, number, number];
|
|
7
8
|
type StyledPart = {
|
|
8
9
|
raw: string;
|
|
@@ -10,8 +11,6 @@ type StyledPart = {
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
const ANSI_RESET = "\x1b[0m";
|
|
13
|
-
const ANSI_PATTERN =
|
|
14
|
-
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
15
14
|
|
|
16
15
|
// 官方 install.sh 静态 logo(4 行原样,短行补尾随空格统一到 8 列)+ 底部空行补到 5 行,
|
|
17
16
|
// 与右侧 tips 行数等高;着色保持现状(accent 渐变)
|
|
@@ -40,10 +39,6 @@ const PALETTE_SPAN = 0.25;
|
|
|
40
39
|
// 行间相位偏移:小步累加 → 整体左上暗→右下亮的对角渐变
|
|
41
40
|
const LOGO_ROW_PHASE_STEP = 0.08;
|
|
42
41
|
|
|
43
|
-
function stripAnsi(text: string): string {
|
|
44
|
-
return text.replace(ANSI_PATTERN, "");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
42
|
function getVisibleLength(text: string): number {
|
|
48
43
|
return [...stripAnsi(text)].length;
|
|
49
44
|
}
|
package/extensions/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { config } from "./config/config.ts";
|
|
|
3
3
|
|
|
4
4
|
// shell
|
|
5
5
|
import piAliases from "./feature/shell/aliases.ts";
|
|
6
|
+
import { installFlushDockedBash } from "./feature/shell/flush-docked-bash.ts";
|
|
6
7
|
import piStartupHeader from "./feature/shell/startup-header.ts";
|
|
7
8
|
import workingMessage from "./feature/shell/working-message.ts";
|
|
8
9
|
|
|
@@ -20,6 +21,7 @@ import markdownEnhance from "./renderer/markdown-enhance.ts";
|
|
|
20
21
|
export default function (pi: ExtensionAPI): void {
|
|
21
22
|
// shell chrome
|
|
22
23
|
if (config.enableAliases) piAliases(pi);
|
|
24
|
+
installFlushDockedBash();
|
|
23
25
|
piStartupHeader(pi);
|
|
24
26
|
if (config.enableWorkingMessage) workingMessage(pi);
|
|
25
27
|
|