pi-copy-message 3.0.0 → 4.0.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/CHANGELOG.md +8 -0
- package/README.md +11 -7
- package/extensions/copy-message.ts +132 -40
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.0 - 2026-10-01
|
|
4
|
+
|
|
5
|
+
- Require Pi 1.0.0 and pin the official 1.0.0 development cohort.
|
|
6
|
+
- Use the native canonical projection for latest-message copies, honoring context edits and compaction while retaining raw historical numbered/picker/user copies and legacy response snapshots.
|
|
7
|
+
- Verify clipboard text before reporting success; keep Pi's complete native/platform/OSC 52 write fallback chain and warn when delivery is unverified, empty, mismatched or remote.
|
|
8
|
+
- Cancel pending pickers on session/tree/shutdown lifecycle changes; suppress stale completion notifications.
|
|
9
|
+
- Add native mouse navigation/copy with restored editor focus and safe isolated clipboard/SDK lifecycle regression coverage.
|
|
10
|
+
|
|
3
11
|
## 3.0.0 - 2026-09-26
|
|
4
12
|
|
|
5
13
|
- Require Node.js 24.15 or newer for repository tooling; `.nvmrc` pins 24.21.0 and `packageManager` is npm 12.1.0.
|
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ A [pi](https://github.com/earendil-works/pi-mono) extension that adds `/copy-mes
|
|
|
16
16
|
- Supports type-to-filter search across role and message text, with `time:<term>` for timestamp search
|
|
17
17
|
- Supports Home/End jumps for oldest/newest visible messages
|
|
18
18
|
- Uses Pi's configured selection bindings for navigation, copy, and cancel
|
|
19
|
-
-
|
|
20
|
-
- Includes fast paths: `/copy-message latest`, `/copy-message last`, and `/copy-message newest
|
|
19
|
+
- Keeps Pi's platform clipboard writes and OSC 52 fallback, but reports success only after local text readback
|
|
20
|
+
- Includes fast paths: `/copy-message latest`, `/copy-message last`, and `/copy-message newest`, using Pi's canonical active context
|
|
21
21
|
- Supports direct numbered copies like `/copy-message 3`
|
|
22
22
|
- Supports metadata copies with `--with-meta`, `--with-metadata`, or `--with-role`
|
|
23
23
|
|
|
@@ -35,7 +35,7 @@ Or install it directly from GitHub with pi:
|
|
|
35
35
|
pi install https://github.com/fitchmultz/pi-copy-message
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Use `/reload` after updating extension code on current official Pi and the maintained fork. Restart Pi after changing dependencies or the host runtime.
|
|
39
39
|
|
|
40
40
|
If you prefer to load it directly from a local checkout during development:
|
|
41
41
|
|
|
@@ -109,6 +109,8 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
109
109
|
## Behavior notes
|
|
110
110
|
|
|
111
111
|
- Entry IDs are hidden from the picker.
|
|
112
|
+
- Mouse wheel navigates; clicking a message copies it and returns keyboard focus to the unchanged draft.
|
|
113
|
+
- Session replacement, tree navigation, shutdown and reload cancel pending pickers without stale-context notifications.
|
|
112
114
|
- The picker caps visible rows and scrolls instead of filling the screen.
|
|
113
115
|
- Search preserves your original selected message and restores it when the search is cleared.
|
|
114
116
|
- General search does not match timestamps; use `time:<term>` when you want to search by displayed time.
|
|
@@ -116,15 +118,17 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
116
118
|
- Filter labels honor the active pi theme.
|
|
117
119
|
- Selection key hints show the active Pi bindings. Configured selection actions take precedence if they collide with the picker's filter, preview, or format shortcuts.
|
|
118
120
|
- Only custom messages with `display: true` can appear or be copied from current `custom_message` entries or legacy message-wrapped entries; visible ones can be toggled with `Alt+C`.
|
|
119
|
-
-
|
|
121
|
+
- A verified local copy notification includes the role and a short preview. Unavailable, empty or mismatched readback warns instead of claiming success; rejected writes report an error. Remote terminal delivery via OSC 52 cannot be verified, even when the remote machine's local clipboard matches.
|
|
122
|
+
- Native clipboard reads are preferred where available. Bounded platform readers preserve exact text, including newlines; the extension never replaces Pi's working write fallback chain.
|
|
123
|
+
- `latest` uses Pi's canonical session projection, honoring context edits, omissions and compaction. Numbered selectors, the picker and `/copy-user` retain raw active-branch history, including older pre-compaction messages.
|
|
120
124
|
- `/copy-message latest` and numbered selectors include user, assistant, and visible custom messages by default while hiding tool/bash messages. `Alt+C` only affects the picker. If only hidden messages exist, `latest` falls back to the newest message so the command still does something useful.
|
|
121
125
|
- `/copy-message` with no direct selector requires interactive TUI mode because the picker is a custom TUI component.
|
|
122
126
|
- Direct commands such as `/copy-user`, `/copy-message latest`, and `/copy-message 3` do not require TUI mode, though non-UI modes may not display notifications.
|
|
123
127
|
|
|
124
128
|
## Compatibility
|
|
125
129
|
|
|
126
|
-
-
|
|
127
|
-
- The
|
|
130
|
+
- Requires Pi 1.0.0 or later. Development and standalone/packed qualification use the official 1.0.0 cohort; a maintained fork must be qualified at its actual supported revision, not by version-string equivalence.
|
|
131
|
+
- The picker coalesces legacy in-progress assistant snapshots within their context-window boundaries so each response appears once. Current official Pi and the maintained fork no longer write those legacy boundaries.
|
|
128
132
|
- Android/Termux clipboard writes use Pi's normal fallback chain, including OSC 52 when native clipboard commands are unavailable.
|
|
129
133
|
- Pi includes `Ctrl+X` for copying the latest assistant response; this extension remains useful for searchable history, other roles, metadata, and direct selectors.
|
|
130
134
|
- Node.js `>=24.15.0`. `.nvmrc` pins Node 24.21.0 for local development.
|
|
@@ -138,7 +142,7 @@ npm ci
|
|
|
138
142
|
npm run check # node --test + tsc --noEmit + pack dry-run
|
|
139
143
|
```
|
|
140
144
|
|
|
141
|
-
No production build or `prepare` is required. Tests run TypeScript directly with Node's type stripping, so source must stay erasable (`erasableSyntaxOnly`). Qualification uses the selected host installed in this checkout's dependency graph. The tests cover picker
|
|
145
|
+
No production build or `prepare` is required. Tests run TypeScript directly with Node's type stripping, so source must stay erasable (`erasableSyntaxOnly`). Qualification uses the selected host installed in this checkout's dependency graph. The tests cover formatting, native command execution, canonical context and branch selection, both TUI modes, pointer focus and pending-picker disposal. Native addon interception and an isolated command PATH keep test clipboard writes in temporary fixture files, never the operator clipboard. Actual Android/Termux, Windows/WSL and remote terminal delivery still require platform qualification.
|
|
142
146
|
|
|
143
147
|
`package-lock.json` must only reference `registry.npmjs.org`; a test fails on private-registry URLs. Behind a registry proxy that rewrites tarball URLs, refresh the lock from public metadata and install through the proxy:
|
|
144
148
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
1
3
|
import { copyToClipboard, type ExtensionAPI, type ExtensionCommandContext, type KeybindingsManager } from "@earendil-works/pi-coding-agent";
|
|
2
4
|
import type { AutocompleteItem, TuiMode } from "@earendil-works/pi-tui";
|
|
3
|
-
import { decodeKittyPrintable, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
5
|
+
import { decodeKittyPrintable, getNativeClipboard, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, type TuiMouseEvent } from "@earendil-works/pi-tui";
|
|
4
6
|
|
|
5
7
|
const MAX_VISIBLE_MESSAGES = 8;
|
|
6
8
|
const MAX_PEEK_LINES = 16;
|
|
@@ -169,6 +171,17 @@ export function collectCopyableMessages(ctx: { sessionManager: { getBranch(): un
|
|
|
169
171
|
});
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
function collectProjectedMessages(ctx: ExtensionCommandContext): CopyableMessage[] {
|
|
175
|
+
const entries = ctx.sessionManager.buildSessionProjection().entries.flatMap(({ sourceEntry, messages }): unknown[] => {
|
|
176
|
+
// Retain saved fork boundaries for legacy response-snapshot coalescing.
|
|
177
|
+
if ((sourceEntry.type as string) === "context_window") return [sourceEntry];
|
|
178
|
+
return messages
|
|
179
|
+
.filter((message) => !(sourceEntry.type === "compaction" && message.role === "system"))
|
|
180
|
+
.map((message) => ({ ...sourceEntry, type: "message", message }));
|
|
181
|
+
});
|
|
182
|
+
return collectCopyableMessages({ sessionManager: { getBranch: () => entries } });
|
|
183
|
+
}
|
|
184
|
+
|
|
172
185
|
export type MostRecentUserMessageResult =
|
|
173
186
|
| { kind: "message"; message: CopyableMessage }
|
|
174
187
|
| { kind: "no-user-message" }
|
|
@@ -414,6 +427,7 @@ export class CopyMessagePickerState {
|
|
|
414
427
|
peek = false;
|
|
415
428
|
private searchAnchorId: string | undefined;
|
|
416
429
|
private readonly messages: CopyableMessage[];
|
|
430
|
+
private firstVisibleIndex = 0;
|
|
417
431
|
|
|
418
432
|
constructor(messages: CopyableMessage[], initialFormat: CopyFormat = "raw") {
|
|
419
433
|
this.messages = messages;
|
|
@@ -435,6 +449,7 @@ export class CopyMessagePickerState {
|
|
|
435
449
|
const maxVisible = Math.min(this.visibleMessages.length, MAX_VISIBLE_MESSAGES);
|
|
436
450
|
const start = maxVisible === 0 ? 0 : Math.max(0, Math.min(this.selectedIndex - maxVisible + 1, this.visibleMessages.length - maxVisible));
|
|
437
451
|
const end = Math.min(this.visibleMessages.length, start + maxVisible);
|
|
452
|
+
this.firstVisibleIndex = start;
|
|
438
453
|
const hasCustomMessages = this.messages.some((message) => message.role === "custom");
|
|
439
454
|
const userState = filterLabel(theme, "user", this.visibility.showUser, "warning");
|
|
440
455
|
const assistantState = filterLabel(theme, "assistant", this.visibility.showAssistant, "accent");
|
|
@@ -584,6 +599,20 @@ export class CopyMessagePickerState {
|
|
|
584
599
|
return "none";
|
|
585
600
|
}
|
|
586
601
|
|
|
602
|
+
handleMouse(event: TuiMouseEvent): PickerInputResult {
|
|
603
|
+
if (event.type === "wheel" && event.wheelDelta) {
|
|
604
|
+
this.move(event.wheelDelta < 0 ? -1 : 1);
|
|
605
|
+
return "render";
|
|
606
|
+
}
|
|
607
|
+
if (event.button !== "left" || (event.type !== "press" && event.type !== "click")) return "none";
|
|
608
|
+
const row = event.y - 2; // The message list follows the title and spacer.
|
|
609
|
+
if (row < 0 || row >= MAX_VISIBLE_MESSAGES) return "none";
|
|
610
|
+
const index = this.firstVisibleIndex + row;
|
|
611
|
+
if (!this.visibleMessages[index]) return "none";
|
|
612
|
+
this.selectedIndex = index;
|
|
613
|
+
return event.type === "click" ? "copy" : "render";
|
|
614
|
+
}
|
|
615
|
+
|
|
587
616
|
private refreshMessages(preferredId?: string) {
|
|
588
617
|
const selectedId = preferredId ?? this.visibleMessages[this.selectedIndex]?.id;
|
|
589
618
|
this.visibleMessages = filteredMessages(this.messages, this.visibility, this.search);
|
|
@@ -660,28 +689,33 @@ export function copyArgumentCompletions(prefix: string, includeSelectors: boolea
|
|
|
660
689
|
return items.length > 0 ? items : null;
|
|
661
690
|
}
|
|
662
691
|
|
|
663
|
-
async function pickMessage(ctx: ExtensionCommandContext, messages: CopyableMessage[], initialFormat: CopyFormat) {
|
|
692
|
+
async function pickMessage(ctx: ExtensionCommandContext, messages: CopyableMessage[], initialFormat: CopyFormat, signal: AbortSignal) {
|
|
693
|
+
if (signal.aborted) return null;
|
|
664
694
|
return ctx.ui.custom<{ message: CopyableMessage; text: string } | null>((tui, theme, keybindings, done) => {
|
|
665
695
|
const state = new CopyMessagePickerState(messages, initialFormat);
|
|
696
|
+
const abort = () => done(null);
|
|
697
|
+
if (signal.aborted) abort();
|
|
698
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
699
|
+
const act = (result: PickerInputResult) => {
|
|
700
|
+
if (result === "copy") {
|
|
701
|
+
const selected = state.selectedMessage();
|
|
702
|
+
const text = state.selectedCopyText();
|
|
703
|
+
done(selected && text !== undefined ? { message: selected, text } : null);
|
|
704
|
+
} else if (result === "cancel") done(null);
|
|
705
|
+
else if (result === "render") tui.requestRender();
|
|
706
|
+
};
|
|
666
707
|
return {
|
|
667
708
|
render(width: number) {
|
|
668
709
|
return state.render(width, theme, keybindings, tui.mode);
|
|
669
710
|
},
|
|
670
711
|
invalidate() {},
|
|
671
|
-
handleInput(data: string) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
done(selected && text !== undefined ? { message: selected, text } : null);
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
if (result === "cancel") {
|
|
680
|
-
done(null);
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
if (result === "render") tui.requestRender();
|
|
712
|
+
handleInput(data: string) { act(state.handleInput(data, keybindings)); },
|
|
713
|
+
handleMouse(event) {
|
|
714
|
+
const result = state.handleMouse(event);
|
|
715
|
+
act(result);
|
|
716
|
+
if (result !== "none") return { handled: true, focus: result === "render" };
|
|
684
717
|
},
|
|
718
|
+
dispose: () => signal.removeEventListener("abort", abort),
|
|
685
719
|
};
|
|
686
720
|
});
|
|
687
721
|
}
|
|
@@ -690,74 +724,132 @@ function copyNotificationText(selected: CopyableMessage): string {
|
|
|
690
724
|
return `Copied ${roleLabel(selected.role)} message: “${compactPreview(selected.text, 48)}”`;
|
|
691
725
|
}
|
|
692
726
|
|
|
693
|
-
|
|
727
|
+
const runClipboardReader = promisify(execFile);
|
|
728
|
+
|
|
729
|
+
async function readCopiedText(expected: string): Promise<string | null | undefined> {
|
|
730
|
+
if (process.platform !== "linux") {
|
|
731
|
+
try {
|
|
732
|
+
const text = await getNativeClipboard()?.getText();
|
|
733
|
+
if (text !== undefined) return text;
|
|
734
|
+
} catch {
|
|
735
|
+
// The platform command fallback can still work if the native helper fails.
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const commands: [string, string[]][] = [];
|
|
739
|
+
const windowsRead = ["-NoProfile", "-Command", "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); [Console]::Write((Get-Clipboard -Raw))"];
|
|
740
|
+
if (process.platform === "darwin") commands.push(["pbpaste", []]);
|
|
741
|
+
else if (process.platform === "win32") commands.push(["powershell", windowsRead]);
|
|
742
|
+
else {
|
|
743
|
+
if (process.env.TERMUX_VERSION) commands.push(["termux-clipboard-get", []]);
|
|
744
|
+
if (process.env.WAYLAND_DISPLAY) commands.push(["wl-paste", ["--no-newline", "--type", "text"]]);
|
|
745
|
+
if (process.env.DISPLAY) commands.push(["xclip", ["-selection", "clipboard", "-out"]], ["xsel", ["--clipboard", "--output"]]);
|
|
746
|
+
if (!commands.length && (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP)) commands.push(["powershell.exe", windowsRead]);
|
|
747
|
+
}
|
|
748
|
+
for (const [command, args] of commands) {
|
|
749
|
+
try {
|
|
750
|
+
const { stdout } = await runClipboardReader(command, args, {
|
|
751
|
+
encoding: "utf8", timeout: 3000, maxBuffer: Math.max(1024 * 1024, Buffer.byteLength(expected) + 4096),
|
|
752
|
+
});
|
|
753
|
+
return stdout || null;
|
|
754
|
+
} catch {
|
|
755
|
+
// Try the next platform reader; never expose clipboard contents on failure.
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return undefined;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
type Notify = ExtensionCommandContext["ui"]["notify"];
|
|
762
|
+
|
|
763
|
+
async function copySelectedMessage(notify: Notify, selected: CopyableMessage, text = selected.text) {
|
|
694
764
|
try {
|
|
695
765
|
await copyToClipboard(text);
|
|
696
|
-
|
|
766
|
+
const actual = await readCopiedText(text);
|
|
767
|
+
const remote = Boolean(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.MOSH_CONNECTION);
|
|
768
|
+
if (actual === text && !remote) notify(copyNotificationText(selected), "info");
|
|
769
|
+
else notify(
|
|
770
|
+
actual === text ? "Local clipboard verified; remote terminal clipboard delivery is unverified."
|
|
771
|
+
: "Clipboard command completed, but delivery could not be verified.",
|
|
772
|
+
"warning",
|
|
773
|
+
);
|
|
697
774
|
} catch (error) {
|
|
698
|
-
|
|
775
|
+
notify(error instanceof Error ? error.message : "Failed to copy to clipboard", "error");
|
|
699
776
|
}
|
|
700
777
|
}
|
|
701
778
|
|
|
702
|
-
async function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionManager"
|
|
779
|
+
async function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionManager">, notify: Notify, format: CopyFormat) {
|
|
703
780
|
const result = getMostRecentUserMessage(ctx);
|
|
704
781
|
if (result.kind === "no-user-message") {
|
|
705
|
-
|
|
782
|
+
notify("No user messages found", "warning");
|
|
706
783
|
return;
|
|
707
784
|
}
|
|
708
785
|
if (result.kind === "no-text") {
|
|
709
|
-
|
|
786
|
+
notify("No user message text found", "warning");
|
|
710
787
|
return;
|
|
711
788
|
}
|
|
712
|
-
|
|
713
|
-
await copySelectedMessage(ctx, result.message, formatMessageForCopy(result.message, format));
|
|
789
|
+
await copySelectedMessage(notify, result.message, formatMessageForCopy(result.message, format));
|
|
714
790
|
}
|
|
715
791
|
|
|
716
|
-
export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCommand">) {
|
|
792
|
+
export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCommand" | "on">) {
|
|
793
|
+
let generation = 0;
|
|
794
|
+
let pickerAbort: AbortController | undefined;
|
|
795
|
+
const reset = () => {
|
|
796
|
+
generation++;
|
|
797
|
+
pickerAbort?.abort();
|
|
798
|
+
pickerAbort = undefined;
|
|
799
|
+
};
|
|
800
|
+
pi.on("session_start", reset);
|
|
801
|
+
pi.on("session_shutdown", reset);
|
|
802
|
+
pi.on("session_tree", reset);
|
|
803
|
+
const notifications = (ctx: ExtensionCommandContext): Notify => {
|
|
804
|
+
const current = generation;
|
|
805
|
+
return (message, type) => { if (current === generation) ctx.ui.notify(message, type); };
|
|
806
|
+
};
|
|
717
807
|
pi.registerCommand("copy-message", {
|
|
718
808
|
description: "Select a session message and copy its text to the clipboard",
|
|
719
809
|
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, true),
|
|
720
810
|
handler: async (args, ctx) => {
|
|
811
|
+
const notify = notifications(ctx);
|
|
721
812
|
const parsedArgs = parseCopyArgs(args);
|
|
722
|
-
const messages = collectCopyableMessages(ctx);
|
|
813
|
+
const messages = parsedArgs.selector === "latest" ? collectProjectedMessages(ctx) : collectCopyableMessages(ctx);
|
|
723
814
|
if (messages.length === 0) {
|
|
724
|
-
|
|
815
|
+
notify("No copyable messages found in the current branch", "error");
|
|
725
816
|
return;
|
|
726
817
|
}
|
|
727
|
-
|
|
728
818
|
if (parsedArgs.selector === "latest") {
|
|
729
819
|
const latestVisible = latestDefaultMessage(messages);
|
|
730
|
-
if (latestVisible) await copySelectedMessage(
|
|
820
|
+
if (latestVisible) await copySelectedMessage(notify, latestVisible, formatMessageForCopy(latestVisible, parsedArgs.format));
|
|
731
821
|
return;
|
|
732
822
|
}
|
|
733
|
-
|
|
734
823
|
if (typeof parsedArgs.selector === "object") {
|
|
735
824
|
const selected = messageByDefaultNumber(messages, parsedArgs.selector.number);
|
|
736
825
|
if (!selected) {
|
|
737
|
-
|
|
826
|
+
notify(`No default visible message #${parsedArgs.selector.number} (found ${defaultVisibleMessages(messages).length})`, "warning");
|
|
738
827
|
return;
|
|
739
828
|
}
|
|
740
|
-
await copySelectedMessage(
|
|
829
|
+
await copySelectedMessage(notify, selected, formatMessageForCopy(selected, parsedArgs.format));
|
|
741
830
|
return;
|
|
742
831
|
}
|
|
743
|
-
|
|
744
832
|
if (ctx.mode !== "tui") {
|
|
745
|
-
|
|
833
|
+
notify("/copy-message requires interactive TUI mode unless you pass latest/last/newest or a message number", "error");
|
|
746
834
|
return;
|
|
747
835
|
}
|
|
748
|
-
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
836
|
+
pickerAbort?.abort();
|
|
837
|
+
const controller = new AbortController();
|
|
838
|
+
pickerAbort = controller;
|
|
839
|
+
try {
|
|
840
|
+
const selected = await pickMessage(ctx, messages, parsedArgs.format, controller.signal);
|
|
841
|
+
if (!selected || controller.signal.aborted) return;
|
|
842
|
+
await copySelectedMessage(notify, selected.message, selected.text);
|
|
843
|
+
} finally {
|
|
844
|
+
if (pickerAbort === controller) pickerAbort = undefined;
|
|
845
|
+
}
|
|
753
846
|
},
|
|
754
847
|
});
|
|
755
|
-
|
|
756
848
|
pi.registerCommand("copy-user", {
|
|
757
849
|
description: "Copy the most recent user message to the clipboard",
|
|
758
850
|
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, false),
|
|
759
851
|
handler: async (args, ctx) => {
|
|
760
|
-
await copyMostRecentUserMessage(ctx, parseCopyArgs(args).format);
|
|
852
|
+
await copyMostRecentUserMessage(ctx, notifications(ctx), parseCopyArgs(args).format);
|
|
761
853
|
},
|
|
762
854
|
});
|
|
763
855
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-copy-message",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Pi extension with a searchable TUI picker for copying raw session messages",
|
|
5
5
|
"author": "Mitch Fultz (https://github.com/fitchmultz)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"LICENSE"
|
|
46
46
|
],
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
49
|
-
"@earendil-works/pi-tui": "0.
|
|
48
|
+
"@earendil-works/pi-coding-agent": "1.0.0",
|
|
49
|
+
"@earendil-works/pi-tui": "1.0.0",
|
|
50
50
|
"@types/node": "24.19.0",
|
|
51
51
|
"typescript": "7.0.2"
|
|
52
52
|
},
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"./extensions"
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
|
-
"packageManager": "npm@12.
|
|
58
|
+
"packageManager": "npm@12.2.0",
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@earendil-works/pi-coding-agent": "*",
|
|
61
61
|
"@earendil-works/pi-tui": "*"
|