pi-copy-message 2.1.0 → 3.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 +12 -0
- package/README.md +19 -11
- package/extensions/copy-message.ts +61 -80
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.0 - 2026-09-26
|
|
4
|
+
|
|
5
|
+
- Require Node.js 24.15 or newer for repository tooling; `.nvmrc` pins 24.21.0 and `packageManager` is npm 12.1.0.
|
|
6
|
+
- Run tests with Node's built-in `node --test` and type stripping instead of `tsx`; typecheck with TypeScript 7.0.2 and `erasableSyntaxOnly`.
|
|
7
|
+
- Refresh the development baseline to Pi 0.87.1, qualified against official Pi and the maintained fork.
|
|
8
|
+
- Guard `package-lock.json` against private-registry URLs.
|
|
9
|
+
- First npm release containing the 2.1.1 clipboard change (2.1.1 was tagged but never published to npm).
|
|
10
|
+
|
|
11
|
+
## 2.1.1 - 2026-08-25
|
|
12
|
+
|
|
13
|
+
- Reuse Pi's clipboard implementation instead of maintaining a duplicate command runner, enabling Pi's OSC 52 fallback on Android/Termux.
|
|
14
|
+
|
|
3
15
|
## 2.1.0 - 2026-08-17
|
|
4
16
|
|
|
5
17
|
- Add an `Alt+C` picker filter and `MessageVisibility.showCustom` option for showing or hiding custom messages.
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ 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
|
+
- Uses Pi's clipboard implementation, including its OSC 52 terminal fallback
|
|
19
20
|
- Includes fast paths: `/copy-message latest`, `/copy-message last`, and `/copy-message newest`
|
|
20
21
|
- Supports direct numbered copies like `/copy-message 3`
|
|
21
22
|
- Supports metadata copies with `--with-meta`, `--with-metadata`, or `--with-role`
|
|
@@ -34,11 +35,7 @@ Or install it directly from GitHub with pi:
|
|
|
34
35
|
pi install https://github.com/fitchmultz/pi-copy-message
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
```text
|
|
40
|
-
/reload
|
|
41
|
-
```
|
|
38
|
+
Restart Pi after installing or updating extension code or dependencies. The maintained fork's `/reload` refreshes resources and reinitializes cached code; it does not apply code updates.
|
|
42
39
|
|
|
43
40
|
If you prefer to load it directly from a local checkout during development:
|
|
44
41
|
|
|
@@ -97,7 +94,7 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
97
94
|
| `Ctrl+PageUp` / `Ctrl+PageDown` | Move by one visible page in fullscreen mode |
|
|
98
95
|
| `Home` / `End` | Jump to oldest/newest visible message in regular mode |
|
|
99
96
|
| `Ctrl+Home` / `Ctrl+End` | Jump to oldest/newest visible message in fullscreen mode |
|
|
100
|
-
| Type text | Filter visible messages |
|
|
97
|
+
| Type or paste text | Filter visible messages; pasted line breaks and tabs become spaces |
|
|
101
98
|
| `time:<term>` | Search timestamps |
|
|
102
99
|
| `Backspace` | Delete one search character |
|
|
103
100
|
| `Ctrl+U` | Toggle user messages |
|
|
@@ -115,6 +112,7 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
115
112
|
- The picker caps visible rows and scrolls instead of filling the screen.
|
|
116
113
|
- Search preserves your original selected message and restores it when the search is cleared.
|
|
117
114
|
- General search does not match timestamps; use `time:<term>` when you want to search by displayed time.
|
|
115
|
+
- Picker previews and copy notifications remove terminal control characters; the copied text stays raw.
|
|
118
116
|
- Filter labels honor the active pi theme.
|
|
119
117
|
- 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.
|
|
120
118
|
- 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`.
|
|
@@ -125,18 +123,28 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
125
123
|
|
|
126
124
|
## Compatibility
|
|
127
125
|
|
|
128
|
-
-
|
|
126
|
+
- Declared Pi floor: 0.84.0. Qualified on official Pi 0.87.1 and the maintained fork's `main` (not a claim that every later release has been tested).
|
|
127
|
+
- The maintained fork persists in-progress assistant snapshots and context-window boundaries; the picker coalesces those so each response appears once. Official Pi writes neither, and the same code handles both.
|
|
128
|
+
- Android/Termux clipboard writes use Pi's normal fallback chain, including OSC 52 when native clipboard commands are unavailable.
|
|
129
129
|
- Pi includes `Ctrl+X` for copying the latest assistant response; this extension remains useful for searchable history, other roles, metadata, and direct selectors.
|
|
130
|
-
-
|
|
131
|
-
- `.nvmrc` pins Node 22.19.0 for local development
|
|
130
|
+
- Node.js `>=24.15.0`. `.nvmrc` pins Node 24.21.0 for local development.
|
|
132
131
|
|
|
133
132
|
This package keeps pi core packages as wildcard peers (`*`) per pi package guidance. Pi aliases these imports to its own bundled copies when loading the extension, so the package never bundles or shadows pi core. Local development uses `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` as dev dependencies for typechecking and tests.
|
|
134
133
|
|
|
135
134
|
## Development
|
|
136
135
|
|
|
137
136
|
```bash
|
|
138
|
-
npm
|
|
139
|
-
npm run check
|
|
137
|
+
npm ci
|
|
138
|
+
npm run check # node --test + tsc --noEmit + pack dry-run
|
|
139
|
+
```
|
|
140
|
+
|
|
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 behavior and formatting without clipboard writes; native clipboard delivery, including Android/Termux and OSC 52, requires separate platform qualification.
|
|
142
|
+
|
|
143
|
+
`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
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm install --package-lock-only --ignore-scripts --registry=https://registry.npmjs.org/
|
|
147
|
+
npm ci
|
|
140
148
|
```
|
|
141
149
|
|
|
142
150
|
Key files:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ExtensionAPI, ExtensionCommandContext, KeybindingsManager } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { copyToClipboard, type ExtensionAPI, type ExtensionCommandContext, type KeybindingsManager } from "@earendil-works/pi-coding-agent";
|
|
3
2
|
import type { AutocompleteItem, TuiMode } from "@earendil-works/pi-tui";
|
|
4
3
|
import { decodeKittyPrintable, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
5
4
|
|
|
@@ -33,7 +32,7 @@ function textFromContent(content: unknown): string {
|
|
|
33
32
|
);
|
|
34
33
|
})
|
|
35
34
|
.map((part) => part.text)
|
|
36
|
-
.join("
|
|
35
|
+
.join("");
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
function textFromMessage(message: Record<string, unknown>): string {
|
|
@@ -56,14 +55,10 @@ function textFromMessage(message: Record<string, unknown>): string {
|
|
|
56
55
|
return textFromContent(message.content);
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
segment(input: string): Iterable<{ segment: string }>;
|
|
61
|
-
};
|
|
58
|
+
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
62
59
|
|
|
63
60
|
function splitGraphemes(text: string): string[] {
|
|
64
|
-
|
|
65
|
-
if (!Segmenter) return Array.from(text);
|
|
66
|
-
return Array.from(new Segmenter(undefined, { granularity: "grapheme" }).segment(text), (part) => part.segment);
|
|
61
|
+
return Array.from(graphemeSegmenter.segment(text), (part) => part.segment);
|
|
67
62
|
}
|
|
68
63
|
|
|
69
64
|
function truncateGraphemes(text: string, max: number): string {
|
|
@@ -74,8 +69,12 @@ function truncateGraphemes(text: string, max: number): string {
|
|
|
74
69
|
return `${graphemes.slice(0, max - 1).join("")}…`;
|
|
75
70
|
}
|
|
76
71
|
|
|
72
|
+
function safeDisplayText(text: string): string {
|
|
73
|
+
return text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/g, "");
|
|
74
|
+
}
|
|
75
|
+
|
|
77
76
|
function compactPreview(text: string, max = 96): string {
|
|
78
|
-
const preview = text.replace(/\s+/gu, " ").trim();
|
|
77
|
+
const preview = safeDisplayText(text).replace(/\s+/gu, " ").trim();
|
|
79
78
|
return truncateGraphemes(preview, max);
|
|
80
79
|
}
|
|
81
80
|
|
|
@@ -96,7 +95,7 @@ function roleLabel(role: string): string {
|
|
|
96
95
|
case "compactionSummary":
|
|
97
96
|
return "compaction";
|
|
98
97
|
default:
|
|
99
|
-
return role || "message";
|
|
98
|
+
return safeDisplayText(role).replace(/\s+/gu, " ").trim() || "message";
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
|
|
@@ -138,7 +137,33 @@ function entryToCopyableMessage(entry: unknown): CopyableMessage | undefined {
|
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
export function collectCopyableMessages(ctx: { sessionManager: { getBranch(): unknown[] } }): CopyableMessage[] {
|
|
141
|
-
|
|
140
|
+
const entries: unknown[] = [];
|
|
141
|
+
// Pi persists assistant snapshots separately and coalesces them within each context window.
|
|
142
|
+
const responses = new Map<string, number>();
|
|
143
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
144
|
+
const record = entry as {
|
|
145
|
+
type?: string;
|
|
146
|
+
message?: { role?: string; responseId?: string; stopReason?: string; content?: unknown[] };
|
|
147
|
+
} | null;
|
|
148
|
+
if (record?.type === "context_window") responses.clear();
|
|
149
|
+
const responseId =
|
|
150
|
+
record?.type === "message" && record.message?.role === "assistant" && typeof record.message.responseId === "string"
|
|
151
|
+
? record.message.responseId
|
|
152
|
+
: undefined;
|
|
153
|
+
const previous = responseId ? responses.get(responseId) : undefined;
|
|
154
|
+
if (previous !== undefined) {
|
|
155
|
+
const earlier = entries[previous] as typeof record;
|
|
156
|
+
if (
|
|
157
|
+
record?.message?.stopReason !== "pending" ||
|
|
158
|
+
(earlier?.message?.stopReason === "pending" && (earlier.message.content?.length ?? 0) <= (record.message.content?.length ?? 0))
|
|
159
|
+
) entries[previous] = entry;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (responseId) responses.set(responseId, entries.length);
|
|
163
|
+
entries.push(entry);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return entries.flatMap((entry) => {
|
|
142
167
|
const message = entryToCopyableMessage(entry);
|
|
143
168
|
return message ? [message] : [];
|
|
144
169
|
});
|
|
@@ -181,58 +206,6 @@ export function getMostRecentUserMessage(ctx: { sessionManager: { getBranch(): u
|
|
|
181
206
|
return sawUserMessage ? { kind: "no-text" } : { kind: "no-user-message" };
|
|
182
207
|
}
|
|
183
208
|
|
|
184
|
-
type ClipboardCommand = {
|
|
185
|
-
name: string;
|
|
186
|
-
args: string[];
|
|
187
|
-
enabled: () => boolean;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const clipboardCommands: ClipboardCommand[] = [
|
|
191
|
-
{ name: "pbcopy", args: [], enabled: () => process.platform === "darwin" },
|
|
192
|
-
{ name: "termux-clipboard-set", args: [], enabled: () => Boolean(process.env.TERMUX_VERSION) },
|
|
193
|
-
{ name: "wl-copy", args: [], enabled: () => true },
|
|
194
|
-
{ name: "xclip", args: ["-selection", "clipboard"], enabled: () => true },
|
|
195
|
-
{ name: "xsel", args: ["--clipboard", "--input"], enabled: () => true },
|
|
196
|
-
];
|
|
197
|
-
|
|
198
|
-
const CLIPBOARD_TIMEOUT_MS = 3000;
|
|
199
|
-
const commandExistsCache = new Map<string, boolean>();
|
|
200
|
-
|
|
201
|
-
function commandExists(command: string): boolean {
|
|
202
|
-
const cached = commandExistsCache.get(command);
|
|
203
|
-
if (cached !== undefined) return cached;
|
|
204
|
-
|
|
205
|
-
const result = spawnSync("sh", ["-c", "command -v \"$1\" >/dev/null 2>&1", "sh", command], {
|
|
206
|
-
stdio: "ignore",
|
|
207
|
-
timeout: CLIPBOARD_TIMEOUT_MS,
|
|
208
|
-
killSignal: "SIGKILL",
|
|
209
|
-
});
|
|
210
|
-
const exists = result.status === 0;
|
|
211
|
-
commandExistsCache.set(command, exists);
|
|
212
|
-
return exists;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function copyWith(command: string, args: string[], text: string): boolean {
|
|
216
|
-
const result = spawnSync(command, args, { input: text, encoding: "utf8", timeout: CLIPBOARD_TIMEOUT_MS, killSignal: "SIGKILL" });
|
|
217
|
-
return !result.error && result.status === 0;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function copyToClipboard(text: string): string | undefined {
|
|
221
|
-
const failedCommands: string[] = [];
|
|
222
|
-
|
|
223
|
-
for (const command of clipboardCommands) {
|
|
224
|
-
if (!command.enabled() || !commandExists(command.name)) continue;
|
|
225
|
-
if (copyWith(command.name, command.args, text)) return undefined;
|
|
226
|
-
failedCommands.push(command.name);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (failedCommands.length > 0) {
|
|
230
|
-
return `Clipboard command${failedCommands.length === 1 ? "" : "s"} failed (${failedCommands.join(", ")})`;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return "No clipboard command found (tried pbcopy, termux-clipboard-set, wl-copy, xclip, xsel)";
|
|
234
|
-
}
|
|
235
|
-
|
|
236
209
|
function isToolMessage(message: CopyableMessage): boolean {
|
|
237
210
|
return message.role === "toolResult" || message.role === "bashExecution";
|
|
238
211
|
}
|
|
@@ -353,7 +326,7 @@ function renderMessageLine(
|
|
|
353
326
|
|
|
354
327
|
function renderPeekLines(message: CopyableMessage, width: number, theme: CopyMessageTheme, format: CopyFormat): string[] {
|
|
355
328
|
const contentWidth = Math.max(1, width - 2);
|
|
356
|
-
const text = formatMessageForCopy(message, format);
|
|
329
|
+
const text = safeDisplayText(formatMessageForCopy(message, format));
|
|
357
330
|
const wrapped = wrapTextWithAnsi(styleRoleText(theme, message.role, text, false), contentWidth);
|
|
358
331
|
const shown = wrapped.slice(0, MAX_PEEK_LINES);
|
|
359
332
|
const remaining = wrapped.length - shown.length;
|
|
@@ -440,8 +413,10 @@ export class CopyMessagePickerState {
|
|
|
440
413
|
format: CopyFormat;
|
|
441
414
|
peek = false;
|
|
442
415
|
private searchAnchorId: string | undefined;
|
|
416
|
+
private readonly messages: CopyableMessage[];
|
|
443
417
|
|
|
444
|
-
constructor(
|
|
418
|
+
constructor(messages: CopyableMessage[], initialFormat: CopyFormat = "raw") {
|
|
419
|
+
this.messages = messages;
|
|
445
420
|
this.format = initialFormat;
|
|
446
421
|
this.visibleMessages = filteredMessages(messages, this.visibility, this.search);
|
|
447
422
|
this.selectedIndex = Math.max(0, this.visibleMessages.length - 1);
|
|
@@ -496,6 +471,13 @@ export class CopyMessagePickerState {
|
|
|
496
471
|
}
|
|
497
472
|
|
|
498
473
|
handleInput(data: string, keybindings?: PickerKeybindings): PickerInputResult {
|
|
474
|
+
// Pi delivers each paste as one input wrapped in bracketed-paste markers.
|
|
475
|
+
if (data.startsWith("\x1b[200~") && data.endsWith("\x1b[201~")) {
|
|
476
|
+
const text = data.slice(6, -6).replace(/\s+/gu, " ");
|
|
477
|
+
if (!isPrintableSearchInput(text)) return "none";
|
|
478
|
+
this.setSearch(this.search + text);
|
|
479
|
+
return "render";
|
|
480
|
+
}
|
|
499
481
|
if (keybindings?.matches(data, "tui.select.up")) {
|
|
500
482
|
this.move(-1);
|
|
501
483
|
return "render";
|
|
@@ -545,7 +527,7 @@ export class CopyMessagePickerState {
|
|
|
545
527
|
return "render";
|
|
546
528
|
}
|
|
547
529
|
if (matchesKey(data, "backspace") || data === "\x7f") {
|
|
548
|
-
this.setSearch(this.search.slice(0, -1));
|
|
530
|
+
this.setSearch(splitGraphemes(this.search).slice(0, -1).join(""));
|
|
549
531
|
return "render";
|
|
550
532
|
}
|
|
551
533
|
const printable = decodeKittyPrintable(data) ?? data;
|
|
@@ -708,17 +690,16 @@ function copyNotificationText(selected: CopyableMessage): string {
|
|
|
708
690
|
return `Copied ${roleLabel(selected.role)} message: “${compactPreview(selected.text, 48)}”`;
|
|
709
691
|
}
|
|
710
692
|
|
|
711
|
-
function copySelectedMessage(ctx: Pick<ExtensionCommandContext, "ui">, selected: CopyableMessage, text = selected.text) {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
ctx.ui.notify(
|
|
715
|
-
|
|
693
|
+
async function copySelectedMessage(ctx: Pick<ExtensionCommandContext, "ui">, selected: CopyableMessage, text = selected.text) {
|
|
694
|
+
try {
|
|
695
|
+
await copyToClipboard(text);
|
|
696
|
+
ctx.ui.notify(copyNotificationText(selected), "info");
|
|
697
|
+
} catch (error) {
|
|
698
|
+
ctx.ui.notify(error instanceof Error ? error.message : "Failed to copy to clipboard", "error");
|
|
716
699
|
}
|
|
717
|
-
|
|
718
|
-
ctx.ui.notify(copyNotificationText(selected), "info");
|
|
719
700
|
}
|
|
720
701
|
|
|
721
|
-
function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionManager" | "ui">, format: CopyFormat) {
|
|
702
|
+
async function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionManager" | "ui">, format: CopyFormat) {
|
|
722
703
|
const result = getMostRecentUserMessage(ctx);
|
|
723
704
|
if (result.kind === "no-user-message") {
|
|
724
705
|
ctx.ui.notify("No user messages found", "warning");
|
|
@@ -729,7 +710,7 @@ function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionMa
|
|
|
729
710
|
return;
|
|
730
711
|
}
|
|
731
712
|
|
|
732
|
-
copySelectedMessage(ctx, result.message, formatMessageForCopy(result.message, format));
|
|
713
|
+
await copySelectedMessage(ctx, result.message, formatMessageForCopy(result.message, format));
|
|
733
714
|
}
|
|
734
715
|
|
|
735
716
|
export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCommand">) {
|
|
@@ -746,7 +727,7 @@ export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCom
|
|
|
746
727
|
|
|
747
728
|
if (parsedArgs.selector === "latest") {
|
|
748
729
|
const latestVisible = latestDefaultMessage(messages);
|
|
749
|
-
if (latestVisible) copySelectedMessage(ctx, latestVisible, formatMessageForCopy(latestVisible, parsedArgs.format));
|
|
730
|
+
if (latestVisible) await copySelectedMessage(ctx, latestVisible, formatMessageForCopy(latestVisible, parsedArgs.format));
|
|
750
731
|
return;
|
|
751
732
|
}
|
|
752
733
|
|
|
@@ -756,7 +737,7 @@ export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCom
|
|
|
756
737
|
ctx.ui.notify(`No default visible message #${parsedArgs.selector.number} (found ${defaultVisibleMessages(messages).length})`, "warning");
|
|
757
738
|
return;
|
|
758
739
|
}
|
|
759
|
-
copySelectedMessage(ctx, selected, formatMessageForCopy(selected, parsedArgs.format));
|
|
740
|
+
await copySelectedMessage(ctx, selected, formatMessageForCopy(selected, parsedArgs.format));
|
|
760
741
|
return;
|
|
761
742
|
}
|
|
762
743
|
|
|
@@ -768,7 +749,7 @@ export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCom
|
|
|
768
749
|
const selected = await pickMessage(ctx, messages, parsedArgs.format);
|
|
769
750
|
if (!selected) return;
|
|
770
751
|
|
|
771
|
-
copySelectedMessage(ctx, selected.message, selected.text);
|
|
752
|
+
await copySelectedMessage(ctx, selected.message, selected.text);
|
|
772
753
|
},
|
|
773
754
|
});
|
|
774
755
|
|
|
@@ -776,7 +757,7 @@ export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCom
|
|
|
776
757
|
description: "Copy the most recent user message to the clipboard",
|
|
777
758
|
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, false),
|
|
778
759
|
handler: async (args, ctx) => {
|
|
779
|
-
copyMostRecentUserMessage(ctx, parseCopyArgs(args).format);
|
|
760
|
+
await copyMostRecentUserMessage(ctx, parseCopyArgs(args).format);
|
|
780
761
|
},
|
|
781
762
|
});
|
|
782
763
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-copy-message",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.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",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
|
-
"node": ">=
|
|
9
|
+
"node": ">=24.15.0"
|
|
10
10
|
},
|
|
11
11
|
"keywords": [
|
|
12
12
|
"pi-package",
|
|
@@ -32,10 +32,11 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/fitchmultz/pi-copy-message#readme",
|
|
34
34
|
"scripts": {
|
|
35
|
-
"test": "
|
|
35
|
+
"test": "node --test tests/*.test.ts",
|
|
36
36
|
"typecheck": "tsc --noEmit",
|
|
37
|
-
"check": "npm test && npm run typecheck",
|
|
38
|
-
"
|
|
37
|
+
"check": "npm test && npm run typecheck && npm pack --dry-run",
|
|
38
|
+
"check:compat": "npm run check",
|
|
39
|
+
"prepublishOnly": "npm run check"
|
|
39
40
|
},
|
|
40
41
|
"files": [
|
|
41
42
|
"extensions",
|
|
@@ -44,18 +45,17 @@
|
|
|
44
45
|
"LICENSE"
|
|
45
46
|
],
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
48
|
-
"@earendil-works/pi-tui": "0.
|
|
49
|
-
"@types/node": "
|
|
50
|
-
"
|
|
51
|
-
"typescript": "^6.0.3"
|
|
48
|
+
"@earendil-works/pi-coding-agent": "0.87.1",
|
|
49
|
+
"@earendil-works/pi-tui": "0.87.1",
|
|
50
|
+
"@types/node": "24.19.0",
|
|
51
|
+
"typescript": "7.0.2"
|
|
52
52
|
},
|
|
53
53
|
"pi": {
|
|
54
54
|
"extensions": [
|
|
55
55
|
"./extensions"
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
|
-
"packageManager": "npm@
|
|
58
|
+
"packageManager": "npm@12.1.0",
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@earendil-works/pi-coding-agent": "*",
|
|
61
61
|
"@earendil-works/pi-tui": "*"
|