@foolsecret/pi-prompt 0.4.8 → 0.4.9
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/CHANGELOG.md +19 -0
- package/README.md +2 -1
- package/package.json +1 -1
- package/src/config.ts +26 -0
- package/src/draft.ts +11 -0
- package/src/prompt-extension.ts +6 -0
- package/src/ui.ts +61 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@
|
|
|
4
4
|
格式参考 Keep a Changelog;版本号遵循 semver,与 `package.json` 一致。
|
|
5
5
|
变更类型:✨新增 / 🐛修复 / 🏗️重构 / 🧹清理 / 🎨样式 / 📄文档。
|
|
6
6
|
|
|
7
|
+
## v0.4.9 (2026-09-13)
|
|
8
|
+
|
|
9
|
+
**压缩指令可配置:引导摘要保留“工作状态”**
|
|
10
|
+
|
|
11
|
+
- ✨ **新增 `compactInstructions`**(config + env `PI_PROMPT_COMPACT_INSTRUCTIONS`):
|
|
12
|
+
传给 `ctx.compact({ customInstructions })`,引导 pi 的摘要保留
|
|
13
|
+
**当前任务 / 已完成到哪 / 下一步 / 阻塞**,避免压缩后丢失工作状态、
|
|
14
|
+
导致后续回合重新勘测与重复劳动。
|
|
15
|
+
- 📄 **默认内置**(用户可覆盖):
|
|
16
|
+
> 摘要时请额外保留:当前正在进行的任务、已完成到哪一步、下一步计划、未解决的阻塞。
|
|
17
|
+
> 不要只总结历史对话,要保留工作状态。
|
|
18
|
+
|
|
19
|
+
显式空串(配置或 env)= 不追加,回到 pi 原生行为。
|
|
20
|
+
- ✨ **设置抽屉**:“上下文”组新增“压缩指令(保留工作状态)”行,
|
|
21
|
+
submenu 选预设档(内置默认 / 关闭);自定义文案请改配置文件(长文本无自由输入)。
|
|
22
|
+
- 📄 **勘误**:pi-prompt 本身**不生成**压缩提示词(那是 pi 宿主的),
|
|
23
|
+
只提供向 `compact()` 追加的**自定义指令**。三轴注入与此无关,一直正常工作。
|
|
24
|
+
- 🧹 测试 93 → **96 全绿**(含“压缩确实带上 customInstructions”端到端);`tsc` 0 错误。
|
|
25
|
+
|
|
7
26
|
## v0.4.8 (2026-09-13)
|
|
8
27
|
|
|
9
28
|
**设置抽屉改为“草稿 + Ctrl+S” + 轴重命名 + Markdown 只读页 + 删除无参 toggle**
|
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ pi extension add @foolsecret/pi-pricer
|
|
|
92
92
|
"autoCompactMaxTokens": 400000,
|
|
93
93
|
"autoCompactPercent": 0.6,
|
|
94
94
|
"autoCompactMinTurns": 50,
|
|
95
|
+
"compactInstructions": "摘要时请额外保留:当前正在进行的任务、已完成到哪一步、下一步计划、未解决的阻塞。不要只总结历史对话,要保留工作状态。",
|
|
95
96
|
"toolOutput": {
|
|
96
97
|
"enabled": false,
|
|
97
98
|
"grep": 8192,
|
|
@@ -107,7 +108,7 @@ pi extension add @foolsecret/pi-pricer
|
|
|
107
108
|
`PI_PROMPT_HYSTERESIS` / `PI_PROMPT_AUTO_COMPACT` / `PI_PROMPT_AUTO_COMPACT_MAX_TOKENS` /
|
|
108
109
|
`PI_PROMPT_AUTO_COMPACT_PERCENT` / `PI_PROMPT_AUTO_COMPACT_MIN_TURNS` /
|
|
109
110
|
`PI_PROMPT_TOOL_OUTPUT` / `PI_PROMPT_TOOL_CAP_GREP` / `PI_PROMPT_TOOL_CAP_READ` /
|
|
110
|
-
`PI_PROMPT_TOOL_CAP_BASH` / `PI_PROMPT_TOOL_CAP_DEFAULT`。
|
|
111
|
+
`PI_PROMPT_TOOL_CAP_BASH` / `PI_PROMPT_TOOL_CAP_DEFAULT` / `PI_PROMPT_COMPACT_INSTRUCTIONS`。
|
|
111
112
|
|
|
112
113
|
以上配置项**均可在 `/prompt config` 抽屉里修改**(根层 4 分类钻取:
|
|
113
114
|
提示词 / 工具 / 上下文 / 其他),无需手改 JSON。
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -65,6 +65,11 @@ export interface PromptConfigFile {
|
|
|
65
65
|
autoCompactPercent?: number;
|
|
66
66
|
/** 会话至少跑多少轮才允许自动压(缺省 50;短会话压不划算) */
|
|
67
67
|
autoCompactMinTurns?: number;
|
|
68
|
+
/**
|
|
69
|
+
* 追加给 pi 压缩的指令(引导摘要保留“工作状态”)。
|
|
70
|
+
* 空串 = 不追加(回到 pi 原生行为)。
|
|
71
|
+
*/
|
|
72
|
+
compactInstructions?: string;
|
|
68
73
|
/** 工具输出截断(默认关;对象形式开启并分层) */
|
|
69
74
|
toolOutput?: ToolOutputConfig;
|
|
70
75
|
perProvider?: Record<string, string | ProviderAxes>;
|
|
@@ -82,6 +87,14 @@ export interface ToolOutputConfig {
|
|
|
82
87
|
bash?: number;
|
|
83
88
|
}
|
|
84
89
|
|
|
90
|
+
/**
|
|
91
|
+
* 内置压缩指令(用户可用配置/env 覆盖,显式空串=不追加)。
|
|
92
|
+
* 为何需要:pi 的摘要器倾向“总结历史对话”,压缩后会丢失“当前在干什么”,
|
|
93
|
+
* 导致后续回合重新勘测、重复劳动。这条指令引导它保留工作状态。
|
|
94
|
+
*/
|
|
95
|
+
export const DEFAULT_COMPACT_INSTRUCTIONS =
|
|
96
|
+
"摘要时请额外保留:当前正在进行的任务、已完成到哪一步、下一步计划、未解决的阻塞。不要只总结历史对话,要保留工作状态。";
|
|
97
|
+
|
|
85
98
|
/** env 布尔开关解析:值存在且非空/0/false/no 视为真;未设置返回 undefined */
|
|
86
99
|
function envTruthy(env: NodeJS.ProcessEnv | undefined, name: string): boolean | undefined {
|
|
87
100
|
const value = env?.[name];
|
|
@@ -321,6 +334,19 @@ export class PromptConfigManager {
|
|
|
321
334
|
return typeof v === "number" && v >= 0 ? v : 50;
|
|
322
335
|
}
|
|
323
336
|
|
|
337
|
+
/**
|
|
338
|
+
* 追加给 pi 压缩的指令(引导摘要保留“工作状态”)。
|
|
339
|
+
* 优先级:env PI_PROMPT_COMPACT_INSTRUCTIONS > 配置 compactInstructions > 内置默认。
|
|
340
|
+
* 显式空串(env/配置)= 不追加,回到 pi 原生行为。
|
|
341
|
+
*/
|
|
342
|
+
compactInstructions(): string {
|
|
343
|
+
const envRaw = this.env?.PI_PROMPT_COMPACT_INSTRUCTIONS;
|
|
344
|
+
if (envRaw !== undefined) return envRaw.trim();
|
|
345
|
+
const v = this.load().compactInstructions;
|
|
346
|
+
if (typeof v === "string") return v.trim();
|
|
347
|
+
return DEFAULT_COMPACT_INSTRUCTIONS;
|
|
348
|
+
}
|
|
349
|
+
|
|
324
350
|
/**
|
|
325
351
|
* 工具输出截断是否开启(默认 **关**)。
|
|
326
352
|
* 为何默认关:pi 自己已把工具输出截到 50KB(read/grep/bash/find)。
|
package/src/draft.ts
CHANGED
|
@@ -154,6 +154,17 @@ export class PromptDraft {
|
|
|
154
154
|
return true;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* 设置压缩指令(marker:"default" = 回内置默认,"off" = 显式空串不追加)。
|
|
159
|
+
* 存**标记结果**而非文案:用户自定义文案在配置文件里,UI 只切“用哪一档”。
|
|
160
|
+
*/
|
|
161
|
+
setCompactInstructionsMode(marker: string): boolean {
|
|
162
|
+
if (marker !== "default" && marker !== "off") return false;
|
|
163
|
+
this.config = { ...this.config, compactInstructions: marker === "off" ? "" : undefined };
|
|
164
|
+
this.mark("上下文");
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
157
168
|
// ── 提交 / 放弃 ──────────────────────────────────────────────────────
|
|
158
169
|
|
|
159
170
|
/**
|
package/src/prompt-extension.ts
CHANGED
|
@@ -196,6 +196,7 @@ export class PromptExtension {
|
|
|
196
196
|
default: dc.toolOutput?.default ?? 8192,
|
|
197
197
|
},
|
|
198
198
|
toolOutputEnabled: dc.toolOutput?.enabled === true,
|
|
199
|
+
compactInstructions: dc.compactInstructions === undefined ? this.config.compactInstructions() : dc.compactInstructions,
|
|
199
200
|
};
|
|
200
201
|
}
|
|
201
202
|
return {
|
|
@@ -228,6 +229,7 @@ export class PromptExtension {
|
|
|
228
229
|
default: this.config.toolOutputCap("default"),
|
|
229
230
|
},
|
|
230
231
|
toolOutputEnabled: this.config.isToolOutputCap(),
|
|
232
|
+
compactInstructions: this.config.compactInstructions(),
|
|
231
233
|
};
|
|
232
234
|
},
|
|
233
235
|
{
|
|
@@ -237,6 +239,7 @@ export class PromptExtension {
|
|
|
237
239
|
onFlagChange: (name, value) => this.draft?.setFlag(name as Parameters<PromptDraft["setFlag"]>[0], value) ?? false,
|
|
238
240
|
onNumberChange: (name, value) => this.draft?.setNumber(name as Parameters<PromptDraft["setNumber"]>[0], value) ?? false,
|
|
239
241
|
onCapChange: (tool, value) => this.draft?.setToolCap(tool, value) ?? false,
|
|
242
|
+
onCompactInstructionsChange: (marker) => this.draft?.setCompactInstructionsMode(marker) ?? false,
|
|
240
243
|
onSave: () => this.draft?.save() ?? { ok: false, reason: "无草稿" },
|
|
241
244
|
onReset: () => {
|
|
242
245
|
this.draft?.reset(this.currentConfigSnapshot(), this.currentAxesSnapshot());
|
|
@@ -667,7 +670,10 @@ export class PromptExtension {
|
|
|
667
670
|
if (!decision.should) return;
|
|
668
671
|
this.lastCompactTurn = this.turnCount;
|
|
669
672
|
const before = usage.tokens ?? 0;
|
|
673
|
+
// 追加压缩指令(引导摘要保留工作状态);空串则不传,回到 pi 原生行为
|
|
674
|
+
const instructions = this.config.compactInstructions();
|
|
670
675
|
ctx.compact({
|
|
676
|
+
...(instructions ? { customInstructions: instructions } : {}),
|
|
671
677
|
onComplete: () => {
|
|
672
678
|
this.compactCount += 1;
|
|
673
679
|
ctx.ui.notify(
|
package/src/ui.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface PromptConfigDrawerState {
|
|
|
41
41
|
toolCaps: { grep: number; read: number; bash: number; default: number };
|
|
42
42
|
/** 工具输出截断总开关 */
|
|
43
43
|
toolOutputEnabled: boolean;
|
|
44
|
+
/** 压缩追加指令(当前值;空串 = 不追加) */
|
|
45
|
+
compactInstructions: string;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/** 抽屉各行的值域(show 含 auto 元档位,放第一项便于循环命中"自动") */
|
|
@@ -56,6 +58,8 @@ const DEFAULT_PREFIX = "default-";
|
|
|
56
58
|
const FLAG_PREFIX = "flag-";
|
|
57
59
|
const NUM_PREFIX = "num-";
|
|
58
60
|
const CAP_PREFIX = "cap-";
|
|
61
|
+
/** 压缩指令行的 id(值域为预设档的“标记”,非真实文案) */
|
|
62
|
+
const INSTR_PREFIX = "compactInstructions";
|
|
59
63
|
|
|
60
64
|
/** 分类 id(根层的 4 个钻取入口) */
|
|
61
65
|
export const GROUP_IDS = ["prompt", "tool", "context", "other"] as const;
|
|
@@ -172,7 +176,7 @@ const GROUP_DESC: Record<GroupId, string> = {
|
|
|
172
176
|
export const GROUP_MEMBERS: Record<GroupId, readonly string[]> = {
|
|
173
177
|
prompt: ["show", "write", "do", "default-show", "default-write", "default-do"],
|
|
174
178
|
tool: ["flag-peakUpgrade", "num-maxTokensCap", "flag-toolOutput", "cap-grep", "cap-read", "cap-bash", "cap-default"],
|
|
175
|
-
context: ["flag-autoCompact", "num-autoCompactMaxTokens", "num-autoCompactPercent", "num-autoCompactMinTurns", "num-hysteresis"],
|
|
179
|
+
context: ["flag-autoCompact", "num-autoCompactMaxTokens", "num-autoCompactPercent", "num-autoCompactMinTurns", "compactInstructions", "num-hysteresis"],
|
|
176
180
|
other: ["flag-autoSample", "num-autoSampleInterval", "num-autoSampleMaxPerDay", "flag-quietStartup", "flag-hideStatus"],
|
|
177
181
|
};
|
|
178
182
|
|
|
@@ -263,6 +267,46 @@ function capItem(tool: "grep" | "read" | "bash" | "default", state: PromptConfig
|
|
|
263
267
|
};
|
|
264
268
|
}
|
|
265
269
|
|
|
270
|
+
/**
|
|
271
|
+
* 压缩指令行:用 submenu 选预设档(SettingItem 无自由文本输入)。
|
|
272
|
+
* 值域用**标记字符串**("default"/"off"/"custom")而非真实文案,
|
|
273
|
+
* 因为真实文案很长且用户可在配置文件里自定义;这里只负责“用哪一档”。
|
|
274
|
+
*/
|
|
275
|
+
function compactInstructionsItem(state: PromptConfigDrawerState, onPick: (marker: string) => void): SettingItem {
|
|
276
|
+
const current = state.compactInstructions === "" ? "off" : "default";
|
|
277
|
+
const choices: Array<{ marker: string; label: string }> = [
|
|
278
|
+
{ marker: "default", label: "内置默认(保留工作状态)" },
|
|
279
|
+
{ marker: "off", label: "关闭(回到 pi 原生摘要)" },
|
|
280
|
+
];
|
|
281
|
+
return {
|
|
282
|
+
id: INSTR_PREFIX,
|
|
283
|
+
label: "压缩指令(保留工作状态)",
|
|
284
|
+
currentValue: current === "off" ? "off" : "默认",
|
|
285
|
+
description:
|
|
286
|
+
"引导 pi 摘要时保留“当前任务/进度/下一步/阻塞”,避免压缩后丢失工作状态。自定义文案请改配置文件 compactInstructions。",
|
|
287
|
+
submenu: (_v, done) => {
|
|
288
|
+
const sub: SettingItem[] = choices.map((c) => ({
|
|
289
|
+
id: `${INSTR_PREFIX}#${c.marker}`,
|
|
290
|
+
label: c.label,
|
|
291
|
+
currentValue: c.marker === current ? "← 当前" : "",
|
|
292
|
+
}));
|
|
293
|
+
return new SettingsList(
|
|
294
|
+
sub,
|
|
295
|
+
Math.min(sub.length + 4, 12),
|
|
296
|
+
getSettingsListTheme(),
|
|
297
|
+
(id) => {
|
|
298
|
+
const marker = String(id).split("#")[1];
|
|
299
|
+
if (!marker) return;
|
|
300
|
+
onPick(marker);
|
|
301
|
+
done(marker);
|
|
302
|
+
},
|
|
303
|
+
() => done(undefined),
|
|
304
|
+
{ enableSearch: true },
|
|
305
|
+
);
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
266
310
|
/**
|
|
267
311
|
* 根层 4 个分类项(每项 Enter 钻入子层)。
|
|
268
312
|
* currentValue 为分类摘要(如 "show=auto"),便于不钻入也能看到关键状态。
|
|
@@ -291,10 +335,12 @@ export function buildGroupItems(
|
|
|
291
335
|
group: GroupId,
|
|
292
336
|
state: PromptConfigDrawerState,
|
|
293
337
|
onNumPick: (name: string, value: number) => void = () => {},
|
|
338
|
+
onInstrPick: (marker: string) => void = () => {},
|
|
294
339
|
): SettingItem[] {
|
|
295
340
|
return GROUP_MEMBERS[group].map((id) => {
|
|
296
341
|
if (id === "show" || id === "write" || id === "do") return axisItem(id, "", state);
|
|
297
342
|
if (id.startsWith(DEFAULT_PREFIX)) return axisItem(id.slice(DEFAULT_PREFIX.length) as AxisName, DEFAULT_PREFIX, state);
|
|
343
|
+
if (id === INSTR_PREFIX) return compactInstructionsItem(state, onInstrPick);
|
|
298
344
|
if (id.startsWith(FLAG_PREFIX)) return flagItem(id.slice(FLAG_PREFIX.length), state);
|
|
299
345
|
if (id.startsWith(NUM_PREFIX)) return numItem(id.slice(NUM_PREFIX.length), state, onNumPick);
|
|
300
346
|
if (id.startsWith(CAP_PREFIX)) return capItem(id.slice(CAP_PREFIX.length) as "grep" | "read" | "bash" | "default", state);
|
|
@@ -313,9 +359,11 @@ export type SettingTarget =
|
|
|
313
359
|
| { kind: "default-axis"; axis: AxisName }
|
|
314
360
|
| { kind: "flag"; name: string }
|
|
315
361
|
| { kind: "number"; name: string }
|
|
316
|
-
| { kind: "cap"; tool: "grep" | "read" | "bash" | "default" }
|
|
362
|
+
| { kind: "cap"; tool: "grep" | "read" | "bash" | "default" }
|
|
363
|
+
| { kind: "compact-instructions" };
|
|
317
364
|
|
|
318
365
|
export function resolveSettingId(id: string): SettingTarget | null {
|
|
366
|
+
if (id === INSTR_PREFIX) return { kind: "compact-instructions" };
|
|
319
367
|
if (SESSION_IDS.includes(id as AxisName)) return { kind: "session-axis", axis: id as AxisName };
|
|
320
368
|
if (id.startsWith(DEFAULT_PREFIX)) {
|
|
321
369
|
const axis = id.slice(DEFAULT_PREFIX.length) as AxisName;
|
|
@@ -345,6 +393,8 @@ export interface PromptConfigDrawerCallbacks {
|
|
|
345
393
|
onSave: () => { ok: boolean; reason: string };
|
|
346
394
|
/** Ctrl+R:丢弃未保存改动 */
|
|
347
395
|
onReset: () => void;
|
|
396
|
+
/** 压缩指令预设档选择(marker:"default" | "off") */
|
|
397
|
+
onCompactInstructionsChange: (marker: string) => boolean;
|
|
348
398
|
}
|
|
349
399
|
|
|
350
400
|
/** 抽屉主题形参(与 pi-pricer 对齐) */
|
|
@@ -521,7 +571,12 @@ export class PromptConfigDrawer {
|
|
|
521
571
|
);
|
|
522
572
|
}
|
|
523
573
|
const group = this.activeGroup;
|
|
524
|
-
const items = buildGroupItems(
|
|
574
|
+
const items = buildGroupItems(
|
|
575
|
+
group,
|
|
576
|
+
this.readState(),
|
|
577
|
+
(name, value) => this.callbacks.onNumberChange(name, value),
|
|
578
|
+
(marker) => this.callbacks.onCompactInstructionsChange(marker),
|
|
579
|
+
);
|
|
525
580
|
return new SettingsList(
|
|
526
581
|
items,
|
|
527
582
|
Math.min(items.length + 4, 16),
|
|
@@ -570,6 +625,9 @@ export class PromptConfigDrawer {
|
|
|
570
625
|
ok = this.callbacks.onCapChange(target.tool, n);
|
|
571
626
|
break;
|
|
572
627
|
}
|
|
628
|
+
case "compact-instructions":
|
|
629
|
+
ok = this.callbacks.onCompactInstructionsChange(value);
|
|
630
|
+
break;
|
|
573
631
|
}
|
|
574
632
|
if (!ok) return;
|
|
575
633
|
const rebuilt = this.buildLevel(ctx);
|