@herbertgao/pi-extensions 2026.9.12 → 2026.9.13

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.
Files changed (42) hide show
  1. package/README.md +4 -4
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  4. package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
  5. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +5 -4
  6. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +39 -16
  7. package/node_modules/@narumitw/pi-btw/README.md +21 -4
  8. package/node_modules/@narumitw/pi-btw/dist/index.ts +1668 -958
  9. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  10. package/node_modules/@narumitw/pi-btw/docs/workflows.md +10 -2
  11. package/node_modules/@narumitw/pi-btw/package.json +1 -1
  12. package/node_modules/@narumitw/pi-btw/src/btw.ts +17 -79
  13. package/node_modules/@narumitw/pi-btw/src/conversation-context.ts +74 -0
  14. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +196 -7
  15. package/node_modules/@narumitw/pi-btw/src/main-thread-updates.ts +40 -0
  16. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
  17. package/node_modules/@narumitw/pi-btw/src/settings.ts +49 -0
  18. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +9 -1
  19. package/node_modules/@narumitw/pi-btw/src/workspace-layout.ts +559 -0
  20. package/node_modules/pi-multi-account/CHANGELOG.md +23 -0
  21. package/node_modules/pi-multi-account/README.md +38 -11
  22. package/node_modules/pi-multi-account/index.ts +371 -96
  23. package/node_modules/pi-multi-account/package.json +5 -4
  24. package/node_modules/pi-multi-account/provider-payload-stream.ts +36 -28
  25. package/node_modules/pi-multi-account/usage.ts +31 -2
  26. package/node_modules/pi-typesafe/README.md +3 -1
  27. package/node_modules/pi-typesafe/dist/auth.d.ts +10 -3
  28. package/node_modules/pi-typesafe/dist/auth.js +13 -7
  29. package/node_modules/pi-typesafe/dist/backends.d.ts +31 -0
  30. package/node_modules/pi-typesafe/dist/backends.js +33 -0
  31. package/node_modules/pi-typesafe/dist/client.d.ts +3 -9
  32. package/node_modules/pi-typesafe/dist/client.js +63 -39
  33. package/node_modules/pi-typesafe/dist/credentials.d.ts +11 -5
  34. package/node_modules/pi-typesafe/dist/credentials.js +15 -8
  35. package/node_modules/pi-typesafe/dist/extension.js +4 -1
  36. package/node_modules/pi-typesafe/dist/index.d.ts +1 -1
  37. package/node_modules/pi-typesafe/dist/index.js +1 -1
  38. package/node_modules/pi-typesafe/dist/login.d.ts +13 -7
  39. package/node_modules/pi-typesafe/dist/login.js +17 -8
  40. package/node_modules/pi-typesafe/dist/schema.js +19 -13
  41. package/node_modules/pi-typesafe/package.json +1 -1
  42. package/package.json +5 -5
@@ -15,9 +15,11 @@ import {
15
15
  validateBtwShortcutEdit,
16
16
  } from "./keybindings.js";
17
17
  import {
18
+ type BtwLayout,
18
19
  type BtwSettings,
19
20
  type BtwSettingsPatch,
20
21
  btwSettingsPath,
22
+ effectiveBtwLayout,
21
23
  effectiveFullscreenCopyOnSelect,
22
24
  effectiveRememberThinkingLevelChanges,
23
25
  parseBtwModelReference,
@@ -65,10 +67,17 @@ type BtwMenuAction =
65
67
  | "set-thinking"
66
68
  | "set-remember"
67
69
  | "set-fullscreen-copy"
70
+ | "set-layout"
68
71
  | "edit-shortcut"
69
72
  | "save-shortcut"
70
73
  | "reset-shortcut";
71
74
  const SAME_AS_MAIN_THREAD = "Same as main thread";
75
+ const BTW_LAYOUT_LABELS: Record<BtwLayout, string> = {
76
+ fullscreen: "Fullscreen",
77
+ "left-pane": "Side thread left",
78
+ "right-pane": "Side thread right",
79
+ };
80
+ const BTW_LAYOUT_VALUES = Object.values(BTW_LAYOUT_LABELS);
72
81
  type BtwCustomOptions = Parameters<ExtensionCommandContext["ui"]["custom"]>[1];
73
82
 
74
83
  type BtwCustomFactory<T> = (
@@ -331,7 +340,7 @@ export async function showBtwCommandMenu(
331
340
  lines: [
332
341
  `Model: ${displayModelValue(state.settings)}`,
333
342
  `Thinking: ${displayThinkingSummary(state.settings)} · Remember changes: ${displayRememberSummary(state.settings)}`,
334
- `Copy on select: ${effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off"}`,
343
+ `Layout: ${BTW_LAYOUT_LABELS[effectiveBtwLayout(state.settings)]} · Copy on select: ${effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off"}`,
335
344
  ],
336
345
  items: [
337
346
  {
@@ -359,7 +368,7 @@ export async function showBtwCommandMenu(
359
368
  {
360
369
  id: "settings",
361
370
  label: "Settings",
362
- description: "Choose model, thinking, keybindings, and selection copying",
371
+ description: "Choose model, thinking, layout, keybindings, and selection copying",
363
372
  to: state.kind === "invalid" ? "invalid" : "settings",
364
373
  },
365
374
  ],
@@ -414,6 +423,14 @@ export async function showBtwCommandMenu(
414
423
  values: ["On", "Off"],
415
424
  action: "set-fullscreen-copy",
416
425
  },
426
+ {
427
+ id: "layout",
428
+ label: "Side-thread layout",
429
+ description: "Use the full workspace or place BTW beside the live, click-to-focus main thread.",
430
+ currentValue: BTW_LAYOUT_LABELS[effectiveBtwLayout(state.settings)],
431
+ values: BTW_LAYOUT_VALUES,
432
+ action: "set-layout",
433
+ },
417
434
  ...BTW_SHORTCUT_ACTIONS.map((action) => ({
418
435
  id: action,
419
436
  label: shortcutLabels[action],
@@ -555,6 +572,21 @@ export async function showBtwCommandMenu(
555
572
  return { kind: "rejected" };
556
573
  }
557
574
  },
575
+ "set-layout": async ({ value, signal }) => {
576
+ const layout = Object.entries(BTW_LAYOUT_LABELS).find(([, label]) => label === value)?.[0] as
577
+ | BtwLayout
578
+ | undefined;
579
+ if (!layout) return { kind: "rejected" };
580
+ try {
581
+ await updateSettings({ layout }, { settingsPath, signal });
582
+ if (signal.aborted) return { kind: "rejected" };
583
+ notifySafely(ctx, `Pi BTW layout: ${BTW_LAYOUT_LABELS[layout]}. Applies when BTW next opens.`, "info");
584
+ return { kind: "stay" };
585
+ } catch (error) {
586
+ if (!signal.aborted) notifySaveFailure(ctx, error);
587
+ return { kind: "rejected" };
588
+ }
589
+ },
558
590
  },
559
591
  });
560
592
 
@@ -7,8 +7,14 @@ import { BTW_SHORTCUT_ACTIONS, type BtwKeybindingOverrides, normalizeBtwKey } fr
7
7
  import { BTW_THINKING_LEVELS, type BtwThinkingLevel } from "./side-thread.js";
8
8
 
9
9
  export const BTW_SETTINGS_FILE = "pi-btw.json";
10
+ export const BTW_LAYOUTS = ["fullscreen", "left-pane", "right-pane"] as const;
11
+ export type BtwLayout = (typeof BTW_LAYOUTS)[number];
12
+ export const DEFAULT_BTW_LAYOUT: BtwLayout = "fullscreen";
10
13
  export const DEFAULT_FULLSCREEN_COPY_ON_SELECT = true;
11
14
  export const DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES = true;
15
+ export const DEFAULT_BTW_SIDE_PANE_RATIO = 0.5;
16
+ export const MIN_BTW_SIDE_PANE_RATIO = 0.2;
17
+ export const MAX_BTW_SIDE_PANE_RATIO = 0.8;
12
18
  const MAX_SETTINGS_BYTES = 64 * 1024;
13
19
 
14
20
  export interface BtwSettings {
@@ -17,6 +23,8 @@ export interface BtwSettings {
17
23
  thinkingLevel?: BtwThinkingLevel;
18
24
  rememberThinkingLevelChanges?: boolean;
19
25
  fullscreenCopyOnSelect?: boolean;
26
+ layout?: BtwLayout;
27
+ sidePaneRatio?: number;
20
28
  }
21
29
 
22
30
  export type BtwSettingsLoadResult =
@@ -30,6 +38,8 @@ export interface BtwSettingsPatch {
30
38
  thinkingLevel?: BtwThinkingLevel;
31
39
  rememberThinkingLevelChanges?: boolean;
32
40
  fullscreenCopyOnSelect?: boolean;
41
+ layout?: BtwLayout;
42
+ sidePaneRatio?: number;
33
43
  }
34
44
 
35
45
  export interface UpdateBtwSettingsOptions {
@@ -83,6 +93,16 @@ export function normalizeBtwSettings(value: unknown): BtwSettings | undefined {
83
93
  if (typeof copyOnSelect !== "boolean") return undefined;
84
94
  settings.fullscreenCopyOnSelect = copyOnSelect;
85
95
  }
96
+ if (Object.hasOwn(value, "layout")) {
97
+ const layout = Reflect.get(value, "layout");
98
+ if (!isBtwLayout(layout)) return undefined;
99
+ settings.layout = layout;
100
+ }
101
+ if (Object.hasOwn(value, "sidePaneRatio")) {
102
+ const sidePaneRatio = Reflect.get(value, "sidePaneRatio");
103
+ if (!isBtwSidePaneRatio(sidePaneRatio)) return undefined;
104
+ settings.sidePaneRatio = sidePaneRatio;
105
+ }
86
106
  return settings;
87
107
  }
88
108
 
@@ -93,6 +113,10 @@ export function parseBtwModelReference(reference: string): { provider: string; m
93
113
  return { provider: reference.slice(0, separator), modelId: reference.slice(separator + 1) };
94
114
  }
95
115
 
116
+ export function effectiveBtwLayout(settings: BtwSettings): BtwLayout {
117
+ return settings.layout ?? DEFAULT_BTW_LAYOUT;
118
+ }
119
+
96
120
  export function effectiveFullscreenCopyOnSelect(settings: BtwSettings): boolean {
97
121
  return settings.fullscreenCopyOnSelect ?? DEFAULT_FULLSCREEN_COPY_ON_SELECT;
98
122
  }
@@ -101,6 +125,10 @@ export function effectiveRememberThinkingLevelChanges(settings: BtwSettings): bo
101
125
  return settings.rememberThinkingLevelChanges ?? DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES;
102
126
  }
103
127
 
128
+ export function effectiveBtwSidePaneRatio(settings: BtwSettings): number {
129
+ return isBtwSidePaneRatio(settings.sidePaneRatio) ? settings.sidePaneRatio : DEFAULT_BTW_SIDE_PANE_RATIO;
130
+ }
131
+
104
132
  export async function readBtwSettings(settingsPath = btwSettingsPath()): Promise<BtwSettingsLoadResult> {
105
133
  await awaitBtwSettingsWrites(settingsPath);
106
134
  return readBtwSettingsUncoordinated(settingsPath);
@@ -270,6 +298,14 @@ function applyBtwSettingsPatch(current: SettingsDocument, patch: BtwSettingsPatc
270
298
  if (patch.fullscreenCopyOnSelect === undefined) delete updated.fullscreenCopyOnSelect;
271
299
  else updated.fullscreenCopyOnSelect = patch.fullscreenCopyOnSelect;
272
300
  }
301
+ if (Object.hasOwn(patch, "layout")) {
302
+ if (patch.layout === undefined) delete updated.layout;
303
+ else updated.layout = patch.layout;
304
+ }
305
+ if (Object.hasOwn(patch, "sidePaneRatio")) {
306
+ if (patch.sidePaneRatio === undefined) delete updated.sidePaneRatio;
307
+ else updated.sidePaneRatio = patch.sidePaneRatio;
308
+ }
273
309
  return updated;
274
310
  }
275
311
 
@@ -281,6 +317,19 @@ function isBtwThinkingLevel(value: unknown): value is BtwThinkingLevel {
281
317
  return BTW_THINKING_LEVELS.includes(value as BtwThinkingLevel);
282
318
  }
283
319
 
320
+ function isBtwLayout(value: unknown): value is BtwLayout {
321
+ return BTW_LAYOUTS.includes(value as BtwLayout);
322
+ }
323
+
324
+ function isBtwSidePaneRatio(value: unknown): value is number {
325
+ return (
326
+ typeof value === "number" &&
327
+ Number.isFinite(value) &&
328
+ value >= MIN_BTW_SIDE_PANE_RATIO &&
329
+ value <= MAX_BTW_SIDE_PANE_RATIO
330
+ );
331
+ }
332
+
284
333
  function invalidSettingsError(settingsPath: string, reason: string): Error {
285
334
  return new Error(`pi-btw settings at ${settingsPath} are invalid: ${reason}`);
286
335
  }
@@ -23,10 +23,10 @@ import {
23
23
  VStack,
24
24
  visibleWidth,
25
25
  } from "@earendil-works/pi-tui";
26
- import type { BtwFullscreenLayoutComponent } from "./fullscreen-ui.js";
27
26
  import { BtwPasteGuard, type BtwShortcuts, getBtwShortcuts } from "./keybindings.js";
28
27
  import type { BtwThinkingLevel, SideThreadTurn } from "./side-thread.js";
29
28
  import { sanitizeSingleLine } from "./text.js";
29
+ import type { BtwFullscreenLayoutComponent } from "./workspace-layout.js";
30
30
 
31
31
  const TRANSCRIPT_CHROME_LINES = 2;
32
32
  const MAX_STEERING_DISPLAY_LINES = 3;
@@ -146,6 +146,10 @@ export class BtwTranscriptPager implements BtwFullscreenLayoutComponent, Focusab
146
146
  return this.layoutRoot;
147
147
  }
148
148
 
149
+ getPrimaryScrollView(): ScrollView {
150
+ return this.scrollView;
151
+ }
152
+
149
153
  render(width: number): string[] {
150
154
  if (width <= 0) return [];
151
155
  const safeWidth = Math.max(1, width);
@@ -397,6 +401,10 @@ export class BtwAnsweringView implements BtwFullscreenLayoutComponent, Focusable
397
401
  return this.layoutRoot;
398
402
  }
399
403
 
404
+ getPrimaryScrollView(): ScrollView {
405
+ return this.scrollView;
406
+ }
407
+
400
408
  render(width: number): string[] {
401
409
  if (width <= 0) return [];
402
410
  const safeWidth = Math.max(1, width);