@xzzpig/pi-starline 0.4.1 → 0.5.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/README.md CHANGED
@@ -176,7 +176,7 @@ Settings live in `~/.pi/agent/starline.json`. The file is optional — anything
176
176
  {
177
177
  "footerStyle": "pill",
178
178
  "colors": { "gitBranch": "bold purple" },
179
- "mouse": { "clickToExpandTools": false }
179
+ "mouse": { "clickToToggleExpandable": false }
180
180
  }
181
181
  ```
182
182
 
@@ -109,8 +109,8 @@ export type MouseConfig = {
109
109
  wheelRouting: boolean;
110
110
  /** Show a "copied to clipboard" notice for a copy Starline itself performs. */
111
111
  copyNotice: boolean;
112
- /** Clicking a tool box's `ctrl+o to expand` hint expands that one box. */
113
- clickToExpandTools: boolean;
112
+ /** Click any collapsible box (a tool box, a custom message, an expandable text) to expand or collapse it. */
113
+ clickToToggleExpandable: boolean;
114
114
  /** Copy transcript selections without the painted rails, rules and frames. */
115
115
  transcriptCleanCopy: boolean;
116
116
  };
@@ -119,7 +119,10 @@ const FIXED_EDITOR_KEY_MAP: Record<string, keyof MouseConfig> = {
119
119
  enabled: "enabled",
120
120
  mouseScroll: "wheelRouting",
121
121
  copyNotice: "copyNotice",
122
- clickToExpandTools: "clickToExpandTools",
122
+ // The 0.2.x-era name for click-to-toggle; Pi 0.86.0 owns the click itself
123
+ // for tool boxes, but the fork keeps the general collapsible-box toggle,
124
+ // so the old value is carried to the renamed key instead of being dropped.
125
+ clickToExpandTools: "clickToToggleExpandable",
123
126
  };
124
127
 
125
128
  /**
@@ -418,7 +421,7 @@ export const defaultConfig: PolishedTuiConfig = {
418
421
  enabled: true,
419
422
  wheelRouting: true,
420
423
  copyNotice: true,
421
- clickToExpandTools: true,
424
+ clickToToggleExpandable: true,
422
425
  transcriptCleanCopy: true,
423
426
  },
424
427
  };
@@ -838,10 +841,14 @@ function normalizeMouseConfig(record: Record<string, unknown>): MouseConfig {
838
841
  : defaultConfig.mouse.wheelRouting,
839
842
  copyNotice:
840
843
  typeof record.copyNotice === "boolean" ? record.copyNotice : defaultConfig.mouse.copyNotice,
841
- clickToExpandTools:
842
- typeof record.clickToExpandTools === "boolean"
843
- ? record.clickToExpandTools
844
- : defaultConfig.mouse.clickToExpandTools,
844
+ clickToToggleExpandable:
845
+ // The new key wins outright; a config carrying the pre-rename
846
+ // mouse.clickToExpandTools value is honoured as a fallback.
847
+ typeof record.clickToToggleExpandable === "boolean"
848
+ ? record.clickToToggleExpandable
849
+ : typeof record.clickToExpandTools === "boolean"
850
+ ? record.clickToExpandTools
851
+ : defaultConfig.mouse.clickToToggleExpandable,
845
852
  transcriptCleanCopy:
846
853
  typeof record.transcriptCleanCopy === "boolean"
847
854
  ? record.transcriptCleanCopy
@@ -1306,8 +1313,8 @@ export function saveMousePatch(patch: Partial<MouseConfig>, path = configPath):
1306
1313
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
1307
1314
  ...(patch.wheelRouting !== undefined ? { wheelRouting: patch.wheelRouting } : {}),
1308
1315
  ...(patch.copyNotice !== undefined ? { copyNotice: patch.copyNotice } : {}),
1309
- ...(patch.clickToExpandTools !== undefined
1310
- ? { clickToExpandTools: patch.clickToExpandTools }
1316
+ ...(patch.clickToToggleExpandable !== undefined
1317
+ ? { clickToToggleExpandable: patch.clickToToggleExpandable }
1311
1318
  : {}),
1312
1319
  ...(patch.transcriptCleanCopy !== undefined
1313
1320
  ? { transcriptCleanCopy: patch.transcriptCleanCopy }
@@ -22,7 +22,7 @@ import type { MouseEventsApi } from "@xzzpig/pi-mouse-events/api";
22
22
  * MUST match `MOUSE_EVENTS_API_KEY` in `@xzzpig/pi-mouse-events/api` — the
23
23
  * contract test above is what keeps that true.
24
24
  */
25
- const MOUSE_EVENTS_API_KEY = Symbol.for("pi-mouse-events.api.v1");
25
+ const MOUSE_EVENTS_API_KEY = Symbol.for("pi-mouse-events.api.v2");
26
26
 
27
27
  export function getMouseEventsApi(): MouseEventsApi | undefined {
28
28
  const candidate = (globalThis as Record<symbol, unknown>)[MOUSE_EVENTS_API_KEY] as
@@ -31,7 +31,7 @@ export function getMouseEventsApi(): MouseEventsApi | undefined {
31
31
  // The symbol key pins the contract name, not its shape: reject an object
32
32
  // from a different contract major so callers never dereference a
33
33
  // mismatched surface (the key is only bumped together with the version).
34
- if (!candidate || candidate.version !== 1) {
34
+ if (!candidate || candidate.version !== 2) {
35
35
  return undefined;
36
36
  }
37
37
  return candidate;
@@ -29,7 +29,7 @@ export type MouseCapability = MouseReceiverCapabilities | "copySlot";
29
29
 
30
30
  export type MouseFeature =
31
31
  | "selectionHint"
32
- | "clickToExpandTools"
32
+ | "clickToToggleExpandable"
33
33
  | "editorWheelScroll"
34
34
  | "editorClickToCaret"
35
35
  | "editorBufferCopy"
@@ -77,7 +77,7 @@ const REQUIREMENTS: Record<MouseFeature, readonly MouseCapability[]> = {
77
77
  // whatever tool box happens to sit behind it — and `requestRender`, because
78
78
  // consuming the press means Pi never reaches its own repaint for that
79
79
  // event.
80
- clickToExpandTools: ["hasOverlay", "requestRender"],
80
+ clickToToggleExpandable: ["hasOverlay", "requestRender"],
81
81
  // Same shape as click-to-expand: the notch arrives through the handler
82
82
  // slot, `hasOverlay` keeps a notch aimed at a dialog from scrolling the
83
83
  // draft hidden behind it, and consuming the notch means `requestRender` is
@@ -113,7 +113,7 @@ const REQUIREMENTS: Record<MouseFeature, readonly MouseCapability[]> = {
113
113
  ],
114
114
  };
115
115
 
116
- function isCallable(receiver: object, name: string): boolean {
116
+ function isCallable<T extends object>(receiver: T, name: string): boolean {
117
117
  try {
118
118
  return typeof (receiver as Record<string, unknown>)[name] === "function";
119
119
  } catch {
@@ -122,7 +122,7 @@ function isCallable(receiver: object, name: string): boolean {
122
122
  }
123
123
  }
124
124
 
125
- export function probeCapabilities(receiver: object): ReadonlySet<MouseCapability> {
125
+ export function probeCapabilities<T extends object>(receiver: T): ReadonlySet<MouseCapability> {
126
126
  const found = new Set<MouseCapability>();
127
127
  for (const capability of CAPABILITIES) {
128
128
  if (capability === "copySlot") continue;
@@ -21,7 +21,7 @@
21
21
  * located and how the live editor is reached; the scroll itself is
22
22
  * `editor-scroll.ts`.
23
23
  *
24
- * `clickToExpandTools` → `addMouseHandler`
24
+ * `clickToToggleExpandable` → `addMouseHandler`
25
25
  * - A left-button press anywhere on an expandable component records the
26
26
  * candidate; when the button is released on the same cell with no motion in
27
27
  * between — a plain click — that one component is toggled and the release
@@ -89,8 +89,9 @@ import { activeEditor, pointerOverEditor } from "./editor-mouse";
89
89
  import { scrollEditorBy } from "./editor-scroll";
90
90
  import { editorVisualRowCount } from "./editor-text-cursor";
91
91
  import { type BoxLike, scrollContentLinesFor } from "./hit-test";
92
+ import { keyTextFor } from "./key-text";
92
93
  import { externalEditorName, selectionHintText } from "./selection-state";
93
- import { type ExpandTarget, expandKeyText, expandTargetAt, keyTextFor } from "./tool-box";
94
+ import { type ExpandTarget, expandKeyText, expandTargetAt } from "./tool-box";
94
95
  import { cleanTranscriptRows } from "./transcript-copy";
95
96
 
96
97
  /** Pi's interrupt chord — refused by the range-delete branch, never consumed. */
@@ -664,7 +665,7 @@ function expandClickTarget(
664
665
  deps: InstallMouseDeps,
665
666
  ): ExpandTarget | undefined {
666
667
  try {
667
- if (!deps.getConfig().mouse.clickToExpandTools) return undefined;
668
+ if (!deps.getConfig().mouse.clickToToggleExpandable) return undefined;
668
669
  // Pi resolves no scroll view while an overlay is up, so neither does this
669
670
  // — a click on a dialog must not reach the transcript behind it.
670
671
  if (receiver.hasOverlay?.()) return undefined;
@@ -806,7 +807,7 @@ export function installMouseFeaturesOn(
806
807
  api.addMouseHandler(
807
808
  ({ event, tui }) => {
808
809
  const receiver = bindReceiver(tui);
809
- if (!receiver || !featureOn("clickToExpandTools")) {
810
+ if (!receiver || !featureOn("clickToToggleExpandable")) {
810
811
  expandPress = undefined;
811
812
  return undefined;
812
813
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The keys spelled the way Pi's on-screen hints spell them.
3
+ *
4
+ * The primary source is `pi-coding-agent`'s own exported `keyText` — literally
5
+ * the function `keyHint` calls to build the hint text the UI shows, so there
6
+ * is no second formatting rule to drift (it is `keyText` that turns `alt`
7
+ * into `option` on macOS and joins alternatives with "/"), and no second
8
+ * keybinding registry to disagree.
9
+ *
10
+ * That last point is not theoretical. `getKeybindings()` is a *singleton* per
11
+ * copy of pi-tui, and a copy is per `node_modules` tree: in this repo
12
+ * `pi-coding-agent` nests its own pi-tui, so the registry Starline's direct
13
+ * import reaches is not the registry the hints are rendered from. Production
14
+ * resolves both to Pi's own copy, but reading the value through the same
15
+ * package that renders it makes the question moot instead of assumed.
16
+ *
17
+ * pi-tui's registry is kept as a fallback for the case where `keyText` yields
18
+ * nothing. Both routes return "" when the binding is unbound, and callers
19
+ * read "" as "show no key" rather than as a wildcard.
20
+ */
21
+
22
+ import { keyText } from "@earendil-works/pi-coding-agent";
23
+ import { getKeybindings } from "@earendil-works/pi-tui";
24
+
25
+ /**
26
+ * `keyText(keybinding)` with the registry fallback, shared by every feature
27
+ * that quotes a key on screen. See the module header for why the primary
28
+ * route is pi-coding-agent's own function rather than this repo's registry.
29
+ */
30
+ export function keyTextFor(keybinding: string): string {
31
+ try {
32
+ const rendered = keyText(keybinding as never);
33
+ if (typeof rendered === "string" && rendered.length > 0) return rendered;
34
+ } catch {
35
+ // A Pi build that has moved this function is one where quoting the key
36
+ // simply shows nothing; it is never a reason to break a mouse feature.
37
+ }
38
+ return fallbackKeyText(keybinding);
39
+ }
40
+
41
+ /**
42
+ * `keyText` re-derived from pi-tui's registry: keys joined with "/", `alt`
43
+ * shown as `option` on macOS, matching `formatKeyText` in
44
+ * `keybinding-hints.js`.
45
+ */
46
+ function fallbackKeyText(keybinding: string): string {
47
+ try {
48
+ const bound: unknown = getKeybindings().getKeys(keybinding as never);
49
+ if (!Array.isArray(bound) || bound.length === 0) return "";
50
+ const keys = bound.filter((key): key is string => typeof key === "string");
51
+ return keys
52
+ .map((key: string) =>
53
+ key
54
+ .split("+")
55
+ .map((part) =>
56
+ process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part,
57
+ )
58
+ .join("+"),
59
+ )
60
+ .join("/");
61
+ } catch {
62
+ return "";
63
+ }
64
+ }
@@ -45,12 +45,11 @@
45
45
  * skill/branch/compaction summaries), so an expanded box used to have no
46
46
  * clickable way back at all without `pi-toolbox`'s collapse anchor.
47
47
  *
48
- * ## A component that handles the mouse keeps its clicks
49
- *
50
- * If any component on the clicked row's path implements `onMouse`, resolution
51
- * declines. The layout folds the whole transcript into one box, so
52
- * `pi-mouse-events` dispatch can never deliver an event to such a component —
53
- * bowing out here is the only way its `onMouse` can mean what it says.
48
+ * Ownership is decided by resolution alone: a click is taken over iff it
49
+ * resolves to an expandable component (one with `setExpanded`). A component
50
+ * that handles the mouse itself — Pi's own tool boxes wrapped in a core
51
+ * `MouseRegion` included — is simply not expandable by this predicate, so it
52
+ * falls through to Pi's native handling instead of being fought over.
54
53
  *
55
54
  * ## Why resolution goes through the component tree
56
55
  *
@@ -86,10 +85,10 @@
86
85
  * same click a row above or below behaves normally.
87
86
  */
88
87
 
89
- import { keyText } from "@earendil-works/pi-coding-agent";
90
- import { getKeybindings, stripTerminalSequences } from "@earendil-works/pi-tui";
88
+ import { stripTerminalSequences } from "@earendil-works/pi-tui";
91
89
  import { createComponentTree, isComponentLike, isExpandableComponent } from "./component-tree";
92
90
  import { type BoxLike, boxesAt, scrollContentLinesFor, scrollContentOrigin } from "./hit-test";
91
+ import { keyTextFor } from "./key-text";
93
92
 
94
93
  /** The slice of a Pi message component this module calls. */
95
94
  export type ExpandableComponent = { setExpanded(expanded: boolean): void };
@@ -165,15 +164,6 @@ export function expandedStateOf(component: object): boolean | undefined {
165
164
  return undefined;
166
165
  }
167
166
 
168
- /** Whether the component declares its own mouse handling (`onMouse`). */
169
- function handlesMouse(component: unknown): boolean {
170
- return (
171
- typeof component === "object" &&
172
- component !== null &&
173
- typeof (component as { onMouse?: unknown }).onMouse === "function"
174
- );
175
- }
176
-
177
167
  /**
178
168
  * The keys spelled the way the hint on screen spells them.
179
169
  *
@@ -200,47 +190,6 @@ export function expandKeyText(): string {
200
190
  return keyTextFor(KEYBINDING);
201
191
  }
202
192
 
203
- /**
204
- * `keyText(keybinding)` with the registry fallback, shared by every feature
205
- * that quotes a key on screen. See `expandKeyText` for why the primary route
206
- * is pi-coding-agent's own function rather than this repo's registry.
207
- */
208
- export function keyTextFor(keybinding: string): string {
209
- try {
210
- const rendered = keyText(keybinding as never);
211
- if (typeof rendered === "string" && rendered.length > 0) return rendered;
212
- } catch {
213
- // A Pi build that has moved this function is one where quoting the key
214
- // simply shows nothing; it is never a reason to break a mouse feature.
215
- }
216
- return fallbackKeyText(keybinding);
217
- }
218
-
219
- /**
220
- * `keyText` re-derived from pi-tui's registry: keys joined with "/", `alt`
221
- * shown as `option` on macOS, matching `formatKeyText` in
222
- * `keybinding-hints.js`.
223
- */
224
- function fallbackKeyText(keybinding: string): string {
225
- try {
226
- const bound: unknown = getKeybindings().getKeys(keybinding as never);
227
- if (!Array.isArray(bound) || bound.length === 0) return "";
228
- const keys = bound.filter((key): key is string => typeof key === "string");
229
- return keys
230
- .map((key: string) =>
231
- key
232
- .split("+")
233
- .map((part) =>
234
- process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part,
235
- )
236
- .join("+"),
237
- )
238
- .join("/");
239
- } catch {
240
- return "";
241
- }
242
- }
243
-
244
193
  /** The innermost scroll view whose box contains the screen cell. */
245
194
  function scrollViewAt(root: BoxLike, x: number, y: number): unknown {
246
195
  let found: unknown;
@@ -291,14 +240,6 @@ export function expandTargetAt(
291
240
  const tree = createComponentTree(origin.component, origin.rect.width, lines);
292
241
  const path = tree.pathAt(row);
293
242
 
294
- // A component anywhere on the path that handles the mouse itself owns this
295
- // click. The layout folds the transcript into one box, so `pi-mouse-events`
296
- // dispatch can never deliver an event to it — the only way its `onMouse`
297
- // can mean what it says is for this module to bow out.
298
- for (const span of path) {
299
- if (handlesMouse(span.component)) return undefined;
300
- }
301
-
302
243
  for (let index = path.length - 1; index >= 0; index--) {
303
244
  const span = path[index];
304
245
  if (!isExpandableComponent(span.component)) continue;
@@ -469,11 +469,12 @@ function buildItems(
469
469
  values: featureStateValues,
470
470
  });
471
471
  items.push({
472
- id: "mouseClickToExpandTools",
473
- label: "Click to expand tools",
472
+ id: "mouseClickToToggleExpandable",
473
+ label: "Click to toggle expandable",
474
474
  description:
475
- "Click a tool box's border or its expand hint to expand that one box. Needs wheel routing.",
476
- currentValue: featureValue(config.mouse.clickToExpandTools),
475
+ "Click any collapsible component (tool boxes, bash boxes, skill/summary blocks, " +
476
+ "custom entries/messages) to expand or collapse just that one.",
477
+ currentValue: featureValue(config.mouse.clickToToggleExpandable),
477
478
  values: featureStateValues,
478
479
  });
479
480
  }
@@ -831,11 +832,11 @@ export function registerStarlineSettingsCommand(pi: ExtensionAPI, deps: Settings
831
832
  tui.requestRender();
832
833
  return;
833
834
  }
834
- if (id === "mouseClickToExpandTools" && isFeatureState(newValue)) {
835
- deps.setMouseConfig({ clickToExpandTools: newValue === "enabled" }, ctx);
835
+ if (id === "mouseClickToToggleExpandable" && isFeatureState(newValue)) {
836
+ deps.setMouseConfig({ clickToToggleExpandable: newValue === "enabled" }, ctx);
836
837
  settingsList.updateValue(id, newValue);
837
838
  deps.requestRender();
838
- ctx.ui.notify(`Click to expand tools: ${newValue}`, "info");
839
+ ctx.ui.notify(`Click to toggle expandable: ${newValue}`, "info");
839
840
  tui.requestRender();
840
841
  return;
841
842
  }
@@ -30,6 +30,7 @@ type UserMessageRenderCache = {
30
30
  width?: number;
31
31
  theme?: Theme;
32
32
  configKey?: string;
33
+ transformers?: readonly unknown[];
33
34
  renderedLines?: string[];
34
35
  };
35
36
 
@@ -69,6 +70,49 @@ function getCachedMarkdownText(instance: object): string | undefined {
69
70
  return text;
70
71
  }
71
72
 
73
+ type MarkdownTransformerFn = (
74
+ markdown: string,
75
+ context: {
76
+ messageType: "user" | "assistant" | "assistant-thinking";
77
+ isStreaming: boolean;
78
+ availableWidth: number;
79
+ },
80
+ ) => unknown;
81
+
82
+ /**
83
+ * pi 官方管道:UserMessageComponent 把扩展注册的 markdownTransformers 通过
84
+ * Markdown 的 transform 选项跑一遍(createMarkdownTransform)。我们重写了 render,
85
+ * 必须自己把这条管道接上,否则任何 registerMarkdownTransformer 的扩展
86
+ * (如 pi-ide-context 的选区引用行)在 starline 样式下都会被旁路。
87
+ */
88
+ function getMarkdownTransformersRef(instance: object): readonly unknown[] | undefined {
89
+ if (!isRecord(instance)) return undefined;
90
+ const t = instance.markdownTransformers;
91
+ return Array.isArray(t) ? t : undefined;
92
+ }
93
+
94
+ function applyMarkdownTransformers(
95
+ text: string,
96
+ availableWidth: number,
97
+ transformers: readonly unknown[],
98
+ ): string {
99
+ let out = text;
100
+ for (const t of transformers) {
101
+ if (typeof t !== "function") continue;
102
+ try {
103
+ const r = (t as MarkdownTransformerFn)(out, {
104
+ messageType: "user",
105
+ isStreaming: false,
106
+ availableWidth,
107
+ });
108
+ if (typeof r === "string") out = r;
109
+ } catch {
110
+ // 与 pi 的 applyMarkdownTransformers 一致:单个 transformer 抛错不影响其他
111
+ }
112
+ }
113
+ return out;
114
+ }
115
+
72
116
  function getUserMessageConfigKey(config: PolishedTuiConfig): string {
73
117
  return [
74
118
  config.features.copyFriendly ? "copy" : "chrome",
@@ -161,12 +205,14 @@ function renderStarlineUserMessage(
161
205
  if (width <= 0) return [""];
162
206
 
163
207
  const configKey = getUserMessageConfigKey(config);
208
+ const transformersRef = getMarkdownTransformersRef(instance);
164
209
  const cached = userMessageRenderCache.get(instance);
165
210
  if (
166
211
  cached?.hasMarkdownText &&
167
212
  cached.width === width &&
168
213
  cached.theme === theme &&
169
214
  cached.configKey === configKey &&
215
+ cached.transformers === transformersRef &&
170
216
  cached.renderedLines
171
217
  ) {
172
218
  return cached.renderedLines;
@@ -174,17 +220,30 @@ function renderStarlineUserMessage(
174
220
 
175
221
  const railWidth = visibleWidth(renderPromptBoxRail(theme, config));
176
222
  const contentWidth = Math.max(1, width - railWidth);
177
- const renderer = new Markdown(text, 0, 0, makeMarkdownTheme(theme), {
178
- color: (content) =>
179
- theme && config.colors.userMessageText
180
- ? renderStyleForSource(
181
- theme,
182
- config.colorSources.userMessages,
183
- config.colors.userMessageText,
184
- content,
185
- )
186
- : themeFg(theme, "userMessageText", content),
187
- });
223
+ const renderer = new Markdown(
224
+ text,
225
+ 0,
226
+ 0,
227
+ makeMarkdownTheme(theme),
228
+ {
229
+ color: (content) =>
230
+ theme && config.colors.userMessageText
231
+ ? renderStyleForSource(
232
+ theme,
233
+ config.colorSources.userMessages,
234
+ config.colors.userMessageText,
235
+ content,
236
+ )
237
+ : themeFg(theme, "userMessageText", content),
238
+ },
239
+ // 接上官方 transformer 管道;Markdown 每次 render 都会按当前宽度重跑
240
+ {
241
+ transform: transformersRef?.length
242
+ ? (markdown, availableWidth) =>
243
+ applyMarkdownTransformers(markdown, availableWidth, transformersRef)
244
+ : undefined,
245
+ },
246
+ );
188
247
  const body = renderer.render(contentWidth);
189
248
  const contentLines = body.length > 0 ? body : [""];
190
249
  const border = theme
@@ -212,6 +271,7 @@ function renderStarlineUserMessage(
212
271
  width,
213
272
  theme,
214
273
  configKey,
274
+ transformers: transformersRef,
215
275
  renderedLines: lines,
216
276
  });
217
277
  return lines;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xzzpig/pi-starline",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "A Starship-inspired statusline and Opencode-style TUI for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,10 +23,10 @@
23
23
  "extensions"
24
24
  ],
25
25
  "peerDependencies": {
26
- "@earendil-works/pi-ai": ">=0.84.4",
27
- "@earendil-works/pi-coding-agent": ">=0.84.4",
28
- "@earendil-works/pi-tui": ">=0.84.4",
29
- "@xzzpig/pi-mouse-events": ">=0.1.0 <2"
26
+ "@earendil-works/pi-ai": ">=0.86.0 <0.88",
27
+ "@earendil-works/pi-coding-agent": ">=0.86.0 <0.88",
28
+ "@earendil-works/pi-tui": ">=0.86.0 <0.88",
29
+ "@xzzpig/pi-mouse-events": ">=0.2.0 <3"
30
30
  },
31
31
  "pi": {
32
32
  "extensions": [
@@ -39,13 +39,13 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@biomejs/biome": "^2.5.0",
42
- "@earendil-works/pi-ai": "^0.84.4",
43
- "@earendil-works/pi-coding-agent": "^0.84.4",
44
- "@earendil-works/pi-tui": "^0.84.4",
42
+ "@earendil-works/pi-ai": "^0.87.1",
43
+ "@earendil-works/pi-coding-agent": "^0.87.1",
44
+ "@earendil-works/pi-tui": "^0.87.1",
45
45
  "@types/node": "^26.0.0",
46
46
  "typescript": "^7.0.2",
47
47
  "vitest": "^4.1.8",
48
- "@xzzpig/pi-mouse-events": "0.1.2"
48
+ "@xzzpig/pi-mouse-events": "0.2.0"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "@xzzpig/pi-mouse-events": {