@zhushanwen/pi-context-engineering 0.1.0

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.
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { createFrozenFreshState } from "../frozen-fresh";
3
+
4
+ describe("FrozenFreshState", () => {
5
+ it("初始状态没有 frozen ID", () => {
6
+ const state = createFrozenFreshState();
7
+ expect(state.isFrozen("c1")).toBe(false);
8
+ expect(state.getReplacement("c1")).toBeUndefined();
9
+ expect(state.getAllFrozenIds().size).toBe(0);
10
+ });
11
+
12
+ it("markFrozen 后 isFrozen 返回 true,getReplacement 返回替换文本", () => {
13
+ const state = createFrozenFreshState();
14
+ state.markFrozen("c1", "[persisted output]");
15
+ expect(state.isFrozen("c1")).toBe(true);
16
+ expect(state.getReplacement("c1")).toBe("[persisted output]");
17
+ });
18
+
19
+ it("reset 清空所有 frozen 状态", () => {
20
+ const state = createFrozenFreshState();
21
+ state.markFrozen("c1", "[persisted]");
22
+ state.markFrozen("c2", "[persisted]");
23
+ expect(state.getAllFrozenIds().size).toBe(2);
24
+
25
+ state.reset();
26
+
27
+ expect(state.isFrozen("c1")).toBe(false);
28
+ expect(state.isFrozen("c2")).toBe(false);
29
+ expect(state.getAllFrozenIds().size).toBe(0);
30
+ });
31
+
32
+ it("getAllFrozenIds 返回所有 frozen ID", () => {
33
+ const state = createFrozenFreshState();
34
+ state.markFrozen("c1", "[a]");
35
+ state.markFrozen("c2", "[b]");
36
+ state.markFrozen("c3", "[c]");
37
+
38
+ const ids = state.getAllFrozenIds();
39
+ expect(ids.has("c1")).toBe(true);
40
+ expect(ids.has("c2")).toBe(true);
41
+ expect(ids.has("c3")).toBe(true);
42
+ expect(ids.size).toBe(3);
43
+ });
44
+ });
@@ -0,0 +1,440 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ compressContext,
4
+ validateToolPairing,
5
+ getToolResultText,
6
+ type AgentMessage,
7
+ type ToolCall,
8
+ type AssistantMessage,
9
+ type ToolResultMessage,
10
+ type BashExecutionMessage,
11
+ type UserMessage,
12
+ type TextContent,
13
+ type ThinkingContent,
14
+ type ContextUsage,
15
+ } from "../compressor";
16
+ import { createRecallStore } from "../recall-store";
17
+ import { DEFAULT_CONFIG, type ContextEngineeringConfig } from "../config";
18
+ import { createFrozenFreshState } from "../frozen-fresh";
19
+ import { handleContextEngineeringCommand, handleContextStatsCommand } from "../commands";
20
+
21
+ // ── Helpers ──
22
+
23
+ const MINUTE = 60 * 1000;
24
+
25
+ function makeToolResult(text: string, ageMs: number, toolCallId: string): ToolResultMessage {
26
+ return {
27
+ role: "toolResult",
28
+ toolCallId,
29
+ toolName: "read",
30
+ content: [{ type: "text" as const, text }],
31
+ isError: false,
32
+ timestamp: Date.now() - ageMs,
33
+ };
34
+ }
35
+
36
+ function makeAssistant(
37
+ toolCalls: ToolCall[],
38
+ thinking?: string,
39
+ ageMs: number = 0,
40
+ ): AssistantMessage {
41
+ const content: (TextContent | ThinkingContent | ToolCall)[] = [];
42
+ if (thinking) content.push({ type: "thinking" as const, thinking });
43
+ content.push(...toolCalls);
44
+ if (toolCalls.length === 0 && !thinking) content.push({ type: "text" as const, text: "ok" });
45
+ return {
46
+ role: "assistant", content, api: "anthropic-messages", provider: "anthropic",
47
+ model: "test", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0,
48
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
49
+ stopReason: "stop", timestamp: Date.now() - ageMs,
50
+ };
51
+ }
52
+
53
+ function makeUser(text: string, ageMs: number = 0): UserMessage {
54
+ return { role: "user", content: text, timestamp: Date.now() - ageMs };
55
+ }
56
+
57
+ function makeBashExecution(output: string, ageMs: number = 0): BashExecutionMessage {
58
+ return {
59
+ role: "bashExecution", command: "cat file.txt", output, exitCode: 0,
60
+ cancelled: false, truncated: false, timestamp: Date.now() - ageMs,
61
+ };
62
+ }
63
+
64
+ function tc(id: string): ToolCall {
65
+ return { type: "toolCall" as const, id, name: "read", arguments: { path: "f.ts" } as Record<string, unknown> };
66
+ }
67
+
68
+ function extractCtxId(text: string): string | null {
69
+ const m = text.match(/ctx-[a-f0-9]{12}/);
70
+ return m ? m[0] : null;
71
+ }
72
+
73
+ // ── Integration Tests ──
74
+
75
+ describe("Integration: TC-1 Tool Result 过期清理", () => {
76
+ it("TC-1-01: 超过30分钟的toolResult被替换", () => {
77
+ const store = createRecallStore();
78
+ const config = { ...DEFAULT_CONFIG, l0: { ...DEFAULT_CONFIG.l0, keepRecent: 0 } };
79
+ // 3 turns: Turn 1 toolResult(35min) outside protectRecentTurns=2
80
+ const messages: AgentMessage[] = [
81
+ makeUser("task0", 45 * MINUTE),
82
+ makeAssistant([tc("c0")], undefined, 45 * MINUTE),
83
+ makeToolResult("old-result", 44 * MINUTE, "c0"),
84
+ makeUser("task1", 40 * MINUTE),
85
+ makeAssistant([tc("c1")], undefined, 40 * MINUTE),
86
+ makeToolResult("original content A", 35 * MINUTE, "c1"),
87
+ makeUser("task2", 1 * MINUTE),
88
+ ];
89
+
90
+ const result = compressContext(messages, config, store, undefined, createFrozenFreshState());
91
+ // c0's toolResult at index 2 is in Turn 0 (before user@40min), outside protected 2 turns
92
+ // c1's toolResult at index 5 is in Turn 1, inside protected 2 turns
93
+ const expired = result.messages[2] as ToolResultMessage;
94
+ // c0's toolResult at index 2 is expired (outside protected 2 turns)
95
+ const text = getToolResultText(expired);
96
+
97
+ expect(text).toContain("[Tool result expired");
98
+ const id = extractCtxId(text);
99
+ expect(id).not.toBeNull();
100
+
101
+ // Recall store 中有原始内容
102
+ const stored = store.recall(id!);
103
+ expect(stored).toBeDefined();
104
+ expect(stored!.original).toContain("old-result");
105
+ expect(stored!.level).toBe("l0-expired");
106
+ });
107
+
108
+ it("TC-1-02: 最近N轮内的toolResult不被过期", () => {
109
+ const store = createRecallStore();
110
+ const messages: AgentMessage[] = [
111
+ makeUser("old", 40 * MINUTE),
112
+ makeAssistant([tc("c1")], undefined, 35 * MINUTE),
113
+ makeToolResult("should-be-protected", 35 * MINUTE, "c1"),
114
+ makeUser("recent", 1 * MINUTE),
115
+ ];
116
+
117
+ const result = compressContext(messages, DEFAULT_CONFIG, store, undefined, createFrozenFreshState());
118
+ const kept = result.messages[2] as ToolResultMessage;
119
+ // protectRecentTurns=2 → turn boundary at user(1min) → toolResult(35min) is in protected turn
120
+ expect(getToolResultText(kept)).toBe("should-be-protected");
121
+ expect(result.stats.l0Expired).toBe(0);
122
+ });
123
+ });
124
+
125
+ describe("Integration: TC-2 Bash 输出截断", () => {
126
+ it("TC-2-01: 超过阈值的bash输出被截断", () => {
127
+ const store = createRecallStore();
128
+ const longOutput = "A".repeat(10000);
129
+ const messages: AgentMessage[] = [makeBashExecution(longOutput)];
130
+
131
+ const result = compressContext(messages, DEFAULT_CONFIG, store, undefined, createFrozenFreshState());
132
+ expect(result.stats.l0Truncated).toBe(1);
133
+
134
+ const bash = result.messages[0] as BashExecutionMessage;
135
+ expect(bash.output.length).toBeLessThan(longOutput.length);
136
+ expect(bash.output).toContain("[truncated");
137
+
138
+ // Recall store 中有完整原始输出
139
+ const id = extractCtxId(bash.output);
140
+ expect(id).not.toBeNull();
141
+ const stored = store.recall(id!);
142
+ expect(stored).toBeDefined();
143
+ expect(stored!.original).toBe(longOutput);
144
+ });
145
+
146
+ it("TC-2-02: 低于阈值的bash输出不被截断", () => {
147
+ const store = createRecallStore();
148
+ const shortOutput = "B".repeat(3000);
149
+ const messages: AgentMessage[] = [makeBashExecution(shortOutput)];
150
+
151
+ const result = compressContext(messages, DEFAULT_CONFIG, store, undefined, createFrozenFreshState());
152
+ expect(result.stats.l0Truncated).toBe(0);
153
+
154
+ const bash = result.messages[0] as BashExecutionMessage;
155
+ expect(bash.output).toBe(shortOutput);
156
+ });
157
+ });
158
+
159
+ describe("Integration: TC-3 Thinking 清理", () => {
160
+ it("TC-3-01: 超过空闲时间的thinking被清空", () => {
161
+ const store = createRecallStore();
162
+ const messages: AgentMessage[] = [
163
+ makeAssistant([], "deep analysis content here", 6 * MINUTE),
164
+ ];
165
+
166
+ const result = compressContext(messages, DEFAULT_CONFIG, store, undefined, createFrozenFreshState());
167
+ expect(result.stats.l0ThinkingCleared).toBe(1);
168
+
169
+ const asst = result.messages[0] as AssistantMessage;
170
+ const thinking = asst.content.find((c) => c.type === "thinking") as ThinkingContent | undefined;
171
+ expect(thinking).toBeDefined();
172
+ expect(thinking!.thinking).toBe("[thinking expired]");
173
+ });
174
+ });
175
+
176
+ describe("Integration: TC-4 配对校验", () => {
177
+ it("TC-4-01: 正常序列压缩后配对完整", () => {
178
+ const store = createRecallStore();
179
+ const messages: AgentMessage[] = [
180
+ makeUser("do it"),
181
+ makeAssistant([tc("c1")]),
182
+ makeToolResult("result content", 0, "c1"),
183
+ makeUser("next"),
184
+ ];
185
+
186
+ const result = compressContext(messages, DEFAULT_CONFIG, store, undefined, createFrozenFreshState());
187
+ expect(result.stats.validationFailed).toBe(false);
188
+ // toolResult 紧随 toolCall
189
+ expect(result.messages[1].role).toBe("assistant");
190
+ expect(result.messages[2].role).toBe("toolResult");
191
+ });
192
+
193
+ it("TC-4-02: 校验失败时返回原始消息", () => {
194
+ // 通过构造让 validateToolPairing 失败的场景很难在正常 pipeline 中触发
195
+ // 直接测试 validateToolPairing 函数
196
+ const orphaned: AgentMessage[] = [
197
+ makeToolResult("orphan result", 0, "c-missing"),
198
+ ];
199
+ expect(validateToolPairing(orphaned)).toBe(false);
200
+
201
+ // compressContext 对孤儿消息应安全降级
202
+ const store = createRecallStore();
203
+ const result = compressContext(orphaned, DEFAULT_CONFIG, store, undefined, createFrozenFreshState());
204
+ expect(result.stats.validationFailed).toBe(true);
205
+ // 返回原始消息
206
+ expect(result.messages).toBe(orphaned);
207
+ });
208
+ });
209
+
210
+ describe("Integration: TC-5 Recall 完整性", () => {
211
+ it("TC-5-01: 存在的ID返回完整原始内容", () => {
212
+ const store = createRecallStore();
213
+ const id = store.store("original full content here", "l0-expired");
214
+ const stored = store.recall(id);
215
+ expect(stored).toBeDefined();
216
+ expect(stored!.original).toBe("original full content here");
217
+ expect(stored!.level).toBe("l0-expired");
218
+ expect(typeof stored!.compressedAt).toBe("number");
219
+ });
220
+
221
+ it("TC-5-02: 不存在的ID返回undefined", () => {
222
+ const store = createRecallStore();
223
+ expect(store.recall("ctx-nonexist")).toBeUndefined();
224
+ });
225
+ });
226
+
227
+ describe("Integration: TC-7 L1 规则化摘要", () => {
228
+ it("TC-7-01: TypeScript代码保留关键行", () => {
229
+ const store = createRecallStore();
230
+ const config = { ...DEFAULT_CONFIG, l1: { ...DEFAULT_CONFIG.l1, protectRecentTurns: 0 } };
231
+ const headLines = Array.from({ length: 10 }, (_, i) => `// head line ${i}`);
232
+ const imports = ["import { read } from 'fs';", "import { join } from 'path';"];
233
+ const defs = ["function process(d: string): void {", "export class Handler {"];
234
+ const filler = Array.from({ length: 600 }, () => " return x + y + z + someVeryLongVariableNamePadding();");
235
+ const tailLines = Array.from({ length: 5 }, (_, i) => `// tail ${i}`);
236
+ const longContent = [...headLines, ...imports, ...filler.slice(0, 150), ...defs, ...filler.slice(150), ...tailLines].join("\n");
237
+ expect(longContent.length).toBeGreaterThan(8000);
238
+
239
+ const messages: AgentMessage[] = [
240
+ makeUser("read"),
241
+ makeAssistant([tc("c1")]),
242
+ makeToolResult(longContent, 0, "c1"),
243
+ ];
244
+
245
+ const result = compressContext(messages, config, store, undefined, createFrozenFreshState());
246
+ expect(result.stats.l1Condensed).toBe(1);
247
+
248
+ const condensed = result.messages[2] as ToolResultMessage;
249
+ const text = getToolResultText(condensed);
250
+
251
+ expect(text).toContain("[Condensed (ID: ctx-");
252
+ expect(text).toContain("import { read }");
253
+ expect(text).toContain("function process");
254
+ expect(text).toContain("export class Handler");
255
+ expect(text.length).toBeLessThan(longContent.length);
256
+
257
+ // Recall store 有原始内容
258
+ const id = extractCtxId(text);
259
+ expect(store.recall(id!)).toBeDefined();
260
+ });
261
+
262
+ it("TC-7-02: 非代码内容fallback到截断", () => {
263
+ const store = createRecallStore();
264
+ const config = { ...DEFAULT_CONFIG, l1: { ...DEFAULT_CONFIG.l1, protectRecentTurns: 0 } };
265
+ // 纯 JSON 内容,无 import/function/class 行
266
+ const jsonContent = JSON.stringify(Array.from({ length: 2000 }, (_, i) => ({ id: i, value: `item-${i}` })));
267
+ expect(jsonContent.length).toBeGreaterThan(8000);
268
+
269
+ const messages: AgentMessage[] = [
270
+ makeUser("read json"),
271
+ makeAssistant([tc("c1")]),
272
+ makeToolResult(jsonContent, 0, "c1"),
273
+ ];
274
+
275
+ const result = compressContext(messages, config, store, undefined, createFrozenFreshState());
276
+ expect(result.stats.l1Condensed).toBe(1);
277
+
278
+ const condensed = result.messages[2] as ToolResultMessage;
279
+ const text = getToolResultText(condensed);
280
+ expect(text).toContain("[Condensed (ID: ctx-");
281
+ // 非代码内容走 fallback 截断,不会包含 import 行
282
+ expect(text).not.toContain("import");
283
+ });
284
+ });
285
+
286
+ describe("Integration: TC-8 L2 紧急压缩", () => {
287
+ it("TC-8-01: 91%使用率触发紧急压缩", () => {
288
+ const store = createRecallStore();
289
+ const contextUsage: ContextUsage = { tokens: null, contextWindow: 200000, percent: 0.91 };
290
+
291
+ // 4 个 turn,L2 protectRecentTurns=3,Turn 1 不在保护范围
292
+ const messages: AgentMessage[] = [
293
+ makeUser("t1", 20 * MINUTE),
294
+ makeAssistant([tc("c1")], undefined, 20 * MINUTE),
295
+ makeToolResult("old-content", 20 * MINUTE, "c1"),
296
+ makeUser("t2", 10 * MINUTE),
297
+ makeAssistant([tc("c2")], undefined, 10 * MINUTE),
298
+ makeToolResult("mid-content", 10 * MINUTE, "c2"),
299
+ makeUser("t3", 5 * MINUTE),
300
+ makeAssistant([tc("c3")], undefined, 5 * MINUTE),
301
+ makeToolResult("recent-content", 5 * MINUTE, "c3"),
302
+ makeUser("t4", 1 * MINUTE),
303
+ ];
304
+
305
+ const result = compressContext(messages, DEFAULT_CONFIG, store, contextUsage, createFrozenFreshState());
306
+ expect(result.stats.l2Triggered).toBe(true);
307
+
308
+ // Turn 1 toolResult 被强制过期
309
+ const forced = result.messages[2] as ToolResultMessage;
310
+ expect(getToolResultText(forced)).toContain("[Tool result expired");
311
+
312
+ // Turn 2 toolResult 保留
313
+ const kept = result.messages[5] as ToolResultMessage;
314
+ expect(getToolResultText(kept)).toBe("mid-content");
315
+ });
316
+
317
+ it("TC-8-02: 85%使用率不触发L2", () => {
318
+ const store = createRecallStore();
319
+ const contextUsage: ContextUsage = { tokens: null, contextWindow: 200000, percent: 0.85 };
320
+
321
+ const messages: AgentMessage[] = [
322
+ makeUser("t1", 20 * MINUTE),
323
+ makeAssistant([tc("c1")], undefined, 20 * MINUTE),
324
+ makeToolResult("content", 20 * MINUTE, "c1"),
325
+ ];
326
+
327
+ const result = compressContext(messages, DEFAULT_CONFIG, store, contextUsage, createFrozenFreshState());
328
+ expect(result.stats.l2Triggered).toBe(false);
329
+ });
330
+ });
331
+
332
+ describe("Integration: TC-9 统计命令", () => {
333
+ it("TC-9-01: /context-stats输出正确统计", () => {
334
+ const stats = {
335
+ l0Expired: 3,
336
+ l0Truncated: 2,
337
+ l0ThinkingCleared: 1,
338
+ l1Condensed: 1,
339
+ l2Triggered: false,
340
+ validationFailed: false,
341
+ };
342
+
343
+ const output = handleContextStatsCommand(stats);
344
+ expect(output).toContain("3"); // expired
345
+ expect(output).toContain("2"); // truncated
346
+ expect(output).toContain("1"); // thinking
347
+ expect(output).toContain("1"); // condensed
348
+ });
349
+ });
350
+
351
+ describe("Integration: TC-10 配置启停", () => {
352
+ it("TC-10-01: 全局启停", () => {
353
+ const config = { ...DEFAULT_CONFIG, enabled: true };
354
+ const store = createRecallStore();
355
+
356
+ // 执行 /context-engineering global off
357
+ const _offResult = handleContextEngineeringCommand("global off", config, {
358
+ l0Expired: 0, l0Truncated: 0, l0ThinkingCleared: 0,
359
+ l1Condensed: 0, l2Triggered: false, validationFailed: false,
360
+ });
361
+ expect(config.enabled).toBe(false);
362
+
363
+ // 禁用时不压缩
364
+ const messages: AgentMessage[] = [
365
+ makeUser("hi"),
366
+ makeAssistant([tc("c1")]),
367
+ makeToolResult("big content", 60 * MINUTE, "c1"),
368
+ ];
369
+ const result = compressContext(messages, config, store, undefined, createFrozenFreshState());
370
+ expect(result.messages).toBe(messages);
371
+
372
+ // 执行 /context-engineering global on
373
+ const _onResult = handleContextEngineeringCommand("global on", config, {
374
+ l0Expired: 0, l0Truncated: 0, l0ThinkingCleared: 0,
375
+ l1Condensed: 0, l2Triggered: false, validationFailed: false,
376
+ });
377
+ expect(config.enabled).toBe(true);
378
+ });
379
+
380
+ it("TC-10-02: 独立级别启停", () => {
381
+ const config: ContextEngineeringConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
382
+
383
+ // /context-engineering l1 off
384
+ const result = handleContextEngineeringCommand("l1 off", config, {
385
+ l0Expired: 0, l0Truncated: 0, l0ThinkingCleared: 0,
386
+ l1Condensed: 0, l2Triggered: false, validationFailed: false,
387
+ });
388
+ expect(result).toContain("L1");
389
+ expect(config.l1.enabled).toBe(false);
390
+ // L0 和 L2 不受影响
391
+ expect(config.l0.enabled).toBe(true);
392
+ expect(config.l2.enabled).toBe(true);
393
+ });
394
+ });
395
+
396
+ // ── TC-11 MC/Budget Commands (AC-8) ──
397
+
398
+ describe("Integration: TC-11 MC/Budget Commands (AC-8)", () => {
399
+ it("mc off → config.mc.enabled 为 false", () => {
400
+ const config: ContextEngineeringConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
401
+
402
+ const _result = handleContextEngineeringCommand("mc off", config, {
403
+ l0Expired: 0, l0Truncated: 0, l0ThinkingCleared: 0,
404
+ l1Condensed: 0, l2Triggered: false, validationFailed: false,
405
+ mcTriggered: false, mcCleared: 0, budgetPersisted: 0,
406
+ });
407
+ expect(config.mc.enabled).toBe(false);
408
+ });
409
+
410
+ it("budget off → config.budget.enabled 为 false", () => {
411
+ const config: ContextEngineeringConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
412
+
413
+ const _result = handleContextEngineeringCommand("budget off", config, {
414
+ l0Expired: 0, l0Truncated: 0, l0ThinkingCleared: 0,
415
+ l1Condensed: 0, l2Triggered: false, validationFailed: false,
416
+ mcTriggered: false, mcCleared: 0, budgetPersisted: 0,
417
+ });
418
+ expect(config.budget.enabled).toBe(false);
419
+ });
420
+
421
+ it("禁用后 compressContext 中对应层不触发", () => {
422
+ const store = createRecallStore();
423
+ const config: ContextEngineeringConfig = {
424
+ ...DEFAULT_CONFIG,
425
+ mc: { ...DEFAULT_CONFIG.mc, enabled: false },
426
+ budget: { ...DEFAULT_CONFIG.budget, enabled: false },
427
+ };
428
+
429
+ const messages: AgentMessage[] = [
430
+ makeUser("task"),
431
+ makeAssistant([tc("c1")]),
432
+ makeToolResult("content", 0, "c1"),
433
+ ];
434
+
435
+ const result = compressContext(messages, config, store, undefined, createFrozenFreshState());
436
+ expect(result.stats.mcTriggered).toBe(false);
437
+ expect(result.stats.mcCleared).toBe(0);
438
+ expect(result.stats.budgetPersisted).toBe(0);
439
+ });
440
+ });
@@ -0,0 +1,154 @@
1
+ import type { ContextEngineeringConfig } from "./config";
2
+ import { parseLevelArgs } from "./config";
3
+ import type { CompressionStats } from "./compressor";
4
+
5
+ // ── 格式化辅助 ──
6
+
7
+ function formatConfigSummary(config: ContextEngineeringConfig): string {
8
+ const lines = [
9
+ "Context Engineering Plugin",
10
+ "═══════════════════════════",
11
+ `Status: ${config.enabled ? "enabled" : "disabled"}`,
12
+ "",
13
+ ];
14
+
15
+ if (config.mc.enabled) {
16
+ lines.push("MC (Microcompact):");
17
+ lines.push(" Enabled: true");
18
+ lines.push(
19
+ ` Gap: ${config.mc.gapThresholdMinutes}min | Keep recent: ${config.mc.keepRecent}`,
20
+ );
21
+ } else {
22
+ lines.push("MC (Microcompact): disabled");
23
+ }
24
+
25
+ lines.push("");
26
+
27
+ if (config.budget.enabled) {
28
+ lines.push("Budget (Tool result budget):");
29
+ lines.push(" Enabled: true");
30
+ lines.push(
31
+ ` Max: ${config.budget.maxToolResultCharsPerMessage} chars | Preview: ${config.budget.previewSize} chars`,
32
+ );
33
+ } else {
34
+ lines.push("Budget (Tool result budget): disabled");
35
+ }
36
+
37
+ lines.push("");
38
+
39
+ if (config.l0.enabled) {
40
+ lines.push("L0 (Zero-cost cleanup):");
41
+ lines.push(" Enabled: true");
42
+ lines.push(
43
+ ` Expire: ${config.l0.expireMinutes}min | Bash truncate: ${config.l0.bashTruncateChars} chars | Thinking: ${config.l0.thinkingExpireMinutes}min`,
44
+ );
45
+ lines.push(` Protect recent: ${config.l0.protectRecentTurns} turns | Keep recent: ${config.l0.keepRecent}`);
46
+ } else {
47
+ lines.push("L0 (Zero-cost cleanup): disabled");
48
+ }
49
+
50
+ lines.push("");
51
+
52
+ if (config.l1.enabled) {
53
+ lines.push("L1 (Rule-based compression):");
54
+ lines.push(" Enabled: true");
55
+ lines.push(
56
+ ` Threshold: ${config.l1.summaryThresholdChars} chars | Head: ${config.l1.keepHeadLines} lines | Tail: ${config.l1.keepTailLines} lines | Protect: ${config.l1.protectRecentTurns} turns`,
57
+ );
58
+ } else {
59
+ lines.push("L1 (Rule-based compression): disabled");
60
+ }
61
+
62
+ lines.push("");
63
+
64
+ if (config.l2.enabled) {
65
+ lines.push("L2 (Emergency compression):");
66
+ lines.push(" Enabled: true");
67
+ const thresholdPercent = Math.round(config.l2.emergencyThreshold * 100);
68
+ lines.push(
69
+ ` Threshold: ${thresholdPercent}% | Protect recent: ${config.l2.protectRecentTurns} turns`,
70
+ );
71
+ } else {
72
+ lines.push("L2 (Emergency compression): disabled");
73
+ }
74
+
75
+ return lines.join("\n");
76
+ }
77
+
78
+ function formatStats(stats: CompressionStats): string {
79
+ const lines = [
80
+ "Statistics:",
81
+ ` MC: triggered=${stats.mcTriggered}, cleared=${stats.mcCleared}`,
82
+ ` Budget: persisted=${stats.budgetPersisted}`,
83
+ ` L0 expired: ${stats.l0Expired} | truncated: ${stats.l0Truncated} | thinking cleared: ${stats.l0ThinkingCleared}`,
84
+ ` L1 condensed: ${stats.l1Condensed}`,
85
+ ` L2 triggered: ${stats.l2Triggered}`,
86
+ ];
87
+ return lines.join("\n");
88
+ }
89
+
90
+ const USAGE_HELP = [
91
+ "Usage:",
92
+ " /context-engineering — Show current config and stats",
93
+ " /context-engineering mc on — Enable Microcompact",
94
+ " /context-engineering mc off — Disable Microcompact",
95
+ " /context-engineering budget on — Enable Budget",
96
+ " /context-engineering budget off — Disable Budget",
97
+ " /context-engineering l0 on — Enable L0 compression",
98
+ " /context-engineering l0 off — Disable L0 compression",
99
+ " /context-engineering l1 on — Enable L1 compression",
100
+ " /context-engineering l1 off — Disable L1 compression",
101
+ " /context-engineering l2 on — Enable L2 compression",
102
+ " /context-engineering l2 off — Disable L2 compression",
103
+ " /context-engineering global on — Enable entire plugin",
104
+ " /context-engineering global off — Disable entire plugin",
105
+ ].join("\n");
106
+
107
+ // ── 导出 ──
108
+
109
+ export function handleContextEngineeringCommand(
110
+ args: string | undefined,
111
+ config: ContextEngineeringConfig,
112
+ stats: CompressionStats,
113
+ ): string {
114
+ if (!args || args.trim().length === 0) {
115
+ return formatConfigSummary(config) + "\n\n" + formatStats(stats);
116
+ }
117
+
118
+ const parsed = parseLevelArgs(args);
119
+ if (!parsed) {
120
+ return USAGE_HELP;
121
+ }
122
+
123
+ const { target, action } = parsed;
124
+ const onOff = action === "on";
125
+
126
+ switch (target) {
127
+ case "global":
128
+ config.enabled = onOff;
129
+ return `Context engineering ${action === "on" ? "enabled" : "disabled"}.`;
130
+ case "mc":
131
+ config.mc.enabled = onOff;
132
+ return `MC (Microcompact) ${action === "on" ? "enabled" : "disabled"}.`;
133
+ case "budget":
134
+ config.budget.enabled = onOff;
135
+ return `Budget (Tool result budget) ${action === "on" ? "enabled" : "disabled"}.`;
136
+ case "l0":
137
+ config.l0.enabled = onOff;
138
+ return `L0 (Zero-cost cleanup) ${action === "on" ? "enabled" : "disabled"}.`;
139
+ case "l1":
140
+ config.l1.enabled = onOff;
141
+ return `L1 (Rule-based compression) ${action === "on" ? "enabled" : "disabled"}.`;
142
+ case "l2":
143
+ config.l2.enabled = onOff;
144
+ return `L2 (Emergency compression) ${action === "on" ? "enabled" : "disabled"}.`;
145
+ }
146
+ }
147
+
148
+ export function handleContextStatsCommand(stats: CompressionStats): string {
149
+ return [
150
+ "Context Compression Statistics",
151
+ "══════════════════════════════",
152
+ formatStats(stats),
153
+ ].join("\n");
154
+ }