pi-ui-extend 0.1.13 → 0.1.15

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 (92) hide show
  1. package/README.md +1 -1
  2. package/dist/app/app.d.ts +5 -0
  3. package/dist/app/app.js +82 -12
  4. package/dist/app/commands/command-controller.js +1 -0
  5. package/dist/app/commands/command-host.d.ts +3 -0
  6. package/dist/app/commands/command-model-actions.d.ts +2 -0
  7. package/dist/app/commands/command-model-actions.js +40 -4
  8. package/dist/app/commands/command-navigation-actions.js +3 -0
  9. package/dist/app/commands/command-registry.d.ts +1 -0
  10. package/dist/app/commands/command-registry.js +8 -0
  11. package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
  12. package/dist/app/extensions/extension-ui-controller.js +99 -61
  13. package/dist/app/input/input-action-controller.d.ts +1 -0
  14. package/dist/app/input/input-action-controller.js +8 -2
  15. package/dist/app/logger.d.ts +25 -0
  16. package/dist/app/logger.js +90 -0
  17. package/dist/app/model/model-usage-status.js +30 -15
  18. package/dist/app/popup/menu-items-controller.d.ts +2 -0
  19. package/dist/app/popup/menu-items-controller.js +45 -6
  20. package/dist/app/popup/popup-action-controller.d.ts +2 -1
  21. package/dist/app/popup/popup-action-controller.js +7 -4
  22. package/dist/app/popup/popup-menu-controller.d.ts +36 -23
  23. package/dist/app/popup/popup-menu-controller.js +68 -322
  24. package/dist/app/rendering/conversation-entry-renderer.js +3 -3
  25. package/dist/app/rendering/conversation-viewport.d.ts +10 -2
  26. package/dist/app/rendering/conversation-viewport.js +157 -16
  27. package/dist/app/rendering/editor-panels.js +4 -2
  28. package/dist/app/rendering/popup-menu-renderer.d.ts +50 -0
  29. package/dist/app/rendering/popup-menu-renderer.js +307 -0
  30. package/dist/app/rendering/render-controller.js +5 -13
  31. package/dist/app/rendering/status-line-renderer.d.ts +1 -1
  32. package/dist/app/rendering/status-line-renderer.js +27 -24
  33. package/dist/app/rendering/toast-controller.d.ts +11 -3
  34. package/dist/app/rendering/toast-controller.js +53 -12
  35. package/dist/app/runtime.d.ts +2 -1
  36. package/dist/app/runtime.js +20 -10
  37. package/dist/app/screen/mouse-controller.d.ts +2 -2
  38. package/dist/app/screen/mouse-controller.js +27 -48
  39. package/dist/app/screen/screen-styler.d.ts +1 -1
  40. package/dist/app/screen/screen-styler.js +9 -7
  41. package/dist/app/screen/scroll-controller.d.ts +11 -9
  42. package/dist/app/screen/scroll-controller.js +50 -45
  43. package/dist/app/session/lazy-session-manager.d.ts +11 -0
  44. package/dist/app/session/lazy-session-manager.js +539 -0
  45. package/dist/app/session/pix-system-message.d.ts +16 -0
  46. package/dist/app/session/pix-system-message.js +64 -0
  47. package/dist/app/session/session-event-controller.d.ts +11 -0
  48. package/dist/app/session/session-event-controller.js +58 -2
  49. package/dist/app/session/session-history.d.ts +18 -0
  50. package/dist/app/session/session-history.js +72 -3
  51. package/dist/app/session/session-lifecycle-controller.d.ts +6 -2
  52. package/dist/app/session/session-lifecycle-controller.js +7 -2
  53. package/dist/app/session/tabs-controller.d.ts +13 -1
  54. package/dist/app/session/tabs-controller.js +248 -27
  55. package/dist/app/todo/todo-model.d.ts +3 -1
  56. package/dist/app/todo/todo-model.js +14 -2
  57. package/dist/app/types.d.ts +5 -2
  58. package/dist/app/workspace/workspace-actions-controller.d.ts +2 -0
  59. package/dist/app/workspace/workspace-actions-controller.js +12 -0
  60. package/dist/config.d.ts +5 -1
  61. package/dist/config.js +73 -25
  62. package/dist/default-pix-config.js +2 -0
  63. package/dist/schemas/pi-tools-suite-schema.d.ts +1 -0
  64. package/dist/schemas/pi-tools-suite-schema.js +1 -0
  65. package/dist/schemas/pix-schema.d.ts +2 -1
  66. package/dist/schemas/pix-schema.js +5 -4
  67. package/dist/terminal-width.d.ts +2 -0
  68. package/dist/terminal-width.js +64 -3
  69. package/external/pi-tools-suite/README.md +1 -0
  70. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +12 -3
  71. package/external/pi-tools-suite/src/antigravity-auth/commands.ts +2 -4
  72. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +2 -2
  73. package/external/pi-tools-suite/src/antigravity-auth/index.ts +8 -2
  74. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +102 -50
  75. package/external/pi-tools-suite/src/antigravity-auth/status.ts +81 -2
  76. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +29 -8
  77. package/external/pi-tools-suite/src/config.ts +8 -0
  78. package/external/pi-tools-suite/src/dcp/index.ts +16 -1
  79. package/external/pi-tools-suite/src/dcp/state.ts +35 -0
  80. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +3 -0
  81. package/external/pi-tools-suite/src/todo/index.ts +181 -11
  82. package/external/pi-tools-suite/src/todo/state/state-reducer.ts +23 -10
  83. package/external/pi-tools-suite/src/todo/todo.ts +10 -5
  84. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +33 -6
  85. package/external/pi-tools-suite/src/todo/tool/types.ts +9 -1
  86. package/external/pi-tools-suite/src/todo/view/format.ts +2 -1
  87. package/external/pi-tools-suite/src/tool-descriptions.ts +2 -1
  88. package/external/pi-tools-suite/src/usage/index.ts +5 -2
  89. package/external/pi-tools-suite/src/usage/lib/google.ts +6 -13
  90. package/package.json +1 -1
  91. package/schemas/pi-tools-suite.json +4 -0
  92. package/schemas/pix.json +6 -2
package/README.md CHANGED
@@ -215,7 +215,7 @@ Type `/` in the prompt to open the command picker. Commands that accept argument
215
215
  | `/settings` | — | Show current session, model, theme, and key settings. |
216
216
  | `/model` | `[provider/model[:thinking]]` | Select the active model. Without arguments, opens the model picker. With a reference like `anthropic/claude-sonnet-4-20250514:medium`, sets the model and optional thinking level directly. |
217
217
  | `/scoped-models` | `[refs…\|reset]` | Show or set the models used by the model selector and cycling. Pass one or more `provider/model[:thinking]` references separated by spaces or commas. Use `reset` to restore the default favorites. |
218
- | `/thinking` | `[level]` | Select the thinking level. Without arguments, opens the thinking picker. Accepts an explicit level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`). |
218
+ | `/thinking` | `[level\|auto]` | Select the thinking level. Without arguments, opens the thinking picker. Accepts an explicit level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`) or `auto`, which chooses a supported level per prompt. |
219
219
  | `/enhance` | — | Improve the current prompt draft using the prompt enhancer model. |
220
220
  | `/export` | `[path]` | Export the session. Defaults to HTML. Pass a `.jsonl` path to export as JSONL. |
221
221
  | `/import` | `<path.jsonl>` | Import and resume a session from a JSONL file. |
package/dist/app/app.d.ts CHANGED
@@ -58,6 +58,7 @@ export declare class PiUiExtendApp {
58
58
  private resumeSessions;
59
59
  private resumeLoading;
60
60
  constructor(options: AppOptions);
61
+ private createRuntime;
61
62
  start(): Promise<void>;
62
63
  private checkPixUpdateOnStartup;
63
64
  private bindCurrentSession;
@@ -67,6 +68,7 @@ export declare class PiUiExtendApp {
67
68
  private afterSessionReplacement;
68
69
  private requireRuntime;
69
70
  private restoreSessionStatus;
71
+ private activeExtensionUiScope;
70
72
  private setInput;
71
73
  private resetInputAfterProgrammaticEdit;
72
74
  private restoreTabInputState;
@@ -77,6 +79,8 @@ export declare class PiUiExtendApp {
77
79
  private resetSessionView;
78
80
  private loadSessionHistory;
79
81
  private openSearchResultInNewTab;
82
+ private scrollToUserMessageJumpTarget;
83
+ private findUserEntryBySessionEntryId;
80
84
  private loadSessionHistoryAsync;
81
85
  private handleSessionEvent;
82
86
  private findEntry;
@@ -93,6 +97,7 @@ export declare class PiUiExtendApp {
93
97
  private toggleTerminalBellSound;
94
98
  private refreshModelUsageStatusFromClick;
95
99
  private showToast;
100
+ private toastNotifierForScope;
96
101
  private clearToastTimers;
97
102
  private render;
98
103
  private scheduleRender;
package/dist/app/app.js CHANGED
@@ -16,6 +16,7 @@ import { createId } from "./id.js";
16
16
  import { NerdFontController } from "./terminal/nerd-font-controller.js";
17
17
  import { AppPopupActionController } from "./popup/popup-action-controller.js";
18
18
  import { AppPopupMenuController } from "./popup/popup-menu-controller.js";
19
+ import { PopupMenuRenderer } from "./rendering/popup-menu-renderer.js";
19
20
  import { AppPromptEnhancerController } from "./input/prompt-enhancer-controller.js";
20
21
  import { AppAutocompleteController } from "./input/autocomplete-controller.js";
21
22
  import { AppQueuedMessageController } from "./session/queued-message-controller.js";
@@ -136,6 +137,7 @@ export class PiUiExtendApp {
136
137
  get mouseSelection() { return app.mouseController.mouseSelection; },
137
138
  });
138
139
  this.toastController = new AppToastController({
140
+ activeScope: () => this.activeExtensionUiScope(),
139
141
  render: () => this.render(),
140
142
  });
141
143
  this.nerdFontController = new NerdFontController({
@@ -157,12 +159,12 @@ export class PiUiExtendApp {
157
159
  options: this.options,
158
160
  blinkController: this.blinkController,
159
161
  runtime: () => this.runtime,
160
- createRuntimeForNewSession: () => createPixRuntime(newTabRuntimeOptions(this.options), { eventBus: this.createExtensionEventBus() }),
161
- createRuntimeForSession: (sessionPath) => createPixRuntime({
162
+ createRuntimeForNewSession: () => this.createRuntime(newTabRuntimeOptions(this.options)),
163
+ createRuntimeForSession: (sessionPath) => this.createRuntime({
162
164
  ...this.options,
163
165
  noSession: false,
164
166
  sessionPath,
165
- }, { eventBus: this.createExtensionEventBus() }),
167
+ }),
166
168
  activateRuntime: (runtime) => this.activateRuntime(runtime),
167
169
  disposeRuntime: (runtime) => this.terminalController.disposeRuntime(runtime),
168
170
  isRunning: () => this.running,
@@ -175,13 +177,14 @@ export class PiUiExtendApp {
175
177
  syncUserSessionEntryMetadata: () => this.workspaceActions.syncUserSessionEntryMetadata(),
176
178
  captureInputState: () => ({ text: this.inputEditor.text, cursor: this.inputEditor.cursor }),
177
179
  restoreInputState: (state) => this.restoreTabInputState(state.text, state.cursor),
180
+ closeMenusForTabSwitch: () => this.popupMenus.closeMenusForTabSwitch(),
178
181
  captureDeferredUserMessages: () => this.queuedMessages.captureDeferredUserMessages(),
179
182
  restoreDeferredUserMessages: (messages) => this.queuedMessages.restoreDeferredUserMessages(messages),
180
183
  addEntry: (entry) => this.addEntry(entry),
181
184
  showToast: (message, kind) => this.showToast(message, kind),
182
185
  render: () => this.render(),
183
186
  });
184
- this.pixConfig = loadPixConfig();
187
+ this.pixConfig = loadPixConfig(this.options.cwd);
185
188
  setAppIconTheme(this.pixConfig.iconTheme.name);
186
189
  this.terminalBellSoundController = new TerminalBellSoundController();
187
190
  this.promptEnhancer = new AppPromptEnhancerController({
@@ -218,13 +221,19 @@ export class PiUiExtendApp {
218
221
  getEntries: () => this.entries,
219
222
  getResumeSessions: () => this.resumeSessions,
220
223
  });
221
- this.popupMenus = new AppPopupMenuController({
224
+ const popupMenuRenderer = new PopupMenuRenderer({
222
225
  theme: this.theme,
223
226
  screenStyler: this.screenStyler,
224
227
  get entries() { return app.entries; },
225
228
  get session() { return app.runtime?.session; },
226
229
  get resumeLoading() { return app.resumeLoading; },
227
230
  get resumeSessionCount() { return app.resumeSessions.length; },
231
+ });
232
+ this.popupMenus = new AppPopupMenuController({
233
+ get entries() { return app.entries; },
234
+ get session() { return app.runtime?.session; },
235
+ get resumeLoading() { return app.resumeLoading; },
236
+ get resumeSessionCount() { return app.resumeSessions.length; },
228
237
  isRunning: () => this.running,
229
238
  getInput: () => this.input,
230
239
  setInput: (value) => this.setInput(value),
@@ -241,7 +250,7 @@ export class PiUiExtendApp {
241
250
  setStatus: (status) => this.setStatus(status),
242
251
  restoreSessionStatus: () => this.restoreSessionStatus(),
243
252
  render: () => this.render(),
244
- });
253
+ }, popupMenuRenderer);
245
254
  this.statusLineRenderer = new StatusLineRenderer({
246
255
  theme: this.theme,
247
256
  screenStyler: this.screenStyler,
@@ -277,10 +286,12 @@ export class PiUiExtendApp {
277
286
  });
278
287
  this.extensionUiController = new ExtensionUiController({
279
288
  theme: this.theme,
289
+ activeExtensionUiScope: () => this.activeExtensionUiScope(),
280
290
  isRunning: () => this.running,
281
291
  render: () => this.render(),
282
- showToast: (message, kind) => this.showToast(message, kind),
292
+ showToast: (message, kind, options) => this.showToast(message, kind, options),
283
293
  toastNotifier: this.toastNotifier,
294
+ toastNotifierForScope: (scopeKey) => this.toastNotifierForScope(scopeKey),
284
295
  menuController: this.popupMenus.menuController,
285
296
  setStatus: (status) => this.setStatus(status),
286
297
  restoreSessionStatus: () => this.restoreSessionStatus(),
@@ -328,11 +339,14 @@ export class PiUiExtendApp {
328
339
  showToast: (message, kind) => this.showToast(message, kind),
329
340
  render: () => this.render(),
330
341
  isRunning: () => this.running,
342
+ forkSessionEntryInNewTab: (sessionEntryId) => this.tabsController.forkSessionEntryInNewTab(sessionEntryId),
331
343
  });
332
344
  this.sessionEvents = new AppSessionEventController({
333
345
  entries: this.entries,
334
346
  runtime: () => this.runtime,
335
347
  conversationViewport: () => this.conversationViewport,
348
+ conversationViewportColumns: () => this.terminalColumns(),
349
+ onHistoryWindowPruned: (edge, lineCount) => this.scrollController.adjustForHistoryWindowPrune(edge, lineCount),
336
350
  isRunning: () => this.running,
337
351
  render: () => this.render(),
338
352
  scheduleRender: () => this.scheduleRender(),
@@ -408,6 +422,9 @@ export class PiUiExtendApp {
408
422
  terminalColumns: () => this.terminalColumns(),
409
423
  terminalRows: () => this.terminalRows(),
410
424
  tabPanelRows: (terminalRows) => this.tabsController.tabPanelRows(terminalRows),
425
+ hasOlderSessionHistory: () => this.sessionEvents.hasOlderSessionHistory(),
426
+ isLoadingOlderSessionHistory: () => this.sessionEvents.isLoadingOlderSessionHistory(),
427
+ loadOlderSessionHistory: (options) => this.sessionEvents.loadOlderSessionHistory(options),
411
428
  render: () => this.render(),
412
429
  });
413
430
  this.commandController = new AppCommandController({
@@ -421,6 +438,10 @@ export class PiUiExtendApp {
421
438
  this.pixConfig.autocomplete.modelRef = modelRef;
422
439
  this.autocompleteController.dispose();
423
440
  },
441
+ ignoreContextFiles: () => this.pixConfig.ignoreContextFiles,
442
+ setIgnoreContextFiles: (ignoreContextFiles) => {
443
+ this.pixConfig.ignoreContextFiles = ignoreContextFiles;
444
+ },
424
445
  enhancePrompt: () => this.promptEnhancer.enhancePrompt(),
425
446
  isRunning: () => this.running,
426
447
  stop: () => this.stop(),
@@ -452,6 +473,7 @@ export class PiUiExtendApp {
452
473
  setDirectPopupMenuQuery: (query) => {
453
474
  this.popupMenus.setDirectQuery(query);
454
475
  },
476
+ refreshUserMessageJumpMenuItems: () => this.menuItems.refreshUserMessageJumpMenuItems(),
455
477
  getResumeLoading: () => this.resumeLoading,
456
478
  getResumeSessions: () => this.resumeSessions,
457
479
  setResumeLoading: (loading) => {
@@ -481,6 +503,7 @@ export class PiUiExtendApp {
481
503
  bindCurrentSession: () => this.bindCurrentSession(),
482
504
  loadSessionHistory: () => this.loadSessionHistory(),
483
505
  scrollToConversationEntry: (entryId) => this.scrollController.scrollToConversationEntry(entryId),
506
+ scrollToUserMessageJumpTarget: (target) => this.scrollToUserMessageJumpTarget(target),
484
507
  }, this.popupMenus, this.commandController, this.menuItems, this.queuedMessages, this.workspaceActions);
485
508
  this.mouseController = new AppMouseController({
486
509
  terminalColumns: () => this.terminalColumns(),
@@ -519,10 +542,11 @@ export class PiUiExtendApp {
519
542
  closeTab: (tabId) => {
520
543
  void this.tabsController.closeTab(tabId);
521
544
  },
522
- toastEntry: (toastId) => this.toastController.toast.entry(toastId),
545
+ toastEntry: (toastId) => this.toastController.entry(toastId),
523
546
  showToast: (message, kind, options) => this.showToast(message, kind, options),
524
547
  dismissToast: (toastId) => this.toastController.dismissToast(toastId),
525
548
  refreshModelUsageStatus: () => this.refreshModelUsageStatusFromClick(),
549
+ refreshUserMessageJumpMenuItems: () => this.menuItems.refreshUserMessageJumpMenuItems(),
526
550
  queueInputFromStatus: () => {
527
551
  void this.inputActions.queueInputFromEditor().catch((error) => {
528
552
  this.addEntry({ id: createId("error"), kind: "error", text: `Queue input failed: ${error instanceof Error ? error.message : String(error)}` });
@@ -653,7 +677,7 @@ export class PiUiExtendApp {
653
677
  });
654
678
  this.sessionLifecycle = new AppSessionLifecycleController({
655
679
  options: this.options,
656
- createRuntime: () => createPixRuntime(this.options, { eventBus: this.createExtensionEventBus() }),
680
+ createRuntime: () => this.createRuntime(this.options),
657
681
  entries: this.entries,
658
682
  runtime: () => this.runtime,
659
683
  setRuntime: (runtime) => {
@@ -671,8 +695,8 @@ export class PiUiExtendApp {
671
695
  loadRequestHistory: () => this.requestHistory.load(),
672
696
  startSubagentsPolling: () => this.subagentsWidgetController.startPolling(),
673
697
  closeSdkMenuForBind: () => this.popupMenus.closeSdkMenu(undefined, { render: false, restoreStatus: false }),
674
- clearExtensionWidgets: () => this.extensionUiController.clearWidgets(),
675
- createExtensionUIContext: () => this.extensionUiController.createExtensionUIContext(),
698
+ clearExtensionWidgets: (scopeKey, options) => this.extensionUiController.clearWidgets(scopeKey, options),
699
+ createExtensionUIContext: (scopeKey) => this.extensionUiController.createExtensionUIContext(scopeKey),
676
700
  extensionShutdownHandler: () => this.extensionShutdownHandler,
677
701
  createExtensionCommandContextActions: (runtime) => this.extensionActions.createCommandContextActions(runtime),
678
702
  handleExtensionError: (error) => this.extensionActions.handleExtensionError(error),
@@ -715,6 +739,11 @@ export class PiUiExtendApp {
715
739
  });
716
740
  this.slashCommands = this.commandController.slashCommands;
717
741
  }
742
+ createRuntime(options) {
743
+ return createPixRuntime(options, {
744
+ eventBus: this.createExtensionEventBus(),
745
+ });
746
+ }
718
747
  async start() {
719
748
  await this.sessionLifecycle.start();
720
749
  this.modelUsageController.startPolling();
@@ -766,6 +795,12 @@ export class PiUiExtendApp {
766
795
  if (this.runtime)
767
796
  this.setSessionStatus(this.runtime.session);
768
797
  }
798
+ activeExtensionUiScope() {
799
+ const session = this.runtime?.session;
800
+ if (!session)
801
+ return undefined;
802
+ return session.sessionFile ?? session.sessionId;
803
+ }
769
804
  setInput(value) {
770
805
  if (value !== this.input) {
771
806
  this.requestHistory.resetNavigation();
@@ -818,7 +853,11 @@ export class PiUiExtendApp {
818
853
  this.sessionLifecycle.resetSessionView();
819
854
  }
820
855
  loadSessionHistory() {
821
- this.sessionLifecycle.loadSessionHistory();
856
+ void this.sessionEvents.loadSessionHistoryAsync({
857
+ isCancelled: () => !this.running,
858
+ render: () => this.render(),
859
+ lazyOlderHistory: true,
860
+ });
822
861
  }
823
862
  async openSearchResultInNewTab(result) {
824
863
  const opened = await this.tabsController.openSessionInNewTab(result.session.path);
@@ -838,6 +877,23 @@ export class PiUiExtendApp {
838
877
  this.showToast("Opened search result", "success");
839
878
  this.setSessionStatus(this.runtime?.session);
840
879
  }
880
+ async scrollToUserMessageJumpTarget(target) {
881
+ if (target.entryId && this.scrollController.scrollToConversationEntry(target.entryId))
882
+ return true;
883
+ if (!target.sessionEntryId)
884
+ return false;
885
+ let entry = this.findUserEntryBySessionEntryId(target.sessionEntryId);
886
+ while (!entry && this.sessionEvents.hasOlderSessionHistory() && !this.sessionEvents.isLoadingOlderSessionHistory()) {
887
+ const loaded = await this.sessionEvents.loadOlderSessionHistory({ render: false });
888
+ if (!loaded)
889
+ break;
890
+ entry = this.findUserEntryBySessionEntryId(target.sessionEntryId);
891
+ }
892
+ return entry ? this.scrollController.scrollToConversationEntry(entry.id) : false;
893
+ }
894
+ findUserEntryBySessionEntryId(sessionEntryId) {
895
+ return this.entries.find((entry) => entry.kind === "user" && entry.sessionEntryId === sessionEntryId);
896
+ }
841
897
  async loadSessionHistoryAsync(options) {
842
898
  return this.sessionEvents.loadSessionHistoryAsync(options);
843
899
  }
@@ -928,6 +984,20 @@ export class PiUiExtendApp {
928
984
  showToast(message, kind = "info", options) {
929
985
  this.toastController.showToast(message, kind, options);
930
986
  }
987
+ toastNotifierForScope(scopeKey) {
988
+ const options = () => {
989
+ if (scopeKey === undefined)
990
+ return {};
991
+ return { scopeKey };
992
+ };
993
+ return {
994
+ show: (message, kind = "info") => this.showToast(message, kind, options()),
995
+ success: (message) => this.showToast(message, "success", options()),
996
+ error: (message) => this.showToast(message, "error", options()),
997
+ warning: (message) => this.showToast(message, "warning", options()),
998
+ info: (message) => this.showToast(message, "info", options()),
999
+ };
1000
+ }
931
1001
  clearToastTimers() {
932
1002
  this.toastController.clearToastTimers();
933
1003
  }
@@ -30,6 +30,7 @@ export class AppCommandController {
30
30
  runModelSlashCommand: (argumentsText) => this.modelActions.runModelSlashCommand(argumentsText),
31
31
  runDefaultModelSlashCommand: (argumentsText) => this.modelActions.runDefaultModelSlashCommand(argumentsText),
32
32
  runAutocompleteSlashCommand: (argumentsText) => this.modelActions.runAutocompleteSlashCommand(argumentsText),
33
+ runNoContextFilesSlashCommand: (argumentsText) => this.modelActions.runNoContextFilesSlashCommand(argumentsText),
33
34
  runScopedModelsCommand: (argumentsText) => this.modelActions.runScopedModelsCommand(argumentsText),
34
35
  runThinkingSlashCommand: (argumentsText) => this.modelActions.runThinkingSlashCommand(argumentsText),
35
36
  runDefaultThinkingSlashCommand: (argumentsText) => this.modelActions.runDefaultThinkingSlashCommand(argumentsText),
@@ -11,6 +11,8 @@ export type CommandControllerHost = {
11
11
  promptEnhancerModelRef(): string;
12
12
  autocompleteModelRef(): string;
13
13
  setAutocompleteModelRef(modelRef: string): void;
14
+ ignoreContextFiles(): boolean;
15
+ setIgnoreContextFiles(ignoreContextFiles: boolean): void;
14
16
  enhancePrompt(): Promise<void>;
15
17
  isRunning(): boolean;
16
18
  stop(): void | Promise<void>;
@@ -37,6 +39,7 @@ export type CommandControllerHost = {
37
39
  setDirectPopupMenuPreserveStatus(preserveStatus: boolean): void;
38
40
  getDirectPopupMenuQuery(): string;
39
41
  setDirectPopupMenuQuery(query: string): void;
42
+ refreshUserMessageJumpMenuItems(): Promise<void>;
40
43
  getResumeLoading(): boolean;
41
44
  getResumeSessions(): readonly SessionInfo[];
42
45
  setResumeLoading(loading: boolean): void;
@@ -7,11 +7,13 @@ export declare class ModelCommandActions {
7
7
  runModelSlashCommand(argumentsText: string): Promise<void>;
8
8
  runDefaultModelSlashCommand(argumentsText: string): Promise<void>;
9
9
  runAutocompleteSlashCommand(argumentsText: string): Promise<void>;
10
+ runNoContextFilesSlashCommand(argumentsText: string): Promise<void>;
10
11
  runScopedModelsCommand(argumentsText: string): Promise<void>;
11
12
  runThinkingSlashCommand(argumentsText: string): Promise<void>;
12
13
  runDefaultThinkingSlashCommand(argumentsText: string): Promise<void>;
13
14
  runModelCommand(model: SessionModel): Promise<void>;
14
15
  runThinkingCommand(level: ThinkingLevel): Promise<void>;
16
+ private addPersistentSystemEntry;
15
17
  private saveDefaultModel;
16
18
  private saveDefaultThinking;
17
19
  }
@@ -1,7 +1,8 @@
1
1
  import { getIdleRuntime, getRuntime } from "./command-runtime.js";
2
- import { savePixAutocompleteModel, savePixDefaultModel, savePixDefaultThinking } from "../../config.js";
2
+ import { getProjectPixConfigPath, savePixAutocompleteModel, savePixDefaultModel, savePixDefaultThinking, saveProjectPixIgnoreContextFiles } from "../../config.js";
3
3
  import { createId } from "../id.js";
4
4
  import { isThinkingLevel, parseScopedModelRef } from "../model/model-ref.js";
5
+ import { appendPixSystemDisplayEntry } from "../session/pix-system-message.js";
5
6
  export class ModelCommandActions {
6
7
  host;
7
8
  constructor(host) {
@@ -24,6 +25,7 @@ export class ModelCommandActions {
24
25
  `model: ${currentModel}`,
25
26
  `prompt enhancer model: ${this.host.promptEnhancerModelRef()}`,
26
27
  `autocomplete model: ${this.host.autocompleteModelRef() || "disabled"}`,
28
+ `context files: ${this.host.ignoreContextFiles() ? "disabled" : "enabled"}`,
27
29
  `thinking: ${runtime.session.thinkingLevel}`,
28
30
  `theme: ${settings.getTheme() ?? this.host.options.themeName}`,
29
31
  `skill commands: ${settings.getEnableSkillCommands() ? "enabled" : "disabled"}`,
@@ -33,7 +35,7 @@ export class ModelCommandActions {
33
35
  "scoped models:",
34
36
  scopedModelText,
35
37
  "",
36
- "Use /model, /thinking, /scoped-models, /export, /import, /reload for editable settings in pix.",
38
+ "Use /model, /thinking, /no-context-files, /scoped-models, /export, /import, /reload for editable settings in pix.",
37
39
  ].join("\n");
38
40
  this.host.addEntry({ id: createId("system"), kind: "system", text });
39
41
  this.host.setSessionStatus(runtime.session);
@@ -68,7 +70,7 @@ export class ModelCommandActions {
68
70
  await this.runModelCommand(model);
69
71
  if (parsed.thinkingLevel !== undefined) {
70
72
  runtime.session.setThinkingLevel(parsed.thinkingLevel);
71
- this.host.addEntry({ id: createId("system"), kind: "system", text: `Selected thinking level ${runtime.session.thinkingLevel}` });
73
+ this.addPersistentSystemEntry(runtime.session, `Selected thinking level ${runtime.session.thinkingLevel}`);
72
74
  this.host.setSessionStatus(runtime.session);
73
75
  }
74
76
  }
@@ -125,6 +127,36 @@ export class ModelCommandActions {
125
127
  this.host.addEntry({ id: createId("system"), kind: "system", text: `Autocomplete model set to ${saved.modelRef}.` });
126
128
  this.host.setSessionStatus(runtime.session);
127
129
  }
130
+ async runNoContextFilesSlashCommand(argumentsText) {
131
+ const value = argumentsText.trim().toLowerCase();
132
+ if (!value) {
133
+ this.host.addEntry({
134
+ id: createId("system"),
135
+ kind: "system",
136
+ text: [
137
+ `Context file loading is currently ${this.host.ignoreContextFiles() ? "disabled" : "enabled"} for this project.`,
138
+ "Usage: /no-context-files <on|off>",
139
+ ].join("\n"),
140
+ });
141
+ this.host.setSessionStatus(this.host.runtime()?.session);
142
+ return;
143
+ }
144
+ if (value !== "on" && value !== "off")
145
+ throw new Error("Usage: /no-context-files <on|off>");
146
+ const ignoreContextFiles = value === "on";
147
+ const saved = saveProjectPixIgnoreContextFiles(this.host.options.cwd, ignoreContextFiles);
148
+ this.host.setIgnoreContextFiles(saved);
149
+ this.host.addEntry({
150
+ id: createId("system"),
151
+ kind: "system",
152
+ text: [
153
+ `Context file loading ${saved ? "disabled" : "enabled"} for this project.`,
154
+ `Saved ignoreContextFiles=${saved ? "true" : "false"} to ${getProjectPixConfigPath(this.host.options.cwd)}.`,
155
+ "Start a new session or restart Pix for the change to affect loaded AGENTS.md/CLAUDE.md context.",
156
+ ].join("\n"),
157
+ });
158
+ this.host.setSessionStatus(this.host.runtime()?.session);
159
+ }
128
160
  async runScopedModelsCommand(argumentsText) {
129
161
  const runtime = getIdleRuntime(this.host, "scoped-models");
130
162
  if (!runtime)
@@ -246,9 +278,13 @@ export class ModelCommandActions {
246
278
  this.host.setStatus(`selecting thinking ${level}`);
247
279
  this.host.render();
248
280
  runtime.session.setThinkingLevel(level);
249
- this.host.addEntry({ id: createId("system"), kind: "system", text: `Selected thinking level ${runtime.session.thinkingLevel}` });
281
+ this.addPersistentSystemEntry(runtime.session, `Selected thinking level ${runtime.session.thinkingLevel}`);
250
282
  this.host.setSessionStatus(runtime.session);
251
283
  }
284
+ addPersistentSystemEntry(session, text) {
285
+ appendPixSystemDisplayEntry(session, text);
286
+ this.host.addEntry({ id: createId("system"), kind: "system", text });
287
+ }
252
288
  saveDefaultModel(modelRef) {
253
289
  const saved = savePixDefaultModel(modelRef);
254
290
  if (!saved)
@@ -98,6 +98,9 @@ export class NavigationCommandActions {
98
98
  const runtime = getRuntime(this.host, "jump");
99
99
  if (!runtime)
100
100
  return;
101
+ this.host.setStatus("scanning session messages…");
102
+ this.host.render();
103
+ await this.host.refreshUserMessageJumpMenuItems();
101
104
  this.host.openDirectPopupMenu("user-message-jump", { preserveStatus: true });
102
105
  this.host.setDirectPopupMenuQuery(argumentsText.trim());
103
106
  this.host.render();
@@ -5,6 +5,7 @@ export type CommandRegistryActions = {
5
5
  runModelSlashCommand(argumentsText: string): Promise<void>;
6
6
  runDefaultModelSlashCommand(argumentsText: string): Promise<void>;
7
7
  runAutocompleteSlashCommand(argumentsText: string): Promise<void>;
8
+ runNoContextFilesSlashCommand(argumentsText: string): Promise<void>;
8
9
  runScopedModelsCommand(argumentsText: string): Promise<void>;
9
10
  runThinkingSlashCommand(argumentsText: string): Promise<void>;
10
11
  runDefaultThinkingSlashCommand(argumentsText: string): Promise<void>;
@@ -32,6 +32,14 @@ export function createSlashCommands(actions, host) {
32
32
  allowArguments: true,
33
33
  run: (argumentsText) => actions.runAutocompleteSlashCommand(argumentsText),
34
34
  },
35
+ {
36
+ name: "no-context-files",
37
+ description: "Set project AGENTS.md/CLAUDE.md loading off or on",
38
+ kind: "builtin",
39
+ keywords: ["context", "agents", "claude", "project", "config"],
40
+ allowArguments: true,
41
+ run: (argumentsText) => actions.runNoContextFilesSlashCommand(argumentsText),
42
+ },
35
43
  {
36
44
  name: "scoped-models",
37
45
  description: "Show or set models used by the model selector/cycling",
@@ -7,10 +7,14 @@ export type ExtensionTerminalInputResult = {
7
7
  };
8
8
  export type ExtensionUiControllerHost = {
9
9
  readonly theme: Theme;
10
+ activeExtensionUiScope?(): string | undefined;
10
11
  isRunning(): boolean;
11
12
  render(): void;
12
- showToast(message: string, kind?: ToastKind): void;
13
+ showToast(message: string, kind?: ToastKind, options?: {
14
+ scopeKey?: string;
15
+ }): void;
13
16
  readonly toastNotifier: ToastNotifier;
17
+ toastNotifierForScope?(scopeKey: string | undefined): ToastNotifier;
14
18
  readonly menuController: PixMenuController;
15
19
  setStatus(status: string): void;
16
20
  restoreSessionStatus(): void;
@@ -23,22 +27,24 @@ export declare class ExtensionUiController {
23
27
  private readonly host;
24
28
  private readonly extensionWidgets;
25
29
  private readonly terminalInputHandlers;
26
- private activeCustomUi;
27
- private readonly aboveInputRenderer;
30
+ private readonly activeCustomUis;
28
31
  constructor(host: ExtensionUiControllerHost);
29
32
  get widgets(): ReadonlyMap<string, ExtensionWidgetRegistration>;
30
33
  createExtensionTheme(): ExtensionWidgetTheme;
31
34
  setWidget(key: string, content: unknown, options?: {
32
35
  placement?: WidgetPlacement;
36
+ scopeKey?: string;
37
+ }): void;
38
+ clearWidgets(scopeKey?: string, options?: {
39
+ cancelCustomUi?: boolean;
33
40
  }): void;
34
- clearWidgets(): void;
35
41
  suppressWidget(key: string): void;
36
42
  handleTerminalInput(data: string): ExtensionTerminalInputResult;
37
43
  renderActiveCustomUi(width: number): string[] | undefined;
38
44
  activeCustomUiUsesEditor(): boolean;
39
45
  handleCustomUiMouse(event: ExtensionInputMouseEvent): boolean;
40
46
  widgetTuiHandle(): WidgetTuiHandle;
41
- createExtensionUIContext(): PixExtensionUIContext;
47
+ createExtensionUIContext(scopeKey?: string): PixExtensionUIContext;
42
48
  private setAboveInputWidget;
43
49
  private clearAboveInputWidget;
44
50
  private selectDialog;
@@ -53,5 +59,10 @@ export declare class ExtensionUiController {
53
59
  private cancelActiveCustomUi;
54
60
  private rejectActiveCustomUi;
55
61
  private finishActiveCustomUi;
62
+ private activeCustomUiForActiveScope;
63
+ private activeScopeKey;
64
+ private normalizeScopeKey;
65
+ private scopedWidgetKey;
66
+ private unscopedWidgetKey;
56
67
  private invalidateWidget;
57
68
  }