@ssweens/pi-handoff 1.2.0 β 1.3.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.
- package/extensions/handoff.ts +88 -11
- package/package.json +1 -1
package/extensions/handoff.ts
CHANGED
|
@@ -114,8 +114,25 @@ function extractFileOpsFromMessage(message: any, fileOps: FileOps): void {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Collapsed file markers
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// File lists are shown as compact markers in the editor (e.g. "[π 12 read files]")
|
|
121
|
+
// and expanded to full XML tags when the user submits via the input event hook.
|
|
122
|
+
|
|
123
|
+
/** Pending file lists keyed by marker text β expanded XML content. */
|
|
124
|
+
type FileMarkerStore = Map<string, string>;
|
|
125
|
+
|
|
126
|
+
function createReadMarker(count: number): string {
|
|
127
|
+
return `[+${count} read filename${count === 1 ? "" : "s"}]`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function createModifiedMarker(count: number): string {
|
|
131
|
+
return `[+${count} modified filename${count === 1 ? "" : "s"}]`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Build collapsed markers + expansion map from tool-call messages. */
|
|
135
|
+
function buildFileOperations(messages: any[]): { markers: string; expansions: FileMarkerStore } | null {
|
|
119
136
|
const fileOps = createFileOps();
|
|
120
137
|
for (const msg of messages) extractFileOpsFromMessage(msg, fileOps);
|
|
121
138
|
|
|
@@ -123,15 +140,32 @@ function appendFileOperations(summary: string, messages: any[]): string {
|
|
|
123
140
|
const readFiles = [...fileOps.read].filter((f) => !modified.has(f)).sort();
|
|
124
141
|
const modifiedFiles = [...modified].sort();
|
|
125
142
|
|
|
126
|
-
|
|
143
|
+
if (readFiles.length === 0 && modifiedFiles.length === 0) return null;
|
|
144
|
+
|
|
145
|
+
const expansions: FileMarkerStore = new Map();
|
|
146
|
+
const markerLines: string[] = [];
|
|
147
|
+
|
|
127
148
|
if (readFiles.length > 0) {
|
|
128
|
-
|
|
149
|
+
const marker = createReadMarker(readFiles.length);
|
|
150
|
+
expansions.set(marker, `<read-files>\n${readFiles.join("\n")}\n</read-files>`);
|
|
151
|
+
markerLines.push(marker);
|
|
129
152
|
}
|
|
130
153
|
if (modifiedFiles.length > 0) {
|
|
131
|
-
|
|
154
|
+
const marker = createModifiedMarker(modifiedFiles.length);
|
|
155
|
+
expansions.set(marker, `<modified-files>\n${modifiedFiles.join("\n")}\n</modified-files>`);
|
|
156
|
+
markerLines.push(marker);
|
|
132
157
|
}
|
|
133
158
|
|
|
134
|
-
return
|
|
159
|
+
return { markers: markerLines.join("\n"), expansions };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Expand all file markers in text using the stored expansions. */
|
|
163
|
+
function expandFileMarkers(text: string, store: FileMarkerStore): string {
|
|
164
|
+
let result = text;
|
|
165
|
+
for (const [marker, expanded] of store) {
|
|
166
|
+
result = result.replaceAll(marker, expanded);
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
135
169
|
}
|
|
136
170
|
|
|
137
171
|
// ---------------------------------------------------------------------------
|
|
@@ -308,6 +342,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
308
342
|
// Store prompt keyed by parent session for the session_switch handler.
|
|
309
343
|
const pendingHandoffText = new Map<string, string>();
|
|
310
344
|
|
|
345
|
+
// -- Collapsed file marker expansion state --------------------------------
|
|
346
|
+
// Stores markerβXML mappings so the input hook can expand them on submit.
|
|
347
|
+
let activeFileMarkers: FileMarkerStore = new Map();
|
|
348
|
+
|
|
311
349
|
// ββ session_switch ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
312
350
|
// Set editor text for command-path handoffs + clear context filter.
|
|
313
351
|
pi.on("session_switch", async (event, ctx) => {
|
|
@@ -341,6 +379,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
341
379
|
}
|
|
342
380
|
});
|
|
343
381
|
|
|
382
|
+
// ββ input: expand collapsed file markers before LLM sees the text βββββββ
|
|
383
|
+
pi.on("input", (event) => {
|
|
384
|
+
if (activeFileMarkers.size === 0) return;
|
|
385
|
+
|
|
386
|
+
// Check if any markers are present in the input text
|
|
387
|
+
let hasMarkers = false;
|
|
388
|
+
for (const marker of activeFileMarkers.keys()) {
|
|
389
|
+
if (event.text.includes(marker)) {
|
|
390
|
+
hasMarkers = true;
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (!hasMarkers) return;
|
|
395
|
+
|
|
396
|
+
const expanded = expandFileMarkers(event.text, activeFileMarkers);
|
|
397
|
+
// Clear after first expansion β markers are single-use (one handoff prompt)
|
|
398
|
+
activeFileMarkers = new Map();
|
|
399
|
+
return { action: "transform" as const, text: expanded, images: event.images };
|
|
400
|
+
});
|
|
401
|
+
|
|
344
402
|
// ββ agent_end: deferred session switch for tool path ββββββββββββββββββββ
|
|
345
403
|
pi.on("agent_end", (_event, ctx) => {
|
|
346
404
|
if (!pendingHandoff) return;
|
|
@@ -404,8 +462,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
404
462
|
return;
|
|
405
463
|
}
|
|
406
464
|
|
|
407
|
-
//
|
|
408
|
-
|
|
465
|
+
// Build collapsed file markers from the messages being summarized
|
|
466
|
+
const fileOps = buildFileOperations(preparation.messagesToSummarize);
|
|
467
|
+
let prompt = fileOps
|
|
468
|
+
? `${handoffResult.text}\n\n${fileOps.markers}`
|
|
469
|
+
: handoffResult.text;
|
|
409
470
|
|
|
410
471
|
// Switch session via raw sessionManager (safe β no agent loop running)
|
|
411
472
|
const currentSessionFile = ctx.sessionManager.getSessionFile();
|
|
@@ -425,6 +486,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
425
486
|
return;
|
|
426
487
|
}
|
|
427
488
|
|
|
489
|
+
// Activate markers for input hook expansion, then set editor text
|
|
490
|
+
if (fileOps) activeFileMarkers = fileOps.expansions;
|
|
428
491
|
ctx.ui.setEditorText(prompt);
|
|
429
492
|
ctx.ui.notify("Handoff ready β edit if needed, press Enter to send", "info");
|
|
430
493
|
|
|
@@ -462,8 +525,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
462
525
|
return;
|
|
463
526
|
}
|
|
464
527
|
|
|
465
|
-
//
|
|
466
|
-
|
|
528
|
+
// Build collapsed file markers from tool calls
|
|
529
|
+
const fileOps = buildFileOperations(conv.messages);
|
|
530
|
+
let prompt = fileOps
|
|
531
|
+
? `${result.text}\n\n${fileOps.markers}`
|
|
532
|
+
: result.text;
|
|
467
533
|
|
|
468
534
|
const currentSessionFile = ctx.sessionManager.getSessionFile();
|
|
469
535
|
|
|
@@ -473,6 +539,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
473
539
|
if (currentSessionFile) {
|
|
474
540
|
pendingHandoffText.set(currentSessionFile, prompt);
|
|
475
541
|
}
|
|
542
|
+
// Stage markers β they'll be activated in session_switch after editor text is set
|
|
543
|
+
const pendingMarkers = fileOps?.expansions;
|
|
476
544
|
|
|
477
545
|
const sessionResult = await ctx.newSession({ parentSession: currentSessionFile ?? undefined });
|
|
478
546
|
|
|
@@ -481,6 +549,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
481
549
|
ctx.ui.notify("New session cancelled.", "info");
|
|
482
550
|
return;
|
|
483
551
|
}
|
|
552
|
+
|
|
553
|
+
// Activate markers for the new session's input hook
|
|
554
|
+
if (pendingMarkers) activeFileMarkers = pendingMarkers;
|
|
484
555
|
},
|
|
485
556
|
});
|
|
486
557
|
|
|
@@ -515,11 +586,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
515
586
|
return { content: [{ type: "text" as const, text: `Handoff failed: ${result.message}` }] };
|
|
516
587
|
}
|
|
517
588
|
|
|
518
|
-
|
|
589
|
+
const fileOps = buildFileOperations(conv.messages);
|
|
590
|
+
let prompt = fileOps
|
|
591
|
+
? `${result.text}\n\n${fileOps.markers}`
|
|
592
|
+
: result.text;
|
|
519
593
|
|
|
520
594
|
const currentSessionFile = ctx.sessionManager.getSessionFile();
|
|
521
595
|
prompt = wrapWithParentSession(prompt, currentSessionFile ?? null);
|
|
522
596
|
|
|
597
|
+
// Stage markers for activation after session switch
|
|
598
|
+
if (fileOps) activeFileMarkers = fileOps.expansions;
|
|
599
|
+
|
|
523
600
|
// Defer session switch to agent_end
|
|
524
601
|
pendingHandoff = {
|
|
525
602
|
prompt,
|