@vaclav-synacek/pi-coding-agent-termux 0.45.7 → 0.46.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +272 -1323
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +1 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +2 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +2 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +2 -2
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/compaction/compaction.d.ts.map +1 -1
  14. package/dist/core/compaction/compaction.js +6 -5
  15. package/dist/core/compaction/compaction.js.map +1 -1
  16. package/dist/core/keybindings.d.ts +1 -5
  17. package/dist/core/keybindings.d.ts.map +1 -1
  18. package/dist/core/keybindings.js +4 -12
  19. package/dist/core/keybindings.js.map +1 -1
  20. package/dist/core/model-resolver.d.ts.map +1 -1
  21. package/dist/core/model-resolver.js +1 -0
  22. package/dist/core/model-resolver.js.map +1 -1
  23. package/dist/core/tools/edit-diff.d.ts +30 -0
  24. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  25. package/dist/core/tools/edit-diff.js +82 -10
  26. package/dist/core/tools/edit-diff.js.map +1 -1
  27. package/dist/core/tools/edit.d.ts.map +1 -1
  28. package/dist/core/tools/edit.js +16 -13
  29. package/dist/core/tools/edit.js.map +1 -1
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/main.d.ts.map +1 -1
  35. package/dist/main.js +38 -9
  36. package/dist/main.js.map +1 -1
  37. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  38. package/dist/modes/interactive/components/bash-execution.js +4 -3
  39. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  40. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  41. package/dist/modes/interactive/components/bordered-loader.js +2 -1
  42. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  43. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  44. package/dist/modes/interactive/components/branch-summary-message.js +4 -1
  45. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  46. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  47. package/dist/modes/interactive/components/compaction-summary-message.js +4 -1
  48. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  49. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  50. package/dist/modes/interactive/components/custom-editor.js +3 -3
  51. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  52. package/dist/modes/interactive/components/extension-editor.d.ts +3 -1
  53. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  54. package/dist/modes/interactive/components/extension-editor.js +14 -8
  55. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  56. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  57. package/dist/modes/interactive/components/extension-input.js +2 -1
  58. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  59. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  60. package/dist/modes/interactive/components/extension-selector.js +6 -1
  61. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  62. package/dist/modes/interactive/components/index.d.ts +1 -0
  63. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  64. package/dist/modes/interactive/components/index.js +1 -0
  65. package/dist/modes/interactive/components/index.js.map +1 -1
  66. package/dist/modes/interactive/components/keybinding-hints.d.ts +41 -0
  67. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  68. package/dist/modes/interactive/components/keybinding-hints.js +61 -0
  69. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  70. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  71. package/dist/modes/interactive/components/login-dialog.js +4 -3
  72. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  73. package/dist/modes/interactive/components/session-selector-search.d.ts +21 -0
  74. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  75. package/dist/modes/interactive/components/session-selector-search.js +146 -0
  76. package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  77. package/dist/modes/interactive/components/session-selector.d.ts +7 -1
  78. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  79. package/dist/modes/interactive/components/session-selector.js +35 -8
  80. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  81. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  82. package/dist/modes/interactive/components/tool-execution.js +14 -8
  83. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  84. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  85. package/dist/modes/interactive/components/tree-selector.js +5 -2
  86. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  87. package/dist/modes/interactive/interactive-mode.d.ts +4 -4
  88. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  89. package/dist/modes/interactive/interactive-mode.js +58 -95
  90. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  91. package/dist/utils/image-convert.d.ts.map +1 -1
  92. package/dist/utils/image-convert.js +12 -14
  93. package/dist/utils/image-convert.js.map +1 -1
  94. package/dist/utils/image-resize.d.ts +2 -2
  95. package/dist/utils/image-resize.d.ts.map +1 -1
  96. package/dist/utils/image-resize.js +102 -122
  97. package/dist/utils/image-resize.js.map +1 -1
  98. package/examples/extensions/plan-mode/README.md +1 -1
  99. package/examples/extensions/plan-mode/index.ts +2 -2
  100. package/examples/extensions/with-deps/package-lock.json +2 -2
  101. package/examples/extensions/with-deps/package.json +1 -1
  102. package/package.json +5 -5
  103. package/dist/utils/vips.d.ts +0 -11
  104. package/dist/utils/vips.d.ts.map +0 -1
  105. package/dist/utils/vips.js +0 -35
  106. package/dist/utils/vips.js.map +0 -1
@@ -7,7 +7,7 @@ import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
9
  import { getOAuthProviders, } from "@mariozechner/pi-ai";
10
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, getEditorKeybindings, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
10
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
11
11
  import { spawn, spawnSync } from "child_process";
12
12
  import { APP_NAME, getAuthPath, getDebugLogPath, isBunBinary, VERSION } from "../../config.js";
13
13
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
@@ -33,6 +33,7 @@ import { ExtensionEditorComponent } from "./components/extension-editor.js";
33
33
  import { ExtensionInputComponent } from "./components/extension-input.js";
34
34
  import { ExtensionSelectorComponent } from "./components/extension-selector.js";
35
35
  import { FooterComponent } from "./components/footer.js";
36
+ import { appKey, appKeyHint, editorKey, keyHint, rawKeyHint } from "./components/keybinding-hints.js";
36
37
  import { LoginDialogComponent } from "./components/login-dialog.js";
37
38
  import { ModelSelectorComponent } from "./components/model-selector.js";
38
39
  import { OAuthSelectorComponent } from "./components/oauth-selector.js";
@@ -66,7 +67,7 @@ export class InteractiveMode {
66
67
  isInitialized = false;
67
68
  onInputCallback;
68
69
  loadingAnimation = undefined;
69
- defaultWorkingMessage = "Working... (esc to interrupt)";
70
+ defaultWorkingMessage = "Working...";
70
71
  lastSigintTime = 0;
71
72
  lastEscapeTime = 0;
72
73
  changelogMarkdown = undefined;
@@ -231,82 +232,30 @@ export class InteractiveMode {
231
232
  this.setupAutocomplete(this.fdPath);
232
233
  // Add header with keybindings from config
233
234
  const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
234
- // Format keybinding for startup display (lowercase, compact)
235
- const formatStartupKey = (keys) => {
236
- const keyArray = Array.isArray(keys) ? keys : [keys];
237
- return keyArray.join("/");
238
- };
235
+ // Build startup instructions using keybinding hint helpers
239
236
  const kb = this.keybindings;
240
- const interrupt = formatStartupKey(kb.getKeys("interrupt"));
241
- const clear = formatStartupKey(kb.getKeys("clear"));
242
- const exit = formatStartupKey(kb.getKeys("exit"));
243
- const suspend = formatStartupKey(kb.getKeys("suspend"));
244
- const deleteToLineEnd = formatStartupKey(getEditorKeybindings().getKeys("deleteToLineEnd"));
245
- const cycleThinkingLevel = formatStartupKey(kb.getKeys("cycleThinkingLevel"));
246
- const cycleModelForward = formatStartupKey(kb.getKeys("cycleModelForward"));
247
- const cycleModelBackward = formatStartupKey(kb.getKeys("cycleModelBackward"));
248
- const selectModel = formatStartupKey(kb.getKeys("selectModel"));
249
- const expandTools = formatStartupKey(kb.getKeys("expandTools"));
250
- const toggleThinking = formatStartupKey(kb.getKeys("toggleThinking"));
251
- const externalEditor = formatStartupKey(kb.getKeys("externalEditor"));
252
- const followUp = formatStartupKey(kb.getKeys("followUp"));
253
- const dequeue = formatStartupKey(kb.getKeys("dequeue"));
254
- const instructions = theme.fg("dim", interrupt) +
255
- theme.fg("muted", " to interrupt") +
256
- "\n" +
257
- theme.fg("dim", clear) +
258
- theme.fg("muted", " to clear") +
259
- "\n" +
260
- theme.fg("dim", `${clear} twice`) +
261
- theme.fg("muted", " to exit") +
262
- "\n" +
263
- theme.fg("dim", exit) +
264
- theme.fg("muted", " to exit (empty)") +
265
- "\n" +
266
- theme.fg("dim", suspend) +
267
- theme.fg("muted", " to suspend") +
268
- "\n" +
269
- theme.fg("dim", deleteToLineEnd) +
270
- theme.fg("muted", " to delete to end") +
271
- "\n" +
272
- theme.fg("dim", cycleThinkingLevel) +
273
- theme.fg("muted", " to cycle thinking") +
274
- "\n" +
275
- theme.fg("dim", `${cycleModelForward}/${cycleModelBackward}`) +
276
- theme.fg("muted", " to cycle models") +
277
- "\n" +
278
- theme.fg("dim", selectModel) +
279
- theme.fg("muted", " to select model") +
280
- "\n" +
281
- theme.fg("dim", expandTools) +
282
- theme.fg("muted", " to expand tools") +
283
- "\n" +
284
- theme.fg("dim", toggleThinking) +
285
- theme.fg("muted", " to toggle thinking") +
286
- "\n" +
287
- theme.fg("dim", externalEditor) +
288
- theme.fg("muted", " for external editor") +
289
- "\n" +
290
- theme.fg("dim", "/") +
291
- theme.fg("muted", " for commands") +
292
- "\n" +
293
- theme.fg("dim", "!") +
294
- theme.fg("muted", " to run bash") +
295
- "\n" +
296
- theme.fg("dim", "!!") +
297
- theme.fg("muted", " to run bash (no context)") +
298
- "\n" +
299
- theme.fg("dim", followUp) +
300
- theme.fg("muted", " to queue follow-up") +
301
- "\n" +
302
- theme.fg("dim", dequeue) +
303
- theme.fg("muted", " to edit all queued messages") +
304
- "\n" +
305
- theme.fg("dim", "ctrl+v") +
306
- theme.fg("muted", " to paste image") +
307
- "\n" +
308
- theme.fg("dim", "drop files") +
309
- theme.fg("muted", " to attach");
237
+ const hint = (action, desc) => appKeyHint(kb, action, desc);
238
+ const instructions = [
239
+ hint("interrupt", "to interrupt"),
240
+ hint("clear", "to clear"),
241
+ rawKeyHint(`${appKey(kb, "clear")} twice`, "to exit"),
242
+ hint("exit", "to exit (empty)"),
243
+ hint("suspend", "to suspend"),
244
+ keyHint("deleteToLineEnd", "to delete to end"),
245
+ hint("cycleThinkingLevel", "to cycle thinking"),
246
+ rawKeyHint(`${appKey(kb, "cycleModelForward")}/${appKey(kb, "cycleModelBackward")}`, "to cycle models"),
247
+ hint("selectModel", "to select model"),
248
+ hint("expandTools", "to expand tools"),
249
+ hint("toggleThinking", "to toggle thinking"),
250
+ hint("externalEditor", "for external editor"),
251
+ rawKeyHint("/", "for commands"),
252
+ rawKeyHint("!", "to run bash"),
253
+ rawKeyHint("!!", "to run bash (no context)"),
254
+ hint("followUp", "to queue follow-up"),
255
+ hint("dequeue", "to edit all queued messages"),
256
+ hint("pasteImage", "to paste image"),
257
+ rawKeyHint("drop files", "to attach"),
258
+ ].join("\n");
310
259
  this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
311
260
  // Setup UI layout
312
261
  this.ui.addChild(new Spacer(1));
@@ -493,6 +442,13 @@ export class InteractiveMode {
493
442
  this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0));
494
443
  this.chatContainer.addChild(new Spacer(1));
495
444
  }
445
+ // Show loaded prompt templates
446
+ const templates = this.session.promptTemplates;
447
+ if (templates.length > 0) {
448
+ const templateList = templates.map((t) => theme.fg("dim", ` /${t.name} ${t.source}`)).join("\n");
449
+ this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded prompt templates:\n") + templateList, 0, 0));
450
+ this.chatContainer.addChild(new Spacer(1));
451
+ }
496
452
  const extensionRunner = this.session.extensionRunner;
497
453
  if (!extensionRunner) {
498
454
  return; // No extensions loaded
@@ -793,7 +749,12 @@ export class InteractiveMode {
793
749
  setStatus: (key, text) => this.setExtensionStatus(key, text),
794
750
  setWorkingMessage: (message) => {
795
751
  if (this.loadingAnimation) {
796
- this.loadingAnimation.setMessage(message ?? this.defaultWorkingMessage);
752
+ if (message) {
753
+ this.loadingAnimation.setMessage(message);
754
+ }
755
+ else {
756
+ this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${appKey(this.keybindings, "interrupt")} to interrupt)`);
757
+ }
797
758
  }
798
759
  },
799
760
  setWidget: (key, content) => this.setExtensionWidget(key, content),
@@ -916,7 +877,7 @@ export class InteractiveMode {
916
877
  */
917
878
  showExtensionEditor(title, prefill) {
918
879
  return new Promise((resolve) => {
919
- this.extensionEditor = new ExtensionEditorComponent(this.ui, title, prefill, (value) => {
880
+ this.extensionEditor = new ExtensionEditorComponent(this.ui, this.keybindings, title, prefill, (value) => {
920
881
  this.hideExtensionEditor();
921
882
  resolve(value);
922
883
  }, () => {
@@ -1508,7 +1469,7 @@ export class InteractiveMode {
1508
1469
  // Show compacting indicator with reason
1509
1470
  this.statusContainer.clear();
1510
1471
  const reasonText = event.reason === "overflow" ? "Context overflow detected, " : "";
1511
- this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `${reasonText}Auto-compacting... (esc to cancel)`);
1472
+ this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `${reasonText}Auto-compacting... (${appKey(this.keybindings, "interrupt")} to cancel)`);
1512
1473
  this.statusContainer.addChild(this.autoCompactionLoader);
1513
1474
  this.ui.requestRender();
1514
1475
  break;
@@ -1555,7 +1516,7 @@ export class InteractiveMode {
1555
1516
  // Show retry indicator
1556
1517
  this.statusContainer.clear();
1557
1518
  const delaySeconds = Math.round(event.delayMs / 1000);
1558
- this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (esc to cancel)`);
1519
+ this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (${appKey(this.keybindings, "interrupt")} to cancel)`);
1559
1520
  this.statusContainer.addChild(this.retryLoader);
1560
1521
  this.ui.requestRender();
1561
1522
  break;
@@ -2503,7 +2464,7 @@ export class InteractiveMode {
2503
2464
  this.session.abortBranchSummary();
2504
2465
  };
2505
2466
  this.chatContainer.addChild(new Spacer(1));
2506
- summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), "Summarizing branch... (esc to cancel)");
2467
+ summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${appKey(this.keybindings, "interrupt")} to cancel)`);
2507
2468
  this.statusContainer.addChild(summaryLoader);
2508
2469
  this.ui.requestRender();
2509
2470
  }
@@ -2868,7 +2829,10 @@ export class InteractiveMode {
2868
2829
  const content = fs.readFileSync(skillPath, "utf-8");
2869
2830
  // Strip YAML frontmatter if present
2870
2831
  const body = content.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
2871
- const message = args ? `${body}\n\n---\n\nUser: ${args}` : body;
2832
+ const skillDir = path.dirname(skillPath);
2833
+ const header = `Skill location: ${skillPath}\nReferences are relative to ${skillDir}.`;
2834
+ const skillMessage = `${header}\n\n${body}`;
2835
+ const message = args ? `${skillMessage}\n\n---\n\nUser: ${args}` : skillMessage;
2872
2836
  await this.session.prompt(message);
2873
2837
  }
2874
2838
  catch (err) {
@@ -2893,30 +2857,28 @@ export class InteractiveMode {
2893
2857
  this.ui.requestRender();
2894
2858
  }
2895
2859
  /**
2896
- * Format keybindings for display (e.g., "ctrl+c" -> "Ctrl+C").
2860
+ * Capitalize keybinding for display (e.g., "ctrl+c" -> "Ctrl+C").
2897
2861
  */
2898
- formatKeyDisplay(keys) {
2899
- const keyArray = Array.isArray(keys) ? keys : [keys];
2900
- return keyArray
2901
- .map((key) => key
2862
+ capitalizeKey(key) {
2863
+ return key
2864
+ .split("/")
2865
+ .map((k) => k
2902
2866
  .split("+")
2903
2867
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2904
2868
  .join("+"))
2905
2869
  .join("/");
2906
2870
  }
2907
2871
  /**
2908
- * Get display string for an app keybinding action.
2872
+ * Get capitalized display string for an app keybinding action.
2909
2873
  */
2910
2874
  getAppKeyDisplay(action) {
2911
- const display = this.keybindings.getDisplayString(action);
2912
- return this.formatKeyDisplay(display);
2875
+ return this.capitalizeKey(appKey(this.keybindings, action));
2913
2876
  }
2914
2877
  /**
2915
- * Get display string for an editor keybinding action.
2878
+ * Get capitalized display string for an editor keybinding action.
2916
2879
  */
2917
2880
  getEditorKeyDisplay(action) {
2918
- const keys = getEditorKeybindings().getKeys(action);
2919
- return this.formatKeyDisplay(keys);
2881
+ return this.capitalizeKey(editorKey(action));
2920
2882
  }
2921
2883
  handleHotkeysCommand() {
2922
2884
  // Navigation keybindings
@@ -3146,7 +3108,8 @@ export class InteractiveMode {
3146
3108
  };
3147
3109
  // Show compacting status
3148
3110
  this.chatContainer.addChild(new Spacer(1));
3149
- const label = isAuto ? "Auto-compacting context... (esc to cancel)" : "Compacting context... (esc to cancel)";
3111
+ const cancelHint = `(${appKey(this.keybindings, "interrupt")} to cancel)`;
3112
+ const label = isAuto ? `Auto-compacting context... ${cancelHint}` : `Compacting context... ${cancelHint}`;
3150
3113
  const compactingLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
3151
3114
  this.statusContainer.addChild(compactingLoader);
3152
3115
  this.ui.requestRender();