Skip to content

Commit b8eda0a

Browse files
compress: restructure schema with topic primary, content nested (startString/endString)
1 parent 3d8ff53 commit b8eda0a

File tree

4 files changed

+66
-88
lines changed

4 files changed

+66
-88
lines changed

lib/prompts/compress.md

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,35 @@
1-
Collapses a contiguous range of conversation into a single summary.
1+
Use this tool to collapse a contiguous range of conversation into a preserved summary.
22

3-
## When to Use This Tool
3+
THE PHILOSOPHY OF COMPRESS
4+
`compress` transforms verbose conversation sequences into dense, high-fidelity summaries. This is not cleanup - it is crystallization. Your summary becomes the authoritative record of what transpired.
45

5-
Use `compress` when you want to condense an entire sequence of work into a brief summary:
6+
Think of compression as phase transitions: raw exploration becomes refined understanding. The original context served its purpose; your summary now carries that understanding forward.
67

7-
- **Phase Completion:** You completed a phase (research, tool calls, implementation) and want to collapse the entire sequence into a summary.
8-
- **Exploration Done:** You explored multiple files or ran multiple commands and only need a summary of what you learned.
9-
- **Failed Attempts:** You tried several unsuccessful approaches and want to condense them into a brief note.
10-
- **Verbose Output:** A section of conversation has grown large but can be summarized without losing critical details.
8+
THE SUMMARY
9+
Your summary must be COMPLETE. Capture file paths, function signatures, decisions made, constraints discovered, key findings... EVERYTHING that maintains context integrity. This is not a brief note - it is a technical substitute so faithful that the original conversation adds no value.
1110

12-
## When NOT to Use This Tool
11+
Yet be LEAN. Strip away the noise: failed attempts that led nowhere, verbose tool outputs, back-and-forth exploration. What remains should be pure signal - golden nuggets of detail that preserve full understanding with zero ambiguity.
1312

14-
- **If you need specific details:** If you'll need exact code, file contents, or error messages from the range, keep them.
15-
- **For individual tool outputs:** Use `prune` or `distill` for single tool outputs. Compress targets conversation ranges.
16-
- **If it's recent content:** You may still need recent work for the current phase.
13+
WHEN TO COMPRESS
14+
Compress when a phase of work is truly complete and the raw conversation is no longer needed:
1715

18-
## How It Works
16+
Research concluded and findings are clear
17+
Implementation finished and verified
18+
Exploration exhausted and patterns understood
1919

20-
1. `startString` — A unique text string that marks the start of the range to compress
21-
2. `endString` — A unique text string that marks the end of the range to compress
22-
3. `topic` — A short label (3-5 words) describing the compressed content
23-
4. `summary` — The replacement text that will be inserted
20+
Do NOT compress when:
21+
You may need exact code, error messages, or file contents from the range
22+
Work in that area is still active or may resume
23+
You're mid-sprint on related functionality
2424

25-
Everything between startString and endString (inclusive) is removed and replaced with your summary.
25+
Before compressing, ask: _"Am I certain this phase is complete?"_ Compression is irreversible. The summary replaces everything in the range.
2626

27-
**Important:** The compress will FAIL if `startString` or `endString` is not found in the conversation. The compress will also FAIL if either string is found multiple times. Provide a larger string with more surrounding context to uniquely identify the intended match.
27+
BOUNDARY MATCHING
28+
You specify boundaries by matching unique text strings in the conversation. CRITICAL: In code-centric conversations, strings repeat often. Provide sufficiently unique text to match exactly once. If a match fails (not found or found multiple times), the tool will error - extend your boundary string with more surrounding context in order to make SURE the tool does NOT error.
2829

29-
## Best Practices
30-
31-
- **Choose unique strings:** Pick text that appears only once in the conversation.
32-
- **Write concise topics:** Examples: "Auth System Exploration", "Token Logic Refactor"
33-
- **Write comprehensive summaries:** Include key information like file names, function signatures, and important findings.
34-
- **Timing:** Best used after finishing a work phase, not during active exploration.
35-
36-
## Format
37-
38-
- `input`: Array with four elements: [startString, endString, topic, summary]
39-
40-
## Example
41-
42-
<example_compress>
43-
Conversation: [Asked about auth] -> [Read 5 files] -> [Analyzed patterns] -> [Found "JWT tokens with 24h expiry"]
44-
45-
[Uses compress with:
46-
input: [
47-
"Asked about authentication",
48-
"JWT tokens with 24h expiry",
49-
"Auth System Exploration",
50-
"Auth: JWT 24h expiry, bcrypt passwords, refresh rotation. Files: auth.ts, tokens.ts, middleware/auth.ts"
51-
]
52-
]
53-
</example_compress>
54-
55-
<example_keep>
56-
Assistant: [Just finished reading auth.ts]
57-
I've read the auth file and now need to make edits based on it. I'm keeping this in context rather than compressing.
58-
</example_keep>
30+
THE FORMAT OF COMPRESS
31+
`topic`: Short label (3-5 words) for display - e.g., "Auth System Exploration"
32+
`content`: Object containing:
33+
`startString`: Unique text string marking the beginning of the range
34+
`endString`: Unique text string marking the end of the range
35+
`summary`: Complete technical summary replacing all content in the range

lib/prompts/distill.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ UNCERTAINTY REMAINS: If you might need to re-examine the original, defer. Distil
2323
Before distilling, ask yourself: _"Will I need the raw output for upcoming work?"_ If you plan to edit a file you just read, keep it intact. Distillation is for completed exploration, not active work.
2424

2525
THE FORMAT OF DISTILL
26-
`items`: Array of objects, each containing:
26+
`targets`: Array of objects, each containing:
2727
`id`: Numeric ID (as string) from the `<prunable-tools>` list
2828
`distillation`: Complete technical substitute for that tool output

lib/tools/compress.ts

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,22 @@ export function createCompressTool(ctx: PruneToolContext): ReturnType<typeof too
1919
return tool({
2020
description: COMPRESS_TOOL_DESCRIPTION,
2121
args: {
22-
input: tool.schema
23-
.array(tool.schema.string())
24-
.describe(
25-
"[startString, endString, topic, summary] - 4 required strings: (1) startString: unique text from conversation marking range start, (2) endString: unique text marking range end, (3) topic: short 3-5 word label for UI, (4) summary: comprehensive text replacing all compressed content",
26-
),
22+
topic: tool.schema
23+
.string()
24+
.describe("Short label (3-5 words) for display - e.g., 'Auth System Exploration'"),
25+
content: tool.schema
26+
.object({
27+
startString: tool.schema
28+
.string()
29+
.describe("Unique text from conversation marking the beginning of range"),
30+
endString: tool.schema
31+
.string()
32+
.describe("Unique text marking the end of range"),
33+
summary: tool.schema
34+
.string()
35+
.describe("Complete technical summary replacing all content in range"),
36+
})
37+
.describe("The compression details: boundaries and replacement summary"),
2738
},
2839
async execute(args, toolCtx) {
2940
const { client, state, logger } = ctx
@@ -36,30 +47,20 @@ export function createCompressTool(ctx: PruneToolContext): ReturnType<typeof too
3647
metadata: {},
3748
})
3849

39-
if (!Array.isArray(args.input)) {
40-
throw new Error(
41-
'input must be an array of 4 strings: ["startString", "endString", "topic", "summary"]',
42-
)
43-
}
44-
if (args.input.length !== 4) {
45-
throw new Error(
46-
`input must be an array of exactly 4 strings: ["startString", "endString", "topic", "summary"], got ${args.input.length} elements`,
47-
)
48-
}
49-
50-
const [startString, endString, topic, summary] = args.input
50+
const { topic, content } = args
51+
const { startString, endString, summary } = content || {}
5152

53+
if (!topic || typeof topic !== "string") {
54+
throw new Error("topic is required and must be a non-empty string")
55+
}
5256
if (!startString || typeof startString !== "string") {
53-
throw new Error("startString is required and must be a non-empty string")
57+
throw new Error("content.startString is required and must be a non-empty string")
5458
}
5559
if (!endString || typeof endString !== "string") {
56-
throw new Error("endString is required and must be a non-empty string")
57-
}
58-
if (!topic || typeof topic !== "string") {
59-
throw new Error("topic is required and must be a non-empty string")
60+
throw new Error("content.endString is required and must be a non-empty string")
6061
}
6162
if (!summary || typeof summary !== "string") {
62-
throw new Error("summary is required and must be a non-empty string")
63+
throw new Error("content.summary is required and must be a non-empty string")
6364
}
6465

6566
logger.info("Compress tool invoked")

lib/tools/distill.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function createDistillTool(ctx: PruneToolContext): ReturnType<typeof tool
1010
return tool({
1111
description: DISTILL_TOOL_DESCRIPTION,
1212
args: {
13-
items: tool.schema
13+
targets: tool.schema
1414
.array(
1515
tool.schema.object({
1616
id: tool.schema
@@ -21,31 +21,31 @@ export function createDistillTool(ctx: PruneToolContext): ReturnType<typeof tool
2121
.describe("Complete technical distillation for this tool output"),
2222
}),
2323
)
24-
.describe(
25-
"Array of distillation entries, each pairing an ID with its distillation",
26-
),
24+
.describe("Tool outputs to distill, each pairing an ID with its distillation"),
2725
},
2826
async execute(args, toolCtx) {
29-
if (!args.items || !Array.isArray(args.items) || args.items.length === 0) {
30-
ctx.logger.debug("Distill tool called without items: " + JSON.stringify(args))
31-
throw new Error("Missing items. Provide at least one { id, distillation } entry.")
27+
if (!args.targets || !Array.isArray(args.targets) || args.targets.length === 0) {
28+
ctx.logger.debug("Distill tool called without targets: " + JSON.stringify(args))
29+
throw new Error("Missing targets. Provide at least one { id, distillation } entry.")
3230
}
3331

34-
for (const item of args.items) {
35-
if (!item.id || typeof item.id !== "string" || item.id.trim() === "") {
36-
ctx.logger.debug("Distill item missing id: " + JSON.stringify(item))
32+
for (const target of args.targets) {
33+
if (!target.id || typeof target.id !== "string" || target.id.trim() === "") {
34+
ctx.logger.debug("Distill target missing id: " + JSON.stringify(target))
3735
throw new Error(
38-
"Each item must have an id (numeric string from <prunable-tools>).",
36+
"Each target must have an id (numeric string from <prunable-tools>).",
3937
)
4038
}
41-
if (!item.distillation || typeof item.distillation !== "string") {
42-
ctx.logger.debug("Distill item missing distillation: " + JSON.stringify(item))
43-
throw new Error("Each item must have a distillation string.")
39+
if (!target.distillation || typeof target.distillation !== "string") {
40+
ctx.logger.debug(
41+
"Distill target missing distillation: " + JSON.stringify(target),
42+
)
43+
throw new Error("Each target must have a distillation string.")
4444
}
4545
}
4646

47-
const ids = args.items.map((item) => item.id)
48-
const distillations = args.items.map((item) => item.distillation)
47+
const ids = args.targets.map((t) => t.id)
48+
const distillations = args.targets.map((t) => t.distillation)
4949

5050
return executePruneOperation(
5151
ctx,

0 commit comments

Comments
 (0)