pi-copy-message 1.0.6 → 1.0.8
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 +14 -0
- package/README.md +2 -2
- package/extensions/copy-message.ts +25 -4
- package/package.json +3 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.8 - 2026-06-24
|
|
4
|
+
|
|
5
|
+
- Add `/copy-message` and `/copy-user` argument completions for `latest`/`last`/`newest` and `--with-meta`/`--with-metadata`/`--with-role`.
|
|
6
|
+
- Bound clipboard commands with a 3s timeout so a wedged `xclip`/`wl-copy` daemon cannot hang the command.
|
|
7
|
+
- Drop the obsolete `basic-ftp` npm override (no longer in the dependency tree).
|
|
8
|
+
- Align pi core peer dependencies with package guidance (non-optional wildcard peers; pi aliases them to its bundled copies at load time).
|
|
9
|
+
- Update the local pi development baseline to `0.80.2` and refresh the npm lockfile.
|
|
10
|
+
|
|
11
|
+
## 1.0.7 - 2026-06-23
|
|
12
|
+
|
|
13
|
+
- updated the local pi development baseline to `@earendil-works/pi-coding-agent` / `@earendil-works/pi-tui` `0.80.1` and refreshed the npm lockfile
|
|
14
|
+
- refreshed the README compatibility note for pi `0.80.1`
|
|
15
|
+
- reviewed the Pi 0.80.0/0.80.1 changelog; no runtime source migration was required
|
|
16
|
+
|
|
3
17
|
## 1.0.6 - 2026-06-22
|
|
4
18
|
|
|
5
19
|
- updated the local pi development baseline to `@earendil-works/pi-coding-agent` / `@earendil-works/pi-tui` `0.79.10` and refreshed the npm lockfile
|
package/README.md
CHANGED
|
@@ -119,11 +119,11 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
119
119
|
|
|
120
120
|
## Compatibility
|
|
121
121
|
|
|
122
|
-
- Tested with pi 0.
|
|
122
|
+
- Tested with pi 0.80.2
|
|
123
123
|
- Supported Node.js range for local repo tooling: `>=22.19.0`
|
|
124
124
|
- `.nvmrc` pins Node 22.19.0 for local development
|
|
125
125
|
|
|
126
|
-
This package keeps pi core packages as
|
|
126
|
+
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.
|
|
127
127
|
|
|
128
128
|
## Development
|
|
129
129
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
3
4
|
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
4
5
|
|
|
5
6
|
const MAX_VISIBLE_MESSAGES = 8;
|
|
@@ -183,20 +184,25 @@ const clipboardCommands: ClipboardCommand[] = [
|
|
|
183
184
|
{ name: "xsel", args: ["--clipboard", "--input"], enabled: () => true },
|
|
184
185
|
];
|
|
185
186
|
|
|
187
|
+
const CLIPBOARD_TIMEOUT_MS = 3000;
|
|
186
188
|
const commandExistsCache = new Map<string, boolean>();
|
|
187
189
|
|
|
188
190
|
function commandExists(command: string): boolean {
|
|
189
191
|
const cached = commandExistsCache.get(command);
|
|
190
192
|
if (cached !== undefined) return cached;
|
|
191
193
|
|
|
192
|
-
const result = spawnSync("sh", ["-c", "command -v \"$1\" >/dev/null 2>&1", "sh", command], {
|
|
194
|
+
const result = spawnSync("sh", ["-c", "command -v \"$1\" >/dev/null 2>&1", "sh", command], {
|
|
195
|
+
stdio: "ignore",
|
|
196
|
+
timeout: CLIPBOARD_TIMEOUT_MS,
|
|
197
|
+
killSignal: "SIGKILL",
|
|
198
|
+
});
|
|
193
199
|
const exists = result.status === 0;
|
|
194
200
|
commandExistsCache.set(command, exists);
|
|
195
201
|
return exists;
|
|
196
202
|
}
|
|
197
203
|
|
|
198
204
|
function copyWith(command: string, args: string[], text: string): boolean {
|
|
199
|
-
const result = spawnSync(command, args, { input: text, encoding: "utf8" });
|
|
205
|
+
const result = spawnSync(command, args, { input: text, encoding: "utf8", timeout: CLIPBOARD_TIMEOUT_MS, killSignal: "SIGKILL" });
|
|
200
206
|
return !result.error && result.status === 0;
|
|
201
207
|
}
|
|
202
208
|
|
|
@@ -513,6 +519,9 @@ export class CopyMessagePickerState {
|
|
|
513
519
|
}
|
|
514
520
|
}
|
|
515
521
|
|
|
522
|
+
const COPY_METADATA_FLAGS = ["--with-meta", "--with-metadata", "--with-role"];
|
|
523
|
+
const COPY_LATEST_SELECTORS = ["latest", "last", "newest"];
|
|
524
|
+
|
|
516
525
|
type ParsedCopyMessageArgs = {
|
|
517
526
|
format: CopyFormat;
|
|
518
527
|
selector?: "latest" | { number: number };
|
|
@@ -521,11 +530,11 @@ type ParsedCopyMessageArgs = {
|
|
|
521
530
|
function parseCopyArgs(args: string | undefined): ParsedCopyMessageArgs {
|
|
522
531
|
const result: ParsedCopyMessageArgs = { format: "raw" };
|
|
523
532
|
for (const token of (args ?? "").trim().toLowerCase().split(/\s+/).filter(Boolean)) {
|
|
524
|
-
if (token
|
|
533
|
+
if (COPY_METADATA_FLAGS.includes(token)) {
|
|
525
534
|
result.format = "metadata";
|
|
526
535
|
continue;
|
|
527
536
|
}
|
|
528
|
-
if (token
|
|
537
|
+
if (COPY_LATEST_SELECTORS.includes(token)) {
|
|
529
538
|
result.selector = "latest";
|
|
530
539
|
continue;
|
|
531
540
|
}
|
|
@@ -536,6 +545,16 @@ function parseCopyArgs(args: string | undefined): ParsedCopyMessageArgs {
|
|
|
536
545
|
return result;
|
|
537
546
|
}
|
|
538
547
|
|
|
548
|
+
export function copyArgumentCompletions(prefix: string, includeSelectors: boolean): AutocompleteItem[] | null {
|
|
549
|
+
if (/^\d+$/.test(prefix)) return null;
|
|
550
|
+
const candidates = includeSelectors ? [...COPY_LATEST_SELECTORS, ...COPY_METADATA_FLAGS] : COPY_METADATA_FLAGS;
|
|
551
|
+
const normalized = prefix.toLowerCase();
|
|
552
|
+
const items = candidates
|
|
553
|
+
.filter((candidate) => candidate.startsWith(normalized))
|
|
554
|
+
.map((candidate) => ({ value: candidate, label: candidate }));
|
|
555
|
+
return items.length > 0 ? items : null;
|
|
556
|
+
}
|
|
557
|
+
|
|
539
558
|
async function pickMessage(ctx: ExtensionCommandContext, messages: CopyableMessage[], initialFormat: CopyFormat) {
|
|
540
559
|
return ctx.ui.custom<{ message: CopyableMessage; text: string } | null>((tui, theme, _keybindings, done) => {
|
|
541
560
|
const state = new CopyMessagePickerState(messages, initialFormat);
|
|
@@ -593,6 +612,7 @@ function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionMa
|
|
|
593
612
|
export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCommand">) {
|
|
594
613
|
pi.registerCommand("copy-message", {
|
|
595
614
|
description: "Select a session message and copy its text to the clipboard",
|
|
615
|
+
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, true),
|
|
596
616
|
handler: async (args, ctx) => {
|
|
597
617
|
const parsedArgs = parseCopyArgs(args);
|
|
598
618
|
const messages = collectCopyableMessages(ctx);
|
|
@@ -631,6 +651,7 @@ export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCom
|
|
|
631
651
|
|
|
632
652
|
pi.registerCommand("copy-user", {
|
|
633
653
|
description: "Copy the most recent user message to the clipboard",
|
|
654
|
+
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, false),
|
|
634
655
|
handler: async (args, ctx) => {
|
|
635
656
|
copyMostRecentUserMessage(ctx, parseCopyArgs(args).format);
|
|
636
657
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-copy-message",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
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",
|
|
@@ -44,15 +44,12 @@
|
|
|
44
44
|
"LICENSE"
|
|
45
45
|
],
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
48
|
-
"@earendil-works/pi-tui": "^0.
|
|
47
|
+
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
48
|
+
"@earendil-works/pi-tui": "^0.80.2",
|
|
49
49
|
"@types/node": "^25.9.1",
|
|
50
50
|
"tsx": "^4.22.4",
|
|
51
51
|
"typescript": "^6.0.3"
|
|
52
52
|
},
|
|
53
|
-
"overrides": {
|
|
54
|
-
"basic-ftp": "6.0.1"
|
|
55
|
-
},
|
|
56
53
|
"pi": {
|
|
57
54
|
"extensions": [
|
|
58
55
|
"./extensions"
|
|
@@ -62,13 +59,5 @@
|
|
|
62
59
|
"peerDependencies": {
|
|
63
60
|
"@earendil-works/pi-coding-agent": "*",
|
|
64
61
|
"@earendil-works/pi-tui": "*"
|
|
65
|
-
},
|
|
66
|
-
"peerDependenciesMeta": {
|
|
67
|
-
"@earendil-works/pi-coding-agent": {
|
|
68
|
-
"optional": true
|
|
69
|
-
},
|
|
70
|
-
"@earendil-works/pi-tui": {
|
|
71
|
-
"optional": true
|
|
72
|
-
}
|
|
73
62
|
}
|
|
74
63
|
}
|