@mariozechner/pi-coding-agent 0.28.0 → 0.29.1

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 (38) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +28 -13
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +5 -5
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/custom-tools/loader.d.ts +5 -0
  7. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  8. package/dist/core/custom-tools/loader.js +58 -3
  9. package/dist/core/custom-tools/loader.js.map +1 -1
  10. package/dist/core/custom-tools/types.d.ts +2 -2
  11. package/dist/core/custom-tools/types.d.ts.map +1 -1
  12. package/dist/core/custom-tools/types.js.map +1 -1
  13. package/dist/core/hooks/loader.d.ts.map +1 -1
  14. package/dist/core/hooks/loader.js +8 -1
  15. package/dist/core/hooks/loader.js.map +1 -1
  16. package/dist/core/hooks/types.d.ts +3 -3
  17. package/dist/core/hooks/types.d.ts.map +1 -1
  18. package/dist/core/hooks/types.js.map +1 -1
  19. package/dist/main.d.ts.map +1 -1
  20. package/dist/main.js +19 -2
  21. package/dist/main.js.map +1 -1
  22. package/dist/modes/interactive/components/settings-selector.d.ts +33 -0
  23. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  24. package/dist/modes/interactive/components/settings-selector.js +164 -0
  25. package/dist/modes/interactive/components/settings-selector.js.map +1 -0
  26. package/dist/modes/interactive/interactive-mode.d.ts +1 -5
  27. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  28. package/dist/modes/interactive/interactive-mode.js +73 -119
  29. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  30. package/dist/modes/interactive/theme/theme.d.ts +1 -0
  31. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  32. package/dist/modes/interactive/theme/theme.js +9 -0
  33. package/dist/modes/interactive/theme/theme.js.map +1 -1
  34. package/docs/custom-tools.md +3 -3
  35. package/docs/hooks.md +9 -9
  36. package/examples/hooks/confirm-destructive.ts +1 -1
  37. package/examples/hooks/dirty-repo-guard.ts +2 -6
  38. package/package.json +4 -4
@@ -5,7 +5,7 @@
5
5
  import * as fs from "node:fs";
6
6
  import * as os from "node:os";
7
7
  import * as path from "node:path";
8
- import { CombinedAutocompleteProvider, Container, getCapabilities, Input, Loader, Markdown, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
8
+ import { CombinedAutocompleteProvider, Container, Input, Loader, Markdown, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
9
9
  import { exec, spawnSync } from "child_process";
10
10
  import { APP_NAME, getAuthPath, getDebugLogPath } from "../../config.js";
11
11
  import { isBashExecutionMessage } from "../../core/messages.js";
@@ -25,15 +25,12 @@ import { HookInputComponent } from "./components/hook-input.js";
25
25
  import { HookSelectorComponent } from "./components/hook-selector.js";
26
26
  import { ModelSelectorComponent } from "./components/model-selector.js";
27
27
  import { OAuthSelectorComponent } from "./components/oauth-selector.js";
28
- import { QueueModeSelectorComponent } from "./components/queue-mode-selector.js";
29
28
  import { SessionSelectorComponent } from "./components/session-selector.js";
30
- import { ShowImagesSelectorComponent } from "./components/show-images-selector.js";
31
- import { ThemeSelectorComponent } from "./components/theme-selector.js";
32
- import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
29
+ import { SettingsSelectorComponent } from "./components/settings-selector.js";
33
30
  import { ToolExecutionComponent } from "./components/tool-execution.js";
34
31
  import { UserMessageComponent } from "./components/user-message.js";
35
32
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
36
- import { getEditorTheme, getMarkdownTheme, onThemeChange, setTheme, theme } from "./theme/theme.js";
33
+ import { getAvailableThemes, getEditorTheme, getMarkdownTheme, onThemeChange, setTheme, theme } from "./theme/theme.js";
37
34
  export class InteractiveMode {
38
35
  setToolUIContext;
39
36
  session;
@@ -107,7 +104,7 @@ export class InteractiveMode {
107
104
  this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
108
105
  // Define slash commands for autocomplete
109
106
  const slashCommands = [
110
- { name: "thinking", description: "Select reasoning level (opens selector UI)" },
107
+ { name: "settings", description: "Open settings menu" },
111
108
  { name: "model", description: "Select model (opens selector UI)" },
112
109
  { name: "export", description: "Export session to HTML file" },
113
110
  { name: "copy", description: "Copy last agent message to clipboard" },
@@ -117,17 +114,10 @@ export class InteractiveMode {
117
114
  { name: "branch", description: "Create a new branch from a previous message" },
118
115
  { name: "login", description: "Login with OAuth provider" },
119
116
  { name: "logout", description: "Logout from OAuth provider" },
120
- { name: "queue", description: "Select message queue mode (opens selector UI)" },
121
- { name: "theme", description: "Select color theme (opens selector UI)" },
122
- { name: "clear", description: "Clear context and start a fresh session" },
117
+ { name: "new", description: "Start a new session" },
123
118
  { name: "compact", description: "Manually compact the session context" },
124
- { name: "autocompact", description: "Toggle automatic context compaction" },
125
119
  { name: "resume", description: "Resume a different session" },
126
120
  ];
127
- // Add image toggle command only if terminal supports images
128
- if (getCapabilities().images) {
129
- slashCommands.push({ name: "show-images", description: "Toggle inline image display" });
130
- }
131
121
  // Load hide thinking block setting
132
122
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
133
123
  // Convert file commands to SlashCommand format
@@ -510,8 +500,8 @@ export class InteractiveMode {
510
500
  if (!text)
511
501
  return;
512
502
  // Handle slash commands
513
- if (text === "/thinking") {
514
- this.showThinkingSelector();
503
+ if (text === "/settings") {
504
+ this.showSettingsSelector();
515
505
  this.editor.setText("");
516
506
  return;
517
507
  }
@@ -560,17 +550,7 @@ export class InteractiveMode {
560
550
  this.editor.setText("");
561
551
  return;
562
552
  }
563
- if (text === "/queue") {
564
- this.showQueueModeSelector();
565
- this.editor.setText("");
566
- return;
567
- }
568
- if (text === "/theme") {
569
- this.showThemeSelector();
570
- this.editor.setText("");
571
- return;
572
- }
573
- if (text === "/clear") {
553
+ if (text === "/new") {
574
554
  this.editor.setText("");
575
555
  await this.handleClearCommand();
576
556
  return;
@@ -587,16 +567,6 @@ export class InteractiveMode {
587
567
  }
588
568
  return;
589
569
  }
590
- if (text === "/autocompact") {
591
- this.handleAutocompactCommand();
592
- this.editor.setText("");
593
- return;
594
- }
595
- if (text === "/show-images") {
596
- this.showShowImagesSelector();
597
- this.editor.setText("");
598
- return;
599
- }
600
570
  if (text === "/debug") {
601
571
  this.handleDebugCommand();
602
572
  this.editor.setText("");
@@ -1202,59 +1172,74 @@ export class InteractiveMode {
1202
1172
  this.ui.setFocus(focus);
1203
1173
  this.ui.requestRender();
1204
1174
  }
1205
- showThinkingSelector() {
1206
- this.showSelector((done) => {
1207
- const selector = new ThinkingSelectorComponent(this.session.thinkingLevel, this.session.getAvailableThinkingLevels(), (level) => {
1208
- this.session.setThinkingLevel(level);
1209
- this.footer.updateState(this.session.state);
1210
- this.updateEditorBorderColor();
1211
- done();
1212
- this.showStatus(`Thinking level: ${level}`);
1213
- }, () => {
1214
- done();
1215
- this.ui.requestRender();
1216
- });
1217
- return { component: selector, focus: selector.getSelectList() };
1218
- });
1219
- }
1220
- showQueueModeSelector() {
1221
- this.showSelector((done) => {
1222
- const selector = new QueueModeSelectorComponent(this.session.queueMode, (mode) => {
1223
- this.session.setQueueMode(mode);
1224
- done();
1225
- this.showStatus(`Queue mode: ${mode}`);
1226
- }, () => {
1227
- done();
1228
- this.ui.requestRender();
1229
- });
1230
- return { component: selector, focus: selector.getSelectList() };
1231
- });
1232
- }
1233
- showThemeSelector() {
1234
- const currentTheme = this.settingsManager.getTheme() || "dark";
1175
+ showSettingsSelector() {
1235
1176
  this.showSelector((done) => {
1236
- const selector = new ThemeSelectorComponent(currentTheme, (themeName) => {
1237
- const result = setTheme(themeName, true);
1238
- this.settingsManager.setTheme(themeName);
1239
- this.ui.invalidate();
1240
- done();
1241
- if (result.success) {
1242
- this.showStatus(`Theme: ${themeName}`);
1243
- }
1244
- else {
1245
- this.showError(`Failed to load theme "${themeName}": ${result.error}\nFell back to dark theme.`);
1246
- }
1247
- }, () => {
1248
- done();
1249
- this.ui.requestRender();
1250
- }, (themeName) => {
1251
- const result = setTheme(themeName, true);
1252
- if (result.success) {
1177
+ const selector = new SettingsSelectorComponent({
1178
+ autoCompact: this.session.autoCompactionEnabled,
1179
+ showImages: this.settingsManager.getShowImages(),
1180
+ queueMode: this.session.queueMode,
1181
+ thinkingLevel: this.session.thinkingLevel,
1182
+ availableThinkingLevels: this.session.getAvailableThinkingLevels(),
1183
+ currentTheme: this.settingsManager.getTheme() || "dark",
1184
+ availableThemes: getAvailableThemes(),
1185
+ hideThinkingBlock: this.hideThinkingBlock,
1186
+ collapseChangelog: this.settingsManager.getCollapseChangelog(),
1187
+ }, {
1188
+ onAutoCompactChange: (enabled) => {
1189
+ this.session.setAutoCompactionEnabled(enabled);
1190
+ this.footer.setAutoCompactEnabled(enabled);
1191
+ },
1192
+ onShowImagesChange: (enabled) => {
1193
+ this.settingsManager.setShowImages(enabled);
1194
+ for (const child of this.chatContainer.children) {
1195
+ if (child instanceof ToolExecutionComponent) {
1196
+ child.setShowImages(enabled);
1197
+ }
1198
+ }
1199
+ },
1200
+ onQueueModeChange: (mode) => {
1201
+ this.session.setQueueMode(mode);
1202
+ },
1203
+ onThinkingLevelChange: (level) => {
1204
+ this.session.setThinkingLevel(level);
1205
+ this.footer.updateState(this.session.state);
1206
+ this.updateEditorBorderColor();
1207
+ },
1208
+ onThemeChange: (themeName) => {
1209
+ const result = setTheme(themeName, true);
1210
+ this.settingsManager.setTheme(themeName);
1253
1211
  this.ui.invalidate();
1212
+ if (!result.success) {
1213
+ this.showError(`Failed to load theme "${themeName}": ${result.error}\nFell back to dark theme.`);
1214
+ }
1215
+ },
1216
+ onThemePreview: (themeName) => {
1217
+ const result = setTheme(themeName, true);
1218
+ if (result.success) {
1219
+ this.ui.invalidate();
1220
+ this.ui.requestRender();
1221
+ }
1222
+ },
1223
+ onHideThinkingBlockChange: (hidden) => {
1224
+ this.hideThinkingBlock = hidden;
1225
+ this.settingsManager.setHideThinkingBlock(hidden);
1226
+ for (const child of this.chatContainer.children) {
1227
+ if (child instanceof AssistantMessageComponent) {
1228
+ child.setHideThinkingBlock(hidden);
1229
+ }
1230
+ }
1231
+ this.chatContainer.clear();
1232
+ this.rebuildChatFromMessages();
1233
+ },
1234
+ onCollapseChangelogChange: (collapsed) => {
1235
+ this.settingsManager.setCollapseChangelog(collapsed);
1236
+ },
1237
+ onCancel: () => {
1238
+ done();
1254
1239
  this.ui.requestRender();
1255
- }
1240
+ },
1256
1241
  });
1257
- return { component: selector, focus: selector.getSelectList() };
1242
+ return { component: selector, focus: selector.getSettingsList() };
1258
1243
  });
1259
1244
  }
1260
1245
  showModelSelector() {
@@ -1564,7 +1549,7 @@ export class InteractiveMode {
1564
1549
  this.pendingTools.clear();
1565
1550
  this.isFirstUserMessage = true;
1566
1551
  this.chatContainer.addChild(new Spacer(1));
1567
- this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ Context cleared")}\n${theme.fg("muted", "Started fresh session")}`, 1, 1));
1552
+ this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
1568
1553
  this.ui.requestRender();
1569
1554
  }
1570
1555
  handleDebugCommand() {
@@ -1640,37 +1625,6 @@ export class InteractiveMode {
1640
1625
  }
1641
1626
  await this.executeCompaction(customInstructions, false);
1642
1627
  }
1643
- handleAutocompactCommand() {
1644
- const newState = !this.session.autoCompactionEnabled;
1645
- this.session.setAutoCompactionEnabled(newState);
1646
- this.footer.setAutoCompactEnabled(newState);
1647
- this.showStatus(`Auto-compaction: ${newState ? "on" : "off"}`);
1648
- }
1649
- showShowImagesSelector() {
1650
- // Only available if terminal supports images
1651
- const caps = getCapabilities();
1652
- if (!caps.images) {
1653
- this.showWarning("Your terminal does not support inline images");
1654
- return;
1655
- }
1656
- this.showSelector((done) => {
1657
- const selector = new ShowImagesSelectorComponent(this.settingsManager.getShowImages(), (newValue) => {
1658
- this.settingsManager.setShowImages(newValue);
1659
- // Update all existing tool execution components with new setting
1660
- for (const child of this.chatContainer.children) {
1661
- if (child instanceof ToolExecutionComponent) {
1662
- child.setShowImages(newValue);
1663
- }
1664
- }
1665
- done();
1666
- this.showStatus(`Inline images: ${newValue ? "on" : "off"}`);
1667
- }, () => {
1668
- done();
1669
- this.ui.requestRender();
1670
- });
1671
- return { component: selector, focus: selector.getSelectList() };
1672
- });
1673
- }
1674
1628
  async executeCompaction(customInstructions, isAuto = false) {
1675
1629
  // Stop loading animation
1676
1630
  if (this.loadingAnimation) {