pi-copy-message 1.0.7 → 1.0.9
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 +16 -0
- package/README.md +9 -6
- package/extensions/copy-message.ts +117 -25
- package/package.json +3 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 1.0.9 - 2026-07-11
|
|
6
|
+
|
|
7
|
+
- Update the local Pi development baseline and compatibility documentation to 0.80.6.
|
|
8
|
+
- Read current branch-summary, compaction-summary, and visible custom-message session entries in `/copy-message`; hidden custom messages stay excluded.
|
|
9
|
+
- Honor configured Pi selection keybindings before local picker shortcuts and render their effective hints.
|
|
10
|
+
|
|
11
|
+
## 1.0.8 - 2026-06-24
|
|
12
|
+
|
|
13
|
+
- Add `/copy-message` and `/copy-user` argument completions for `latest`/`last`/`newest` and `--with-meta`/`--with-metadata`/`--with-role`.
|
|
14
|
+
- Bound clipboard commands with a 3s timeout so a wedged `xclip`/`wl-copy` daemon cannot hang the command.
|
|
15
|
+
- Drop the obsolete `basic-ftp` npm override (no longer in the dependency tree).
|
|
16
|
+
- Align pi core peer dependencies with package guidance (non-optional wildcard peers; pi aliases them to its bundled copies at load time).
|
|
17
|
+
- Update the local pi development baseline to `0.80.2` and refresh the npm lockfile.
|
|
18
|
+
|
|
3
19
|
## 1.0.7 - 2026-06-23
|
|
4
20
|
|
|
5
21
|
- updated the local pi development baseline to `@earendil-works/pi-coding-agent` / `@earendil-works/pi-tui` `0.80.1` and refreshed the npm lockfile
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A [pi](https://github.com/earendil-works/pi-mono) extension that adds `/copy-mes
|
|
|
15
15
|
- Hides tool/bash messages by default
|
|
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
|
+
- Uses Pi's configured selection bindings for navigation, copy, and cancel
|
|
18
19
|
- Includes fast paths: `/copy-message latest`, `/copy-message last`, and `/copy-message newest`
|
|
19
20
|
- Supports direct numbered copies like `/copy-message 3`
|
|
20
21
|
- Supports metadata copies with `--with-meta`, `--with-metadata`, or `--with-role`
|
|
@@ -90,8 +91,8 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
90
91
|
|
|
91
92
|
| Key | Action |
|
|
92
93
|
|---|---|
|
|
93
|
-
| `↑` | Move to older visible message |
|
|
94
|
-
| `↓` | Move to newer visible message |
|
|
94
|
+
| Configured `tui.select.up` (default: `↑`) | Move to older visible message |
|
|
95
|
+
| Configured `tui.select.down` (default: `↓`) | Move to newer visible message |
|
|
95
96
|
| `Home` | Jump to oldest visible message |
|
|
96
97
|
| `End` | Jump to newest visible message |
|
|
97
98
|
| Type text | Filter visible messages |
|
|
@@ -102,8 +103,8 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
102
103
|
| `Ctrl+T` | Toggle tool/bash messages |
|
|
103
104
|
| `Tab` | Toggle a wrapped preview of the selected message |
|
|
104
105
|
| `Alt+M` | Toggle raw vs metadata copy format |
|
|
105
|
-
| `Enter` | Copy selected message text |
|
|
106
|
-
| `Esc` | Cancel |
|
|
106
|
+
| Configured `tui.select.confirm` (default: `Enter`) | Copy selected message text |
|
|
107
|
+
| Configured `tui.select.cancel` (default: `Esc`/`Ctrl+C`) | Cancel |
|
|
107
108
|
|
|
108
109
|
## Behavior notes
|
|
109
110
|
|
|
@@ -112,6 +113,8 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
112
113
|
- Search preserves your original selected message and restores it when the search is cleared.
|
|
113
114
|
- General search does not match timestamps; use `time:<term>` when you want to search by displayed time.
|
|
114
115
|
- Filter labels honor the active pi theme.
|
|
116
|
+
- 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.
|
|
117
|
+
- Hidden custom messages are excluded in both current `custom_message` entries and legacy message-wrapped entries; only messages with `display: true` can appear or be copied.
|
|
115
118
|
- Copy notifications include the role and a short preview so you can verify what was copied.
|
|
116
119
|
- `/copy-message latest` respects default visibility: user and assistant messages are visible, tool/bash messages are hidden. If only hidden messages exist, it falls back to the newest message so the command still does something useful.
|
|
117
120
|
- `/copy-message` with no direct selector requires interactive TUI mode because the picker is a custom TUI component.
|
|
@@ -119,11 +122,11 @@ Copy with role and timestamp metadata instead of raw text only:
|
|
|
119
122
|
|
|
120
123
|
## Compatibility
|
|
121
124
|
|
|
122
|
-
- Tested with pi 0.80.
|
|
125
|
+
- Tested with pi 0.80.6
|
|
123
126
|
- Supported Node.js range for local repo tooling: `>=22.19.0`
|
|
124
127
|
- `.nvmrc` pins Node 22.19.0 for local development
|
|
125
128
|
|
|
126
|
-
This package keeps pi core packages as
|
|
129
|
+
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
130
|
|
|
128
131
|
## Development
|
|
129
132
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ExtensionAPI, ExtensionCommandContext, KeybindingsManager } 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;
|
|
@@ -109,14 +110,25 @@ function formatTime(timestamp: unknown): string {
|
|
|
109
110
|
function entryToCopyableMessage(entry: unknown): CopyableMessage | undefined {
|
|
110
111
|
if (entry === null || typeof entry !== "object") return undefined;
|
|
111
112
|
const record = entry as Record<string, unknown>;
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
let role: string;
|
|
114
|
+
let text: string;
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
if (record.type === "message" && record.message !== null && typeof record.message === "object") {
|
|
117
|
+
const message = record.message as Record<string, unknown>;
|
|
118
|
+
role = typeof message.role === "string" ? message.role : "message";
|
|
119
|
+
if (role === "custom" && message.display !== true) return undefined;
|
|
120
|
+
text = textFromMessage(message);
|
|
121
|
+
} else if (record.type === "branch_summary" || record.type === "compaction") {
|
|
122
|
+
role = record.type === "branch_summary" ? "branchSummary" : "compactionSummary";
|
|
123
|
+
text = typeof record.summary === "string" ? record.summary : "";
|
|
124
|
+
} else if (record.type === "custom_message" && record.display === true) {
|
|
125
|
+
role = "custom";
|
|
126
|
+
text = textFromContent(record.content);
|
|
127
|
+
} else {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
119
130
|
|
|
131
|
+
if (!text.trim()) return undefined;
|
|
120
132
|
return {
|
|
121
133
|
id: typeof record.id === "string" ? record.id : "unknown",
|
|
122
134
|
role,
|
|
@@ -183,20 +195,25 @@ const clipboardCommands: ClipboardCommand[] = [
|
|
|
183
195
|
{ name: "xsel", args: ["--clipboard", "--input"], enabled: () => true },
|
|
184
196
|
];
|
|
185
197
|
|
|
198
|
+
const CLIPBOARD_TIMEOUT_MS = 3000;
|
|
186
199
|
const commandExistsCache = new Map<string, boolean>();
|
|
187
200
|
|
|
188
201
|
function commandExists(command: string): boolean {
|
|
189
202
|
const cached = commandExistsCache.get(command);
|
|
190
203
|
if (cached !== undefined) return cached;
|
|
191
204
|
|
|
192
|
-
const result = spawnSync("sh", ["-c", "command -v \"$1\" >/dev/null 2>&1", "sh", command], {
|
|
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
|
+
});
|
|
193
210
|
const exists = result.status === 0;
|
|
194
211
|
commandExistsCache.set(command, exists);
|
|
195
212
|
return exists;
|
|
196
213
|
}
|
|
197
214
|
|
|
198
215
|
function copyWith(command: string, args: string[], text: string): boolean {
|
|
199
|
-
const result = spawnSync(command, args, { input: text, encoding: "utf8" });
|
|
216
|
+
const result = spawnSync(command, args, { input: text, encoding: "utf8", timeout: CLIPBOARD_TIMEOUT_MS, killSignal: "SIGKILL" });
|
|
200
217
|
return !result.error && result.status === 0;
|
|
201
218
|
}
|
|
202
219
|
|
|
@@ -344,10 +361,58 @@ function renderPeekLines(message: CopyableMessage, width: number, theme: CopyMes
|
|
|
344
361
|
return lines;
|
|
345
362
|
}
|
|
346
363
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
364
|
+
type PickerKeybindings = Pick<KeybindingsManager, "getKeys" | "matches">;
|
|
365
|
+
|
|
366
|
+
function bindingHint(keybindings: PickerKeybindings | undefined, action: "up" | "down" | "confirm" | "cancel"): string {
|
|
367
|
+
const id = `tui.select.${action}` as const;
|
|
368
|
+
if (!keybindings) return { up: "up", down: "down", confirm: "enter", cancel: "escape/ctrl+c" }[action];
|
|
369
|
+
const keys = keybindings.getKeys(id);
|
|
370
|
+
return keys.reduce((shortest, key) => visibleWidth(key) < visibleWidth(shortest) ? key : shortest, keys[0] ?? "unbound");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function helpLines(width: number, keybindings?: PickerKeybindings): string[] {
|
|
374
|
+
const up = bindingHint(keybindings, "up");
|
|
375
|
+
const down = bindingHint(keybindings, "down");
|
|
376
|
+
const confirm = bindingHint(keybindings, "confirm");
|
|
377
|
+
const cancel = bindingHint(keybindings, "cancel");
|
|
378
|
+
const available = (data: string) =>
|
|
379
|
+
!keybindings ||
|
|
380
|
+
!(keybindings.matches(data, "tui.select.up") ||
|
|
381
|
+
keybindings.matches(data, "tui.select.down") ||
|
|
382
|
+
keybindings.matches(data, "tui.select.confirm") ||
|
|
383
|
+
keybindings.matches(data, "tui.select.cancel"));
|
|
384
|
+
const peek = available("\t") ? "Tab peek" : undefined;
|
|
385
|
+
const filters = [
|
|
386
|
+
{ hint: "U", data: "\x15" },
|
|
387
|
+
{ hint: "A", data: "\x01" },
|
|
388
|
+
{ hint: "T", data: "\x14" },
|
|
389
|
+
].filter(({ data }) => available(data));
|
|
390
|
+
const filterHint = filters.length > 0 ? `Ctrl+${filters.map(({ hint }) => hint).join("/")} filters` : undefined;
|
|
391
|
+
const meta = available("\x1bm") ? "Alt+M meta" : undefined;
|
|
392
|
+
const jumps = [
|
|
393
|
+
{ hint: "Home", data: "\x1b[H" },
|
|
394
|
+
{ hint: "End", data: "\x1b[F" },
|
|
395
|
+
].filter(({ data }) => available(data));
|
|
396
|
+
const jumpHint = jumps.length > 0 ? `${jumps.map(({ hint }) => hint).join("/")} jump` : undefined;
|
|
397
|
+
const join = (...hints: Array<string | undefined>) => hints.filter(Boolean).join(" · ");
|
|
398
|
+
const core = width < 74 ? [`${up}/${down} nav`, `${confirm} copy`, `${cancel} cancel`] : [`${up} older`, `${down} newer`, `${confirm} copy`, `${cancel} cancel`];
|
|
399
|
+
const optional: string[] = [];
|
|
400
|
+
|
|
401
|
+
if (visibleWidth(join(...core)) > width) {
|
|
402
|
+
const lines: string[] = [];
|
|
403
|
+
for (const hint of [`${up} older`, `${down} newer`, `${confirm} copy`, `${cancel} cancel`]) {
|
|
404
|
+
const candidate = join(lines.at(-1), hint);
|
|
405
|
+
if (lines.length === 0 || visibleWidth(candidate) > width) lines.push(hint);
|
|
406
|
+
else lines[lines.length - 1] = candidate;
|
|
407
|
+
}
|
|
408
|
+
return lines;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
for (const hint of width < 74 ? [jumpHint, peek, filterHint, meta] : ["type search", jumpHint, peek, filterHint, meta]) {
|
|
412
|
+
if (hint && visibleWidth(join(...optional, hint, ...core)) <= width) optional.push(hint);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return [join(...optional, ...core)];
|
|
351
416
|
}
|
|
352
417
|
|
|
353
418
|
type PickerInputResult = "copy" | "cancel" | "render" | "none";
|
|
@@ -380,7 +445,7 @@ export class CopyMessagePickerState {
|
|
|
380
445
|
return selected ? formatMessageForCopy(selected, this.format) : undefined;
|
|
381
446
|
}
|
|
382
447
|
|
|
383
|
-
render(width: number, theme: CopyMessageTheme): string[] {
|
|
448
|
+
render(width: number, theme: CopyMessageTheme, keybindings?: PickerKeybindings): string[] {
|
|
384
449
|
const maxVisible = Math.min(this.visibleMessages.length, MAX_VISIBLE_MESSAGES);
|
|
385
450
|
const start = maxVisible === 0 ? 0 : Math.max(0, Math.min(this.selectedIndex - maxVisible + 1, this.visibleMessages.length - maxVisible));
|
|
386
451
|
const end = Math.min(this.visibleMessages.length, start + maxVisible);
|
|
@@ -411,12 +476,24 @@ export class CopyMessagePickerState {
|
|
|
411
476
|
const position = this.visibleMessages.length === 0 ? "0/0" : `${this.selectedIndex + 1}/${this.visibleMessages.length}`;
|
|
412
477
|
lines.push(`${theme.fg("dim", `(${position})`)} · ${userState} · ${assistantState} · ${toolState} · ${formatState} · ${searchState}`);
|
|
413
478
|
lines.push("");
|
|
414
|
-
lines.push(hotkeyHint(theme,
|
|
479
|
+
lines.push(...helpLines(width, keybindings).map((line) => hotkeyHint(theme, line)));
|
|
415
480
|
lines.push("");
|
|
416
481
|
return lines.map((line) => truncateToWidth(line, width, ""));
|
|
417
482
|
}
|
|
418
483
|
|
|
419
|
-
handleInput(data: string): PickerInputResult {
|
|
484
|
+
handleInput(data: string, keybindings?: PickerKeybindings): PickerInputResult {
|
|
485
|
+
if (keybindings?.matches(data, "tui.select.up")) {
|
|
486
|
+
this.move(-1);
|
|
487
|
+
return "render";
|
|
488
|
+
}
|
|
489
|
+
if (keybindings?.matches(data, "tui.select.down")) {
|
|
490
|
+
this.move(1);
|
|
491
|
+
return "render";
|
|
492
|
+
}
|
|
493
|
+
if (keybindings?.matches(data, "tui.select.confirm")) {
|
|
494
|
+
return this.visibleMessages.length > 0 ? "copy" : "none";
|
|
495
|
+
}
|
|
496
|
+
if (keybindings?.matches(data, "tui.select.cancel")) return "cancel";
|
|
420
497
|
if (matchesKey(data, "ctrl+t")) {
|
|
421
498
|
this.visibility.showTools = !this.visibility.showTools;
|
|
422
499
|
this.refreshMessages();
|
|
@@ -448,11 +525,11 @@ export class CopyMessagePickerState {
|
|
|
448
525
|
this.setSearch(this.search + data);
|
|
449
526
|
return "render";
|
|
450
527
|
}
|
|
451
|
-
if (matchesKey(data, "up")) {
|
|
528
|
+
if (!keybindings && matchesKey(data, "up")) {
|
|
452
529
|
this.move(-1);
|
|
453
530
|
return "render";
|
|
454
531
|
}
|
|
455
|
-
if (matchesKey(data, "down")) {
|
|
532
|
+
if (!keybindings && matchesKey(data, "down")) {
|
|
456
533
|
this.move(1);
|
|
457
534
|
return "render";
|
|
458
535
|
}
|
|
@@ -464,10 +541,10 @@ export class CopyMessagePickerState {
|
|
|
464
541
|
this.jumpToBottom();
|
|
465
542
|
return "render";
|
|
466
543
|
}
|
|
467
|
-
if (matchesKey(data, "enter") || matchesKey(data, "return")) {
|
|
544
|
+
if (!keybindings && (matchesKey(data, "enter") || matchesKey(data, "return"))) {
|
|
468
545
|
return this.visibleMessages.length > 0 ? "copy" : "none";
|
|
469
546
|
}
|
|
470
|
-
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
547
|
+
if (!keybindings && (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))) {
|
|
471
548
|
return "cancel";
|
|
472
549
|
}
|
|
473
550
|
return "none";
|
|
@@ -513,6 +590,9 @@ export class CopyMessagePickerState {
|
|
|
513
590
|
}
|
|
514
591
|
}
|
|
515
592
|
|
|
593
|
+
const COPY_METADATA_FLAGS = ["--with-meta", "--with-metadata", "--with-role"];
|
|
594
|
+
const COPY_LATEST_SELECTORS = ["latest", "last", "newest"];
|
|
595
|
+
|
|
516
596
|
type ParsedCopyMessageArgs = {
|
|
517
597
|
format: CopyFormat;
|
|
518
598
|
selector?: "latest" | { number: number };
|
|
@@ -521,11 +601,11 @@ type ParsedCopyMessageArgs = {
|
|
|
521
601
|
function parseCopyArgs(args: string | undefined): ParsedCopyMessageArgs {
|
|
522
602
|
const result: ParsedCopyMessageArgs = { format: "raw" };
|
|
523
603
|
for (const token of (args ?? "").trim().toLowerCase().split(/\s+/).filter(Boolean)) {
|
|
524
|
-
if (token
|
|
604
|
+
if (COPY_METADATA_FLAGS.includes(token)) {
|
|
525
605
|
result.format = "metadata";
|
|
526
606
|
continue;
|
|
527
607
|
}
|
|
528
|
-
if (token
|
|
608
|
+
if (COPY_LATEST_SELECTORS.includes(token)) {
|
|
529
609
|
result.selector = "latest";
|
|
530
610
|
continue;
|
|
531
611
|
}
|
|
@@ -536,16 +616,26 @@ function parseCopyArgs(args: string | undefined): ParsedCopyMessageArgs {
|
|
|
536
616
|
return result;
|
|
537
617
|
}
|
|
538
618
|
|
|
619
|
+
export function copyArgumentCompletions(prefix: string, includeSelectors: boolean): AutocompleteItem[] | null {
|
|
620
|
+
if (/^\d+$/.test(prefix)) return null;
|
|
621
|
+
const candidates = includeSelectors ? [...COPY_LATEST_SELECTORS, ...COPY_METADATA_FLAGS] : COPY_METADATA_FLAGS;
|
|
622
|
+
const normalized = prefix.toLowerCase();
|
|
623
|
+
const items = candidates
|
|
624
|
+
.filter((candidate) => candidate.startsWith(normalized))
|
|
625
|
+
.map((candidate) => ({ value: candidate, label: candidate }));
|
|
626
|
+
return items.length > 0 ? items : null;
|
|
627
|
+
}
|
|
628
|
+
|
|
539
629
|
async function pickMessage(ctx: ExtensionCommandContext, messages: CopyableMessage[], initialFormat: CopyFormat) {
|
|
540
|
-
return ctx.ui.custom<{ message: CopyableMessage; text: string } | null>((tui, theme,
|
|
630
|
+
return ctx.ui.custom<{ message: CopyableMessage; text: string } | null>((tui, theme, keybindings, done) => {
|
|
541
631
|
const state = new CopyMessagePickerState(messages, initialFormat);
|
|
542
632
|
return {
|
|
543
633
|
render(width: number) {
|
|
544
|
-
return state.render(width, theme);
|
|
634
|
+
return state.render(width, theme, keybindings);
|
|
545
635
|
},
|
|
546
636
|
invalidate() {},
|
|
547
637
|
handleInput(data: string) {
|
|
548
|
-
const result = state.handleInput(data);
|
|
638
|
+
const result = state.handleInput(data, keybindings);
|
|
549
639
|
if (result === "copy") {
|
|
550
640
|
const selected = state.selectedMessage();
|
|
551
641
|
const text = state.selectedCopyText();
|
|
@@ -593,6 +683,7 @@ function copyMostRecentUserMessage(ctx: Pick<ExtensionCommandContext, "sessionMa
|
|
|
593
683
|
export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCommand">) {
|
|
594
684
|
pi.registerCommand("copy-message", {
|
|
595
685
|
description: "Select a session message and copy its text to the clipboard",
|
|
686
|
+
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, true),
|
|
596
687
|
handler: async (args, ctx) => {
|
|
597
688
|
const parsedArgs = parseCopyArgs(args);
|
|
598
689
|
const messages = collectCopyableMessages(ctx);
|
|
@@ -631,6 +722,7 @@ export default function copyMessageExtension(pi: Pick<ExtensionAPI, "registerCom
|
|
|
631
722
|
|
|
632
723
|
pi.registerCommand("copy-user", {
|
|
633
724
|
description: "Copy the most recent user message to the clipboard",
|
|
725
|
+
getArgumentCompletions: (argumentPrefix) => copyArgumentCompletions(argumentPrefix, false),
|
|
634
726
|
handler: async (args, ctx) => {
|
|
635
727
|
copyMostRecentUserMessage(ctx, parseCopyArgs(args).format);
|
|
636
728
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-copy-message",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
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.80.
|
|
48
|
-
"@earendil-works/pi-tui": "^0.80.
|
|
47
|
+
"@earendil-works/pi-coding-agent": "^0.80.6",
|
|
48
|
+
"@earendil-works/pi-tui": "^0.80.6",
|
|
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
|
}
|